├── .appcast.xml
├── .github
└── workflows
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── README.md
├── assets
├── flat-export-logo-transparent.png
├── flat-export-logo.png
├── icon.png
├── img-dialog.png
├── img-finder-files.png
├── img-sketch-symbols.png
└── white-flat-export-logo.png
├── flat-export.sketchplugin
└── Contents
│ ├── Resources
│ ├── flat-export-logo-transparent.png
│ ├── flat-export-logo.png
│ ├── icon.png
│ ├── img-dialog.png
│ ├── img-finder-files.png
│ ├── img-sketch-symbols.png
│ └── white-flat-export-logo.png
│ └── Sketch
│ ├── __rename-export.js
│ ├── __rename-export.js.map
│ ├── manifest.json
│ ├── rename-export.js
│ └── rename-export.js.map
├── jest.config.js
├── package-lock.json
├── package.json
└── src
├── dialog.js
├── macos-ui.js
├── manifest.json
├── rename-export.js
├── utils.js
└── utils.test.js
/.appcast.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 | -
8 |
9 |
10 | -
11 |
12 |
13 | -
14 |
15 |
16 | -
17 |
18 |
19 | -
20 |
21 |
22 | -
23 |
24 |
25 | -
26 |
27 |
28 | -
29 |
30 |
31 | -
32 |
33 |
34 | -
35 |
36 |
37 | -
38 |
39 |
40 | -
41 |
42 |
43 | -
44 |
45 |
46 | -
47 |
48 |
49 | -
50 |
51 |
52 | -
53 |
54 |
55 | -
56 |
57 |
58 | -
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | # ignore commits made after publishing
7 | tags-ignore:
8 | - v*
9 |
10 | jobs:
11 | publish:
12 | runs-on: macos-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | with:
16 | repo-token: ${{ secrets.GITHUB_TOKEN }}
17 |
18 | - name: Install
19 | uses: bahmutov/npm-install@v1.1.0
20 |
21 | - name: Install SKPM
22 | run: npm i -g skpm
23 |
24 | - name: Login
25 | run: skpm login ${{ secrets.GH_ACCESS_TOKEN }}
26 |
27 | - name: Publish
28 | run: npm run publish
29 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | # Controls when the action will run. Triggers the workflow on push or pull request
4 | # events but only for the master branch
5 | on:
6 | push:
7 | branches: [ master ]
8 | pull_request:
9 | branches: [ master ]
10 |
11 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
12 | jobs:
13 | # This workflow contains a single job called "build"
14 | test:
15 | # The type of runner that the job will run on
16 | runs-on: ubuntu-latest
17 |
18 | # Steps represent a sequence of tasks that will be executed as part of the job
19 | steps:
20 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
21 | - uses: actions/checkout@v2
22 | # Install
23 | - name: Install
24 | uses: bahmutov/npm-install@v1.1.0
25 | # Run Jest
26 | - name: Run tests
27 | run: npm run test
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # build artifacts
2 | # *.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 |
15 | .skpmrc
16 | *.sketch
17 | .env
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sketch Flat Export
2 |
3 | 
4 |
5 | 
6 |
7 | ## Purpose
8 |
9 | This plugin lets you export your symbols and layers with manageable filenames into one folder, without needing to change your Sketch symbol nesting or naming conventions.
10 |
11 | #### These Sketch symbols...
12 | 
13 |
14 | #### ...export to these files
15 | 
16 |
17 | ## Usage
18 |
19 | Download the Sketch Plugin file, and double click it, or move it to your plugins folder manually.
20 |
21 | To use, press `⌃ Ctrl` + `⇧ Shift` + `E` to open the dialog.
22 |
23 | 
24 |
25 | ## Parameters
26 |
27 | ### Name format
28 | Choose between `kebab-case`, `snake_case` or `camelCase`
29 |
30 | ### Use full layer name
31 | Uses the full layer name when exporting
32 |
33 | e.g. The layer `Icon/Search` exports as: `icon-search.svg`.
34 | When turned off, this same layer will export as: `search.svg`
35 |
36 | ### Prefix
37 | Text to add to the beginning of the filename
38 |
39 | e.g. `company-icon-search.svg`
40 |
41 | ### File format
42 | Export as `svg`, `png`, or `jpg`
43 |
44 | ### Scale
45 | (since v2.2.3)
46 | Select export size `@1x`, `@2x` or `@3x`
47 |
48 |
--------------------------------------------------------------------------------
/assets/flat-export-logo-transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/assets/flat-export-logo-transparent.png
--------------------------------------------------------------------------------
/assets/flat-export-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/assets/flat-export-logo.png
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/assets/icon.png
--------------------------------------------------------------------------------
/assets/img-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/assets/img-dialog.png
--------------------------------------------------------------------------------
/assets/img-finder-files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/assets/img-finder-files.png
--------------------------------------------------------------------------------
/assets/img-sketch-symbols.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/assets/img-sketch-symbols.png
--------------------------------------------------------------------------------
/assets/white-flat-export-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/assets/white-flat-export-logo.png
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Resources/flat-export-logo-transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/flat-export.sketchplugin/Contents/Resources/flat-export-logo-transparent.png
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Resources/flat-export-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/flat-export.sketchplugin/Contents/Resources/flat-export-logo.png
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/flat-export.sketchplugin/Contents/Resources/icon.png
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Resources/img-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/flat-export.sketchplugin/Contents/Resources/img-dialog.png
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Resources/img-finder-files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/flat-export.sketchplugin/Contents/Resources/img-finder-files.png
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Resources/img-sketch-symbols.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/flat-export.sketchplugin/Contents/Resources/img-sketch-symbols.png
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Resources/white-flat-export-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/c10b463a4bd4c4c1fb30d1b7b6dbe5fa602a420e/flat-export.sketchplugin/Contents/Resources/white-flat-export-logo.png
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Sketch/__rename-export.js:
--------------------------------------------------------------------------------
1 | var globalThis = this;
2 | var global = this;
3 | function __skpm_run (key, context) {
4 | globalThis.context = context;
5 | try {
6 |
7 | var exports =
8 | /******/ (function(modules) { // webpackBootstrap
9 | /******/ // The module cache
10 | /******/ var installedModules = {};
11 | /******/
12 | /******/ // The require function
13 | /******/ function __webpack_require__(moduleId) {
14 | /******/
15 | /******/ // Check if module is in cache
16 | /******/ if(installedModules[moduleId]) {
17 | /******/ return installedModules[moduleId].exports;
18 | /******/ }
19 | /******/ // Create a new module (and put it into the cache)
20 | /******/ var module = installedModules[moduleId] = {
21 | /******/ i: moduleId,
22 | /******/ l: false,
23 | /******/ exports: {}
24 | /******/ };
25 | /******/
26 | /******/ // Execute the module function
27 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
28 | /******/
29 | /******/ // Flag the module as loaded
30 | /******/ module.l = true;
31 | /******/
32 | /******/ // Return the exports of the module
33 | /******/ return module.exports;
34 | /******/ }
35 | /******/
36 | /******/
37 | /******/ // expose the modules object (__webpack_modules__)
38 | /******/ __webpack_require__.m = modules;
39 | /******/
40 | /******/ // expose the module cache
41 | /******/ __webpack_require__.c = installedModules;
42 | /******/
43 | /******/ // define getter function for harmony exports
44 | /******/ __webpack_require__.d = function(exports, name, getter) {
45 | /******/ if(!__webpack_require__.o(exports, name)) {
46 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
47 | /******/ }
48 | /******/ };
49 | /******/
50 | /******/ // define __esModule on exports
51 | /******/ __webpack_require__.r = function(exports) {
52 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
53 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
54 | /******/ }
55 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
56 | /******/ };
57 | /******/
58 | /******/ // create a fake namespace object
59 | /******/ // mode & 1: value is a module id, require it
60 | /******/ // mode & 2: merge all properties of value into the ns
61 | /******/ // mode & 4: return value when already ns object
62 | /******/ // mode & 8|1: behave like require
63 | /******/ __webpack_require__.t = function(value, mode) {
64 | /******/ if(mode & 1) value = __webpack_require__(value);
65 | /******/ if(mode & 8) return value;
66 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
67 | /******/ var ns = Object.create(null);
68 | /******/ __webpack_require__.r(ns);
69 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
70 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
71 | /******/ return ns;
72 | /******/ };
73 | /******/
74 | /******/ // getDefaultExport function for compatibility with non-harmony modules
75 | /******/ __webpack_require__.n = function(module) {
76 | /******/ var getter = module && module.__esModule ?
77 | /******/ function getDefault() { return module['default']; } :
78 | /******/ function getModuleExports() { return module; };
79 | /******/ __webpack_require__.d(getter, 'a', getter);
80 | /******/ return getter;
81 | /******/ };
82 | /******/
83 | /******/ // Object.prototype.hasOwnProperty.call
84 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
85 | /******/
86 | /******/ // __webpack_public_path__
87 | /******/ __webpack_require__.p = "";
88 | /******/
89 | /******/
90 | /******/ // Load entry module and return exports
91 | /******/ return __webpack_require__(__webpack_require__.s = "./src/rename-export.js");
92 | /******/ })
93 | /************************************************************************/
94 | /******/ ({
95 |
96 | /***/ "./src/dialog.js":
97 | /*!***********************!*\
98 | !*** ./src/dialog.js ***!
99 | \***********************/
100 | /*! exports provided: createDialog, viewContents, DIALOG_ELEMENTS */
101 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
102 |
103 | "use strict";
104 | __webpack_require__.r(__webpack_exports__);
105 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createDialog", function() { return createDialog; });
106 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "viewContents", function() { return viewContents; });
107 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DIALOG_ELEMENTS", function() { return DIALOG_ELEMENTS; });
108 | /* harmony import */ var _macos_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./macos-ui */ "./src/macos-ui.js");
109 |
110 | var DIALOG_TITLE = "Flat Export";
111 | var ELEMENT_HEIGHT = 24;
112 | var DIALOG_ELEMENTS = [{
113 | type: 'label',
114 | id: 'caseLabel',
115 | value: 'Name format:',
116 | paddingBottom: -2
117 | }, {
118 | type: 'select',
119 | id: 'selectCase',
120 | value: ['kebab-case', 'snake_case', 'camelCase'],
121 | paddingBottom: 8
122 | }, {
123 | type: 'checkbox',
124 | id: 'fullName',
125 | label: 'Use full layer name',
126 | value: 'full-name',
127 | default: true,
128 | paddingBottom: 8
129 | }, {
130 | type: 'label',
131 | id: 'prefixLabel',
132 | value: 'Name prefix:',
133 | paddingBottom: -2
134 | }, {
135 | type: 'text',
136 | id: 'prefix',
137 | value: '',
138 | placeholder: 'Prefix',
139 | paddingBottom: 8
140 | }, {
141 | type: 'label',
142 | id: 'fileFormat',
143 | value: 'File format:',
144 | paddingBottom: -2
145 | }, {
146 | type: 'select',
147 | id: 'selectFormat',
148 | value: ['svg', 'png', 'jpg'],
149 | paddingBottom: 8
150 | }, {
151 | type: 'label',
152 | id: 'outputScale',
153 | value: 'Scale',
154 | paddingBottom: -2
155 | }, {
156 | type: 'select',
157 | id: 'selectScale',
158 | value: ['@1x', '@2x', '@3x'],
159 | paddingBottom: 8
160 | }];
161 | var viewContents = null; // Create a custom dialog
162 |
163 | function createDialog(previousSetting) {
164 | var dialog = NSAlert.alloc().init();
165 | dialog.setMessageText(DIALOG_TITLE);
166 | dialog.addButtonWithTitle("Continue");
167 | dialog.addButtonWithTitle("Cancel");
168 | var COUNT_ELEMENTS = DIALOG_ELEMENTS.length;
169 | var PADDING = DIALOG_ELEMENTS.map(function (elem) {
170 | return elem.paddingBottom;
171 | }).reduce(function (acc, pad) {
172 | return acc + pad;
173 | }, 0);
174 | var TOTAL_MODAL_HEIGHT = COUNT_ELEMENTS * ELEMENT_HEIGHT + PADDING;
175 | var customView = NSView.alloc().initWithFrame(NSMakeRect(0, 0, 200, TOTAL_MODAL_HEIGHT));
176 | var position_next_elem = ELEMENT_HEIGHT;
177 | viewContents = DIALOG_ELEMENTS.map(function (element, i) {
178 | var type = element.type; // let padding = (i === 0 || i === 1 || i === 4) ? 0 : element.paddingBottom
179 |
180 | var yPos = TOTAL_MODAL_HEIGHT - position_next_elem; // ((i+1) * (ELEMENT_HEIGHT))
181 |
182 | var UIElement;
183 |
184 | if (type == 'label') {
185 | UIElement = Object(_macos_ui__WEBPACK_IMPORTED_MODULE_0__["createLabel"])(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), 12, false, element.value);
186 | } else if (type == 'select') {
187 | UIElement = Object(_macos_ui__WEBPACK_IMPORTED_MODULE_0__["createSelect"])(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), element.value);
188 | } else if (type == 'checkbox') {
189 | UIElement = Object(_macos_ui__WEBPACK_IMPORTED_MODULE_0__["createCheckbox"])(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), element.label, element.value, element.default, true);
190 | } else if (type == 'text') {
191 | UIElement = Object(_macos_ui__WEBPACK_IMPORTED_MODULE_0__["createTextbox"])({
192 | frame: NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT),
193 | size: 12,
194 | text: element.value,
195 | placeholder: element.placeholder
196 | });
197 | }
198 |
199 | if (typeof previousSetting !== 'undefined') {
200 | if (element.id == 'selectCase') {
201 | UIElement.selectItemWithTitle(previousSetting.selectCase);
202 | } else if (element.id == 'fullName') {
203 | UIElement.setEnabled(previousSetting.useFullName);
204 | } else if (element.id == 'prefix') {
205 | UIElement.setStringValue(previousSetting.prefix);
206 | } else if (element.id == 'selectFormat') {
207 | UIElement.selectItemWithTitle(previousSetting.selectFormat);
208 | } else if (element.id == 'selectScale') {
209 | UIElement.selectItemWithTitle(previousSetting.selectScale);
210 | }
211 | }
212 |
213 | position_next_elem += ELEMENT_HEIGHT + element.paddingBottom;
214 | return UIElement;
215 | });
216 | viewContents.forEach(function (subview) {
217 | customView.addSubview(subview);
218 | });
219 | dialog.setAccessoryView(customView);
220 | return dialog;
221 | }
222 |
223 |
224 |
225 | /***/ }),
226 |
227 | /***/ "./src/macos-ui.js":
228 | /*!*************************!*\
229 | !*** ./src/macos-ui.js ***!
230 | \*************************/
231 | /*! exports provided: rect, createLabel, createTextbox, createSelect, createCheckbox */
232 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
233 |
234 | "use strict";
235 | __webpack_require__.r(__webpack_exports__);
236 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "rect", function() { return rect; });
237 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createLabel", function() { return createLabel; });
238 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createTextbox", function() { return createTextbox; });
239 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createSelect", function() { return createSelect; });
240 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createCheckbox", function() { return createCheckbox; });
241 | // -------------------------------------------------
242 | // --------------- Dialog formatting ---------------
243 | // -------------------------------------------------
244 | function rect(x, y, w, h) {
245 | var rect = NSMakeRect(x, y, w, h);
246 | return rect;
247 | }
248 | function createLabel(frame, size, bold, text) {
249 | var label = NSTextField.alloc().initWithFrame(frame);
250 | label.setStringValue(text);
251 | label.setBezeled(false);
252 | label.setDrawsBackground(false);
253 | label.setEditable(false);
254 | label.setSelectable(false);
255 |
256 | if (bold) {
257 | label.setFont(NSFont.boldSystemFontOfSize(size));
258 | } else {
259 | label.setFont(NSFont.systemFontOfSize(size));
260 | }
261 |
262 | return label;
263 | }
264 | function createTextbox(_ref) {
265 | var frame = _ref.frame,
266 | size = _ref.size,
267 | bold = _ref.bold,
268 | text = _ref.text,
269 | placeholder = _ref.placeholder;
270 | var label = NSTextField.alloc().initWithFrame(frame);
271 | label.setStringValue(text);
272 | label.setBezeled(true);
273 | label.setDrawsBackground(true);
274 | label.setEditable(true);
275 | label.setSelectable(true);
276 | label.placeholderString = placeholder;
277 |
278 | if (bold) {
279 | label.setFont(NSFont.boldSystemFontOfSize(size));
280 | } else {
281 | label.setFont(NSFont.systemFontOfSize(size));
282 | }
283 |
284 | return label;
285 | }
286 | function createSelect(frame, items) {
287 | var select = NSPopUpButton.alloc().initWithFrame(frame);
288 |
289 | for (var i = 0; i < items.length; i++) {
290 | if (items[i] == "--") {
291 | select.menu().addItem(NSMenuItem.separatorItem());
292 | } else {
293 | select.addItemWithTitle(items[i]);
294 | }
295 | }
296 |
297 | return select;
298 | }
299 | function createCheckbox(frame, name, value, onstate, enabled) {
300 | var checkbox = NSButton.alloc().initWithFrame(frame);
301 | checkbox.setButtonType(NSSwitchButton); // checkbox.setBezelStyle(1);
302 |
303 | checkbox.setTitle(name);
304 | checkbox.setTag(value);
305 | checkbox.setState(onstate ? NSOnState : NSOffState);
306 | checkbox.setEnabled(enabled);
307 | return checkbox;
308 | }
309 |
310 | /***/ }),
311 |
312 | /***/ "./src/rename-export.js":
313 | /*!******************************!*\
314 | !*** ./src/rename-export.js ***!
315 | \******************************/
316 | /*! exports provided: default */
317 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
318 |
319 | "use strict";
320 | __webpack_require__.r(__webpack_exports__);
321 | /* harmony import */ var sketch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! sketch */ "sketch");
322 | /* harmony import */ var sketch__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(sketch__WEBPACK_IMPORTED_MODULE_0__);
323 | /* harmony import */ var sketch_settings__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! sketch/settings */ "sketch/settings");
324 | /* harmony import */ var sketch_settings__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(sketch_settings__WEBPACK_IMPORTED_MODULE_1__);
325 | /* harmony import */ var _dialog__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./dialog */ "./src/dialog.js");
326 | /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils */ "./src/utils.js");
327 | // documentation: https://developer.sketchapp.com/reference/api/
328 |
329 |
330 |
331 |
332 | var PREFS_KEY = "previous_settings"; // -------------------------------------------------
333 | // ------------------- The Plugin ------------------
334 | // -------------------------------------------------
335 |
336 | /* harmony default export */ __webpack_exports__["default"] = (function (context) {
337 | var doc = sketch__WEBPACK_IMPORTED_MODULE_0___default.a.getSelectedDocument();
338 | if (!doc) return;
339 | var selection = doc.selectedLayers;
340 |
341 | if (selection.length === 0) {
342 | sketch__WEBPACK_IMPORTED_MODULE_0___default.a.UI.message('No layers are selected.');
343 | return;
344 | } else {
345 | // Load user settings
346 | var previousSettings = sketch_settings__WEBPACK_IMPORTED_MODULE_1___default.a.settingForKey(PREFS_KEY);
347 | var dialog = Object(_dialog__WEBPACK_IMPORTED_MODULE_2__["createDialog"])(previousSettings); // Run the dialog
348 |
349 | if (dialog.runModal() !== NSAlertFirstButtonReturn) {
350 | return;
351 | } else {
352 | var caseElemIdx = _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"].findIndex(function (elem) {
353 | return elem.id === 'selectCase';
354 | });
355 | var prefixElemIdx = _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"].findIndex(function (elem) {
356 | return elem.id === 'prefix';
357 | });
358 | var fullNameElemIdx = _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"].findIndex(function (elem) {
359 | return elem.id === 'fullName';
360 | });
361 | var formatElemIdx = _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"].findIndex(function (elem) {
362 | return elem.id === 'selectFormat';
363 | });
364 | var scaleElemIdx = _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"].findIndex(function (elem) {
365 | return elem.id === 'selectScale';
366 | }); // Save the responses from that modal
367 |
368 | var caseIndex = _dialog__WEBPACK_IMPORTED_MODULE_2__["viewContents"][caseElemIdx].indexOfSelectedItem();
369 | var prefix = _dialog__WEBPACK_IMPORTED_MODULE_2__["viewContents"][prefixElemIdx].stringValue();
370 | var isFullName = _dialog__WEBPACK_IMPORTED_MODULE_2__["viewContents"][fullNameElemIdx].state();
371 | var formatvalueIndex = _dialog__WEBPACK_IMPORTED_MODULE_2__["viewContents"][formatElemIdx].indexOfSelectedItem();
372 | var scaleValueIndex = _dialog__WEBPACK_IMPORTED_MODULE_2__["viewContents"][scaleElemIdx].indexOfSelectedItem(); // sketch.UI.message(`${caseIndex}, ${prefix}, ${isFullName}, ${formatvalueIndex}`)
373 | // Save user settings
374 |
375 | var prefs = {
376 | "selectCase": _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"][caseElemIdx].value[caseIndex],
377 | "useFullName": new Boolean(isFullName),
378 | "prefix": "".concat(prefix),
379 | "selectFormat": _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"][formatElemIdx].value[formatvalueIndex],
380 | "selectScale": _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"][scaleElemIdx].value[scaleValueIndex]
381 | };
382 | sketch_settings__WEBPACK_IMPORTED_MODULE_1___default.a.setSettingForKey(PREFS_KEY, prefs); // Create an Open dialog
383 |
384 | var open = NSOpenPanel.openPanel();
385 | open.canChooseFiles = false;
386 | open.canChooseDirectories = true;
387 | open.canCreateDirectories = true; // run the open dialog
388 |
389 | if (open.runModal()) {
390 | var path = open.URL().path();
391 | var layers = selection.layers; // Preserve the original layer names so we can change them back
392 |
393 | var originalLayerNames = layers.map(function (layer) {
394 | return layer.name;
395 | }); // Get the selected file format
396 |
397 | var fileFormat = _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"][formatElemIdx].value[formatvalueIndex];
398 | var scale = _dialog__WEBPACK_IMPORTED_MODULE_2__["DIALOG_ELEMENTS"][scaleElemIdx].value[scaleValueIndex].replace(/[@x]/, ''); // Change the file names appropriately
399 |
400 | layers.forEach(function (layer) {
401 | var newName = isFullName ? layer.name : layer.name.substring(layer.name.lastIndexOf('/') + 1, layer.name.length);
402 | newName = prefix == '' ? // NOT === because prefix is an object (...?)
403 | newName : "".concat(prefix, "-").concat(newName);
404 |
405 | if (caseIndex === 0) {
406 | layer.name = Object(_utils__WEBPACK_IMPORTED_MODULE_3__["toKebab"])(newName);
407 | } else if (caseIndex === 1) {
408 | layer.name = Object(_utils__WEBPACK_IMPORTED_MODULE_3__["toSnake"])(newName);
409 | } else if (caseIndex === 2) {
410 | layer.name = Object(_utils__WEBPACK_IMPORTED_MODULE_3__["toCamel"])(newName);
411 | }
412 | }); // Set the format and save path
413 |
414 | var exportOptions = {
415 | formats: fileFormat,
416 | scales: scale,
417 | output: path
418 | }; // Export the layers
419 |
420 | sketch__WEBPACK_IMPORTED_MODULE_0___default.a.export(layers, exportOptions); // Reset the layer names
421 |
422 | layers.forEach(function (layer, i) {
423 | layer.name = originalLayerNames[i];
424 | }); // Show confirmation
425 |
426 | sketch__WEBPACK_IMPORTED_MODULE_0___default.a.UI.message("Exported ".concat(layers.length, " layers."));
427 | }
428 | }
429 | }
430 | });
431 |
432 | /***/ }),
433 |
434 | /***/ "./src/utils.js":
435 | /*!**********************!*\
436 | !*** ./src/utils.js ***!
437 | \**********************/
438 | /*! exports provided: delimitString, toSnake, toKebab, toCamel, toPascal */
439 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
440 |
441 | "use strict";
442 | __webpack_require__.r(__webpack_exports__);
443 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delimitString", function() { return delimitString; });
444 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toSnake", function() { return toSnake; });
445 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toKebab", function() { return toKebab; });
446 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toCamel", function() { return toCamel; });
447 | /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toPascal", function() { return toPascal; });
448 | // -------------------------------------------------
449 | // ---------------- Text formatting ----------------
450 | // -------------------------------------------------
451 | var allDelimiters = /[\ \\\/\_\-\–\—]/g;
452 | function delimitString(str, delimiter) {
453 | return str.replace(allDelimiters, delimiter).split(delimiter).filter(function (chunk) {
454 | return chunk.length !== 0;
455 | }).join(delimiter).toLowerCase();
456 | }
457 | function toSnake(str) {
458 | var delimiter = '_';
459 | if (typeof str !== 'string') return "";
460 | return delimitString(str, delimiter);
461 | }
462 | function toKebab(str) {
463 | var delimiter = '-';
464 | if (typeof str !== 'string') return "";
465 | return delimitString(str, delimiter);
466 | }
467 | function toCamel(str) {
468 | if (typeof str !== 'string') return "";
469 | str = delimitString(str, ' ').split(allDelimiters);
470 |
471 | if (str.length > 1) {
472 | for (var i = 1; i < str.length; i++) {
473 | str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1);
474 | }
475 | }
476 |
477 | return str.join('');
478 | }
479 | function toPascal(str) {
480 | var camel = toCamel(str);
481 | return camel.charAt(0).toUpperCase() + camel.slice(1);
482 | }
483 |
484 | /***/ }),
485 |
486 | /***/ "sketch":
487 | /*!*************************!*\
488 | !*** external "sketch" ***!
489 | \*************************/
490 | /*! no static exports found */
491 | /***/ (function(module, exports) {
492 |
493 | module.exports = require("sketch");
494 |
495 | /***/ }),
496 |
497 | /***/ "sketch/settings":
498 | /*!**********************************!*\
499 | !*** external "sketch/settings" ***!
500 | \**********************************/
501 | /*! no static exports found */
502 | /***/ (function(module, exports) {
503 |
504 | module.exports = require("sketch/settings");
505 |
506 | /***/ })
507 |
508 | /******/ });
509 | if (key === 'default' && typeof exports === 'function') {
510 | exports(context);
511 | } else if (typeof exports[key] !== 'function') {
512 | throw new Error('Missing export named "' + key + '". Your command should contain something like `export function " + key +"() {}`.');
513 | } else {
514 | exports[key](context);
515 | }
516 | } catch (err) {
517 | if (typeof process !== 'undefined' && process.listenerCount && process.listenerCount('uncaughtException')) {
518 | process.emit("uncaughtException", err, "uncaughtException");
519 | } else {
520 | throw err
521 | }
522 | }
523 | }
524 | globalThis['onRun'] = __skpm_run.bind(this, 'default')
525 |
526 | //# sourceMappingURL=__rename-export.js.map
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Sketch/__rename-export.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./src/dialog.js","webpack://exports/./src/macos-ui.js","webpack://exports/./src/rename-export.js","webpack://exports/./src/utils.js","webpack://exports/external \"sketch\"","webpack://exports/external \"sketch/settings\""],"names":["DIALOG_TITLE","ELEMENT_HEIGHT","DIALOG_ELEMENTS","type","id","value","paddingBottom","label","default","placeholder","viewContents","createDialog","previousSetting","dialog","NSAlert","alloc","init","setMessageText","addButtonWithTitle","COUNT_ELEMENTS","length","PADDING","map","elem","reduce","acc","pad","TOTAL_MODAL_HEIGHT","customView","NSView","initWithFrame","NSMakeRect","position_next_elem","element","i","yPos","UIElement","createLabel","createSelect","createCheckbox","createTextbox","frame","size","text","selectItemWithTitle","selectCase","setEnabled","useFullName","setStringValue","prefix","selectFormat","selectScale","forEach","subview","addSubview","setAccessoryView","rect","x","y","w","h","bold","NSTextField","setBezeled","setDrawsBackground","setEditable","setSelectable","setFont","NSFont","boldSystemFontOfSize","systemFontOfSize","placeholderString","items","select","NSPopUpButton","menu","addItem","NSMenuItem","separatorItem","addItemWithTitle","name","onstate","enabled","checkbox","NSButton","setButtonType","NSSwitchButton","setTitle","setTag","setState","NSOnState","NSOffState","PREFS_KEY","context","doc","sketch","getSelectedDocument","selection","selectedLayers","UI","message","previousSettings","settings","settingForKey","runModal","NSAlertFirstButtonReturn","caseElemIdx","findIndex","prefixElemIdx","fullNameElemIdx","formatElemIdx","scaleElemIdx","caseIndex","indexOfSelectedItem","stringValue","isFullName","state","formatvalueIndex","scaleValueIndex","prefs","Boolean","setSettingForKey","open","NSOpenPanel","openPanel","canChooseFiles","canChooseDirectories","canCreateDirectories","path","URL","layers","originalLayerNames","layer","fileFormat","scale","replace","newName","substring","lastIndexOf","toKebab","toSnake","toCamel","exportOptions","formats","scales","output","export","allDelimiters","delimitString","str","delimiter","split","filter","chunk","join","toLowerCase","charAt","toUpperCase","slice","toPascal","camel"],"mappings":"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","file":"__rename-export.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/rename-export.js\");\n","import {\n createLabel,\n createTextbox,\n createSelect,\n createCheckbox\n} from './macos-ui'\n\nconst DIALOG_TITLE = \"Flat Export\"\n\nconst ELEMENT_HEIGHT = 24;\nconst DIALOG_ELEMENTS = [\n {\n type: 'label',\n id: 'caseLabel',\n value: 'Name format:',\n paddingBottom: -2\n },\n {\n type: 'select',\n id: 'selectCase',\n value: ['kebab-case', 'snake_case', 'camelCase'],\n paddingBottom: 8\n },\n {\n type: 'checkbox',\n id: 'fullName',\n label: 'Use full layer name',\n value: 'full-name',\n default: true,\n paddingBottom: 8\n },\n {\n type: 'label',\n id: 'prefixLabel',\n value: 'Name prefix:',\n paddingBottom: -2\n },\n {\n type: 'text',\n id: 'prefix',\n value: '',\n placeholder: 'Prefix',\n paddingBottom: 8\n },\n {\n type: 'label',\n id: 'fileFormat',\n value: 'File format:',\n paddingBottom: -2\n },\n {\n type: 'select',\n id: 'selectFormat',\n value: ['svg', 'png', 'jpg'],\n paddingBottom: 8\n },\n {\n type: 'label',\n id: 'outputScale',\n value: 'Scale',\n paddingBottom: -2\n },\n {\n type: 'select',\n id: 'selectScale',\n value: ['@1x', '@2x', '@3x'],\n paddingBottom: 8\n },\n]\n\nlet viewContents = null\n\n// Create a custom dialog\nfunction createDialog(previousSetting) {\n const dialog = NSAlert.alloc().init();\n\n dialog.setMessageText(DIALOG_TITLE);\n dialog.addButtonWithTitle(\"Continue\");\n dialog.addButtonWithTitle(\"Cancel\");\n\n const COUNT_ELEMENTS = DIALOG_ELEMENTS.length\n const PADDING = DIALOG_ELEMENTS.map(elem => elem.paddingBottom).reduce((acc, pad) => acc + pad, 0)\n const TOTAL_MODAL_HEIGHT = COUNT_ELEMENTS * (ELEMENT_HEIGHT) + PADDING\n const customView = NSView.alloc().initWithFrame(NSMakeRect(0, 0, 200, TOTAL_MODAL_HEIGHT));\n let position_next_elem = ELEMENT_HEIGHT\n\n viewContents = DIALOG_ELEMENTS.map((element, i) => {\n let type = element.type\n\n // let padding = (i === 0 || i === 1 || i === 4) ? 0 : element.paddingBottom\n let yPos = TOTAL_MODAL_HEIGHT - position_next_elem // ((i+1) * (ELEMENT_HEIGHT))\n\n let UIElement;\n\n if (type == 'label') {\n UIElement = createLabel(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), 12, false, element.value);\n } else if (type == 'select') {\n UIElement = createSelect(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), element.value)\n } else if (type == 'checkbox') {\n UIElement = createCheckbox(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), element.label, element.value, element.default, true);\n } else if (type == 'text') {\n UIElement = createTextbox({\n frame: NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT),\n size: 12,\n text: element.value,\n placeholder: element.placeholder\n })\n }\n\n if (typeof previousSetting !== 'undefined') {\n if (element.id == 'selectCase') {\n UIElement.selectItemWithTitle(previousSetting.selectCase)\n } else if (element.id == 'fullName') {\n UIElement.setEnabled(previousSetting.useFullName)\n } else if (element.id == 'prefix') {\n UIElement.setStringValue(previousSetting.prefix)\n } else if (element.id == 'selectFormat') {\n UIElement.selectItemWithTitle(previousSetting.selectFormat)\n } else if (element.id == 'selectScale') {\n UIElement.selectItemWithTitle(previousSetting.selectScale)\n }\n }\n\n position_next_elem += ELEMENT_HEIGHT + element.paddingBottom\n\n return UIElement\n })\n\n viewContents.forEach(subview => {\n customView.addSubview(subview)\n })\n dialog.setAccessoryView(customView)\n\n return dialog\n}\n\nexport { createDialog, viewContents, DIALOG_ELEMENTS }","// -------------------------------------------------\n// --------------- Dialog formatting ---------------\n// -------------------------------------------------\nexport function rect(x, y, w, h) {\n var rect = NSMakeRect(x, y, w, h)\n return rect;\n}\n\nexport function createLabel(frame, size, bold, text) {\n var label = NSTextField.alloc().initWithFrame(frame);\n label.setStringValue(text);\n label.setBezeled(false);\n label.setDrawsBackground(false);\n label.setEditable(false);\n label.setSelectable(false);\n if (bold) {\n label.setFont(NSFont.boldSystemFontOfSize(size));\n }\n else {\n label.setFont(NSFont.systemFontOfSize(size));\n }\n return label;\n}\n\nexport function createTextbox({ frame, size, bold, text, placeholder }) {\n var label = NSTextField.alloc().initWithFrame(frame);\n label.setStringValue(text);\n label.setBezeled(true);\n label.setDrawsBackground(true);\n label.setEditable(true);\n label.setSelectable(true);\n label.placeholderString = placeholder\n if (bold) {\n label.setFont(NSFont.boldSystemFontOfSize(size));\n }\n else {\n label.setFont(NSFont.systemFontOfSize(size));\n }\n return label;\n}\n\nexport function createSelect(frame, items) {\n var select = NSPopUpButton.alloc().initWithFrame(frame);\n for (var i = 0; i < items.length; i++) {\n if (items[i] == \"--\") {\n select.menu().addItem(NSMenuItem.separatorItem())\n } else {\n select.addItemWithTitle(items[i])\n }\n }\n return select;\n}\n\nexport function createCheckbox(frame, name, value, onstate, enabled) {\n var checkbox = NSButton.alloc().initWithFrame(frame);\n checkbox.setButtonType(NSSwitchButton);\n // checkbox.setBezelStyle(1);\n checkbox.setTitle(name);\n checkbox.setTag(value);\n checkbox.setState(onstate ? NSOnState : NSOffState);\n checkbox.setEnabled(enabled);\n return checkbox;\n}","// documentation: https://developer.sketchapp.com/reference/api/\nimport sketch from 'sketch'\nimport settings from 'sketch/settings'\n\nimport { viewContents, DIALOG_ELEMENTS, createDialog } from './dialog'\n\nimport {\n toSnake,\n toKebab,\n toCamel\n} from './utils'\n\nconst PREFS_KEY = \"previous_settings\"\n\n// -------------------------------------------------\n// ------------------- The Plugin ------------------\n// -------------------------------------------------\n\nexport default function(context) {\n\n const doc = sketch.getSelectedDocument()\n if (!doc) return\n const selection = doc.selectedLayers\n\n if (selection.length === 0) {\n sketch.UI.message('No layers are selected.')\n return\n } else {\n\n // Load user settings\n var previousSettings = settings.settingForKey(PREFS_KEY)\n const dialog = createDialog(previousSettings)\n\n // Run the dialog\n if (dialog.runModal() !== NSAlertFirstButtonReturn) {\n return\n } else {\n const caseElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'selectCase')\n const prefixElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'prefix')\n const fullNameElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'fullName')\n const formatElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'selectFormat')\n const scaleElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'selectScale')\n \n // Save the responses from that modal\n const caseIndex = viewContents[caseElemIdx].indexOfSelectedItem();\n const prefix = viewContents[prefixElemIdx].stringValue()\n const isFullName = viewContents[fullNameElemIdx].state();\n const formatvalueIndex = viewContents[formatElemIdx].indexOfSelectedItem();\n const scaleValueIndex = viewContents[scaleElemIdx].indexOfSelectedItem();\n \n // sketch.UI.message(`${caseIndex}, ${prefix}, ${isFullName}, ${formatvalueIndex}`)\n\n // Save user settings\n let prefs = {\n \"selectCase\": DIALOG_ELEMENTS[caseElemIdx].value[caseIndex],\n \"useFullName\": new Boolean(isFullName),\n \"prefix\": `${prefix}`,\n \"selectFormat\": DIALOG_ELEMENTS[formatElemIdx].value[formatvalueIndex],\n \"selectScale\": DIALOG_ELEMENTS[scaleElemIdx].value[scaleValueIndex]\n }\n settings.setSettingForKey(PREFS_KEY, prefs)\n\n // Create an Open dialog\n const open = NSOpenPanel.openPanel();\n open.canChooseFiles = false\n open.canChooseDirectories = true\n open.canCreateDirectories = true\n \n // run the open dialog\n if (open.runModal()) {\n const path = open.URL().path();\n const layers = selection.layers\n \n // Preserve the original layer names so we can change them back\n const originalLayerNames = layers.map(layer => layer.name)\n \n // Get the selected file format\n const fileFormat = DIALOG_ELEMENTS[formatElemIdx].value[formatvalueIndex]\n\n const scale = DIALOG_ELEMENTS[scaleElemIdx].value[scaleValueIndex].replace(/[@x]/, '')\n \n // Change the file names appropriately\n layers.forEach(layer => {\n let newName = isFullName ? layer.name : layer.name.substring(layer.name.lastIndexOf('/') + 1, layer.name.length) \n\n newName = (prefix == '') // NOT === because prefix is an object (...?)\n ? newName\n : `${prefix}-${newName}`\n \n if (caseIndex === 0) {\n layer.name = toKebab(newName)\n }\n else if (caseIndex === 1) {\n layer.name = toSnake(newName)\n }\n else if (caseIndex === 2) {\n layer.name = toCamel(newName)\n }\n })\n\n // Set the format and save path\n const exportOptions = {\n formats: fileFormat,\n scales: scale,\n output: path,\n }\n \n // Export the layers\n sketch.export(layers, exportOptions)\n \n // Reset the layer names\n layers.forEach((layer, i) => {\n layer.name = originalLayerNames[i]\n })\n // Show confirmation\n sketch.UI.message(`Exported ${layers.length} layers.`)\n }\n }\n }\n \n}","// -------------------------------------------------\n// ---------------- Text formatting ----------------\n// -------------------------------------------------\n\nconst allDelimiters = /[\\ \\\\\\/\\_\\-\\–\\—]/g\n\nexport function delimitString(str, delimiter) {\n return str.replace(allDelimiters, delimiter)\n .split(delimiter)\n .filter(chunk => chunk.length !== 0)\n .join(delimiter)\n .toLowerCase()\n}\n\nexport function toSnake(str) {\n const delimiter = '_'\n if (typeof str !== 'string') return \"\"\n return delimitString(str, delimiter)\n}\n\nexport function toKebab(str) {\n const delimiter = '-'\n if (typeof str !== 'string') return \"\"\n return delimitString(str, delimiter)\n}\n\nexport function toCamel(str) {\n if (typeof str !== 'string') return \"\"\n str = delimitString(str, ' ').split(allDelimiters)\n if(str.length > 1) {\n for (var i = 1; i < str.length; i++) {\n str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1);\n }\n }\n return str.join('');\n}\n\nexport function toPascal(str) {\n const camel = toCamel(str)\n return camel.charAt(0).toUpperCase() + camel.slice(1)\n}","module.exports = require(\"sketch\");","module.exports = require(\"sketch/settings\");"],"sourceRoot":""}
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Sketch/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "compatibleVersion": 3,
3 | "bundleVersion": 1,
4 | "icon": "flat-export-logo.png",
5 | "name": "Flat Export",
6 | "identifier": "com.adamthompson.sketch.batch-export-rename",
7 | "commands": [
8 | {
9 | "name": "Flat export",
10 | "identifier": "batch-rename-export",
11 | "script": "__rename-export.js",
12 | "shortcut": "ctrl shift e"
13 | }
14 | ],
15 | "menu": {
16 | "title": "Flat Export",
17 | "items": [
18 | "batch-rename-export"
19 | ]
20 | },
21 | "version": "2.3.4",
22 | "description": "Use this plugin to batch export layers to a single folder.",
23 | "disableCocoaScriptPreprocessor": true,
24 | "appcast": "https://raw.githubusercontent.com/TheSonOfThomp/sketch-flat-export/master/.appcast.xml",
25 | "author": "Adam Thompson",
26 | "authorEmail": "adam.thompson.dev@icloud.com"
27 | }
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Sketch/rename-export.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, { enumerable: true, get: getter });
45 | /******/ }
46 | /******/ };
47 | /******/
48 | /******/ // define __esModule on exports
49 | /******/ __webpack_require__.r = function(exports) {
50 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
51 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
52 | /******/ }
53 | /******/ Object.defineProperty(exports, '__esModule', { value: true });
54 | /******/ };
55 | /******/
56 | /******/ // create a fake namespace object
57 | /******/ // mode & 1: value is a module id, require it
58 | /******/ // mode & 2: merge all properties of value into the ns
59 | /******/ // mode & 4: return value when already ns object
60 | /******/ // mode & 8|1: behave like require
61 | /******/ __webpack_require__.t = function(value, mode) {
62 | /******/ if(mode & 1) value = __webpack_require__(value);
63 | /******/ if(mode & 8) return value;
64 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
65 | /******/ var ns = Object.create(null);
66 | /******/ __webpack_require__.r(ns);
67 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
68 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
69 | /******/ return ns;
70 | /******/ };
71 | /******/
72 | /******/ // getDefaultExport function for compatibility with non-harmony modules
73 | /******/ __webpack_require__.n = function(module) {
74 | /******/ var getter = module && module.__esModule ?
75 | /******/ function getDefault() { return module['default']; } :
76 | /******/ function getModuleExports() { return module; };
77 | /******/ __webpack_require__.d(getter, 'a', getter);
78 | /******/ return getter;
79 | /******/ };
80 | /******/
81 | /******/ // Object.prototype.hasOwnProperty.call
82 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
83 | /******/
84 | /******/ // __webpack_public_path__
85 | /******/ __webpack_require__.p = "";
86 | /******/
87 | /******/
88 | /******/ // Load entry module and return exports
89 | /******/ return __webpack_require__(__webpack_require__.s = "./src/rename-export.js");
90 | /******/ })
91 | /************************************************************************/
92 | /******/ ({
93 |
94 | /***/ "./src/rename-export.js":
95 | /*!******************************!*\
96 | !*** ./src/rename-export.js ***!
97 | \******************************/
98 | /*! exports provided: default */
99 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
100 |
101 | "use strict";
102 | __webpack_require__.r(__webpack_exports__);
103 | /* harmony import */ var sketch__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! sketch */ "sketch");
104 | /* harmony import */ var sketch__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(sketch__WEBPACK_IMPORTED_MODULE_0__);
105 | // documentation: https://developer.sketchapp.com/reference/api/
106 | // -------------------------------------------------
107 | // --------------- Dialog formatting ---------------
108 | // -------------------------------------------------
109 |
110 | function rect(x, y, w, h) {
111 | var rect = NSMakeRect(x, y, w, h);
112 | return rect;
113 | }
114 |
115 | function createLabel(frame, size, bold, text) {
116 | var label = NSTextField.alloc().initWithFrame(frame);
117 | label.setStringValue(text);
118 | label.setBezeled(false);
119 | label.setDrawsBackground(false);
120 | label.setEditable(false);
121 | label.setSelectable(false);
122 |
123 | if (bold) {
124 | label.setFont(NSFont.boldSystemFontOfSize(size));
125 | } else {
126 | label.setFont(NSFont.systemFontOfSize(size));
127 | }
128 |
129 | return label;
130 | }
131 |
132 | function createTextbox(_ref) {
133 | var frame = _ref.frame,
134 | size = _ref.size,
135 | bold = _ref.bold,
136 | text = _ref.text,
137 | placeholder = _ref.placeholder;
138 | var label = NSTextField.alloc().initWithFrame(frame);
139 | label.setStringValue(text);
140 | label.setBezeled(true);
141 | label.setDrawsBackground(true);
142 | label.setEditable(true);
143 | label.setSelectable(true);
144 | label.placeholderString = placeholder;
145 |
146 | if (bold) {
147 | label.setFont(NSFont.boldSystemFontOfSize(size));
148 | } else {
149 | label.setFont(NSFont.systemFontOfSize(size));
150 | }
151 |
152 | return label;
153 | }
154 |
155 | function createSelect(frame, items) {
156 | var select = NSPopUpButton.alloc().initWithFrame(frame);
157 |
158 | for (var i = 0; i < items.length; i++) {
159 | if (items[i] == "--") {
160 | select.menu().addItem(NSMenuItem.separatorItem());
161 | } else {
162 | select.addItemWithTitle(items[i]);
163 | }
164 | }
165 |
166 | return select;
167 | }
168 |
169 | function createCheckbox(frame, name, value, onstate, enabled) {
170 | var checkbox = NSButton.alloc().initWithFrame(frame);
171 | checkbox.setButtonType(NSSwitchButton); // checkbox.setBezelStyle(1);
172 |
173 | checkbox.setTitle(name);
174 | checkbox.setTag(value);
175 | checkbox.setState(onstate ? NSOnState : NSOffState);
176 | checkbox.setEnabled(enabled);
177 | return checkbox;
178 | } // -------------------------------------------------
179 | // ---------------- Text formatting ----------------
180 | // -------------------------------------------------
181 |
182 |
183 | function toSnake(str) {
184 | if (typeof str !== 'string') return "";
185 | return str.replace(/[\ \\\/\-\–\—]/g, '_').toLowerCase();
186 | }
187 |
188 | function toKebab(str) {
189 | if (typeof str !== 'string') return "";
190 | return str.replace(/[\ \\\/\_]/g, '-').toLowerCase();
191 | }
192 |
193 | function toCamel(str) {
194 | if (typeof str !== 'string') return "";
195 | str = str.toLowerCase().split(/[\ \\\/\_\-\–\—]/g);
196 |
197 | for (var i = 0; i < str.length; i++) {
198 | str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1);
199 | }
200 |
201 | return str.join('');
202 | } // -------------------------------------------------
203 | // ------------------- The Plugin ------------------
204 | // -------------------------------------------------
205 |
206 |
207 | /* harmony default export */ __webpack_exports__["default"] = (function (context) {
208 | var doc = sketch__WEBPACK_IMPORTED_MODULE_0___default.a.getSelectedDocument();
209 | var selection = doc.selectedLayers;
210 |
211 | if (selection.length === 0) {
212 | sketch__WEBPACK_IMPORTED_MODULE_0___default.a.UI.message('No layers are selected.');
213 | } else {
214 | // Create a custom dialog
215 | var dialog = NSAlert.alloc().init();
216 | dialog.setMessageText("Flat Export");
217 | dialog.addButtonWithTitle("Continue");
218 | dialog.addButtonWithTitle("Cancel"); // Create custom view and fields
219 |
220 | var dialogContents = [{
221 | type: 'label',
222 | id: 'caseLabel',
223 | value: 'Name format:',
224 | paddingBottom: -2
225 | }, {
226 | type: 'select',
227 | id: 'selectCase',
228 | value: ['kebab-case', 'snake_case', 'camelCase'],
229 | paddingBottom: 8
230 | }, {
231 | type: 'checkbox',
232 | id: 'fullName',
233 | label: 'Use full layer name',
234 | value: 'full-name',
235 | default: true,
236 | paddingBottom: 8
237 | }, {
238 | type: 'label',
239 | id: 'prefixLabel',
240 | value: 'Name prefix:',
241 | paddingBottom: -2
242 | }, {
243 | type: 'text',
244 | id: 'prefix',
245 | value: '',
246 | placeholder: 'Prefix',
247 | paddingBottom: 8
248 | }, {
249 | type: 'label',
250 | id: 'fileFormat',
251 | value: 'File format:',
252 | paddingBottom: -2
253 | }, {
254 | type: 'select',
255 | id: 'selectFormat',
256 | value: ['svg', 'png', 'jpg'],
257 | paddingBottom: 8
258 | }];
259 | var numElements = dialogContents.length;
260 | var elemHeight = 24;
261 | var totalPadding = dialogContents.map(function (elem) {
262 | return elem.paddingBottom;
263 | }).reduce(function (acc, pad) {
264 | return acc + pad;
265 | }, 0);
266 | var totalHeight = numElements * elemHeight + totalPadding;
267 | var customView = NSView.alloc().initWithFrame(NSMakeRect(0, 0, 200, totalHeight));
268 | var posOfNextElement = 24;
269 | var viewContents = dialogContents.map(function (element, i) {
270 | var type = element.type; // let padding = (i === 0 || i === 1 || i === 4) ? 0 : element.paddingBottom
271 |
272 | var yPos = totalHeight - posOfNextElement; // ((i+1) * (elemHeight))
273 |
274 | var UIElement;
275 |
276 | if (type == 'label') {
277 | UIElement = createLabel(NSMakeRect(0, yPos, 200, elemHeight), 12, false, element.value);
278 | } else if (type == 'select') {
279 | UIElement = createSelect(NSMakeRect(0, yPos, 200, elemHeight), element.value);
280 | } else if (type == 'checkbox') {
281 | UIElement = createCheckbox(NSMakeRect(0, yPos, 200, elemHeight), element.label, element.value, element.default, true);
282 | } else if (type == 'text') {
283 | UIElement = createTextbox({
284 | frame: NSMakeRect(0, yPos, 200, elemHeight),
285 | size: 12,
286 | text: element.value,
287 | placeholder: element.placeholder
288 | });
289 | }
290 |
291 | posOfNextElement += elemHeight + element.paddingBottom;
292 | return UIElement;
293 | });
294 | viewContents.forEach(function (subview) {
295 | customView.addSubview(subview);
296 | });
297 | dialog.setAccessoryView(customView); // Run the dialog
298 |
299 | if (dialog.runModal() !== NSAlertFirstButtonReturn) {
300 | return;
301 | } else {
302 | var caseElemIdx = dialogContents.findIndex(function (elem) {
303 | return elem.id === 'selectCase';
304 | });
305 | var prefixElemIdx = dialogContents.findIndex(function (elem) {
306 | return elem.id === 'prefix';
307 | });
308 | var fullNameElemIdx = dialogContents.findIndex(function (elem) {
309 | return elem.id === 'fullName';
310 | });
311 | var formatElemIdx = dialogContents.findIndex(function (elem) {
312 | return elem.id === 'selectFormat';
313 | }); // Save the responses from that modal
314 |
315 | var caseIndex = viewContents[caseElemIdx].indexOfSelectedItem();
316 | var prefix = viewContents[prefixElemIdx].stringValue();
317 | var isFullName = viewContents[fullNameElemIdx].state();
318 | var formatIndex = viewContents[formatElemIdx].indexOfSelectedItem(); // sketch.UI.message(`${caseIndex}, ${prefix}, ${isFullName}, ${formatIndex}`)
319 | // Create an Open dialog
320 |
321 | var open = NSOpenPanel.openPanel();
322 | open.canChooseFiles = false;
323 | open.canChooseDirectories = true;
324 | open.canCreateDirectories = true; // run the open dialog
325 |
326 | if (open.runModal()) {
327 | var path = open.URL().path();
328 | var layers = selection.layers; // Preserve the original layer names so we can change them back
329 |
330 | var originalLayerNames = layers.map(function (layer) {
331 | return layer.name;
332 | }); // Get the selected file format
333 |
334 | var fileFormat = dialogContents[formatElemIdx].value[formatIndex]; // Change the file names appropriately
335 |
336 | layers.forEach(function (layer) {
337 | var newName = isFullName ? layer.name : layer.name.substring(layer.name.lastIndexOf('/') + 1, layer.name.length);
338 | newName = prefix == '' // NOT === because prefix is an object (...?)
339 | ? newName : "".concat(prefix, "-").concat(newName);
340 |
341 | if (caseIndex === 0) {
342 | layer.name = toKebab(newName);
343 | } else if (caseIndex === 1) {
344 | layer.name = toSnake(newName);
345 | } else if (caseIndex === 2) {
346 | layer.name = toCamel(newName);
347 | }
348 | }); // Set the format and save path
349 |
350 | var exportOptions = {
351 | formats: fileFormat,
352 | output: path // Export the layers
353 |
354 | };
355 | sketch__WEBPACK_IMPORTED_MODULE_0___default.a.export(layers, exportOptions); // Reset the layer names
356 |
357 | layers.forEach(function (layer, i) {
358 | layer.name = originalLayerNames[i];
359 | }); // Show confirmation
360 |
361 | sketch__WEBPACK_IMPORTED_MODULE_0___default.a.UI.message("Exported ".concat(layers.length, " layers"));
362 | }
363 | }
364 | }
365 | });
366 |
367 | /***/ }),
368 |
369 | /***/ "sketch":
370 | /*!*************************!*\
371 | !*** external "sketch" ***!
372 | \*************************/
373 | /*! no static exports found */
374 | /***/ (function(module, exports) {
375 |
376 | module.exports = require("sketch");
377 |
378 | /***/ })
379 |
380 | /******/ });
381 | if (key === 'default' && typeof exports === 'function') {
382 | exports(context);
383 | } else {
384 | exports[key](context);
385 | }
386 | }
387 | that['onRun'] = __skpm_run.bind(this, 'default')
388 |
389 | //# sourceMappingURL=rename-export.js.map
--------------------------------------------------------------------------------
/flat-export.sketchplugin/Contents/Sketch/rename-export.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./src/rename-export.js","webpack://exports/external \"sketch\""],"names":["rect","x","y","w","h","NSMakeRect","createLabel","frame","size","bold","text","label","NSTextField","alloc","initWithFrame","setStringValue","setBezeled","setDrawsBackground","setEditable","setSelectable","setFont","NSFont","boldSystemFontOfSize","systemFontOfSize","createTextbox","placeholder","placeholderString","createSelect","items","select","NSPopUpButton","i","length","menu","addItem","NSMenuItem","separatorItem","addItemWithTitle","createCheckbox","name","value","onstate","enabled","checkbox","NSButton","setButtonType","NSSwitchButton","setTitle","setTag","setState","NSOnState","NSOffState","setEnabled","toSnake","str","replace","toLowerCase","toKebab","toCamel","split","charAt","toUpperCase","slice","join","context","doc","sketch","getSelectedDocument","selection","selectedLayers","UI","message","dialog","NSAlert","init","setMessageText","addButtonWithTitle","dialogContents","type","id","paddingBottom","default","numElements","elemHeight","totalPadding","map","elem","reduce","acc","pad","totalHeight","customView","NSView","posOfNextElement","viewContents","element","yPos","UIElement","forEach","subview","addSubview","setAccessoryView","runModal","NSAlertFirstButtonReturn","caseElemIdx","findIndex","prefixElemIdx","fullNameElemIdx","formatElemIdx","caseIndex","indexOfSelectedItem","prefix","stringValue","isFullName","state","formatIndex","open","NSOpenPanel","openPanel","canChooseFiles","canChooseDirectories","canCreateDirectories","path","URL","layers","originalLayerNames","layer","fileFormat","newName","substring","lastIndexOf","exportOptions","formats","output","export"],"mappings":"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","file":"rename-export.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/rename-export.js\");\n","import sketch from 'sketch'\n// documentation: https://developer.sketchapp.com/reference/api/\n\n// -------------------------------------------------\n// --------------- Dialog formatting ---------------\n// -------------------------------------------------\nfunction rect(x, y, w, h) {\n var rect = NSMakeRect(x, y, w, h)\n return rect;\n}\n\nfunction createLabel(frame, size, bold, text) {\n var label = NSTextField.alloc().initWithFrame(frame);\n label.setStringValue(text);\n label.setBezeled(false);\n label.setDrawsBackground(false);\n label.setEditable(false);\n label.setSelectable(false);\n if (bold) {\n label.setFont(NSFont.boldSystemFontOfSize(size));\n }\n else {\n label.setFont(NSFont.systemFontOfSize(size));\n }\n return label;\n}\n\nfunction createTextbox({frame, size, bold, text, placeholder}) {\n var label = NSTextField.alloc().initWithFrame(frame);\n label.setStringValue(text);\n label.setBezeled(true);\n label.setDrawsBackground(true);\n label.setEditable(true);\n label.setSelectable(true);\n label.placeholderString = placeholder\n if (bold) {\n label.setFont(NSFont.boldSystemFontOfSize(size));\n }\n else {\n label.setFont(NSFont.systemFontOfSize(size));\n }\n return label;\n}\n\nfunction createSelect(frame, items) {\n var select = NSPopUpButton.alloc().initWithFrame(frame);\n for (var i = 0; i < items.length; i++) {\n if (items[i] == \"--\") {\n select.menu().addItem(NSMenuItem.separatorItem())\n } else {\n select.addItemWithTitle(items[i])\n }\n }\n return select;\n}\n\nfunction createCheckbox(frame, name, value, onstate, enabled) {\n var checkbox = NSButton.alloc().initWithFrame(frame);\n checkbox.setButtonType(NSSwitchButton);\n // checkbox.setBezelStyle(1);\n checkbox.setTitle(name);\n checkbox.setTag(value);\n checkbox.setState(onstate ? NSOnState : NSOffState);\n checkbox.setEnabled(enabled);\n return checkbox;\n}\n\n// -------------------------------------------------\n// ---------------- Text formatting ----------------\n// -------------------------------------------------\n\nfunction toSnake(str) {\n if (typeof str !== 'string') return \"\"\n return str.replace(/[\\ \\\\\\/\\-\\–\\—]/g, '_').toLowerCase()\n}\n\nfunction toKebab(str) {\n if (typeof str !== 'string') return \"\"\n return str.replace(/[\\ \\\\\\/\\_]/g, '-').toLowerCase()\n}\n\nfunction toCamel(str){\n if (typeof str !== 'string') return \"\"\n str = str.toLowerCase().split(/[\\ \\\\\\/\\_\\-\\–\\—]/g)\n for (var i = 0; i < str.length; i++) {\n str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1);\n }\n return str.join('');\n}\n\n// -------------------------------------------------\n// ------------------- The Plugin ------------------\n// -------------------------------------------------\n\nexport default function(context) {\n\n const doc = sketch.getSelectedDocument()\n const selection = doc.selectedLayers\n\n if (selection.length === 0) {\n sketch.UI.message('No layers are selected.')\n } else {\n // Create a custom dialog\n var dialog = NSAlert.alloc().init();\n\n dialog.setMessageText(\"Flat Export\");\n dialog.addButtonWithTitle(\"Continue\");\n dialog.addButtonWithTitle(\"Cancel\");\n\n // Create custom view and fields\n \n var dialogContents = [\n {\n type: 'label',\n id: 'caseLabel',\n value: 'Name format:',\n paddingBottom: -2\n },\n {\n type: 'select',\n id: 'selectCase',\n value: ['kebab-case', 'snake_case', 'camelCase'],\n paddingBottom: 8\n },\n {\n type: 'checkbox',\n id: 'fullName',\n label: 'Use full layer name',\n value: 'full-name',\n default: true,\n paddingBottom: 8\n },\n {\n type: 'label',\n id: 'prefixLabel',\n value: 'Name prefix:',\n paddingBottom: -2\n },\n {\n type: 'text',\n id: 'prefix',\n value: '',\n placeholder: 'Prefix',\n paddingBottom: 8\n },\n {\n type: 'label',\n id: 'fileFormat',\n value: 'File format:',\n paddingBottom: -2\n },\n {\n type: 'select',\n id: 'selectFormat',\n value: ['svg', 'png', 'jpg'],\n paddingBottom: 8\n },\n ]\n \n const numElements = dialogContents.length\n const elemHeight = 24;\n const totalPadding = dialogContents.map(elem => elem.paddingBottom).reduce((acc, pad) => acc + pad, 0)\n const totalHeight = numElements * (elemHeight) + totalPadding\n var customView = NSView.alloc().initWithFrame(NSMakeRect(0, 0, 200, totalHeight));\n var posOfNextElement = 24\n\n const viewContents = dialogContents.map((element, i) => {\n let type = element.type\n\n // let padding = (i === 0 || i === 1 || i === 4) ? 0 : element.paddingBottom\n let yPos = totalHeight - posOfNextElement // ((i+1) * (elemHeight))\n\n let UIElement; \n\n if (type == 'label') {\n UIElement = createLabel(NSMakeRect(0, yPos, 200, elemHeight), 12, false, element.value);\n } else if (type == 'select') {\n UIElement = createSelect(NSMakeRect(0, yPos, 200, elemHeight), element.value)\n } else if (type == 'checkbox') {\n UIElement = createCheckbox(NSMakeRect(0, yPos, 200, elemHeight), element.label, element.value, element.default, true);\n } else if (type == 'text') {\n UIElement = createTextbox({\n frame: NSMakeRect(0, yPos, 200, elemHeight), \n size: 12, \n text: element.value,\n placeholder: element.placeholder\n })\n }\n\n posOfNextElement += elemHeight + element.paddingBottom\n\n return UIElement\n })\n\n viewContents.forEach(subview => {\n customView.addSubview(subview)\n })\n dialog.setAccessoryView(customView)\n\n // Run the dialog\n if (dialog.runModal() !== NSAlertFirstButtonReturn) {\n return\n } else {\n \n const caseElemIdx = dialogContents.findIndex(elem => elem.id === 'selectCase')\n const prefixElemIdx = dialogContents.findIndex(elem => elem.id === 'prefix')\n const fullNameElemIdx = dialogContents.findIndex(elem => elem.id === 'fullName')\n const formatElemIdx = dialogContents.findIndex(elem => elem.id === 'selectFormat')\n\n // Save the responses from that modal\n const caseIndex = viewContents[caseElemIdx].indexOfSelectedItem();\n const prefix = viewContents[prefixElemIdx].stringValue()\n const isFullName = viewContents[fullNameElemIdx].state();\n const formatIndex = viewContents[formatElemIdx].indexOfSelectedItem();\n\n // sketch.UI.message(`${caseIndex}, ${prefix}, ${isFullName}, ${formatIndex}`)\n \n // Create an Open dialog\n var open = NSOpenPanel.openPanel();\n open.canChooseFiles = false\n open.canChooseDirectories = true\n open.canCreateDirectories = true\n \n // run the open dialog\n if (open.runModal()) {\n var path = open.URL().path();\n const layers = selection.layers\n\n // Preserve the original layer names so we can change them back\n const originalLayerNames = layers.map(layer => layer.name)\n\n // Get the selected file format\n const fileFormat = dialogContents[formatElemIdx].value[formatIndex]\n\n // Change the file names appropriately\n layers.forEach(layer => {\n let newName = isFullName ? layer.name : layer.name.substring(layer.name.lastIndexOf('/') + 1, layer.name.length) \n newName = prefix == '' // NOT === because prefix is an object (...?)\n ? newName\n : `${prefix}-${newName}`\n \n if (caseIndex === 0) {\n layer.name = toKebab(newName)\n }\n else if (caseIndex === 1) {\n layer.name = toSnake(newName)\n }\n else if (caseIndex === 2) {\n layer.name = toCamel(newName)\n }\n })\n\n // Set the format and save path\n const exportOptions = {\n formats: fileFormat,\n output: path\n }\n \n // Export the layers\n sketch.export(layers, exportOptions)\n \n \n // Reset the layer names\n layers.forEach((layer, i) => {\n layer.name = originalLayerNames[i]\n })\n // Show confirmation\n sketch.UI.message(`Exported ${layers.length} layers`)\n }\n }\n }\n}","module.exports = require(\"sketch\");"],"sourceRoot":""}
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | transform: {
3 | '.js': 'jest-esm-transformer'
4 | }
5 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sketch-flat-export",
3 | "version": "2.3.5",
4 | "engines": {
5 | "sketch": ">=3.0"
6 | },
7 | "skpm": {
8 | "name": "sketch-flat-export",
9 | "manifest": "src/manifest.json",
10 | "main": "flat-export.sketchplugin",
11 | "assets": [
12 | "assets/**/*"
13 | ]
14 | },
15 | "scripts": {
16 | "build": "skpm-build",
17 | "watch": "skpm-build --watch",
18 | "start": "skpm-build --watch --run",
19 | "test": "jest",
20 | "publish": "skpm publish patch",
21 | "postinstall": "npm run build && skpm-link",
22 | "debugger": "skpm log -f"
23 | },
24 | "devDependencies": {
25 | "@skpm/builder": "^0.7.7",
26 | "jest": "^26.1.0",
27 | "jest-esm-transformer": "^1.0.0",
28 | "skpm": "^1.3.1"
29 | },
30 | "author": "Adam Thompson ",
31 | "repository": "https://github.com/TheSonOfThomp/sketch-flat-export",
32 | "description": "Use this plugin to batch export layers to a single folder."
33 | }
34 |
--------------------------------------------------------------------------------
/src/dialog.js:
--------------------------------------------------------------------------------
1 | import {
2 | createLabel,
3 | createTextbox,
4 | createSelect,
5 | createCheckbox
6 | } from './macos-ui'
7 |
8 | const DIALOG_TITLE = "Flat Export"
9 |
10 | const ELEMENT_HEIGHT = 24;
11 | const DIALOG_ELEMENTS = [
12 | {
13 | type: 'label',
14 | id: 'caseLabel',
15 | value: 'Name format:',
16 | paddingBottom: -2
17 | },
18 | {
19 | type: 'select',
20 | id: 'selectCase',
21 | value: ['kebab-case', 'snake_case', 'camelCase'],
22 | paddingBottom: 8
23 | },
24 | {
25 | type: 'checkbox',
26 | id: 'fullName',
27 | label: 'Use full layer name',
28 | value: 'full-name',
29 | default: true,
30 | paddingBottom: 8
31 | },
32 | {
33 | type: 'label',
34 | id: 'prefixLabel',
35 | value: 'Name prefix:',
36 | paddingBottom: -2
37 | },
38 | {
39 | type: 'text',
40 | id: 'prefix',
41 | value: '',
42 | placeholder: 'Prefix',
43 | paddingBottom: 8
44 | },
45 | {
46 | type: 'label',
47 | id: 'fileFormat',
48 | value: 'File format:',
49 | paddingBottom: -2
50 | },
51 | {
52 | type: 'select',
53 | id: 'selectFormat',
54 | value: ['svg', 'png', 'jpg'],
55 | paddingBottom: 8
56 | },
57 | {
58 | type: 'label',
59 | id: 'outputScale',
60 | value: 'Scale',
61 | paddingBottom: -2
62 | },
63 | {
64 | type: 'select',
65 | id: 'selectScale',
66 | value: ['@1x', '@2x', '@3x'],
67 | paddingBottom: 8
68 | },
69 | ]
70 |
71 | let viewContents = null
72 |
73 | // Create a custom dialog
74 | function createDialog(previousSetting) {
75 | const dialog = NSAlert.alloc().init();
76 |
77 | dialog.setMessageText(DIALOG_TITLE);
78 | dialog.addButtonWithTitle("Continue");
79 | dialog.addButtonWithTitle("Cancel");
80 |
81 | const COUNT_ELEMENTS = DIALOG_ELEMENTS.length
82 | const PADDING = DIALOG_ELEMENTS.map(elem => elem.paddingBottom).reduce((acc, pad) => acc + pad, 0)
83 | const TOTAL_MODAL_HEIGHT = COUNT_ELEMENTS * (ELEMENT_HEIGHT) + PADDING
84 | const customView = NSView.alloc().initWithFrame(NSMakeRect(0, 0, 200, TOTAL_MODAL_HEIGHT));
85 | let position_next_elem = ELEMENT_HEIGHT
86 |
87 | viewContents = DIALOG_ELEMENTS.map((element, i) => {
88 | let type = element.type
89 |
90 | // let padding = (i === 0 || i === 1 || i === 4) ? 0 : element.paddingBottom
91 | let yPos = TOTAL_MODAL_HEIGHT - position_next_elem // ((i+1) * (ELEMENT_HEIGHT))
92 |
93 | let UIElement;
94 |
95 | if (type == 'label') {
96 | UIElement = createLabel(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), 12, false, element.value);
97 | } else if (type == 'select') {
98 | UIElement = createSelect(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), element.value)
99 | } else if (type == 'checkbox') {
100 | UIElement = createCheckbox(NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT), element.label, element.value, element.default, true);
101 | } else if (type == 'text') {
102 | UIElement = createTextbox({
103 | frame: NSMakeRect(0, yPos, 200, ELEMENT_HEIGHT),
104 | size: 12,
105 | text: element.value,
106 | placeholder: element.placeholder
107 | })
108 | }
109 |
110 | if (typeof previousSetting !== 'undefined') {
111 | if (element.id == 'selectCase') {
112 | UIElement.selectItemWithTitle(previousSetting.selectCase)
113 | } else if (element.id == 'fullName') {
114 | UIElement.setEnabled(previousSetting.useFullName)
115 | } else if (element.id == 'prefix') {
116 | UIElement.setStringValue(previousSetting.prefix)
117 | } else if (element.id == 'selectFormat') {
118 | UIElement.selectItemWithTitle(previousSetting.selectFormat)
119 | } else if (element.id == 'selectScale') {
120 | UIElement.selectItemWithTitle(previousSetting.selectScale)
121 | }
122 | }
123 |
124 | position_next_elem += ELEMENT_HEIGHT + element.paddingBottom
125 |
126 | return UIElement
127 | })
128 |
129 | viewContents.forEach(subview => {
130 | customView.addSubview(subview)
131 | })
132 | dialog.setAccessoryView(customView)
133 |
134 | return dialog
135 | }
136 |
137 | export { createDialog, viewContents, DIALOG_ELEMENTS }
--------------------------------------------------------------------------------
/src/macos-ui.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------
2 | // --------------- Dialog formatting ---------------
3 | // -------------------------------------------------
4 | export function rect(x, y, w, h) {
5 | var rect = NSMakeRect(x, y, w, h)
6 | return rect;
7 | }
8 |
9 | export function createLabel(frame, size, bold, text) {
10 | var label = NSTextField.alloc().initWithFrame(frame);
11 | label.setStringValue(text);
12 | label.setBezeled(false);
13 | label.setDrawsBackground(false);
14 | label.setEditable(false);
15 | label.setSelectable(false);
16 | if (bold) {
17 | label.setFont(NSFont.boldSystemFontOfSize(size));
18 | }
19 | else {
20 | label.setFont(NSFont.systemFontOfSize(size));
21 | }
22 | return label;
23 | }
24 |
25 | export function createTextbox({ frame, size, bold, text, placeholder }) {
26 | var label = NSTextField.alloc().initWithFrame(frame);
27 | label.setStringValue(text);
28 | label.setBezeled(true);
29 | label.setDrawsBackground(true);
30 | label.setEditable(true);
31 | label.setSelectable(true);
32 | label.placeholderString = placeholder
33 | if (bold) {
34 | label.setFont(NSFont.boldSystemFontOfSize(size));
35 | }
36 | else {
37 | label.setFont(NSFont.systemFontOfSize(size));
38 | }
39 | return label;
40 | }
41 |
42 | export function createSelect(frame, items) {
43 | var select = NSPopUpButton.alloc().initWithFrame(frame);
44 | for (var i = 0; i < items.length; i++) {
45 | if (items[i] == "--") {
46 | select.menu().addItem(NSMenuItem.separatorItem())
47 | } else {
48 | select.addItemWithTitle(items[i])
49 | }
50 | }
51 | return select;
52 | }
53 |
54 | export function createCheckbox(frame, name, value, onstate, enabled) {
55 | var checkbox = NSButton.alloc().initWithFrame(frame);
56 | checkbox.setButtonType(NSSwitchButton);
57 | // checkbox.setBezelStyle(1);
58 | checkbox.setTitle(name);
59 | checkbox.setTag(value);
60 | checkbox.setState(onstate ? NSOnState : NSOffState);
61 | checkbox.setEnabled(enabled);
62 | return checkbox;
63 | }
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "compatibleVersion": 3,
3 | "bundleVersion": 1,
4 | "icon": "flat-export-logo.png",
5 | "name": "Flat Export",
6 | "identifier": "com.adamthompson.sketch.batch-export-rename",
7 | "commands": [
8 | {
9 | "name": "Flat export",
10 | "identifier": "batch-rename-export",
11 | "script": "./rename-export.js",
12 | "shortcut": "ctrl shift e"
13 | }
14 | ],
15 | "menu": {
16 | "title": "Flat Export",
17 | "items": [
18 | "batch-rename-export"
19 | ]
20 | }
21 | }
--------------------------------------------------------------------------------
/src/rename-export.js:
--------------------------------------------------------------------------------
1 | // documentation: https://developer.sketchapp.com/reference/api/
2 | import sketch from 'sketch'
3 | import settings from 'sketch/settings'
4 |
5 | import { viewContents, DIALOG_ELEMENTS, createDialog } from './dialog'
6 |
7 | import {
8 | toSnake,
9 | toKebab,
10 | toCamel
11 | } from './utils'
12 |
13 | const PREFS_KEY = "previous_settings"
14 |
15 | // -------------------------------------------------
16 | // ------------------- The Plugin ------------------
17 | // -------------------------------------------------
18 |
19 | export default function(context) {
20 |
21 | const doc = sketch.getSelectedDocument()
22 | if (!doc) return
23 | const selection = doc.selectedLayers
24 |
25 | if (selection.length === 0) {
26 | sketch.UI.message('No layers are selected.')
27 | return
28 | } else {
29 |
30 | // Load user settings
31 | var previousSettings = settings.settingForKey(PREFS_KEY)
32 | const dialog = createDialog(previousSettings)
33 |
34 | // Run the dialog
35 | if (dialog.runModal() !== NSAlertFirstButtonReturn) {
36 | return
37 | } else {
38 | const caseElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'selectCase')
39 | const prefixElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'prefix')
40 | const fullNameElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'fullName')
41 | const formatElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'selectFormat')
42 | const scaleElemIdx = DIALOG_ELEMENTS.findIndex(elem => elem.id === 'selectScale')
43 |
44 | // Save the responses from that modal
45 | const caseIndex = viewContents[caseElemIdx].indexOfSelectedItem();
46 | const prefix = viewContents[prefixElemIdx].stringValue()
47 | const isFullName = viewContents[fullNameElemIdx].state();
48 | const formatvalueIndex = viewContents[formatElemIdx].indexOfSelectedItem();
49 | const scaleValueIndex = viewContents[scaleElemIdx].indexOfSelectedItem();
50 |
51 | // sketch.UI.message(`${caseIndex}, ${prefix}, ${isFullName}, ${formatvalueIndex}`)
52 |
53 | // Save user settings
54 | let prefs = {
55 | "selectCase": DIALOG_ELEMENTS[caseElemIdx].value[caseIndex],
56 | "useFullName": new Boolean(isFullName),
57 | "prefix": `${prefix}`,
58 | "selectFormat": DIALOG_ELEMENTS[formatElemIdx].value[formatvalueIndex],
59 | "selectScale": DIALOG_ELEMENTS[scaleElemIdx].value[scaleValueIndex]
60 | }
61 | settings.setSettingForKey(PREFS_KEY, prefs)
62 |
63 | // Create an Open dialog
64 | const open = NSOpenPanel.openPanel();
65 | open.canChooseFiles = false
66 | open.canChooseDirectories = true
67 | open.canCreateDirectories = true
68 |
69 | // run the open dialog
70 | if (open.runModal()) {
71 | const path = open.URL().path();
72 | const layers = selection.layers
73 |
74 | // Preserve the original layer names so we can change them back
75 | const originalLayerNames = layers.map(layer => layer.name)
76 |
77 | // Get the selected file format
78 | const fileFormat = DIALOG_ELEMENTS[formatElemIdx].value[formatvalueIndex]
79 |
80 | const scale = DIALOG_ELEMENTS[scaleElemIdx].value[scaleValueIndex].replace(/[@x]/, '')
81 |
82 | // Change the file names appropriately
83 | layers.forEach(layer => {
84 | let newName = isFullName ? layer.name : layer.name.substring(layer.name.lastIndexOf('/') + 1, layer.name.length)
85 |
86 | newName = (prefix == '') // NOT === because prefix is an object (...?)
87 | ? newName
88 | : `${prefix}-${newName}`
89 |
90 | if (caseIndex === 0) {
91 | layer.name = toKebab(newName)
92 | }
93 | else if (caseIndex === 1) {
94 | layer.name = toSnake(newName)
95 | }
96 | else if (caseIndex === 2) {
97 | layer.name = toCamel(newName)
98 | }
99 | })
100 |
101 | // Set the format and save path
102 | const exportOptions = {
103 | formats: fileFormat,
104 | scales: scale,
105 | output: path,
106 | }
107 |
108 | // Export the layers
109 | sketch.export(layers, exportOptions)
110 |
111 | // Reset the layer names
112 | layers.forEach((layer, i) => {
113 | layer.name = originalLayerNames[i]
114 | })
115 | // Show confirmation
116 | sketch.UI.message(`Exported ${layers.length} layers.`)
117 | }
118 | }
119 | }
120 |
121 | }
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | // -------------------------------------------------
2 | // ---------------- Text formatting ----------------
3 | // -------------------------------------------------
4 |
5 | const allDelimiters = /[\ \\\/\_\-\–\—]/g
6 |
7 | export function delimitString(str, delimiter) {
8 | return str.replace(allDelimiters, delimiter)
9 | .split(delimiter)
10 | .filter(chunk => chunk.length !== 0)
11 | .join(delimiter)
12 | .toLowerCase()
13 | }
14 |
15 | export function toSnake(str) {
16 | const delimiter = '_'
17 | if (typeof str !== 'string') return ""
18 | return delimitString(str, delimiter)
19 | }
20 |
21 | export function toKebab(str) {
22 | const delimiter = '-'
23 | if (typeof str !== 'string') return ""
24 | return delimitString(str, delimiter)
25 | }
26 |
27 | export function toCamel(str) {
28 | if (typeof str !== 'string') return ""
29 | str = delimitString(str, ' ').split(allDelimiters)
30 | if(str.length > 1) {
31 | for (var i = 1; i < str.length; i++) {
32 | str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1);
33 | }
34 | }
35 | return str.join('');
36 | }
37 |
38 | export function toPascal(str) {
39 | const camel = toCamel(str)
40 | return camel.charAt(0).toUpperCase() + camel.slice(1)
41 | }
--------------------------------------------------------------------------------
/src/utils.test.js:
--------------------------------------------------------------------------------
1 | import {toSnake, toKebab, toCamel, toPascal} from './utils'
2 |
3 | test('toSnake', () => {
4 | expect(toSnake('Icon/Arrow/Left')).toBe('icon_arrow_left');
5 | expect(toSnake('Icon/ArrowLeft')).toBe('icon_arrowleft');
6 | expect(toSnake('Icon / Arrow / Left')).toBe('icon_arrow_left');
7 | expect(toSnake(' Icon/ Arrow /Left ')).toBe('icon_arrow_left');
8 | });
9 |
10 | test('toKebab', () => {
11 | expect(toKebab('Icon/Arrow/Left')).toBe('icon-arrow-left');
12 | expect(toKebab('Icon/ArrowLeft')).toBe('icon-arrowleft');
13 | expect(toKebab('Icon / Arrow / Left')).toBe('icon-arrow-left');
14 | expect(toKebab(' Icon/ Arrow /Left ')).toBe('icon-arrow-left');
15 | });
16 |
17 | test('toCamel', () => {
18 | expect(toCamel('Icon/Arrow/Left')).toBe('iconArrowLeft');
19 | expect(toCamel('Icon/ArrowLeft')).toBe('iconArrowleft');
20 | expect(toCamel('Icon / Arrow / Left')).toBe('iconArrowLeft');
21 | expect(toCamel(' Icon/ Arrow /Left ')).toBe('iconArrowLeft');
22 | });
23 |
24 | test('toPascal', () => {
25 | expect(toPascal('Icon/Arrow/Left')).toBe('IconArrowLeft');
26 | expect(toPascal('Icon/ArrowLeft')).toBe('IconArrowleft');
27 | expect(toPascal('Icon / Arrow / Left')).toBe('IconArrowLeft');
28 | expect(toPascal(' Icon/ Arrow /Left ')).toBe('IconArrowLeft');
29 | });
30 |
--------------------------------------------------------------------------------