figma-plugin-react-hooks
Advanced tools
+29
-7
@@ -225,2 +225,4 @@ "use strict"; | ||
| // src/index.ts | ||
| var currentPage; | ||
| var registeredForChanges = false; | ||
| var uiApiMethods = { | ||
@@ -242,5 +244,3 @@ _onSelectionChange(selection) { | ||
| var changesApplyToSelectedNodesOrDescendants = (e, nodes) => { | ||
| const changesApplyToNodes = e.documentChanges.some( | ||
| (change) => nodes.findIndex((node) => node.id === change.id) !== -1 | ||
| ); | ||
| const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1); | ||
| if (changesApplyToNodes) { | ||
@@ -260,3 +260,3 @@ return true; | ||
| }; | ||
| var documentChangeHandler = (e) => { | ||
| var nodeChangeHandler = (e) => { | ||
| if (figma.currentPage.selection.length > 0) { | ||
@@ -269,2 +269,9 @@ const selection = figma.currentPage.selection; | ||
| }; | ||
| var pageChangeHandler = () => { | ||
| if (registeredForChanges) { | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on("nodechange", nodeChangeHandler); | ||
| } | ||
| }; | ||
| var apiMethods = { | ||
@@ -278,4 +285,8 @@ _registerForSelectionChange(opts) { | ||
| } | ||
| figma.on("selectionchange", selectionChangeHandler); | ||
| figma.on("documentchange", documentChangeHandler); | ||
| if (!registeredForChanges) { | ||
| figma.on("selectionchange", selectionChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on("nodechange", nodeChangeHandler); | ||
| registeredForChanges = true; | ||
| } | ||
| selectionChangeHandler(); | ||
@@ -285,3 +296,4 @@ }, | ||
| figma.off("selectionchange", selectionChangeHandler); | ||
| figma.off("documentchange", documentChangeHandler); | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| registeredForChanges = false; | ||
| }, | ||
@@ -308,2 +320,12 @@ _setSelection(newSelection) { | ||
| } | ||
| if (typeof figma !== "undefined") { | ||
| figma.on("currentpagechange", () => { | ||
| pageChangeHandler(); | ||
| }); | ||
| figma.on("close", () => { | ||
| figma.off("currentpagechange", pageChangeHandler); | ||
| figma.off("selectionchange", selectionChangeHandler); | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| }); | ||
| } | ||
@@ -310,0 +332,0 @@ // src/hook.ts |
+2
-2
| { | ||
| "version": 3, | ||
| "sources": ["../src/hook.ts", "../src/constants.ts", "../src/useMountedEffect.ts", "../src/index.ts", "../src/typeUtils.ts", "../src/utils.ts"], | ||
| "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport { DEFAULT_HOOK_OPTIONS } from './constants';\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport { CombineObjects } from './typePrimitives';\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n FigmaSelectionHookType\n} from './types';\n\nexport { FigmaSelectionHookOptions, FigmaSelectionHookNode, FigmaSelectionHookType } from './types';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * Only one config will take presence and it will be the config of the first hook that is mounted\n */\nconst useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(\n hookOptions?: Options\n): FigmaSelectionHookType<Options> => {\n const opts = { ...DEFAULT_HOOK_OPTIONS, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<\n readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[]\n >([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [\n selection as readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[],\n api._setSelection\n ];\n};\n\nexport default useFigmaSelection;\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id,\n type: node.type\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n for (const node of nodes) {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n }\n } else {\n for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"], | ||
| "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAAA,gBAAoC;;;ACG7B,IAAM,cAAc;AAKpB,IAAM,uBAAuB;AAAA,EAClC,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,kBAAkB,CAAC;AAAA,EACnB,mBAAmB;AAAA,EACnB,6BAA6B;AAAA,EAC7B,gBAAgB,CAAC;AAAA,EACjB,sBAAsB,CAAC;AACzB;;;AClBA,mBAAkC;AAElC,IAAM,mBAAmB,CAAC,QAA8B,SAA4C;AAClG,QAAM,kBAAc,qBAAO,KAAK;AAEhC,8BAAU,MAAM;AACd,QAAI,YAAY,SAAS;AACvB,aAAO,OAAO;AAAA,IAChB;AACA,gBAAY,UAAU;AAAA,EAExB,GAAG,IAAI;AACT;AAEA,IAAO,2BAAQ;;;ACdf,8BAA6C;;;ACEtC,IAAM,UAAU,MAAM;AAEtB,IAAM,mBAAmB,OAAO;AAEhC,IAAM,sBAAsB,CAAsB,SAAuC;AAC9F,SAAO,cAAc;AACvB;;;ACQA,IAAM,kCAAkC,CAAyB,KAAiB,SAAwB;AAhB1G;AAiBE;AAAA;AAAA,IAEE,EAAE,QAAQ,oCAAkC,UAAK,WAAL,mBAAa,UAAS;AAAA,IAElE,QAAQ,uBACR,QAAQ;AAAA;AAEZ;AAEA,IAAM,wBAAwB,CAC5B,MACA,iBAC6B;AAE7B,QAAM,cAAc,OAAO,0BAAgC,OAAO,eAAe,IAAI,CAAC;AAMtF,QAAM,aAAa,iBAAiB,WAAW;AAC/C,MAAI,UAAU,WAAW,OAAO,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ,UAAU;AAEnF,MAAI,cAAc;AAChB,cAAU,QAAQ,OAAO,CAAC,QAAQ,aAAa,SAAS,GAA2B,CAAC;AAAA,EACtF;AAEA,YAAU,QAAQ,OAAO,CAAC,QAAQ,gCAAgC,KAAK,IAAI,CAAC;AAE5E,QAAM,uBAAuB;AAAA,IAC3B,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,EACb;AAEA,aAAW,UAAU,SAAS;AAC5B,yBAAqB,MAAM,IAAI,KAAK,MAAM;AAAA,EAC5C;AAEA,SAAO;AACT;AAEA,IAAM,4BAA4B,CAAC,aAAiC;AAElE,QAAM,cAAc,OAAO,0BAAoC,OAAO,eAAe,QAAQ,CAAC;AAM9F,QAAM,aAAa,iBAAiB,WAAW;AAC/C,QAAM,UAAU,WAAW;AAAA,IACzB,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ;AAAA,EAC3C;AAEA,UAAQ,KAAK,IAAI;AAEjB,QAAM,uBAAuB,CAAC;AAE9B,aAAW,UAAU,SAAS;AAE5B,yBAAqB,MAAM,IAAI,SAAS,MAAM;AAAA,EAChD;AAEA,SAAO;AACT;AAEA,IAAM,wBAAwB,CAI5B,gBACAC,aAC2B;AAC3B,QAAM,EAAE,iBAAiB,IAAIA;AAC7B,QAAM,SAA0C,CAAC;AAEjD,aAAW,oBAAoB,iBAAiB,cAAc,GAAG;AAC/D,QACE,qBAAqB,SACpB,QAAQ,gBAAgB,KAAK,iBAAiB,SAAS,gBAAqD,GAC7G;AACA,YAAM,gBAAgB,eAAe,gBAAgB;AAGrD,UAAI,MAAM,QAAQ,aAAa,GAAG;AAChC,cAAM,UAA2B;AACjC,cAAM,oBAAgC,CAAC;AAEvC,mBAAW,iBAAiB,SAAS;AACnC,gBAAM,WAAW,MAAM,UAAU,gBAAgB,cAAc,EAAE;AACjE,cAAI,UAAU;AACZ,8BAAkB,KAAK,0BAA0B,QAAQ,CAAC;AAAA,UAC5D;AAAA,QACF;AAEA,0BAAkB,SAAS,MACxB,OAAO,gBAAmD,IAAI;AAAA,MACnE,WAAW,qBAAqB,uBAAuB;AACrD,cAAM,cAAc;AACpB,cAAM,iBAA2C,CAAC;AAElD,mBAAW,wBAAwB,OAAO,KAAK,WAAW,GAAG;AAC3D,gBAAM,WAAW,MAAM,UAAU,gBAAgB,YAAY,oBAAoB,EAAE,EAAE;AACrF,uBAAa,eAAe,oBAAoB,IAAI,0BAA0B,QAAQ;AAAA,QACxF;AAEA,eAAO,KAAK,cAAc,EAAE,SAAS,MAAM,OAAO,gBAAyC,IAAI;AAAA,MACjG,OAAO;AACL,cAAM,QAAQ;AACd,cAAM,mBAAmB,MAAM,UAAU,gBAAgB,MAAM,EAAE;AAEjE,6BACG,OAAO,gBAAmD,IAAI,0BAA0B,gBAAgB;AAAA,MAC7G;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,oBAAoB,CAAC,MAAiB,mBAAqD;AAC/F,QAAM,aAAqC,CAAC;AAE5C,aAAW,OAAO,gBAAgB;AAChC,UAAM,QAAQ,KAAK,cAAc,GAAG;AACpC,QAAI,UAAU,QAAW;AACvB,iBAAW,GAAG,IAAI;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,0BAA0B,CAC9B,MACA,yBACwB;AACxB,QAAM,mBAAqE,CAAC;AAE5E,aAAW,aAAa,iBAAiB,oBAAoB,GAAG;AAC9D,qBAAiB,SAAS,IAAI,CAAC;AAC/B,eAAW,OAAO,qBAAqB,SAAS,GAAG;AACjD,MAAC,iBAAiB,SAAS,EAA6B,GAAG,IAAI,KAAK,oBAAoB,WAAqB,GAAG;AAAA,IAClH;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,oCAAoC,CACxC,MACAA,UACA,qBACoC;AACpC,QAAM,EAAE,mBAAmB,kBAAkB,6BAA6B,gBAAgB,qBAAqB,IAC7GA;AAEF,QAAM,eAAe;AAAA,IACnB;AAAA,IACA,sBAAsB,QAAQ,SAAY;AAAA,EAC5C;AAEA,aAAW,OAAO,iBAAiB,YAAY,GAAG;AAChD,QAAI,aAAa,GAAG,MAAM,MAAM,OAAO;AACrC,mBAAa,GAAG,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,MACE,KAAK,mBAAmB,WACvB,qBAAqB,SAAU,QAAQ,gBAAgB,KAAK,iBAAiB,SAAS,IACvF;AACA,iBAAa,yBAAyB,sBAAsB,KAAK,gBAAgBA,QAAO;AAAA,EAC1F;AAEA,MAAI,6BAA6B;AAC/B,iBAAa,mBAAmB;AAAA,EAClC;AAEA,MAAI,eAAe,SAAS,GAAG;AAC7B,iBAAa,aAAa,kBAAkB,MAAM,cAAc;AAAA,EAClE;AAEA,MAAI,OAAO,KAAK,oBAAoB,EAAE,SAAS,GAAG;AAChD,iBAAa,mBAAmB,wBAAwB,MAAM,oBAAoB;AAAA,EACpF;AAEA,SAAO;AACT;AAEO,IAAM,wBAAwB,CACnC,OACAA,UACA,mBAA4B,SACU;AACtC,QAAM,SAA4C,CAAC;AACnD,QAAM,EAAE,WAAW,gBAAgB,IAAIA;AAEvC,MAAI,cAAc,QAAW;AAC3B,eAAW,QAAQ,OAAO;AACxB,UAAI,UAAU,SAAS,KAAK,IAAI,GAAG;AACjC,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG,WAAW,oBAAoB,IAAI,KAAK,iBAAiB;AACvD,eAAO,KAAK,GAAG,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF;AAAA,EACF,OAAO;AACL,eAAW,QAAQ,OAAO;AACxB,UAAI,oBAAoB,IAAI,KAAK,iBAAiB;AAChD,cAAM,UAAU,iCACX,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,IADtE;AAAA,UAEd,UAAU,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO;AAAA,QAC1F;AACA,eAAO,KAAK,OAAO;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AFtNA,IAAM,eAAe;AAAA,EACnB,mBAAmB,WAA+D;AAChF,QAAI,OAAO,OAAO,6BAA6B,aAAa;AAC1D,aAAO,yBAAyB,SAAS;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,IAAI,YAAQ,qCAAY,YAAY;AAEpC,IAAM,yBAAyB,CAAC,eAA2B;AAChE,cAAQ,qCAAY,cAAc,UAAU;AAC9C;AAEA,IAAM,yBAAyB,MAAM;AACnC,QAAM,oBAAoB,sBAAsB,MAAM,YAAY,WAAW,OAAO;AACpF,QAAM,mBAAmB,iBAAiB;AAC5C;AAEA,IAAM,2CAA2C,CAAC,GAAwB,UAAyC;AACjH,QAAM,sBAAsB,EAAE,gBAAgB;AAAA,IAC5C,CAAC,WAAW,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,OAAO,EAAE,MAAM;AAAA,EACnE;AAEA,MAAI,qBAAqB;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,cAA2B,CAAC;AAElC,aAAW,QAAQ,OAAO;AACxB,QAAI,oBAAoB,IAAI,GAAG;AAC7B,kBAAY,KAAK,GAAG,KAAK,QAAQ;AAAA,IACnC;AAAA,EACF;AAEA,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,yCAAyC,GAAG,WAAW;AAChE;AAEA,IAAM,wBAAwB,CAAC,MAA2B;AACxD,MAAI,MAAM,YAAY,UAAU,SAAS,GAAG;AAC1C,UAAM,YAAY,MAAM,YAAY;AAEpC,QAAI,yCAAyC,GAAG,SAAS,GAAG;AAC1D,6BAAuB;AAAA,IACzB;AAAA,EACF;AACF;AAEA,IAAM,aAAa;AAAA,EACjB,4BAA4B,MAAmD;AAC7E,cAAU;AAEV,UAAM,aAAa,KAAK;AACxB,QAAI,YAAY;AACd,6BAAuB,UAAU;AACjC,2BAAqB,UAAU;AAAA,IACjC;AAEA,UAAM,GAAG,mBAAmB,sBAAsB;AAClD,UAAM,GAAG,kBAAkB,qBAAqB;AAChD,2BAAuB;AAAA,EACzB;AAAA,EACA,gCAAgC;AAC9B,UAAM,IAAI,mBAAmB,sBAAsB;AACnD,UAAM,IAAI,kBAAkB,qBAAqB;AAAA,EACnD;AAAA,EACA,cAA6C,cAAiB;AAC5D,UAAM,YAAY,YAAY;AAAA,EAChC;AACF;AAEO,IAAI,UAAM,yCAAgB,UAAU;AAEpC,IAAM,uBAAuB,CAAC,eAA2B;AAC9D,YAAM,yCAAgB,YAAY,UAAU;AAC9C;AAEA,IAAI;AAEG,IAAI,YAAsC,CAAC;AAE3C,IAAM,eAAe,CAAC,iBAA2C;AACtE,cAAY;AACd;AAGA,IAAI,OAAO,WAAW,aAAa;AACjC,SAAO,2BAA2B,CAAC,cAAc;AAC/C,cAAU,QAAQ,CAAC,MAAM;AACvB,QAAE,SAAS;AAAA,IACb,CAAC;AAAA,EACH;AACF;;;AHlGA,IAAM,oBAAoB,CACxB,gBACoC;AACpC,QAAM,OAAO,kCAAK,uBAAyB;AAE3C,QAAM,CAAC,WAAW,YAAY,QAAI,wBAEhC,CAAC,CAAC;AAEJ,2BAAiB,MAAM;AACrB,YAAQ,KAAK,2FAA2F;AAAA,EAC1G,GAAG,CAAC,WAAW,CAAC;AAEhB,+BAAU,MAAM;AACd,UAAM,QAAQ,YAAY;AAExB,gBAAU,KAAK,YAAiD;AAGhE,UAAI,UAAU,WAAW,GAAG;AAC1B,YAAI;AACF,cAAI,KAAK,YAAY;AACnB,iCAAqB,KAAK,UAAU;AACpC,mCAAuB,KAAK,UAAU;AAAA,UACxC;AACA,gBAAM,IAAI,4BAA4B,IAAI;AAAA,QAC5C,SAAS,GAAG;AACV,kBAAQ,MAAM,CAAC;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAEA,UAAM;AAEN,WAAO,MAAM;AACX,mBAAa,UAAU,OAAO,CAAC,MAAM,MAAO,YAAkD,CAAC;AAC/F,UAAI,CAAC,UAAU,QAAQ;AAErB,YAAI,8BAA8B;AAAA,MACpC;AAAA,IACF;AAAA,EAEF,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA,IAAI;AAAA,EACN;AACF;AAEA,IAAO,eAAQ;", | ||
| "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport { DEFAULT_HOOK_OPTIONS } from './constants';\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport { CombineObjects } from './typePrimitives';\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n FigmaSelectionHookType\n} from './types';\n\nexport { FigmaSelectionHookOptions, FigmaSelectionHookNode, FigmaSelectionHookType } from './types';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * Only one config will take presence and it will be the config of the first hook that is mounted\n */\nconst useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(\n hookOptions?: Options\n): FigmaSelectionHookType<Options> => {\n const opts = { ...DEFAULT_HOOK_OPTIONS, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<\n readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[]\n >([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [\n selection as readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[],\n api._setSelection\n ];\n};\n\nexport default useFigmaSelection;\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\nlet currentPage: PageNode | undefined;\nlet registeredForChanges = false;\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: NodeChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1);\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst nodeChangeHandler = (e: NodeChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst pageChangeHandler = () => {\n if (registeredForChanges) {\n currentPage?.off('nodechange', nodeChangeHandler);\n currentPage = figma.currentPage;\n currentPage.on('nodechange', nodeChangeHandler);\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n if (!registeredForChanges) {\n figma.on('selectionchange', selectionChangeHandler);\n\n currentPage = figma.currentPage;\n currentPage.on('nodechange', nodeChangeHandler);\n\n registeredForChanges = true;\n }\n\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n currentPage?.off('nodechange', nodeChangeHandler);\n\n registeredForChanges = false;\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n\n// In plugin logic, set a listener for current page change\nif (typeof figma !== 'undefined') {\n figma.on('currentpagechange', () => {\n pageChangeHandler();\n });\n\n figma.on('close', () => {\n figma.off('currentpagechange', pageChangeHandler);\n figma.off('selectionchange', selectionChangeHandler);\n currentPage?.off('nodechange', nodeChangeHandler);\n });\n}\n", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id,\n type: node.type\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n for (const node of nodes) {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n }\n } else {\n for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"], | ||
| "mappings": "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", | ||
| "names": ["import_react", "options"] | ||
| } |
+29
-7
@@ -203,2 +203,4 @@ var __defProp = Object.defineProperty; | ||
| // src/index.ts | ||
| var currentPage; | ||
| var registeredForChanges = false; | ||
| var uiApiMethods = { | ||
@@ -220,5 +222,3 @@ _onSelectionChange(selection) { | ||
| var changesApplyToSelectedNodesOrDescendants = (e, nodes) => { | ||
| const changesApplyToNodes = e.documentChanges.some( | ||
| (change) => nodes.findIndex((node) => node.id === change.id) !== -1 | ||
| ); | ||
| const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1); | ||
| if (changesApplyToNodes) { | ||
@@ -238,3 +238,3 @@ return true; | ||
| }; | ||
| var documentChangeHandler = (e) => { | ||
| var nodeChangeHandler = (e) => { | ||
| if (figma.currentPage.selection.length > 0) { | ||
@@ -247,2 +247,9 @@ const selection = figma.currentPage.selection; | ||
| }; | ||
| var pageChangeHandler = () => { | ||
| if (registeredForChanges) { | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on("nodechange", nodeChangeHandler); | ||
| } | ||
| }; | ||
| var apiMethods = { | ||
@@ -256,4 +263,8 @@ _registerForSelectionChange(opts) { | ||
| } | ||
| figma.on("selectionchange", selectionChangeHandler); | ||
| figma.on("documentchange", documentChangeHandler); | ||
| if (!registeredForChanges) { | ||
| figma.on("selectionchange", selectionChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on("nodechange", nodeChangeHandler); | ||
| registeredForChanges = true; | ||
| } | ||
| selectionChangeHandler(); | ||
@@ -263,3 +274,4 @@ }, | ||
| figma.off("selectionchange", selectionChangeHandler); | ||
| figma.off("documentchange", documentChangeHandler); | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| registeredForChanges = false; | ||
| }, | ||
@@ -286,2 +298,12 @@ _setSelection(newSelection) { | ||
| } | ||
| if (typeof figma !== "undefined") { | ||
| figma.on("currentpagechange", () => { | ||
| pageChangeHandler(); | ||
| }); | ||
| figma.on("close", () => { | ||
| figma.off("currentpagechange", pageChangeHandler); | ||
| figma.off("selectionchange", selectionChangeHandler); | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| }); | ||
| } | ||
@@ -288,0 +310,0 @@ // src/hook.ts |
+2
-2
| { | ||
| "version": 3, | ||
| "sources": ["../src/hook.ts", "../src/constants.ts", "../src/useMountedEffect.ts", "../src/index.ts", "../src/typeUtils.ts", "../src/utils.ts"], | ||
| "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport { DEFAULT_HOOK_OPTIONS } from './constants';\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport { CombineObjects } from './typePrimitives';\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n FigmaSelectionHookType\n} from './types';\n\nexport { FigmaSelectionHookOptions, FigmaSelectionHookNode, FigmaSelectionHookType } from './types';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * Only one config will take presence and it will be the config of the first hook that is mounted\n */\nconst useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(\n hookOptions?: Options\n): FigmaSelectionHookType<Options> => {\n const opts = { ...DEFAULT_HOOK_OPTIONS, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<\n readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[]\n >([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [\n selection as readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[],\n api._setSelection\n ];\n};\n\nexport default useFigmaSelection;\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id,\n type: node.type\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n for (const node of nodes) {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n }\n } else {\n for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"], | ||
| "mappings": "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", | ||
| "sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport { DEFAULT_HOOK_OPTIONS } from './constants';\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport { CombineObjects } from './typePrimitives';\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n FigmaSelectionHookType\n} from './types';\n\nexport { FigmaSelectionHookOptions, FigmaSelectionHookNode, FigmaSelectionHookType } from './types';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * Only one config will take presence and it will be the config of the first hook that is mounted\n */\nconst useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(\n hookOptions?: Options\n): FigmaSelectionHookType<Options> => {\n const opts = { ...DEFAULT_HOOK_OPTIONS, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<\n readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[]\n >([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [\n selection as readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[],\n api._setSelection\n ];\n};\n\nexport default useFigmaSelection;\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\nlet currentPage: PageNode | undefined;\nlet registeredForChanges = false;\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: NodeChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1);\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst nodeChangeHandler = (e: NodeChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst pageChangeHandler = () => {\n if (registeredForChanges) {\n currentPage?.off('nodechange', nodeChangeHandler);\n currentPage = figma.currentPage;\n currentPage.on('nodechange', nodeChangeHandler);\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n if (!registeredForChanges) {\n figma.on('selectionchange', selectionChangeHandler);\n\n currentPage = figma.currentPage;\n currentPage.on('nodechange', nodeChangeHandler);\n\n registeredForChanges = true;\n }\n\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n currentPage?.off('nodechange', nodeChangeHandler);\n\n registeredForChanges = false;\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n\n// In plugin logic, set a listener for current page change\nif (typeof figma !== 'undefined') {\n figma.on('currentpagechange', () => {\n pageChangeHandler();\n });\n\n figma.on('close', () => {\n figma.off('currentpagechange', pageChangeHandler);\n figma.off('selectionchange', selectionChangeHandler);\n currentPage?.off('nodechange', nodeChangeHandler);\n });\n}\n", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id,\n type: node.type\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n for (const node of nodes) {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n }\n } else {\n for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"], | ||
| "mappings": "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", | ||
| "names": ["useEffect", "options", "useEffect"] | ||
| } |
+29
-7
@@ -205,2 +205,4 @@ "use strict"; | ||
| // src/index.ts | ||
| var currentPage; | ||
| var registeredForChanges = false; | ||
| var uiApiMethods = { | ||
@@ -222,5 +224,3 @@ _onSelectionChange(selection) { | ||
| var changesApplyToSelectedNodesOrDescendants = (e, nodes) => { | ||
| const changesApplyToNodes = e.documentChanges.some( | ||
| (change) => nodes.findIndex((node) => node.id === change.id) !== -1 | ||
| ); | ||
| const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1); | ||
| if (changesApplyToNodes) { | ||
@@ -240,3 +240,3 @@ return true; | ||
| }; | ||
| var documentChangeHandler = (e) => { | ||
| var nodeChangeHandler = (e) => { | ||
| if (figma.currentPage.selection.length > 0) { | ||
@@ -249,2 +249,9 @@ const selection = figma.currentPage.selection; | ||
| }; | ||
| var pageChangeHandler = () => { | ||
| if (registeredForChanges) { | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on("nodechange", nodeChangeHandler); | ||
| } | ||
| }; | ||
| var apiMethods = { | ||
@@ -258,4 +265,8 @@ _registerForSelectionChange(opts) { | ||
| } | ||
| figma.on("selectionchange", selectionChangeHandler); | ||
| figma.on("documentchange", documentChangeHandler); | ||
| if (!registeredForChanges) { | ||
| figma.on("selectionchange", selectionChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on("nodechange", nodeChangeHandler); | ||
| registeredForChanges = true; | ||
| } | ||
| selectionChangeHandler(); | ||
@@ -265,3 +276,4 @@ }, | ||
| figma.off("selectionchange", selectionChangeHandler); | ||
| figma.off("documentchange", documentChangeHandler); | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| registeredForChanges = false; | ||
| }, | ||
@@ -288,2 +300,12 @@ _setSelection(newSelection) { | ||
| } | ||
| if (typeof figma !== "undefined") { | ||
| figma.on("currentpagechange", () => { | ||
| pageChangeHandler(); | ||
| }); | ||
| figma.on("close", () => { | ||
| figma.off("currentpagechange", pageChangeHandler); | ||
| figma.off("selectionchange", selectionChangeHandler); | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| }); | ||
| } | ||
| //# sourceMappingURL=index.cjs.map |
| { | ||
| "version": 3, | ||
| "sources": ["../src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts"], | ||
| "sourcesContent": ["import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id,\n type: node.type\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n for (const node of nodes) {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n }\n } else {\n for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"], | ||
| "mappings": "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", | ||
| "sourcesContent": ["import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\nlet currentPage: PageNode | undefined;\nlet registeredForChanges = false;\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: NodeChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1);\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst nodeChangeHandler = (e: NodeChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst pageChangeHandler = () => {\n if (registeredForChanges) {\n currentPage?.off('nodechange', nodeChangeHandler);\n currentPage = figma.currentPage;\n currentPage.on('nodechange', nodeChangeHandler);\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n if (!registeredForChanges) {\n figma.on('selectionchange', selectionChangeHandler);\n\n currentPage = figma.currentPage;\n currentPage.on('nodechange', nodeChangeHandler);\n\n registeredForChanges = true;\n }\n\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n currentPage?.off('nodechange', nodeChangeHandler);\n\n registeredForChanges = false;\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n\n// In plugin logic, set a listener for current page change\nif (typeof figma !== 'undefined') {\n figma.on('currentpagechange', () => {\n pageChangeHandler();\n });\n\n figma.on('close', () => {\n figma.off('currentpagechange', pageChangeHandler);\n figma.off('selectionchange', selectionChangeHandler);\n currentPage?.off('nodechange', nodeChangeHandler);\n });\n}\n", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id,\n type: node.type\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n for (const node of nodes) {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n }\n } else {\n for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"], | ||
| "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAA6C;;;ACEtC,IAAM,UAAU,MAAM;AAEtB,IAAM,mBAAmB,OAAO;AAEhC,IAAM,sBAAsB,CAAsB,SAAuC;AAC9F,SAAO,cAAc;AACvB;;;ACHO,IAAM,cAAc;;;ACW3B,IAAM,kCAAkC,CAAyB,KAAiB,SAAwB;AAhB1G;AAiBE;AAAA;AAAA,IAEE,EAAE,QAAQ,oCAAkC,UAAK,WAAL,mBAAa,UAAS;AAAA,IAElE,QAAQ,uBACR,QAAQ;AAAA;AAEZ;AAEA,IAAM,wBAAwB,CAC5B,MACA,iBAC6B;AAE7B,QAAM,cAAc,OAAO,0BAAgC,OAAO,eAAe,IAAI,CAAC;AAMtF,QAAM,aAAa,iBAAiB,WAAW;AAC/C,MAAI,UAAU,WAAW,OAAO,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ,UAAU;AAEnF,MAAI,cAAc;AAChB,cAAU,QAAQ,OAAO,CAAC,QAAQ,aAAa,SAAS,GAA2B,CAAC;AAAA,EACtF;AAEA,YAAU,QAAQ,OAAO,CAAC,QAAQ,gCAAgC,KAAK,IAAI,CAAC;AAE5E,QAAM,uBAAuB;AAAA,IAC3B,IAAI,KAAK;AAAA,IACT,MAAM,KAAK;AAAA,EACb;AAEA,aAAW,UAAU,SAAS;AAC5B,yBAAqB,MAAM,IAAI,KAAK,MAAM;AAAA,EAC5C;AAEA,SAAO;AACT;AAEA,IAAM,4BAA4B,CAAC,aAAiC;AAElE,QAAM,cAAc,OAAO,0BAAoC,OAAO,eAAe,QAAQ,CAAC;AAM9F,QAAM,aAAa,iBAAiB,WAAW;AAC/C,QAAM,UAAU,WAAW;AAAA,IACzB,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ;AAAA,EAC3C;AAEA,UAAQ,KAAK,IAAI;AAEjB,QAAM,uBAAuB,CAAC;AAE9B,aAAW,UAAU,SAAS;AAE5B,yBAAqB,MAAM,IAAI,SAAS,MAAM;AAAA,EAChD;AAEA,SAAO;AACT;AAEA,IAAM,wBAAwB,CAI5B,gBACAA,aAC2B;AAC3B,QAAM,EAAE,iBAAiB,IAAIA;AAC7B,QAAM,SAA0C,CAAC;AAEjD,aAAW,oBAAoB,iBAAiB,cAAc,GAAG;AAC/D,QACE,qBAAqB,SACpB,QAAQ,gBAAgB,KAAK,iBAAiB,SAAS,gBAAqD,GAC7G;AACA,YAAM,gBAAgB,eAAe,gBAAgB;AAGrD,UAAI,MAAM,QAAQ,aAAa,GAAG;AAChC,cAAM,UAA2B;AACjC,cAAM,oBAAgC,CAAC;AAEvC,mBAAW,iBAAiB,SAAS;AACnC,gBAAM,WAAW,MAAM,UAAU,gBAAgB,cAAc,EAAE;AACjE,cAAI,UAAU;AACZ,8BAAkB,KAAK,0BAA0B,QAAQ,CAAC;AAAA,UAC5D;AAAA,QACF;AAEA,0BAAkB,SAAS,MACxB,OAAO,gBAAmD,IAAI;AAAA,MACnE,WAAW,qBAAqB,uBAAuB;AACrD,cAAM,cAAc;AACpB,cAAM,iBAA2C,CAAC;AAElD,mBAAW,wBAAwB,OAAO,KAAK,WAAW,GAAG;AAC3D,gBAAM,WAAW,MAAM,UAAU,gBAAgB,YAAY,oBAAoB,EAAE,EAAE;AACrF,uBAAa,eAAe,oBAAoB,IAAI,0BAA0B,QAAQ;AAAA,QACxF;AAEA,eAAO,KAAK,cAAc,EAAE,SAAS,MAAM,OAAO,gBAAyC,IAAI;AAAA,MACjG,OAAO;AACL,cAAM,QAAQ;AACd,cAAM,mBAAmB,MAAM,UAAU,gBAAgB,MAAM,EAAE;AAEjE,6BACG,OAAO,gBAAmD,IAAI,0BAA0B,gBAAgB;AAAA,MAC7G;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,oBAAoB,CAAC,MAAiB,mBAAqD;AAC/F,QAAM,aAAqC,CAAC;AAE5C,aAAW,OAAO,gBAAgB;AAChC,UAAM,QAAQ,KAAK,cAAc,GAAG;AACpC,QAAI,UAAU,QAAW;AACvB,iBAAW,GAAG,IAAI;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,0BAA0B,CAC9B,MACA,yBACwB;AACxB,QAAM,mBAAqE,CAAC;AAE5E,aAAW,aAAa,iBAAiB,oBAAoB,GAAG;AAC9D,qBAAiB,SAAS,IAAI,CAAC;AAC/B,eAAW,OAAO,qBAAqB,SAAS,GAAG;AACjD,MAAC,iBAAiB,SAAS,EAA6B,GAAG,IAAI,KAAK,oBAAoB,WAAqB,GAAG;AAAA,IAClH;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,oCAAoC,CACxC,MACAA,UACA,qBACoC;AACpC,QAAM,EAAE,mBAAmB,kBAAkB,6BAA6B,gBAAgB,qBAAqB,IAC7GA;AAEF,QAAM,eAAe;AAAA,IACnB;AAAA,IACA,sBAAsB,QAAQ,SAAY;AAAA,EAC5C;AAEA,aAAW,OAAO,iBAAiB,YAAY,GAAG;AAChD,QAAI,aAAa,GAAG,MAAM,MAAM,OAAO;AACrC,mBAAa,GAAG,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,MACE,KAAK,mBAAmB,WACvB,qBAAqB,SAAU,QAAQ,gBAAgB,KAAK,iBAAiB,SAAS,IACvF;AACA,iBAAa,yBAAyB,sBAAsB,KAAK,gBAAgBA,QAAO;AAAA,EAC1F;AAEA,MAAI,6BAA6B;AAC/B,iBAAa,mBAAmB;AAAA,EAClC;AAEA,MAAI,eAAe,SAAS,GAAG;AAC7B,iBAAa,aAAa,kBAAkB,MAAM,cAAc;AAAA,EAClE;AAEA,MAAI,OAAO,KAAK,oBAAoB,EAAE,SAAS,GAAG;AAChD,iBAAa,mBAAmB,wBAAwB,MAAM,oBAAoB;AAAA,EACpF;AAEA,SAAO;AACT;AAEO,IAAM,wBAAwB,CACnC,OACAA,UACA,mBAA4B,SACU;AACtC,QAAM,SAA4C,CAAC;AACnD,QAAM,EAAE,WAAW,gBAAgB,IAAIA;AAEvC,MAAI,cAAc,QAAW;AAC3B,eAAW,QAAQ,OAAO;AACxB,UAAI,UAAU,SAAS,KAAK,IAAI,GAAG;AACjC,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG,WAAW,oBAAoB,IAAI,KAAK,iBAAiB;AACvD,eAAO,KAAK,GAAG,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF;AAAA,EACF,OAAO;AACL,eAAW,QAAQ,OAAO;AACxB,UAAI,oBAAoB,IAAI,KAAK,iBAAiB;AAChD,cAAM,UAAU,iCACX,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,IADtE;AAAA,UAEd,UAAU,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO;AAAA,QAC1F;AACA,eAAO,KAAK,OAAO;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;AH5NA,IAAI;AACJ,IAAI,uBAAuB;AAQ3B,IAAM,eAAe;AAAA,EACnB,mBAAmB,WAA+D;AAChF,QAAI,OAAO,OAAO,6BAA6B,aAAa;AAC1D,aAAO,yBAAyB,SAAS;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,IAAI,YAAQ,qCAAY,YAAY;AAEpC,IAAM,yBAAyB,CAAC,eAA2B;AAChE,cAAQ,qCAAY,cAAc,UAAU;AAC9C;AAEA,IAAM,yBAAyB,MAAM;AACnC,QAAM,oBAAoB,sBAAsB,MAAM,YAAY,WAAW,OAAO;AACpF,QAAM,mBAAmB,iBAAiB;AAC5C;AAEA,IAAM,2CAA2C,CAAC,GAAoB,UAAyC;AAC7G,QAAM,sBAAsB,EAAE,YAAY,KAAK,CAAC,WAAW,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,OAAO,EAAE,MAAM,EAAE;AAElH,MAAI,qBAAqB;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,cAA2B,CAAC;AAElC,aAAW,QAAQ,OAAO;AACxB,QAAI,oBAAoB,IAAI,GAAG;AAC7B,kBAAY,KAAK,GAAG,KAAK,QAAQ;AAAA,IACnC;AAAA,EACF;AAEA,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,yCAAyC,GAAG,WAAW;AAChE;AAEA,IAAM,oBAAoB,CAAC,MAAuB;AAChD,MAAI,MAAM,YAAY,UAAU,SAAS,GAAG;AAC1C,UAAM,YAAY,MAAM,YAAY;AAEpC,QAAI,yCAAyC,GAAG,SAAS,GAAG;AAC1D,6BAAuB;AAAA,IACzB;AAAA,EACF;AACF;AAEA,IAAM,oBAAoB,MAAM;AAC9B,MAAI,sBAAsB;AACxB,+CAAa,IAAI,cAAc;AAC/B,kBAAc,MAAM;AACpB,gBAAY,GAAG,cAAc,iBAAiB;AAAA,EAChD;AACF;AAEA,IAAM,aAAa;AAAA,EACjB,4BAA4B,MAAmD;AAC7E,cAAU;AAEV,UAAM,aAAa,KAAK;AACxB,QAAI,YAAY;AACd,6BAAuB,UAAU;AACjC,2BAAqB,UAAU;AAAA,IACjC;AAEA,QAAI,CAAC,sBAAsB;AACzB,YAAM,GAAG,mBAAmB,sBAAsB;AAElD,oBAAc,MAAM;AACpB,kBAAY,GAAG,cAAc,iBAAiB;AAE9C,6BAAuB;AAAA,IACzB;AAEA,2BAAuB;AAAA,EACzB;AAAA,EACA,gCAAgC;AAC9B,UAAM,IAAI,mBAAmB,sBAAsB;AACnD,+CAAa,IAAI,cAAc;AAE/B,2BAAuB;AAAA,EACzB;AAAA,EACA,cAA6C,cAAiB;AAC5D,UAAM,YAAY,YAAY;AAAA,EAChC;AACF;AAEO,IAAI,UAAM,yCAAgB,UAAU;AAEpC,IAAM,uBAAuB,CAAC,eAA2B;AAC9D,YAAM,yCAAgB,YAAY,UAAU;AAC9C;AAEA,IAAI;AAEG,IAAI,YAAsC,CAAC;AAE3C,IAAM,eAAe,CAAC,iBAA2C;AACtE,cAAY;AACd;AAGA,IAAI,OAAO,WAAW,aAAa;AACjC,SAAO,2BAA2B,CAAC,cAAc;AAC/C,cAAU,QAAQ,CAAC,MAAM;AACvB,QAAE,SAAS;AAAA,IACb,CAAC;AAAA,EACH;AACF;AAGA,IAAI,OAAO,UAAU,aAAa;AAChC,QAAM,GAAG,qBAAqB,MAAM;AAClC,sBAAkB;AAAA,EACpB,CAAC;AAED,QAAM,GAAG,SAAS,MAAM;AACtB,UAAM,IAAI,qBAAqB,iBAAiB;AAChD,UAAM,IAAI,mBAAmB,sBAAsB;AACnD,+CAAa,IAAI,cAAc;AAAA,EACjC,CAAC;AACH;", | ||
| "names": ["options"] | ||
| } |
+29
-7
@@ -178,2 +178,4 @@ var __defProp = Object.defineProperty; | ||
| // src/index.ts | ||
| var currentPage; | ||
| var registeredForChanges = false; | ||
| var uiApiMethods = { | ||
@@ -195,5 +197,3 @@ _onSelectionChange(selection) { | ||
| var changesApplyToSelectedNodesOrDescendants = (e, nodes) => { | ||
| const changesApplyToNodes = e.documentChanges.some( | ||
| (change) => nodes.findIndex((node) => node.id === change.id) !== -1 | ||
| ); | ||
| const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1); | ||
| if (changesApplyToNodes) { | ||
@@ -213,3 +213,3 @@ return true; | ||
| }; | ||
| var documentChangeHandler = (e) => { | ||
| var nodeChangeHandler = (e) => { | ||
| if (figma.currentPage.selection.length > 0) { | ||
@@ -222,2 +222,9 @@ const selection = figma.currentPage.selection; | ||
| }; | ||
| var pageChangeHandler = () => { | ||
| if (registeredForChanges) { | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on("nodechange", nodeChangeHandler); | ||
| } | ||
| }; | ||
| var apiMethods = { | ||
@@ -231,4 +238,8 @@ _registerForSelectionChange(opts) { | ||
| } | ||
| figma.on("selectionchange", selectionChangeHandler); | ||
| figma.on("documentchange", documentChangeHandler); | ||
| if (!registeredForChanges) { | ||
| figma.on("selectionchange", selectionChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on("nodechange", nodeChangeHandler); | ||
| registeredForChanges = true; | ||
| } | ||
| selectionChangeHandler(); | ||
@@ -238,3 +249,4 @@ }, | ||
| figma.off("selectionchange", selectionChangeHandler); | ||
| figma.off("documentchange", documentChangeHandler); | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| registeredForChanges = false; | ||
| }, | ||
@@ -261,2 +273,12 @@ _setSelection(newSelection) { | ||
| } | ||
| if (typeof figma !== "undefined") { | ||
| figma.on("currentpagechange", () => { | ||
| pageChangeHandler(); | ||
| }); | ||
| figma.on("close", () => { | ||
| figma.off("currentpagechange", pageChangeHandler); | ||
| figma.off("selectionchange", selectionChangeHandler); | ||
| currentPage == null ? void 0 : currentPage.off("nodechange", nodeChangeHandler); | ||
| }); | ||
| } | ||
| export { | ||
@@ -263,0 +285,0 @@ FIGMA_MIXED, |
| { | ||
| "version": 3, | ||
| "sources": ["../src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts"], | ||
| "sourcesContent": ["import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id,\n type: node.type\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n for (const node of nodes) {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n }\n } else {\n for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"], | ||
| "mappings": "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", | ||
| "sourcesContent": ["import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\nlet currentPage: PageNode | undefined;\nlet registeredForChanges = false;\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: NodeChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1);\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst nodeChangeHandler = (e: NodeChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst pageChangeHandler = () => {\n if (registeredForChanges) {\n currentPage?.off('nodechange', nodeChangeHandler);\n currentPage = figma.currentPage;\n currentPage.on('nodechange', nodeChangeHandler);\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n if (!registeredForChanges) {\n figma.on('selectionchange', selectionChangeHandler);\n\n currentPage = figma.currentPage;\n currentPage.on('nodechange', nodeChangeHandler);\n\n registeredForChanges = true;\n }\n\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n currentPage?.off('nodechange', nodeChangeHandler);\n\n registeredForChanges = false;\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n\n// In plugin logic, set a listener for current page change\nif (typeof figma !== 'undefined') {\n figma.on('currentpagechange', () => {\n pageChangeHandler();\n });\n\n figma.on('close', () => {\n figma.off('currentpagechange', pageChangeHandler);\n figma.off('selectionchange', selectionChangeHandler);\n currentPage?.off('nodechange', nodeChangeHandler);\n });\n}\n", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id,\n type: node.type\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n for (const node of nodes) {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n }\n } else {\n for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"], | ||
| "mappings": "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", | ||
| "names": ["options"] | ||
| } |
+11
-7
@@ -36,3 +36,3 @@ /// <reference types="@figma/plugin-typings" /> | ||
| * | ||
| * This allows us to infer the type of the returned nodes correctly. | ||
| * This allows the hook to infer the type of the returned nodes correctly. | ||
| * | ||
@@ -122,4 +122,5 @@ * Example: | ||
| export type ResolverOptions = Required<Omit<FigmaSelectionHookOptions, 'nodeTypes' | 'apiOptions'>> & Pick<FigmaSelectionHookOptions, 'nodeTypes'>; | ||
| type SerializedNodeProperty<Prop, Node extends SceneNode, Options extends ResolverOptions> = Prop extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : Prop extends readonly SceneNode[] ? Options['resolveChildren'] extends true ? readonly SerializedNode<Node, Options>[] : readonly BareNode[] : Prop; | ||
| type SerializedNodeProperty<Prop, Options extends ResolverOptions> = Prop extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : Prop extends readonly SceneNode[] ? Options['resolveChildren'] extends true ? readonly SerializedNode<SceneNodeFromTypes<Options['nodeTypes']>, Options>[] : readonly BareNode[] : Prop extends SceneNode ? BareNode : Prop; | ||
| type ApplicableNodeKeys<Node extends SceneNode, Properties extends OptSceneNodeProperties> = Properties extends readonly SceneNodePropertyKey[] ? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Properties>> : NonFunctionPropertyKeys<Node>; | ||
| type ResolvePropertiesDefined<Node extends SceneNode, Options extends ResolverOptions> = Options['resolveProperties'] extends 'all' ? true : Options['resolveProperties'] extends SceneNodePropertyKey[] ? ApplicableNodeKeys<Node, Options['resolveProperties']> extends never ? false : true : false; | ||
| /** | ||
@@ -131,5 +132,5 @@ * @internal | ||
| id: Node['id']; | ||
| } & { | ||
| [Key in Options['resolveProperties'] extends SceneNodePropertyKey[] ? ApplicableNodeKeys<Node, Options['resolveProperties']> : keyof Node]: SerializedNodeProperty<Node[Key], Node, Options>; | ||
| } : never; | ||
| } & (ResolvePropertiesDefined<Node, Options> extends true ? { | ||
| [Key in Options['resolveProperties'] extends SceneNodePropertyKey[] ? ApplicableNodeKeys<Node, Options['resolveProperties']> : keyof Node]: SerializedNodeProperty<Node[Key], Options>; | ||
| } : unknown) : never; | ||
| /** | ||
@@ -139,3 +140,6 @@ * @internal | ||
| export type ResolvedNode<Node extends SceneNode, Keys extends readonly SceneNodePropertyKey[] | undefined = undefined> = Pick<Node, Keys extends readonly SceneNodePropertyKey[] ? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Keys>> | 'id' | 'type' : ApplicableNonFunctionPropertyKeys<Node, SceneNodePropertyKey>>; | ||
| type SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode ? SerializedNode<Node, Options> : never; | ||
| /** | ||
| * @internal | ||
| */ | ||
| export type SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode ? SerializedNode<Node, Options> : never; | ||
| type AncestorsVisibleMixin<Options extends ResolverOptions> = Options['addAncestorsVisibleProperty'] extends true ? { | ||
@@ -193,3 +197,3 @@ ancestorsVisible: boolean; | ||
| /** | ||
| * Utility type to get the inferred type of the hook using the options object | ||
| * Utility type to get the inferred type of a node from the hook using the options object | ||
| */ | ||
@@ -196,0 +200,0 @@ export type FigmaSelectionHookNode<Options extends FigmaSelectionHookOptions = Record<string, never>> = SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>; |
+13
-13
| { | ||
| "name": "figma-plugin-react-hooks", | ||
| "version": "3.1.1", | ||
| "version": "3.2.0", | ||
| "description": "", | ||
@@ -55,20 +55,20 @@ "exports": { | ||
| "@figma/plugin-typings": "*", | ||
| "@types/react": "^18.2.48", | ||
| "@typescript-eslint/eslint-plugin": "^6.19.1", | ||
| "@typescript-eslint/parser": "^6.19.1", | ||
| "@types/react": "^18.2.61", | ||
| "@typescript-eslint/eslint-plugin": "^7.1.0", | ||
| "@typescript-eslint/parser": "^7.1.0", | ||
| "concurrently": "^8.2.2", | ||
| "esbuild": "^0.19.11", | ||
| "eslint": "^8.49.0", | ||
| "eslint-config-prettier": "^9.0.0", | ||
| "eslint-import-resolver-typescript": "^3.6.0", | ||
| "eslint-plugin-import": "^2.28.1", | ||
| "esbuild": "^0.20.1", | ||
| "eslint": "^8.57.0", | ||
| "eslint-config-prettier": "^9.1.0", | ||
| "eslint-import-resolver-typescript": "^3.6.1", | ||
| "eslint-plugin-import": "^2.29.1", | ||
| "eslint-plugin-prettier": "^5.1.3", | ||
| "eslint-plugin-react": "^7.33.2", | ||
| "eslint-plugin-react-hooks": "^4.6.0", | ||
| "prettier": "^3.2.4", | ||
| "tsd": "^0.30.4", | ||
| "typedoc": "^0.25.7", | ||
| "prettier": "^3.2.5", | ||
| "tsd": "^0.30.7", | ||
| "typedoc": "^0.25.9", | ||
| "typedoc-plugin-markdown": "^3.17.1", | ||
| "typescript": "^5.2.2" | ||
| "typescript": "^5.3.3" | ||
| } | ||
| } |
+2
-2
@@ -145,3 +145,3 @@ # figma-plugin-react-hooks | ||
| This allows us to infer the type of the returned nodes correctly. | ||
| This allows the hook to infer the type of the returned nodes correctly. | ||
@@ -175,3 +175,3 @@ Example: | ||
| Utility type to get the inferred type of the hook using the options object | ||
| Utility type to get the inferred type of a node from the hook using the options object | ||
@@ -178,0 +178,0 @@ #### Type parameters |
+39
-8
@@ -19,2 +19,5 @@ import { createUIAPI, createPluginAPI } from 'figma-plugin-api'; | ||
| let currentPage: PageNode | undefined; | ||
| let registeredForChanges = false; | ||
| declare global { | ||
@@ -45,6 +48,4 @@ interface Window { | ||
| const changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => { | ||
| const changesApplyToNodes = e.documentChanges.some( | ||
| (change) => nodes.findIndex((node) => node.id === change.id) !== -1 | ||
| ); | ||
| const changesApplyToSelectedNodesOrDescendants = (e: NodeChangeEvent, nodes: readonly SceneNode[]): boolean => { | ||
| const changesApplyToNodes = e.nodeChanges.some((change) => nodes.findIndex((node) => node.id === change.id) !== -1); | ||
@@ -70,3 +71,3 @@ if (changesApplyToNodes) { | ||
| const documentChangeHandler = (e: DocumentChangeEvent) => { | ||
| const nodeChangeHandler = (e: NodeChangeEvent) => { | ||
| if (figma.currentPage.selection.length > 0) { | ||
@@ -81,2 +82,10 @@ const selection = figma.currentPage.selection; | ||
| const pageChangeHandler = () => { | ||
| if (registeredForChanges) { | ||
| currentPage?.off('nodechange', nodeChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on('nodechange', nodeChangeHandler); | ||
| } | ||
| }; | ||
| const apiMethods = { | ||
@@ -92,4 +101,11 @@ _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) { | ||
| figma.on('selectionchange', selectionChangeHandler); | ||
| figma.on('documentchange', documentChangeHandler); | ||
| if (!registeredForChanges) { | ||
| figma.on('selectionchange', selectionChangeHandler); | ||
| currentPage = figma.currentPage; | ||
| currentPage.on('nodechange', nodeChangeHandler); | ||
| registeredForChanges = true; | ||
| } | ||
| selectionChangeHandler(); | ||
@@ -99,3 +115,5 @@ }, | ||
| figma.off('selectionchange', selectionChangeHandler); | ||
| figma.off('documentchange', documentChangeHandler); | ||
| currentPage?.off('nodechange', nodeChangeHandler); | ||
| registeredForChanges = false; | ||
| }, | ||
@@ -129,1 +147,14 @@ _setSelection<N extends readonly BareNode[]>(newSelection: N) { | ||
| } | ||
| // In plugin logic, set a listener for current page change | ||
| if (typeof figma !== 'undefined') { | ||
| figma.on('currentpagechange', () => { | ||
| pageChangeHandler(); | ||
| }); | ||
| figma.on('close', () => { | ||
| figma.off('currentpagechange', pageChangeHandler); | ||
| figma.off('selectionchange', selectionChangeHandler); | ||
| currentPage?.off('nodechange', nodeChangeHandler); | ||
| }); | ||
| } |
+41
-15
@@ -61,3 +61,3 @@ import { DEFAULT_HOOK_OPTIONS, FIGMA_MIXED } from './constants'; | ||
| * | ||
| * This allows us to infer the type of the returned nodes correctly. | ||
| * This allows the hook to infer the type of the returned nodes correctly. | ||
| * | ||
@@ -157,13 +157,12 @@ * Example: | ||
| type SerializedNodeProperty< | ||
| Prop, | ||
| Node extends SceneNode, | ||
| Options extends ResolverOptions | ||
| > = Prop extends PluginAPI['mixed'] | ||
| // TODO: Add mainComponent resolving for component instances | ||
| type SerializedNodeProperty<Prop, Options extends ResolverOptions> = Prop extends PluginAPI['mixed'] | ||
| ? typeof FIGMA_MIXED | ||
| : Prop extends readonly SceneNode[] | ||
| ? Options['resolveChildren'] extends true | ||
| ? readonly SerializedNode<Node, Options>[] | ||
| ? readonly SerializedNode<SceneNodeFromTypes<Options['nodeTypes']>, Options>[] | ||
| : readonly BareNode[] | ||
| : Prop; | ||
| : Prop extends SceneNode | ||
| ? BareNode | ||
| : Prop; | ||
@@ -177,5 +176,27 @@ type ApplicableNodeKeys< | ||
| type ResolvePropertiesDefined< | ||
| Node extends SceneNode, | ||
| Options extends ResolverOptions | ||
| > = Options['resolveProperties'] extends 'all' | ||
| ? true | ||
| : Options['resolveProperties'] extends SceneNodePropertyKey[] | ||
| ? ApplicableNodeKeys<Node, Options['resolveProperties']> extends never | ||
| ? false | ||
| : true | ||
| : false; | ||
| /** | ||
| * @internal | ||
| */ | ||
| // TODO: Ensure the children prop gets picked up even if it's not included in resolveProperties | ||
| /* | ||
| & Options['resolveChildren'] extends true | ||
| ? | ||
| Node extends ChildrenMixin | ||
| ? { | ||
| children: SerializedNodeProperty<Node['children'], Options>; | ||
| } | ||
| : unknown | ||
| : unknown | ||
| */ | ||
| export type SerializedNode<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode | ||
@@ -185,7 +206,9 @@ ? { | ||
| id: Node['id']; | ||
| } & { | ||
| [Key in Options['resolveProperties'] extends SceneNodePropertyKey[] | ||
| ? ApplicableNodeKeys<Node, Options['resolveProperties']> | ||
| : keyof Node]: SerializedNodeProperty<Node[Key], Node, Options>; | ||
| } | ||
| } & (ResolvePropertiesDefined<Node, Options> extends true | ||
| ? { | ||
| [Key in Options['resolveProperties'] extends SceneNodePropertyKey[] | ||
| ? ApplicableNodeKeys<Node, Options['resolveProperties']> | ||
| : keyof Node]: SerializedNodeProperty<Node[Key], Options>; | ||
| } | ||
| : unknown) | ||
| : never; | ||
@@ -206,3 +229,6 @@ | ||
| type SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode | ||
| /** | ||
| * @internal | ||
| */ | ||
| export type SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode | ||
| ? SerializedNode<Node, Options> | ||
@@ -305,3 +331,3 @@ : never; | ||
| /** | ||
| * Utility type to get the inferred type of the hook using the options object | ||
| * Utility type to get the inferred type of a node from the hook using the options object | ||
| */ | ||
@@ -308,0 +334,0 @@ export type FigmaSelectionHookNode<Options extends FigmaSelectionHookOptions = Record<string, never>> = |
188131
5.78%2317
6.48%