Files
2026-06-24 09:48:54 +02:00

143 lines
5.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** 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