├── .prettierrc.js ├── .prettierignore ├── typings └── index.d.ts ├── src ├── isFillableNode │ ├── index.ts │ ├── index.md │ └── __tests__ │ │ └── index.test.ts ├── isLocked │ ├── index.ts │ ├── index.md │ └── __tests__ │ │ └── index.test.ts ├── isEqual │ ├── index.ts │ ├── index.md │ └── __tests__ │ │ └── index.test.ts ├── isVisible │ ├── index.ts │ ├── index.md │ └── __tests__ │ │ └── index.test.ts ├── isImagePaint │ ├── index.tsx │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── isWithinInstance │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── toHsl │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── toRgb │ ├── index.ts │ ├── index.md │ └── __tests__ │ │ └── index.test.ts ├── isFrameNode │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── isPageNode │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── isTextNode │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── isEllipseNode │ ├── index.ts │ └── index.md ├── isInstanceNode │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── isComponentNode │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── toHex │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── _internal │ ├── isOfType.ts │ ├── toColor.ts │ ├── hasMatch.ts │ └── isDeepEqual.ts ├── getPluginData │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── setPluginData │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── isGradientPaint │ ├── index.tsx │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── copyToClipboardAsync │ ├── index.ts │ └── index.md ├── getColorContrast │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── isValidColor │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── isSameColor │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── copyToClipboard │ ├── __tests__ │ │ └── index.test.ts │ ├── index.ts │ └── index.md ├── getSelectedNodes │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── getRandomId │ ├── __tests__ │ │ └── index.test.ts │ ├── index.ts │ └── index.md ├── toSolidPaint │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── toImagePaint │ ├── index.ts │ ├── index.md │ └── __tests__ │ │ └── index.test.ts ├── solidPaintToWebRgb │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── getComponentProps │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── clone │ ├── index.ts │ ├── __tests__ │ │ └── index.test.ts │ └── index.md ├── getDropPosition │ ├── index.md │ └── index.ts ├── interface │ └── index.ts └── index.ts ├── .fatherrc.ts ├── .editorconfig ├── .gitignore ├── tsconfig.json ├── .github └── workflows │ └── test.yaml ├── docs ├── guide │ └── index.md └── index.md ├── LICENSE ├── README.md ├── package.json └── .umirc.ts /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@umijs/fabric').prettier; 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.svg 2 | **/*.ejs 3 | **/*.html 4 | package.json 5 | .umi 6 | .umi-production 7 | .umi-test 8 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Window { 3 | copy: (value: string) => void; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/isFillableNode/index.ts: -------------------------------------------------------------------------------- 1 | export default function isFillableNode(node: BaseNode): boolean { 2 | return 'fills' in node; 3 | } 4 | -------------------------------------------------------------------------------- /src/isLocked/index.ts: -------------------------------------------------------------------------------- 1 | import hasMatch from '../_internal/hasMatch'; 2 | 3 | export default hasMatch((node) => node.locked, true); 4 | -------------------------------------------------------------------------------- /src/isEqual/index.ts: -------------------------------------------------------------------------------- 1 | import isDeepEqual from '../_internal/isDeepEqual'; 2 | 3 | const isEqual = isDeepEqual; 4 | 5 | export default isEqual; 6 | -------------------------------------------------------------------------------- /src/isVisible/index.ts: -------------------------------------------------------------------------------- 1 | import hasMatch from '../_internal/hasMatch'; 2 | 3 | export default hasMatch((node) => node.visible === false, false); 4 | -------------------------------------------------------------------------------- /src/isImagePaint/index.tsx: -------------------------------------------------------------------------------- 1 | export default function isImagePaint(paint: Paint | null | undefined): boolean { 2 | return paint?.type === 'IMAGE'; 3 | } 4 | -------------------------------------------------------------------------------- /src/isWithinInstance/index.ts: -------------------------------------------------------------------------------- 1 | import hasMatch from '../_internal/hasMatch'; 2 | 3 | export default hasMatch((node) => node.type === 'INSTANCE', true); 4 | -------------------------------------------------------------------------------- /src/toHsl/index.ts: -------------------------------------------------------------------------------- 1 | import toColor from '../_internal/toColor'; 2 | import { ColorModel } from '../interface'; 3 | 4 | export default toColor(ColorModel.HSL); 5 | -------------------------------------------------------------------------------- /src/toRgb/index.ts: -------------------------------------------------------------------------------- 1 | import toColor from '../_internal/toColor'; 2 | import { ColorModel } from '../interface'; 3 | 4 | export default toColor(ColorModel.RGB); 5 | -------------------------------------------------------------------------------- /.fatherrc.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | // https://github.com/umijs/father-next/blob/master/docs/config.md 3 | esm: 'babel', 4 | cjs: 'babel', 5 | umd: true, 6 | }; 7 | -------------------------------------------------------------------------------- /src/isFrameNode/index.ts: -------------------------------------------------------------------------------- 1 | import isOfType from '../_internal/isOfType'; 2 | import { NodeTypeEnum } from '../interface'; 3 | 4 | export default isOfType(NodeTypeEnum.FRAME); 5 | -------------------------------------------------------------------------------- /src/isPageNode/index.ts: -------------------------------------------------------------------------------- 1 | import isOfType from '../_internal/isOfType'; 2 | import { NodeTypeEnum } from '../interface'; 3 | 4 | export default isOfType(NodeTypeEnum.PAGE); 5 | -------------------------------------------------------------------------------- /src/isTextNode/index.ts: -------------------------------------------------------------------------------- 1 | import isOfType from '../_internal/isOfType'; 2 | import { NodeTypeEnum } from '../interface'; 3 | 4 | export default isOfType(NodeTypeEnum.TEXT); 5 | -------------------------------------------------------------------------------- /src/isEllipseNode/index.ts: -------------------------------------------------------------------------------- 1 | import isOfType from '../_internal/isOfType'; 2 | import { NodeTypeEnum } from '../interface'; 3 | 4 | export default isOfType(NodeTypeEnum.ELLIPSE); 5 | -------------------------------------------------------------------------------- /src/isInstanceNode/index.ts: -------------------------------------------------------------------------------- 1 | import isOfType from '../_internal/isOfType'; 2 | import { NodeTypeEnum } from '../interface'; 3 | 4 | export default isOfType(NodeTypeEnum.INSTANCE); 5 | -------------------------------------------------------------------------------- /src/isComponentNode/index.ts: -------------------------------------------------------------------------------- 1 | import isOfType from '../_internal/isOfType'; 2 | import { NodeTypeEnum } from '../interface'; 3 | 4 | export default isOfType(NodeTypeEnum.COMPONENT); 5 | -------------------------------------------------------------------------------- /src/toHex/index.ts: -------------------------------------------------------------------------------- 1 | import { IColor } from '../interface'; 2 | import Color from 'color'; 3 | 4 | export default function toHex(color: IColor) { 5 | const theColor = Color(color); 6 | return theColor.hex(); 7 | } 8 | -------------------------------------------------------------------------------- /src/_internal/isOfType.ts: -------------------------------------------------------------------------------- 1 | import { NodeTypeEnum } from '../interface'; 2 | 3 | export default function isOfType(nodeType: NodeTypeEnum) { 4 | return (node: Partial) => { 5 | return node.type === nodeType; 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /src/getPluginData/index.ts: -------------------------------------------------------------------------------- 1 | export default function getPluginData(node: BaseNode, key: string): string | void { 2 | if (!node || node.removed || !node.getPluginData) { 3 | return; 4 | } 5 | return node.getPluginData(key); 6 | } 7 | -------------------------------------------------------------------------------- /src/setPluginData/index.ts: -------------------------------------------------------------------------------- 1 | export default function setPluginData(node: BaseNode, key: string, value: string): void { 2 | if (!node || node.removed || !node.setPluginData) { 3 | return; 4 | } 5 | node.setPluginData(key, value); 6 | } 7 | -------------------------------------------------------------------------------- /src/isGradientPaint/index.tsx: -------------------------------------------------------------------------------- 1 | export default function isGradientPaint(paint: Paint | null | undefined): boolean { 2 | return ['GRADIENT_LINEAR', 'GRADIENT_RADIAL', 'GRADIENT_ANGULAR', 'GRADIENT_DIAMOND'].some( 3 | (type) => paint?.type === type, 4 | ); 5 | } 6 | -------------------------------------------------------------------------------- /src/copyToClipboardAsync/index.ts: -------------------------------------------------------------------------------- 1 | export default function copyToClipboardAsync(value: string): Promise { 2 | if (navigator.clipboard) { 3 | return navigator.clipboard.writeText(value); 4 | } 5 | return Promise.reject(`Clipboard API is NOT supported in the browser`); 6 | } 7 | -------------------------------------------------------------------------------- /src/getColorContrast/index.ts: -------------------------------------------------------------------------------- 1 | import { IColor } from '../interface'; 2 | import Color from 'color'; 3 | 4 | export default function getColorContrast(colorA: IColor, colorB: IColor) { 5 | const _colorA = Color(colorA); 6 | const _colorB = Color(colorB); 7 | return _colorA.contrast(_colorB); 8 | } 9 | -------------------------------------------------------------------------------- /src/isValidColor/index.ts: -------------------------------------------------------------------------------- 1 | import { IColor } from '../interface'; 2 | import Color from 'color'; 3 | 4 | export default function isValidColor(color: IColor) { 5 | try { 6 | let _color = Color(color); 7 | return _color != null; 8 | } catch (e) { 9 | return false; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/isSameColor/index.ts: -------------------------------------------------------------------------------- 1 | import { IColor } from '../interface'; 2 | import toHex from '../toHex'; 3 | 4 | export default function isSameColor(colorA: IColor, colorB: IColor) { 5 | const colorAInHex = toHex(colorA); 6 | const colorBInHex = toHex(colorB); 7 | return colorAInHex === colorBInHex; 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /src/copyToClipboard/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import copyToClipboard from '../index'; 2 | 3 | describe('copyToClipboard', () => { 4 | it('should copy to clipboard', () => { 5 | document.execCommand = jest.fn(() => true); 6 | copyToClipboard('Copied'); 7 | expect(document.execCommand).toHaveBeenCalledWith('copy'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/getSelectedNodes/index.ts: -------------------------------------------------------------------------------- 1 | type Predicate = (node: SceneNode) => boolean; 2 | 3 | export default function getSelectedNodes(predicate: Predicate = () => true): SceneNode[] { 4 | const allSelectedNodes = figma.currentPage.selection; 5 | if (allSelectedNodes.length === 0) { 6 | return []; 7 | } 8 | return allSelectedNodes.filter(predicate); 9 | } 10 | -------------------------------------------------------------------------------- /src/getRandomId/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import getRandomId from '../index'; 2 | 3 | describe('getRandomId', () => { 4 | it('should work with default length', () => { 5 | const id = getRandomId(); 6 | expect(id.length).toEqual(5); 7 | }); 8 | 9 | it('should work with specified length', () => { 10 | const id = getRandomId(10); 11 | expect(id.length).toEqual(10); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/_internal/toColor.ts: -------------------------------------------------------------------------------- 1 | import { IColor, ColorModel, ColorFormat } from '../interface'; 2 | import Color from 'color'; 3 | 4 | export default function toColor(colorModel: ColorModel) { 5 | return (color: IColor, format: ColorFormat = ColorFormat.STRING) => { 6 | const methodName = format; 7 | const theColor = Color(color)[colorModel]() as Color; 8 | return theColor[methodName](); 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/getRandomId/index.ts: -------------------------------------------------------------------------------- 1 | export default function getRandomId(length: number = 5) { 2 | const result = []; 3 | const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 4 | const charactersLength = characters.length; 5 | for (let i = 0; i < length; i++) { 6 | result.push(characters.charAt(Math.floor(Math.random() * charactersLength))); 7 | } 8 | return result.join(''); 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /npm-debug.log* 6 | /yarn-error.log 7 | /package-lock.json 8 | 9 | # production 10 | /es 11 | /docs-dist 12 | /lib 13 | 14 | # misc 15 | .DS_Store 16 | /coverage 17 | 18 | # umi 19 | .umi 20 | .umi-production 21 | .umi-test 22 | .env.local 23 | 24 | # ide 25 | /.vscode 26 | /.idea 27 | 28 | /dist -------------------------------------------------------------------------------- /src/getColorContrast/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import getColorContrast from '../index'; 2 | 3 | describe('getColorContrast', () => { 4 | it('should work for same color', () => { 5 | expect(getColorContrast({ r: 255, g: 0, b: 0 }, 'rgb(255,0,0)')).toEqual(1); 6 | }); 7 | 8 | it('should work for different color', () => { 9 | expect(getColorContrast({ r: 255, g: 0, b: 0 }, 'rgb(0,255,200)')).toEqual(3.0727391766908636); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "jsx": "react", 7 | "esModuleInterop": true, 8 | "allowJs": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "declaration": true, 12 | "allowSyntheticDefaultImports": true, 13 | "typeRoots": ["./node_modules/@types", "./node_modules/@figma", "./typings"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/toHex/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import toHex from '../index'; 2 | 3 | describe('toHex', () => { 4 | it('should work for rgb object', () => { 5 | expect(toHex({ r: 255, g: 0, b: 0 })).toEqual('#FF0000'); 6 | }); 7 | it('should work for rgb string', () => { 8 | expect(toHex('rgb(255, 0, 0)')).toEqual('#FF0000'); 9 | }); 10 | it('should work for hsl', () => { 11 | expect(toHex('hsl(0, 100%, 50%)')).toEqual('#FF0000'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/toSolidPaint/index.ts: -------------------------------------------------------------------------------- 1 | import Color from 'color'; 2 | 3 | function toSolidPaint(colorInString: string, opacity?: number): SolidPaint { 4 | const color = Color(colorInString); 5 | const { r, g, b, alpha = 1 } = color.rgb().unitObject(); 6 | // https://www.figma.com/plugin-docs/api/Paint/#solidpaint 7 | return { 8 | type: 'SOLID', 9 | color: { r, g, b }, 10 | opacity: opacity != null ? opacity : alpha, 11 | }; 12 | } 13 | 14 | export default toSolidPaint; 15 | -------------------------------------------------------------------------------- /src/_internal/hasMatch.ts: -------------------------------------------------------------------------------- 1 | import isPageNode from '../isPageNode'; 2 | 3 | type Pred = (node: SceneNode) => boolean; 4 | 5 | function hasMatch(pred: Pred, matchedResult: boolean) { 6 | return (node: SceneNode) => { 7 | do { 8 | if (pred(node)) { 9 | return matchedResult; 10 | } 11 | node = node.parent as SceneNode; 12 | } while (node != null && !isPageNode(node)); 13 | return !matchedResult; 14 | }; 15 | } 16 | 17 | export default hasMatch; 18 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | name: Node.js ${{ matrix.node-version }} 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | node-version: 13 | - 16 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/setup-node@v2 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - run: yarn 20 | - run: yarn test 21 | -------------------------------------------------------------------------------- /src/isPageNode/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import isPageNode from '../index'; 3 | 4 | describe('isPageNode', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | it('should work for page node', () => { 10 | expect(isPageNode(figma.createPage())).toBeTruthy(); 11 | }); 12 | 13 | it('should work for non-page node', () => { 14 | expect(isPageNode(figma.createFrame())).toBeFalsy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/isTextNode/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import isTextNode from '../index'; 3 | 4 | describe('isTextNode', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | it('should work for text node', () => { 10 | expect(isTextNode(figma.createText())).toBeTruthy(); 11 | }); 12 | 13 | it('should work for non-text node', () => { 14 | expect(isTextNode(figma.createFrame())).toBeFalsy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/isFrameNode/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import isFrameNode from '../index'; 3 | 4 | describe('isFrameNode', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | it('should work for frame node', () => { 10 | expect(isFrameNode(figma.createFrame())).toBeTruthy(); 11 | }); 12 | 13 | it('should work for non-frame node', () => { 14 | expect(isFrameNode(figma.createText())).toBeFalsy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/isImagePaint/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import isImagePaint from '../index'; 2 | 3 | describe('isImagePaint', () => { 4 | it('should work for undefined', () => { 5 | expect(isImagePaint(null)).toBeFalsy(); 6 | }); 7 | it('should work for image paint', () => { 8 | expect( 9 | isImagePaint({ 10 | imageHash: 'helloworld', 11 | type: 'IMAGE', 12 | scaleMode: 'FILL', 13 | scalingFactor: 0.5, 14 | opacity: 1, 15 | }), 16 | ).toBeTruthy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/toImagePaint/index.ts: -------------------------------------------------------------------------------- 1 | const DEFAULT_OPTIONS = { 2 | scaleMode: 'FILL' as ImagePaint['scaleMode'], 3 | scalingFactor: 0.5, 4 | opacity: 1, 5 | }; 6 | 7 | function toImagePaint( 8 | bytes: Uint8Array, 9 | options: Omit = DEFAULT_OPTIONS, 10 | ): ImagePaint { 11 | const image = figma.createImage(bytes); 12 | return { 13 | ...DEFAULT_OPTIONS, 14 | ...options, 15 | imageHash: image.hash, 16 | type: 'IMAGE', 17 | }; 18 | } 19 | 20 | export default toImagePaint; 21 | -------------------------------------------------------------------------------- /src/solidPaintToWebRgb/index.ts: -------------------------------------------------------------------------------- 1 | import { IColor, ColorFormat } from '../interface'; 2 | import toRgb from '../toRgb'; 3 | 4 | function solidPaintToWebRgb(paint: SolidPaint, format: ColorFormat = ColorFormat.STRING) { 5 | const { color, opacity } = paint; 6 | const { r, g, b } = color; 7 | const webColor: IColor = { 8 | r: Math.round(r * 255), 9 | g: Math.round(g * 255), 10 | b: Math.round(b * 255), 11 | alpha: opacity, 12 | }; 13 | return toRgb(webColor, format); 14 | } 15 | 16 | export default solidPaintToWebRgb; 17 | -------------------------------------------------------------------------------- /src/isSameColor/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import isSameColor from '../index'; 2 | 3 | describe('isSameColor', () => { 4 | it('should work for same model', () => { 5 | expect(isSameColor('rgb(255,0,0)', { r: 255, g: 0, b: 0 })).toBeTruthy(); 6 | expect(isSameColor('rgb(255,0,0)', { r: 255, g: 0, b: 1 })).toBeFalsy(); 7 | }); 8 | 9 | it('should work for different models', () => { 10 | expect(isSameColor('rgb(255,0,0)', '#ff0000')).toBeTruthy(); 11 | expect(isSameColor('rgb(255,0,0)', '#ff0001')).toBeFalsy(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /src/isComponentNode/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import isComponentNode from '../index'; 3 | 4 | describe('isComponentNode', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | it('should work for component node', () => { 10 | expect(isComponentNode(figma.createComponent())).toBeTruthy(); 11 | }); 12 | 13 | it('should work for non-component node', () => { 14 | expect(isComponentNode(figma.createFrame())).toBeFalsy(); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/isGradientPaint/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import isGradientPaint from '../index'; 2 | 3 | describe('GradientPaint', () => { 4 | it('should work for undefined', () => { 5 | expect(isGradientPaint(null)).toBeFalsy(); 6 | }); 7 | it('should work for gradient paint', () => { 8 | expect( 9 | isGradientPaint({ 10 | type: 'GRADIENT_LINEAR', 11 | gradientTransform: [ 12 | [0, 0, 0], 13 | [1, 1, 1], 14 | ], 15 | gradientStops: [], 16 | }), 17 | ).toBeTruthy(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/_internal/isDeepEqual.ts: -------------------------------------------------------------------------------- 1 | export default function isDeepEqual(x: T, y: T): boolean { 2 | if (x === y) { 3 | return true; 4 | } else if (typeof x == 'object' && x != null && typeof y == 'object' && y != null) { 5 | if (Object.keys(x).length !== Object.keys(y).length) { 6 | return false; 7 | } 8 | 9 | for (let propName in x) { 10 | if (!(propName in y)) { 11 | return false; 12 | } 13 | if (!isDeepEqual(x[propName], y[propName])) { 14 | return false; 15 | } 16 | } 17 | return true; 18 | } 19 | return false; 20 | } 21 | -------------------------------------------------------------------------------- /src/setPluginData/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import setPluginData from '../index'; 3 | 4 | describe('setPluginData', () => { 5 | let figma: PluginAPI; 6 | const storageKey = 'data'; 7 | const storageValue = JSON.stringify({ count: 1 }); 8 | beforeAll(() => { 9 | figma = createFigma({}); 10 | }); 11 | it('Should be able to set plugin data', () => { 12 | const frame = figma.createFrame(); 13 | setPluginData(frame, storageKey, storageValue); 14 | expect(frame.getPluginData(storageKey)).toEqual(storageValue); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/isWithinInstance/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import isWithinInstance from '../index'; 3 | 4 | describe('isWithinInstance', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | 10 | it('should end at page node', () => { 11 | const node = figma.createFrame(); 12 | const parent = figma.createFrame(); 13 | const grandParent = figma.createPage(); 14 | grandParent.appendChild(parent); 15 | parent.appendChild(node); 16 | expect(isWithinInstance(node)).toBeFalsy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/isInstanceNode/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import isInstanceNode from '../index'; 3 | 4 | describe('isInstanceNode', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | it('should work for instance node', () => { 10 | const componentNode = figma.createComponent(); 11 | const instanceNode = componentNode.createInstance(); 12 | expect(isInstanceNode(instanceNode)).toBeTruthy(); 13 | }); 14 | 15 | it('should work for non-component node', () => { 16 | expect(isInstanceNode(figma.createFrame())).toBeFalsy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /docs/guide/index.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 3 | `figx` is a comprehensive and reliable figma utilities library. 4 | 5 | ## ✨ Features 6 | 7 | - A comprehensive collection of utilities 8 | - Easy to learn and use with clear documentation 9 | - Written in TypeScript with native type support 10 | 11 | ## 📦 Install 12 | 13 | ```bash 14 | $ npm install figx --save 15 | # or 16 | $ yarn add figx 17 | ``` 18 | 19 | ## 🔨 Usage 20 | 21 | ```ts 22 | import { toRgb, ColorFormat } from 'figx'; 23 | toRgb('#FF0000'); // => 'rgb(255, 0, 0)'; 24 | toRgb({ r: 255, g: 0, b: 0 }, ColorFormat.ARRAY); // => [255, 0, 0] 25 | toRgb('hsl(0, 100%, 50%)', ColorFormat.OBJECT); // => { r: 255, g: 0, b: 0 } 26 | ``` 27 | -------------------------------------------------------------------------------- /src/getComponentProps/index.ts: -------------------------------------------------------------------------------- 1 | import { IObject, NodeTypeEnum } from '../interface'; 2 | 3 | type ReturnType = IObject; 4 | 5 | export default function getComponentProps(node: ComponentNode): ReturnType { 6 | if (node.type !== NodeTypeEnum.COMPONENT || node.parent?.type !== NodeTypeEnum.COMPONENT_SET) { 7 | return {}; 8 | } 9 | const { name } = node; 10 | const variants = name.split(','); 11 | return variants.reduce((temp, variant) => { 12 | if (!variant.includes('=')) { 13 | return temp; 14 | } 15 | const [key, value] = variant.split('='); 16 | return { 17 | ...temp, 18 | [key.trim()]: value.trim(), 19 | }; 20 | }, {}); 21 | } 22 | -------------------------------------------------------------------------------- /src/isValidColor/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import isValidColor from '../index'; 2 | 3 | describe('isValidColor', () => { 4 | it('should work for rgb', () => { 5 | expect(isValidColor('rgb(a,0,0)')).toBeFalsy(); 6 | expect(isValidColor('rgb(1,0)')).toBeFalsy(); 7 | expect(isValidColor('rgb(2,0,0)')).toBeTruthy(); 8 | // rgb should round to max - 255 9 | expect(isValidColor('rgb(266,0,0)')).toBeTruthy(); 10 | }); 11 | 12 | it('should work for hex', () => { 13 | expect(isValidColor('#ab')).toBeFalsy(); 14 | expect(isValidColor('#aabbcw')).toBeFalsy(); 15 | expect(isValidColor('#abc')).toBeTruthy(); 16 | expect(isValidColor('#aabbcc')).toBeTruthy(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/copyToClipboard/index.ts: -------------------------------------------------------------------------------- 1 | export default function copyToClipboard(value: string) { 2 | try { 3 | // @ts-ignore 4 | if (window.copy) { 5 | // @ts-ignore 6 | window.copy(value); 7 | } else { 8 | const area = document.createElement('textarea'); 9 | document.body.appendChild(area); 10 | area.value = value; 11 | area.focus(); 12 | area.select(); 13 | const result = document.execCommand('copy'); 14 | document.body.removeChild(area); 15 | if (!result) { 16 | throw new Error(); 17 | } 18 | } 19 | } catch (e) { 20 | console.error(`Unable to copy the value: ${value}`); 21 | return false; 22 | } 23 | return true; 24 | } 25 | -------------------------------------------------------------------------------- /src/clone/index.ts: -------------------------------------------------------------------------------- 1 | type Predicate = (propName: string, element: T) => boolean; 2 | 3 | export default function clone(element: T, ignorePredicate: Predicate = () => false): T { 4 | const type = typeof element; 5 | if ( 6 | element == null || 7 | type === 'boolean' || 8 | type === 'number' || 9 | type === 'string' || 10 | type === 'symbol' 11 | ) { 12 | return element; 13 | } 14 | if (Array.isArray(element)) { 15 | return element.map((ele) => clone(ele)) as unknown as T; 16 | } 17 | const result: { [keyProp: string]: any } = {}; 18 | for (let key in element) { 19 | if (!ignorePredicate(key, element)) { 20 | result[key] = clone(element[key]); 21 | } 22 | } 23 | return result as T; 24 | } 25 | -------------------------------------------------------------------------------- /src/getRandomId/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # getRandomId 7 | 8 | Generate a random id with specified length. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { getRandomId } from 'figx'; 14 | getRandomId(15); // => 'randomString123' 15 | ``` 16 | 17 | ## API 18 | 19 | ```ts 20 | const str = getRandomId(length); 21 | ``` 22 | 23 | ### Params 24 | 25 | | Property | Description | Type | Default | 26 | | -------- | --------------------------------- | ------ | ------- | 27 | | length | Length of the generated id string | Number | 5 | 28 | 29 | ### Result 30 | 31 | | Property | Description | Type | 32 | | -------- | ----------- | -------- | 33 | | str | Random id | `string` | 34 | -------------------------------------------------------------------------------- /src/isImagePaint/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isImagePaint 7 | 8 | Check whether a `paint` is `ImagePaint`. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isImagePaint } from 'figx'; 14 | const node = figma.createFrame(); 15 | isImagePaint(node.fills[0]); // => false 16 | ``` 17 | 18 | ```` 19 | 20 | ## API 21 | 22 | ```ts 23 | const bool = isImagePaint(node); 24 | ```` 25 | 26 | ### Params 27 | 28 | | Property | Description | Type | Default | 29 | | -------- | ----------- | ------- | ------- | 30 | | paint | the paint | `Paint` | - | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | --------------------------------- | --------- | 36 | | bool | Whether the paint is `ImagePaint` | `boolean` | 37 | -------------------------------------------------------------------------------- /src/toHex/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # toHex 7 | 8 | Turn color into hex format. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { toHex } from 'figx'; 14 | 15 | toHex({ r: 255, g: 0, b: 0 }); // '#FF0000' 16 | toHex('rgb(255, 0, 0)'); // '#FF0000' 17 | toHex('hsl(0, 100%, 50%)'); // '#FF0000' 18 | ``` 19 | 20 | ## API 21 | 22 | ```ts 23 | const result = toHex(color); 24 | ``` 25 | 26 | ### Params 27 | 28 | | Property | Description | Type | Default | 29 | | -------- | --------------------------- | ------ | ------- | 30 | | color | The color to be transformed | IColor | - | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | ---------------------- | -------- | 36 | | result | Hex value of the color | `string` | 37 | -------------------------------------------------------------------------------- /src/isGradientPaint/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isImagePaint 7 | 8 | Check whether a `paint` is `GradientPaint`. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { GradientPaint } from 'figx'; 14 | const node = figma.createFrame(); 15 | GradientPaint(node.fills[0]); // => false 16 | ``` 17 | 18 | ```` 19 | 20 | ## API 21 | 22 | ```ts 23 | const bool = GradientPaint(node); 24 | ```` 25 | 26 | ### Params 27 | 28 | | Property | Description | Type | Default | 29 | | -------- | ----------- | ------- | ------- | 30 | | paint | the paint | `Paint` | - | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | ------------------------------------ | --------- | 36 | | bool | Whether the paint is `GradientPaint` | `boolean` | 37 | -------------------------------------------------------------------------------- /src/isPageNode/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isPageNode 7 | 8 | Check whether a node is a page node. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isPageNode } from 'figx'; 14 | const page = figma.createPage(); 15 | isPageNode(page); // =>true 16 | ``` 17 | 18 | ## API 19 | 20 | ```ts 21 | const bool = isPageNode(node); 22 | ``` 23 | 24 | ### Params 25 | 26 | | Property | Description | Type | Default | 27 | | -------- | ---------------------- | ------------------- | ------- | 28 | | node | The node to be checked | `Partial` | - | 29 | 30 | ### Result 31 | 32 | | Property | Description | Type | 33 | | -------- | ------------------------------- | --------- | 34 | | bool | Whether the node is a page node | `boolean` | 35 | -------------------------------------------------------------------------------- /src/isTextNode/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isTextNode 7 | 8 | Check whether a node is a text node. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isTextNode } from 'figx'; 14 | const textNode = figma.createText(); 15 | isTextNode(textNode); // =>true 16 | ``` 17 | 18 | ## API 19 | 20 | ```ts 21 | const bool = isTextNode(node); 22 | ``` 23 | 24 | ### Params 25 | 26 | | Property | Description | Type | Default | 27 | | -------- | ---------------------- | ------------------- | ------- | 28 | | node | The node to be checked | `Partial` | - | 29 | 30 | ### Result 31 | 32 | | Property | Description | Type | 33 | | -------- | ------------------------------- | --------- | 34 | | bool | Whether the node is a text node | `boolean` | 35 | -------------------------------------------------------------------------------- /src/isFrameNode/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isFrameNode 7 | 8 | Check whether a node is a frame node. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isFrameNode } from 'figx'; 14 | const frameNode = figma.createFrame(); 15 | isFrameNode(frameNode); // =>true 16 | ``` 17 | 18 | ## API 19 | 20 | ```ts 21 | const bool = isFrameNode(node); 22 | ``` 23 | 24 | ### Params 25 | 26 | | Property | Description | Type | Default | 27 | | -------- | ---------------------- | ------------------- | ------- | 28 | | node | The node to be checked | `Partial` | - | 29 | 30 | ### Result 31 | 32 | | Property | Description | Type | 33 | | -------- | -------------------------------- | --------- | 34 | | bool | Whether the node is a frame node | `boolean` | 35 | -------------------------------------------------------------------------------- /src/isEllipseNode/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isFrameNode 7 | 8 | Check whether a node is an ellipse node. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isEllipseNode } from 'figx'; 14 | const node = figma.createEllipse(); 15 | isEllipseNode(node); // =>true 16 | ``` 17 | 18 | ## API 19 | 20 | ```ts 21 | const bool = isEllipseNode(node); 22 | ``` 23 | 24 | ### Params 25 | 26 | | Property | Description | Type | Default | 27 | | -------- | ---------------------- | ------------------- | ------- | 28 | | node | The node to be checked | `Partial` | - | 29 | 30 | ### Result 31 | 32 | | Property | Description | Type | 33 | | -------- | ---------------------------------- | --------- | 34 | | bool | Whether the node is a ellipse node | `boolean` | 35 | -------------------------------------------------------------------------------- /src/getPluginData/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import getPluginData from '../index'; 3 | 4 | describe('getPluginData', () => { 5 | let figma: PluginAPI; 6 | const storageKey = 'data'; 7 | const storageValue = JSON.stringify({ count: 1 }); 8 | beforeAll(() => { 9 | figma = createFigma({}); 10 | }); 11 | it('Should be able to get plugin data', () => { 12 | const frame = figma.createFrame(); 13 | frame.setPluginData(storageKey, storageValue); 14 | expect(getPluginData(frame, storageKey)).toEqual(storageValue); 15 | }); 16 | 17 | it('Removed node should NOT be able to get plugin data', () => { 18 | const frame = figma.createFrame(); 19 | frame.setPluginData(storageKey, storageValue); 20 | frame.remove(); 21 | expect(getPluginData(frame, storageKey)).toBeUndefined(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/isVisible/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isVisible 7 | 8 | Check whether a node is visible. A node is invisible if itself or any of its direct ancestor is invisible. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isVisible } from 'figx'; 14 | const textNode = figma.createText(); 15 | isVisible(textNode); // => true 16 | ``` 17 | 18 | ## API 19 | 20 | ```ts 21 | const bool = isVisible(node); 22 | ``` 23 | 24 | ### Params 25 | 26 | | Property | Description | Type | Default | 27 | | -------- | ---------------------- | ----------- | ------- | 28 | | node | The node to be checked | `SceneNode` | - | 29 | 30 | ### Result 31 | 32 | | Property | Description | Type | 33 | | -------- | --------------------------- | --------- | 34 | | bool | Whether the node is visible | `boolean` | 35 | -------------------------------------------------------------------------------- /src/isComponentNode/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isComponentNode 7 | 8 | Check whether a node is a component node. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isComponentNode } from 'figx'; 14 | const componentNode = figma.createComponent(); 15 | isComponentNode(componentNode); // =>true 16 | ``` 17 | 18 | ## API 19 | 20 | ```ts 21 | const bool = isComponentNode(node); 22 | ``` 23 | 24 | ### Params 25 | 26 | | Property | Description | Type | Default | 27 | | -------- | ---------------------- | ------------------- | ------- | 28 | | node | The node to be checked | `Partial` | - | 29 | 30 | ### Result 31 | 32 | | Property | Description | Type | 33 | | -------- | ------------------------------------ | --------- | 34 | | bool | Whether the node is a component node | `boolean` | 35 | -------------------------------------------------------------------------------- /src/solidPaintToWebRgb/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import solidPaintToWebRgb from '../index'; 2 | import { ColorFormat } from '../../interface'; 3 | 4 | describe('solidPaintToWebRgb', () => { 5 | const paint: SolidPaint = { 6 | type: 'SOLID', 7 | color: { 8 | r: 0.5285416841506958, 9 | g: 0.5314041376113892, 10 | b: 0.737500011920929, 11 | }, 12 | opacity: 1, 13 | }; 14 | it('should work for rgb string', () => { 15 | expect(solidPaintToWebRgb(paint, ColorFormat.STRING)).toEqual('rgb(135, 136, 188)'); 16 | }); 17 | 18 | it('should work for rgb array', () => { 19 | expect(solidPaintToWebRgb(paint, ColorFormat.ARRAY)).toEqual([135, 136, 188]); 20 | }); 21 | 22 | it('should work for rgb object', () => { 23 | expect(solidPaintToWebRgb(paint, ColorFormat.OBJECT)).toEqual({ 24 | r: 135, 25 | g: 136, 26 | b: 188, 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/isLocked/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isLocked 7 | 8 | Check whether a node is locked. A node is locked if itself or any of its direct ancestor is locked. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isLocked } from 'figx'; 14 | const frameNode = figma.createFrame(); 15 | isLocked(frameNode); // => false 16 | frameNode.locked = true; 17 | isLocked(figma.root); // => true 18 | ``` 19 | 20 | ## API 21 | 22 | ```ts 23 | const bool = isLocked(node); 24 | ``` 25 | 26 | ### Params 27 | 28 | | Property | Description | Type | Default | 29 | | -------- | ---------------------- | ----------- | ------- | 30 | | node | The node to be checked | `SceneNode` | - | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | -------------------------- | --------- | 36 | | bool | Whether the node is locked | `boolean` | 37 | -------------------------------------------------------------------------------- /src/isFillableNode/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isFillableNode 7 | 8 | Check whether a node is fillable (i.e. can be filled with `Paint`). 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isFillableNode } from 'figx'; 14 | const frameNode = figma.createFrame(); 15 | isFillableNode(frameNode); // => true 16 | isFillableNode(figma.root); // => false 17 | ``` 18 | 19 | ## API 20 | 21 | ```ts 22 | const bool = isFillableNode(node); 23 | ``` 24 | 25 | ### Params 26 | 27 | | Property | Description | Type | Default | 28 | | -------- | ---------------------- | ------------------- | ------- | 29 | | node | The node to be checked | `Partial` | - | 30 | 31 | ### Result 32 | 33 | | Property | Description | Type | 34 | | -------- | ---------------------------- | --------- | 35 | | bool | Whether the node is fillable | `boolean` | 36 | -------------------------------------------------------------------------------- /src/toHsl/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import toHsl from '../index'; 2 | import { ColorFormat } from '../../interface'; 3 | 4 | const hex = '#FF0000'; 5 | const rgb = { r: 255, g: 0, b: 0 }; 6 | 7 | describe('toHsl', () => { 8 | it('should work for string', () => { 9 | expect(toHsl(hex, ColorFormat.STRING)).toEqual('hsl(0, 100%, 50%)'); 10 | expect(toHsl(rgb, ColorFormat.STRING)).toEqual('hsl(0, 100%, 50%)'); 11 | }); 12 | 13 | it('should work for rgb array', () => { 14 | expect(toHsl(hex, ColorFormat.ARRAY)).toEqual([0, 100, 50]); 15 | expect(toHsl(rgb, ColorFormat.ARRAY)).toEqual([0, 100, 50]); 16 | }); 17 | 18 | it('should work for rgb object', () => { 19 | expect(toHsl(hex, ColorFormat.OBJECT)).toEqual({ 20 | h: 0, 21 | s: 100, 22 | l: 50, 23 | }); 24 | expect(toHsl(rgb, ColorFormat.OBJECT)).toEqual({ 25 | h: 0, 26 | s: 100, 27 | l: 50, 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/isInstanceNode/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isInstanceNode 7 | 8 | Check whether a node is a instance node. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isInstanceNode } from 'figx'; 14 | const componentNode = figma.createComponent(); 15 | const instanceNode = componentNode.createInstance(); 16 | isInstanceNode(instanceNode); // =>true 17 | ``` 18 | 19 | ## API 20 | 21 | ```ts 22 | const bool = instanceNode(node); 23 | ``` 24 | 25 | ### Params 26 | 27 | | Property | Description | Type | Default | 28 | | -------- | ---------------------- | ------------------- | ------- | 29 | | node | The node to be checked | `Partial` | - | 30 | 31 | ### Result 32 | 33 | | Property | Description | Type | 34 | | -------- | ----------------------------------- | --------- | 35 | | bool | Whether the node is a instance node | `boolean` | 36 | -------------------------------------------------------------------------------- /src/isSameColor/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isSameColor 7 | 8 | Check whether two colors are the same 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isSameColor } from 'figx'; 14 | isSameColor('rgb(255,0,0)', '#ff0000'); // true 15 | isSameColor('rgb(255,0,0)', '#ff0001'); // false 16 | ``` 17 | 18 | ## API 19 | 20 | ```ts 21 | const result = isSameColor(colorA, colorB); 22 | ``` 23 | 24 | ### Params 25 | 26 | | Property | Description | Type | Default | 27 | | -------- | ------------------------ | ------ | ------- | 28 | | colorA | The color to be compared | IColor | - | 29 | | colorB | The color to be compared | IColor | - | 30 | 31 | ### Result 32 | 33 | | Property | Description | Type | 34 | | -------- | ------------------------------------------ | --------- | 35 | | result | Whether the two colors have the same value | `boolean` | 36 | -------------------------------------------------------------------------------- /src/isWithinInstance/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isWithinInstance 7 | 8 | Check whether a node is within an [instance node](https://www.figma.com/plugin-docs/api/InstanceNode/). 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isWithinInstance } from 'figx'; 14 | const frameNode = figma.createFrame(); 15 | isWithinInstance(frameNode); // => false 16 | ``` 17 | 18 | ## API 19 | 20 | ```ts 21 | const bool = isWithinInstance(node); 22 | ``` 23 | 24 | ### Params 25 | 26 | | Property | Description | Type | Default | 27 | | -------- | ---------------------- | ----------- | ------- | 28 | | node | The node to be checked | `SceneNode` | - | 29 | 30 | ### Result 31 | 32 | | Property | Description | Type | 33 | | -------- | -------------------------------------------- | --------- | 34 | | bool | Whether the node is part of an instance node | `boolean` | 35 | -------------------------------------------------------------------------------- /src/clone/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import clone from '../index'; 2 | 3 | const object = { count: 1, sum: 2 }; 4 | const deepObject = { 5 | ...object, 6 | children: [object, object], 7 | }; 8 | 9 | describe('clone', () => { 10 | it('should work for normal object', () => { 11 | expect(clone(object)).toEqual(object); 12 | expect(clone(deepObject)).toEqual(deepObject); 13 | }); 14 | it('should work for properties on prototype chain', () => { 15 | const _object = { 16 | ...deepObject, 17 | __proto__: { 18 | a: 1, 19 | }, 20 | }; 21 | expect(clone(_object)).toEqual({ 22 | ...deepObject, 23 | a: 1, 24 | }); 25 | }); 26 | it('should work for ignorePredicate', () => { 27 | const _object = { 28 | ...deepObject, 29 | }; 30 | // don't copy children 31 | const pred = (propName: string) => propName === 'children'; 32 | expect(clone(_object, pred)).toEqual({ ...object }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/getColorContrast/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # getColorContrast 7 | 8 | Get the [WCAG contrast ratio](https://www.w3.org/TR/WCAG20/#contrast-ratiodef) between two colors. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { getColorContrast } from 'figx'; 14 | 15 | getColorContrast({ r: 255, g: 0, b: 0 }, 'rgb(255,0,0)'); // 1 16 | getColorContrast({ r: 255, g: 0, b: 0 }, 'rgb(0,255,200)')); // 3.0727391766908636 17 | ``` 18 | 19 | ## API 20 | 21 | ```ts 22 | const result = getColorContrast(colorA, colorB); 23 | ``` 24 | 25 | ### Params 26 | 27 | | Property | Description | Type | Default | 28 | | -------- | ------------------------ | ------ | ------- | 29 | | colorA | The color to be compared | IColor | - | 30 | | colorB | The color to be compared | IColor | - | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | -------------- | -------- | 36 | | result | Contrast ratio | `number` | 37 | -------------------------------------------------------------------------------- /src/getSelectedNodes/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import getSelectionNodes from '../index'; 3 | import isFrameNode from '../../isFrameNode'; 4 | 5 | describe('getSelectionNodes', () => { 6 | let figma: PluginAPI; 7 | beforeAll(() => { 8 | figma = createFigma({}); 9 | // @ts-ignore 10 | global.figma = figma; 11 | }); 12 | it('Should be able to get selected nodes', () => { 13 | const frame = figma.createFrame(); 14 | figma.currentPage.selection = [frame]; 15 | const nodes = getSelectionNodes(); 16 | expect(nodes[0].id).toEqual(frame.id); 17 | }); 18 | 19 | it('Should be able to get selected nodes with predicate', () => { 20 | const frame = figma.createFrame(); 21 | const text = figma.createText(); 22 | figma.currentPage.selection = [frame, text]; 23 | const nodes = getSelectionNodes(isFrameNode); 24 | expect(nodes.length).toEqual(1); 25 | expect(nodes[0].id).toEqual(frame.id); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/isValidColor/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isValidColor 7 | 8 | Check whether the passed in `color` parameter can be parsed into a valid color. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isValidColor } from 'figx'; 14 | isValidColor('rgb(a,0,0)'); // => false 15 | isValidColor('rgb(2,0,0)'); // => true 16 | isValidColor('#aabbcw'); // => false 17 | isValidColor('#abc'); // => true 18 | ``` 19 | 20 | ## API 21 | 22 | ```ts 23 | const result = isValidColor(color); 24 | ``` 25 | 26 | ### Params 27 | 28 | | Property | Description | Type | Default | 29 | | -------- | ----------------------- | ------ | ------- | 30 | | color | The color to be checked | IColor | - | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | -------------------------------------------------- | --------- | 36 | | result | Whether the color can be parsed into a valid color | `boolean` | 37 | -------------------------------------------------------------------------------- /src/toSolidPaint/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import toSolidPaint from '../index'; 2 | 3 | const hex = '#008080'; 4 | const rgb = 'rgb(0, 128, 128, 0.1)'; 5 | 6 | describe('toSolidPaint', () => { 7 | it('should work for hex string', () => { 8 | expect(toSolidPaint(hex)).toEqual({ 9 | type: 'SOLID', 10 | color: { 11 | r: 0, 12 | g: 128 / 255, 13 | b: 128 / 255, 14 | }, 15 | opacity: 1, 16 | }); 17 | }); 18 | 19 | it('should work for rgb string', () => { 20 | expect(toSolidPaint(rgb)).toEqual({ 21 | type: 'SOLID', 22 | color: { 23 | r: 0, 24 | g: 128 / 255, 25 | b: 128 / 255, 26 | }, 27 | opacity: 0.1, 28 | }); 29 | }); 30 | 31 | it('should work for opacity', () => { 32 | expect(toSolidPaint(rgb, 0.2)).toEqual({ 33 | type: 'SOLID', 34 | color: { 35 | r: 0, 36 | g: 128 / 255, 37 | b: 128 / 255, 38 | }, 39 | opacity: 0.2, 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/isFillableNode/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import isFillableNode from '../index'; 3 | 4 | describe('isFrameNode', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | it('frame node should be fillable', () => { 10 | expect(isFillableNode(figma.createFrame())).toBeTruthy(); 11 | }); 12 | 13 | it('text node should be fillable', () => { 14 | expect(isFillableNode(figma.createText())).toBeTruthy(); 15 | }); 16 | 17 | it('document node should be NOT fillable', () => { 18 | expect(isFillableNode(figma.root)).toBeFalsy(); 19 | }); 20 | 21 | it('page node should be NOT fillable', () => { 22 | expect(isFillableNode(figma.createPage())).toBeFalsy(); 23 | }); 24 | 25 | it('instance node should be NOT fillable', () => { 26 | const component = figma.createComponent(); 27 | const instance = component.createInstance(); 28 | expect(isFillableNode(instance)).toBeFalsy(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/getComponentProps/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import getComponentProps from '../index'; 3 | 4 | describe('getSelectionNodes', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | // @ts-ignore 9 | global.figma = figma; 10 | }); 11 | it('Should work for component node not under componentSetNode', () => { 12 | const componentNode = figma.createComponent(); 13 | componentNode.name = 'type=primary, size=medium, disabled=True'; 14 | expect(getComponentProps(componentNode)).toEqual({}); 15 | }); 16 | // it('Should be able to get props', () => { 17 | // const componentNode = figma.createComponent(); 18 | // componentNode.name = 'type=primary, size=medium, disabled=True'; 19 | // figma.combineAsVariants([componentNode], figma.currentPage); 20 | // expect(getComponentProps(componentNode)).toEqual({ 21 | // type: 'primary', 22 | // size: 'medium', 23 | // disabled: 'True' 24 | // }); 25 | // }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/toHsl/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # toHSL 7 | 8 | Turn color into hsl format. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { toHsl, ColorFormat } from 'figx'; 14 | toHsl('#FF0000'); // => 'hsl(0, 100%, 50%)' 15 | toHsl('rgb(0,0,255)', ColorFormat.ARRAY; // [0, 100, 50] 16 | toHsl({r: 0, g: 0, b: 255}, ColorFormat.OBJECT); // { h: 0, s: 100, l: 50 } 17 | ``` 18 | 19 | ## API 20 | 21 | ```ts 22 | const result = toHsl(color, colorFormat); 23 | ``` 24 | 25 | ### Params 26 | 27 | | Property | Description | Type | Default | 28 | | ----------- | --------------------------- | ------------- | ------------------ | 29 | | color | The color to be transformed | IColor | - | 30 | | colorFormat | Color format | `ColorFormat` | ColorFormat.STRING | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | --- | --- | --- | 36 | | result | hsl value of the color | `string` \| `array` \| `object` depending on the `colorFormat` argument | 37 | -------------------------------------------------------------------------------- /src/toRgb/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # toRgb 7 | 8 | Turn color into rgb format. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { toRgb, ColorFormat } from 'figx'; 14 | toRgb('#FF0000'); // => 'rgb(255, 0, 0)'; 15 | toRgb({ r: 255, g: 0, b: 0 }, ColorFormat.ARRAY); // => [255, 0, 0] 16 | toRgb('hsl(0, 100%, 50%)', ColorFormat.OBJECT); // => { r: 255, g: 0, b: 0 } 17 | ``` 18 | 19 | ## API 20 | 21 | ```ts 22 | const result = toRgb(color, colorFormat); 23 | ``` 24 | 25 | ### Params 26 | 27 | | Property | Description | Type | Default | 28 | | ----------- | --------------------------- | ----------- | ------------------ | 29 | | color | The color to be transformed | IColor | - | 30 | | colorFormat | Color format | ColorFormat | ColorFormat.STRING | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | --- | --- | --- | 36 | | result | Rgb value of the color | `string` \| `array` \| `object` depending on the `colorFormat` argument | 37 | -------------------------------------------------------------------------------- /src/getPluginData/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # getPluginData 7 | 8 | Retrieves custom information that was stored on the node or style using `setPluginData`. Wraps around native `setPluginData` with safe check. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { getPluginData, setPluginData } from 'figx'; 14 | const frame = figma.createFrame(); 15 | setPluginData('count', '1'); 16 | getPluginData(frame, 'count'); // '1' 17 | ``` 18 | 19 | ## API 20 | 21 | ```ts 22 | const value = getPluginData(node, key); 23 | ``` 24 | 25 | ### Params 26 | 27 | | Property | Description | Type | Default | 28 | | -------- | --------------------------------- | ------------------- | ------- | 29 | | node | The node to hold the storage data | `Partial` | - | 30 | | key | Storage key | `string` | - | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | ------------ | -------- | 36 | | value | Storage data | `string` | 37 | -------------------------------------------------------------------------------- /src/setPluginData/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # setPluginData 7 | 8 | Lets you store custom information on any node or style, private to your plugin. Wraps around [setPluginData](https://www.figma.com/plugin-docs/api/properties/nodes-setplugindata/) with safe check. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { setPluginData, getPluginData } from 'figx'; 14 | const frame = figma.createFrame(); 15 | setPluginData(frame, 'count', '1'); 16 | getPluginData(frame, 'count'); // '1' 17 | ``` 18 | 19 | ## API 20 | 21 | ```ts 22 | setPluginData(node, key, value); 23 | ``` 24 | 25 | ### Params 26 | 27 | | Property | Description | Type | Default | 28 | | -------- | --------------------------------- | ------------------- | ------- | 29 | | node | The node to hold the storage data | `Partial` | - | 30 | | key | Storage key | `string` | - | 31 | | value | Storage value | `string` | - | 32 | 33 | ### Result 34 | 35 | `void` 36 | -------------------------------------------------------------------------------- /src/isLocked/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import { createFigma } from 'figma-api-stub'; 2 | import isLocked from '../index'; 3 | 4 | describe('isLocked', () => { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | it('should work for node itself', () => { 10 | const node = figma.createFrame(); 11 | node.locked = true; 12 | expect(isLocked(node)).toBeTruthy(); 13 | }); 14 | 15 | it('should check for ancestors', () => { 16 | const node = figma.createFrame(); 17 | const parent = figma.createFrame(); 18 | const grandParent = figma.createFrame(); 19 | grandParent.appendChild(parent); 20 | parent.appendChild(node); 21 | grandParent.locked = true; 22 | expect(isLocked(grandParent)).toBeTruthy(); 23 | }); 24 | 25 | it('should end at page node', () => { 26 | const node = figma.createFrame(); 27 | const parent = figma.createFrame(); 28 | const grandParent = figma.createPage(); 29 | grandParent.appendChild(parent); 30 | parent.appendChild(node); 31 | expect(isLocked(node)).toBeFalsy(); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/getDropPosition/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # getDropPosition 7 | 8 | Get the canvas position of the drop point 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { getDropPosition } from 'figx'; 14 | getDropPosition({ x: 100, y: 10 }, { 20, 10 }, 400); 15 | ``` 16 | 17 | ## API 18 | 19 | ```ts 20 | const result = getDropPosition(dropPositionRelativeToPlugin, offset, windowWidth); 21 | ``` 22 | 23 | ### Params 24 | 25 | | Property | Description | Type | Default | 26 | | --- | --- | --- | --- | 27 | | dropPositionRelativeToPlugin | Drop position relative to the plugin, available as `clientX` and `clientY` of the `DragEnd` event | `IPosition` | - | 28 | | offset | Offset of the drag point, available as `offsetX` and `offsetY` of the `DragStart` event | `IPosition` | - | 29 | | windowWidth | Window width, available as `window.outerWidth` in plugin UI | `number` | - | 30 | 31 | ### Result 32 | 33 | | Property | Description | Type | 34 | | -------- | -------------------------- | ----------- | 35 | | result | Position of the drop point | `IPosition` | 36 | -------------------------------------------------------------------------------- /src/toImagePaint/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # toImagePaint 7 | 8 | Turn [Uint8Array][uint8array-link] into figma [ImagePaint][image-paint-link] 9 | 10 | [uint8array-link]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array 11 | [image-paint-link]: https://www.figma.com/plugin-docs/api/Paint/#imagepaint 12 | 13 | ## Example 14 | 15 | ```tsx 16 | import { toImagePaint } from 'figx'; 17 | const bytes = new Uint8Array([1, 2, 3]); 18 | toImagePaint(bytes); // { imageHash: 'xxx', type: 'IMAGE'} 19 | ``` 20 | 21 | ## API 22 | 23 | ```ts 24 | const paint = toImagePaint(bytes, options); 25 | ``` 26 | 27 | ### Params 28 | 29 | | Property | Description | Type | Default | 30 | | --- | --- | --- | --- | 31 | | bytes | Image data in Uint8Array | `Uint8Array` | - | 32 | | options | Extra options | `Omit { 5 | let figma: PluginAPI; 6 | beforeAll(() => { 7 | figma = createFigma({}); 8 | }); 9 | it('should work for node itself', () => { 10 | const node = figma.createFrame(); 11 | expect(isVisible(node)).toBeTruthy(); 12 | node.visible = false; 13 | expect(isVisible(node)).toBeFalsy(); 14 | }); 15 | 16 | it('should check for ancestors', () => { 17 | const node = figma.createFrame(); 18 | const parent = figma.createFrame(); 19 | const grandParent = figma.createFrame(); 20 | grandParent.appendChild(parent); 21 | parent.appendChild(node); 22 | grandParent.visible = false; 23 | expect(isVisible(grandParent)).toBeFalsy(); 24 | }); 25 | 26 | it('should end at page node', () => { 27 | const node = figma.createFrame(); 28 | const parent = figma.createFrame(); 29 | const grandParent = figma.createPage(); 30 | grandParent.appendChild(parent); 31 | parent.appendChild(node); 32 | expect(isVisible(node)).toBeTruthy(); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/isEqual/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # isEqual 7 | 8 | Check whether two `element`s (Can't be primitives or objects) are the equivalent 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { isEqual } from 'figx'; 14 | 15 | const solidPaintA = { 16 | type: 'SOLID', 17 | color: { 18 | r: 1, 19 | g: 0, 20 | b: 0, 21 | }, 22 | }; 23 | 24 | const solidPaintB = { 25 | type: 'SOLID', 26 | color: { 27 | r: 1, 28 | g: 0, 29 | b: 1, 30 | }, 31 | }; 32 | isEqual(solidPaintA, solidPaintB); // => false 33 | ``` 34 | 35 | ## API 36 | 37 | ```ts 38 | const result = isEqual(elementA, elementB); 39 | ``` 40 | 41 | ### Params 42 | 43 | | Property | Description | Type | Default | 44 | | -------- | -------------------------- | ---- | ------- | 45 | | elementA | The element to be compared | `T` | - | 46 | | elementB | The element to be compared | `T` | - | 47 | 48 | ### Result 49 | 50 | | Property | Description | Type | 51 | | -------- | --------------------------------------- | --------- | 52 | | result | Whether the two elements are equivalent | `boolean` | 53 | -------------------------------------------------------------------------------- /src/toSolidPaint/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # toSolidPaint 7 | 8 | Turn color into figma [SolidPaint][solid-paint-link] 9 | 10 | [solid-paint-link]: https://www.figma.com/plugin-docs/api/Paint/#solidpaint 11 | 12 | ## Example 13 | 14 | ```tsx 15 | import { toSolidPaint } from 'figx'; 16 | // { type: 'SOLID', color: { r: 0, g: 128 / 255, b: 128 / 255 }, opacity: 1 } 17 | toSolidPaint('#008080'); 18 | // { type: 'SOLID', color: { r: 0, g: 128 / 255, b: 128 / 255 }, opacity: 0.1 } 19 | toSolidPaint('rgb(0, 128, 128, 0.1)'); 20 | // { type: 'SOLID', color: { r: 0, g: 128 / 255, b: 128 / 255 }, opacity: 0.1 } 21 | toSolidPaint('rgb(0, 128, 128)', 0.1)); 22 | ``` 23 | 24 | ## API 25 | 26 | ```ts 27 | const paint = toSolidPaint(color); 28 | ``` 29 | 30 | ### Params 31 | 32 | | Property | Description | Type | Default | 33 | | -------- | --------------- | ------ | ------- | 34 | | color | Color in string | string | - | 35 | | opacity | Opacity | number | - | 36 | 37 | ### Result 38 | 39 | | Property | Description | Type | 40 | | -------- | ----------- | ------------ | 41 | | paint | Solid paint | `SolidPaint` | 42 | -------------------------------------------------------------------------------- /src/getComponentProps/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # getComponentProps 7 | 8 | Get the variants properties of the component node. Only works for component nodes under [ComponentSetNode](https://www.figma.com/plugin-docs/api/ComponentSetNode) 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { getComponentProps } from 'figx'; 14 | const componentNode = figma.createComponent(); 15 | componentNode.name = 'type=primary, size=medium, disabled=True'; 16 | figma.combineAsVariants([componentNode], figma.currentPage); 17 | getComponentProps(componentNode); // { type: 'primary', size: 'medium', disabled: 'True' } 18 | ``` 19 | 20 | ## API 21 | 22 | ```ts 23 | const result = getComponentProps(node); 24 | ``` 25 | 26 | ### Params 27 | 28 | | Property | Description | Type | Default | 29 | | -------- | ------------------------------------- | --------------- | ------- | 30 | | node | Component node to get properties from | `ComponentNode` | | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | ------------------- | ----------------- | 36 | | result | Variants properties | `IObject` | 37 | -------------------------------------------------------------------------------- /src/getSelectedNodes/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # getSelectedNodes 7 | 8 | Get the selected nodes of the current page, filtered with the specified predicate. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { getSelectedNodes, isFrameNode } from 'figx'; 14 | const theFrameNode = figma.createFrame(); 15 | const theTextNode = figma.createText(); 16 | figma.currentPage.selection = [frame, text]; 17 | getSelectedNodes(isFrameNode); // => theFrameNode 18 | ``` 19 | 20 | ## API 21 | 22 | ```ts 23 | const nodes = getSelectedNodes(predicate); 24 | ``` 25 | 26 | ### Params 27 | 28 | | Property | Description | Type | Default | 29 | | --------- | ------------------------------------------- | ---------------------- | ------------ | 30 | | predicate | Predicate used to filter out selected nodes | `SceneNode => boolean` | `() => true` | 31 | 32 | ### Result 33 | 34 | | Property | Description | Type | 35 | | -------- | ------------------------------------------------------------- | ------------ | 36 | | nodes | Selected nodes on the page that matches the predicate, if any | `SceneNodes` | 37 | -------------------------------------------------------------------------------- /src/isEqual/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import isEqual from '../index'; 2 | import { IObject } from '../../interface'; 3 | 4 | const solidPaint: IObject = { 5 | type: 'SOLID', 6 | color: { 7 | r: 1, 8 | g: 0, 9 | b: 0, 10 | }, 11 | }; 12 | 13 | const gradientPaint: IObject = { 14 | type: 'GRADIENT_LINEAR', 15 | gradientTransform: [ 16 | [1, 2, 3], 17 | [2, 3, 4], 18 | ], 19 | }; 20 | 21 | describe('isEqual', () => { 22 | it('should work for primitives', () => { 23 | expect(isEqual(1, 1)).toBeTruthy(); 24 | expect(isEqual('a', 'b')).toBeFalsy(); 25 | }); 26 | 27 | it('should work for objects', () => { 28 | expect(isEqual({ ...solidPaint }, { ...solidPaint })).toBeTruthy(); 29 | expect(isEqual({ ...solidPaint }, { ...solidPaint, color: { r: 0, g: 0, b: 0 } })).toBeFalsy(); 30 | }); 31 | 32 | it('should work for nested arrays', () => { 33 | expect(isEqual({ ...gradientPaint }, JSON.parse(JSON.stringify(gradientPaint)))).toBeTruthy(); 34 | expect( 35 | isEqual( 36 | { ...gradientPaint }, 37 | { 38 | ...gradientPaint, 39 | gradientTransform: [ 40 | [1, 2, 3], 41 | [2, 3, 5], 42 | ], 43 | }, 44 | ), 45 | ).toBeFalsy(); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /src/toImagePaint/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import toImagePaint from '../index'; 2 | import { createFigma } from 'figma-api-stub'; 3 | 4 | const hash = 'xxxxx'; 5 | describe('toImagePaint', () => { 6 | let figma: PluginAPI; 7 | beforeAll(() => { 8 | figma = createFigma({}); 9 | // @ts-ignore 10 | global.figma = figma; 11 | figma.createImage = function (bytes) { 12 | const _hash = hash; 13 | return { 14 | hash: _hash, 15 | getBytesAsync: function () { 16 | return Promise.resolve(bytes); 17 | }, 18 | }; 19 | }; 20 | }); 21 | it('should work for paint', () => { 22 | const bytes = new Uint8Array([1, 2, 3]); 23 | const imgPaint = toImagePaint(bytes); 24 | expect(imgPaint).toEqual({ 25 | imageHash: hash, 26 | type: 'IMAGE', 27 | scaleMode: 'FILL', 28 | scalingFactor: 0.5, 29 | opacity: 1, 30 | }); 31 | }); 32 | it('should work for paint with options', () => { 33 | const bytes = new Uint8Array([1, 2, 3]); 34 | const imgPaint = toImagePaint(bytes, { scaleMode: 'FIT' }); 35 | expect(imgPaint).toEqual({ 36 | imageHash: hash, 37 | type: 'IMAGE', 38 | scaleMode: 'FIT', 39 | scalingFactor: 0.5, 40 | opacity: 1, 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/interface/index.ts: -------------------------------------------------------------------------------- 1 | // figma node type. Reference: https://www.figma.com/plugin-docs/api/nodes/ 2 | export enum NodeTypeEnum { 3 | BOOLEAN_OPERATION = 'BOOLEAN_OPERATION', 4 | CODE_BLOCK = 'CODE_BLOCK', 5 | COMPONENT = 'COMPONENT', 6 | COMPONENT_SET = 'COMPONENT_SET', 7 | CONNECTOR = 'CONNECTOR', 8 | DOCUMENT = 'DOCUMENT', 9 | ELLIPSE = 'ELLIPSE', 10 | FRAME = 'FRAME', 11 | GROUP = 'GROUP', 12 | INSTANCE = 'INSTANCE', 13 | LINE = 'LINE', 14 | PAGE = 'PAGE', 15 | POLYGON = 'POLYGON', 16 | RECTANGLE = 'RECTANGLE', 17 | SHAPE_WITH_TEXT = 'SHAPE_WITH_TEXT', 18 | SLICE = 'SLICE', 19 | STAMP = 'STAMP', 20 | STAR = 'STAR', 21 | STICKY = 'STICKY', 22 | TEXT = 'TEXT', 23 | VECTOR = 'VECTOR', 24 | WIDGET = 'WIDGET', 25 | } 26 | 27 | export enum ColorFormat { 28 | STRING = 'string', 29 | ARRAY = 'array', 30 | OBJECT = 'object', 31 | UNIT_ARRAY = 'unitArray', 32 | UNIT_OBJECT = 'unitObject', 33 | } 34 | 35 | export enum ColorModel { 36 | HSL = 'hsl', 37 | RGB = 'rgb', 38 | HEX = 'hex', 39 | } 40 | 41 | export type IColor = 42 | | string 43 | | { r: number; g: number; b: number; alpha?: number } 44 | | { h: number; s: number; l: number }; 45 | 46 | export type IObject = Record; 47 | 48 | export interface IPosition { 49 | x: number; 50 | y: number; 51 | } 52 | -------------------------------------------------------------------------------- /src/clone/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # clone 7 | 8 | Clone an element with properties in prototype hoisted, useful for cloning [figma nodes](https://www.figma.com/plugin-docs/api/nodes/). Accepts an optional parameter `ignorePredicate` to filter out unwanted properties. 9 | 10 | ## Example 11 | 12 | ```tsx 13 | import { clone } from 'figx'; 14 | 15 | const object = { count: 1, sum: 2 }; 16 | const deepObject = { 17 | ...object, 18 | children: [{ a: 1 }], 19 | }; 20 | 21 | clone(object); // => { count: 1, sum: 2 } 22 | clone(deepObject); // => { count: 1, sum: 2, children: [{ a: 1}] } 23 | 24 | // don't copy children 25 | const pred = (propName) => propName === 'children'; 26 | clone(deepObject, pred); // => { count: 1, sum: 2 } 27 | ``` 28 | 29 | ## API 30 | 31 | ```ts 32 | const result = clone(element: T, predicate: (propName: string, element: T) => boolean); 33 | ``` 34 | 35 | ### Params 36 | 37 | | Property | Description | Type | Default | 38 | | --- | --- | --- | --- | 39 | | element | The element to be cloned | T | - | 40 | | predicate | Logic to ignore properties from being copied | (propName: string, element: T) => boolean) | - | 41 | 42 | ### Result 43 | 44 | | Property | Description | Type | 45 | | -------- | -------------- | ---- | 46 | | result | Cloned element | `T` | 47 | -------------------------------------------------------------------------------- /src/getDropPosition/index.ts: -------------------------------------------------------------------------------- 1 | import { IPosition } from '../interface'; 2 | 3 | const RIGHT_PANEL_WIDTH = 240; 4 | const TOP_BAR_HEIGHT = 40; 5 | 6 | // Credit: https://github.com/jackiecorn/figma-plugin-drag-and-drop/blob/master/code.ts 7 | export default function getDropPosition( 8 | dropPositionRelativeToPlugin: IPosition, 9 | offset: IPosition, 10 | windowWidth: number, 11 | ): IPosition { 12 | // bounds: the position and size of the visible area of the canvas. 13 | const { bounds, zoom } = figma.viewport; 14 | // There are two states of the Figma interface: With or without the UI (toolbar + left and right panes) 15 | const hasUI = Math.round(bounds.width * zoom) !== windowWidth; 16 | // Since the left pane is resizable, we need to get its width by subtracting the right pane and the canvas width from the window width. 17 | const leftPaneWidth = windowWidth - bounds.width * zoom - RIGHT_PANEL_WIDTH; 18 | 19 | // Getting the position of the cursor relative to the top-left corner of the canvas. 20 | const xFromCanvas = hasUI 21 | ? dropPositionRelativeToPlugin.x - leftPaneWidth 22 | : dropPositionRelativeToPlugin.x; 23 | const yFromCanvas = hasUI 24 | ? dropPositionRelativeToPlugin.y - TOP_BAR_HEIGHT 25 | : dropPositionRelativeToPlugin.y; 26 | 27 | // The canvas position of the drop point can be calculated using the following: 28 | const x = bounds.x + xFromCanvas / zoom - offset.x; 29 | const y = bounds.y + yFromCanvas / zoom - offset.y; 30 | 31 | return { x, y }; 32 | } 33 | -------------------------------------------------------------------------------- /src/copyToClipboardAsync/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # copyToClipboardAsync 7 | 8 | Copy text to clipboard with [`Clipboard.writeText`](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText) 9 | 10 | ## Example 11 | 12 | ```tsx | preview 13 | import { copyToClipboardAsync } from 'figx'; 14 | import { Input, Button, Message, Space } from '@arco-design/web-react'; 15 | import React, { useState } from 'react'; 16 | 17 | function App() { 18 | const [textValue, setTextValue] = useState(''); 19 | const onClick = async () => { 20 | await copyToClipboardAsync(textValue); 21 | Message.info(`Successfully copied text: ${textValue}`); 22 | }; 23 | return ( 24 | 25 | 26 | 29 | 30 | ); 31 | } 32 | 33 | export default () => ; 34 | ``` 35 | 36 | ## API 37 | 38 | ```ts 39 | const promise = copyToClipboardAsync(text); 40 | ``` 41 | 42 | ### Params 43 | 44 | | Property | Description | Type | Default | 45 | | -------- | ---------------------------------- | -------- | ------- | 46 | | text | The text to be copied to clipboard | `string` | - | 47 | 48 | ### Result 49 | 50 | | Property | Description | Type | 51 | | --- | --- | --- | 52 | | promise | The promise is resolved once the text has been copied to clipboard successfully. The promise is rejected if no permission to write to the clipboard or `Clipboard` API is not supported | `Promise` | 53 | -------------------------------------------------------------------------------- /src/copyToClipboard/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # copyToClipboard 7 | 8 | Copy text to clipboard with `document.execCommand` 9 | 10 | > Note: `document.execCommand('copy')` doesn't work in figma browser due to `copy` command is disabled. `window.copy` is put in as a workaround for this issue. However, it's only supported in chrome. 11 | 12 | ## Example 13 | 14 | ```tsx | preview 15 | import { copyToClipboard } from 'figx'; 16 | import { Input, Button, Message, Space } from '@arco-design/web-react'; 17 | import React, { useState } from 'react'; 18 | 19 | function App() { 20 | const [textValue, setTextValue] = useState(''); 21 | const onClick = () => { 22 | copyToClipboard(textValue); 23 | Message.info(`Successfully copied text: ${textValue}`); 24 | }; 25 | return ( 26 | 27 | 28 | 31 | 32 | ); 33 | } 34 | 35 | export default () => ; 36 | ``` 37 | 38 | ## API 39 | 40 | ```ts 41 | const isCopied = copyToClipboard(text); 42 | ``` 43 | 44 | ### Params 45 | 46 | | Property | Description | Type | Default | 47 | | -------- | ---------------------------------- | -------- | ------- | 48 | | text | The text to be copied to clipboard | `string` | - | 49 | 50 | ### Result 51 | 52 | | Property | Description | Type | Default | 53 | | -------- | --------------------------------------- | --------- | ------- | 54 | | isCopied | Whether the text is copied to clipboard | `boolean` | - | 55 | -------------------------------------------------------------------------------- /src/solidPaintToWebRgb/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | nav: 3 | path: /utilities 4 | --- 5 | 6 | # solidPaintToWebRgb 7 | 8 | Turn solid paint into web rgb 9 | 10 | ## Example 11 | 12 | ```tsx | preview 13 | import { solidPaintToWebRgb, ColorFormat } from 'figx'; 14 | import React from 'react'; 15 | import { Descriptions } from '@arco-design/web-react'; 16 | 17 | export default () => { 18 | const paint: SolidPaint = { 19 | type: 'SOLID', 20 | color: { 21 | r: 0.5285416841506958, 22 | g: 0.5314041376113892, 23 | b: 0.737500011920929, 24 | }, 25 | opacity: 1, 26 | }; 27 | 28 | const data = [ 29 | { 30 | label: 'Paint', 31 | value:
{JSON.stringify(paint, null, 2)}
, 32 | }, 33 | { 34 | label: 'RGB in string', 35 | value:
{solidPaintToWebRgb(paint)}
, 36 | }, 37 | { 38 | label: 'RGB in object', 39 | value:
{JSON.stringify(solidPaintToWebRgb(paint, ColorFormat.OBJECT), null, 2)}
, 40 | }, 41 | { 42 | label: 'RGB in array', 43 | value:
{`[${solidPaintToWebRgb(paint, ColorFormat.ARRAY).join(',')}]`}
, 44 | }, 45 | ]; 46 | 47 | return ; 48 | }; 49 | ``` 50 | 51 | ## API 52 | 53 | ```ts 54 | const result = solidPaintToWebRgb(paint, colorFormat); 55 | ``` 56 | 57 | ### Params 58 | 59 | | Property | Description | Type | Default | 60 | | ----------- | ------------ | ------------- | ------------------ | 61 | | paint | Solid paint | `SolidPaint` | - | 62 | | colorFormat | Color format | `ColorFormat` | ColorFormat.STRING | 63 | 64 | ### Result 65 | 66 | | Property | Description | Type | 67 | | --- | --- | --- | 68 | | result | rgb value of the color | `string` \| `array` \| `object` depending on the `colorFormat` argument | 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Figx

3 |
4 | 5 |
6 | 7 | A comprehensive and reliable figma utilities library 8 | 9 | [![npm-version-img]][npm-link] [![npm-downloads-img]][npm-link] [![license-img]][license-link] [![bundle-size-img]](https://bundlephobia.com/result?p=figx) 10 | 11 | [npm-link]: https://www.npmjs.com/package/figx 12 | [npm-version-img]: https://img.shields.io/npm/v/figx.svg?style=flat 13 | [npm-downloads-img]: https://img.shields.io/npm/dm/figx.svg?style=flat 14 | [license-img]: https://img.shields.io/badge/license-MIT-blue.svg 15 | [license-link]: https://github.com/headwindz/figx/blob/main/LICENSE 16 | [bundle-size-img]: https://img.shields.io/bundlephobia/minzip/figx?cacheSeconds=1800 17 | 18 |
19 | 20 | ## 📚 Documentation 21 | 22 | - [Home](https://figx.vercel.app) 23 | - [Guide](https://figx.vercel.app/guide) 24 | - [Utilities](https://figx.vercel.app/utilities) 25 | 26 | ## ✨ Features 27 | 28 | - A comprehensive collection of utilities 29 | - Easy to learn and use with clear documentation 30 | - Written in TypeScript with native type support 31 | 32 | ## 📦 Install 33 | 34 | ```bash 35 | $ npm install figx --save 36 | # or 37 | $ yarn add figx 38 | ``` 39 | 40 | ## 🔨 Usage 41 | 42 | ```ts 43 | import { toRgb, ColorFormat } from 'figx'; 44 | toRgb('#FF0000'); // => 'rgb(255, 0, 0)'; 45 | toRgb({ r: 255, g: 0, b: 0 }, ColorFormat.ARRAY); // => [255, 0, 0] 46 | toRgb('hsl(0, 100%, 50%)', ColorFormat.OBJECT); // => { r: 255, g: 0, b: 0 } 47 | ``` 48 | 49 | ## 🤝 Contributing 50 | 51 | ```bash 52 | $ git clone git@github.com:headwindz/figx.git 53 | $ yarn 54 | $ yarn start 55 | ``` 56 | 57 | Open your browser and visit http://127.0.0.1:8000 58 | 59 | ## 🙏 Credit 60 | 61 | Inspired by the following awesome work: 62 | 63 | [create-figma-plugin](https://github.com/yuanqing/create-figma-plugin) 64 | 65 | [figma-plugin-helpers](https://github.com/figma-plugin-helper-functions/figma-plugin-helpers) 66 | -------------------------------------------------------------------------------- /src/toRgb/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import toRgb from '../index'; 2 | import { ColorFormat } from '../../interface'; 3 | 4 | const hex = '#FF0000'; 5 | const hsl = 'hsl(0, 100%, 50%)'; 6 | const rgba = { r: 255, g: 0, b: 0, alpha: 0.3 }; 7 | 8 | describe('toRgb', () => { 9 | it('should work for rgb string', () => { 10 | expect(toRgb(hex, ColorFormat.STRING)).toEqual('rgb(255, 0, 0)'); 11 | expect(toRgb(rgba, ColorFormat.STRING)).toEqual('rgba(255, 0, 0, 0.3)'); 12 | expect(toRgb(hsl, ColorFormat.STRING)).toEqual('rgb(255, 0, 0)'); 13 | }); 14 | 15 | it('should work for rgb array', () => { 16 | expect(toRgb(hex, ColorFormat.ARRAY)).toEqual([255, 0, 0]); 17 | expect(toRgb(rgba, ColorFormat.ARRAY)).toEqual([255, 0, 0, 0.3]); 18 | expect(toRgb(hsl, ColorFormat.ARRAY)).toEqual([255, 0, 0]); 19 | }); 20 | 21 | it('should work for rgb unit array', () => { 22 | expect(toRgb(hex, ColorFormat.UNIT_ARRAY)).toEqual([1, 0, 0]); 23 | expect(toRgb(rgba, ColorFormat.UNIT_ARRAY)).toEqual([1, 0, 0, 0.3]); 24 | expect(toRgb(hsl, ColorFormat.UNIT_ARRAY)).toEqual([1, 0, 0]); 25 | }); 26 | 27 | it('should work for rgb object', () => { 28 | expect(toRgb(hex, ColorFormat.OBJECT)).toEqual({ 29 | r: 255, 30 | g: 0, 31 | b: 0, 32 | }); 33 | expect(toRgb(rgba, ColorFormat.OBJECT)).toEqual({ 34 | r: 255, 35 | g: 0, 36 | b: 0, 37 | alpha: 0.3, 38 | }); 39 | expect(toRgb(hsl, ColorFormat.OBJECT)).toEqual({ 40 | r: 255, 41 | g: 0, 42 | b: 0, 43 | }); 44 | }); 45 | 46 | it('should work for rgb unit object', () => { 47 | expect(toRgb(hex, ColorFormat.UNIT_OBJECT)).toEqual({ 48 | r: 1, 49 | g: 0, 50 | b: 0, 51 | }); 52 | expect(toRgb(rgba, ColorFormat.UNIT_OBJECT)).toEqual({ 53 | r: 1, 54 | g: 0, 55 | b: 0, 56 | alpha: 0.3, 57 | }); 58 | expect(toRgb(hsl, ColorFormat.UNIT_OBJECT)).toEqual({ 59 | r: 1, 60 | g: 0, 61 | b: 0, 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "figx", 3 | "version": "0.1.3", 4 | "description": "A comprehensive and reliable figma utilities library", 5 | "keywords": [ 6 | "utilities", 7 | "figma", 8 | "figma plugin", 9 | "figma api" 10 | ], 11 | "scripts": { 12 | "start": " NODE_OPTIONS=--openssl-legacy-provider dumi dev", 13 | "docs:build": "NODE_OPTIONS=--openssl-legacy-provider dumi build", 14 | "docs:deploy": "gh-pages -d docs-dist", 15 | "build": "father-build", 16 | "deploy": "npm run docs:build && npm run docs:deploy", 17 | "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"", 18 | "test": "umi-test", 19 | "test:coverage": "umi-test --coverage", 20 | "prepublishOnly": "npm run build" 21 | }, 22 | "publishConfig": { 23 | "registry": "https://registry.npmjs.org/" 24 | }, 25 | "module": "./es/index.js", 26 | "main": "./lib/index.js", 27 | "unpkg": "./dist/index.umd.min.js", 28 | "typings": "es/index.d.ts", 29 | "gitHooks": { 30 | "pre-commit": "lint-staged" 31 | }, 32 | "lint-staged": { 33 | "*.{js,jsx,less,md,json}": [ 34 | "prettier --write" 35 | ], 36 | "*.ts?(x)": [ 37 | "prettier --parser=typescript --write" 38 | ] 39 | }, 40 | "files": [ 41 | "dist", 42 | "es", 43 | "lib", 44 | "package.json", 45 | "README.md" 46 | ], 47 | "license": "MIT", 48 | "dependencies": { 49 | "color": "3.2.1" 50 | }, 51 | "devDependencies": { 52 | "@arco-design/web-react": "^2.28.0", 53 | "@figma/plugin-typings": "^1.40.0", 54 | "@testing-library/jest-dom": "^5.15.1", 55 | "@testing-library/react": "^12.1.2", 56 | "@types/color": "^3.0.2", 57 | "@types/jest": "^27.0.3", 58 | "@umijs/fabric": "^2.8.1", 59 | "@umijs/test": "^3.0.5", 60 | "babel-plugin-import": "^1.13.3", 61 | "dumi": "^1.1.0", 62 | "father-build": "^1.17.2", 63 | "figma-api-stub": "^0.0.49", 64 | "gh-pages": "^3.0.0", 65 | "lint-staged": "^10.0.7", 66 | "prettier": "^2.2.1", 67 | "react": "^17.0.2", 68 | "react-dom": "^17.0.2", 69 | "yorkie": "^2.0.0" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // Interfaces 2 | export { NodeTypeEnum, ColorFormat, ColorModel } from './interface'; 3 | export type { IColor, IPosition } from './interface'; 4 | 5 | // Node 6 | export { default as isTextNode } from './isTextNode'; 7 | export { default as isFrameNode } from './isFrameNode'; 8 | export { default as isEllipseNode } from './isEllipseNode'; 9 | export { default as isPageNode } from './isPageNode'; 10 | export { default as isComponentNode } from './isComponentNode'; 11 | export { default as isInstanceNode } from './isInstanceNode'; 12 | export { default as isVisible } from './isVisible'; 13 | export { default as isFillableNode } from './isFillableNode'; 14 | export { default as getSelectionNodes } from './getSelectedNodes'; 15 | export { default as isLocked } from './isLocked'; 16 | export { default as isWithinInstance } from './isWithinInstance'; 17 | export { default as getComponentProps } from './getComponentProps'; 18 | 19 | // Color 20 | export { default as toRgb } from './toRgb'; 21 | export { default as toHsl } from './toHsl'; 22 | export { default as toHex } from './toHex'; 23 | export { default as isSameColor } from './isSameColor'; 24 | export { default as isValidColor } from './isValidColor'; 25 | export { default as getColorContrast } from './getColorContrast'; 26 | 27 | // Paint 28 | export { default as toSolidPaint } from './toSolidPaint'; 29 | export { default as solidPaintToWebRgb } from './solidPaintToWebRgb'; 30 | export { default as toImagePaint } from './toImagePaint'; 31 | export { default as isImagePaint } from './isImagePaint'; 32 | export { default as isGradientPaint } from './isGradientPaint'; 33 | 34 | // Storage 35 | export { default as getPluginData } from './getPluginData'; 36 | export { default as setPluginData } from './setPluginData'; 37 | 38 | // Others 39 | export { default as getRandomId } from './getRandomId'; 40 | export { default as isEqual } from './isEqual'; 41 | export { default as clone } from './clone'; 42 | export { default as copyToClipboard } from './copyToClipboard'; 43 | export { default as copyToClipboardAsync } from './copyToClipboardAsync'; 44 | export { default as getDropPosition } from './getDropPosition'; 45 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: figx - Figma Utilities Library 3 | hero: 4 | title: figx 5 | desc: A comprehensive and reliable figma utilities library 6 | actions: 7 | - text: Guide 8 | link: /guide 9 | - text: Getting Started 10 | link: /utilities 11 | footer: Open-source MIT Licensed | Copyright © 2020-present headwindz
12 | --- 13 | 14 | [![npm-version-img]][npm-link] [![npm-downloads-img]][npm-link] [![license-img]][license-link] [![bundle-size-img]](https://bundlephobia.com/result?p=figx) 15 | 16 | [npm-link]: https://www.npmjs.com/package/figx 17 | [npm-version-img]: https://img.shields.io/npm/v/figx.svg?style=flat 18 | [npm-downloads-img]: https://img.shields.io/npm/dm/figx.svg?style=flat 19 | [license-img]: https://img.shields.io/badge/license-MIT-blue.svg 20 | [license-link]: https://github.com/headwindz/figx/blob/main/LICENSE 21 | [bundle-size-img]: https://img.shields.io/bundlephobia/minzip/figx?cacheSeconds=1800 22 | 23 | ## ✨ Features 24 | 25 | - A comprehensive collection of utilities 26 | - Easy to learn and use with clear documentation 27 | - Written in TypeScript with native type support 28 | 29 | ## 📦 Install 30 | 31 | ```bash 32 | $ npm install figx --save 33 | # or 34 | $ yarn add figx 35 | ``` 36 | 37 | ## 🔨 Usage 38 | 39 | ```ts 40 | import { toRgb, ColorFormat } from 'figx'; 41 | toRgb('#FF0000'); // => 'rgb(255, 0, 0)'; 42 | toRgb({ r: 255, g: 0, b: 0 }, ColorFormat.ARRAY); // => [255, 0, 0] 43 | toRgb('hsl(0, 100%, 50%)', ColorFormat.OBJECT); // => { r: 255, g: 0, b: 0 } 44 | ``` 45 | 46 | ## 💻 Demos 47 | 48 | - [Sandbox][sandbox-link] 49 | - [Runkit][runkit-link] 50 | 51 | [sandbox-link]: https://codesandbox.io/s/demo-316l9?file=/src/index.js 52 | [runkit-link]: https://runkit.com/headwindz/61d919edcdc69d0008742627 53 | 54 | ## 🤝 Contributing 55 | 56 | ```bash 57 | $ git clone git@github.com:headwindz/figx.git 58 | $ yarn 59 | $ yarn start 60 | ``` 61 | 62 | Open your browser and visit http://127.0.0.1:8000 63 | 64 | ## 🙏 Credit 65 | 66 | Inspired by the following awesome work: 67 | 68 | [create-figma-plugin](https://github.com/yuanqing/create-figma-plugin) 69 | 70 | [figma-plugin-helpers](https://github.com/figma-plugin-helper-functions/figma-plugin-helpers) 71 | -------------------------------------------------------------------------------- /.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'dumi'; 2 | 3 | export default defineConfig({ 4 | title: 'figx', 5 | favicon: 6 | 'https://user-images.githubusercontent.com/9554297/83762004-a0761b00-a6a9-11ea-83b4-9c8ff721d4b8.png', 7 | logo: 'https://user-images.githubusercontent.com/9554297/83762004-a0761b00-a6a9-11ea-83b4-9c8ff721d4b8.png', 8 | outputPath: 'docs-dist', 9 | mode: 'site', 10 | // more config: https://d.umijs.org/config 11 | resolve: { 12 | passivePreview: true, 13 | }, 14 | navs: [ 15 | { title: 'Guide', path: '/guide' }, 16 | { 17 | title: 'Utilities', 18 | path: '/utilities', 19 | }, 20 | { 21 | title: 'GitHub', 22 | path: 'https://github.com/n0ruSh/figx', 23 | }, 24 | ], 25 | menus: { 26 | '/utilities': [ 27 | { 28 | title: 'Node', 29 | children: [ 30 | 'isTextNode', 31 | 'isFrameNode', 32 | 'isPageNode', 33 | 'isEllipseNode', 34 | 'isComponentNode', 35 | 'isInstanceNode', 36 | 'isVisible', 37 | 'isFillableNode', 38 | 'isLocked', 39 | 'getSelectedNodes', 40 | 'isWithinInstance', 41 | 'getComponentProps', 42 | ], 43 | }, 44 | { 45 | title: 'Color', 46 | children: ['toRgb', 'toHsl', 'toHex', 'isSameColor', 'getColorContrast', 'isValidColor'], 47 | }, 48 | { 49 | title: 'Paint', 50 | children: [ 51 | 'toSolidPaint', 52 | 'solidPaintToWebRgb', 53 | 'toImagePaint', 54 | 'isImagePaint', 55 | 'isGradientPaint', 56 | ], 57 | }, 58 | { 59 | title: 'Storage', 60 | children: ['getPluginData', 'setPluginData'], 61 | }, 62 | { 63 | title: 'Others', 64 | children: [ 65 | 'getRandomId', 66 | 'copyToClipboard', 67 | 'copyToClipboardAsync', 68 | 'clone', 69 | 'isEqual', 70 | 'getDropPosition', 71 | ], 72 | }, 73 | ], 74 | }, 75 | theme: { 76 | '@c-primary': '#e78c56', 77 | }, 78 | extraBabelPlugins: [ 79 | [ 80 | 'import', 81 | { 82 | libraryName: '@arco-design/web-react', 83 | libraryDirectory: 'es', 84 | camel2DashComponentName: false, 85 | style: true, // 样式按需加载 86 | }, 87 | '@arco-design/web-react', 88 | ], 89 | ], 90 | }); 91 | --------------------------------------------------------------------------------