/** DOM utilities for the layer-dropdown module */ import { isInstrumentedElement, getElementSelectorId } from "../utils.js"; import { MAX_PARENT_DEPTH, MAX_CHILD_DEPTH } from "./consts.js"; /** Apply a style map to an element */ export function applyStyles(element, styles) { for (const key of Object.keys(styles)) { element.style.setProperty(key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`), styles[key]); } } /** Display name for a layer — just the real tag name */ export function getLayerDisplayName(layer) { return layer.tagName; } function toLayerInfo(element, depth) { const info = { element, tagName: element.tagName.toLowerCase(), selectorId: getElementSelectorId(element), }; if (depth !== undefined) info.depth = depth; return info; } /** * Collect instrumented descendants up to `maxDepth` instrumented nesting levels. * Non-instrumented wrappers are walked through without counting toward depth. * Results are in DOM order. * When `startDepth` is provided, assigns `depth` to each item during collection. */ export function getInstrumentedDescendants(parent, maxDepth, startDepth) { const result = []; function walk(el, instrDepth) { if (instrDepth > maxDepth) return; for (let i = 0; i < el.children.length; i++) { const child = el.children[i]; if (isInstrumentedElement(child)) { const info = { element: child, tagName: child.tagName.toLowerCase(), selectorId: getElementSelectorId(child), }; if (startDepth !== undefined) { info.depth = startDepth + instrDepth - 1; } result.push(info); walk(child, instrDepth + 1); } else { walk(child, instrDepth); } } } walk(parent, 1); return result; } /** Collect instrumented ancestors from selected element up to MAX_PARENT_DEPTH (outermost first). */ function collectInstrumentedParents(selectedElement) { const parents = []; let current = selectedElement.parentElement; while (current && current !== document.documentElement && current !== document.body && parents.length < MAX_PARENT_DEPTH) { if (isInstrumentedElement(current)) { parents.push(toLayerInfo(current)); } current = current.parentElement; } parents.reverse(); return parents; } /** Add parents to chain with depth 0, 1, …; returns depth of selected (parents.length). */ function addParentsToChain(chain, parents) { parents.forEach((p, i) => { chain.push({ ...p, depth: i }); }); return parents.length; } /** Add selected element and its descendants at the given depth. */ function addSelfAndDescendantsToChain(chain, selectedElement, selfDepth) { chain.push(toLayerInfo(selectedElement, selfDepth)); const descendants = getInstrumentedDescendants(selectedElement, MAX_CHILD_DEPTH, selfDepth + 1); chain.push(...descendants); } /** Get the innermost instrumented parent's DOM element, or null if none. */ function getImmediateInstrParent(parents) { return parents.at(-1)?.element ?? null; } /** Collect instrumented siblings of the selected element from its parent (DOM order). */ function collectSiblings(parent, selectedElement) { const siblings = getInstrumentedDescendants(parent, 1); if (!siblings.some((s) => s.element === selectedElement)) { siblings.push(toLayerInfo(selectedElement)); } return siblings; } /** Add siblings at selfDepth, expanding children only for the selected element. */ function appendSiblingsWithSelected(chain, siblings, selectedElement, selfDepth) { const selectedSelectorId = getElementSelectorId(selectedElement); const seen = new Set(); for (const sibling of siblings) { if (sibling.element === selectedElement) { addSelfAndDescendantsToChain(chain, selectedElement, selfDepth); if (selectedSelectorId) seen.add(selectedSelectorId); } else { const id = sibling.selectorId; if (id != null) { if (id === selectedSelectorId || seen.has(id)) continue; seen.add(id); } chain.push({ ...sibling, depth: selfDepth }); } } } /** * Build the layer chain for the dropdown: * * Parents – up to MAX_PARENT_DEPTH instrumented ancestors, outer → inner. * Siblings – instrumented children of the immediate parent, at the same depth. * Current – the selected element (highlighted), with children expanded. * Children – instrumented descendants within MAX_CHILD_DEPTH levels, DOM order. * * Each item carries a `depth` for visual indentation. */ export function buildLayerChain(selectedElement) { const parents = collectInstrumentedParents(selectedElement); const chain = []; const selfDepth = addParentsToChain(chain, parents); const instrParent = getImmediateInstrParent(parents); if (instrParent) { const siblings = collectSiblings(instrParent, selectedElement); appendSiblingsWithSelected(chain, siblings, selectedElement, selfDepth); } else { addSelfAndDescendantsToChain(chain, selectedElement, selfDepth); } return chain; } //# sourceMappingURL=utils.js.map