├── .appcast.xml ├── .gitignore ├── LICENSE ├── README.md ├── assets └── icon.png ├── context-menu.png ├── html-fontbook-export.png ├── icon.png ├── json-export.png ├── package-lock.json ├── package.json ├── sass-export.png ├── src ├── css-export.js ├── custom-export.js ├── export │ ├── export-components.js │ └── open-export-dialog.js ├── html-fontbook-export.js ├── json-export.js ├── manifest.json ├── sass-mixins-export.js └── util │ ├── export.js │ ├── number.js │ ├── sketch.js │ ├── string.js │ ├── ui.js │ └── util.js └── typex.sketchplugin └── Contents ├── Resources └── icon.png └── Sketch ├── advanced-json-export.js ├── advanced-json-export.js.map ├── css-export.js ├── css-export.js.map ├── custom-export.js ├── custom-export.js.map ├── html-export.js ├── html-export.js.map ├── html-fontbook-export.js ├── html-fontbook-export.js.map ├── json-export.js ├── json-export.js.map ├── manifest.json ├── my-command.js.map ├── sass-mixins-export.js ├── sass-mixins-export.js.map ├── web-export.js ├── web-export.js.map ├── window-test.js └── window-test.js.map /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | plugin.sketchplugin 3 | 4 | # npm 5 | node_modules 6 | .npm 7 | npm-debug.log 8 | 9 | # mac 10 | .DS_Store 11 | 12 | # WebStorm 13 | .idea 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Rein Van Oyen 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

Export your text styles to CSS, SASS mixins, JSON, HTML, ...

6 |

Highly configurable (Including rem, em units, etc)

7 |

8 |

9 | 10 | 11 | 12 |

13 | 14 | # Installation 15 | 1. Download the plugin. 16 | 2. Unzip the downloaded file. 17 | 3. Double click on typex.sketchplugin 18 | 19 | # Typex? 20 | 21 | Typex is a Sketch plugin which provides you with all the tools you need to export your text styles to the web platform, ready for your developer to put them to use. Instead of just giving you the typical simplistic copy-pastable CSS snippet for your text styles, you can now actually configure how to export them to CSS, SASS mixins and JSON. 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinvanoyen/typex/297f2b00d5fb21793b0fa6060a1b031a326299b0/assets/icon.png -------------------------------------------------------------------------------- /context-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinvanoyen/typex/297f2b00d5fb21793b0fa6060a1b031a326299b0/context-menu.png -------------------------------------------------------------------------------- /html-fontbook-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinvanoyen/typex/297f2b00d5fb21793b0fa6060a1b031a326299b0/html-fontbook-export.png -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinvanoyen/typex/297f2b00d5fb21793b0fa6060a1b031a326299b0/icon.png -------------------------------------------------------------------------------- /json-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinvanoyen/typex/297f2b00d5fb21793b0fa6060a1b031a326299b0/json-export.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typex", 3 | "version": "1.1.0", 4 | "description": "Text styles to CSS, SASS mixins, HTML, JSON, ...", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/reinvanoyen/typex" 8 | }, 9 | "engines": { 10 | "sketch": ">=3.0" 11 | }, 12 | "skpm": { 13 | "name": "typex", 14 | "manifest": "src/manifest.json", 15 | "main": "typex.sketchplugin", 16 | "assets": [ 17 | "assets/**/*" 18 | ] 19 | }, 20 | "scripts": { 21 | "build": "skpm-build", 22 | "watch": "skpm-build --watch", 23 | "start": "skpm-build --watch --run", 24 | "postinstall": "npm run build && skpm-link" 25 | }, 26 | "devDependencies": { 27 | "@skpm/builder": "^0.5.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sass-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinvanoyen/typex/297f2b00d5fb21793b0fa6060a1b031a326299b0/sass-export.png -------------------------------------------------------------------------------- /src/css-export.js: -------------------------------------------------------------------------------- 1 | import ui from './util/ui'; 2 | import stringUtils from './util/string'; 3 | import exportUtils from './util/export'; 4 | 5 | import openExportDialog from './export/open-export-dialog'; 6 | 7 | export default function(context) { 8 | 9 | openExportDialog(context, { 10 | title: 'CSS classes export', 11 | informativeText: 'Export each text style as a class' 12 | }, (textStyles, data) => { 13 | 14 | let css = {}; 15 | 16 | textStyles.forEach(textStyle => { 17 | css[stringUtils.slugify(textStyle.name)] = exportUtils.createCssProps(textStyle, data); 18 | }); 19 | 20 | let output = ''; 21 | let i = 0; 22 | 23 | for (let identifier in css) { 24 | if (css.hasOwnProperty(identifier)) { 25 | 26 | let className = data.namingPrefix + '-' + (data.namingConvention === 'Numeric' ? i+1 : identifier); 27 | 28 | output += ( i !== 0 ? "\n" : '' ) + '.' + className + "\n"; 29 | output += '{'+"\n"; 30 | output += exportUtils.createStyleBlock(css[identifier]); 31 | output += '}'+"\n"; 32 | i++; 33 | } 34 | } 35 | 36 | ui.createSavePanel('typex-stylesheet.css', output); 37 | }); 38 | }; -------------------------------------------------------------------------------- /src/custom-export.js: -------------------------------------------------------------------------------- 1 | import ui from './util/ui'; 2 | 3 | import openExportDialog from './export/open-export-dialog'; 4 | 5 | export default function(context) { 6 | 7 | openExportDialog(context, { 8 | title: 'Custom script export', 9 | informativeText: 'Export each text style by using a custom export script in Javascript' 10 | }, (textStyles, data) => { 11 | 12 | ui.createSettingsDialog(context, { 13 | title: 'Custom export script', 14 | informativeText: 'Customize your export by using Javascript' 15 | }, [ 16 | { 17 | type: 'text', 18 | id: 'customScript', 19 | value: 'console.log(output)', 20 | label: 'Custom Javascript export' 21 | } 22 | ], (customData) => { 23 | 24 | // @TODO 25 | }); 26 | }); 27 | }; -------------------------------------------------------------------------------- /src/export/export-components.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | type: 'checkbox', 4 | id: 'merge', 5 | label: 'Merge', 6 | value: 'Merge identical styles' 7 | }, 8 | { 9 | type: 'multicheckbox', 10 | id: 'excludeProps', 11 | label: 'Exclude properties', 12 | values: [ 13 | 'Color', 14 | 'Line height' 15 | ] 16 | }, 17 | { 18 | type: 'select', 19 | id: 'cssUnit', 20 | options: [ 21 | 'px', 22 | 'em', 23 | 'rem', 24 | '%', 25 | 'vh', 26 | 'vw', 27 | 'No unit' 28 | ], 29 | label: 'CSS unit' 30 | }, 31 | { 32 | type: 'text', 33 | id: 'scalingFactor', 34 | value: 1, 35 | label: 'Size scaling factor' 36 | }, 37 | { 38 | type: 'text', 39 | id: 'maxDecimalPlaces', 40 | value: 2, 41 | label: 'Maximal decimal places' 42 | }, 43 | { 44 | type: 'text', 45 | id: 'namingPrefix', 46 | value: 'type', 47 | label: 'Naming prefix' 48 | }, 49 | { 50 | type: 'select', 51 | id: 'namingConvention', 52 | options: [ 53 | 'Numeric', 54 | 'Text style name' 55 | ], 56 | label: 'Naming convention' 57 | } 58 | ]; -------------------------------------------------------------------------------- /src/export/open-export-dialog.js: -------------------------------------------------------------------------------- 1 | import ui from '../util/ui'; 2 | import exportUtils from '../util/export'; 3 | import sketchUtils from '../util/sketch'; 4 | 5 | import exportComponents from './export-components'; 6 | 7 | export default function(context, opts, cb) { 8 | 9 | ui.createSettingsDialog(context, opts, exportComponents, (data) => { 10 | 11 | // Defaults 12 | data.propertyNamingConvention = data.propertyNamingConvention || 'Numeric'; 13 | data.cssUnit = (data.cssUnit === 'No unit' ? 0 : data.cssUnit); 14 | 15 | // First store the properties we should exclude 16 | let excludeProps = []; 17 | if (data['excludeProps']['Color']) { 18 | excludeProps.push('color'); 19 | } 20 | 21 | if (data['excludeProps']['Line height']) { 22 | excludeProps.push('lineHeight'); 23 | } 24 | 25 | // Get the text styles from the Sketch document 26 | let textStyles = sketchUtils.getTextStyles(context); 27 | textStyles = exportUtils.sortTextStyles(textStyles); 28 | textStyles = exportUtils.excludeTextStyleProperties(textStyles, excludeProps); 29 | 30 | if (data['merge']) { 31 | textStyles = exportUtils.removeDoubleTextStyles(textStyles); 32 | } 33 | 34 | cb(textStyles, data); 35 | }); 36 | }; -------------------------------------------------------------------------------- /src/html-fontbook-export.js: -------------------------------------------------------------------------------- 1 | import ui from './util/ui'; 2 | import exportUtils from './util/export'; 3 | import openExportDialog from "./export/open-export-dialog"; 4 | 5 | export default function(context) { 6 | 7 | openExportDialog(context, { 8 | title: 'Create HTML fontbook', 9 | informativeText: 'Create a handy HTML fontbook from your text styles', 10 | confirmBtnText: 'Export HTML fontbook' 11 | }, (textStyles, data) => { 12 | 13 | // Create a HTML fontbook with these styles 14 | let html = exportUtils.createHtmlFontbook(textStyles, data); 15 | 16 | // Ask the user to save the file 17 | ui.createSavePanel('typex-fontbook.html', html); 18 | }); 19 | }; -------------------------------------------------------------------------------- /src/json-export.js: -------------------------------------------------------------------------------- 1 | import ui from './util/ui'; 2 | import stringUtils from './util/string'; 3 | import exportUtils from './util/export'; 4 | 5 | import openExportDialog from './export/open-export-dialog'; 6 | 7 | export default function(context) { 8 | 9 | openExportDialog(context, { 10 | title: 'JSON export', 11 | informativeText: 'Export text styles in JSON format' 12 | }, (textStyles, data) => { 13 | 14 | // Export as JSON 15 | let textStyleJson = {}; 16 | 17 | textStyles.forEach((textStyle, i) => { 18 | 19 | let textStyleIdentifier = stringUtils.slugify(textStyle.name); 20 | let stylePropertyNaming = data.namingPrefix + '-' + (data.namingConvention === 'Numeric' ? (i+1) : textStyleIdentifier); 21 | 22 | textStyleJson[stylePropertyNaming] = exportUtils.createCssProps(textStyle, data); 23 | }); 24 | 25 | // Ask the user to save the file 26 | ui.createSavePanel('typex-text-styles.json', JSON.stringify(textStyleJson)); 27 | }); 28 | }; -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3, 3 | "bundleVersion": 1, 4 | "icon": "icon.png", 5 | "commands": [ 6 | { 7 | "name": "HTML fontbook", 8 | "shortcut": "ctrl shift f", 9 | "identifier": "html-fontbook-export", 10 | "script": "./html-fontbook-export.js" 11 | }, 12 | { 13 | "name": "JSON", 14 | "shortcut": "ctrl shift j", 15 | "identifier": "json-export", 16 | "script": "./json-export.js" 17 | }, 18 | { 19 | "name": "CSS classes", 20 | "shortcut": "ctrl shift c", 21 | "identifier": "css-export", 22 | "script": "./css-export.js" 23 | }, 24 | { 25 | "name": "SASS mixins", 26 | "shortcut": "ctrl shift s", 27 | "identifier": "sass-mixins-export", 28 | "script": "./sass-mixins-export.js" 29 | } 30 | ], 31 | "menu": { 32 | "title": "Typex (Text style export)", 33 | "items": [ 34 | { 35 | "title": "Export", 36 | "items": [ 37 | "html-fontbook-export", 38 | "json-export", 39 | "css-export", 40 | "sass-mixins-export" 41 | ] 42 | } 43 | ] 44 | } 45 | } -------------------------------------------------------------------------------- /src/sass-mixins-export.js: -------------------------------------------------------------------------------- 1 | import ui from './util/ui'; 2 | import stringUtils from './util/string'; 3 | import exportUtils from './util/export'; 4 | 5 | import openExportDialog from './export/open-export-dialog'; 6 | 7 | export default function(context) { 8 | 9 | openExportDialog(context, { 10 | title: 'SASS mixins export', 11 | informativeText: 'Export each text style as a SASS mixin' 12 | }, (textStyles, data) => { 13 | 14 | let sass = {}; 15 | 16 | textStyles.forEach(textStyle => { 17 | sass[stringUtils.slugify(textStyle.name)] = exportUtils.createCssProps(textStyle, data); 18 | }); 19 | 20 | let output = ''; 21 | let i = 0; 22 | 23 | for (let identifier in sass) { 24 | 25 | if (sass.hasOwnProperty(identifier)) { 26 | 27 | let mixinName = data.namingPrefix + '-' + (data.namingConvention === 'Numeric' ? i+1 : identifier); 28 | 29 | output += ( i !== 0 ? "\n" : '' ) + '@mixin ' + mixinName + "\n"; 30 | output += '{'+"\n"; 31 | output += exportUtils.createStyleBlock(sass[identifier]); 32 | output += '}'+"\n"; 33 | i++; 34 | } 35 | } 36 | 37 | ui.createSavePanel('typex-mixins.scss', output); 38 | }); 39 | }; -------------------------------------------------------------------------------- /src/util/export.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import util from './util'; 4 | import numberUtils from './number'; 5 | 6 | const exportUtils = { 7 | sortTextStyles(textStyles) { 8 | 9 | // Sort text styles by size 10 | textStyles.sort((a, b) => { 11 | return a.fontSize - b.fontSize; 12 | }); 13 | 14 | return textStyles; 15 | }, 16 | excludeTextStyleProperties(textStyles, excludedProps = []) { 17 | 18 | textStyles.forEach(textStyle => { 19 | excludedProps.forEach(prop => { 20 | if (textStyle[prop]) { 21 | delete textStyle[prop]; 22 | } 23 | }); 24 | }); 25 | 26 | return textStyles; 27 | }, 28 | removeDoubleTextStyles(textStyles) { 29 | 30 | let uniqueTextStyles = {}; 31 | let filtered = []; 32 | 33 | textStyles.forEach((textStyle, i) => { 34 | 35 | let id = util.createTextStyleId(textStyle); 36 | 37 | if (! uniqueTextStyles[id]) { 38 | uniqueTextStyles[id] = true; 39 | filtered.push(textStyle); 40 | } 41 | }); 42 | 43 | return filtered; 44 | }, 45 | createCssProps(textStyle, opts = {}) { 46 | 47 | opts.cssUnit = opts.cssUnit || 0; 48 | opts.scalingFactor = opts.scalingFactor || 1; 49 | opts.maxDecimalPlaces = opts.maxDecimalPlaces || 2; 50 | 51 | let cssProps = {}; 52 | 53 | cssProps['font-family'] = textStyle.fontFamily; 54 | cssProps['font-weight'] = 400; 55 | cssProps['text-transform'] = 'none'; 56 | 57 | let fontParts = textStyle.fontFamily.split('-'); 58 | 59 | let fontWeightMap = { 60 | 'Thin': 100, 61 | 'Light': 300, 62 | 'Regular': 400, 63 | 'Medium': 500, 64 | 'Bold': 700, 65 | 'Black': 900 66 | }; 67 | 68 | if (fontParts[1] && fontWeightMap[fontParts[1]]) { 69 | cssProps['font-family'] = fontParts[0]; 70 | cssProps['font-weight'] = fontWeightMap[fontParts[1]]; 71 | } 72 | 73 | cssProps['font-size'] = numberUtils.parseFloatMaxDecimal(opts.scalingFactor * textStyle.fontSize, opts.maxDecimalPlaces)+opts.cssUnit; 74 | cssProps['letter-spacing'] = numberUtils.parseFloatMaxDecimal(opts.scalingFactor * textStyle.letterSpacing, opts.maxDecimalPlaces)+opts.cssUnit; 75 | 76 | if (textStyle.textTransform === 1) { 77 | cssProps['text-transform'] = 'uppercase'; 78 | } 79 | 80 | if (textStyle.textTransform === 2) { 81 | cssProps['text-transform'] = 'lowercase'; 82 | } 83 | 84 | if (textStyle.lineHeight) { 85 | cssProps['line-height'] = numberUtils.parseFloatMaxDecimal(1 + (textStyle.lineHeight - textStyle.fontSize) / textStyle.lineHeight, opts.maxDecimalPlaces); 86 | } 87 | 88 | if (textStyle.color) { 89 | cssProps['color'] = exportUtils.createRgbaString(textStyle.color); 90 | } 91 | 92 | return cssProps; 93 | }, 94 | createRgbaString(colorObj) { 95 | return 'rgba('+exportUtils.createColorValue(colorObj.r)+', '+exportUtils.createColorValue(colorObj.g)+', '+exportUtils.createColorValue(colorObj.b)+', '+colorObj.a+')'; 96 | }, 97 | createColorValue(normalizedValue) { 98 | return Math.round(normalizedValue * 255); 99 | }, 100 | createStyleBlock(cssProps) { 101 | 102 | let output = ''; 103 | 104 | for (let prop in cssProps) { 105 | output += "\t"+prop+': '+cssProps[prop]+';'+"\n"; 106 | } 107 | 108 | return output; 109 | }, 110 | createInlineStyleString(cssProps) { 111 | 112 | let styleString = ''; 113 | 114 | for (let prop in cssProps) { 115 | styleString += prop + ': ' + cssProps[prop] +'; '; 116 | } 117 | 118 | return styleString; 119 | }, 120 | createHtmlFontbook(textStyles, opts = {}) { 121 | 122 | let output = ` 123 | 124 | 125 | 126 | 127 | Typex text styles 128 | 129 | 130 | `; 131 | 132 | textStyles.forEach((textStyle, i) => { 133 | 134 | let cssProps = exportUtils.createCssProps(textStyle, opts); 135 | let inlineStyleString = exportUtils.createInlineStyleString(cssProps); 136 | let cssPropsBlock = exportUtils.createStyleBlock(cssProps); 137 | 138 | let textStyleName; 139 | 140 | if (opts.namingConvention === 'Numeric') { 141 | 142 | textStyleName = opts.namingPrefix + ' ' + (i+1); 143 | 144 | } else if (opts.namingConvention === 'Text style name') { 145 | 146 | textStyleName = opts.namingPrefix + ' ' + textStyle.name; 147 | 148 | } else { 149 | 150 | textStyleName = opts.namingPrefix + ' ' + (i+1) + ' ('+textStyle.name+')'; 151 | } 152 | 153 | output += ` 154 |
155 |
156 | ${i+1}. 157 | 158 | ${textStyleName} 159 | 160 |
161 |
162 |
163 | The quick brown fox jumps over the lazy dog 164 |
165 |
166 | 167 |
168 |
169 |
170 | `; 171 | }); 172 | 173 | output += ` 174 | 175 | 176 | `; 177 | 178 | return output; 179 | } 180 | }; 181 | 182 | export default exportUtils; -------------------------------------------------------------------------------- /src/util/number.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const number = { 4 | parseFloatMaxDecimal(number, maxDecimalPlaces) { 5 | return Number(number.toFixed(maxDecimalPlaces).replace(/[.,]00$/, '')); 6 | } 7 | }; 8 | 9 | export default number; -------------------------------------------------------------------------------- /src/util/sketch.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const sketch = { 4 | getTextStyles(context) { 5 | 6 | let texts = context.document.documentData().layerTextStyles().objects(); 7 | let rawTextStyles = []; 8 | 9 | texts.forEach((text, i) => { 10 | 11 | rawTextStyles.push({ 12 | attributes: text.style().textStyle().attributes(), 13 | textStyle: text, 14 | name: text.name() 15 | }); 16 | }); 17 | 18 | let textStyles = []; 19 | 20 | rawTextStyles.forEach(rawTextStyle => { 21 | 22 | let textStyle = {}; 23 | 24 | textStyle.name = rawTextStyle.name; 25 | textStyle.fontFamily = String(rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontNameAttribute)); 26 | textStyle.fontSize = rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontSizeAttribute); 27 | textStyle.paragraph = rawTextStyle.attributes.NSParagraphStyle; 28 | 29 | if (textStyle.paragraph) { 30 | textStyle.lineHeight = textStyle.paragraph.maximumLineHeight(); 31 | } 32 | 33 | let color = rawTextStyle.attributes.MSAttributedStringColorAttribute; 34 | 35 | if (color) { 36 | let r = color.red(); 37 | let g = color.green(); 38 | let b = color.blue(); 39 | let a = color.alpha(); 40 | 41 | textStyle.color = { 42 | r: r, 43 | g: g, 44 | b: b, 45 | a: a 46 | }; 47 | } 48 | 49 | textStyle.letterSpacing = rawTextStyle.attributes.NSKern || 0; 50 | textStyle.textTransform = parseInt(rawTextStyle.attributes.MSAttributedStringTextTransformAttribute || 0); 51 | 52 | // @TODO strikethrough & underline, or is this not needed? 53 | 54 | textStyles.push(textStyle); 55 | }); 56 | 57 | return textStyles; 58 | } 59 | }; 60 | 61 | export default sketch; -------------------------------------------------------------------------------- /src/util/string.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const string = { 4 | slugify(str) { 5 | 6 | str = str.replace(/^\s+|\s+$/g, ''); // trim 7 | str = str.toLowerCase(); 8 | 9 | // remove accents, swap ñ for n, etc 10 | let from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;'; 11 | let to = 'aaaaeeeeiiiioooouuuunc------'; 12 | 13 | for (let i = 0, l = from.length; i < l; i++) { 14 | str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); 15 | } 16 | 17 | str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars 18 | .replace(/\s+/g, '-') // collapse whitespace and replace by - 19 | .replace(/-+/g, '-') // collapse dashes 20 | ; 21 | 22 | return str; 23 | } 24 | }; 25 | 26 | export default string; -------------------------------------------------------------------------------- /src/util/ui.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const ui = { 4 | createSavePanel(defaultFileName, contents) { 5 | 6 | let save = NSSavePanel.savePanel(); 7 | 8 | save.setNameFieldStringValue(defaultFileName); 9 | save.setAllowsOtherFileTypes(false); 10 | save.setExtensionHidden(false); 11 | 12 | if (save.runModal()) { 13 | 14 | let file = NSString.stringWithString(contents); 15 | let path = save.URL().path(); 16 | 17 | file.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, null); 18 | } 19 | }, 20 | createLabel: (text = '') => { 21 | 22 | let label = NSTextField.alloc().init(); 23 | 24 | label.setStringValue(text); 25 | label.setFont(NSFont.boldSystemFontOfSize(12)); 26 | label.setBezeled(false); 27 | label.setDrawsBackground(false); 28 | label.setEditable(false); 29 | label.setSelectable(false); 30 | 31 | return label; 32 | }, 33 | createTextField(value) { 34 | 35 | let field = NSTextField.alloc().init(); 36 | 37 | field.setStringValue(value); 38 | 39 | return field; 40 | }, 41 | createSelect: (options) => { 42 | 43 | let comboBox = NSPopUpButton.alloc().init(); 44 | 45 | comboBox.addItemsWithTitles(options); 46 | comboBox.selectItemAtIndex(0); 47 | 48 | return comboBox; 49 | }, 50 | createStepper: (value) => { 51 | 52 | let stepper = NSStepper.alloc().init(); 53 | return stepper; 54 | }, 55 | createCheckbox: (title) => { 56 | 57 | let checkbox = NSButton.alloc().init(); 58 | checkbox.setButtonType(NSSwitchButton); 59 | checkbox.title = title; 60 | return checkbox; 61 | }, 62 | createSettingsDialog(context, opts = {}, components, cb) { 63 | 64 | opts.title = opts.title || 'Alert'; 65 | opts.informativeText = opts.informativeText || ''; 66 | opts.cancelBtnText = opts.cancelBtnText || 'Cancel'; 67 | opts.confirmBtnText = opts.confirmBtnText || 'Ok'; 68 | 69 | let dialog = NSAlert.alloc().init(); 70 | let dialogIconPath = context.plugin.urlForResourceNamed('icon.png').path(); 71 | let dialogIcon = NSImage.alloc().initByReferencingFile(dialogIconPath); 72 | 73 | dialog.setIcon(dialogIcon); 74 | dialog.setMessageText(opts.title); 75 | dialog.setInformativeText(opts.informativeText); 76 | 77 | let btnConfirm = dialog.addButtonWithTitle(opts.confirmBtnText); 78 | let btnCancel = dialog.addButtonWithTitle(opts.cancelBtnText); 79 | 80 | // Create grid view 81 | let gridView = NSGridView.alloc().init(); 82 | 83 | // Create object to hold all inputs 84 | let inputs = {}; 85 | let height = 0; 86 | 87 | let rowSpacing = 8; 88 | 89 | // Loop each component 90 | components.forEach(c => { 91 | 92 | let label, field; 93 | 94 | switch (c.type) { 95 | 96 | case 'text': 97 | 98 | label = ui.createLabel(c.label); 99 | field = ui.createTextField(c.value); 100 | height += 22 + rowSpacing; 101 | gridView.addRowWithViews([label, field]); 102 | 103 | break; 104 | 105 | case 'stepper': 106 | 107 | label = ui.createLabel(c.label); 108 | field = ui.createStepper(c.value); 109 | height += 22 + rowSpacing; 110 | gridView.addRowWithViews([label, field]); 111 | 112 | break; 113 | 114 | case 'checkbox': 115 | 116 | label = ui.createLabel(c.label); 117 | field = ui.createCheckbox(c.value); 118 | height += 22 + rowSpacing; 119 | gridView.addRowWithViews([label, field]); 120 | 121 | break; 122 | 123 | case 'multicheckbox': 124 | 125 | field = []; 126 | 127 | c.values.forEach((v, i) => { 128 | 129 | label = (i ? ui.createLabel() : ui.createLabel(c.label)); 130 | 131 | let checkbox = ui.createCheckbox(v); 132 | height += 22 + rowSpacing; 133 | 134 | field.push(checkbox); 135 | gridView.addRowWithViews([label, checkbox]); 136 | }); 137 | 138 | break; 139 | 140 | case 'select': 141 | 142 | label = ui.createLabel(c.label); 143 | field = ui.createSelect(c.options); 144 | height += 28 + rowSpacing; 145 | gridView.addRowWithViews([label, field]); 146 | 147 | break; 148 | } 149 | 150 | inputs[c.id] = field; 151 | }); 152 | 153 | // Set grid view as view of dialog 154 | dialog.accessoryView = gridView; 155 | 156 | gridView.columnSpacing = 30; 157 | gridView.rowSpacing = rowSpacing; 158 | gridView.frame = NSMakeRect(0, 0, 400, height); 159 | 160 | // Open the dialog and store the response code 161 | let responseCode = dialog.runModal(); 162 | 163 | // The dialog is being 'submitted' 164 | if (responseCode === 1000) { 165 | 166 | let data = {}; 167 | 168 | components.forEach(c => { 169 | 170 | switch (c.type) { 171 | case 'text': 172 | data[c.id] = inputs[c.id].stringValue(); 173 | break; 174 | case 'select': 175 | data[c.id] = c.options[inputs[c.id].indexOfSelectedItem()]; 176 | break; 177 | case 'checkbox': 178 | data[c.id] = (inputs[c.id].state() === 1); 179 | break; 180 | 181 | case 'multicheckbox': 182 | let values = {}; 183 | 184 | c.values.forEach((v, i) => { 185 | values[v] = ( inputs[c.id][i].state() === 1 ); 186 | }); 187 | 188 | data[c.id] = values; 189 | } 190 | }); 191 | 192 | cb(data); 193 | return; 194 | } 195 | 196 | return dialog; 197 | } 198 | }; 199 | 200 | export default ui; -------------------------------------------------------------------------------- /src/util/util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const util = { 4 | createTextStyleId(textStyle) { 5 | 6 | let textStyleId = ''; 7 | 8 | // Make sure this id incorporates every possible property of the text style 9 | 10 | textStyleId += textStyle.fontFamily; 11 | textStyleId += '-'+textStyle.fontSize; 12 | textStyleId += '-'+textStyle.letterSpacing; 13 | textStyleId += '-'+textStyle.textTransform; 14 | 15 | if (textStyle.lineHeight) { 16 | textStyleId += '-'+textStyle.lineHeight; 17 | } 18 | 19 | if (textStyle.color) { 20 | textStyleId += '-'+textStyle.color.r+'-'+textStyle.color.g+'-'+textStyle.color.b+'-'+textStyle.color.a; 21 | } 22 | 23 | return textStyleId; 24 | } 25 | }; 26 | 27 | export default util; -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reinvanoyen/typex/297f2b00d5fb21793b0fa6060a1b031a326299b0/typex.sketchplugin/Contents/Resources/icon.png -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/advanced-json-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/advanced-json-export.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/advanced-json-export.js": 95 | /*!*************************************!*\ 96 | !*** ./src/advanced-json-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 _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util/ui */ "./src/util/ui.js"); 104 | /* harmony import */ var _util_string__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util/string */ "./src/util/string.js"); 105 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./util/export */ "./src/util/export.js"); 106 | /* harmony import */ var _export_open_export_dialog__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export/open-export-dialog */ "./src/export/open-export-dialog.js"); 107 | 108 | 109 | 110 | 111 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 112 | Object(_export_open_export_dialog__WEBPACK_IMPORTED_MODULE_3__["default"])(context, { 113 | title: 'JSON export', 114 | informativeText: 'Export text styles in JSON format' 115 | }, function (textStyles, data) { 116 | // Export as JSON 117 | var textStyleJson = {}; 118 | textStyles.forEach(function (textStyle, i) { 119 | var textStyleIdentifier = _util_string__WEBPACK_IMPORTED_MODULE_1__["default"].slugify(textStyle.name); 120 | var stylePropertyNaming = data.namingPrefix + '-' + (data.namingConvention === 'Numeric' ? i + 1 : textStyleIdentifier); 121 | textStyleJson[stylePropertyNaming] = _util_export__WEBPACK_IMPORTED_MODULE_2__["default"].createCssProps(textStyle, data); 122 | }); // Ask the user to save the file 123 | 124 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSavePanel('typex-text-styles.json', JSON.stringify(textStyleJson)); 125 | }); 126 | }); 127 | ; 128 | 129 | /***/ }), 130 | 131 | /***/ "./src/export/export-components.js": 132 | /*!*****************************************!*\ 133 | !*** ./src/export/export-components.js ***! 134 | \*****************************************/ 135 | /*! exports provided: default */ 136 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 137 | 138 | "use strict"; 139 | __webpack_require__.r(__webpack_exports__); 140 | /* harmony default export */ __webpack_exports__["default"] = ([{ 141 | type: 'checkbox', 142 | id: 'merge', 143 | label: 'Merge', 144 | value: 'Merge identical styles' 145 | }, { 146 | type: 'multicheckbox', 147 | id: 'excludeProps', 148 | label: 'Exclude properties', 149 | values: ['Color', 'Line height'] 150 | }, { 151 | type: 'select', 152 | id: 'cssUnit', 153 | options: ['px', 'em', 'rem', '%', 'vh', 'vw', 'No unit'], 154 | label: 'CSS unit' 155 | }, { 156 | type: 'text', 157 | id: 'scalingFactor', 158 | value: 1, 159 | label: 'Size scaling factor' 160 | }, { 161 | type: 'text', 162 | id: 'maxDecimalPlaces', 163 | value: 2, 164 | label: 'Maximal decimal places' 165 | }, { 166 | type: 'text', 167 | id: 'namingPrefix', 168 | value: 'type', 169 | label: 'Naming prefix' 170 | }, { 171 | type: 'select', 172 | id: 'namingConvention', 173 | options: ['Numeric', 'Text style name'], 174 | label: 'Naming convention' 175 | }]); 176 | 177 | /***/ }), 178 | 179 | /***/ "./src/export/open-export-dialog.js": 180 | /*!******************************************!*\ 181 | !*** ./src/export/open-export-dialog.js ***! 182 | \******************************************/ 183 | /*! exports provided: default */ 184 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 185 | 186 | "use strict"; 187 | __webpack_require__.r(__webpack_exports__); 188 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/ui */ "./src/util/ui.js"); 189 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/export */ "./src/util/export.js"); 190 | /* harmony import */ var _util_sketch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/sketch */ "./src/util/sketch.js"); 191 | /* harmony import */ var _export_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export-components */ "./src/export/export-components.js"); 192 | 193 | 194 | 195 | 196 | /* harmony default export */ __webpack_exports__["default"] = (function (context, opts, cb) { 197 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSettingsDialog(context, opts, _export_components__WEBPACK_IMPORTED_MODULE_3__["default"], function (data) { 198 | // Defaults 199 | data.propertyNamingConvention = data.propertyNamingConvention || 'Numeric'; 200 | data.cssUnit = data.cssUnit === 'No unit' ? 0 : data.cssUnit; // First store the properties we should exclude 201 | 202 | var excludeProps = []; 203 | 204 | if (data['excludeProps']['Color']) { 205 | excludeProps.push('color'); 206 | } 207 | 208 | if (data['excludeProps']['Line height']) { 209 | excludeProps.push('lineHeight'); 210 | } // Get the text styles from the Sketch document 211 | 212 | 213 | var textStyles = _util_sketch__WEBPACK_IMPORTED_MODULE_2__["default"].getTextStyles(context); 214 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].sortTextStyles(textStyles); 215 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].excludeTextStyleProperties(textStyles, excludeProps); 216 | 217 | if (data['merge']) { 218 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].removeDoubleTextStyles(textStyles); 219 | } 220 | 221 | cb(textStyles, data); 222 | }); 223 | }); 224 | ; 225 | 226 | /***/ }), 227 | 228 | /***/ "./src/util/export.js": 229 | /*!****************************!*\ 230 | !*** ./src/util/export.js ***! 231 | \****************************/ 232 | /*! exports provided: default */ 233 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 234 | 235 | "use strict"; 236 | __webpack_require__.r(__webpack_exports__); 237 | /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ "./src/util/util.js"); 238 | /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ "./src/util/number.js"); 239 | 240 | 241 | 242 | 243 | var exportUtils = { 244 | sortTextStyles: function sortTextStyles(textStyles) { 245 | // Sort text styles by size 246 | textStyles.sort(function (a, b) { 247 | return a.fontSize - b.fontSize; 248 | }); 249 | return textStyles; 250 | }, 251 | excludeTextStyleProperties: function excludeTextStyleProperties(textStyles) { 252 | var excludedProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 253 | textStyles.forEach(function (textStyle) { 254 | excludedProps.forEach(function (prop) { 255 | if (textStyle[prop]) { 256 | delete textStyle[prop]; 257 | } 258 | }); 259 | }); 260 | return textStyles; 261 | }, 262 | removeDoubleTextStyles: function removeDoubleTextStyles(textStyles) { 263 | var uniqueTextStyles = {}; 264 | var filtered = []; 265 | textStyles.forEach(function (textStyle, i) { 266 | var id = _util__WEBPACK_IMPORTED_MODULE_0__["default"].createTextStyleId(textStyle); 267 | 268 | if (!uniqueTextStyles[id]) { 269 | uniqueTextStyles[id] = true; 270 | filtered.push(textStyle); 271 | } 272 | }); 273 | return filtered; 274 | }, 275 | createCssProps: function createCssProps(textStyle) { 276 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 277 | opts.cssUnit = opts.cssUnit || 0; 278 | opts.scalingFactor = opts.scalingFactor || 1; 279 | opts.maxDecimalPlaces = opts.maxDecimalPlaces || 2; 280 | var cssProps = {}; 281 | cssProps['font-family'] = textStyle.fontFamily; 282 | cssProps['font-weight'] = 400; 283 | cssProps['text-transform'] = 'none'; 284 | var fontParts = textStyle.fontFamily.split('-'); 285 | var fontWeightMap = { 286 | 'Thin': 100, 287 | 'Light': 300, 288 | 'Regular': 400, 289 | 'Medium': 500, 290 | 'Bold': 700, 291 | 'Black': 900 292 | }; 293 | 294 | if (fontParts[1] && fontWeightMap[fontParts[1]]) { 295 | cssProps['font-family'] = fontParts[0]; 296 | cssProps['font-weight'] = fontWeightMap[fontParts[1]]; 297 | } 298 | 299 | cssProps['font-size'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.fontSize, opts.maxDecimalPlaces) + opts.cssUnit; 300 | cssProps['letter-spacing'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.letterSpacing, opts.maxDecimalPlaces) + opts.cssUnit; 301 | 302 | if (textStyle.textTransform === 1) { 303 | cssProps['text-transform'] = 'uppercase'; 304 | } 305 | 306 | if (textStyle.textTransform === 2) { 307 | cssProps['text-transform'] = 'lowercase'; 308 | } 309 | 310 | if (textStyle.lineHeight) { 311 | cssProps['line-height'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(1 + (textStyle.lineHeight - textStyle.fontSize) / textStyle.lineHeight, opts.maxDecimalPlaces); 312 | } 313 | 314 | if (textStyle.color) { 315 | cssProps['color'] = exportUtils.createRgbaString(textStyle.color); 316 | } 317 | 318 | return cssProps; 319 | }, 320 | createRgbaString: function createRgbaString(colorObj) { 321 | return 'rgba(' + exportUtils.createColorValue(colorObj.r) + ', ' + exportUtils.createColorValue(colorObj.g) + ', ' + exportUtils.createColorValue(colorObj.b) + ', ' + colorObj.a + ')'; 322 | }, 323 | createColorValue: function createColorValue(normalizedValue) { 324 | return Math.round(normalizedValue * 255); 325 | }, 326 | createStyleBlock: function createStyleBlock(cssProps) { 327 | var output = ''; 328 | 329 | for (var prop in cssProps) { 330 | output += "\t" + prop + ': ' + cssProps[prop] + ';' + "\n"; 331 | } 332 | 333 | return output; 334 | }, 335 | createInlineStyleString: function createInlineStyleString(cssProps) { 336 | var styleString = ''; 337 | 338 | for (var prop in cssProps) { 339 | styleString += prop + ': ' + cssProps[prop] + '; '; 340 | } 341 | 342 | return styleString; 343 | }, 344 | createHtmlFontbook: function createHtmlFontbook(textStyles) { 345 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 346 | var output = "\n \n \n \n \n Typex text styles\n \n \n "; 347 | textStyles.forEach(function (textStyle, i) { 348 | var cssProps = exportUtils.createCssProps(textStyle, opts); 349 | var inlineStyleString = exportUtils.createInlineStyleString(cssProps); 350 | var cssPropsBlock = exportUtils.createStyleBlock(cssProps); 351 | var textStyleName; 352 | 353 | if (opts.namingConvention === 'Numeric') { 354 | textStyleName = opts.namingPrefix + ' ' + (i + 1); 355 | } else if (opts.namingConvention === 'Text style name') { 356 | textStyleName = opts.namingPrefix + ' ' + textStyle.name; 357 | } else { 358 | textStyleName = opts.namingPrefix + ' ' + (i + 1) + ' (' + textStyle.name + ')'; 359 | } 360 | 361 | output += "\n
\n
\n ".concat(i + 1, ".\n \n ").concat(textStyleName, "\n \n
\n
\n
\n The quick brown fox jumps over the lazy dog\n
\n
\n \n
\n
\n
\n "); 362 | }); 363 | output += "\n \n \n "; 364 | return output; 365 | } 366 | }; 367 | /* harmony default export */ __webpack_exports__["default"] = (exportUtils); 368 | 369 | /***/ }), 370 | 371 | /***/ "./src/util/number.js": 372 | /*!****************************!*\ 373 | !*** ./src/util/number.js ***! 374 | \****************************/ 375 | /*! exports provided: default */ 376 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 377 | 378 | "use strict"; 379 | __webpack_require__.r(__webpack_exports__); 380 | 381 | 382 | var number = { 383 | parseFloatMaxDecimal: function parseFloatMaxDecimal(number, maxDecimalPlaces) { 384 | return Number(number.toFixed(maxDecimalPlaces).replace(/[.,]00$/, '')); 385 | } 386 | }; 387 | /* harmony default export */ __webpack_exports__["default"] = (number); 388 | 389 | /***/ }), 390 | 391 | /***/ "./src/util/sketch.js": 392 | /*!****************************!*\ 393 | !*** ./src/util/sketch.js ***! 394 | \****************************/ 395 | /*! exports provided: default */ 396 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 397 | 398 | "use strict"; 399 | __webpack_require__.r(__webpack_exports__); 400 | 401 | 402 | var sketch = { 403 | getTextStyles: function getTextStyles(context) { 404 | var texts = context.document.documentData().layerTextStyles().objects(); 405 | var rawTextStyles = []; 406 | texts.forEach(function (text, i) { 407 | rawTextStyles.push({ 408 | attributes: text.style().textStyle().attributes(), 409 | textStyle: text, 410 | name: text.name() 411 | }); 412 | }); 413 | var textStyles = []; 414 | rawTextStyles.forEach(function (rawTextStyle) { 415 | var textStyle = {}; 416 | textStyle.name = rawTextStyle.name; 417 | textStyle.fontFamily = String(rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontNameAttribute)); 418 | textStyle.fontSize = rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontSizeAttribute); 419 | textStyle.paragraph = rawTextStyle.attributes.NSParagraphStyle; 420 | 421 | if (textStyle.paragraph) { 422 | textStyle.lineHeight = textStyle.paragraph.maximumLineHeight(); 423 | } 424 | 425 | var color = rawTextStyle.attributes.MSAttributedStringColorAttribute; 426 | 427 | if (color) { 428 | var r = color.red(); 429 | var g = color.green(); 430 | var b = color.blue(); 431 | var a = color.alpha(); 432 | textStyle.color = { 433 | r: r, 434 | g: g, 435 | b: b, 436 | a: a 437 | }; 438 | } 439 | 440 | textStyle.letterSpacing = rawTextStyle.attributes.NSKern || 0; 441 | textStyle.textTransform = parseInt(rawTextStyle.attributes.MSAttributedStringTextTransformAttribute || 0); // @TODO strikethrough & underline, or is this not needed? 442 | 443 | textStyles.push(textStyle); 444 | }); 445 | return textStyles; 446 | } 447 | }; 448 | /* harmony default export */ __webpack_exports__["default"] = (sketch); 449 | 450 | /***/ }), 451 | 452 | /***/ "./src/util/string.js": 453 | /*!****************************!*\ 454 | !*** ./src/util/string.js ***! 455 | \****************************/ 456 | /*! exports provided: default */ 457 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 458 | 459 | "use strict"; 460 | __webpack_require__.r(__webpack_exports__); 461 | 462 | 463 | var string = { 464 | slugify: function slugify(str) { 465 | str = str.replace(/^\s+|\s+$/g, ''); // trim 466 | 467 | str = str.toLowerCase(); // remove accents, swap ñ for n, etc 468 | 469 | var from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;'; 470 | var to = 'aaaaeeeeiiiioooouuuunc------'; 471 | 472 | for (var i = 0, l = from.length; i < l; i++) { 473 | str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); 474 | } 475 | 476 | str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars 477 | .replace(/\s+/g, '-') // collapse whitespace and replace by - 478 | .replace(/-+/g, '-') // collapse dashes 479 | ; 480 | return str; 481 | } 482 | }; 483 | /* harmony default export */ __webpack_exports__["default"] = (string); 484 | 485 | /***/ }), 486 | 487 | /***/ "./src/util/ui.js": 488 | /*!************************!*\ 489 | !*** ./src/util/ui.js ***! 490 | \************************/ 491 | /*! exports provided: default */ 492 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 493 | 494 | "use strict"; 495 | __webpack_require__.r(__webpack_exports__); 496 | 497 | 498 | var ui = { 499 | createSavePanel: function createSavePanel(defaultFileName, contents) { 500 | var save = NSSavePanel.savePanel(); 501 | save.setNameFieldStringValue(defaultFileName); 502 | save.setAllowsOtherFileTypes(false); 503 | save.setExtensionHidden(false); 504 | 505 | if (save.runModal()) { 506 | var file = NSString.stringWithString(contents); 507 | var path = save.URL().path(); 508 | file.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, null); 509 | } 510 | }, 511 | createLabel: function createLabel() { 512 | var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 513 | var label = NSTextField.alloc().init(); 514 | label.setStringValue(text); 515 | label.setFont(NSFont.boldSystemFontOfSize(12)); 516 | label.setBezeled(false); 517 | label.setDrawsBackground(false); 518 | label.setEditable(false); 519 | label.setSelectable(false); 520 | return label; 521 | }, 522 | createTextField: function createTextField(value) { 523 | var field = NSTextField.alloc().init(); 524 | field.setStringValue(value); 525 | return field; 526 | }, 527 | createSelect: function createSelect(options) { 528 | var comboBox = NSPopUpButton.alloc().init(); 529 | comboBox.addItemsWithTitles(options); 530 | comboBox.selectItemAtIndex(0); 531 | return comboBox; 532 | }, 533 | createStepper: function createStepper(value) { 534 | var stepper = NSStepper.alloc().init(); 535 | return stepper; 536 | }, 537 | createCheckbox: function createCheckbox(title) { 538 | var checkbox = NSButton.alloc().init(); 539 | checkbox.setButtonType(NSSwitchButton); 540 | checkbox.title = title; 541 | return checkbox; 542 | }, 543 | createSettingsDialog: function createSettingsDialog(context) { 544 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 545 | var components = arguments.length > 2 ? arguments[2] : undefined; 546 | var cb = arguments.length > 3 ? arguments[3] : undefined; 547 | opts.title = opts.title || 'Alert'; 548 | opts.informativeText = opts.informativeText || ''; 549 | opts.cancelBtnText = opts.cancelBtnText || 'Cancel'; 550 | opts.confirmBtnText = opts.confirmBtnText || 'Ok'; 551 | var dialog = NSAlert.alloc().init(); 552 | var dialogIconPath = context.plugin.urlForResourceNamed('icon.png').path(); 553 | var dialogIcon = NSImage.alloc().initByReferencingFile(dialogIconPath); 554 | dialog.setIcon(dialogIcon); 555 | dialog.setMessageText(opts.title); 556 | dialog.setInformativeText(opts.informativeText); 557 | var btnConfirm = dialog.addButtonWithTitle(opts.confirmBtnText); 558 | var btnCancel = dialog.addButtonWithTitle(opts.cancelBtnText); // Create grid view 559 | 560 | var gridView = NSGridView.alloc().init(); // Create object to hold all inputs 561 | 562 | var inputs = {}; 563 | var height = 0; 564 | var rowSpacing = 8; // Loop each component 565 | 566 | components.forEach(function (c) { 567 | var label, field; 568 | 569 | switch (c.type) { 570 | case 'text': 571 | label = ui.createLabel(c.label); 572 | field = ui.createTextField(c.value); 573 | height += 22 + rowSpacing; 574 | gridView.addRowWithViews([label, field]); 575 | break; 576 | 577 | case 'stepper': 578 | label = ui.createLabel(c.label); 579 | field = ui.createStepper(c.value); 580 | height += 22 + rowSpacing; 581 | gridView.addRowWithViews([label, field]); 582 | break; 583 | 584 | case 'checkbox': 585 | label = ui.createLabel(c.label); 586 | field = ui.createCheckbox(c.value); 587 | height += 22 + rowSpacing; 588 | gridView.addRowWithViews([label, field]); 589 | break; 590 | 591 | case 'multicheckbox': 592 | field = []; 593 | c.values.forEach(function (v, i) { 594 | label = i ? ui.createLabel() : ui.createLabel(c.label); 595 | var checkbox = ui.createCheckbox(v); 596 | height += 22 + rowSpacing; 597 | field.push(checkbox); 598 | gridView.addRowWithViews([label, checkbox]); 599 | }); 600 | break; 601 | 602 | case 'select': 603 | label = ui.createLabel(c.label); 604 | field = ui.createSelect(c.options); 605 | height += 28 + rowSpacing; 606 | gridView.addRowWithViews([label, field]); 607 | break; 608 | } 609 | 610 | inputs[c.id] = field; 611 | }); // Set grid view as view of dialog 612 | 613 | dialog.accessoryView = gridView; 614 | gridView.columnSpacing = 30; 615 | gridView.rowSpacing = rowSpacing; 616 | gridView.frame = NSMakeRect(0, 0, 400, height); // Open the dialog and store the response code 617 | 618 | var responseCode = dialog.runModal(); // The dialog is being 'submitted' 619 | 620 | if (responseCode === 1000) { 621 | var data = {}; 622 | components.forEach(function (c) { 623 | switch (c.type) { 624 | case 'text': 625 | data[c.id] = inputs[c.id].stringValue(); 626 | break; 627 | 628 | case 'select': 629 | data[c.id] = c.options[inputs[c.id].indexOfSelectedItem()]; 630 | break; 631 | 632 | case 'checkbox': 633 | data[c.id] = inputs[c.id].state() === 1; 634 | break; 635 | 636 | case 'multicheckbox': 637 | var values = {}; 638 | c.values.forEach(function (v, i) { 639 | values[v] = inputs[c.id][i].state() === 1; 640 | }); 641 | data[c.id] = values; 642 | } 643 | }); 644 | cb(data); 645 | return; 646 | } 647 | 648 | return dialog; 649 | } 650 | }; 651 | /* harmony default export */ __webpack_exports__["default"] = (ui); 652 | 653 | /***/ }), 654 | 655 | /***/ "./src/util/util.js": 656 | /*!**************************!*\ 657 | !*** ./src/util/util.js ***! 658 | \**************************/ 659 | /*! exports provided: default */ 660 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 661 | 662 | "use strict"; 663 | __webpack_require__.r(__webpack_exports__); 664 | 665 | 666 | var util = { 667 | createTextStyleId: function createTextStyleId(textStyle) { 668 | var textStyleId = ''; // Make sure this id incorporates every possible property of the text style 669 | 670 | textStyleId += textStyle.fontFamily; 671 | textStyleId += '-' + textStyle.fontSize; 672 | textStyleId += '-' + textStyle.letterSpacing; 673 | textStyleId += '-' + textStyle.textTransform; 674 | 675 | if (textStyle.lineHeight) { 676 | textStyleId += '-' + textStyle.lineHeight; 677 | } 678 | 679 | if (textStyle.color) { 680 | textStyleId += '-' + textStyle.color.r + '-' + textStyle.color.g + '-' + textStyle.color.b + '-' + textStyle.color.a; 681 | } 682 | 683 | return textStyleId; 684 | } 685 | }; 686 | /* harmony default export */ __webpack_exports__["default"] = (util); 687 | 688 | /***/ }) 689 | 690 | /******/ }); 691 | if (key === 'default' && typeof exports === 'function') { 692 | exports(context); 693 | } else { 694 | exports[key](context); 695 | } 696 | } 697 | that['onRun'] = __skpm_run.bind(this, 'default') 698 | 699 | //# sourceMappingURL=advanced-json-export.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/css-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/css-export.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/css-export.js": 95 | /*!***************************!*\ 96 | !*** ./src/css-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 _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util/ui */ "./src/util/ui.js"); 104 | /* harmony import */ var _util_string__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util/string */ "./src/util/string.js"); 105 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./util/export */ "./src/util/export.js"); 106 | /* harmony import */ var _export_open_export_dialog__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export/open-export-dialog */ "./src/export/open-export-dialog.js"); 107 | 108 | 109 | 110 | 111 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 112 | Object(_export_open_export_dialog__WEBPACK_IMPORTED_MODULE_3__["default"])(context, { 113 | title: 'CSS classes export', 114 | informativeText: 'Export each text style as a class' 115 | }, function (textStyles, data) { 116 | var css = {}; 117 | textStyles.forEach(function (textStyle) { 118 | css[_util_string__WEBPACK_IMPORTED_MODULE_1__["default"].slugify(textStyle.name)] = _util_export__WEBPACK_IMPORTED_MODULE_2__["default"].createCssProps(textStyle, data); 119 | }); 120 | var output = ''; 121 | var i = 0; 122 | 123 | for (var identifier in css) { 124 | if (css.hasOwnProperty(identifier)) { 125 | var className = data.namingPrefix + '-' + (data.namingConvention === 'Numeric' ? i + 1 : identifier); 126 | output += (i !== 0 ? "\n" : '') + '.' + className + "\n"; 127 | output += '{' + "\n"; 128 | output += _util_export__WEBPACK_IMPORTED_MODULE_2__["default"].createStyleBlock(css[identifier]); 129 | output += '}' + "\n"; 130 | i++; 131 | } 132 | } 133 | 134 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSavePanel('typex-stylesheet.css', output); 135 | }); 136 | }); 137 | ; 138 | 139 | /***/ }), 140 | 141 | /***/ "./src/export/export-components.js": 142 | /*!*****************************************!*\ 143 | !*** ./src/export/export-components.js ***! 144 | \*****************************************/ 145 | /*! exports provided: default */ 146 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 147 | 148 | "use strict"; 149 | __webpack_require__.r(__webpack_exports__); 150 | /* harmony default export */ __webpack_exports__["default"] = ([{ 151 | type: 'checkbox', 152 | id: 'merge', 153 | label: 'Merge', 154 | value: 'Merge identical styles' 155 | }, { 156 | type: 'multicheckbox', 157 | id: 'excludeProps', 158 | label: 'Exclude properties', 159 | values: ['Color', 'Line height'] 160 | }, { 161 | type: 'select', 162 | id: 'cssUnit', 163 | options: ['px', 'em', 'rem', '%', 'vh', 'vw', 'No unit'], 164 | label: 'CSS unit' 165 | }, { 166 | type: 'text', 167 | id: 'scalingFactor', 168 | value: 1, 169 | label: 'Size scaling factor' 170 | }, { 171 | type: 'text', 172 | id: 'maxDecimalPlaces', 173 | value: 2, 174 | label: 'Maximal decimal places' 175 | }, { 176 | type: 'text', 177 | id: 'namingPrefix', 178 | value: 'type', 179 | label: 'Naming prefix' 180 | }, { 181 | type: 'select', 182 | id: 'namingConvention', 183 | options: ['Numeric', 'Text style name'], 184 | label: 'Naming convention' 185 | }]); 186 | 187 | /***/ }), 188 | 189 | /***/ "./src/export/open-export-dialog.js": 190 | /*!******************************************!*\ 191 | !*** ./src/export/open-export-dialog.js ***! 192 | \******************************************/ 193 | /*! exports provided: default */ 194 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 195 | 196 | "use strict"; 197 | __webpack_require__.r(__webpack_exports__); 198 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/ui */ "./src/util/ui.js"); 199 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/export */ "./src/util/export.js"); 200 | /* harmony import */ var _util_sketch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/sketch */ "./src/util/sketch.js"); 201 | /* harmony import */ var _export_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export-components */ "./src/export/export-components.js"); 202 | 203 | 204 | 205 | 206 | /* harmony default export */ __webpack_exports__["default"] = (function (context, opts, cb) { 207 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSettingsDialog(context, opts, _export_components__WEBPACK_IMPORTED_MODULE_3__["default"], function (data) { 208 | // Defaults 209 | data.propertyNamingConvention = data.propertyNamingConvention || 'Numeric'; 210 | data.cssUnit = data.cssUnit === 'No unit' ? 0 : data.cssUnit; // First store the properties we should exclude 211 | 212 | var excludeProps = []; 213 | 214 | if (data['excludeProps']['Color']) { 215 | excludeProps.push('color'); 216 | } 217 | 218 | if (data['excludeProps']['Line height']) { 219 | excludeProps.push('lineHeight'); 220 | } // Get the text styles from the Sketch document 221 | 222 | 223 | var textStyles = _util_sketch__WEBPACK_IMPORTED_MODULE_2__["default"].getTextStyles(context); 224 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].sortTextStyles(textStyles); 225 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].excludeTextStyleProperties(textStyles, excludeProps); 226 | 227 | if (data['merge']) { 228 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].removeDoubleTextStyles(textStyles); 229 | } 230 | 231 | cb(textStyles, data); 232 | }); 233 | }); 234 | ; 235 | 236 | /***/ }), 237 | 238 | /***/ "./src/util/export.js": 239 | /*!****************************!*\ 240 | !*** ./src/util/export.js ***! 241 | \****************************/ 242 | /*! exports provided: default */ 243 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 244 | 245 | "use strict"; 246 | __webpack_require__.r(__webpack_exports__); 247 | /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ "./src/util/util.js"); 248 | /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ "./src/util/number.js"); 249 | 250 | 251 | 252 | 253 | var exportUtils = { 254 | sortTextStyles: function sortTextStyles(textStyles) { 255 | // Sort text styles by size 256 | textStyles.sort(function (a, b) { 257 | return a.fontSize - b.fontSize; 258 | }); 259 | return textStyles; 260 | }, 261 | excludeTextStyleProperties: function excludeTextStyleProperties(textStyles) { 262 | var excludedProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 263 | textStyles.forEach(function (textStyle) { 264 | excludedProps.forEach(function (prop) { 265 | if (textStyle[prop]) { 266 | delete textStyle[prop]; 267 | } 268 | }); 269 | }); 270 | return textStyles; 271 | }, 272 | removeDoubleTextStyles: function removeDoubleTextStyles(textStyles) { 273 | var uniqueTextStyles = {}; 274 | var filtered = []; 275 | textStyles.forEach(function (textStyle, i) { 276 | var id = _util__WEBPACK_IMPORTED_MODULE_0__["default"].createTextStyleId(textStyle); 277 | 278 | if (!uniqueTextStyles[id]) { 279 | uniqueTextStyles[id] = true; 280 | filtered.push(textStyle); 281 | } 282 | }); 283 | return filtered; 284 | }, 285 | createCssProps: function createCssProps(textStyle) { 286 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 287 | opts.cssUnit = opts.cssUnit || 0; 288 | opts.scalingFactor = opts.scalingFactor || 1; 289 | opts.maxDecimalPlaces = opts.maxDecimalPlaces || 2; 290 | var cssProps = {}; 291 | cssProps['font-family'] = textStyle.fontFamily; 292 | cssProps['font-weight'] = 400; 293 | cssProps['text-transform'] = 'none'; 294 | var fontParts = textStyle.fontFamily.split('-'); 295 | var fontWeightMap = { 296 | 'Thin': 100, 297 | 'Light': 300, 298 | 'Regular': 400, 299 | 'Medium': 500, 300 | 'Bold': 700, 301 | 'Black': 900 302 | }; 303 | 304 | if (fontParts[1] && fontWeightMap[fontParts[1]]) { 305 | cssProps['font-family'] = fontParts[0]; 306 | cssProps['font-weight'] = fontWeightMap[fontParts[1]]; 307 | } 308 | 309 | cssProps['font-size'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.fontSize, opts.maxDecimalPlaces) + opts.cssUnit; 310 | cssProps['letter-spacing'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.letterSpacing, opts.maxDecimalPlaces) + opts.cssUnit; 311 | 312 | if (textStyle.textTransform === 1) { 313 | cssProps['text-transform'] = 'uppercase'; 314 | } 315 | 316 | if (textStyle.textTransform === 2) { 317 | cssProps['text-transform'] = 'lowercase'; 318 | } 319 | 320 | if (textStyle.lineHeight) { 321 | cssProps['line-height'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(1 + (textStyle.lineHeight - textStyle.fontSize) / textStyle.lineHeight, opts.maxDecimalPlaces); 322 | } 323 | 324 | if (textStyle.color) { 325 | cssProps['color'] = exportUtils.createRgbaString(textStyle.color); 326 | } 327 | 328 | return cssProps; 329 | }, 330 | createRgbaString: function createRgbaString(colorObj) { 331 | return 'rgba(' + exportUtils.createColorValue(colorObj.r) + ', ' + exportUtils.createColorValue(colorObj.g) + ', ' + exportUtils.createColorValue(colorObj.b) + ', ' + colorObj.a + ')'; 332 | }, 333 | createColorValue: function createColorValue(normalizedValue) { 334 | return Math.round(normalizedValue * 255); 335 | }, 336 | createStyleBlock: function createStyleBlock(cssProps) { 337 | var output = ''; 338 | 339 | for (var prop in cssProps) { 340 | output += "\t" + prop + ': ' + cssProps[prop] + ';' + "\n"; 341 | } 342 | 343 | return output; 344 | }, 345 | createInlineStyleString: function createInlineStyleString(cssProps) { 346 | var styleString = ''; 347 | 348 | for (var prop in cssProps) { 349 | styleString += prop + ': ' + cssProps[prop] + '; '; 350 | } 351 | 352 | return styleString; 353 | }, 354 | createHtmlFontbook: function createHtmlFontbook(textStyles) { 355 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 356 | var output = "\n \n \n \n \n Typex text styles\n \n \n "; 357 | textStyles.forEach(function (textStyle, i) { 358 | var cssProps = exportUtils.createCssProps(textStyle, opts); 359 | var inlineStyleString = exportUtils.createInlineStyleString(cssProps); 360 | var cssPropsBlock = exportUtils.createStyleBlock(cssProps); 361 | var textStyleName; 362 | 363 | if (opts.namingConvention === 'Numeric') { 364 | textStyleName = opts.namingPrefix + ' ' + (i + 1); 365 | } else if (opts.namingConvention === 'Text style name') { 366 | textStyleName = opts.namingPrefix + ' ' + textStyle.name; 367 | } else { 368 | textStyleName = opts.namingPrefix + ' ' + (i + 1) + ' (' + textStyle.name + ')'; 369 | } 370 | 371 | output += "\n
\n
\n ".concat(i + 1, ".\n \n ").concat(textStyleName, "\n \n
\n
\n
\n The quick brown fox jumps over the lazy dog\n
\n
\n \n
\n
\n
\n "); 372 | }); 373 | output += "\n \n \n "; 374 | return output; 375 | } 376 | }; 377 | /* harmony default export */ __webpack_exports__["default"] = (exportUtils); 378 | 379 | /***/ }), 380 | 381 | /***/ "./src/util/number.js": 382 | /*!****************************!*\ 383 | !*** ./src/util/number.js ***! 384 | \****************************/ 385 | /*! exports provided: default */ 386 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 387 | 388 | "use strict"; 389 | __webpack_require__.r(__webpack_exports__); 390 | 391 | 392 | var number = { 393 | parseFloatMaxDecimal: function parseFloatMaxDecimal(number, maxDecimalPlaces) { 394 | return Number(number.toFixed(maxDecimalPlaces).replace(/[.,]00$/, '')); 395 | } 396 | }; 397 | /* harmony default export */ __webpack_exports__["default"] = (number); 398 | 399 | /***/ }), 400 | 401 | /***/ "./src/util/sketch.js": 402 | /*!****************************!*\ 403 | !*** ./src/util/sketch.js ***! 404 | \****************************/ 405 | /*! exports provided: default */ 406 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 407 | 408 | "use strict"; 409 | __webpack_require__.r(__webpack_exports__); 410 | 411 | 412 | var sketch = { 413 | getTextStyles: function getTextStyles(context) { 414 | var texts = context.document.documentData().layerTextStyles().objects(); 415 | var rawTextStyles = []; 416 | texts.forEach(function (text, i) { 417 | rawTextStyles.push({ 418 | attributes: text.style().textStyle().attributes(), 419 | textStyle: text, 420 | name: text.name() 421 | }); 422 | }); 423 | var textStyles = []; 424 | rawTextStyles.forEach(function (rawTextStyle) { 425 | var textStyle = {}; 426 | textStyle.name = rawTextStyle.name; 427 | textStyle.fontFamily = String(rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontNameAttribute)); 428 | textStyle.fontSize = rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontSizeAttribute); 429 | textStyle.paragraph = rawTextStyle.attributes.NSParagraphStyle; 430 | 431 | if (textStyle.paragraph) { 432 | textStyle.lineHeight = textStyle.paragraph.maximumLineHeight(); 433 | } 434 | 435 | var color = rawTextStyle.attributes.MSAttributedStringColorAttribute; 436 | 437 | if (color) { 438 | var r = color.red(); 439 | var g = color.green(); 440 | var b = color.blue(); 441 | var a = color.alpha(); 442 | textStyle.color = { 443 | r: r, 444 | g: g, 445 | b: b, 446 | a: a 447 | }; 448 | } 449 | 450 | textStyle.letterSpacing = rawTextStyle.attributes.NSKern || 0; 451 | textStyle.textTransform = parseInt(rawTextStyle.attributes.MSAttributedStringTextTransformAttribute || 0); // @TODO strikethrough & underline, or is this not needed? 452 | 453 | textStyles.push(textStyle); 454 | }); 455 | return textStyles; 456 | } 457 | }; 458 | /* harmony default export */ __webpack_exports__["default"] = (sketch); 459 | 460 | /***/ }), 461 | 462 | /***/ "./src/util/string.js": 463 | /*!****************************!*\ 464 | !*** ./src/util/string.js ***! 465 | \****************************/ 466 | /*! exports provided: default */ 467 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 468 | 469 | "use strict"; 470 | __webpack_require__.r(__webpack_exports__); 471 | 472 | 473 | var string = { 474 | slugify: function slugify(str) { 475 | str = str.replace(/^\s+|\s+$/g, ''); // trim 476 | 477 | str = str.toLowerCase(); // remove accents, swap ñ for n, etc 478 | 479 | var from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;'; 480 | var to = 'aaaaeeeeiiiioooouuuunc------'; 481 | 482 | for (var i = 0, l = from.length; i < l; i++) { 483 | str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); 484 | } 485 | 486 | str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars 487 | .replace(/\s+/g, '-') // collapse whitespace and replace by - 488 | .replace(/-+/g, '-') // collapse dashes 489 | ; 490 | return str; 491 | } 492 | }; 493 | /* harmony default export */ __webpack_exports__["default"] = (string); 494 | 495 | /***/ }), 496 | 497 | /***/ "./src/util/ui.js": 498 | /*!************************!*\ 499 | !*** ./src/util/ui.js ***! 500 | \************************/ 501 | /*! exports provided: default */ 502 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 503 | 504 | "use strict"; 505 | __webpack_require__.r(__webpack_exports__); 506 | 507 | 508 | var ui = { 509 | createSavePanel: function createSavePanel(defaultFileName, contents) { 510 | var save = NSSavePanel.savePanel(); 511 | save.setNameFieldStringValue(defaultFileName); 512 | save.setAllowsOtherFileTypes(false); 513 | save.setExtensionHidden(false); 514 | 515 | if (save.runModal()) { 516 | var file = NSString.stringWithString(contents); 517 | var path = save.URL().path(); 518 | file.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, null); 519 | } 520 | }, 521 | createLabel: function createLabel() { 522 | var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 523 | var label = NSTextField.alloc().init(); 524 | label.setStringValue(text); 525 | label.setFont(NSFont.boldSystemFontOfSize(12)); 526 | label.setBezeled(false); 527 | label.setDrawsBackground(false); 528 | label.setEditable(false); 529 | label.setSelectable(false); 530 | return label; 531 | }, 532 | createTextField: function createTextField(value) { 533 | var field = NSTextField.alloc().init(); 534 | field.setStringValue(value); 535 | return field; 536 | }, 537 | createSelect: function createSelect(options) { 538 | var comboBox = NSPopUpButton.alloc().init(); 539 | comboBox.addItemsWithTitles(options); 540 | comboBox.selectItemAtIndex(0); 541 | return comboBox; 542 | }, 543 | createStepper: function createStepper(value) { 544 | var stepper = NSStepper.alloc().init(); 545 | return stepper; 546 | }, 547 | createCheckbox: function createCheckbox(title) { 548 | var checkbox = NSButton.alloc().init(); 549 | checkbox.setButtonType(NSSwitchButton); 550 | checkbox.title = title; 551 | return checkbox; 552 | }, 553 | createSettingsDialog: function createSettingsDialog(context) { 554 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 555 | var components = arguments.length > 2 ? arguments[2] : undefined; 556 | var cb = arguments.length > 3 ? arguments[3] : undefined; 557 | opts.title = opts.title || 'Alert'; 558 | opts.informativeText = opts.informativeText || ''; 559 | opts.cancelBtnText = opts.cancelBtnText || 'Cancel'; 560 | opts.confirmBtnText = opts.confirmBtnText || 'Ok'; 561 | var dialog = NSAlert.alloc().init(); 562 | var dialogIconPath = context.plugin.urlForResourceNamed('icon.png').path(); 563 | var dialogIcon = NSImage.alloc().initByReferencingFile(dialogIconPath); 564 | dialog.setIcon(dialogIcon); 565 | dialog.setMessageText(opts.title); 566 | dialog.setInformativeText(opts.informativeText); 567 | var btnConfirm = dialog.addButtonWithTitle(opts.confirmBtnText); 568 | var btnCancel = dialog.addButtonWithTitle(opts.cancelBtnText); // Create grid view 569 | 570 | var gridView = NSGridView.alloc().init(); // Create object to hold all inputs 571 | 572 | var inputs = {}; 573 | var height = 0; 574 | var rowSpacing = 8; // Loop each component 575 | 576 | components.forEach(function (c) { 577 | var label, field; 578 | 579 | switch (c.type) { 580 | case 'text': 581 | label = ui.createLabel(c.label); 582 | field = ui.createTextField(c.value); 583 | height += 22 + rowSpacing; 584 | gridView.addRowWithViews([label, field]); 585 | break; 586 | 587 | case 'stepper': 588 | label = ui.createLabel(c.label); 589 | field = ui.createStepper(c.value); 590 | height += 22 + rowSpacing; 591 | gridView.addRowWithViews([label, field]); 592 | break; 593 | 594 | case 'checkbox': 595 | label = ui.createLabel(c.label); 596 | field = ui.createCheckbox(c.value); 597 | height += 22 + rowSpacing; 598 | gridView.addRowWithViews([label, field]); 599 | break; 600 | 601 | case 'multicheckbox': 602 | field = []; 603 | c.values.forEach(function (v, i) { 604 | label = i ? ui.createLabel() : ui.createLabel(c.label); 605 | var checkbox = ui.createCheckbox(v); 606 | height += 22 + rowSpacing; 607 | field.push(checkbox); 608 | gridView.addRowWithViews([label, checkbox]); 609 | }); 610 | break; 611 | 612 | case 'select': 613 | label = ui.createLabel(c.label); 614 | field = ui.createSelect(c.options); 615 | height += 28 + rowSpacing; 616 | gridView.addRowWithViews([label, field]); 617 | break; 618 | } 619 | 620 | inputs[c.id] = field; 621 | }); // Set grid view as view of dialog 622 | 623 | dialog.accessoryView = gridView; 624 | gridView.columnSpacing = 30; 625 | gridView.rowSpacing = rowSpacing; 626 | gridView.frame = NSMakeRect(0, 0, 400, height); // Open the dialog and store the response code 627 | 628 | var responseCode = dialog.runModal(); // The dialog is being 'submitted' 629 | 630 | if (responseCode === 1000) { 631 | var data = {}; 632 | components.forEach(function (c) { 633 | switch (c.type) { 634 | case 'text': 635 | data[c.id] = inputs[c.id].stringValue(); 636 | break; 637 | 638 | case 'select': 639 | data[c.id] = c.options[inputs[c.id].indexOfSelectedItem()]; 640 | break; 641 | 642 | case 'checkbox': 643 | data[c.id] = inputs[c.id].state() === 1; 644 | break; 645 | 646 | case 'multicheckbox': 647 | var values = {}; 648 | c.values.forEach(function (v, i) { 649 | values[v] = inputs[c.id][i].state() === 1; 650 | }); 651 | data[c.id] = values; 652 | } 653 | }); 654 | cb(data); 655 | return; 656 | } 657 | 658 | return dialog; 659 | } 660 | }; 661 | /* harmony default export */ __webpack_exports__["default"] = (ui); 662 | 663 | /***/ }), 664 | 665 | /***/ "./src/util/util.js": 666 | /*!**************************!*\ 667 | !*** ./src/util/util.js ***! 668 | \**************************/ 669 | /*! exports provided: default */ 670 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 671 | 672 | "use strict"; 673 | __webpack_require__.r(__webpack_exports__); 674 | 675 | 676 | var util = { 677 | createTextStyleId: function createTextStyleId(textStyle) { 678 | var textStyleId = ''; // Make sure this id incorporates every possible property of the text style 679 | 680 | textStyleId += textStyle.fontFamily; 681 | textStyleId += '-' + textStyle.fontSize; 682 | textStyleId += '-' + textStyle.letterSpacing; 683 | textStyleId += '-' + textStyle.textTransform; 684 | 685 | if (textStyle.lineHeight) { 686 | textStyleId += '-' + textStyle.lineHeight; 687 | } 688 | 689 | if (textStyle.color) { 690 | textStyleId += '-' + textStyle.color.r + '-' + textStyle.color.g + '-' + textStyle.color.b + '-' + textStyle.color.a; 691 | } 692 | 693 | return textStyleId; 694 | } 695 | }; 696 | /* harmony default export */ __webpack_exports__["default"] = (util); 697 | 698 | /***/ }) 699 | 700 | /******/ }); 701 | if (key === 'default' && typeof exports === 'function') { 702 | exports(context); 703 | } else { 704 | exports[key](context); 705 | } 706 | } 707 | that['onRun'] = __skpm_run.bind(this, 'default') 708 | 709 | //# sourceMappingURL=css-export.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/custom-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/custom-export.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/custom-export.js": 95 | /*!******************************!*\ 96 | !*** ./src/custom-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 _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util/ui */ "./src/util/ui.js"); 104 | /* harmony import */ var _export_open_export_dialog__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./export/open-export-dialog */ "./src/export/open-export-dialog.js"); 105 | 106 | 107 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 108 | Object(_export_open_export_dialog__WEBPACK_IMPORTED_MODULE_1__["default"])(context, { 109 | title: 'Custom script export', 110 | informativeText: 'Export each text style by using a custom export script in Javascript' 111 | }, function (textStyles, data) { 112 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSettingsDialog(context, { 113 | title: 'Custom export script', 114 | informativeText: 'Customize your export by using Javascript' 115 | }, [{ 116 | type: 'text', 117 | id: 'customScript', 118 | value: 'console.log(output)', 119 | label: 'Custom Javascript export' 120 | }], function (customData) {// @TODO 121 | }); 122 | }); 123 | }); 124 | ; 125 | 126 | /***/ }), 127 | 128 | /***/ "./src/export/export-components.js": 129 | /*!*****************************************!*\ 130 | !*** ./src/export/export-components.js ***! 131 | \*****************************************/ 132 | /*! exports provided: default */ 133 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 134 | 135 | "use strict"; 136 | __webpack_require__.r(__webpack_exports__); 137 | /* harmony default export */ __webpack_exports__["default"] = ([{ 138 | type: 'checkbox', 139 | id: 'merge', 140 | label: 'Merge', 141 | value: 'Merge identical styles' 142 | }, { 143 | type: 'multicheckbox', 144 | id: 'excludeProps', 145 | label: 'Exclude properties', 146 | values: ['Color', 'Line height'] 147 | }, { 148 | type: 'select', 149 | id: 'cssUnit', 150 | options: ['px', 'em', 'rem', '%', 'vh', 'vw', 'No unit'], 151 | label: 'CSS unit' 152 | }, { 153 | type: 'text', 154 | id: 'scalingFactor', 155 | value: 1, 156 | label: 'Size scaling factor' 157 | }, { 158 | type: 'text', 159 | id: 'maxDecimalPlaces', 160 | value: 2, 161 | label: 'Maximal decimal places' 162 | }, { 163 | type: 'text', 164 | id: 'namingPrefix', 165 | value: 'type', 166 | label: 'Naming prefix' 167 | }, { 168 | type: 'select', 169 | id: 'namingConvention', 170 | options: ['Numeric', 'Text style name'], 171 | label: 'Naming convention' 172 | }]); 173 | 174 | /***/ }), 175 | 176 | /***/ "./src/export/open-export-dialog.js": 177 | /*!******************************************!*\ 178 | !*** ./src/export/open-export-dialog.js ***! 179 | \******************************************/ 180 | /*! exports provided: default */ 181 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 182 | 183 | "use strict"; 184 | __webpack_require__.r(__webpack_exports__); 185 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/ui */ "./src/util/ui.js"); 186 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/export */ "./src/util/export.js"); 187 | /* harmony import */ var _util_sketch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/sketch */ "./src/util/sketch.js"); 188 | /* harmony import */ var _export_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export-components */ "./src/export/export-components.js"); 189 | 190 | 191 | 192 | 193 | /* harmony default export */ __webpack_exports__["default"] = (function (context, opts, cb) { 194 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSettingsDialog(context, opts, _export_components__WEBPACK_IMPORTED_MODULE_3__["default"], function (data) { 195 | // Defaults 196 | data.propertyNamingConvention = data.propertyNamingConvention || 'Numeric'; 197 | data.cssUnit = data.cssUnit === 'No unit' ? 0 : data.cssUnit; // First store the properties we should exclude 198 | 199 | var excludeProps = []; 200 | 201 | if (data['excludeProps']['Color']) { 202 | excludeProps.push('color'); 203 | } 204 | 205 | if (data['excludeProps']['Line height']) { 206 | excludeProps.push('lineHeight'); 207 | } // Get the text styles from the Sketch document 208 | 209 | 210 | var textStyles = _util_sketch__WEBPACK_IMPORTED_MODULE_2__["default"].getTextStyles(context); 211 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].sortTextStyles(textStyles); 212 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].excludeTextStyleProperties(textStyles, excludeProps); 213 | 214 | if (data['merge']) { 215 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].removeDoubleTextStyles(textStyles); 216 | } 217 | 218 | cb(textStyles, data); 219 | }); 220 | }); 221 | ; 222 | 223 | /***/ }), 224 | 225 | /***/ "./src/util/export.js": 226 | /*!****************************!*\ 227 | !*** ./src/util/export.js ***! 228 | \****************************/ 229 | /*! exports provided: default */ 230 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 231 | 232 | "use strict"; 233 | __webpack_require__.r(__webpack_exports__); 234 | /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ "./src/util/util.js"); 235 | /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ "./src/util/number.js"); 236 | 237 | 238 | 239 | 240 | var exportUtils = { 241 | sortTextStyles: function sortTextStyles(textStyles) { 242 | // Sort text styles by size 243 | textStyles.sort(function (a, b) { 244 | return a.fontSize - b.fontSize; 245 | }); 246 | return textStyles; 247 | }, 248 | excludeTextStyleProperties: function excludeTextStyleProperties(textStyles) { 249 | var excludedProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 250 | textStyles.forEach(function (textStyle) { 251 | excludedProps.forEach(function (prop) { 252 | if (textStyle[prop]) { 253 | delete textStyle[prop]; 254 | } 255 | }); 256 | }); 257 | return textStyles; 258 | }, 259 | removeDoubleTextStyles: function removeDoubleTextStyles(textStyles) { 260 | var uniqueTextStyles = {}; 261 | var filtered = []; 262 | textStyles.forEach(function (textStyle, i) { 263 | var id = _util__WEBPACK_IMPORTED_MODULE_0__["default"].createTextStyleId(textStyle); 264 | 265 | if (!uniqueTextStyles[id]) { 266 | uniqueTextStyles[id] = true; 267 | filtered.push(textStyle); 268 | } 269 | }); 270 | return filtered; 271 | }, 272 | createCssProps: function createCssProps(textStyle) { 273 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 274 | opts.cssUnit = opts.cssUnit || 0; 275 | opts.scalingFactor = opts.scalingFactor || 1; 276 | opts.maxDecimalPlaces = opts.maxDecimalPlaces || 2; 277 | var cssProps = {}; 278 | cssProps['font-family'] = textStyle.fontFamily; 279 | cssProps['font-weight'] = 400; 280 | cssProps['text-transform'] = 'none'; 281 | var fontParts = textStyle.fontFamily.split('-'); 282 | var fontWeightMap = { 283 | 'Thin': 100, 284 | 'Light': 300, 285 | 'Regular': 400, 286 | 'Medium': 500, 287 | 'Bold': 700, 288 | 'Black': 900 289 | }; 290 | 291 | if (fontParts[1] && fontWeightMap[fontParts[1]]) { 292 | cssProps['font-family'] = fontParts[0]; 293 | cssProps['font-weight'] = fontWeightMap[fontParts[1]]; 294 | } 295 | 296 | cssProps['font-size'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.fontSize, opts.maxDecimalPlaces) + opts.cssUnit; 297 | cssProps['letter-spacing'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.letterSpacing, opts.maxDecimalPlaces) + opts.cssUnit; 298 | 299 | if (textStyle.textTransform === 1) { 300 | cssProps['text-transform'] = 'uppercase'; 301 | } 302 | 303 | if (textStyle.textTransform === 2) { 304 | cssProps['text-transform'] = 'lowercase'; 305 | } 306 | 307 | if (textStyle.lineHeight) { 308 | cssProps['line-height'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(1 + (textStyle.lineHeight - textStyle.fontSize) / textStyle.lineHeight, opts.maxDecimalPlaces); 309 | } 310 | 311 | if (textStyle.color) { 312 | cssProps['color'] = exportUtils.createRgbaString(textStyle.color); 313 | } 314 | 315 | return cssProps; 316 | }, 317 | createRgbaString: function createRgbaString(colorObj) { 318 | return 'rgba(' + exportUtils.createColorValue(colorObj.r) + ', ' + exportUtils.createColorValue(colorObj.g) + ', ' + exportUtils.createColorValue(colorObj.b) + ', ' + colorObj.a + ')'; 319 | }, 320 | createColorValue: function createColorValue(normalizedValue) { 321 | return Math.round(normalizedValue * 255); 322 | }, 323 | createStyleBlock: function createStyleBlock(cssProps) { 324 | var output = ''; 325 | 326 | for (var prop in cssProps) { 327 | output += "\t" + prop + ': ' + cssProps[prop] + ';' + "\n"; 328 | } 329 | 330 | return output; 331 | }, 332 | createInlineStyleString: function createInlineStyleString(cssProps) { 333 | var styleString = ''; 334 | 335 | for (var prop in cssProps) { 336 | styleString += prop + ': ' + cssProps[prop] + '; '; 337 | } 338 | 339 | return styleString; 340 | }, 341 | createHtmlFontbook: function createHtmlFontbook(textStyles) { 342 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 343 | var output = "\n \n \n \n \n Typex text styles\n \n \n "; 344 | textStyles.forEach(function (textStyle, i) { 345 | var cssProps = exportUtils.createCssProps(textStyle, opts); 346 | var inlineStyleString = exportUtils.createInlineStyleString(cssProps); 347 | var cssPropsBlock = exportUtils.createStyleBlock(cssProps); 348 | var textStyleName; 349 | 350 | if (opts.namingConvention === 'Numeric') { 351 | textStyleName = opts.namingPrefix + ' ' + (i + 1); 352 | } else if (opts.namingConvention === 'Text style name') { 353 | textStyleName = opts.namingPrefix + ' ' + textStyle.name; 354 | } else { 355 | textStyleName = opts.namingPrefix + ' ' + (i + 1) + ' (' + textStyle.name + ')'; 356 | } 357 | 358 | output += "\n
\n
\n ".concat(i + 1, ".\n \n ").concat(textStyleName, "\n \n
\n
\n
\n The quick brown fox jumps over the lazy dog\n
\n
\n \n
\n
\n
\n "); 359 | }); 360 | output += "\n \n \n "; 361 | return output; 362 | } 363 | }; 364 | /* harmony default export */ __webpack_exports__["default"] = (exportUtils); 365 | 366 | /***/ }), 367 | 368 | /***/ "./src/util/number.js": 369 | /*!****************************!*\ 370 | !*** ./src/util/number.js ***! 371 | \****************************/ 372 | /*! exports provided: default */ 373 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 374 | 375 | "use strict"; 376 | __webpack_require__.r(__webpack_exports__); 377 | 378 | 379 | var number = { 380 | parseFloatMaxDecimal: function parseFloatMaxDecimal(number, maxDecimalPlaces) { 381 | return Number(number.toFixed(maxDecimalPlaces).replace(/[.,]00$/, '')); 382 | } 383 | }; 384 | /* harmony default export */ __webpack_exports__["default"] = (number); 385 | 386 | /***/ }), 387 | 388 | /***/ "./src/util/sketch.js": 389 | /*!****************************!*\ 390 | !*** ./src/util/sketch.js ***! 391 | \****************************/ 392 | /*! exports provided: default */ 393 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 394 | 395 | "use strict"; 396 | __webpack_require__.r(__webpack_exports__); 397 | 398 | 399 | var sketch = { 400 | getTextStyles: function getTextStyles(context) { 401 | var texts = context.document.documentData().layerTextStyles().objects(); 402 | var rawTextStyles = []; 403 | texts.forEach(function (text, i) { 404 | rawTextStyles.push({ 405 | attributes: text.style().textStyle().attributes(), 406 | textStyle: text, 407 | name: text.name() 408 | }); 409 | }); 410 | var textStyles = []; 411 | rawTextStyles.forEach(function (rawTextStyle) { 412 | var textStyle = {}; 413 | textStyle.name = rawTextStyle.name; 414 | textStyle.fontFamily = String(rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontNameAttribute)); 415 | textStyle.fontSize = rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontSizeAttribute); 416 | textStyle.paragraph = rawTextStyle.attributes.NSParagraphStyle; 417 | 418 | if (textStyle.paragraph) { 419 | textStyle.lineHeight = textStyle.paragraph.maximumLineHeight(); 420 | } 421 | 422 | var color = rawTextStyle.attributes.MSAttributedStringColorAttribute; 423 | 424 | if (color) { 425 | var r = color.red(); 426 | var g = color.green(); 427 | var b = color.blue(); 428 | var a = color.alpha(); 429 | textStyle.color = { 430 | r: r, 431 | g: g, 432 | b: b, 433 | a: a 434 | }; 435 | } 436 | 437 | textStyle.letterSpacing = rawTextStyle.attributes.NSKern || 0; 438 | textStyle.textTransform = parseInt(rawTextStyle.attributes.MSAttributedStringTextTransformAttribute || 0); // @TODO strikethrough & underline, or is this not needed? 439 | 440 | textStyles.push(textStyle); 441 | }); 442 | return textStyles; 443 | } 444 | }; 445 | /* harmony default export */ __webpack_exports__["default"] = (sketch); 446 | 447 | /***/ }), 448 | 449 | /***/ "./src/util/ui.js": 450 | /*!************************!*\ 451 | !*** ./src/util/ui.js ***! 452 | \************************/ 453 | /*! exports provided: default */ 454 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 455 | 456 | "use strict"; 457 | __webpack_require__.r(__webpack_exports__); 458 | 459 | 460 | var ui = { 461 | createSavePanel: function createSavePanel(defaultFileName, contents) { 462 | var save = NSSavePanel.savePanel(); 463 | save.setNameFieldStringValue(defaultFileName); 464 | save.setAllowsOtherFileTypes(false); 465 | save.setExtensionHidden(false); 466 | 467 | if (save.runModal()) { 468 | var file = NSString.stringWithString(contents); 469 | var path = save.URL().path(); 470 | file.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, null); 471 | } 472 | }, 473 | createLabel: function createLabel() { 474 | var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 475 | var label = NSTextField.alloc().init(); 476 | label.setStringValue(text); 477 | label.setFont(NSFont.boldSystemFontOfSize(12)); 478 | label.setBezeled(false); 479 | label.setDrawsBackground(false); 480 | label.setEditable(false); 481 | label.setSelectable(false); 482 | return label; 483 | }, 484 | createTextField: function createTextField(value) { 485 | var field = NSTextField.alloc().init(); 486 | field.setStringValue(value); 487 | return field; 488 | }, 489 | createSelect: function createSelect(options) { 490 | var comboBox = NSPopUpButton.alloc().init(); 491 | comboBox.addItemsWithTitles(options); 492 | comboBox.selectItemAtIndex(0); 493 | return comboBox; 494 | }, 495 | createStepper: function createStepper(value) { 496 | var stepper = NSStepper.alloc().init(); 497 | return stepper; 498 | }, 499 | createCheckbox: function createCheckbox(title) { 500 | var checkbox = NSButton.alloc().init(); 501 | checkbox.setButtonType(NSSwitchButton); 502 | checkbox.title = title; 503 | return checkbox; 504 | }, 505 | createSettingsDialog: function createSettingsDialog(context) { 506 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 507 | var components = arguments.length > 2 ? arguments[2] : undefined; 508 | var cb = arguments.length > 3 ? arguments[3] : undefined; 509 | opts.title = opts.title || 'Alert'; 510 | opts.informativeText = opts.informativeText || ''; 511 | opts.cancelBtnText = opts.cancelBtnText || 'Cancel'; 512 | opts.confirmBtnText = opts.confirmBtnText || 'Ok'; 513 | var dialog = NSAlert.alloc().init(); 514 | var dialogIconPath = context.plugin.urlForResourceNamed('icon.png').path(); 515 | var dialogIcon = NSImage.alloc().initByReferencingFile(dialogIconPath); 516 | dialog.setIcon(dialogIcon); 517 | dialog.setMessageText(opts.title); 518 | dialog.setInformativeText(opts.informativeText); 519 | var btnConfirm = dialog.addButtonWithTitle(opts.confirmBtnText); 520 | var btnCancel = dialog.addButtonWithTitle(opts.cancelBtnText); // Create grid view 521 | 522 | var gridView = NSGridView.alloc().init(); // Create object to hold all inputs 523 | 524 | var inputs = {}; 525 | var height = 0; 526 | var rowSpacing = 8; // Loop each component 527 | 528 | components.forEach(function (c) { 529 | var label, field; 530 | 531 | switch (c.type) { 532 | case 'text': 533 | label = ui.createLabel(c.label); 534 | field = ui.createTextField(c.value); 535 | height += 22 + rowSpacing; 536 | gridView.addRowWithViews([label, field]); 537 | break; 538 | 539 | case 'stepper': 540 | label = ui.createLabel(c.label); 541 | field = ui.createStepper(c.value); 542 | height += 22 + rowSpacing; 543 | gridView.addRowWithViews([label, field]); 544 | break; 545 | 546 | case 'checkbox': 547 | label = ui.createLabel(c.label); 548 | field = ui.createCheckbox(c.value); 549 | height += 22 + rowSpacing; 550 | gridView.addRowWithViews([label, field]); 551 | break; 552 | 553 | case 'multicheckbox': 554 | field = []; 555 | c.values.forEach(function (v, i) { 556 | label = i ? ui.createLabel() : ui.createLabel(c.label); 557 | var checkbox = ui.createCheckbox(v); 558 | height += 22 + rowSpacing; 559 | field.push(checkbox); 560 | gridView.addRowWithViews([label, checkbox]); 561 | }); 562 | break; 563 | 564 | case 'select': 565 | label = ui.createLabel(c.label); 566 | field = ui.createSelect(c.options); 567 | height += 28 + rowSpacing; 568 | gridView.addRowWithViews([label, field]); 569 | break; 570 | } 571 | 572 | inputs[c.id] = field; 573 | }); // Set grid view as view of dialog 574 | 575 | dialog.accessoryView = gridView; 576 | gridView.columnSpacing = 30; 577 | gridView.rowSpacing = rowSpacing; 578 | gridView.frame = NSMakeRect(0, 0, 400, height); // Open the dialog and store the response code 579 | 580 | var responseCode = dialog.runModal(); // The dialog is being 'submitted' 581 | 582 | if (responseCode === 1000) { 583 | var data = {}; 584 | components.forEach(function (c) { 585 | switch (c.type) { 586 | case 'text': 587 | data[c.id] = inputs[c.id].stringValue(); 588 | break; 589 | 590 | case 'select': 591 | data[c.id] = c.options[inputs[c.id].indexOfSelectedItem()]; 592 | break; 593 | 594 | case 'checkbox': 595 | data[c.id] = inputs[c.id].state() === 1; 596 | break; 597 | 598 | case 'multicheckbox': 599 | var values = {}; 600 | c.values.forEach(function (v, i) { 601 | values[v] = inputs[c.id][i].state() === 1; 602 | }); 603 | data[c.id] = values; 604 | } 605 | }); 606 | cb(data); 607 | return; 608 | } 609 | 610 | return dialog; 611 | } 612 | }; 613 | /* harmony default export */ __webpack_exports__["default"] = (ui); 614 | 615 | /***/ }), 616 | 617 | /***/ "./src/util/util.js": 618 | /*!**************************!*\ 619 | !*** ./src/util/util.js ***! 620 | \**************************/ 621 | /*! exports provided: default */ 622 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 623 | 624 | "use strict"; 625 | __webpack_require__.r(__webpack_exports__); 626 | 627 | 628 | var util = { 629 | createTextStyleId: function createTextStyleId(textStyle) { 630 | var textStyleId = ''; // Make sure this id incorporates every possible property of the text style 631 | 632 | textStyleId += textStyle.fontFamily; 633 | textStyleId += '-' + textStyle.fontSize; 634 | textStyleId += '-' + textStyle.letterSpacing; 635 | textStyleId += '-' + textStyle.textTransform; 636 | 637 | if (textStyle.lineHeight) { 638 | textStyleId += '-' + textStyle.lineHeight; 639 | } 640 | 641 | if (textStyle.color) { 642 | textStyleId += '-' + textStyle.color.r + '-' + textStyle.color.g + '-' + textStyle.color.b + '-' + textStyle.color.a; 643 | } 644 | 645 | return textStyleId; 646 | } 647 | }; 648 | /* harmony default export */ __webpack_exports__["default"] = (util); 649 | 650 | /***/ }) 651 | 652 | /******/ }); 653 | if (key === 'default' && typeof exports === 'function') { 654 | exports(context); 655 | } else { 656 | exports[key](context); 657 | } 658 | } 659 | that['onRun'] = __skpm_run.bind(this, 'default') 660 | 661 | //# sourceMappingURL=custom-export.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/html-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/html-export.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/html-export.js": 95 | /*!****************************!*\ 96 | !*** ./src/html-export.js ***! 97 | \****************************/ 98 | /*! exports provided: default */ 99 | /***/ (function(module, exports) { 100 | 101 | throw new Error("Module build failed (from ./node_modules/babel-loader/lib/index.js):\nError: ENOENT: no such file or directory, open '/Users/rein/Workspace/sketch-plugins/typex/src/html-export.js'"); 102 | 103 | /***/ }) 104 | 105 | /******/ }); 106 | if (key === 'default' && typeof exports === 'function') { 107 | exports(context); 108 | } else { 109 | exports[key](context); 110 | } 111 | } 112 | that['onRun'] = __skpm_run.bind(this, 'default') 113 | 114 | //# sourceMappingURL=html-export.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/html-export.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap"],"names":[],"mappings":";;;;;;AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA","file":"html-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/html-export.js\");\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/html-fontbook-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/html-fontbook-export.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/export/export-components.js": 95 | /*!*****************************************!*\ 96 | !*** ./src/export/export-components.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 default export */ __webpack_exports__["default"] = ([{ 104 | type: 'checkbox', 105 | id: 'merge', 106 | label: 'Merge', 107 | value: 'Merge identical styles' 108 | }, { 109 | type: 'multicheckbox', 110 | id: 'excludeProps', 111 | label: 'Exclude properties', 112 | values: ['Color', 'Line height'] 113 | }, { 114 | type: 'select', 115 | id: 'cssUnit', 116 | options: ['px', 'em', 'rem', '%', 'vh', 'vw', 'No unit'], 117 | label: 'CSS unit' 118 | }, { 119 | type: 'text', 120 | id: 'scalingFactor', 121 | value: 1, 122 | label: 'Size scaling factor' 123 | }, { 124 | type: 'text', 125 | id: 'maxDecimalPlaces', 126 | value: 2, 127 | label: 'Maximal decimal places' 128 | }, { 129 | type: 'text', 130 | id: 'namingPrefix', 131 | value: 'type', 132 | label: 'Naming prefix' 133 | }, { 134 | type: 'select', 135 | id: 'namingConvention', 136 | options: ['Numeric', 'Text style name'], 137 | label: 'Naming convention' 138 | }]); 139 | 140 | /***/ }), 141 | 142 | /***/ "./src/export/open-export-dialog.js": 143 | /*!******************************************!*\ 144 | !*** ./src/export/open-export-dialog.js ***! 145 | \******************************************/ 146 | /*! exports provided: default */ 147 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 148 | 149 | "use strict"; 150 | __webpack_require__.r(__webpack_exports__); 151 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/ui */ "./src/util/ui.js"); 152 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/export */ "./src/util/export.js"); 153 | /* harmony import */ var _util_sketch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/sketch */ "./src/util/sketch.js"); 154 | /* harmony import */ var _export_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export-components */ "./src/export/export-components.js"); 155 | 156 | 157 | 158 | 159 | /* harmony default export */ __webpack_exports__["default"] = (function (context, opts, cb) { 160 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSettingsDialog(context, opts, _export_components__WEBPACK_IMPORTED_MODULE_3__["default"], function (data) { 161 | // Defaults 162 | data.propertyNamingConvention = data.propertyNamingConvention || 'Numeric'; 163 | data.cssUnit = data.cssUnit === 'No unit' ? 0 : data.cssUnit; // First store the properties we should exclude 164 | 165 | var excludeProps = []; 166 | 167 | if (data['excludeProps']['Color']) { 168 | excludeProps.push('color'); 169 | } 170 | 171 | if (data['excludeProps']['Line height']) { 172 | excludeProps.push('lineHeight'); 173 | } // Get the text styles from the Sketch document 174 | 175 | 176 | var textStyles = _util_sketch__WEBPACK_IMPORTED_MODULE_2__["default"].getTextStyles(context); 177 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].sortTextStyles(textStyles); 178 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].excludeTextStyleProperties(textStyles, excludeProps); 179 | 180 | if (data['merge']) { 181 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].removeDoubleTextStyles(textStyles); 182 | } 183 | 184 | cb(textStyles, data); 185 | }); 186 | }); 187 | ; 188 | 189 | /***/ }), 190 | 191 | /***/ "./src/html-fontbook-export.js": 192 | /*!*************************************!*\ 193 | !*** ./src/html-fontbook-export.js ***! 194 | \*************************************/ 195 | /*! exports provided: default */ 196 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 197 | 198 | "use strict"; 199 | __webpack_require__.r(__webpack_exports__); 200 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util/ui */ "./src/util/ui.js"); 201 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util/export */ "./src/util/export.js"); 202 | /* harmony import */ var _export_open_export_dialog__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./export/open-export-dialog */ "./src/export/open-export-dialog.js"); 203 | 204 | 205 | 206 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 207 | Object(_export_open_export_dialog__WEBPACK_IMPORTED_MODULE_2__["default"])(context, { 208 | title: 'Create HTML fontbook', 209 | informativeText: 'Create a handy HTML fontbook from your text styles', 210 | confirmBtnText: 'Export HTML fontbook' 211 | }, function (textStyles, data) { 212 | // Create a HTML fontbook with these styles 213 | var html = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].createHtmlFontbook(textStyles, data); // Ask the user to save the file 214 | 215 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSavePanel('typex-fontbook.html', html); 216 | }); 217 | }); 218 | ; 219 | 220 | /***/ }), 221 | 222 | /***/ "./src/util/export.js": 223 | /*!****************************!*\ 224 | !*** ./src/util/export.js ***! 225 | \****************************/ 226 | /*! exports provided: default */ 227 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 228 | 229 | "use strict"; 230 | __webpack_require__.r(__webpack_exports__); 231 | /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ "./src/util/util.js"); 232 | /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ "./src/util/number.js"); 233 | 234 | 235 | 236 | 237 | var exportUtils = { 238 | sortTextStyles: function sortTextStyles(textStyles) { 239 | // Sort text styles by size 240 | textStyles.sort(function (a, b) { 241 | return a.fontSize - b.fontSize; 242 | }); 243 | return textStyles; 244 | }, 245 | excludeTextStyleProperties: function excludeTextStyleProperties(textStyles) { 246 | var excludedProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 247 | textStyles.forEach(function (textStyle) { 248 | excludedProps.forEach(function (prop) { 249 | if (textStyle[prop]) { 250 | delete textStyle[prop]; 251 | } 252 | }); 253 | }); 254 | return textStyles; 255 | }, 256 | removeDoubleTextStyles: function removeDoubleTextStyles(textStyles) { 257 | var uniqueTextStyles = {}; 258 | var filtered = []; 259 | textStyles.forEach(function (textStyle, i) { 260 | var id = _util__WEBPACK_IMPORTED_MODULE_0__["default"].createTextStyleId(textStyle); 261 | 262 | if (!uniqueTextStyles[id]) { 263 | uniqueTextStyles[id] = true; 264 | filtered.push(textStyle); 265 | } 266 | }); 267 | return filtered; 268 | }, 269 | createCssProps: function createCssProps(textStyle) { 270 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 271 | opts.cssUnit = opts.cssUnit || 0; 272 | opts.scalingFactor = opts.scalingFactor || 1; 273 | opts.maxDecimalPlaces = opts.maxDecimalPlaces || 2; 274 | var cssProps = {}; 275 | cssProps['font-family'] = textStyle.fontFamily; 276 | cssProps['font-weight'] = 400; 277 | cssProps['text-transform'] = 'none'; 278 | var fontParts = textStyle.fontFamily.split('-'); 279 | var fontWeightMap = { 280 | 'Thin': 100, 281 | 'Light': 300, 282 | 'Regular': 400, 283 | 'Medium': 500, 284 | 'Bold': 700, 285 | 'Black': 900 286 | }; 287 | 288 | if (fontParts[1] && fontWeightMap[fontParts[1]]) { 289 | cssProps['font-family'] = fontParts[0]; 290 | cssProps['font-weight'] = fontWeightMap[fontParts[1]]; 291 | } 292 | 293 | cssProps['font-size'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.fontSize, opts.maxDecimalPlaces) + opts.cssUnit; 294 | cssProps['letter-spacing'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.letterSpacing, opts.maxDecimalPlaces) + opts.cssUnit; 295 | 296 | if (textStyle.textTransform === 1) { 297 | cssProps['text-transform'] = 'uppercase'; 298 | } 299 | 300 | if (textStyle.textTransform === 2) { 301 | cssProps['text-transform'] = 'lowercase'; 302 | } 303 | 304 | if (textStyle.lineHeight) { 305 | cssProps['line-height'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(1 + (textStyle.lineHeight - textStyle.fontSize) / textStyle.lineHeight, opts.maxDecimalPlaces); 306 | } 307 | 308 | if (textStyle.color) { 309 | cssProps['color'] = exportUtils.createRgbaString(textStyle.color); 310 | } 311 | 312 | return cssProps; 313 | }, 314 | createRgbaString: function createRgbaString(colorObj) { 315 | return 'rgba(' + exportUtils.createColorValue(colorObj.r) + ', ' + exportUtils.createColorValue(colorObj.g) + ', ' + exportUtils.createColorValue(colorObj.b) + ', ' + colorObj.a + ')'; 316 | }, 317 | createColorValue: function createColorValue(normalizedValue) { 318 | return Math.round(normalizedValue * 255); 319 | }, 320 | createStyleBlock: function createStyleBlock(cssProps) { 321 | var output = ''; 322 | 323 | for (var prop in cssProps) { 324 | output += "\t" + prop + ': ' + cssProps[prop] + ';' + "\n"; 325 | } 326 | 327 | return output; 328 | }, 329 | createInlineStyleString: function createInlineStyleString(cssProps) { 330 | var styleString = ''; 331 | 332 | for (var prop in cssProps) { 333 | styleString += prop + ': ' + cssProps[prop] + '; '; 334 | } 335 | 336 | return styleString; 337 | }, 338 | createHtmlFontbook: function createHtmlFontbook(textStyles) { 339 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 340 | var output = "\n \n \n \n \n Typex text styles\n \n \n "; 341 | textStyles.forEach(function (textStyle, i) { 342 | var cssProps = exportUtils.createCssProps(textStyle, opts); 343 | var inlineStyleString = exportUtils.createInlineStyleString(cssProps); 344 | var cssPropsBlock = exportUtils.createStyleBlock(cssProps); 345 | var textStyleName; 346 | 347 | if (opts.namingConvention === 'Numeric') { 348 | textStyleName = opts.namingPrefix + ' ' + (i + 1); 349 | } else if (opts.namingConvention === 'Text style name') { 350 | textStyleName = opts.namingPrefix + ' ' + textStyle.name; 351 | } else { 352 | textStyleName = opts.namingPrefix + ' ' + (i + 1) + ' (' + textStyle.name + ')'; 353 | } 354 | 355 | output += "\n
\n
\n ".concat(i + 1, ".\n \n ").concat(textStyleName, "\n \n
\n
\n
\n The quick brown fox jumps over the lazy dog\n
\n
\n \n
\n
\n
\n "); 356 | }); 357 | output += "\n \n \n "; 358 | return output; 359 | } 360 | }; 361 | /* harmony default export */ __webpack_exports__["default"] = (exportUtils); 362 | 363 | /***/ }), 364 | 365 | /***/ "./src/util/number.js": 366 | /*!****************************!*\ 367 | !*** ./src/util/number.js ***! 368 | \****************************/ 369 | /*! exports provided: default */ 370 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 371 | 372 | "use strict"; 373 | __webpack_require__.r(__webpack_exports__); 374 | 375 | 376 | var number = { 377 | parseFloatMaxDecimal: function parseFloatMaxDecimal(number, maxDecimalPlaces) { 378 | return Number(number.toFixed(maxDecimalPlaces).replace(/[.,]00$/, '')); 379 | } 380 | }; 381 | /* harmony default export */ __webpack_exports__["default"] = (number); 382 | 383 | /***/ }), 384 | 385 | /***/ "./src/util/sketch.js": 386 | /*!****************************!*\ 387 | !*** ./src/util/sketch.js ***! 388 | \****************************/ 389 | /*! exports provided: default */ 390 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 391 | 392 | "use strict"; 393 | __webpack_require__.r(__webpack_exports__); 394 | 395 | 396 | var sketch = { 397 | getTextStyles: function getTextStyles(context) { 398 | var texts = context.document.documentData().layerTextStyles().objects(); 399 | var rawTextStyles = []; 400 | texts.forEach(function (text, i) { 401 | rawTextStyles.push({ 402 | attributes: text.style().textStyle().attributes(), 403 | textStyle: text, 404 | name: text.name() 405 | }); 406 | }); 407 | var textStyles = []; 408 | rawTextStyles.forEach(function (rawTextStyle) { 409 | var textStyle = {}; 410 | textStyle.name = rawTextStyle.name; 411 | textStyle.fontFamily = String(rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontNameAttribute)); 412 | textStyle.fontSize = rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontSizeAttribute); 413 | textStyle.paragraph = rawTextStyle.attributes.NSParagraphStyle; 414 | 415 | if (textStyle.paragraph) { 416 | textStyle.lineHeight = textStyle.paragraph.maximumLineHeight(); 417 | } 418 | 419 | var color = rawTextStyle.attributes.MSAttributedStringColorAttribute; 420 | 421 | if (color) { 422 | var r = color.red(); 423 | var g = color.green(); 424 | var b = color.blue(); 425 | var a = color.alpha(); 426 | textStyle.color = { 427 | r: r, 428 | g: g, 429 | b: b, 430 | a: a 431 | }; 432 | } 433 | 434 | textStyle.letterSpacing = rawTextStyle.attributes.NSKern || 0; 435 | textStyle.textTransform = parseInt(rawTextStyle.attributes.MSAttributedStringTextTransformAttribute || 0); // @TODO strikethrough & underline, or is this not needed? 436 | 437 | textStyles.push(textStyle); 438 | }); 439 | return textStyles; 440 | } 441 | }; 442 | /* harmony default export */ __webpack_exports__["default"] = (sketch); 443 | 444 | /***/ }), 445 | 446 | /***/ "./src/util/ui.js": 447 | /*!************************!*\ 448 | !*** ./src/util/ui.js ***! 449 | \************************/ 450 | /*! exports provided: default */ 451 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 452 | 453 | "use strict"; 454 | __webpack_require__.r(__webpack_exports__); 455 | 456 | 457 | var ui = { 458 | createSavePanel: function createSavePanel(defaultFileName, contents) { 459 | var save = NSSavePanel.savePanel(); 460 | save.setNameFieldStringValue(defaultFileName); 461 | save.setAllowsOtherFileTypes(false); 462 | save.setExtensionHidden(false); 463 | 464 | if (save.runModal()) { 465 | var file = NSString.stringWithString(contents); 466 | var path = save.URL().path(); 467 | file.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, null); 468 | } 469 | }, 470 | createLabel: function createLabel() { 471 | var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 472 | var label = NSTextField.alloc().init(); 473 | label.setStringValue(text); 474 | label.setFont(NSFont.boldSystemFontOfSize(12)); 475 | label.setBezeled(false); 476 | label.setDrawsBackground(false); 477 | label.setEditable(false); 478 | label.setSelectable(false); 479 | return label; 480 | }, 481 | createTextField: function createTextField(value) { 482 | var field = NSTextField.alloc().init(); 483 | field.setStringValue(value); 484 | return field; 485 | }, 486 | createSelect: function createSelect(options) { 487 | var comboBox = NSPopUpButton.alloc().init(); 488 | comboBox.addItemsWithTitles(options); 489 | comboBox.selectItemAtIndex(0); 490 | return comboBox; 491 | }, 492 | createStepper: function createStepper(value) { 493 | var stepper = NSStepper.alloc().init(); 494 | return stepper; 495 | }, 496 | createCheckbox: function createCheckbox(title) { 497 | var checkbox = NSButton.alloc().init(); 498 | checkbox.setButtonType(NSSwitchButton); 499 | checkbox.title = title; 500 | return checkbox; 501 | }, 502 | createSettingsDialog: function createSettingsDialog(context) { 503 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 504 | var components = arguments.length > 2 ? arguments[2] : undefined; 505 | var cb = arguments.length > 3 ? arguments[3] : undefined; 506 | opts.title = opts.title || 'Alert'; 507 | opts.informativeText = opts.informativeText || ''; 508 | opts.cancelBtnText = opts.cancelBtnText || 'Cancel'; 509 | opts.confirmBtnText = opts.confirmBtnText || 'Ok'; 510 | var dialog = NSAlert.alloc().init(); 511 | var dialogIconPath = context.plugin.urlForResourceNamed('icon.png').path(); 512 | var dialogIcon = NSImage.alloc().initByReferencingFile(dialogIconPath); 513 | dialog.setIcon(dialogIcon); 514 | dialog.setMessageText(opts.title); 515 | dialog.setInformativeText(opts.informativeText); 516 | var btnConfirm = dialog.addButtonWithTitle(opts.confirmBtnText); 517 | var btnCancel = dialog.addButtonWithTitle(opts.cancelBtnText); // Create grid view 518 | 519 | var gridView = NSGridView.alloc().init(); // Create object to hold all inputs 520 | 521 | var inputs = {}; 522 | var height = 0; 523 | var rowSpacing = 8; // Loop each component 524 | 525 | components.forEach(function (c) { 526 | var label, field; 527 | 528 | switch (c.type) { 529 | case 'text': 530 | label = ui.createLabel(c.label); 531 | field = ui.createTextField(c.value); 532 | height += 22 + rowSpacing; 533 | gridView.addRowWithViews([label, field]); 534 | break; 535 | 536 | case 'stepper': 537 | label = ui.createLabel(c.label); 538 | field = ui.createStepper(c.value); 539 | height += 22 + rowSpacing; 540 | gridView.addRowWithViews([label, field]); 541 | break; 542 | 543 | case 'checkbox': 544 | label = ui.createLabel(c.label); 545 | field = ui.createCheckbox(c.value); 546 | height += 22 + rowSpacing; 547 | gridView.addRowWithViews([label, field]); 548 | break; 549 | 550 | case 'multicheckbox': 551 | field = []; 552 | c.values.forEach(function (v, i) { 553 | label = i ? ui.createLabel() : ui.createLabel(c.label); 554 | var checkbox = ui.createCheckbox(v); 555 | height += 22 + rowSpacing; 556 | field.push(checkbox); 557 | gridView.addRowWithViews([label, checkbox]); 558 | }); 559 | break; 560 | 561 | case 'select': 562 | label = ui.createLabel(c.label); 563 | field = ui.createSelect(c.options); 564 | height += 28 + rowSpacing; 565 | gridView.addRowWithViews([label, field]); 566 | break; 567 | } 568 | 569 | inputs[c.id] = field; 570 | }); // Set grid view as view of dialog 571 | 572 | dialog.accessoryView = gridView; 573 | gridView.columnSpacing = 30; 574 | gridView.rowSpacing = rowSpacing; 575 | gridView.frame = NSMakeRect(0, 0, 400, height); // Open the dialog and store the response code 576 | 577 | var responseCode = dialog.runModal(); // The dialog is being 'submitted' 578 | 579 | if (responseCode === 1000) { 580 | var data = {}; 581 | components.forEach(function (c) { 582 | switch (c.type) { 583 | case 'text': 584 | data[c.id] = inputs[c.id].stringValue(); 585 | break; 586 | 587 | case 'select': 588 | data[c.id] = c.options[inputs[c.id].indexOfSelectedItem()]; 589 | break; 590 | 591 | case 'checkbox': 592 | data[c.id] = inputs[c.id].state() === 1; 593 | break; 594 | 595 | case 'multicheckbox': 596 | var values = {}; 597 | c.values.forEach(function (v, i) { 598 | values[v] = inputs[c.id][i].state() === 1; 599 | }); 600 | data[c.id] = values; 601 | } 602 | }); 603 | cb(data); 604 | return; 605 | } 606 | 607 | return dialog; 608 | } 609 | }; 610 | /* harmony default export */ __webpack_exports__["default"] = (ui); 611 | 612 | /***/ }), 613 | 614 | /***/ "./src/util/util.js": 615 | /*!**************************!*\ 616 | !*** ./src/util/util.js ***! 617 | \**************************/ 618 | /*! exports provided: default */ 619 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 620 | 621 | "use strict"; 622 | __webpack_require__.r(__webpack_exports__); 623 | 624 | 625 | var util = { 626 | createTextStyleId: function createTextStyleId(textStyle) { 627 | var textStyleId = ''; // Make sure this id incorporates every possible property of the text style 628 | 629 | textStyleId += textStyle.fontFamily; 630 | textStyleId += '-' + textStyle.fontSize; 631 | textStyleId += '-' + textStyle.letterSpacing; 632 | textStyleId += '-' + textStyle.textTransform; 633 | 634 | if (textStyle.lineHeight) { 635 | textStyleId += '-' + textStyle.lineHeight; 636 | } 637 | 638 | if (textStyle.color) { 639 | textStyleId += '-' + textStyle.color.r + '-' + textStyle.color.g + '-' + textStyle.color.b + '-' + textStyle.color.a; 640 | } 641 | 642 | return textStyleId; 643 | } 644 | }; 645 | /* harmony default export */ __webpack_exports__["default"] = (util); 646 | 647 | /***/ }) 648 | 649 | /******/ }); 650 | if (key === 'default' && typeof exports === 'function') { 651 | exports(context); 652 | } else { 653 | exports[key](context); 654 | } 655 | } 656 | that['onRun'] = __skpm_run.bind(this, 'default') 657 | 658 | //# sourceMappingURL=html-fontbook-export.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/json-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/json-export.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/export/export-components.js": 95 | /*!*****************************************!*\ 96 | !*** ./src/export/export-components.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 default export */ __webpack_exports__["default"] = ([{ 104 | type: 'checkbox', 105 | id: 'merge', 106 | label: 'Merge', 107 | value: 'Merge identical styles' 108 | }, { 109 | type: 'multicheckbox', 110 | id: 'excludeProps', 111 | label: 'Exclude properties', 112 | values: ['Color', 'Line height'] 113 | }, { 114 | type: 'select', 115 | id: 'cssUnit', 116 | options: ['px', 'em', 'rem', '%', 'vh', 'vw', 'No unit'], 117 | label: 'CSS unit' 118 | }, { 119 | type: 'text', 120 | id: 'scalingFactor', 121 | value: 1, 122 | label: 'Size scaling factor' 123 | }, { 124 | type: 'text', 125 | id: 'maxDecimalPlaces', 126 | value: 2, 127 | label: 'Maximal decimal places' 128 | }, { 129 | type: 'text', 130 | id: 'namingPrefix', 131 | value: 'type', 132 | label: 'Naming prefix' 133 | }, { 134 | type: 'select', 135 | id: 'namingConvention', 136 | options: ['Numeric', 'Text style name'], 137 | label: 'Naming convention' 138 | }]); 139 | 140 | /***/ }), 141 | 142 | /***/ "./src/export/open-export-dialog.js": 143 | /*!******************************************!*\ 144 | !*** ./src/export/open-export-dialog.js ***! 145 | \******************************************/ 146 | /*! exports provided: default */ 147 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 148 | 149 | "use strict"; 150 | __webpack_require__.r(__webpack_exports__); 151 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/ui */ "./src/util/ui.js"); 152 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/export */ "./src/util/export.js"); 153 | /* harmony import */ var _util_sketch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/sketch */ "./src/util/sketch.js"); 154 | /* harmony import */ var _export_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export-components */ "./src/export/export-components.js"); 155 | 156 | 157 | 158 | 159 | /* harmony default export */ __webpack_exports__["default"] = (function (context, opts, cb) { 160 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSettingsDialog(context, opts, _export_components__WEBPACK_IMPORTED_MODULE_3__["default"], function (data) { 161 | // Defaults 162 | data.propertyNamingConvention = data.propertyNamingConvention || 'Numeric'; 163 | data.cssUnit = data.cssUnit === 'No unit' ? 0 : data.cssUnit; // First store the properties we should exclude 164 | 165 | var excludeProps = []; 166 | 167 | if (data['excludeProps']['Color']) { 168 | excludeProps.push('color'); 169 | } 170 | 171 | if (data['excludeProps']['Line height']) { 172 | excludeProps.push('lineHeight'); 173 | } // Get the text styles from the Sketch document 174 | 175 | 176 | var textStyles = _util_sketch__WEBPACK_IMPORTED_MODULE_2__["default"].getTextStyles(context); 177 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].sortTextStyles(textStyles); 178 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].excludeTextStyleProperties(textStyles, excludeProps); 179 | 180 | if (data['merge']) { 181 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].removeDoubleTextStyles(textStyles); 182 | } 183 | 184 | cb(textStyles, data); 185 | }); 186 | }); 187 | ; 188 | 189 | /***/ }), 190 | 191 | /***/ "./src/json-export.js": 192 | /*!****************************!*\ 193 | !*** ./src/json-export.js ***! 194 | \****************************/ 195 | /*! exports provided: default */ 196 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 197 | 198 | "use strict"; 199 | __webpack_require__.r(__webpack_exports__); 200 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util/ui */ "./src/util/ui.js"); 201 | /* harmony import */ var _util_string__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util/string */ "./src/util/string.js"); 202 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./util/export */ "./src/util/export.js"); 203 | /* harmony import */ var _export_open_export_dialog__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export/open-export-dialog */ "./src/export/open-export-dialog.js"); 204 | 205 | 206 | 207 | 208 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 209 | Object(_export_open_export_dialog__WEBPACK_IMPORTED_MODULE_3__["default"])(context, { 210 | title: 'JSON export', 211 | informativeText: 'Export text styles in JSON format' 212 | }, function (textStyles, data) { 213 | // Export as JSON 214 | var textStyleJson = {}; 215 | textStyles.forEach(function (textStyle, i) { 216 | var textStyleIdentifier = _util_string__WEBPACK_IMPORTED_MODULE_1__["default"].slugify(textStyle.name); 217 | var stylePropertyNaming = data.namingPrefix + '-' + (data.namingConvention === 'Numeric' ? i + 1 : textStyleIdentifier); 218 | textStyleJson[stylePropertyNaming] = _util_export__WEBPACK_IMPORTED_MODULE_2__["default"].createCssProps(textStyle, data); 219 | }); // Ask the user to save the file 220 | 221 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSavePanel('typex-text-styles.json', JSON.stringify(textStyleJson)); 222 | }); 223 | }); 224 | ; 225 | 226 | /***/ }), 227 | 228 | /***/ "./src/util/export.js": 229 | /*!****************************!*\ 230 | !*** ./src/util/export.js ***! 231 | \****************************/ 232 | /*! exports provided: default */ 233 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 234 | 235 | "use strict"; 236 | __webpack_require__.r(__webpack_exports__); 237 | /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ "./src/util/util.js"); 238 | /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ "./src/util/number.js"); 239 | 240 | 241 | 242 | 243 | var exportUtils = { 244 | sortTextStyles: function sortTextStyles(textStyles) { 245 | // Sort text styles by size 246 | textStyles.sort(function (a, b) { 247 | return a.fontSize - b.fontSize; 248 | }); 249 | return textStyles; 250 | }, 251 | excludeTextStyleProperties: function excludeTextStyleProperties(textStyles) { 252 | var excludedProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 253 | textStyles.forEach(function (textStyle) { 254 | excludedProps.forEach(function (prop) { 255 | if (textStyle[prop]) { 256 | delete textStyle[prop]; 257 | } 258 | }); 259 | }); 260 | return textStyles; 261 | }, 262 | removeDoubleTextStyles: function removeDoubleTextStyles(textStyles) { 263 | var uniqueTextStyles = {}; 264 | var filtered = []; 265 | textStyles.forEach(function (textStyle, i) { 266 | var id = _util__WEBPACK_IMPORTED_MODULE_0__["default"].createTextStyleId(textStyle); 267 | 268 | if (!uniqueTextStyles[id]) { 269 | uniqueTextStyles[id] = true; 270 | filtered.push(textStyle); 271 | } 272 | }); 273 | return filtered; 274 | }, 275 | createCssProps: function createCssProps(textStyle) { 276 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 277 | opts.cssUnit = opts.cssUnit || 0; 278 | opts.scalingFactor = opts.scalingFactor || 1; 279 | opts.maxDecimalPlaces = opts.maxDecimalPlaces || 2; 280 | var cssProps = {}; 281 | cssProps['font-family'] = textStyle.fontFamily; 282 | cssProps['font-weight'] = 400; 283 | cssProps['text-transform'] = 'none'; 284 | var fontParts = textStyle.fontFamily.split('-'); 285 | var fontWeightMap = { 286 | 'Thin': 100, 287 | 'Light': 300, 288 | 'Regular': 400, 289 | 'Medium': 500, 290 | 'Bold': 700, 291 | 'Black': 900 292 | }; 293 | 294 | if (fontParts[1] && fontWeightMap[fontParts[1]]) { 295 | cssProps['font-family'] = fontParts[0]; 296 | cssProps['font-weight'] = fontWeightMap[fontParts[1]]; 297 | } 298 | 299 | cssProps['font-size'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.fontSize, opts.maxDecimalPlaces) + opts.cssUnit; 300 | cssProps['letter-spacing'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.letterSpacing, opts.maxDecimalPlaces) + opts.cssUnit; 301 | 302 | if (textStyle.textTransform === 1) { 303 | cssProps['text-transform'] = 'uppercase'; 304 | } 305 | 306 | if (textStyle.textTransform === 2) { 307 | cssProps['text-transform'] = 'lowercase'; 308 | } 309 | 310 | if (textStyle.lineHeight) { 311 | cssProps['line-height'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(1 + (textStyle.lineHeight - textStyle.fontSize) / textStyle.lineHeight, opts.maxDecimalPlaces); 312 | } 313 | 314 | if (textStyle.color) { 315 | cssProps['color'] = exportUtils.createRgbaString(textStyle.color); 316 | } 317 | 318 | return cssProps; 319 | }, 320 | createRgbaString: function createRgbaString(colorObj) { 321 | return 'rgba(' + exportUtils.createColorValue(colorObj.r) + ', ' + exportUtils.createColorValue(colorObj.g) + ', ' + exportUtils.createColorValue(colorObj.b) + ', ' + colorObj.a + ')'; 322 | }, 323 | createColorValue: function createColorValue(normalizedValue) { 324 | return Math.round(normalizedValue * 255); 325 | }, 326 | createStyleBlock: function createStyleBlock(cssProps) { 327 | var output = ''; 328 | 329 | for (var prop in cssProps) { 330 | output += "\t" + prop + ': ' + cssProps[prop] + ';' + "\n"; 331 | } 332 | 333 | return output; 334 | }, 335 | createInlineStyleString: function createInlineStyleString(cssProps) { 336 | var styleString = ''; 337 | 338 | for (var prop in cssProps) { 339 | styleString += prop + ': ' + cssProps[prop] + '; '; 340 | } 341 | 342 | return styleString; 343 | }, 344 | createHtmlFontbook: function createHtmlFontbook(textStyles) { 345 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 346 | var output = "\n \n \n \n \n Typex text styles\n \n \n "; 347 | textStyles.forEach(function (textStyle, i) { 348 | var cssProps = exportUtils.createCssProps(textStyle, opts); 349 | var inlineStyleString = exportUtils.createInlineStyleString(cssProps); 350 | var cssPropsBlock = exportUtils.createStyleBlock(cssProps); 351 | var textStyleName; 352 | 353 | if (opts.namingConvention === 'Numeric') { 354 | textStyleName = opts.namingPrefix + ' ' + (i + 1); 355 | } else if (opts.namingConvention === 'Text style name') { 356 | textStyleName = opts.namingPrefix + ' ' + textStyle.name; 357 | } else { 358 | textStyleName = opts.namingPrefix + ' ' + (i + 1) + ' (' + textStyle.name + ')'; 359 | } 360 | 361 | output += "\n
\n
\n ".concat(i + 1, ".\n \n ").concat(textStyleName, "\n \n
\n
\n
\n The quick brown fox jumps over the lazy dog\n
\n
\n \n
\n
\n
\n "); 362 | }); 363 | output += "\n \n \n "; 364 | return output; 365 | } 366 | }; 367 | /* harmony default export */ __webpack_exports__["default"] = (exportUtils); 368 | 369 | /***/ }), 370 | 371 | /***/ "./src/util/number.js": 372 | /*!****************************!*\ 373 | !*** ./src/util/number.js ***! 374 | \****************************/ 375 | /*! exports provided: default */ 376 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 377 | 378 | "use strict"; 379 | __webpack_require__.r(__webpack_exports__); 380 | 381 | 382 | var number = { 383 | parseFloatMaxDecimal: function parseFloatMaxDecimal(number, maxDecimalPlaces) { 384 | return Number(number.toFixed(maxDecimalPlaces).replace(/[.,]00$/, '')); 385 | } 386 | }; 387 | /* harmony default export */ __webpack_exports__["default"] = (number); 388 | 389 | /***/ }), 390 | 391 | /***/ "./src/util/sketch.js": 392 | /*!****************************!*\ 393 | !*** ./src/util/sketch.js ***! 394 | \****************************/ 395 | /*! exports provided: default */ 396 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 397 | 398 | "use strict"; 399 | __webpack_require__.r(__webpack_exports__); 400 | 401 | 402 | var sketch = { 403 | getTextStyles: function getTextStyles(context) { 404 | var texts = context.document.documentData().layerTextStyles().objects(); 405 | var rawTextStyles = []; 406 | texts.forEach(function (text, i) { 407 | rawTextStyles.push({ 408 | attributes: text.style().textStyle().attributes(), 409 | textStyle: text, 410 | name: text.name() 411 | }); 412 | }); 413 | var textStyles = []; 414 | rawTextStyles.forEach(function (rawTextStyle) { 415 | var textStyle = {}; 416 | textStyle.name = rawTextStyle.name; 417 | textStyle.fontFamily = String(rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontNameAttribute)); 418 | textStyle.fontSize = rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontSizeAttribute); 419 | textStyle.paragraph = rawTextStyle.attributes.NSParagraphStyle; 420 | 421 | if (textStyle.paragraph) { 422 | textStyle.lineHeight = textStyle.paragraph.maximumLineHeight(); 423 | } 424 | 425 | var color = rawTextStyle.attributes.MSAttributedStringColorAttribute; 426 | 427 | if (color) { 428 | var r = color.red(); 429 | var g = color.green(); 430 | var b = color.blue(); 431 | var a = color.alpha(); 432 | textStyle.color = { 433 | r: r, 434 | g: g, 435 | b: b, 436 | a: a 437 | }; 438 | } 439 | 440 | textStyle.letterSpacing = rawTextStyle.attributes.NSKern || 0; 441 | textStyle.textTransform = parseInt(rawTextStyle.attributes.MSAttributedStringTextTransformAttribute || 0); // @TODO strikethrough & underline, or is this not needed? 442 | 443 | textStyles.push(textStyle); 444 | }); 445 | return textStyles; 446 | } 447 | }; 448 | /* harmony default export */ __webpack_exports__["default"] = (sketch); 449 | 450 | /***/ }), 451 | 452 | /***/ "./src/util/string.js": 453 | /*!****************************!*\ 454 | !*** ./src/util/string.js ***! 455 | \****************************/ 456 | /*! exports provided: default */ 457 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 458 | 459 | "use strict"; 460 | __webpack_require__.r(__webpack_exports__); 461 | 462 | 463 | var string = { 464 | slugify: function slugify(str) { 465 | str = str.replace(/^\s+|\s+$/g, ''); // trim 466 | 467 | str = str.toLowerCase(); // remove accents, swap ñ for n, etc 468 | 469 | var from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;'; 470 | var to = 'aaaaeeeeiiiioooouuuunc------'; 471 | 472 | for (var i = 0, l = from.length; i < l; i++) { 473 | str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); 474 | } 475 | 476 | str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars 477 | .replace(/\s+/g, '-') // collapse whitespace and replace by - 478 | .replace(/-+/g, '-') // collapse dashes 479 | ; 480 | return str; 481 | } 482 | }; 483 | /* harmony default export */ __webpack_exports__["default"] = (string); 484 | 485 | /***/ }), 486 | 487 | /***/ "./src/util/ui.js": 488 | /*!************************!*\ 489 | !*** ./src/util/ui.js ***! 490 | \************************/ 491 | /*! exports provided: default */ 492 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 493 | 494 | "use strict"; 495 | __webpack_require__.r(__webpack_exports__); 496 | 497 | 498 | var ui = { 499 | createSavePanel: function createSavePanel(defaultFileName, contents) { 500 | var save = NSSavePanel.savePanel(); 501 | save.setNameFieldStringValue(defaultFileName); 502 | save.setAllowsOtherFileTypes(false); 503 | save.setExtensionHidden(false); 504 | 505 | if (save.runModal()) { 506 | var file = NSString.stringWithString(contents); 507 | var path = save.URL().path(); 508 | file.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, null); 509 | } 510 | }, 511 | createLabel: function createLabel() { 512 | var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 513 | var label = NSTextField.alloc().init(); 514 | label.setStringValue(text); 515 | label.setFont(NSFont.boldSystemFontOfSize(12)); 516 | label.setBezeled(false); 517 | label.setDrawsBackground(false); 518 | label.setEditable(false); 519 | label.setSelectable(false); 520 | return label; 521 | }, 522 | createTextField: function createTextField(value) { 523 | var field = NSTextField.alloc().init(); 524 | field.setStringValue(value); 525 | return field; 526 | }, 527 | createSelect: function createSelect(options) { 528 | var comboBox = NSPopUpButton.alloc().init(); 529 | comboBox.addItemsWithTitles(options); 530 | comboBox.selectItemAtIndex(0); 531 | return comboBox; 532 | }, 533 | createStepper: function createStepper(value) { 534 | var stepper = NSStepper.alloc().init(); 535 | return stepper; 536 | }, 537 | createCheckbox: function createCheckbox(title) { 538 | var checkbox = NSButton.alloc().init(); 539 | checkbox.setButtonType(NSSwitchButton); 540 | checkbox.title = title; 541 | return checkbox; 542 | }, 543 | createSettingsDialog: function createSettingsDialog(context) { 544 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 545 | var components = arguments.length > 2 ? arguments[2] : undefined; 546 | var cb = arguments.length > 3 ? arguments[3] : undefined; 547 | opts.title = opts.title || 'Alert'; 548 | opts.informativeText = opts.informativeText || ''; 549 | opts.cancelBtnText = opts.cancelBtnText || 'Cancel'; 550 | opts.confirmBtnText = opts.confirmBtnText || 'Ok'; 551 | var dialog = NSAlert.alloc().init(); 552 | var dialogIconPath = context.plugin.urlForResourceNamed('icon.png').path(); 553 | var dialogIcon = NSImage.alloc().initByReferencingFile(dialogIconPath); 554 | dialog.setIcon(dialogIcon); 555 | dialog.setMessageText(opts.title); 556 | dialog.setInformativeText(opts.informativeText); 557 | var btnConfirm = dialog.addButtonWithTitle(opts.confirmBtnText); 558 | var btnCancel = dialog.addButtonWithTitle(opts.cancelBtnText); // Create grid view 559 | 560 | var gridView = NSGridView.alloc().init(); // Create object to hold all inputs 561 | 562 | var inputs = {}; 563 | var height = 0; 564 | var rowSpacing = 8; // Loop each component 565 | 566 | components.forEach(function (c) { 567 | var label, field; 568 | 569 | switch (c.type) { 570 | case 'text': 571 | label = ui.createLabel(c.label); 572 | field = ui.createTextField(c.value); 573 | height += 22 + rowSpacing; 574 | gridView.addRowWithViews([label, field]); 575 | break; 576 | 577 | case 'stepper': 578 | label = ui.createLabel(c.label); 579 | field = ui.createStepper(c.value); 580 | height += 22 + rowSpacing; 581 | gridView.addRowWithViews([label, field]); 582 | break; 583 | 584 | case 'checkbox': 585 | label = ui.createLabel(c.label); 586 | field = ui.createCheckbox(c.value); 587 | height += 22 + rowSpacing; 588 | gridView.addRowWithViews([label, field]); 589 | break; 590 | 591 | case 'multicheckbox': 592 | field = []; 593 | c.values.forEach(function (v, i) { 594 | label = i ? ui.createLabel() : ui.createLabel(c.label); 595 | var checkbox = ui.createCheckbox(v); 596 | height += 22 + rowSpacing; 597 | field.push(checkbox); 598 | gridView.addRowWithViews([label, checkbox]); 599 | }); 600 | break; 601 | 602 | case 'select': 603 | label = ui.createLabel(c.label); 604 | field = ui.createSelect(c.options); 605 | height += 28 + rowSpacing; 606 | gridView.addRowWithViews([label, field]); 607 | break; 608 | } 609 | 610 | inputs[c.id] = field; 611 | }); // Set grid view as view of dialog 612 | 613 | dialog.accessoryView = gridView; 614 | gridView.columnSpacing = 30; 615 | gridView.rowSpacing = rowSpacing; 616 | gridView.frame = NSMakeRect(0, 0, 400, height); // Open the dialog and store the response code 617 | 618 | var responseCode = dialog.runModal(); // The dialog is being 'submitted' 619 | 620 | if (responseCode === 1000) { 621 | var data = {}; 622 | components.forEach(function (c) { 623 | switch (c.type) { 624 | case 'text': 625 | data[c.id] = inputs[c.id].stringValue(); 626 | break; 627 | 628 | case 'select': 629 | data[c.id] = c.options[inputs[c.id].indexOfSelectedItem()]; 630 | break; 631 | 632 | case 'checkbox': 633 | data[c.id] = inputs[c.id].state() === 1; 634 | break; 635 | 636 | case 'multicheckbox': 637 | var values = {}; 638 | c.values.forEach(function (v, i) { 639 | values[v] = inputs[c.id][i].state() === 1; 640 | }); 641 | data[c.id] = values; 642 | } 643 | }); 644 | cb(data); 645 | return; 646 | } 647 | 648 | return dialog; 649 | } 650 | }; 651 | /* harmony default export */ __webpack_exports__["default"] = (ui); 652 | 653 | /***/ }), 654 | 655 | /***/ "./src/util/util.js": 656 | /*!**************************!*\ 657 | !*** ./src/util/util.js ***! 658 | \**************************/ 659 | /*! exports provided: default */ 660 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 661 | 662 | "use strict"; 663 | __webpack_require__.r(__webpack_exports__); 664 | 665 | 666 | var util = { 667 | createTextStyleId: function createTextStyleId(textStyle) { 668 | var textStyleId = ''; // Make sure this id incorporates every possible property of the text style 669 | 670 | textStyleId += textStyle.fontFamily; 671 | textStyleId += '-' + textStyle.fontSize; 672 | textStyleId += '-' + textStyle.letterSpacing; 673 | textStyleId += '-' + textStyle.textTransform; 674 | 675 | if (textStyle.lineHeight) { 676 | textStyleId += '-' + textStyle.lineHeight; 677 | } 678 | 679 | if (textStyle.color) { 680 | textStyleId += '-' + textStyle.color.r + '-' + textStyle.color.g + '-' + textStyle.color.b + '-' + textStyle.color.a; 681 | } 682 | 683 | return textStyleId; 684 | } 685 | }; 686 | /* harmony default export */ __webpack_exports__["default"] = (util); 687 | 688 | /***/ }) 689 | 690 | /******/ }); 691 | if (key === 'default' && typeof exports === 'function') { 692 | exports(context); 693 | } else { 694 | exports[key](context); 695 | } 696 | } 697 | that['onRun'] = __skpm_run.bind(this, 'default') 698 | 699 | //# sourceMappingURL=json-export.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "compatibleVersion": 3, 3 | "bundleVersion": 1, 4 | "icon": "icon.png", 5 | "commands": [ 6 | { 7 | "name": "HTML fontbook", 8 | "shortcut": "ctrl shift f", 9 | "identifier": "html-fontbook-export", 10 | "script": "html-fontbook-export.js" 11 | }, 12 | { 13 | "name": "JSON", 14 | "shortcut": "ctrl shift j", 15 | "identifier": "json-export", 16 | "script": "json-export.js" 17 | }, 18 | { 19 | "name": "CSS classes", 20 | "shortcut": "ctrl shift c", 21 | "identifier": "css-export", 22 | "script": "css-export.js" 23 | }, 24 | { 25 | "name": "SASS mixins", 26 | "shortcut": "ctrl shift s", 27 | "identifier": "sass-mixins-export", 28 | "script": "sass-mixins-export.js" 29 | } 30 | ], 31 | "menu": { 32 | "title": "Typex (Text style export)", 33 | "items": [ 34 | { 35 | "title": "Export", 36 | "items": [ 37 | "html-fontbook-export", 38 | "json-export", 39 | "css-export", 40 | "sass-mixins-export" 41 | ] 42 | } 43 | ] 44 | }, 45 | "version": "1.0.0", 46 | "description": "Text styles to CSS, SASS mixins, HTML, JSON, ...", 47 | "name": "typex", 48 | "identifier": "typex", 49 | "disableCocoaScriptPreprocessor": true, 50 | "appcast": "https://raw.githubusercontent.com/reinvanoyen/typex/master/.appcast.xml" 51 | } -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/my-command.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap","webpack://exports/./src/my-command.js"],"names":["context","document","showMessage"],"mappings":";;;;;;AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA;;;;;;;;;;;;;;AClFA,+DAAe,UAASA,OAAT,EAAkB;AAC/BA,SAAO,CAACC,QAAR,CAAiBC,WAAjB,CAA6B,eAA7B;AACD,C","file":"my-command.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/my-command.js\");\n","export default function(context) {\n context.document.showMessage(\"It's alive 🙌\")\n}\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/sass-mixins-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/sass-mixins-export.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/export/export-components.js": 95 | /*!*****************************************!*\ 96 | !*** ./src/export/export-components.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 default export */ __webpack_exports__["default"] = ([{ 104 | type: 'checkbox', 105 | id: 'merge', 106 | label: 'Merge', 107 | value: 'Merge identical styles' 108 | }, { 109 | type: 'multicheckbox', 110 | id: 'excludeProps', 111 | label: 'Exclude properties', 112 | values: ['Color', 'Line height'] 113 | }, { 114 | type: 'select', 115 | id: 'cssUnit', 116 | options: ['px', 'em', 'rem', '%', 'vh', 'vw', 'No unit'], 117 | label: 'CSS unit' 118 | }, { 119 | type: 'text', 120 | id: 'scalingFactor', 121 | value: 1, 122 | label: 'Size scaling factor' 123 | }, { 124 | type: 'text', 125 | id: 'maxDecimalPlaces', 126 | value: 2, 127 | label: 'Maximal decimal places' 128 | }, { 129 | type: 'text', 130 | id: 'namingPrefix', 131 | value: 'type', 132 | label: 'Naming prefix' 133 | }, { 134 | type: 'select', 135 | id: 'namingConvention', 136 | options: ['Numeric', 'Text style name'], 137 | label: 'Naming convention' 138 | }]); 139 | 140 | /***/ }), 141 | 142 | /***/ "./src/export/open-export-dialog.js": 143 | /*!******************************************!*\ 144 | !*** ./src/export/open-export-dialog.js ***! 145 | \******************************************/ 146 | /*! exports provided: default */ 147 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 148 | 149 | "use strict"; 150 | __webpack_require__.r(__webpack_exports__); 151 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/ui */ "./src/util/ui.js"); 152 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/export */ "./src/util/export.js"); 153 | /* harmony import */ var _util_sketch__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../util/sketch */ "./src/util/sketch.js"); 154 | /* harmony import */ var _export_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export-components */ "./src/export/export-components.js"); 155 | 156 | 157 | 158 | 159 | /* harmony default export */ __webpack_exports__["default"] = (function (context, opts, cb) { 160 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSettingsDialog(context, opts, _export_components__WEBPACK_IMPORTED_MODULE_3__["default"], function (data) { 161 | // Defaults 162 | data.propertyNamingConvention = data.propertyNamingConvention || 'Numeric'; 163 | data.cssUnit = data.cssUnit === 'No unit' ? 0 : data.cssUnit; // First store the properties we should exclude 164 | 165 | var excludeProps = []; 166 | 167 | if (data['excludeProps']['Color']) { 168 | excludeProps.push('color'); 169 | } 170 | 171 | if (data['excludeProps']['Line height']) { 172 | excludeProps.push('lineHeight'); 173 | } // Get the text styles from the Sketch document 174 | 175 | 176 | var textStyles = _util_sketch__WEBPACK_IMPORTED_MODULE_2__["default"].getTextStyles(context); 177 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].sortTextStyles(textStyles); 178 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].excludeTextStyleProperties(textStyles, excludeProps); 179 | 180 | if (data['merge']) { 181 | textStyles = _util_export__WEBPACK_IMPORTED_MODULE_1__["default"].removeDoubleTextStyles(textStyles); 182 | } 183 | 184 | cb(textStyles, data); 185 | }); 186 | }); 187 | ; 188 | 189 | /***/ }), 190 | 191 | /***/ "./src/sass-mixins-export.js": 192 | /*!***********************************!*\ 193 | !*** ./src/sass-mixins-export.js ***! 194 | \***********************************/ 195 | /*! exports provided: default */ 196 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 197 | 198 | "use strict"; 199 | __webpack_require__.r(__webpack_exports__); 200 | /* harmony import */ var _util_ui__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util/ui */ "./src/util/ui.js"); 201 | /* harmony import */ var _util_string__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./util/string */ "./src/util/string.js"); 202 | /* harmony import */ var _util_export__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./util/export */ "./src/util/export.js"); 203 | /* harmony import */ var _export_open_export_dialog__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./export/open-export-dialog */ "./src/export/open-export-dialog.js"); 204 | 205 | 206 | 207 | 208 | /* harmony default export */ __webpack_exports__["default"] = (function (context) { 209 | Object(_export_open_export_dialog__WEBPACK_IMPORTED_MODULE_3__["default"])(context, { 210 | title: 'SASS mixins export', 211 | informativeText: 'Export each text style as a SASS mixin' 212 | }, function (textStyles, data) { 213 | var sass = {}; 214 | textStyles.forEach(function (textStyle) { 215 | sass[_util_string__WEBPACK_IMPORTED_MODULE_1__["default"].slugify(textStyle.name)] = _util_export__WEBPACK_IMPORTED_MODULE_2__["default"].createCssProps(textStyle, data); 216 | }); 217 | var output = ''; 218 | var i = 0; 219 | 220 | for (var identifier in sass) { 221 | if (sass.hasOwnProperty(identifier)) { 222 | var mixinName = data.namingPrefix + '-' + (data.namingConvention === 'Numeric' ? i + 1 : identifier); 223 | output += (i !== 0 ? "\n" : '') + '@mixin ' + mixinName + "\n"; 224 | output += '{' + "\n"; 225 | output += _util_export__WEBPACK_IMPORTED_MODULE_2__["default"].createStyleBlock(sass[identifier]); 226 | output += '}' + "\n"; 227 | i++; 228 | } 229 | } 230 | 231 | _util_ui__WEBPACK_IMPORTED_MODULE_0__["default"].createSavePanel('typex-mixins.scss', output); 232 | }); 233 | }); 234 | ; 235 | 236 | /***/ }), 237 | 238 | /***/ "./src/util/export.js": 239 | /*!****************************!*\ 240 | !*** ./src/util/export.js ***! 241 | \****************************/ 242 | /*! exports provided: default */ 243 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 244 | 245 | "use strict"; 246 | __webpack_require__.r(__webpack_exports__); 247 | /* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util */ "./src/util/util.js"); 248 | /* harmony import */ var _number__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./number */ "./src/util/number.js"); 249 | 250 | 251 | 252 | 253 | var exportUtils = { 254 | sortTextStyles: function sortTextStyles(textStyles) { 255 | // Sort text styles by size 256 | textStyles.sort(function (a, b) { 257 | return a.fontSize - b.fontSize; 258 | }); 259 | return textStyles; 260 | }, 261 | excludeTextStyleProperties: function excludeTextStyleProperties(textStyles) { 262 | var excludedProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 263 | textStyles.forEach(function (textStyle) { 264 | excludedProps.forEach(function (prop) { 265 | if (textStyle[prop]) { 266 | delete textStyle[prop]; 267 | } 268 | }); 269 | }); 270 | return textStyles; 271 | }, 272 | removeDoubleTextStyles: function removeDoubleTextStyles(textStyles) { 273 | var uniqueTextStyles = {}; 274 | var filtered = []; 275 | textStyles.forEach(function (textStyle, i) { 276 | var id = _util__WEBPACK_IMPORTED_MODULE_0__["default"].createTextStyleId(textStyle); 277 | 278 | if (!uniqueTextStyles[id]) { 279 | uniqueTextStyles[id] = true; 280 | filtered.push(textStyle); 281 | } 282 | }); 283 | return filtered; 284 | }, 285 | createCssProps: function createCssProps(textStyle) { 286 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 287 | opts.cssUnit = opts.cssUnit || 0; 288 | opts.scalingFactor = opts.scalingFactor || 1; 289 | opts.maxDecimalPlaces = opts.maxDecimalPlaces || 2; 290 | var cssProps = {}; 291 | cssProps['font-family'] = textStyle.fontFamily; 292 | cssProps['font-weight'] = 400; 293 | cssProps['text-transform'] = 'none'; 294 | var fontParts = textStyle.fontFamily.split('-'); 295 | var fontWeightMap = { 296 | 'Thin': 100, 297 | 'Light': 300, 298 | 'Regular': 400, 299 | 'Medium': 500, 300 | 'Bold': 700, 301 | 'Black': 900 302 | }; 303 | 304 | if (fontParts[1] && fontWeightMap[fontParts[1]]) { 305 | cssProps['font-family'] = fontParts[0]; 306 | cssProps['font-weight'] = fontWeightMap[fontParts[1]]; 307 | } 308 | 309 | cssProps['font-size'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.fontSize, opts.maxDecimalPlaces) + opts.cssUnit; 310 | cssProps['letter-spacing'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(opts.scalingFactor * textStyle.letterSpacing, opts.maxDecimalPlaces) + opts.cssUnit; 311 | 312 | if (textStyle.textTransform === 1) { 313 | cssProps['text-transform'] = 'uppercase'; 314 | } 315 | 316 | if (textStyle.textTransform === 2) { 317 | cssProps['text-transform'] = 'lowercase'; 318 | } 319 | 320 | if (textStyle.lineHeight) { 321 | cssProps['line-height'] = _number__WEBPACK_IMPORTED_MODULE_1__["default"].parseFloatMaxDecimal(1 + (textStyle.lineHeight - textStyle.fontSize) / textStyle.lineHeight, opts.maxDecimalPlaces); 322 | } 323 | 324 | if (textStyle.color) { 325 | cssProps['color'] = exportUtils.createRgbaString(textStyle.color); 326 | } 327 | 328 | return cssProps; 329 | }, 330 | createRgbaString: function createRgbaString(colorObj) { 331 | return 'rgba(' + exportUtils.createColorValue(colorObj.r) + ', ' + exportUtils.createColorValue(colorObj.g) + ', ' + exportUtils.createColorValue(colorObj.b) + ', ' + colorObj.a + ')'; 332 | }, 333 | createColorValue: function createColorValue(normalizedValue) { 334 | return Math.round(normalizedValue * 255); 335 | }, 336 | createStyleBlock: function createStyleBlock(cssProps) { 337 | var output = ''; 338 | 339 | for (var prop in cssProps) { 340 | output += "\t" + prop + ': ' + cssProps[prop] + ';' + "\n"; 341 | } 342 | 343 | return output; 344 | }, 345 | createInlineStyleString: function createInlineStyleString(cssProps) { 346 | var styleString = ''; 347 | 348 | for (var prop in cssProps) { 349 | styleString += prop + ': ' + cssProps[prop] + '; '; 350 | } 351 | 352 | return styleString; 353 | }, 354 | createHtmlFontbook: function createHtmlFontbook(textStyles) { 355 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 356 | var output = "\n \n \n \n \n Typex text styles\n \n \n "; 357 | textStyles.forEach(function (textStyle, i) { 358 | var cssProps = exportUtils.createCssProps(textStyle, opts); 359 | var inlineStyleString = exportUtils.createInlineStyleString(cssProps); 360 | var cssPropsBlock = exportUtils.createStyleBlock(cssProps); 361 | var textStyleName; 362 | 363 | if (opts.namingConvention === 'Numeric') { 364 | textStyleName = opts.namingPrefix + ' ' + (i + 1); 365 | } else if (opts.namingConvention === 'Text style name') { 366 | textStyleName = opts.namingPrefix + ' ' + textStyle.name; 367 | } else { 368 | textStyleName = opts.namingPrefix + ' ' + (i + 1) + ' (' + textStyle.name + ')'; 369 | } 370 | 371 | output += "\n
\n
\n ".concat(i + 1, ".\n \n ").concat(textStyleName, "\n \n
\n
\n
\n The quick brown fox jumps over the lazy dog\n
\n
\n \n
\n
\n
\n "); 372 | }); 373 | output += "\n \n \n "; 374 | return output; 375 | } 376 | }; 377 | /* harmony default export */ __webpack_exports__["default"] = (exportUtils); 378 | 379 | /***/ }), 380 | 381 | /***/ "./src/util/number.js": 382 | /*!****************************!*\ 383 | !*** ./src/util/number.js ***! 384 | \****************************/ 385 | /*! exports provided: default */ 386 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 387 | 388 | "use strict"; 389 | __webpack_require__.r(__webpack_exports__); 390 | 391 | 392 | var number = { 393 | parseFloatMaxDecimal: function parseFloatMaxDecimal(number, maxDecimalPlaces) { 394 | return Number(number.toFixed(maxDecimalPlaces).replace(/[.,]00$/, '')); 395 | } 396 | }; 397 | /* harmony default export */ __webpack_exports__["default"] = (number); 398 | 399 | /***/ }), 400 | 401 | /***/ "./src/util/sketch.js": 402 | /*!****************************!*\ 403 | !*** ./src/util/sketch.js ***! 404 | \****************************/ 405 | /*! exports provided: default */ 406 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 407 | 408 | "use strict"; 409 | __webpack_require__.r(__webpack_exports__); 410 | 411 | 412 | var sketch = { 413 | getTextStyles: function getTextStyles(context) { 414 | var texts = context.document.documentData().layerTextStyles().objects(); 415 | var rawTextStyles = []; 416 | texts.forEach(function (text, i) { 417 | rawTextStyles.push({ 418 | attributes: text.style().textStyle().attributes(), 419 | textStyle: text, 420 | name: text.name() 421 | }); 422 | }); 423 | var textStyles = []; 424 | rawTextStyles.forEach(function (rawTextStyle) { 425 | var textStyle = {}; 426 | textStyle.name = rawTextStyle.name; 427 | textStyle.fontFamily = String(rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontNameAttribute)); 428 | textStyle.fontSize = rawTextStyle.attributes.NSFont.fontDescriptor().objectForKey(NSFontSizeAttribute); 429 | textStyle.paragraph = rawTextStyle.attributes.NSParagraphStyle; 430 | 431 | if (textStyle.paragraph) { 432 | textStyle.lineHeight = textStyle.paragraph.maximumLineHeight(); 433 | } 434 | 435 | var color = rawTextStyle.attributes.MSAttributedStringColorAttribute; 436 | 437 | if (color) { 438 | var r = color.red(); 439 | var g = color.green(); 440 | var b = color.blue(); 441 | var a = color.alpha(); 442 | textStyle.color = { 443 | r: r, 444 | g: g, 445 | b: b, 446 | a: a 447 | }; 448 | } 449 | 450 | textStyle.letterSpacing = rawTextStyle.attributes.NSKern || 0; 451 | textStyle.textTransform = parseInt(rawTextStyle.attributes.MSAttributedStringTextTransformAttribute || 0); // @TODO strikethrough & underline, or is this not needed? 452 | 453 | textStyles.push(textStyle); 454 | }); 455 | return textStyles; 456 | } 457 | }; 458 | /* harmony default export */ __webpack_exports__["default"] = (sketch); 459 | 460 | /***/ }), 461 | 462 | /***/ "./src/util/string.js": 463 | /*!****************************!*\ 464 | !*** ./src/util/string.js ***! 465 | \****************************/ 466 | /*! exports provided: default */ 467 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 468 | 469 | "use strict"; 470 | __webpack_require__.r(__webpack_exports__); 471 | 472 | 473 | var string = { 474 | slugify: function slugify(str) { 475 | str = str.replace(/^\s+|\s+$/g, ''); // trim 476 | 477 | str = str.toLowerCase(); // remove accents, swap ñ for n, etc 478 | 479 | var from = 'àáäâèéëêìíïîòóöôùúüûñç·/_,:;'; 480 | var to = 'aaaaeeeeiiiioooouuuunc------'; 481 | 482 | for (var i = 0, l = from.length; i < l; i++) { 483 | str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); 484 | } 485 | 486 | str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars 487 | .replace(/\s+/g, '-') // collapse whitespace and replace by - 488 | .replace(/-+/g, '-') // collapse dashes 489 | ; 490 | return str; 491 | } 492 | }; 493 | /* harmony default export */ __webpack_exports__["default"] = (string); 494 | 495 | /***/ }), 496 | 497 | /***/ "./src/util/ui.js": 498 | /*!************************!*\ 499 | !*** ./src/util/ui.js ***! 500 | \************************/ 501 | /*! exports provided: default */ 502 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 503 | 504 | "use strict"; 505 | __webpack_require__.r(__webpack_exports__); 506 | 507 | 508 | var ui = { 509 | createSavePanel: function createSavePanel(defaultFileName, contents) { 510 | var save = NSSavePanel.savePanel(); 511 | save.setNameFieldStringValue(defaultFileName); 512 | save.setAllowsOtherFileTypes(false); 513 | save.setExtensionHidden(false); 514 | 515 | if (save.runModal()) { 516 | var file = NSString.stringWithString(contents); 517 | var path = save.URL().path(); 518 | file.writeToFile_atomically_encoding_error(path, true, NSUTF8StringEncoding, null); 519 | } 520 | }, 521 | createLabel: function createLabel() { 522 | var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; 523 | var label = NSTextField.alloc().init(); 524 | label.setStringValue(text); 525 | label.setFont(NSFont.boldSystemFontOfSize(12)); 526 | label.setBezeled(false); 527 | label.setDrawsBackground(false); 528 | label.setEditable(false); 529 | label.setSelectable(false); 530 | return label; 531 | }, 532 | createTextField: function createTextField(value) { 533 | var field = NSTextField.alloc().init(); 534 | field.setStringValue(value); 535 | return field; 536 | }, 537 | createSelect: function createSelect(options) { 538 | var comboBox = NSPopUpButton.alloc().init(); 539 | comboBox.addItemsWithTitles(options); 540 | comboBox.selectItemAtIndex(0); 541 | return comboBox; 542 | }, 543 | createStepper: function createStepper(value) { 544 | var stepper = NSStepper.alloc().init(); 545 | return stepper; 546 | }, 547 | createCheckbox: function createCheckbox(title) { 548 | var checkbox = NSButton.alloc().init(); 549 | checkbox.setButtonType(NSSwitchButton); 550 | checkbox.title = title; 551 | return checkbox; 552 | }, 553 | createSettingsDialog: function createSettingsDialog(context) { 554 | var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 555 | var components = arguments.length > 2 ? arguments[2] : undefined; 556 | var cb = arguments.length > 3 ? arguments[3] : undefined; 557 | opts.title = opts.title || 'Alert'; 558 | opts.informativeText = opts.informativeText || ''; 559 | opts.cancelBtnText = opts.cancelBtnText || 'Cancel'; 560 | opts.confirmBtnText = opts.confirmBtnText || 'Ok'; 561 | var dialog = NSAlert.alloc().init(); 562 | var dialogIconPath = context.plugin.urlForResourceNamed('icon.png').path(); 563 | var dialogIcon = NSImage.alloc().initByReferencingFile(dialogIconPath); 564 | dialog.setIcon(dialogIcon); 565 | dialog.setMessageText(opts.title); 566 | dialog.setInformativeText(opts.informativeText); 567 | var btnConfirm = dialog.addButtonWithTitle(opts.confirmBtnText); 568 | var btnCancel = dialog.addButtonWithTitle(opts.cancelBtnText); // Create grid view 569 | 570 | var gridView = NSGridView.alloc().init(); // Create object to hold all inputs 571 | 572 | var inputs = {}; 573 | var height = 0; 574 | var rowSpacing = 8; // Loop each component 575 | 576 | components.forEach(function (c) { 577 | var label, field; 578 | 579 | switch (c.type) { 580 | case 'text': 581 | label = ui.createLabel(c.label); 582 | field = ui.createTextField(c.value); 583 | height += 22 + rowSpacing; 584 | gridView.addRowWithViews([label, field]); 585 | break; 586 | 587 | case 'stepper': 588 | label = ui.createLabel(c.label); 589 | field = ui.createStepper(c.value); 590 | height += 22 + rowSpacing; 591 | gridView.addRowWithViews([label, field]); 592 | break; 593 | 594 | case 'checkbox': 595 | label = ui.createLabel(c.label); 596 | field = ui.createCheckbox(c.value); 597 | height += 22 + rowSpacing; 598 | gridView.addRowWithViews([label, field]); 599 | break; 600 | 601 | case 'multicheckbox': 602 | field = []; 603 | c.values.forEach(function (v, i) { 604 | label = i ? ui.createLabel() : ui.createLabel(c.label); 605 | var checkbox = ui.createCheckbox(v); 606 | height += 22 + rowSpacing; 607 | field.push(checkbox); 608 | gridView.addRowWithViews([label, checkbox]); 609 | }); 610 | break; 611 | 612 | case 'select': 613 | label = ui.createLabel(c.label); 614 | field = ui.createSelect(c.options); 615 | height += 28 + rowSpacing; 616 | gridView.addRowWithViews([label, field]); 617 | break; 618 | } 619 | 620 | inputs[c.id] = field; 621 | }); // Set grid view as view of dialog 622 | 623 | dialog.accessoryView = gridView; 624 | gridView.columnSpacing = 30; 625 | gridView.rowSpacing = rowSpacing; 626 | gridView.frame = NSMakeRect(0, 0, 400, height); // Open the dialog and store the response code 627 | 628 | var responseCode = dialog.runModal(); // The dialog is being 'submitted' 629 | 630 | if (responseCode === 1000) { 631 | var data = {}; 632 | components.forEach(function (c) { 633 | switch (c.type) { 634 | case 'text': 635 | data[c.id] = inputs[c.id].stringValue(); 636 | break; 637 | 638 | case 'select': 639 | data[c.id] = c.options[inputs[c.id].indexOfSelectedItem()]; 640 | break; 641 | 642 | case 'checkbox': 643 | data[c.id] = inputs[c.id].state() === 1; 644 | break; 645 | 646 | case 'multicheckbox': 647 | var values = {}; 648 | c.values.forEach(function (v, i) { 649 | values[v] = inputs[c.id][i].state() === 1; 650 | }); 651 | data[c.id] = values; 652 | } 653 | }); 654 | cb(data); 655 | return; 656 | } 657 | 658 | return dialog; 659 | } 660 | }; 661 | /* harmony default export */ __webpack_exports__["default"] = (ui); 662 | 663 | /***/ }), 664 | 665 | /***/ "./src/util/util.js": 666 | /*!**************************!*\ 667 | !*** ./src/util/util.js ***! 668 | \**************************/ 669 | /*! exports provided: default */ 670 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 671 | 672 | "use strict"; 673 | __webpack_require__.r(__webpack_exports__); 674 | 675 | 676 | var util = { 677 | createTextStyleId: function createTextStyleId(textStyle) { 678 | var textStyleId = ''; // Make sure this id incorporates every possible property of the text style 679 | 680 | textStyleId += textStyle.fontFamily; 681 | textStyleId += '-' + textStyle.fontSize; 682 | textStyleId += '-' + textStyle.letterSpacing; 683 | textStyleId += '-' + textStyle.textTransform; 684 | 685 | if (textStyle.lineHeight) { 686 | textStyleId += '-' + textStyle.lineHeight; 687 | } 688 | 689 | if (textStyle.color) { 690 | textStyleId += '-' + textStyle.color.r + '-' + textStyle.color.g + '-' + textStyle.color.b + '-' + textStyle.color.a; 691 | } 692 | 693 | return textStyleId; 694 | } 695 | }; 696 | /* harmony default export */ __webpack_exports__["default"] = (util); 697 | 698 | /***/ }) 699 | 700 | /******/ }); 701 | if (key === 'default' && typeof exports === 'function') { 702 | exports(context); 703 | } else { 704 | exports[key](context); 705 | } 706 | } 707 | that['onRun'] = __skpm_run.bind(this, 'default') 708 | 709 | //# sourceMappingURL=sass-mixins-export.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/web-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/web-export.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/web-export.js": 95 | /*!***************************!*\ 96 | !*** ./src/web-export.js ***! 97 | \***************************/ 98 | /*! exports provided: default */ 99 | /***/ (function(module, exports) { 100 | 101 | throw new Error("Module build failed (from ./node_modules/babel-loader/lib/index.js):\nError: ENOENT: no such file or directory, open '/Users/rein/Workspace/sketch-plugins/typex/src/web-export.js'"); 102 | 103 | /***/ }) 104 | 105 | /******/ }); 106 | if (key === 'default' && typeof exports === 'function') { 107 | exports(context); 108 | } else { 109 | exports[key](context); 110 | } 111 | } 112 | that['onRun'] = __skpm_run.bind(this, 'default') 113 | 114 | //# sourceMappingURL=web-export.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/web-export.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap"],"names":[],"mappings":";;;;;;AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA","file":"web-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/web-export.js\");\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/window-test.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/window-test.js"); 90 | /******/ }) 91 | /************************************************************************/ 92 | /******/ ({ 93 | 94 | /***/ "./src/window-test.js": 95 | /*!****************************!*\ 96 | !*** ./src/window-test.js ***! 97 | \****************************/ 98 | /*! exports provided: default */ 99 | /***/ (function(module, exports) { 100 | 101 | throw new Error("Module build failed (from ./node_modules/babel-loader/lib/index.js):\nError: ENOENT: no such file or directory, open '/Users/rein/Workspace/sketch-plugins/typex/src/window-test.js'"); 102 | 103 | /***/ }) 104 | 105 | /******/ }); 106 | if (key === 'default' && typeof exports === 'function') { 107 | exports(context); 108 | } else { 109 | exports[key](context); 110 | } 111 | } 112 | that['onRun'] = __skpm_run.bind(this, 'default') 113 | 114 | //# sourceMappingURL=window-test.js.map -------------------------------------------------------------------------------- /typex.sketchplugin/Contents/Sketch/window-test.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack://exports/webpack/bootstrap"],"names":[],"mappings":";;;;;;AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA","file":"window-test.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/window-test.js\");\n"],"sourceRoot":""} --------------------------------------------------------------------------------