├── .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 | ![Test](https://github.com/TheSonOfThomp/sketch-flat-export/workflows/Test/badge.svg) 4 | 5 | ![plugin-logo](assets/white-flat-export-logo.png) 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 | ![sketch-symbols](assets/img-sketch-symbols.png) 13 | 14 | #### ...export to these files 15 | ![sketch-symbols](assets/img-finder-files.png) 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 | ![dialog](assets/img-dialog.png) 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 | --------------------------------------------------------------------------------