@base44-preview/vite-plugin 0.2.21-pr.35.60ab963 → 0.2.22-pr.36.019589c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/injections/layer-dropdown/component/dropdown-ui.d.ts +16 -0
- package/dist/injections/layer-dropdown/component/dropdown-ui.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/component/dropdown-ui.js +166 -0
- package/dist/injections/layer-dropdown/component/dropdown-ui.js.map +1 -0
- package/dist/injections/layer-dropdown/consts.d.ts +15 -0
- package/dist/injections/layer-dropdown/consts.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/consts.js +36 -0
- package/dist/injections/layer-dropdown/consts.js.map +1 -0
- package/dist/injections/layer-dropdown/utils.d.ts +26 -0
- package/dist/injections/layer-dropdown/utils.d.ts.map +1 -0
- package/dist/injections/layer-dropdown/utils.js +109 -0
- package/dist/injections/layer-dropdown/utils.js.map +1 -0
- package/dist/injections/utils.d.ts +6 -0
- package/dist/injections/utils.d.ts.map +1 -1
- package/dist/injections/utils.js +25 -0
- package/dist/injections/utils.js.map +1 -1
- package/dist/injections/visual-edit-agent.d.ts.map +1 -1
- package/dist/injections/visual-edit-agent.js +142 -1
- package/dist/injections/visual-edit-agent.js.map +1 -1
- package/dist/statics/index.mjs +1 -1
- package/dist/statics/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/injections/layer-dropdown/component/dropdown-ui.ts +220 -0
- package/src/injections/layer-dropdown/consts.ts +44 -0
- package/src/injections/layer-dropdown/utils.ts +138 -0
- package/src/injections/utils.ts +34 -0
- package/src/injections/visual-edit-agent.ts +171 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Dropdown UI component for layer navigation */
|
|
2
|
+
import type { LayerInfo } from "../utils.js";
|
|
3
|
+
export type OnLayerSelect = (layer: LayerInfo) => void;
|
|
4
|
+
export type OnLayerHover = (layer: LayerInfo) => void;
|
|
5
|
+
export type OnLayerHoverEnd = () => void;
|
|
6
|
+
/** Create the dropdown DOM element with layer items */
|
|
7
|
+
export declare function createDropdownElement(layers: LayerInfo[], currentSelectorId: string | null, onSelect: OnLayerSelect, onHover?: OnLayerHover, onHoverEnd?: OnLayerHoverEnd): HTMLDivElement;
|
|
8
|
+
/** Add chevron indicator and pointer-events to the label */
|
|
9
|
+
export declare function enhanceLabelWithChevron(label: HTMLDivElement): void;
|
|
10
|
+
/** Show the dropdown below the label element */
|
|
11
|
+
export declare function showDropdown(label: HTMLDivElement, layers: LayerInfo[], currentSelectorId: string | null, onSelect: OnLayerSelect, onHover?: OnLayerHover, onHoverEnd?: OnLayerHoverEnd): void;
|
|
12
|
+
/** Close the active dropdown and clean up listeners */
|
|
13
|
+
export declare function closeDropdown(): void;
|
|
14
|
+
/** Check if a dropdown is currently visible */
|
|
15
|
+
export declare function isDropdownOpen(): boolean;
|
|
16
|
+
//# sourceMappingURL=dropdown-ui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dropdown-ui.d.ts","sourceRoot":"","sources":["../../../../src/injections/layer-dropdown/component/dropdown-ui.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAcjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AACvD,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AACtD,MAAM,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC;AAOzC,uDAAuD;AACvD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EAAE,EACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,QAAQ,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,YAAY,EACtB,UAAU,CAAC,EAAE,eAAe,GAC3B,cAAc,CAsDhB;AAED,4DAA4D;AAC5D,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAQnE;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAC1B,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,SAAS,EAAE,EACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,QAAQ,EAAE,aAAa,EACvB,OAAO,CAAC,EAAE,YAAY,EACtB,UAAU,CAAC,EAAE,eAAe,GAC3B,IAAI,CAoFN;AAED,uDAAuD;AACvD,wBAAgB,aAAa,IAAI,IAAI,CAoBpC;AAED,+CAA+C;AAC/C,wBAAgB,cAAc,IAAI,OAAO,CAExC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/** Dropdown UI component for layer navigation */
|
|
2
|
+
import { DROPDOWN_CONTAINER_STYLES, DROPDOWN_ITEM_BASE_STYLES, DROPDOWN_ITEM_ACTIVE_COLOR, DROPDOWN_ITEM_ACTIVE_BG, DROPDOWN_ITEM_ACTIVE_FONT_WEIGHT, DROPDOWN_ITEM_HOVER_BG, DEPTH_INDENT_PX, LABEL_CHEVRON, LAYER_DROPDOWN_ATTR, } from "../consts.js";
|
|
3
|
+
import { getLayerDisplayName } from "../utils.js";
|
|
4
|
+
let activeDropdown = null;
|
|
5
|
+
let outsideMousedownHandler = null;
|
|
6
|
+
let activeOnHoverEnd = null;
|
|
7
|
+
let activeKeydownHandler = null;
|
|
8
|
+
/** Create the dropdown DOM element with layer items */
|
|
9
|
+
export function createDropdownElement(layers, currentSelectorId, onSelect, onHover, onHoverEnd) {
|
|
10
|
+
const container = document.createElement("div");
|
|
11
|
+
container.setAttribute(LAYER_DROPDOWN_ATTR, "true");
|
|
12
|
+
Object.keys(DROPDOWN_CONTAINER_STYLES).forEach((key) => {
|
|
13
|
+
container.style[key] = DROPDOWN_CONTAINER_STYLES[key];
|
|
14
|
+
});
|
|
15
|
+
layers.forEach((layer) => {
|
|
16
|
+
const item = document.createElement("div");
|
|
17
|
+
const isActive = layer.selectorId === currentSelectorId;
|
|
18
|
+
item.textContent = getLayerDisplayName(layer);
|
|
19
|
+
Object.keys(DROPDOWN_ITEM_BASE_STYLES).forEach((key) => {
|
|
20
|
+
item.style[key] = DROPDOWN_ITEM_BASE_STYLES[key];
|
|
21
|
+
});
|
|
22
|
+
// Indent based on depth
|
|
23
|
+
const depth = layer.depth ?? 0;
|
|
24
|
+
if (depth > 0) {
|
|
25
|
+
item.style.paddingLeft = `${12 + depth * DEPTH_INDENT_PX}px`;
|
|
26
|
+
}
|
|
27
|
+
if (isActive) {
|
|
28
|
+
item.style.color = DROPDOWN_ITEM_ACTIVE_COLOR;
|
|
29
|
+
item.style.backgroundColor = DROPDOWN_ITEM_ACTIVE_BG;
|
|
30
|
+
item.style.fontWeight = DROPDOWN_ITEM_ACTIVE_FONT_WEIGHT;
|
|
31
|
+
}
|
|
32
|
+
item.addEventListener("mouseenter", () => {
|
|
33
|
+
if (!isActive) {
|
|
34
|
+
item.style.backgroundColor = DROPDOWN_ITEM_HOVER_BG;
|
|
35
|
+
}
|
|
36
|
+
if (onHover)
|
|
37
|
+
onHover(layer);
|
|
38
|
+
});
|
|
39
|
+
item.addEventListener("mouseleave", () => {
|
|
40
|
+
if (!isActive) {
|
|
41
|
+
item.style.backgroundColor = "transparent";
|
|
42
|
+
}
|
|
43
|
+
if (onHoverEnd)
|
|
44
|
+
onHoverEnd();
|
|
45
|
+
});
|
|
46
|
+
item.addEventListener("click", (e) => {
|
|
47
|
+
e.stopPropagation();
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
onSelect(layer);
|
|
50
|
+
});
|
|
51
|
+
container.appendChild(item);
|
|
52
|
+
});
|
|
53
|
+
return container;
|
|
54
|
+
}
|
|
55
|
+
/** Add chevron indicator and pointer-events to the label */
|
|
56
|
+
export function enhanceLabelWithChevron(label) {
|
|
57
|
+
if (label.textContent?.includes(LABEL_CHEVRON))
|
|
58
|
+
return;
|
|
59
|
+
label.textContent = label.textContent + LABEL_CHEVRON;
|
|
60
|
+
label.style.cursor = "pointer";
|
|
61
|
+
label.style.userSelect = "none";
|
|
62
|
+
label.style.pointerEvents = "auto";
|
|
63
|
+
label.setAttribute(LAYER_DROPDOWN_ATTR, "true");
|
|
64
|
+
}
|
|
65
|
+
/** Show the dropdown below the label element */
|
|
66
|
+
export function showDropdown(label, layers, currentSelectorId, onSelect, onHover, onHoverEnd) {
|
|
67
|
+
closeDropdown();
|
|
68
|
+
const dropdown = createDropdownElement(layers, currentSelectorId, (layer) => {
|
|
69
|
+
if (onHoverEnd)
|
|
70
|
+
onHoverEnd();
|
|
71
|
+
onSelect(layer);
|
|
72
|
+
closeDropdown();
|
|
73
|
+
}, onHover, onHoverEnd);
|
|
74
|
+
const overlay = label.parentElement;
|
|
75
|
+
if (!overlay)
|
|
76
|
+
return;
|
|
77
|
+
// Position below the label
|
|
78
|
+
dropdown.style.top = `${label.offsetTop + label.offsetHeight + 2}px`;
|
|
79
|
+
dropdown.style.left = `${label.offsetLeft}px`;
|
|
80
|
+
overlay.appendChild(dropdown);
|
|
81
|
+
activeDropdown = dropdown;
|
|
82
|
+
activeOnHoverEnd = onHoverEnd ?? null;
|
|
83
|
+
// --- Arrow key navigation ---
|
|
84
|
+
const items = Array.from(dropdown.children);
|
|
85
|
+
let focusedIndex = -1;
|
|
86
|
+
const setFocusedItem = (index) => {
|
|
87
|
+
// Clear previous focus styling
|
|
88
|
+
if (focusedIndex >= 0 && focusedIndex < items.length) {
|
|
89
|
+
const prev = items[focusedIndex];
|
|
90
|
+
const prevIsActive = prev.style.color === DROPDOWN_ITEM_ACTIVE_COLOR;
|
|
91
|
+
if (!prevIsActive) {
|
|
92
|
+
prev.style.backgroundColor = "transparent";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
focusedIndex = index;
|
|
96
|
+
// Apply hover styling to new focused item
|
|
97
|
+
if (focusedIndex >= 0 && focusedIndex < items.length) {
|
|
98
|
+
const cur = items[focusedIndex];
|
|
99
|
+
const curIsActive = cur.style.color === DROPDOWN_ITEM_ACTIVE_COLOR;
|
|
100
|
+
if (!curIsActive) {
|
|
101
|
+
cur.style.backgroundColor = DROPDOWN_ITEM_HOVER_BG;
|
|
102
|
+
}
|
|
103
|
+
cur.scrollIntoView({ block: "nearest" });
|
|
104
|
+
if (onHover)
|
|
105
|
+
onHover(layers[focusedIndex]);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
activeKeydownHandler = (e) => {
|
|
109
|
+
if (e.key === "ArrowDown") {
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
e.stopPropagation();
|
|
112
|
+
const next = focusedIndex < items.length - 1 ? focusedIndex + 1 : 0;
|
|
113
|
+
setFocusedItem(next);
|
|
114
|
+
}
|
|
115
|
+
else if (e.key === "ArrowUp") {
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
e.stopPropagation();
|
|
118
|
+
const prev = focusedIndex > 0 ? focusedIndex - 1 : items.length - 1;
|
|
119
|
+
setFocusedItem(prev);
|
|
120
|
+
}
|
|
121
|
+
else if (e.key === "Enter" && focusedIndex >= 0) {
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
e.stopPropagation();
|
|
124
|
+
if (onHoverEnd)
|
|
125
|
+
onHoverEnd();
|
|
126
|
+
onSelect(layers[focusedIndex]);
|
|
127
|
+
closeDropdown();
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
document.addEventListener("keydown", activeKeydownHandler, true);
|
|
131
|
+
// Close on outside mousedown (mousedown fires before click, avoiding
|
|
132
|
+
// conflict with handleElementClick's stopPropagation on click events)
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
outsideMousedownHandler = (e) => {
|
|
135
|
+
const target = e.target;
|
|
136
|
+
if (!dropdown.contains(target) && target !== label) {
|
|
137
|
+
closeDropdown();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
document.addEventListener("mousedown", outsideMousedownHandler, true);
|
|
141
|
+
}, 0);
|
|
142
|
+
}
|
|
143
|
+
/** Close the active dropdown and clean up listeners */
|
|
144
|
+
export function closeDropdown() {
|
|
145
|
+
if (activeOnHoverEnd) {
|
|
146
|
+
activeOnHoverEnd();
|
|
147
|
+
activeOnHoverEnd = null;
|
|
148
|
+
}
|
|
149
|
+
if (activeDropdown && activeDropdown.parentNode) {
|
|
150
|
+
activeDropdown.remove();
|
|
151
|
+
}
|
|
152
|
+
activeDropdown = null;
|
|
153
|
+
if (outsideMousedownHandler) {
|
|
154
|
+
document.removeEventListener("mousedown", outsideMousedownHandler, true);
|
|
155
|
+
outsideMousedownHandler = null;
|
|
156
|
+
}
|
|
157
|
+
if (activeKeydownHandler) {
|
|
158
|
+
document.removeEventListener("keydown", activeKeydownHandler, true);
|
|
159
|
+
activeKeydownHandler = null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Check if a dropdown is currently visible */
|
|
163
|
+
export function isDropdownOpen() {
|
|
164
|
+
return activeDropdown !== null;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=dropdown-ui.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dropdown-ui.js","sourceRoot":"","sources":["../../../../src/injections/layer-dropdown/component/dropdown-ui.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Style constants for the layer dropdown UI */
|
|
2
|
+
export declare const DROPDOWN_CONTAINER_STYLES: Record<string, string>;
|
|
3
|
+
export declare const DROPDOWN_ITEM_BASE_STYLES: Record<string, string>;
|
|
4
|
+
export declare const DROPDOWN_ITEM_ACTIVE_COLOR = "#526cff";
|
|
5
|
+
export declare const DROPDOWN_ITEM_ACTIVE_BG = "#DBEAFE";
|
|
6
|
+
export declare const DROPDOWN_ITEM_ACTIVE_FONT_WEIGHT = "600";
|
|
7
|
+
export declare const DROPDOWN_ITEM_HOVER_BG = "#f1f5f9";
|
|
8
|
+
export declare const DEPTH_INDENT_PX = 10;
|
|
9
|
+
export declare const LABEL_CHEVRON = " \u25BE";
|
|
10
|
+
export declare const LAYER_DROPDOWN_ATTR = "data-layer-dropdown";
|
|
11
|
+
/** Max instrumented ancestors to show above the selected element */
|
|
12
|
+
export declare const MAX_PARENT_DEPTH = 2;
|
|
13
|
+
/** Max instrumented depth levels to show below the selected element */
|
|
14
|
+
export declare const MAX_CHILD_DEPTH = 2;
|
|
15
|
+
//# sourceMappingURL=consts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../../../src/injections/layer-dropdown/consts.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAa5D,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQ5D,CAAC;AAEF,eAAO,MAAM,0BAA0B,YAAY,CAAC;AACpD,eAAO,MAAM,uBAAuB,YAAY,CAAC;AACjD,eAAO,MAAM,gCAAgC,QAAQ,CAAC;AAEtD,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAEhD,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,eAAO,MAAM,aAAa,YAAY,CAAC;AAEvC,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,uEAAuE;AACvE,eAAO,MAAM,eAAe,IAAI,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Style constants for the layer dropdown UI */
|
|
2
|
+
export const DROPDOWN_CONTAINER_STYLES = {
|
|
3
|
+
position: "absolute",
|
|
4
|
+
backgroundColor: "#ffffff",
|
|
5
|
+
border: "1px solid #e2e8f0",
|
|
6
|
+
borderRadius: "6px",
|
|
7
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
8
|
+
fontSize: "12px",
|
|
9
|
+
minWidth: "120px",
|
|
10
|
+
maxHeight: "200px",
|
|
11
|
+
overflowY: "auto",
|
|
12
|
+
zIndex: "10001",
|
|
13
|
+
padding: "4px 0",
|
|
14
|
+
pointerEvents: "auto",
|
|
15
|
+
};
|
|
16
|
+
export const DROPDOWN_ITEM_BASE_STYLES = {
|
|
17
|
+
padding: "4px 12px",
|
|
18
|
+
cursor: "pointer",
|
|
19
|
+
color: "#334155",
|
|
20
|
+
backgroundColor: "transparent",
|
|
21
|
+
whiteSpace: "nowrap",
|
|
22
|
+
lineHeight: "1.5",
|
|
23
|
+
fontWeight: "400",
|
|
24
|
+
};
|
|
25
|
+
export const DROPDOWN_ITEM_ACTIVE_COLOR = "#526cff";
|
|
26
|
+
export const DROPDOWN_ITEM_ACTIVE_BG = "#DBEAFE";
|
|
27
|
+
export const DROPDOWN_ITEM_ACTIVE_FONT_WEIGHT = "600";
|
|
28
|
+
export const DROPDOWN_ITEM_HOVER_BG = "#f1f5f9";
|
|
29
|
+
export const DEPTH_INDENT_PX = 10;
|
|
30
|
+
export const LABEL_CHEVRON = " \u25BE";
|
|
31
|
+
export const LAYER_DROPDOWN_ATTR = "data-layer-dropdown";
|
|
32
|
+
/** Max instrumented ancestors to show above the selected element */
|
|
33
|
+
export const MAX_PARENT_DEPTH = 2;
|
|
34
|
+
/** Max instrumented depth levels to show below the selected element */
|
|
35
|
+
export const MAX_CHILD_DEPTH = 2;
|
|
36
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../../../src/injections/layer-dropdown/consts.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAEhD,MAAM,CAAC,MAAM,yBAAyB,GAA2B;IAC/D,QAAQ,EAAE,UAAU;IACpB,eAAe,EAAE,SAAS;IAC1B,MAAM,EAAE,mBAAmB;IAC3B,YAAY,EAAE,KAAK;IACnB,SAAS,EAAE,gCAAgC;IAC3C,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,OAAO;IACjB,SAAS,EAAE,OAAO;IAClB,SAAS,EAAE,MAAM;IACjB,MAAM,EAAE,OAAO;IACf,OAAO,EAAE,OAAO;IAChB,aAAa,EAAE,MAAM;CACtB,CAAC;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAA2B;IAC/D,OAAO,EAAE,UAAU;IACnB,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,SAAS;IAChB,eAAe,EAAE,aAAa;IAC9B,UAAU,EAAE,QAAQ;IACpB,UAAU,EAAE,KAAK;IACjB,UAAU,EAAE,KAAK;CAClB,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,SAAS,CAAC;AACpD,MAAM,CAAC,MAAM,uBAAuB,GAAG,SAAS,CAAC;AACjD,MAAM,CAAC,MAAM,gCAAgC,GAAG,KAAK,CAAC;AAEtD,MAAM,CAAC,MAAM,sBAAsB,GAAG,SAAS,CAAC;AAEhD,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;AAEvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAEzD,oEAAoE;AACpE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,uEAAuE;AACvE,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** DOM traversal utilities for building the layer chain of instrumented elements */
|
|
2
|
+
export interface LayerInfo {
|
|
3
|
+
element: Element;
|
|
4
|
+
tagName: string;
|
|
5
|
+
selectorId: string | null;
|
|
6
|
+
depth?: number;
|
|
7
|
+
}
|
|
8
|
+
/** Display name for a layer — just the real tag name */
|
|
9
|
+
export declare function getLayerDisplayName(layer: LayerInfo): string;
|
|
10
|
+
/**
|
|
11
|
+
* Collect instrumented descendants up to `maxDepth` instrumented nesting levels.
|
|
12
|
+
* Non-instrumented wrappers are walked through without counting toward depth.
|
|
13
|
+
* Results are in DOM order.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getInstrumentedDescendants(parent: Element, maxDepth: number): LayerInfo[];
|
|
16
|
+
/**
|
|
17
|
+
* Build the layer chain for the dropdown:
|
|
18
|
+
*
|
|
19
|
+
* Parents – up to MAX_PARENT_DEPTH instrumented ancestors, outer → inner.
|
|
20
|
+
* Current – the selected element.
|
|
21
|
+
* Children – instrumented descendants within MAX_CHILD_DEPTH levels, DOM order.
|
|
22
|
+
*
|
|
23
|
+
* Each item carries a `depth` for visual indentation.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildLayerChain(selectedElement: Element): LayerInfo[];
|
|
26
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/injections/layer-dropdown/utils.ts"],"names":[],"mappings":"AAAA,oFAAoF;AAKpF,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,MAAM,GACf,SAAS,EAAE,CAsBb;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,eAAe,EAAE,OAAO,GAAG,SAAS,EAAE,CAkDrE"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/** DOM traversal utilities for building the layer chain of instrumented elements */
|
|
2
|
+
import { isInstrumentedElement, getElementSelectorId } from "../utils.js";
|
|
3
|
+
import { MAX_PARENT_DEPTH, MAX_CHILD_DEPTH } from "./consts.js";
|
|
4
|
+
/** Display name for a layer — just the real tag name */
|
|
5
|
+
export function getLayerDisplayName(layer) {
|
|
6
|
+
return layer.tagName;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Collect instrumented descendants up to `maxDepth` instrumented nesting levels.
|
|
10
|
+
* Non-instrumented wrappers are walked through without counting toward depth.
|
|
11
|
+
* Results are in DOM order.
|
|
12
|
+
*/
|
|
13
|
+
export function getInstrumentedDescendants(parent, maxDepth) {
|
|
14
|
+
const result = [];
|
|
15
|
+
function walk(el, instrDepth) {
|
|
16
|
+
if (instrDepth > maxDepth)
|
|
17
|
+
return;
|
|
18
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
19
|
+
const child = el.children[i];
|
|
20
|
+
if (isInstrumentedElement(child)) {
|
|
21
|
+
result.push({
|
|
22
|
+
element: child,
|
|
23
|
+
tagName: child.tagName.toLowerCase(),
|
|
24
|
+
selectorId: getElementSelectorId(child),
|
|
25
|
+
});
|
|
26
|
+
walk(child, instrDepth + 1);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
walk(child, instrDepth);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
walk(parent, 1);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build the layer chain for the dropdown:
|
|
38
|
+
*
|
|
39
|
+
* Parents – up to MAX_PARENT_DEPTH instrumented ancestors, outer → inner.
|
|
40
|
+
* Current – the selected element.
|
|
41
|
+
* Children – instrumented descendants within MAX_CHILD_DEPTH levels, DOM order.
|
|
42
|
+
*
|
|
43
|
+
* Each item carries a `depth` for visual indentation.
|
|
44
|
+
*/
|
|
45
|
+
export function buildLayerChain(selectedElement) {
|
|
46
|
+
// --- Parents (walk up, collect at most MAX_PARENT_DEPTH) ---
|
|
47
|
+
const parents = [];
|
|
48
|
+
let current = selectedElement.parentElement;
|
|
49
|
+
while (current &&
|
|
50
|
+
current !== document.documentElement &&
|
|
51
|
+
current !== document.body &&
|
|
52
|
+
parents.length < MAX_PARENT_DEPTH) {
|
|
53
|
+
if (isInstrumentedElement(current)) {
|
|
54
|
+
parents.push({
|
|
55
|
+
element: current,
|
|
56
|
+
tagName: current.tagName.toLowerCase(),
|
|
57
|
+
selectorId: getElementSelectorId(current),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
current = current.parentElement;
|
|
61
|
+
}
|
|
62
|
+
// Reverse so outermost parent comes first
|
|
63
|
+
parents.reverse();
|
|
64
|
+
// --- Build the chain with depth ---
|
|
65
|
+
const chain = [];
|
|
66
|
+
const baseDepth = 0;
|
|
67
|
+
// Parents: depth 0, 1, …
|
|
68
|
+
parents.forEach((p, i) => {
|
|
69
|
+
chain.push({ ...p, depth: baseDepth + i });
|
|
70
|
+
});
|
|
71
|
+
// Self
|
|
72
|
+
const selfDepth = parents.length;
|
|
73
|
+
chain.push({
|
|
74
|
+
element: selectedElement,
|
|
75
|
+
tagName: selectedElement.tagName.toLowerCase(),
|
|
76
|
+
selectorId: getElementSelectorId(selectedElement),
|
|
77
|
+
depth: selfDepth,
|
|
78
|
+
});
|
|
79
|
+
// Children: up to MAX_CHILD_DEPTH instrumented levels below selected
|
|
80
|
+
const descendants = getInstrumentedDescendants(selectedElement, MAX_CHILD_DEPTH);
|
|
81
|
+
// Assign visual depth: we need to track the instrumented nesting to set depth correctly
|
|
82
|
+
assignDescendantDepths(selectedElement, descendants, selfDepth + 1);
|
|
83
|
+
chain.push(...descendants);
|
|
84
|
+
return chain;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Walk the DOM tree again to assign correct visual depth to each descendant.
|
|
88
|
+
* This avoids storing depth during collection and keeps the API simple.
|
|
89
|
+
*/
|
|
90
|
+
function assignDescendantDepths(root, descendants, startDepth) {
|
|
91
|
+
// Build a set for O(1) lookup
|
|
92
|
+
const descSet = new Set(descendants.map((d) => d.element));
|
|
93
|
+
// Map element → LayerInfo for mutation
|
|
94
|
+
const descMap = new Map(descendants.map((d) => [d.element, d]));
|
|
95
|
+
function walk(el, instrDepth) {
|
|
96
|
+
for (let i = 0; i < el.children.length; i++) {
|
|
97
|
+
const child = el.children[i];
|
|
98
|
+
if (descSet.has(child)) {
|
|
99
|
+
descMap.get(child).depth = startDepth + instrDepth - 1;
|
|
100
|
+
walk(child, instrDepth + 1);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
walk(child, instrDepth);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
walk(root, 1);
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/injections/layer-dropdown/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/** Check if an element has instrumentation attributes */
|
|
2
|
+
export declare function isInstrumentedElement(element: Element): boolean;
|
|
3
|
+
/** Get the selector ID from an element's data attributes (prefers source-location) */
|
|
4
|
+
export declare function getElementSelectorId(element: Element): string | null;
|
|
5
|
+
/** Find the nearest instrumented ancestor (not the element itself) */
|
|
6
|
+
export declare function getImmediateInstrumentedParent(element: Element): Element | null;
|
|
1
7
|
/** Find elements by ID - first try data-source-location, fallback to data-visual-selector-id */
|
|
2
8
|
export declare function findElementsById(id: string | null): Element[];
|
|
3
9
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/injections/utils.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,EAAE,CAW7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI/E"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/injections/utils.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAK/D;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAOpE;AAED,sEAAsE;AACtE,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,IAAI,CAa/E;AAED,gGAAgG;AAChG,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,EAAE,CAW7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI/E"}
|
package/dist/injections/utils.js
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
/** Check if an element has instrumentation attributes */
|
|
2
|
+
export function isInstrumentedElement(element) {
|
|
3
|
+
const htmlEl = element;
|
|
4
|
+
return !!(htmlEl.dataset?.sourceLocation || htmlEl.dataset?.visualSelectorId);
|
|
5
|
+
}
|
|
6
|
+
/** Get the selector ID from an element's data attributes (prefers source-location) */
|
|
7
|
+
export function getElementSelectorId(element) {
|
|
8
|
+
const htmlEl = element;
|
|
9
|
+
return (htmlEl.dataset?.sourceLocation ||
|
|
10
|
+
htmlEl.dataset?.visualSelectorId ||
|
|
11
|
+
null);
|
|
12
|
+
}
|
|
13
|
+
/** Find the nearest instrumented ancestor (not the element itself) */
|
|
14
|
+
export function getImmediateInstrumentedParent(element) {
|
|
15
|
+
let current = element.parentElement;
|
|
16
|
+
while (current &&
|
|
17
|
+
current !== document.documentElement &&
|
|
18
|
+
current !== document.body) {
|
|
19
|
+
if (isInstrumentedElement(current)) {
|
|
20
|
+
return current;
|
|
21
|
+
}
|
|
22
|
+
current = current.parentElement;
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
1
26
|
/** Find elements by ID - first try data-source-location, fallback to data-visual-selector-id */
|
|
2
27
|
export function findElementsById(id) {
|
|
3
28
|
if (!id)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/injections/utils.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,EAAiB;IAChD,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACnB,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAC/B,QAAQ,CAAC,gBAAgB,CAAC,0BAA0B,EAAE,IAAI,CAAC,CAC5D,CAAC;IACF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CACf,QAAQ,CAAC,gBAAgB,CAAC,6BAA6B,EAAE,IAAI,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAmB,EAAE,OAAe;IACvE,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,OAAO,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/injections/utils.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,MAAM,GAAG,OAAsB,CAAC;IACtC,OAAO,CAAC,CAAC,CACP,MAAM,CAAC,OAAO,EAAE,cAAc,IAAI,MAAM,CAAC,OAAO,EAAE,gBAAgB,CACnE,CAAC;AACJ,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,MAAM,GAAG,OAAsB,CAAC;IACtC,OAAO,CACL,MAAM,CAAC,OAAO,EAAE,cAAc;QAC9B,MAAM,CAAC,OAAO,EAAE,gBAAgB;QAChC,IAAI,CACL,CAAC;AACJ,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,8BAA8B,CAAC,OAAgB;IAC7D,IAAI,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IACpC,OACE,OAAO;QACP,OAAO,KAAK,QAAQ,CAAC,eAAe;QACpC,OAAO,KAAK,QAAQ,CAAC,IAAI,EACzB,CAAC;QACD,IAAI,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,EAAiB;IAChD,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACnB,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAC/B,QAAQ,CAAC,gBAAgB,CAAC,0BAA0B,EAAE,IAAI,CAAC,CAC5D,CAAC;IACF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CACf,QAAQ,CAAC,gBAAgB,CAAC,6BAA6B,EAAE,IAAI,CAAC,CAC/D,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAmB,EAAE,OAAe;IACvE,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,OAAO,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visual-edit-agent.d.ts","sourceRoot":"","sources":["../../src/injections/visual-edit-agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"visual-edit-agent.d.ts","sourceRoot":"","sources":["../../src/injections/visual-edit-agent.ts"],"names":[],"mappings":"AAWA,wBAAgB,oBAAoB,SA+sBnC"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { findElementsById, updateElementClasses } from "./utils.js";
|
|
1
|
+
import { findElementsById, updateElementClasses, getElementSelectorId } from "./utils.js";
|
|
2
|
+
import { buildLayerChain } from "./layer-dropdown/utils.js";
|
|
3
|
+
import { enhanceLabelWithChevron, showDropdown, closeDropdown as closeLayerDropdown, isDropdownOpen as isLayerDropdownOpen, } from "./layer-dropdown/component/dropdown-ui.js";
|
|
4
|
+
import { LAYER_DROPDOWN_ATTR } from "./layer-dropdown/consts.js";
|
|
2
5
|
export function setupVisualEditAgent() {
|
|
3
6
|
// State variables (replacing React useState/useRef)
|
|
4
7
|
let isVisualEditMode = false;
|
|
@@ -119,6 +122,9 @@ export function setupVisualEditAgent() {
|
|
|
119
122
|
if (!isVisualEditMode)
|
|
120
123
|
return;
|
|
121
124
|
const target = e.target;
|
|
125
|
+
// Let layer dropdown clicks pass through without interference
|
|
126
|
+
if (target.closest(`[${LAYER_DROPDOWN_ATTR}]`))
|
|
127
|
+
return;
|
|
122
128
|
// Close dropdowns when clicking anywhere in iframe if a dropdown is open
|
|
123
129
|
if (isDropdownOpen) {
|
|
124
130
|
e.preventDefault();
|
|
@@ -162,6 +168,8 @@ export function setupVisualEditAgent() {
|
|
|
162
168
|
selectedElementId = visualSelectorId || null;
|
|
163
169
|
// Clear hover overlays
|
|
164
170
|
clearHoverOverlays();
|
|
171
|
+
// Attach layer dropdown to the selected overlay label
|
|
172
|
+
attachLayerDropdownToOverlay(selectedOverlays[0], element);
|
|
165
173
|
// Calculate element position for popover positioning
|
|
166
174
|
const rect = element.getBoundingClientRect();
|
|
167
175
|
const elementPosition = {
|
|
@@ -250,10 +258,143 @@ export function setupVisualEditAgent() {
|
|
|
250
258
|
}
|
|
251
259
|
}, 50);
|
|
252
260
|
};
|
|
261
|
+
// --- Layer dropdown: hover preview overlay ---
|
|
262
|
+
let layerPreviewOverlay = null;
|
|
263
|
+
const clearLayerPreview = () => {
|
|
264
|
+
if (layerPreviewOverlay && layerPreviewOverlay.parentNode) {
|
|
265
|
+
layerPreviewOverlay.remove();
|
|
266
|
+
}
|
|
267
|
+
layerPreviewOverlay = null;
|
|
268
|
+
};
|
|
269
|
+
const showLayerPreview = (layer) => {
|
|
270
|
+
clearLayerPreview();
|
|
271
|
+
// Don't preview the currently selected element
|
|
272
|
+
if (getElementSelectorId(layer.element) === selectedElementId)
|
|
273
|
+
return;
|
|
274
|
+
const overlay = createOverlay(false);
|
|
275
|
+
overlay.style.zIndex = "9998";
|
|
276
|
+
document.body.appendChild(overlay);
|
|
277
|
+
positionOverlay(overlay, layer.element);
|
|
278
|
+
layerPreviewOverlay = overlay;
|
|
279
|
+
};
|
|
280
|
+
// --- Layer dropdown: select a different element from the layer chain ---
|
|
281
|
+
const selectElementFromLayer = (layer) => {
|
|
282
|
+
clearLayerPreview();
|
|
283
|
+
closeLayerDropdown();
|
|
284
|
+
if (escapeHandler) {
|
|
285
|
+
document.removeEventListener("keydown", escapeHandler, true);
|
|
286
|
+
escapeHandler = null;
|
|
287
|
+
}
|
|
288
|
+
dropdownSourceElement = null;
|
|
289
|
+
const element = layer.element;
|
|
290
|
+
const htmlElement = element;
|
|
291
|
+
const visualSelectorId = getElementSelectorId(element);
|
|
292
|
+
// Clear existing selected overlays
|
|
293
|
+
selectedOverlays.forEach((overlay) => {
|
|
294
|
+
if (overlay && overlay.parentNode) {
|
|
295
|
+
overlay.remove();
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
selectedOverlays = [];
|
|
299
|
+
// Find all elements with the same ID
|
|
300
|
+
const elements = findElementsById(visualSelectorId || null);
|
|
301
|
+
// Create selected overlays for all matching elements
|
|
302
|
+
elements.forEach((el) => {
|
|
303
|
+
const overlay = createOverlay(true);
|
|
304
|
+
document.body.appendChild(overlay);
|
|
305
|
+
selectedOverlays.push(overlay);
|
|
306
|
+
positionOverlay(overlay, el, true);
|
|
307
|
+
});
|
|
308
|
+
selectedElementId = visualSelectorId || null;
|
|
309
|
+
clearHoverOverlays();
|
|
310
|
+
// Attach layer dropdown to the new overlay label
|
|
311
|
+
attachLayerDropdownToOverlay(selectedOverlays[0], element);
|
|
312
|
+
// Notify parent of the newly selected element
|
|
313
|
+
const rect = element.getBoundingClientRect();
|
|
314
|
+
const elementPosition = {
|
|
315
|
+
top: rect.top,
|
|
316
|
+
left: rect.left,
|
|
317
|
+
right: rect.right,
|
|
318
|
+
bottom: rect.bottom,
|
|
319
|
+
width: rect.width,
|
|
320
|
+
height: rect.height,
|
|
321
|
+
centerX: rect.left + rect.width / 2,
|
|
322
|
+
centerY: rect.top + rect.height / 2,
|
|
323
|
+
};
|
|
324
|
+
const svgElement = element;
|
|
325
|
+
const elementData = {
|
|
326
|
+
type: "element-selected",
|
|
327
|
+
tagName: element.tagName,
|
|
328
|
+
classes: svgElement.className?.baseVal ||
|
|
329
|
+
element.className ||
|
|
330
|
+
"",
|
|
331
|
+
visualSelectorId: visualSelectorId,
|
|
332
|
+
content: element.innerText,
|
|
333
|
+
dataSourceLocation: htmlElement.dataset.sourceLocation,
|
|
334
|
+
isDynamicContent: htmlElement.dataset.dynamicContent === "true",
|
|
335
|
+
linenumber: htmlElement.dataset.linenumber,
|
|
336
|
+
filename: htmlElement.dataset.filename,
|
|
337
|
+
position: elementPosition,
|
|
338
|
+
};
|
|
339
|
+
window.parent.postMessage(elementData, "*");
|
|
340
|
+
};
|
|
341
|
+
let escapeHandler = null;
|
|
342
|
+
let dropdownSourceElement = null;
|
|
343
|
+
const attachLayerDropdownToOverlay = (overlay, element) => {
|
|
344
|
+
if (!overlay)
|
|
345
|
+
return;
|
|
346
|
+
const label = overlay.querySelector("div");
|
|
347
|
+
if (!label)
|
|
348
|
+
return;
|
|
349
|
+
const layers = buildLayerChain(element);
|
|
350
|
+
// Only show dropdown when there are multiple layers to navigate
|
|
351
|
+
if (layers.length <= 1)
|
|
352
|
+
return;
|
|
353
|
+
const currentId = getElementSelectorId(element);
|
|
354
|
+
enhanceLabelWithChevron(label);
|
|
355
|
+
label.addEventListener("click", (e) => {
|
|
356
|
+
e.stopPropagation();
|
|
357
|
+
e.preventDefault();
|
|
358
|
+
if (isLayerDropdownOpen()) {
|
|
359
|
+
closeLayerDropdown();
|
|
360
|
+
reselectDropdownSource();
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
dropdownSourceElement = element;
|
|
364
|
+
selectedElementId = null;
|
|
365
|
+
window.parent.postMessage({ type: "element-selected", visualSelectorId: null }, "*");
|
|
366
|
+
escapeHandler = (ev) => {
|
|
367
|
+
if (ev.key === "Escape") {
|
|
368
|
+
ev.stopPropagation();
|
|
369
|
+
closeLayerDropdown();
|
|
370
|
+
reselectDropdownSource();
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
document.addEventListener("keydown", escapeHandler, true);
|
|
374
|
+
showDropdown(label, layers, currentId, selectElementFromLayer, showLayerPreview, clearLayerPreview);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
const reselectDropdownSource = () => {
|
|
379
|
+
if (escapeHandler) {
|
|
380
|
+
document.removeEventListener("keydown", escapeHandler, true);
|
|
381
|
+
escapeHandler = null;
|
|
382
|
+
}
|
|
383
|
+
if (dropdownSourceElement) {
|
|
384
|
+
selectElementFromLayer({
|
|
385
|
+
element: dropdownSourceElement,
|
|
386
|
+
tagName: dropdownSourceElement.tagName.toLowerCase(),
|
|
387
|
+
selectorId: getElementSelectorId(dropdownSourceElement),
|
|
388
|
+
});
|
|
389
|
+
dropdownSourceElement = null;
|
|
390
|
+
}
|
|
391
|
+
};
|
|
253
392
|
// Toggle visual edit mode
|
|
254
393
|
const toggleVisualEditMode = (isEnabled) => {
|
|
255
394
|
isVisualEditMode = isEnabled;
|
|
256
395
|
if (!isEnabled) {
|
|
396
|
+
clearLayerPreview();
|
|
397
|
+
closeLayerDropdown();
|
|
257
398
|
clearHoverOverlays();
|
|
258
399
|
selectedOverlays.forEach((overlay) => {
|
|
259
400
|
if (overlay && overlay.parentNode) {
|