├── .appcast.xml
├── .gitignore
├── README.md
├── assets
└── icon.png
├── package.json
├── preview.gif
├── sketch-textbox-fit-content.sketchplugin
└── Contents
│ ├── Resources
│ └── icon.png
│ └── Sketch
│ ├── fit.js
│ └── manifest.json
└── src
├── fit.js
└── manifest.json
/.appcast.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 | -
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # build artifacts
2 | plugin.sketchplugin
3 |
4 | # npm
5 | node_modules
6 | .npm
7 | npm-debug.log
8 |
9 | # mac
10 | .DS_Store
11 |
12 | # WebStorm
13 | .idea
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Textbox fit Content - Sketch Plugin
2 | This plugin sets the height of a selected text layer or all text layers in a selected group to it's content's height.
3 |
4 | 
5 |
6 |
7 | ## Usage
8 | Select a text layer or a group containing text layers.
9 |
10 |
11 | **To remove whitespace and linebreaks on the beginning and end of the text:**
12 |
13 | Select `Plugins` > `Textbox fit Content` > `Trim and fit text` or press `⌘`+ `Shift`+`F`
14 |
15 |
16 | **To keep all whitespace and linebreaks on the beginning and end of the text:**
17 |
18 | Select `Plugins` > `Textbox fit Content` > `Fit text` or press `⌘`+`alt`+`Shift`+`F`
19 |
20 | **To automatically make the textbox fit it's content height when the text changes enable the auto-fit mode:**
21 |
22 | Select `Plugins` > `Textbox fit Content` > `Toggle auto-fit text` or press `⌘`+`ctrl`+`Shift`+`F`
23 |
24 |
25 | ## Changelog
26 |
27 | ### 1.4.0
28 |
29 | [Fixed] Run auto fit after the text box has been resized.
30 |
31 | ### 1.3.0
32 |
33 | [New] Auto fit mode: Automatically adjust text box size when it's content changes
34 |
35 | ### 1.2.0
36 |
37 | [Fixed] \> 48.1 compatibilty
38 |
39 | [New] Plugin update Support
40 |
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliussohn/sketch-textbox-fit-content/48297c99103978b6a7bd6870b892bf75ee7f696f/assets/icon.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sketch-textbox-fit-content",
3 | "version": "1.4.0",
4 | "description": "This plugin sets the height of a selected text layer or all text layers in a selected group to it's content's height.",
5 | "engines": {
6 | "sketch": ">=3.0"
7 | },
8 | "skpm": {
9 | "name": "Text box fit content",
10 | "manifest": "src/manifest.json",
11 | "main": "sketch-textbox-fit-content.sketchplugin",
12 | "assets": [
13 | "assets/**/*"
14 | ]
15 | },
16 | "scripts": {
17 | "build": "skpm-build",
18 | "watch": "skpm-build --watch",
19 | "start": "skpm-build --watch --run",
20 | "postinstall": "npm run build && skpm-link"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/juliussohn/sketch-textbox-fit-content.git"
25 | },
26 | "devDependencies": {
27 | "@skpm/builder": "^0.4.0"
28 | },
29 | "author": "Julius Sohn "
30 | }
31 |
--------------------------------------------------------------------------------
/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliussohn/sketch-textbox-fit-content/48297c99103978b6a7bd6870b892bf75ee7f696f/preview.gif
--------------------------------------------------------------------------------
/sketch-textbox-fit-content.sketchplugin/Contents/Resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/juliussohn/sketch-textbox-fit-content/48297c99103978b6a7bd6870b892bf75ee7f696f/sketch-textbox-fit-content.sketchplugin/Contents/Resources/icon.png
--------------------------------------------------------------------------------
/sketch-textbox-fit-content.sketchplugin/Contents/Sketch/fit.js:
--------------------------------------------------------------------------------
1 | var that = this;
2 | function __skpm_run (key, context) {
3 | that.context = context;
4 |
5 | var exports =
6 | /******/ (function(modules) { // webpackBootstrap
7 | /******/ // The module cache
8 | /******/ var installedModules = {};
9 | /******/
10 | /******/ // The require function
11 | /******/ function __webpack_require__(moduleId) {
12 | /******/
13 | /******/ // Check if module is in cache
14 | /******/ if(installedModules[moduleId]) {
15 | /******/ return installedModules[moduleId].exports;
16 | /******/ }
17 | /******/ // Create a new module (and put it into the cache)
18 | /******/ var module = installedModules[moduleId] = {
19 | /******/ i: moduleId,
20 | /******/ l: false,
21 | /******/ exports: {}
22 | /******/ };
23 | /******/
24 | /******/ // Execute the module function
25 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
26 | /******/
27 | /******/ // Flag the module as loaded
28 | /******/ module.l = true;
29 | /******/
30 | /******/ // Return the exports of the module
31 | /******/ return module.exports;
32 | /******/ }
33 | /******/
34 | /******/
35 | /******/ // expose the modules object (__webpack_modules__)
36 | /******/ __webpack_require__.m = modules;
37 | /******/
38 | /******/ // expose the module cache
39 | /******/ __webpack_require__.c = installedModules;
40 | /******/
41 | /******/ // define getter function for harmony exports
42 | /******/ __webpack_require__.d = function(exports, name, getter) {
43 | /******/ if(!__webpack_require__.o(exports, name)) {
44 | /******/ Object.defineProperty(exports, name, {
45 | /******/ configurable: false,
46 | /******/ enumerable: true,
47 | /******/ get: getter
48 | /******/ });
49 | /******/ }
50 | /******/ };
51 | /******/
52 | /******/ // getDefaultExport function for compatibility with non-harmony modules
53 | /******/ __webpack_require__.n = function(module) {
54 | /******/ var getter = module && module.__esModule ?
55 | /******/ function getDefault() { return module['default']; } :
56 | /******/ function getModuleExports() { return module; };
57 | /******/ __webpack_require__.d(getter, 'a', getter);
58 | /******/ return getter;
59 | /******/ };
60 | /******/
61 | /******/ // Object.prototype.hasOwnProperty.call
62 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
63 | /******/
64 | /******/ // __webpack_public_path__
65 | /******/ __webpack_require__.p = "";
66 | /******/
67 | /******/ // Load entry module and return exports
68 | /******/ return __webpack_require__(__webpack_require__.s = 1);
69 | /******/ })
70 | /************************************************************************/
71 | /******/ ([
72 | /* 0 */
73 | /***/ (function(module, exports) {
74 |
75 | module.exports = require("sketch/dom");
76 |
77 | /***/ }),
78 | /* 1 */
79 | /***/ (function(module, exports, __webpack_require__) {
80 |
81 | Object.defineProperty(exports, "__esModule", {
82 | value: true
83 | });
84 | exports.runWithTrim = runWithTrim;
85 | exports.runWithoutTrim = runWithoutTrim;
86 | exports.onTextChanged = onTextChanged;
87 | exports.onHandlerLostFocus = onHandlerLostFocus;
88 | exports.toggleAutoResizing = toggleAutoResizing;
89 | exports.run = run;
90 | var Group = __webpack_require__(0).Group;
91 | var Rectangle = __webpack_require__(0).Rectangle;
92 | var Text = __webpack_require__(0).Text;
93 | var UI = __webpack_require__(2);
94 | var Settings = __webpack_require__(3);
95 | var Document = __webpack_require__(0).Document;
96 | var document = Document.getSelectedDocument();
97 | var trim = true;
98 |
99 | function runWithTrim() {
100 | trim = true;
101 | run();
102 | }
103 | function runWithoutTrim() {
104 | trim = false;
105 | run();
106 | }
107 |
108 | function onTextChanged() {
109 | var autoResize = Settings.settingForKey('auto-resize');
110 | if (autoResize) {
111 | runWithoutTrim();
112 | }
113 | }
114 | function onHandlerLostFocus(e) {
115 | onTextChanged();
116 | }
117 |
118 | function toggleAutoResizing() {
119 | var autoResize = Settings.settingForKey('auto-resize');
120 |
121 | if (!autoResize) {
122 | Settings.setSettingForKey('auto-resize', true);
123 | UI.message('✅ Text box auto-fit enabled ');
124 | runWithoutTrim();
125 | } else {
126 | Settings.setSettingForKey('auto-resize', false);
127 | UI.message('❌ Text box auto-fit disabled');
128 | }
129 | }
130 |
131 | function run() {
132 | var selectedLayers = document.selectedLayers;
133 | var selectedCount = selectedLayers.length;
134 |
135 | if (selectedCount === 0) {
136 | UI.message('No layers selected.');
137 | } else {
138 | selectedLayers.forEach(function (layer) {
139 | return checkLayer(layer);
140 | });
141 | }
142 | }
143 |
144 | function checkLayer(layer) {
145 | if (layer.type === "Text") {
146 | fitLayer(layer);
147 | } else if (layer.type === "Group") {
148 | var layers = layer.layers();
149 | for (var i = 0; i < layers.count(); i++) {
150 | checkLayer(layers[i]);
151 | }
152 | Group.fromNative(layer).adjustToFit();
153 | }
154 | }
155 |
156 | function fitLayer(textLayer) {
157 | if (trim) {
158 | var content = textLayer.sketchObject.stringValue();
159 | textLayer.sketchObject.setStringValue(content.replace(/^\s+|\s+$/g, '').trim());
160 | }
161 |
162 | var lineCount = textLayer.fragments.length;
163 | var baseHeight = textLayer.fragments[lineCount - 1].rect.y + textLayer.fragments[lineCount - 1].rect.height;
164 | textLayer.sketchObject.frame().height = baseHeight;
165 | }
166 |
167 | /***/ }),
168 | /* 2 */
169 | /***/ (function(module, exports) {
170 |
171 | module.exports = require("sketch/ui");
172 |
173 | /***/ }),
174 | /* 3 */
175 | /***/ (function(module, exports) {
176 |
177 | module.exports = require("sketch/settings");
178 |
179 | /***/ })
180 | /******/ ]);
181 | if (key === 'default' && typeof exports === 'function') {
182 | exports(context);
183 | } else {
184 | exports[key](context);
185 | }
186 | }
187 | that['runWithTrim'] = __skpm_run.bind(this, 'runWithTrim');
188 | that['onRun'] = __skpm_run.bind(this, 'default');
189 | that['runWithoutTrim'] = __skpm_run.bind(this, 'runWithoutTrim');
190 | that['toggleAutoResizing'] = __skpm_run.bind(this, 'toggleAutoResizing');
191 | that['onTextChanged'] = __skpm_run.bind(this, 'onTextChanged');
192 | that['onHandlerLostFocus'] = __skpm_run.bind(this, 'onHandlerLostFocus')
193 |
--------------------------------------------------------------------------------
/sketch-textbox-fit-content.sketchplugin/Contents/Sketch/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "compatibleVersion": 3,
3 | "bundleVersion": 1,
4 | "commands": [
5 | {
6 | "script": "fit.js",
7 | "handler": "runWithTrim",
8 | "shortcut": "command shift f",
9 | "name": "Trim and fit text",
10 | "identifier": "tfc.fitWithTrim"
11 | },
12 | {
13 | "script": "fit.js",
14 | "handler": "runWithoutTrim",
15 | "shortcut": "command alt shift f",
16 | "name": "Fit text",
17 | "identifier": "tfc.fitWithoutTrim"
18 | },
19 | {
20 | "script": "fit.js",
21 | "handler": "toggleAutoResizing",
22 | "name": "Toggle auto-fit text",
23 | "shortcut": "command ctrl shift f",
24 | "identifier": "tfc.toggleAutoResizing"
25 | },
26 | {
27 | "script": "fit.js",
28 | "handlers": {
29 | "actions": {
30 | "TextChanged.finish": "onTextChanged",
31 | "HandlerLostFocus": "onHandlerLostFocus"
32 | }
33 | },
34 | "name": "Auto resize",
35 | "identifier": "tfc.autoResize"
36 | }
37 | ],
38 | "menu": {
39 | "title": "Textbox fit content",
40 | "items": [
41 | "tfc.fitWithTrim",
42 | "tfc.fitWithoutTrim",
43 | "-",
44 | "tfc.toggleAutoResizing"
45 | ]
46 | },
47 | "version": "1.4.0",
48 | "description": "This plugin sets the height of a selected text layer or all text layers in a selected group to it's content's height.",
49 | "name": "Text box fit content",
50 | "disableCocoaScriptPreprocessor": true,
51 | "appcast": "https://raw.githubusercontent.com/juliussohn/sketch-textbox-fit-content/master/.appcast.xml",
52 | "author": "Julius Sohn",
53 | "authorEmail": "juliussohn@icloud.com"
54 | }
--------------------------------------------------------------------------------
/src/fit.js:
--------------------------------------------------------------------------------
1 | var Group = require('sketch/dom').Group
2 | var Rectangle = require('sketch/dom').Rectangle
3 | var Text = require('sketch/dom').Text
4 | var UI = require('sketch/ui')
5 | var Settings = require('sketch/settings')
6 | var Document = require('sketch/dom').Document
7 | var document = Document.getSelectedDocument()
8 | var trim = true;
9 |
10 | export function runWithTrim() {
11 | trim = true;
12 | run();
13 | }
14 | export function runWithoutTrim() {
15 | trim = false;
16 | run();
17 | }
18 |
19 | export function onTextChanged() {
20 | var autoResize = Settings.settingForKey('auto-resize')
21 | if(autoResize){
22 | runWithoutTrim()
23 | }
24 | }
25 | export function onHandlerLostFocus(e){
26 | onTextChanged();
27 | }
28 |
29 | export function toggleAutoResizing() {
30 | var autoResize = Settings.settingForKey('auto-resize')
31 |
32 | if(!autoResize){
33 | Settings.setSettingForKey('auto-resize', true);
34 | UI.message('✅ Text box auto-fit enabled ');
35 | runWithoutTrim();
36 | }else{
37 | Settings.setSettingForKey('auto-resize', false)
38 | UI.message('❌ Text box auto-fit disabled');
39 | }
40 | }
41 |
42 | export function run() {
43 | var selectedLayers = document.selectedLayers
44 | var selectedCount = selectedLayers.length;
45 |
46 | if (selectedCount === 0) {
47 | UI.message('No layers selected.')
48 | } else {
49 | selectedLayers.forEach(layer => checkLayer(layer));
50 | }
51 | }
52 |
53 | function checkLayer(layer) {
54 | if (layer.type === "Text") {
55 | fitLayer(layer)
56 | } else if (layer.type === "Group") {
57 | var layers = layer.layers();
58 | for (var i = 0; i < layers.count(); i++) {
59 | checkLayer(layers[i]);
60 | }
61 | Group.fromNative(layer).adjustToFit()
62 |
63 | }
64 | }
65 |
66 | function fitLayer(textLayer) {
67 | if (trim) {
68 | var content = textLayer.sketchObject.stringValue();
69 | textLayer.sketchObject.setStringValue(content.replace(/^\s+|\s+$/g, '').trim())
70 | }
71 |
72 | var lineCount = textLayer.fragments.length
73 | var baseHeight = textLayer.fragments[lineCount - 1].rect.y + textLayer.fragments[lineCount - 1].rect.height
74 | textLayer.sketchObject.frame().height = baseHeight
75 |
76 | }
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "compatibleVersion": 3,
3 | "bundleVersion": 1,
4 | "commands": [
5 | {
6 | "script": "fit.js",
7 | "handler": "runWithTrim",
8 | "shortcut": "command shift f",
9 | "name": "Trim and fit text",
10 | "identifier": "tfc.fitWithTrim"
11 | },
12 | {
13 | "script": "fit.js",
14 | "handler": "runWithoutTrim",
15 | "shortcut": "command alt shift f",
16 | "name": "Fit text",
17 | "identifier": "tfc.fitWithoutTrim"
18 | },
19 | {
20 | "script": "fit.js",
21 | "handler": "toggleAutoResizing",
22 | "name": "Toggle auto-fit text",
23 | "shortcut": "command ctrl shift f",
24 | "identifier": "tfc.toggleAutoResizing"
25 | },
26 | {
27 | "script": "fit.js",
28 | "handlers": {
29 | "actions": {
30 | "TextChanged.finish": "onTextChanged",
31 | "HandlerLostFocus":"onHandlerLostFocus"
32 | }
33 | },
34 | "name": "Auto resize",
35 | "identifier": "tfc.autoResize"
36 | }
37 | ],
38 | "menu": {
39 | "title": "Textbox fit content",
40 | "items": [
41 | "tfc.fitWithTrim",
42 | "tfc.fitWithoutTrim",
43 | "-",
44 | "tfc.toggleAutoResizing"
45 | ]
46 | }
47 | }
--------------------------------------------------------------------------------