Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ulf Schuster committed Jun 27, 2023
0 parents commit d6ae39c
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Node
*.log
*.log.*
node_modules

out/
dist/
code.js
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:8080",
"webRoot": "${workspaceFolder}"
}
]
}
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
GitHub Copilot: Sure, here's a possible repository description for this Figma plugin:

# Figma Spacing and Typography Plugin

This is a plugin for Figma that helps you update the spacing and typography of your designs. It replaces padding, gap, and font size values in selected frames and text nodes, using a set of predefined rules. The plugin also loads all the fonts used in the text nodes asynchronously, to ensure that the new font sizes are applied correctly.

## Features

- Replace padding, gap, and font size values in selected frames and text nodes
- Load all fonts used in the text nodes asynchronously
- Apply a set of predefined rules to calculate the new values
- Show an error message if no frame is selected or no text nodes are found
- Notify the user when the spacings and font sizes have been updated successfully

## Usage

To use the plugin, select a frame that contains nodes with padding, gap, or font size values that you want to update. Then, run the plugin from the Figma menu or using the keyboard shortcut. The plugin will replace the values in all the selected nodes, and notify you when the process is complete.

## Installation

To install the plugin, download the source code from this repository and open it in Figma. Then, go to the Plugins menu, select "Development" > "Create Plugin", and choose the "manifest.json" file from the source code folder. Finally, click "Create Plugin" and the plugin will be installed in your Figma account.

## License

This plugin is licensed under the MIT License. See the LICENSE file for more information.

199 changes: 199 additions & 0 deletions code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Function for loading all fonts used in a text node
const loadFonts = async (textNode: TextNode) => {
// Get an array of all the font names used in the text node
const fontNames = textNode.getRangeAllFontNames(0, textNode.characters.length);

// Load all the fonts asynchronously
await Promise.all(fontNames.map(figma.loadFontAsync));
};

// Define the new line heights as an array
const skipLineHeights = false;

// Get the selected frame
const selectedFrame = figma.currentPage.selection[0] as FrameNode;

if (selectedFrame) {
const nodesToReplace = [];

// Find all nodes with padding, gap, or font size values to replace
function findNodesToReplace(node: BaseNode) {
if ('paddingLeft' in node || 'paddingRight' in node || 'paddingTop' in node || 'paddingBottom' in node || 'itemSpacing' in node || 'gridStyleId' in node) {
nodesToReplace.push(node);
}

if ('children' in node) {
node.children.forEach(child => findNodesToReplace(child));
}
}

findNodesToReplace(selectedFrame);

// Replace padding and gap values in all nodes
nodesToReplace.forEach(node => {
if ('paddingLeft' in node) {
node.paddingLeft = replacePaddingValue(node.paddingLeft);
}

if ('paddingRight' in node) {
node.paddingRight = replacePaddingValue(node.paddingRight);
}

if ('paddingTop' in node) {
node.paddingTop = replacePaddingValue(node.paddingTop);
}

if ('paddingBottom' in node) {
node.paddingBottom = replacePaddingValue(node.paddingBottom);
}

if ('itemSpacing' in node) {
node.itemSpacing = replacePaddingValue(node.itemSpacing);
}

if ('gridStyleId' in node) {
node.gridStyleId = replacePaddingValue(node.gridStyleId);
}
});

// Replace font sizes and line heights in all text nodes
const textNodes = selectedFrame.findAll(node => node.type === "TEXT") as TextNode[];
if (textNodes.length === 0) {
// Show an error message if no text nodes are found
figma.notify("Spacings updated successfully.");
figma.closePlugin();
} else {
// Keep track of the number of fonts that are still loading
let numFontsLoading = 0;

textNodes.forEach(async textNode => {
// Load all fonts used in the text node
numFontsLoading++;
await loadFonts(textNode);

// Replace font size in the text node
const oldFontSize = textNode.fontSize;
const newFontSize = getNewFontSize(oldFontSize);
textNode.fontSize = newFontSize;

// Replace line height in the text node
if (textNode.lineHeight.unit === "PIXELS") {
const lineHeight = textNode.lineHeight.value;
const newLineHeight = getNewLineHeight(lineHeight);
textNode.lineHeight = { unit: "PIXELS", value: newLineHeight };
}
// Reset the line height of the text node to "auto"
if (skipLineHeights === true) {
textNode.lineHeight = { unit: "AUTO" };
}

// Decrement the count of fonts that are still loading
numFontsLoading--;
if (numFontsLoading === 0) {
// Close the plugin when all fonts have finished loading
figma.notify("Font sizes and spacings updated successfully.");
figma.closePlugin();
}
});
}
} else {
// Show an error message if no frame is selected
figma.notify("Please select a frame.");
figma.closePlugin();
}

function replacePaddingValue(value: number): number {
const paddingValuesToReplace = getPaddingValuesToReplace();
const paddingNewValues = paddingValuesToReplace.map(getNewPaddingValue);
const index = paddingValuesToReplace.indexOf(value);
if (index !== -1) {
return paddingNewValues[index];
}
return value;
}

function getPaddingValuesToReplace(): number[] {
const paddingValuesToReplace = new Set<number>();
const nodesToCheck = [selectedFrame];
while (nodesToCheck.length > 0) {
const node = nodesToCheck.pop();
if ('paddingLeft' in node) {
paddingValuesToReplace.add(node.paddingLeft);
}
if ('paddingRight' in node) {
paddingValuesToReplace.add(node.paddingRight);
}
if ('paddingTop' in node) {
paddingValuesToReplace.add(node.paddingTop);
}
if ('paddingBottom' in node) {
paddingValuesToReplace.add(node.paddingBottom);
}
if ('itemSpacing' in node) {
paddingValuesToReplace.add(node.itemSpacing);
}
if ('gridStyleId' in node) {
paddingValuesToReplace.add(node.gridStyleId);
}
if ('children' in node) {
nodesToCheck.push(...node.children);
}
}
return Array.from(paddingValuesToReplace);
}

function getNewPaddingValue(oldValue: number): number {
switch (true) {
case (oldValue <= 16):
return oldValue;
case (oldValue <= 24):
return Math.floor(oldValue / 16) * 14;
case (oldValue <= 32):
return Math.floor(oldValue / 16) * 12;
case (oldValue <= 48):
return Math.floor(oldValue / 16) * 10;
case (oldValue <= 80):
return Math.floor(oldValue / 16) * 8;
case (oldValue <= 128):
return Math.floor(oldValue / 16) * 6;
default:
return Math.floor(oldValue / 16) * 4;
}
}

function getNewLineHeight(oldValue: number): number {
switch (true) {
case (oldValue <= 16):
return oldValue;
case (oldValue <= 24):
case (oldValue <= 32):
return Math.floor(oldValue / 16) * 14;
case (oldValue <= 48):
return Math.floor(oldValue / 16) * 12;
case (oldValue <= 80):
return Math.floor(oldValue / 16) * 10;
case (oldValue <= 128):
case (oldValue <= 256):
return Math.floor(oldValue / 16) * 8;
default:
return Math.floor(oldValue / 16) * 4;
}
}
function getNewFontSize(oldValue: number): number {
switch (true) {
case (oldValue <= 16):
return oldValue;
case (oldValue <= 24):
case (oldValue <= 32):
return Math.floor(oldValue / 16) * 14;
case (oldValue <= 48):
return Math.floor(oldValue / 16) * 12;
case (oldValue <= 80):
return Math.floor(oldValue / 16) * 10;
case (oldValue <= 128):
case (oldValue <= 256):
return Math.floor(oldValue / 16) * 8;
default:
return Math.floor(oldValue / 16) * 4;
}
}
16 changes: 16 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Responsive Replacer Dynamic",
"id": "1252632497510105166",
"api": "1.0.0",
"main": "code.js",
"capabilities": [],
"enableProposedApi": false,
"editorType": [
"figma"
],
"networkAccess": {
"allowedDomains": [
"none"
]
}
}
35 changes: 35 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "Responsive Replacer Dynamic",
"version": "1.0.0",
"description": "Your Figma Plugin",
"main": "code.js",
"scripts": {
"build": "tsc -p tsconfig.json",
"watch": "npm run build -- --watch"
},
"author": "",
"license": "",
"devDependencies": {
"@figma/plugin-typings": "^1.65.0",
"typescript": "*"
}
}
11 changes: 11 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es6",
"lib": ["es6"],
"strict": true,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@figma"
]
}
}

0 comments on commit d6ae39c

Please sign in to comment.