├── .appcast.xml ├── .gitignore ├── Copy-Paste-Position-Size ├── .gitignore ├── README.md ├── assets │ └── icon.png ├── package-lock.json ├── package.json ├── sketch-assets │ └── icons.sketch └── src │ ├── manifest.json │ └── my-command.js ├── README.md ├── assets └── icon.png ├── demo-copy-paste-position-size.gif ├── package-lock.json ├── package.json ├── sketch-assets └── icons.sketch └── src ├── copy-paste-position-size.js └── manifest.json /.appcast.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | sketch-copy-paste-position-size.sketchplugin 3 | 4 | # npm 5 | node_modules 6 | .npm 7 | npm-debug.log 8 | 9 | # mac 10 | .DS_Store 11 | 12 | # WebStorm 13 | .idea 14 | 15 | # sketch 16 | # sketch-assets 17 | -------------------------------------------------------------------------------- /Copy-Paste-Position-Size/.gitignore: -------------------------------------------------------------------------------- 1 | # build artifacts 2 | copy-paste-position-size.sketchplugin 3 | 4 | # npm 5 | node_modules 6 | .npm 7 | npm-debug.log 8 | 9 | # mac 10 | .DS_Store 11 | 12 | # WebStorm 13 | .idea 14 | 15 | # sketch 16 | # sketch-assets 17 | -------------------------------------------------------------------------------- /Copy-Paste-Position-Size/README.md: -------------------------------------------------------------------------------- 1 | # Copy-Paste-Position-Size 2 | 3 | ## Installation 4 | 5 | - [Download](../../releases/latest/download/copy-paste-position-size.sketchplugin.zip) the latest release of the plugin 6 | - Un-zip 7 | - Double-click on copy-paste-position-size.sketchplugin 8 | 9 | ## Development Guide 10 | 11 | _This plugin was created using `skpm`. For a detailed explanation on how things work, checkout the [skpm Readme](https://github.com/skpm/skpm/blob/master/README.md)._ 12 | 13 | ### Usage 14 | 15 | Install the dependencies 16 | 17 | ```bash 18 | npm install 19 | ``` 20 | 21 | Once the installation is done, you can run some commands inside the project folder: 22 | 23 | ```bash 24 | npm run build 25 | ``` 26 | 27 | To watch for changes: 28 | 29 | ```bash 30 | npm run watch 31 | ``` 32 | 33 | Additionally, if you wish to run the plugin every time it is built: 34 | 35 | ```bash 36 | npm run start 37 | ``` 38 | 39 | ### Custom Configuration 40 | 41 | #### Babel 42 | 43 | To customize Babel, you have two options: 44 | 45 | - You may create a [`.babelrc`](https://babeljs.io/docs/usage/babelrc) file in your project's root directory. Any settings you define here will overwrite matching config-keys within skpm preset. For example, if you pass a "presets" object, it will replace & reset all Babel presets that skpm defaults to. 46 | 47 | - If you'd like to modify or add to the existing Babel config, you must use a `webpack.skpm.config.js` file. Visit the [Webpack](#webpack) section for more info. 48 | 49 | #### Webpack 50 | 51 | To customize webpack create `webpack.skpm.config.js` file which exports function that will change webpack's config. 52 | 53 | ```js 54 | /** 55 | * Function that mutates original webpack config. 56 | * Supports asynchronous changes when promise is returned. 57 | * 58 | * @param {object} config - original webpack config. 59 | * @param {boolean} isPluginCommand - whether the config is for a plugin command or a resource 60 | **/ 61 | module.exports = function(config, isPluginCommand) { 62 | /** you can change config here **/ 63 | } 64 | ``` 65 | 66 | ### Debugging 67 | 68 | To view the output of your `console.log`, you have a few different options: 69 | 70 | - Use the [`sketch-dev-tools`](https://github.com/skpm/sketch-dev-tools) 71 | - Run `skpm log` in your Terminal, with the optional `-f` argument (`skpm log -f`) which causes `skpm log` to not stop when the end of logs is reached, but rather to wait for additional data to be appended to the input 72 | 73 | ### Publishing your plugin 74 | 75 | ```bash 76 | skpm publish 77 | ``` 78 | 79 | (where `bump` can be `patch`, `minor` or `major`) 80 | 81 | `skpm publish` will create a new release on your GitHub repository and create an appcast file in order for Sketch users to be notified of the update. 82 | 83 | You will need to specify a `repository` in the `package.json`: 84 | 85 | ```diff 86 | ... 87 | + "repository" : { 88 | + "type": "git", 89 | + "url": "git+https://github.com/ORG/NAME.git" 90 | + } 91 | ... 92 | ``` 93 | -------------------------------------------------------------------------------- /Copy-Paste-Position-Size/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejilderda/Sketch-Copy-Paste-Position-Size/47162efa13fe7fbccc81f5197f465fcac380833e/Copy-Paste-Position-Size/assets/icon.png -------------------------------------------------------------------------------- /Copy-Paste-Position-Size/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "copy-paste-position-size", 3 | "description": "", 4 | "version": "0.1.0", 5 | "engines": { 6 | "sketch": ">=49.0" 7 | }, 8 | "skpm": { 9 | "name": "Copy-Paste-Position-Size", 10 | "manifest": "src/manifest.json", 11 | "main": "copy-paste-position-size.sketchplugin", 12 | "assets": [ 13 | "assets/**/*" 14 | ], 15 | "sketch-assets-file": "sketch-assets/icons.sketch" 16 | }, 17 | "scripts": { 18 | "build": "skpm-build", 19 | "watch": "skpm-build --watch", 20 | "start": "skpm-build --watch --run", 21 | "postinstall": "npm run build && skpm-link" 22 | }, 23 | "devDependencies": { 24 | "@skpm/builder": "^0.7.0" 25 | }, 26 | "author": "Andre Jilderda " 27 | } 28 | -------------------------------------------------------------------------------- /Copy-Paste-Position-Size/sketch-assets/icons.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejilderda/Sketch-Copy-Paste-Position-Size/47162efa13fe7fbccc81f5197f465fcac380833e/Copy-Paste-Position-Size/sketch-assets/icons.sketch -------------------------------------------------------------------------------- /Copy-Paste-Position-Size/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/sketch-hq/SketchAPI/develop/docs/sketch-plugin-manifest-schema.json", 3 | "icon": "icon.png", 4 | "commands": [ 5 | { 6 | "name": "my-command", 7 | "identifier": "copy-paste-position-size.my-command-identifier", 8 | "script": "./my-command.js" 9 | } 10 | ], 11 | "menu": { 12 | "title": "Copy-Paste-Position-Size", 13 | "items": [ 14 | "copy-paste-position-size.my-command-identifier" 15 | ] 16 | } 17 | } -------------------------------------------------------------------------------- /Copy-Paste-Position-Size/src/my-command.js: -------------------------------------------------------------------------------- 1 | import sketch from 'sketch' 2 | // documentation: https://developer.sketchapp.com/reference/api/ 3 | 4 | export default function() { 5 | sketch.UI.message("It's alive 🙌") 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sketch Copy Paste Position & Size 2 | A Sketch plugin that lets you easily copy & paste width, height, x & y values from and to objects! 3 | 4 | ## Demo 5 | ![Using Copy Paste Position & Size](demo-copy-paste-position-size.gif) 6 | 7 | ## How to use 8 | Just run 'Copy' ctrl + shift + c to store an object's width, height, x & y axis for reuse. Select another layer and use one of the 'Paste' plugins. For example ctrl + shift + v) will paste the size to the selected object(s). 9 | 10 | Multiple layer support for copying (will store the selection bounds) as well as pasting. The following actions are available: 11 | 12 | ## Shortcuts 13 | | Action | Shortcut | 14 | | :------- | :---- | 15 | | Copy* | ctrl + shift + c | 16 | | Paste Size | ctrl + shift + v | 17 | | Paste Width | ctrl + shift + w | 18 | | Paste Height | ctrl + shift + h | 19 | | Paste X | ctrl + shift + x | 20 | | Paste Y | ctrl + shift + y | 21 | | Paste Position *(X & Y)* | ctrl + shift + p | 22 | | Paste Width Porportionally** | ctrl + shift + alt + w | 23 | | Paste Height Porportionally** | ctrl + shift + alt + h | 24 | 25 | \*The values are copied within Sketch and won't interfere with your OS' clipboard. 26 | \**Paste proportionally will respect the selected layer's aspect ratio. 27 | 28 | ## Installation 29 | 1. [Download](https://github.com/ajilderda/Sketch-Copy-Paste-Position-Size/releases/latest/download/sketch-copy-paste-position-size.sketchplugin.zip) the latest release. 30 | 2. Run ‘Copy Paste Position Size.sketchplugin’ to install 31 | 32 | ## Thanks to 33 | This plugin is inspired by John Dunning's [Copy Paste WHXY](http://johndunning.com/fireworks/about/CopyPasteWHXY), a Fireworks plugin I've used for a long time! 34 | 35 | ## Feedback 36 | If you find any issues or have any suggestions, please create an issue. Pull requests are welcome also! 37 | 38 | ## Created by 39 | [Andre Jilderda](https://github.com/ajilderda) 40 | -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejilderda/Sketch-Copy-Paste-Position-Size/47162efa13fe7fbccc81f5197f465fcac380833e/assets/icon.png -------------------------------------------------------------------------------- /demo-copy-paste-position-size.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejilderda/Sketch-Copy-Paste-Position-Size/47162efa13fe7fbccc81f5197f465fcac380833e/demo-copy-paste-position-size.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sketch-copy-paste-position-size", 3 | "description": "Easily copy and paste width, height, x & y values from and to objects", 4 | "version": "1.6.1", 5 | "engines": { 6 | "sketch": ">=49.0" 7 | }, 8 | "skpm": { 9 | "name": "Sketch-Copy-Paste-Position-Size", 10 | "manifest": "src/manifest.json", 11 | "main": "sketch-copy-paste-position-size.sketchplugin", 12 | "assets": [ 13 | "assets/**/*" 14 | ], 15 | "sketch-assets-file": "sketch-assets/icons.sketch" 16 | }, 17 | "scripts": { 18 | "build": "skpm-build", 19 | "watch": "skpm-build --watch", 20 | "start": "skpm-build --watch --run", 21 | "postinstall": "npm run build && skpm-link" 22 | }, 23 | "devDependencies": { 24 | "@skpm/builder": "^0.7.0" 25 | }, 26 | "author": "Andre Jilderda ", 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/ajilderda/Sketch-Copy-Paste-Position-Size.git" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sketch-assets/icons.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejilderda/Sketch-Copy-Paste-Position-Size/47162efa13fe7fbccc81f5197f465fcac380833e/sketch-assets/icons.sketch -------------------------------------------------------------------------------- /src/copy-paste-position-size.js: -------------------------------------------------------------------------------- 1 | const Group = require('sketch/dom').Group; 2 | const Text = require('sketch/dom').Text; 3 | const document = require('sketch/dom').getSelectedDocument(); 4 | const Settings = require('sketch/settings'); 5 | const UI = require('sketch/ui'); 6 | 7 | const pixelFit = (input) => { 8 | return Settings.globalSettingForKey("tryToFitToPixelBounds") ? Math.round(input) : input; 9 | } 10 | 11 | const getRelativeCoordinates = layer => layer.frame.changeBasis({ 12 | from: layer.parent, 13 | to: layer.getParentArtboard() 14 | }); 15 | 16 | export function onCopy() { 17 | const selection = document.selectedLayers; 18 | if (!selection.length) return UI.message('Please select at least one layer to copy from.'); 19 | 20 | if (selection.length === 1) { 21 | const layer = selection.layers[0]; 22 | const frame = getRelativeCoordinates(layer); 23 | 24 | const copiedWidth = pixelFit(frame.width); 25 | const copiedHeight = pixelFit(frame.height); 26 | const copiedX = pixelFit(frame.x); 27 | const copiedY = pixelFit(frame.y); 28 | 29 | Settings.setSettingForKey("copiedWidth", copiedWidth); 30 | Settings.setSettingForKey("copiedHeight", copiedHeight); 31 | Settings.setSettingForKey("copiedX", copiedX); 32 | Settings.setSettingForKey("copiedY", copiedY); 33 | 34 | UI.message(`📋 Width: ${pixelFit(copiedWidth)} Height: ${pixelFit(copiedHeight)} X: ${pixelFit(copiedX)} Y: ${pixelFit(copiedY)}`); 35 | } else { 36 | const { layers } = selection; 37 | 38 | const selectionBounds = layers.reduce((acc, layer) => { 39 | const coordinates = getRelativeCoordinates(layer); 40 | const selectionBoundR = coordinates.x + coordinates.width; 41 | const selectionBoundB = coordinates.y + coordinates.height; 42 | 43 | return { 44 | l: [...acc.l, coordinates.x], 45 | t: [...acc.t, coordinates.y], 46 | r: [...acc.r, selectionBoundR], 47 | b: [...acc.b, selectionBoundB], 48 | } 49 | }, { l: [], t: [], r: [], b: [] }); 50 | 51 | const selectionBoundL = Math.min(...selectionBounds.l); 52 | const selectionBoundT = Math.min(...selectionBounds.t); 53 | const selectionBoundR = Math.max(...selectionBounds.r); 54 | const selectionBoundB = Math.max(...selectionBounds.b); 55 | const selectionWidth = pixelFit(selectionBoundR - selectionBoundL); 56 | const selectionHeight = pixelFit(selectionBoundB - selectionBoundT); 57 | 58 | Settings.setSettingForKey("copiedWidth", selectionWidth); 59 | Settings.setSettingForKey("copiedHeight", selectionHeight); 60 | Settings.setSettingForKey("copiedX", pixelFit(selectionBoundL)); 61 | Settings.setSettingForKey("copiedY", pixelFit(selectionBoundT)); 62 | 63 | UI.message(`📋 Width: ${pixelFit(selectionWidth)} Height: ${pixelFit(selectionHeight)} X: ${pixelFit(selectionBoundL)} Y: ${pixelFit(selectionBoundT)}`); 64 | } 65 | } 66 | 67 | export function pasteWHXY(w,h,x,y,proportional) { 68 | const { selectedLayers } = document; 69 | 70 | selectedLayers.forEach( layer => { 71 | const { frame } = layer; 72 | 73 | const newWidth = pixelFit(Settings.settingForKey('copiedWidth')); 74 | const newHeight = pixelFit(Settings.settingForKey('copiedHeight')); 75 | const newX = pixelFit(Settings.settingForKey('copiedX')); 76 | const newY = pixelFit(Settings.settingForKey('copiedY')); 77 | 78 | // we must run this function twice to get the layer frame updated properly. 79 | // once before and once after the layer was resized. 80 | if ( proportional || w || h ) fitTextFrame( layer ); 81 | 82 | // Set width / height 83 | if( proportional ) { 84 | const oldWidth = frame.width; 85 | const oldHeight = frame.height; 86 | 87 | if (w) { 88 | const proportion = newWidth / oldWidth; 89 | frame.width = newWidth; 90 | frame.height = oldHeight * proportion; 91 | } 92 | if (h) { 93 | const proportion = newHeight / oldHeight; 94 | frame.height = newHeight; 95 | frame.width = oldWidth * proportion; 96 | } 97 | } 98 | else { 99 | if (w) frame.width = newWidth; 100 | if (h) frame.height = newHeight; 101 | } 102 | 103 | // run the fitTextFrame function again 104 | if ( proportional || w || h ) fitTextFrame( layer ); 105 | 106 | // Set position 107 | if (x) frame.x = newX; 108 | if (y) frame.y = newY; 109 | }) 110 | } 111 | 112 | // below 2 functions were partly borrowed from https://github.com/juliussohn/sketch-textbox-fit-content 113 | // attempt to fix the textbox height after resizing. There are however issues with (text)layers with the 114 | // 'Fix height' property set and there doesn't seem to be a way to set this value programmatically 115 | function fitTextFrame( layer ) { 116 | if ( layer instanceof MSTextLayer ) { 117 | // convert to wrapped API object, since some methods are only available in the Sketch API 118 | layer = Text.fromNative( layer ); 119 | layer.fixedWidth = true; 120 | 121 | // adjust the layer frame height based on the text lines 122 | const lineCount = layer.fragments.length; 123 | const baseHeight = layer.fragments[lineCount - 1].rect.y + layer.fragments[lineCount - 1].rect.height; 124 | layer.frame.height = baseHeight; 125 | } 126 | else if ( layer instanceof MSLayerGroup ) { 127 | // adjust group frame to fit children 128 | layer.resizeToFitChildrenWithOption(0) 129 | 130 | const group = Group.fromNative(layer); 131 | const groupLayers = group.layers; 132 | for (let layer of groupLayers) { 133 | fitTextFrame( layer.sketchObject ); 134 | }; 135 | group.adjustToFit(); 136 | } 137 | } 138 | 139 | export function onPasteSize() { 140 | pasteWHXY(true, true, false, false, false); //WHXY & proportional 141 | } 142 | 143 | export function onPasteWidth() { 144 | pasteWHXY(true, false, false, false, false); 145 | } 146 | 147 | export function onPasteHeight() { 148 | pasteWHXY(false, true, false, false, false); 149 | } 150 | 151 | export function onPasteSizePorportionally() { 152 | pasteWHXY(true, true, false, false, true); 153 | } 154 | 155 | export function onPasteWidthPorportionally() { 156 | pasteWHXY(true, false, false, false, true); 157 | } 158 | 159 | export function onPasteHeightPorportionally() { 160 | pasteWHXY(false, true, false, false, true); 161 | } 162 | 163 | export function onPastePosition() { 164 | pasteWHXY(false, false, true, true, false); 165 | } 166 | 167 | export function onPasteX() { 168 | pasteWHXY(false, false, true, false, false); 169 | } 170 | 171 | export function onPasteY() { 172 | pasteWHXY(false, false, false, true, false); 173 | } 174 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/sketch-hq/SketchAPI/develop/docs/sketch-plugin-manifest-schema.json", 3 | "icon": "icon.png", 4 | "name" : "Copy Paste Position Size", 5 | "identifier" : "com.sketchapp.copypastepositionsize", 6 | "description" : "Easily copy and paste width, height, x & y values from and to objects", 7 | "authorEmail" : "mail@andrejilderda.nl", 8 | "author" : "Andre Jilderda", 9 | "commands": [ 10 | { 11 | "name" : "Copy", 12 | "script" : "copy-paste-position-size.js", 13 | "handler" : "onCopy", 14 | "shortcut" : "ctrl shift c", 15 | "identifier" : "onCopy" 16 | }, 17 | { 18 | "name" : "Paste Size", 19 | "script" : "copy-paste-position-size.js", 20 | "handler" : "onPasteSize", 21 | "shortcut" : "ctrl shift v", 22 | "identifier" : "onPasteSize" 23 | }, 24 | { 25 | "name" : "Paste Width", 26 | "script" : "copy-paste-position-size.js", 27 | "handler" : "onPasteWidth", 28 | "shortcut" : "ctrl shift w", 29 | "identifier" : "onPasteWidth" 30 | }, 31 | { 32 | "name" : "Paste Height", 33 | "script" : "copy-paste-position-size.js", 34 | "handler" : "onPasteHeight", 35 | "shortcut" : "ctrl shift h", 36 | "identifier" : "onPasteHeight" 37 | }, 38 | { 39 | "name" : "Paste Width Porportionally", 40 | "script" : "copy-paste-position-size.js", 41 | "handler" : "onPasteWidthPorportionally", 42 | "shortcut" : "ctrl shift alt w", 43 | "identifier" : "onPasteWidthPorportionally" 44 | }, 45 | { 46 | "name" : "Paste Height Porportionally", 47 | "script" : "copy-paste-position-size.js", 48 | "handler" : "onPasteHeightPorportionally", 49 | "shortcut" : "ctrl shift alt h", 50 | "identifier" : "onPasteHeightPorportionally" 51 | }, 52 | { 53 | "name" : "Paste Position", 54 | "script" : "copy-paste-position-size.js", 55 | "handler" : "onPastePosition", 56 | "shortcut" : "ctrl shift p", 57 | "identifier" : "onPastePosition" 58 | }, 59 | { 60 | "name" : "Paste X", 61 | "script" : "copy-paste-position-size.js", 62 | "handler" : "onPasteX", 63 | "shortcut" : "ctrl shift x", 64 | "identifier" : "onPasteX" 65 | }, 66 | { 67 | "name" : "Paste Y", 68 | "script" : "copy-paste-position-size.js", 69 | "handler" : "onPasteY", 70 | "shortcut" : "ctrl shift y", 71 | "identifier" : "onPasteY" 72 | } 73 | ], 74 | "menu": { 75 | "title": "Copy Paste Position Size", 76 | "items": [ 77 | "onCopy", 78 | "onPasteSize", 79 | "onPasteWidth", 80 | "onPasteHeight", 81 | "onPasteSizePorportionally", 82 | "onPasteWidthPorportionally", 83 | "onPasteHeightPorportionally", 84 | "onPastePosition", 85 | "onPasteX", 86 | "onPasteY" 87 | ] 88 | } 89 | } --------------------------------------------------------------------------------