├── .gitignore ├── README.md ├── code.js ├── code.ts ├── cover.png ├── manifest.json ├── package-lock.json ├── package.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | public/index.html 4 | src/build 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Wire Box](https://d2ddoduugvun08.cloudfront.net/items/3T13420R151f3z36032C/cover.png "Wire Box") 2 | 3 | A Figma plugin to make your designs go from hi-fi to lo-fi 4 | 5 | ### Setup 6 | 7 | 1. Go To Plugins > Manage Plugins 8 | 2. On the right You'll see `Installed`. There's a spot for `Development` underneath 9 | 3. Click the `+` 10 | 4. Find or drag the `manifest.json` from this repo to link the plugin 11 | 12 | ### How to use 13 | 14 | 1. Grab the components or instances or frame you want to convert 15 | 2. Run Wire Box from Plugins > Development > Wire Box 16 | 3. Profit -------------------------------------------------------------------------------- /code.js: -------------------------------------------------------------------------------- 1 | figma.loadFontAsync({ family: "Roboto", style: "Regular" }); 2 | let pink1 = 221 / 255; 3 | let pink2 = 0 / 255; 4 | let pink3 = 169 / 255; 5 | let blue1 = 36 / 255; 6 | let blue2 = 0 / 255; 7 | let blue3 = 255 / 255; 8 | let gold1 = 170 / 255; 9 | let gold2 = 102 / 255; 10 | let gold3 = 0 / 255; 11 | let green1 = 2 / 255; 12 | let green2 = 97 / 255; 13 | let green3 = 40 / 255; 14 | let charcoal1 = 31 / 255; 15 | function fireItUp(color1, color2, color3) { 16 | setTimeout(function () { 17 | const nodes = figma.currentPage.selection; 18 | let selectedLayers = nodes; 19 | let nodesParent = nodes; 20 | let entirePage = figma.currentPage.children; 21 | let arrayParentX = []; 22 | let arrayParentY = []; 23 | let arrayAll = []; 24 | let arrayPagePosX = []; 25 | let arrayPagePosY = []; 26 | let arrayPageWidth = []; 27 | let frameNames = []; 28 | function errorMsg() { 29 | figma.closePlugin('⚠️ Please select a Frame to run Wire Box ⚠️'); 30 | } 31 | if (selectedLayers.length === 0) { 32 | errorMsg(); 33 | } 34 | else { 35 | entirePage.forEach(pageFrame => { 36 | if (pageFrame.type === 'FRAME') { 37 | let pageX = pageFrame.x; 38 | let pageY = pageFrame.y; 39 | let pageWidth = pageFrame.width; 40 | arrayPagePosX.push(pageX); 41 | arrayPagePosY.push(pageY); 42 | arrayPageWidth.push(pageWidth); 43 | frameNames.push(pageFrame.name); 44 | let finalPagePosX = Math.max(...arrayPagePosX); 45 | let finalPagePosY = Math.min(...arrayPagePosY); 46 | } 47 | }); 48 | nodesParent.forEach(mainParent => { 49 | // Ensure that we only fire the plugin when a Frame is selected 50 | if (mainParent.type === 'FRAME') { 51 | const frameX = mainParent.x; 52 | const frameY = mainParent.y; 53 | arrayParentX.push(frameX); 54 | arrayParentY.push(frameY); 55 | const frame = figma.createFrame(); 56 | frame.name = "Wire Box / " + mainParent.name; 57 | frame.clipsContent = true; 58 | frame.setRelaunchData({ edit: "This frame was created with Wire Box, a hi-fi to lo-fi plugin" }); 59 | // Define shape types 60 | function rectOutline(node) { 61 | const rect = figma.createRectangle(); 62 | let transformPos = node.absoluteTransform; 63 | let newX = transformPos[0][2]; 64 | let newY = transformPos[1][2]; 65 | rect.name = node.name; 66 | rect.x = newX; 67 | rect.y = newY; 68 | rect.rotation = node.rotation; 69 | rect.resize(node.width, node.height); 70 | rect.fills = [{ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }]; 71 | rect.strokes = [{ type: 'SOLID', color: { r: color1, g: color2, b: color3 } }]; 72 | if (node.topLeftRadius >= 1 || node.topRightRadius >= 1 || node.bottomLeftRadius >= 1 || node.bottomRightRadius >= 1) { 73 | rect.topRightRadius = node.topRightRadius; 74 | rect.topLeftRadius = node.topLeftRadius; 75 | rect.bottomLeftRadius = node.bottomLeftRadius; 76 | rect.bottomRightRadius = node.bottomRightRadius; 77 | } 78 | frame.appendChild(rect); 79 | arrayAll.push(rect); 80 | } 81 | function rectOutlineNoBg(node) { 82 | const rect = figma.createRectangle(); 83 | let transformPos = node.absoluteTransform; 84 | let newX = transformPos[0][2]; 85 | let newY = transformPos[1][2]; 86 | rect.name = node.name; 87 | rect.x = newX; 88 | rect.y = newY; 89 | rect.rotation = node.rotation; 90 | rect.resize(node.width, node.height); 91 | rect.fills = []; 92 | rect.strokes = [{ type: 'SOLID', color: { r: color1, g: color2, b: color3 } }]; 93 | if (node.topLeftRadius >= 1 || node.topRightRadius >= 1 || node.bottomLeftRadius >= 1 || node.bottomRightRadius >= 1) { 94 | rect.topRightRadius = node.topRightRadius; 95 | rect.topLeftRadius = node.topLeftRadius; 96 | rect.bottomLeftRadius = node.bottomLeftRadius; 97 | rect.bottomRightRadius = node.bottomRightRadius; 98 | } 99 | frame.appendChild(rect); 100 | arrayAll.push(rect); 101 | } 102 | function vectorOutline(node) { 103 | const vect = figma.createVector(); 104 | const height = node.height; 105 | const width = node.width; 106 | let transformPos = node.absoluteTransform; 107 | let newX = transformPos[0][2]; 108 | let newY = transformPos[1][2]; 109 | vect.rotation = node.rotation; 110 | vect.resize(width, height); 111 | vect.vectorNetwork = node.vectorNetwork; 112 | vect.x = newX; 113 | vect.y = newY; 114 | vect.strokeWeight = 1; 115 | vect.strokes = [{ type: 'SOLID', color: { r: color1, g: color2, b: color3 } }]; 116 | frame.appendChild(vect); 117 | arrayAll.push(vect); 118 | } 119 | function textOutline(node) { 120 | const textBlock = figma.createText(); 121 | const height = node.height; 122 | const width = node.width; 123 | let transformPos = node.absoluteTransform; 124 | let newX = transformPos[0][2]; 125 | let newY = transformPos[1][2]; 126 | // console.log(node.name, newX, newY) 127 | textBlock.rotation = node.rotation; 128 | textBlock.name = node.name; 129 | textBlock.resize(width, height); 130 | textBlock.x = newX; 131 | textBlock.y = newY; 132 | textBlock.characters = node.characters; 133 | textBlock.fills = [{ type: 'SOLID', color: { r: color1, g: color2, b: color3 } }]; 134 | if (node.fontSize !== figma.mixed) { 135 | textBlock.fontSize = node.fontSize; 136 | } 137 | else { 138 | textBlock.fontSize = 14; 139 | } 140 | if (node.lineHeight !== figma.mixed) { 141 | textBlock.lineHeight = node.lineHeight; 142 | } 143 | if (node.paragraphSpacing !== figma.mixed) { 144 | textBlock.paragraphSpacing = node.paragraphSpacing; 145 | } 146 | textBlock.textAlignHorizontal = node.textAlignHorizontal; 147 | textBlock.textAlignVertical = node.textAlignVertical; 148 | if (node.textCase !== figma.mixed) { 149 | textBlock.textCase = node.textCase; 150 | } 151 | frame.appendChild(textBlock); 152 | arrayAll.push(textBlock); 153 | } 154 | function ellipseOutline(node) { 155 | const ellip = figma.createEllipse(); 156 | const height = node.height; 157 | const width = node.width; 158 | let transformPos = node.absoluteTransform; 159 | let newX = transformPos[0][2]; 160 | let newY = transformPos[1][2]; 161 | ellip.rotation = node.rotation; 162 | ellip.resize(width, height); 163 | ellip.x = newX; 164 | ellip.y = newY; 165 | ellip.strokeWeight = 1; 166 | ellip.fills = [{ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }]; 167 | ellip.strokes = [{ type: 'SOLID', color: { r: color1, g: color2, b: color3 } }]; 168 | frame.appendChild(ellip); 169 | arrayAll.push(ellip); 170 | } 171 | function polygonOutline(node) { 172 | const poly = figma.createPolygon(); 173 | const height = node.height; 174 | const width = node.width; 175 | let transformPos = node.absoluteTransform; 176 | let newX = transformPos[0][2]; 177 | let newY = transformPos[1][2]; 178 | poly.rotation = node.rotation; 179 | poly.resize(width, height); 180 | poly.x = newX; 181 | poly.y = newY; 182 | poly.strokeWeight = 1; 183 | poly.fills = [{ type: 'SOLID', color: { r: 1, g: 1, b: 1 } }]; 184 | poly.strokes = [{ type: 'SOLID', color: { r: color1, g: color2, b: color3 } }]; 185 | frame.appendChild(poly); 186 | arrayAll.push(poly); 187 | } 188 | // These functions dive deeper into the configuration of layers - sometimes layers have fills but arent visible 189 | function hasVisibleBackgrounds(backgrounds) { 190 | return backgrounds.find(background => background.visible); 191 | } 192 | function hasVisibleFills(fills) { 193 | return fills.find(fill => fill.visible && (fill.type === 'SOLID' || 194 | fill.type === 'GRADIENT_LINEAR' || 195 | fill.type === 'GRADIENT_RADIAL' || 196 | fill.type === 'GRADIENT_ANGULAR' || 197 | fill.type === 'GRADIENT_DIAMOND' || 198 | fill.type === 'IMAGE')); 199 | } 200 | function hasVisibleStrokes(strokes) { 201 | return strokes.find(stroke => stroke.visible && stroke.type === 'SOLID'); 202 | } 203 | // Determine parameters for showing 204 | function drawNode(node) { 205 | if ((node.type === 'INSTANCE' || node.type === 'FRAME' || node.type === 'COMPONENT') && hasVisibleBackgrounds(node.backgrounds)) { 206 | rectOutline(node); 207 | } 208 | // Check the width and height of the nodes as sometimes they can have a 0 height or width - which might just be a bug from Figmas end 209 | if (node.type === 'VECTOR') { 210 | if (node.width >= 0.1 && node.height >= 0.1 && node.isMask !== true) { 211 | vectorOutline(node); 212 | } 213 | } 214 | if (node.type === 'POLYGON') { 215 | if (node.width >= 0.1 && node.height >= 0.1 && node.isMask !== true) { 216 | polygonOutline(node); 217 | } 218 | } 219 | if (node.type === 'RECTANGLE') { 220 | if (node.fills.length >= 1 && 221 | node.width >= 0.1 && 222 | node.height >= 0.1 && 223 | node.opacity >= 0.5 && 224 | node.isMask !== true && 225 | node.name !== 'Bounds' && 226 | node.name !== 'bounds' && 227 | hasVisibleFills(node.fills)) { 228 | rectOutline(node); 229 | } 230 | if (node.strokeWeight >= 1.1 && 231 | node.name !== 'Bounds' && 232 | node.name !== 'bounds' && 233 | hasVisibleStrokes(node.strokes)) { 234 | rectOutlineNoBg(node); 235 | } 236 | if (node.strokeWeight === 1 && 237 | node.fills.length === 0 && 238 | node.name !== 'Bounds' && 239 | node.name !== 'bounds' && 240 | hasVisibleStrokes(node.strokes)) { 241 | rectOutlineNoBg(node); 242 | } 243 | } 244 | if (node.type === 'ELLIPSE' && node.width >= 0.1 && node.height >= 0.1) { 245 | ellipseOutline(node); 246 | } 247 | if (node.type === 'TEXT' && node.width >= 0.1 && node.height >= 0.1) { 248 | textOutline(node); 249 | } 250 | } 251 | // execute 252 | function recurse(parents) { 253 | let arrayWidth = []; 254 | let arrayHeight = []; 255 | function drawChildren(children) { 256 | children.forEach(child => { 257 | if (!child.visible) 258 | return; 259 | drawNode(child); 260 | // Recursively draw childen 261 | if ("children" in child) 262 | drawChildren(child.children); 263 | }); 264 | } 265 | parents.forEach(parent => { 266 | if (!parent.visible) 267 | return; 268 | drawNode(parent); 269 | arrayWidth.push(parent.width); 270 | arrayHeight.push(parent.height); 271 | drawChildren(parent.children); 272 | }); 273 | // Some math for determining the new size of the created frame 274 | let frameWidth = Math.max(...arrayWidth); 275 | let frameheight = Math.max(...arrayHeight); 276 | frame.resize(frameWidth, frameheight); 277 | frame.x = frameX + frameWidth + 100; 278 | frame.y = frameY; 279 | // Group all the newly created layers, which are then placed at 0x 0y so everythings neat and tidy 280 | figma.group(arrayAll, frame); 281 | const location = figma.group(arrayAll, frame); 282 | location.x = 0; 283 | location.y = 0; 284 | } 285 | recurse(nodes); 286 | // console.log(frameNames) 287 | figma.closePlugin(); 288 | figma.notify("Wire Box created"); 289 | } 290 | else { 291 | errorMsg(); 292 | } 293 | }); 294 | } 295 | console.log("fired new way"); 296 | }, 100); 297 | } // close function: fireitup 298 | figma.parameters.on('input', ({ query, key, result }) => { 299 | switch (key) { 300 | case 'color': 301 | const colors = ['pink', 'blue', 'charcoal', 'gold', 'green']; 302 | result.setSuggestions(colors.filter(s => s.includes(query))); 303 | break; 304 | default: 305 | return; 306 | } 307 | }); 308 | // check for parameters 309 | figma.on('run', ({ parameters }) => { 310 | if (parameters) { 311 | startPluginWithParameters(parameters); 312 | } 313 | else { 314 | fireItUp(pink1, pink2, pink3); 315 | } 316 | }); 317 | // if parameters selected 318 | function startPluginWithParameters(parameters) { 319 | var _a; 320 | if (parameters['color']) { 321 | const color = (_a = parameters['color']) !== null && _a !== void 0 ? _a : 'pink'; 322 | if (color == 'pink') { 323 | fireItUp(pink1, pink2, pink3); 324 | } 325 | if (color == 'blue') { 326 | fireItUp(blue1, blue2, blue3); 327 | } 328 | if (color == 'gold') { 329 | fireItUp(gold1, gold2, gold3); 330 | } 331 | if (color == 'charcoal') { 332 | fireItUp(charcoal1, charcoal1, charcoal1); 333 | } 334 | if (color == 'green') { 335 | fireItUp(green1, green2, green3); 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /code.ts: -------------------------------------------------------------------------------- 1 | figma.loadFontAsync({ family: "Roboto", style: "Regular" }) 2 | 3 | let pink1 = 221/255 4 | let pink2 = 0/255 5 | let pink3 = 169/255 6 | 7 | let blue1 = 36/255 8 | let blue2 = 0/255 9 | let blue3 = 255/255 10 | 11 | let gold1 = 170/255 12 | let gold2 = 102/255 13 | let gold3 = 0/255 14 | 15 | let green1 = 2/255 16 | let green2 = 97/255 17 | let green3 = 40/255 18 | 19 | let charcoal1 = 31/255 20 | 21 | function fireItUp(color1,color2,color3){ 22 | 23 | setTimeout(function(){ 24 | 25 | const nodes = figma.currentPage.selection 26 | let selectedLayers = nodes 27 | let nodesParent = nodes 28 | let entirePage = figma.currentPage.children 29 | 30 | let arrayParentX = [] 31 | let arrayParentY = [] 32 | let arrayAll = [] 33 | let arrayPagePosX = [] 34 | let arrayPagePosY = [] 35 | let arrayPageWidth = [] 36 | let frameNames = [] 37 | 38 | function errorMsg() { 39 | figma.closePlugin('⚠️ Please select a Frame to run Wire Box ⚠️') 40 | } 41 | 42 | if (selectedLayers.length === 0) { 43 | 44 | errorMsg() 45 | 46 | } else { 47 | 48 | entirePage.forEach(pageFrame => { 49 | 50 | if(pageFrame.type === 'FRAME') { 51 | 52 | let pageX = pageFrame.x 53 | let pageY = pageFrame.y 54 | let pageWidth = pageFrame.width 55 | 56 | arrayPagePosX.push(pageX) 57 | arrayPagePosY.push(pageY) 58 | arrayPageWidth.push(pageWidth) 59 | frameNames.push(pageFrame.name) 60 | 61 | let finalPagePosX = Math.max(...arrayPagePosX) 62 | let finalPagePosY = Math.min(...arrayPagePosY) 63 | 64 | } 65 | }) 66 | 67 | nodesParent.forEach(mainParent => { 68 | 69 | // Ensure that we only fire the plugin when a Frame is selected 70 | 71 | if (mainParent.type === 'FRAME') { 72 | 73 | const frameX = mainParent.x 74 | const frameY = mainParent.y 75 | 76 | arrayParentX.push(frameX) 77 | arrayParentY.push(frameY) 78 | 79 | const frame = figma.createFrame() 80 | 81 | frame.name = "Wire Box / " + mainParent.name 82 | frame.clipsContent = true 83 | 84 | frame.setRelaunchData({edit: "This frame was created with Wire Box, a hi-fi to lo-fi plugin"}) 85 | 86 | // Define shape types 87 | 88 | function rectOutline(node) { 89 | const rect = figma.createRectangle() 90 | 91 | let transformPos = node.absoluteTransform 92 | let newX = transformPos[0][2] 93 | let newY = transformPos[1][2] 94 | 95 | rect.name = node.name 96 | rect.x = newX 97 | rect.y = newY 98 | rect.rotation = node.rotation 99 | rect.resize(node.width,node.height) 100 | rect.fills = [{type: 'SOLID', color: {r: 1, g: 1, b: 1}}] 101 | rect.strokes = [{type: 'SOLID', color: {r: color1, g: color2, b: color3}}] 102 | 103 | if(node.topLeftRadius >=1 || node.topRightRadius >=1 || node.bottomLeftRadius >=1 || node.bottomRightRadius >=1) { 104 | rect.topRightRadius = node.topRightRadius 105 | rect.topLeftRadius = node.topLeftRadius 106 | rect.bottomLeftRadius = node.bottomLeftRadius 107 | rect.bottomRightRadius = node.bottomRightRadius 108 | } 109 | 110 | frame.appendChild(rect) 111 | arrayAll.push(rect) 112 | } 113 | 114 | function rectOutlineNoBg(node) { 115 | const rect = figma.createRectangle() 116 | 117 | let transformPos = node.absoluteTransform 118 | let newX = transformPos[0][2] 119 | let newY = transformPos[1][2] 120 | 121 | rect.name = node.name 122 | rect.x = newX 123 | rect.y = newY 124 | rect.rotation = node.rotation 125 | rect.resize(node.width,node.height) 126 | rect.fills = [] 127 | rect.strokes = [{type: 'SOLID', color: {r: color1, g: color2, b: color3}}] 128 | 129 | if(node.topLeftRadius >=1 || node.topRightRadius >=1 || node.bottomLeftRadius >=1 || node.bottomRightRadius >=1) { 130 | rect.topRightRadius = node.topRightRadius 131 | rect.topLeftRadius = node.topLeftRadius 132 | rect.bottomLeftRadius = node.bottomLeftRadius 133 | rect.bottomRightRadius = node.bottomRightRadius 134 | } 135 | 136 | frame.appendChild(rect) 137 | arrayAll.push(rect) 138 | } 139 | 140 | function vectorOutline(node) { 141 | const vect = figma.createVector() 142 | const height = node.height 143 | const width = node.width 144 | 145 | let transformPos = node.absoluteTransform 146 | let newX = transformPos[0][2] 147 | let newY = transformPos[1][2] 148 | 149 | vect.rotation = node.rotation 150 | vect.resize(width,height) 151 | vect.vectorNetwork = node.vectorNetwork 152 | vect.x = newX 153 | vect.y = newY 154 | vect.strokeWeight = 1 155 | vect.strokes = [{type: 'SOLID', color: {r: color1, g: color2, b: color3}}] 156 | 157 | frame.appendChild(vect) 158 | arrayAll.push(vect) 159 | } 160 | 161 | function textOutline(node) { 162 | const textBlock = figma.createText() 163 | const height = node.height 164 | const width = node.width 165 | 166 | let transformPos = node.absoluteTransform 167 | let newX = transformPos[0][2] 168 | let newY = transformPos[1][2] 169 | 170 | // console.log(node.name, newX, newY) 171 | 172 | textBlock.rotation = node.rotation 173 | textBlock.name = node.name 174 | textBlock.resize(width,height) 175 | textBlock.x = newX 176 | textBlock.y = newY 177 | 178 | textBlock.characters = node.characters 179 | textBlock.fills = [{type: 'SOLID', color: {r: color1, g: color2, b: color3}}] 180 | 181 | if (node.fontSize !== figma.mixed) { 182 | textBlock.fontSize = node.fontSize 183 | } else { 184 | textBlock.fontSize = 14 185 | } 186 | 187 | if (node.lineHeight !== figma.mixed) { 188 | textBlock.lineHeight = node.lineHeight 189 | } 190 | 191 | if (node.paragraphSpacing !== figma.mixed) { 192 | textBlock.paragraphSpacing = node.paragraphSpacing 193 | } 194 | 195 | textBlock.textAlignHorizontal = node.textAlignHorizontal 196 | textBlock.textAlignVertical = node.textAlignVertical 197 | 198 | if (node.textCase !== figma.mixed) { 199 | textBlock.textCase = node.textCase 200 | } 201 | 202 | 203 | frame.appendChild(textBlock) 204 | arrayAll.push(textBlock) 205 | } 206 | 207 | function ellipseOutline(node) { 208 | const ellip = figma.createEllipse() 209 | const height = node.height 210 | const width = node.width 211 | 212 | let transformPos = node.absoluteTransform 213 | let newX = transformPos[0][2] 214 | let newY = transformPos[1][2] 215 | 216 | ellip.rotation = node.rotation 217 | ellip.resize(width,height) 218 | ellip.x = newX 219 | ellip.y = newY 220 | ellip.strokeWeight = 1 221 | ellip.fills = [{type: 'SOLID', color: {r: 1, g: 1, b: 1}}] 222 | ellip.strokes = [{type: 'SOLID', color: {r: color1, g: color2, b: color3}}] 223 | 224 | frame.appendChild(ellip) 225 | arrayAll.push(ellip) 226 | } 227 | 228 | function polygonOutline(node) { 229 | const poly = figma.createPolygon() 230 | const height = node.height 231 | const width = node.width 232 | 233 | let transformPos = node.absoluteTransform 234 | let newX = transformPos[0][2] 235 | let newY = transformPos[1][2] 236 | 237 | poly.rotation = node.rotation 238 | poly.resize(width,height) 239 | poly.x = newX 240 | poly.y = newY 241 | poly.strokeWeight = 1 242 | poly.fills = [{type: 'SOLID', color: {r: 1, g: 1, b: 1}}] 243 | poly.strokes = [{type: 'SOLID', color: {r: color1, g: color2, b: color3}}] 244 | 245 | frame.appendChild(poly) 246 | arrayAll.push(poly) 247 | } 248 | 249 | // These functions dive deeper into the configuration of layers - sometimes layers have fills but arent visible 250 | 251 | function hasVisibleBackgrounds(backgrounds) { 252 | return backgrounds.find(background => background.visible) 253 | } 254 | 255 | function hasVisibleFills(fills) { 256 | return fills.find(fill => fill.visible && ( 257 | fill.type === 'SOLID' || 258 | fill.type === 'GRADIENT_LINEAR' || 259 | fill.type === 'GRADIENT_RADIAL' || 260 | fill.type === 'GRADIENT_ANGULAR' || 261 | fill.type === 'GRADIENT_DIAMOND' || 262 | fill.type === 'IMAGE' 263 | )) 264 | } 265 | 266 | function hasVisibleStrokes(strokes) { 267 | return strokes.find(stroke => stroke.visible && stroke.type === 'SOLID') 268 | } 269 | 270 | // Determine parameters for showing 271 | 272 | function drawNode(node) { 273 | if ((node.type === 'INSTANCE' || node.type === 'FRAME' || node.type ==='COMPONENT') && hasVisibleBackgrounds(node.backgrounds)) { 274 | rectOutline(node) 275 | } 276 | 277 | // Check the width and height of the nodes as sometimes they can have a 0 height or width - which might just be a bug from Figmas end 278 | 279 | if (node.type === 'VECTOR') { 280 | if (node.width >= 0.1 && node.height >= 0.1 && node.isMask !== true) { 281 | vectorOutline(node) 282 | } 283 | } 284 | if (node.type === 'POLYGON') { 285 | if (node.width >= 0.1 && node.height >= 0.1 && node.isMask !== true) { 286 | polygonOutline(node) 287 | } 288 | } 289 | if (node.type === 'RECTANGLE') { 290 | if ( 291 | node.fills.length >=1 && 292 | node.width >= 0.1 && 293 | node.height >= 0.1 && 294 | node.opacity >= 0.5 && 295 | node.isMask !== true && 296 | node.name !== 'Bounds' && 297 | node.name !== 'bounds' && 298 | hasVisibleFills(node.fills) 299 | ) { 300 | 301 | rectOutline(node) 302 | 303 | } 304 | if ( 305 | node.strokeWeight >= 1.1 && 306 | node.name !== 'Bounds' && 307 | node.name !== 'bounds' && 308 | hasVisibleStrokes(node.strokes) 309 | ) { 310 | rectOutlineNoBg(node) 311 | } 312 | 313 | if ( 314 | node.strokeWeight === 1 && 315 | node.fills.length === 0 && 316 | node.name !== 'Bounds' && 317 | node.name !== 'bounds' && 318 | hasVisibleStrokes(node.strokes) 319 | ) { 320 | rectOutlineNoBg(node) 321 | 322 | } 323 | } 324 | if (node.type === 'ELLIPSE' && node.width >= 0.1 && node.height >= 0.1) { 325 | ellipseOutline(node) 326 | } 327 | if (node.type === 'TEXT' && node.width >= 0.1 && node.height >= 0.1) { 328 | textOutline(node) 329 | } 330 | } 331 | 332 | // execute 333 | 334 | function recurse(parents){ 335 | 336 | let arrayWidth = [] 337 | let arrayHeight = [] 338 | 339 | function drawChildren(children) { 340 | children.forEach(child => { 341 | if (!child.visible) return 342 | drawNode(child) 343 | 344 | // Recursively draw childen 345 | if ("children" in child) drawChildren(child.children) 346 | }) 347 | } 348 | 349 | parents.forEach(parent => { 350 | if (!parent.visible) return 351 | 352 | drawNode(parent) 353 | 354 | arrayWidth.push(parent.width) 355 | arrayHeight.push(parent.height) 356 | 357 | drawChildren(parent.children) 358 | }) 359 | 360 | // Some math for determining the new size of the created frame 361 | 362 | let frameWidth = Math.max(...arrayWidth) 363 | let frameheight = Math.max(...arrayHeight) 364 | frame.resize(frameWidth,frameheight) 365 | 366 | frame.x = frameX + frameWidth + 100 367 | frame.y = frameY 368 | 369 | // Group all the newly created layers, which are then placed at 0x 0y so everythings neat and tidy 370 | 371 | figma.group(arrayAll,frame) 372 | const location = figma.group(arrayAll,frame) 373 | 374 | location.x = 0 375 | location.y = 0 376 | } 377 | 378 | recurse(nodes) 379 | // console.log(frameNames) 380 | 381 | figma.closePlugin() 382 | figma.notify("Wire Box created") 383 | 384 | } else { 385 | errorMsg() 386 | } 387 | }) 388 | } 389 | 390 | console.log("fired new way") 391 | 392 | }, 100) 393 | 394 | } // close function: fireitup 395 | 396 | figma.parameters.on('input', ({query, key, result}: ParameterInputEvent) => { 397 | switch (key) { 398 | case 'color': 399 | const colors = ['pink', 'blue', 'charcoal', 'gold', 'green'] 400 | result.setSuggestions(colors.filter(s => s.includes(query))) 401 | break 402 | default: 403 | return 404 | } 405 | }) 406 | 407 | // check for parameters 408 | 409 | figma.on('run', ({parameters}: RunEvent) => { 410 | if (parameters) { 411 | startPluginWithParameters(parameters) 412 | } else { 413 | fireItUp(pink1,pink2,pink3) 414 | } 415 | }) 416 | 417 | // if parameters selected 418 | 419 | function startPluginWithParameters(parameters: ParameterValues) { 420 | if (parameters['color']) { 421 | const color = parameters['color'] ?? 'pink' 422 | 423 | if (color == 'pink') { 424 | fireItUp(pink1,pink2,pink3) 425 | } 426 | 427 | if (color == 'blue') { 428 | fireItUp(blue1,blue2,blue3) 429 | } 430 | 431 | if (color == 'gold') { 432 | fireItUp(gold1,gold2,gold3) 433 | } 434 | 435 | if (color == 'charcoal') { 436 | fireItUp(charcoal1,charcoal1,charcoal1) 437 | } 438 | 439 | if (color == 'green') { 440 | fireItUp(green1,green2,green3) 441 | } 442 | } 443 | } 444 | 445 | -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helpscout/figma-wirebox/25a73f8629641b758beaebeeacc1e341f9686e13/cover.png -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Wire Box", 3 | "id": "764471577604277919", 4 | "api": "1.0.0", 5 | "main": "code.js", 6 | "editorType": ["figma"], 7 | "relaunchButtons": [ 8 | {"command": "edit", "name": "Wire Box"} 9 | ], 10 | "parameterOnly": false, 11 | "parameters": [ 12 | { 13 | "name": "Color", 14 | "key": "color", 15 | "description": "Set the color of the Wire Box you want to add" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wirebox", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@figma/plugin-typings": { 8 | "version": "1.37.0", 9 | "resolved": "https://registry.npmjs.org/@figma/plugin-typings/-/plugin-typings-1.37.0.tgz", 10 | "integrity": "sha512-PfZWzRazevtDp6nfaypxS7jfkbLATTM6dLi0tIJiDvd27vYhX3wBiW1AeTqxtxjBdYY2QV1T34IJYjrOybr5wg==", 11 | "dev": true 12 | }, 13 | "@types/node": { 14 | "version": "16.11.1", 15 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz", 16 | "integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA==", 17 | "dev": true 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wirebox", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "code.js", 6 | "scripts": { 7 | "build": "tsc -p tsconfig.json" 8 | }, 9 | "author": "Figma", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/helpscout/figma-wirebox" 13 | }, 14 | "homepage": "https://github.com/helpscout/figma-wirebox", 15 | "devDependencies": { 16 | "@figma/plugin-typings": "^1.37.0", 17 | "@types/node": "^16.11.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "lib": ["es6"], 5 | "typeRoots": [ 6 | "./node_modules/@types", 7 | "./node_modules/@figma" 8 | ] 9 | }, 10 | } --------------------------------------------------------------------------------