UNPKG

figma-node-selector-utility

Version:

A utility for selecting Figma nodes with Figma Tags and Attributes plugin.

119 lines (118 loc) 4.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.$F = exports.FigmaNodeSelectorUtility = void 0; class FigmaNodeSelectorUtility { constructor() { this.nodes = []; this.updateNodes(); // Correctly attach the event listener to the `figma` global object. figma.on('selectionchange', () => { this.updateNodes(); }); } updateNodes() { this.nodes = []; if (figma.currentPage.selection.length > 0) { this.collectAllNodes(figma.currentPage.selection[0]); } } collectAllNodes(node) { this.nodes.push(node); if (node.children) { node.children.forEach((child) => { this.collectAllNodes(child); }); } } parseNodeData(node) { return JSON.parse(node.getSharedPluginData("figma.attributes", "attributes") || '{}'); } findByClassName(className, nodes) { return nodes.filter(node => { const data = this.parseNodeData(node); return data.attributes && data.attributes.class && data.attributes.class.split(' ').includes(className); }); } findByTagName(tagName, nodes) { return nodes.filter(node => { const data = this.parseNodeData(node); return data.tag === tagName; }); } findByAttribute(attribute, value = null, nodes) { return nodes.filter(node => { const data = this.parseNodeData(node); // Check if the attribute exists const hasAttribute = data.attributes && data.attributes.hasOwnProperty(attribute); if (value === null) { // If no value is provided, return nodes that have the attribute return hasAttribute; } else { // If a value is provided, return nodes where the attribute's value matches return hasAttribute && data.attributes[attribute] === value; } }); } find(selector) { let currentNodes = [...this.nodes]; // Splitting the selector for group selectors (e.g., div, .class) const groupSelectors = selector.split(',').map(s => s.trim()); let foundNodes = []; groupSelectors.forEach(groupSelector => { let tempNodes = [...currentNodes]; // Start with a copy of all current nodes // Process compound selectors (e.g., div.className[attr=value] or div[attr]) const compoundSelectors = groupSelector.split(' ').filter(Boolean); compoundSelectors.forEach(compoundSelector => { // Handling attribute selectors within a compound selector // Adjusted regex to optionally match attribute selectors without a value const attrMatch = compoundSelector.match(/\[([^=\]]+)(?:=([^\]]+))?\]/); if (attrMatch) { const attrName = attrMatch[1].trim(); // Adjust to handle attribute presence without value const attrValue = attrMatch.length > 2 ? attrMatch[2].trim().replace(/^"|"$/g, '').replace(/^'|'$/g, '') : null; // Removing possible quotes tempNodes = this.findByAttribute(attrName, attrValue, tempNodes); compoundSelector = compoundSelector.replace(attrMatch[0], ''); // Remove attribute selector from compound } // Process the rest (class, id, tagName) tempNodes = this.processBasicSelectors(compoundSelector, tempNodes); }); foundNodes = [...foundNodes, ...tempNodes]; }); return foundNodes; } // Supporting method for processing class, id, and tag name selectors. processBasicSelectors(selector, nodes) { // If the selector is empty after removing attribute selectors, return the nodes as is. if (!selector) return nodes; // Class selector if (selector.startsWith('.')) { const className = selector.substring(1); return this.findByClassName(className, nodes); } // ID selector else if (selector.startsWith('#')) { const idValue = selector.substring(1); return this.findByAttribute('id', idValue, nodes); } // Tag name or other selectors else { return this.findByTagName(selector, nodes); } } first(selector) { return this.find(selector)[0] || null; } last(selector) { const found = this.find(selector); return found[found.length - 1] || null; } } exports.FigmaNodeSelectorUtility = FigmaNodeSelectorUtility; exports.$F = (function () { const selectorInstance = new FigmaNodeSelectorUtility(); return function (selectorString) { return selectorInstance.find(selectorString); }; })();