UNPKG

@silexlabs/grapesjs-symbols

Version:
149 lines 5.76 kB
/** * Attributes that can contain references to element IDs. * These should be updated when IDs are made unique. */ const ID_REFERENCE_ATTRIBUTES = [ 'for', // <label for="..."> 'aria-labelledby', // ARIA: labels 'aria-describedby', // ARIA: descriptions 'aria-controls', // ARIA: controls 'aria-owns', // ARIA: ownership 'aria-activedescendant', // ARIA: active descendant 'aria-flowto', // ARIA: flow 'aria-errormessage', // ARIA: error message 'list', // <input list="..."> for datalist 'form', // <input form="..."> for form association 'headers', // <td headers="..."> for table headers ]; /** * Attributes that contain ID references prefixed with # */ const HASH_ID_ATTRIBUTES = [ 'href', // <a href="#id"> 'data-target', // Bootstrap 4: data-target="#id" 'data-bs-target', // Bootstrap 5: data-bs-target="#id" ]; /** * Helper to get all components under a root component in depth-first order. */ function flattenComponents(root) { const components = []; root.onAll((c) => components.push(c)); return components; } /** * Build a map from main symbol component IDs to instance component IDs * Uses component order to correlate since both have the same structure */ function buildIdMap(mainSymbol, instance) { const idMap = new Map(); const mainComponents = flattenComponents(mainSymbol); const instanceComponents = flattenComponents(instance); const n = Math.min(mainComponents.length, instanceComponents.length); for (let i = 0; i < n; i++) { const mainId = mainComponents[i].getAttributes().id; const instanceId = instanceComponents[i].getAttributes().id; if (mainId && instanceId) { idMap.set(mainId, instanceId); } } return idMap; } /** * Collect all ID reference values from the main symbol. * Returns a map of: referenced ID -> list of {component index, attribute name, isHash} */ function collectIdReferences(mainSymbol) { const refs = new Map(); flattenComponents(mainSymbol).forEach((component, index) => { const attrs = component.getAttributes(); // Check direct ID references for (const attr of ID_REFERENCE_ATTRIBUTES) { const value = attrs[attr]; if (typeof value === 'string' && value) { const ids = value.split(/\s+/); ids.forEach(id => { if (!refs.has(id)) refs.set(id, []); refs.get(id).push({ index, attr, isHash: false }); }); } } // Check hash-prefixed ID references for (const attr of HASH_ID_ATTRIBUTES) { const value = attrs[attr]; if (typeof value === 'string' && value.startsWith('#')) { const id = value.substring(1); if (!refs.has(id)) refs.set(id, []); refs.get(id).push({ index, attr, isHash: true }); } } }); return refs; } /** * Update ID references in a symbol instance to match the new unique IDs. * * This function: * 1. Gets the main symbol for this instance * 2. Builds a map of main symbol IDs -> instance IDs * 3. Finds all ID references in the main symbol (for, aria-*, href="#...", etc.) * 4. Updates those references in the instance to use the new IDs * * @param editor - The GrapesJS editor instance * @param instance - The root component of the symbol instance */ export function makeInstanceIdsUnique(editor, instance) { const symbolInfo = editor.Components.getSymbolInfo(instance); if (!(symbolInfo === null || symbolInfo === void 0 ? void 0 : symbolInfo.main)) return; const mainSymbol = symbolInfo.main; const idMap = buildIdMap(mainSymbol, instance); if (idMap.size === 0) return; const refs = collectIdReferences(mainSymbol); const instanceComponents = flattenComponents(instance); refs.forEach((refList, referencedId) => { // Prefer exact match, fallback to prefix match (e.g., for GrapesJS suffixes) let newId; idMap.forEach((instanceId, mainId) => { if (mainId === referencedId || mainId.startsWith(referencedId + '-')) { newId = instanceId; } }); if (!newId) return; refList.forEach(ref => { const component = instanceComponents[ref.index]; if (!component) return; const attrs = component.getAttributes(); const currentValue = attrs[ref.attr]; let newValue; if (ref.isHash) { newValue = `#${newId}`; } else if (typeof currentValue === 'string' && currentValue.includes(' ')) { // Handle space-separated list of IDs newValue = currentValue .split(/\s+/) .map(id => id === referencedId ? newId : id) .join(' '); } else { newValue = newId; } // Temporarily add symbol override for attributes, then set attribute, then restore overrides const overrides = component.getSymbolOverride() || []; const overridesArray = Array.isArray(overrides) ? overrides : []; const hasAttrOverride = overridesArray.includes('attributes'); if (!hasAttrOverride) component.setSymbolOverride([...overridesArray, 'attributes']); component.setAttributes({ [ref.attr]: newValue }); if (!hasAttrOverride) component.setSymbolOverride(overrides); }); }); } //# sourceMappingURL=id-utils.js.map