@salesforce/ui-bundle 1.117.2
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/LICENSE.txt +82 -0
- package/README.md +3 -0
- package/dist/api/clients.d.ts +22 -0
- package/dist/api/clients.d.ts.map +1 -0
- package/dist/api/clients.js +84 -0
- package/dist/api/graphql-operations-types.d.ts +225 -0
- package/dist/api/graphql-operations-types.d.ts.map +1 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +13 -0
- package/dist/api/utils/accounts.d.ts +33 -0
- package/dist/api/utils/accounts.d.ts.map +1 -0
- package/dist/api/utils/accounts.js +47 -0
- package/dist/api/utils/records.d.ts +16 -0
- package/dist/api/utils/records.d.ts.map +1 -0
- package/dist/api/utils/records.js +26 -0
- package/dist/api/utils/user.d.ts +17 -0
- package/dist/api/utils/user.d.ts.map +1 -0
- package/dist/api/utils/user.js +25 -0
- package/dist/app/index.d.ts +10 -0
- package/dist/app/index.d.ts.map +1 -0
- package/dist/app/index.js +7 -0
- package/dist/app/manifest.d.ts +34 -0
- package/dist/app/manifest.d.ts.map +1 -0
- package/dist/app/manifest.js +28 -0
- package/dist/app/org.d.ts +28 -0
- package/dist/app/org.d.ts.map +1 -0
- package/dist/app/org.js +67 -0
- package/dist/design/design-mode-interactions.js +761 -0
- package/dist/design/index.d.ts +12 -0
- package/dist/design/index.d.ts.map +1 -0
- package/dist/design/index.js +14 -0
- package/dist/design/interactions/communicationManager.d.ts +25 -0
- package/dist/design/interactions/communicationManager.d.ts.map +1 -0
- package/dist/design/interactions/componentMatcher.d.ts +43 -0
- package/dist/design/interactions/componentMatcher.d.ts.map +1 -0
- package/dist/design/interactions/editableManager.d.ts +25 -0
- package/dist/design/interactions/editableManager.d.ts.map +1 -0
- package/dist/design/interactions/eventHandlers.d.ts +40 -0
- package/dist/design/interactions/eventHandlers.d.ts.map +1 -0
- package/dist/design/interactions/index.d.ts +7 -0
- package/dist/design/interactions/index.d.ts.map +1 -0
- package/dist/design/interactions/interactionsController.d.ts +36 -0
- package/dist/design/interactions/interactionsController.d.ts.map +1 -0
- package/dist/design/interactions/styleManager.d.ts +49 -0
- package/dist/design/interactions/styleManager.d.ts.map +1 -0
- package/dist/design/interactions/utils/cssUtils.d.ts +54 -0
- package/dist/design/interactions/utils/cssUtils.d.ts.map +1 -0
- package/dist/design/interactions/utils/sourceUtils.d.ts +36 -0
- package/dist/design/interactions/utils/sourceUtils.d.ts.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/package.json.js +4 -0
- package/dist/proxy/handler.d.ts +38 -0
- package/dist/proxy/handler.d.ts.map +1 -0
- package/dist/proxy/handler.js +530 -0
- package/dist/proxy/index.d.ts +8 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +7 -0
- package/dist/proxy/livePreviewScript.d.ts +21 -0
- package/dist/proxy/livePreviewScript.d.ts.map +1 -0
- package/dist/proxy/livePreviewScript.js +16 -0
- package/dist/proxy/routing.d.ts +35 -0
- package/dist/proxy/routing.d.ts.map +1 -0
- package/dist/proxy/routing.js +83 -0
- package/dist/proxy/templates/livePreviewScript.js +553 -0
- package/package.json +65 -0
|
@@ -0,0 +1,761 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design Mode Interactions (Bundled)
|
|
3
|
+
*
|
|
4
|
+
* This file is auto-generated by esbuild from the modular design mode interactions files.
|
|
5
|
+
* Do not edit this file directly - edit the modules in src/design/interactions/
|
|
6
|
+
*/
|
|
7
|
+
(function () {
|
|
8
|
+
|
|
9
|
+
"use strict";
|
|
10
|
+
(() => {
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
14
|
+
|
|
15
|
+
// src/design/interactions/utils/sourceUtils.ts
|
|
16
|
+
function parseOptionalInt(value) {
|
|
17
|
+
if (value === null || value === void 0 || value === "") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
21
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
22
|
+
}
|
|
23
|
+
function parseSourceFileAttribute(value) {
|
|
24
|
+
const match = /^(.*):(\d+):(\d+)$/.exec(value);
|
|
25
|
+
if (!match) {
|
|
26
|
+
return { fileName: value, lineNumber: null, columnNumber: null };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
fileName: match[1] ?? value,
|
|
30
|
+
lineNumber: parseOptionalInt(match[2]),
|
|
31
|
+
columnNumber: parseOptionalInt(match[3])
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function getSourceFromDataAttributes(element) {
|
|
35
|
+
if (!element) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const source = element.getAttribute("data-source-file") || null;
|
|
39
|
+
if (!source) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return parseSourceFileAttribute(source);
|
|
43
|
+
}
|
|
44
|
+
function findElementsBySourceLocation(location) {
|
|
45
|
+
const results = [];
|
|
46
|
+
const elements = document.querySelectorAll("[data-source-file]");
|
|
47
|
+
for (const el of elements) {
|
|
48
|
+
const elSource = getSourceFromDataAttributes(el);
|
|
49
|
+
if (elSource && elSource.fileName === location.fileName && elSource.lineNumber === location.lineNumber && elSource.columnNumber === location.columnNumber) {
|
|
50
|
+
results.push(el);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return results;
|
|
54
|
+
}
|
|
55
|
+
function parseSourceLocation(sl) {
|
|
56
|
+
if (!sl || typeof sl !== "object") return void 0;
|
|
57
|
+
const obj = sl;
|
|
58
|
+
return {
|
|
59
|
+
fileName: String(obj.sourceFile ?? ""),
|
|
60
|
+
lineNumber: typeof obj.lineNumber === "number" ? obj.lineNumber : null,
|
|
61
|
+
columnNumber: typeof obj.columnNumber === "number" ? obj.columnNumber : null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function getLabelFromSource(element) {
|
|
65
|
+
if (!element) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
const source = element.getAttribute("data-source-file");
|
|
69
|
+
if (!source) {
|
|
70
|
+
return element.tagName ? element.tagName.toLowerCase() : "";
|
|
71
|
+
}
|
|
72
|
+
const { fileName } = parseSourceFileAttribute(source);
|
|
73
|
+
const parts = fileName.split(/[/\\]/);
|
|
74
|
+
const baseName = parts[parts.length - 1] || fileName;
|
|
75
|
+
console.log("baseName", baseName);
|
|
76
|
+
return baseName;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/design/interactions/editableManager.ts
|
|
80
|
+
var TEXT_TAGS = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "SPAN", "A", "BUTTON", "LABEL"];
|
|
81
|
+
var EditableManager = class {
|
|
82
|
+
constructor(communicationManager) {
|
|
83
|
+
__publicField(this, "communicationManager");
|
|
84
|
+
__publicField(this, "boundHandleBlur");
|
|
85
|
+
__publicField(this, "boundHandleKeydown");
|
|
86
|
+
__publicField(this, "boundHandleInput");
|
|
87
|
+
__publicField(this, "editableGroup");
|
|
88
|
+
this.communicationManager = communicationManager;
|
|
89
|
+
this.boundHandleBlur = this._handleBlur.bind(this);
|
|
90
|
+
this.boundHandleKeydown = this._handleKeydown.bind(this);
|
|
91
|
+
this.boundHandleInput = this._handleInput.bind(this);
|
|
92
|
+
this.editableGroup = [];
|
|
93
|
+
}
|
|
94
|
+
makeEditableIfText(element) {
|
|
95
|
+
if (!this._isTextElement(element)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const source = getSourceFromDataAttributes(element);
|
|
99
|
+
const siblings = source ? findElementsBySourceLocation(source) : [];
|
|
100
|
+
const others = siblings.filter((el) => el !== element);
|
|
101
|
+
this.editableGroup = [element, ...others];
|
|
102
|
+
for (const el of this.editableGroup) {
|
|
103
|
+
el.dataset.originalText = el.textContent ?? "";
|
|
104
|
+
}
|
|
105
|
+
element.contentEditable = "true";
|
|
106
|
+
element.addEventListener("blur", this.boundHandleBlur);
|
|
107
|
+
element.addEventListener("keydown", this.boundHandleKeydown);
|
|
108
|
+
element.addEventListener("input", this.boundHandleInput);
|
|
109
|
+
}
|
|
110
|
+
removeEditable(element) {
|
|
111
|
+
if (element.contentEditable === "true") {
|
|
112
|
+
element.contentEditable = "false";
|
|
113
|
+
}
|
|
114
|
+
element.removeEventListener("blur", this.boundHandleBlur);
|
|
115
|
+
element.removeEventListener("keydown", this.boundHandleKeydown);
|
|
116
|
+
element.removeEventListener("input", this.boundHandleInput);
|
|
117
|
+
for (const el of this.editableGroup) {
|
|
118
|
+
delete el.dataset.originalText;
|
|
119
|
+
}
|
|
120
|
+
this.editableGroup = [];
|
|
121
|
+
}
|
|
122
|
+
_isTextElement(element) {
|
|
123
|
+
return TEXT_TAGS.includes(element.tagName) && (element.textContent ?? "").trim().length > 0 && element.dataset.textType === "static";
|
|
124
|
+
}
|
|
125
|
+
_handleInput(e) {
|
|
126
|
+
const primary = e.target;
|
|
127
|
+
const text = primary.textContent ?? "";
|
|
128
|
+
for (let i = 1; i < this.editableGroup.length; i++) {
|
|
129
|
+
this.editableGroup[i].textContent = text;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
_handleBlur(e) {
|
|
133
|
+
const element = e.target;
|
|
134
|
+
const newText = element.textContent ?? "";
|
|
135
|
+
const originalText = element.dataset.originalText ?? "";
|
|
136
|
+
if (newText !== originalText) {
|
|
137
|
+
for (const el of this.editableGroup) {
|
|
138
|
+
el.dataset.originalText = newText;
|
|
139
|
+
}
|
|
140
|
+
if (this.communicationManager) {
|
|
141
|
+
this.communicationManager.notifyTextChange(element, originalText, newText);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
this.removeEditable(element);
|
|
145
|
+
}
|
|
146
|
+
_handleKeydown(e) {
|
|
147
|
+
const element = e.target;
|
|
148
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
element.blur();
|
|
151
|
+
}
|
|
152
|
+
if (e.key === "Escape") {
|
|
153
|
+
for (const el of this.editableGroup) {
|
|
154
|
+
if (el.dataset.originalText) {
|
|
155
|
+
el.textContent = el.dataset.originalText;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
element.blur();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// src/design/interactions/utils/cssUtils.ts
|
|
164
|
+
function getElementStyles(element) {
|
|
165
|
+
if (!element) return {};
|
|
166
|
+
const computed = window.getComputedStyle(element);
|
|
167
|
+
const inlineStyle = element.style;
|
|
168
|
+
const prop = (name, fallback) => ({
|
|
169
|
+
inline: inlineStyle[name] || (fallback ? inlineStyle[fallback] : "") || "",
|
|
170
|
+
computed: (fallback ? computed[fallback] : computed[name]) || ""
|
|
171
|
+
});
|
|
172
|
+
const box = (shorthand, top, right, bottom, left) => {
|
|
173
|
+
let inlineValue = inlineStyle[shorthand] || "";
|
|
174
|
+
if (!inlineValue) {
|
|
175
|
+
const iT = inlineStyle[top] || "", iR = inlineStyle[right] || "", iB = inlineStyle[bottom] || "", iL = inlineStyle[left] || "";
|
|
176
|
+
if (iT || iR || iB || iL) {
|
|
177
|
+
inlineValue = `${iT || "0px"} ${iR || "0px"} ${iB || "0px"} ${iL || "0px"}`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
inline: inlineValue,
|
|
182
|
+
computed: `${computed[top] || ""} ${computed[right] || ""} ${computed[bottom] || ""} ${computed[left] || ""}`
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
return {
|
|
186
|
+
width: prop("width"),
|
|
187
|
+
minWidth: prop("minWidth"),
|
|
188
|
+
maxWidth: prop("maxWidth"),
|
|
189
|
+
height: prop("height"),
|
|
190
|
+
minHeight: prop("minHeight"),
|
|
191
|
+
maxHeight: prop("maxHeight"),
|
|
192
|
+
overflow: prop("overflow"),
|
|
193
|
+
padding: box("padding", "paddingTop", "paddingRight", "paddingBottom", "paddingLeft"),
|
|
194
|
+
margin: box("margin", "marginTop", "marginRight", "marginBottom", "marginLeft"),
|
|
195
|
+
backgroundColor: prop("backgroundColor"),
|
|
196
|
+
borderWidth: box(
|
|
197
|
+
"borderWidth",
|
|
198
|
+
"borderTopWidth",
|
|
199
|
+
"borderRightWidth",
|
|
200
|
+
"borderBottomWidth",
|
|
201
|
+
"borderLeftWidth"
|
|
202
|
+
),
|
|
203
|
+
borderStyle: prop("borderStyle", "borderTopStyle"),
|
|
204
|
+
borderColor: prop("borderColor", "borderTopColor"),
|
|
205
|
+
borderRadius: box(
|
|
206
|
+
"borderRadius",
|
|
207
|
+
"borderTopLeftRadius",
|
|
208
|
+
"borderTopRightRadius",
|
|
209
|
+
"borderBottomRightRadius",
|
|
210
|
+
"borderBottomLeftRadius"
|
|
211
|
+
),
|
|
212
|
+
color: prop("color"),
|
|
213
|
+
fontFamily: prop("fontFamily"),
|
|
214
|
+
fontSize: prop("fontSize"),
|
|
215
|
+
fontWeight: prop("fontWeight"),
|
|
216
|
+
fontStyle: prop("fontStyle"),
|
|
217
|
+
lineHeight: prop("lineHeight"),
|
|
218
|
+
letterSpacing: prop("letterSpacing"),
|
|
219
|
+
textAlign: prop("textAlign"),
|
|
220
|
+
textDecoration: prop("textDecoration", "textDecorationLine"),
|
|
221
|
+
textTransform: prop("textTransform")
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/design/interactions/communicationManager.ts
|
|
226
|
+
var CommunicationManager = class {
|
|
227
|
+
constructor() {
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Notify the extension about a selected component
|
|
231
|
+
* @param element - The selected element
|
|
232
|
+
*/
|
|
233
|
+
notifyComponentSelected(element) {
|
|
234
|
+
const label = getLabelFromSource(element);
|
|
235
|
+
const wasSelected = element.classList.contains("design-mode-selected");
|
|
236
|
+
if (wasSelected) {
|
|
237
|
+
element.classList.remove("design-mode-selected");
|
|
238
|
+
}
|
|
239
|
+
const styles = getElementStyles(element);
|
|
240
|
+
if (wasSelected) {
|
|
241
|
+
element.classList.add("design-mode-selected");
|
|
242
|
+
}
|
|
243
|
+
const debugSource = getSourceFromDataAttributes(element);
|
|
244
|
+
const textType = element.dataset?.textType ?? "none";
|
|
245
|
+
const hasNonEditableText = TEXT_TAGS.includes(element.tagName) && (textType === "dynamic" || textType === "mixed");
|
|
246
|
+
try {
|
|
247
|
+
if (window.parent !== window) {
|
|
248
|
+
window.parent.postMessage(
|
|
249
|
+
{
|
|
250
|
+
type: "component-selected",
|
|
251
|
+
component: {
|
|
252
|
+
name: label,
|
|
253
|
+
tagName: element.tagName,
|
|
254
|
+
styles: {
|
|
255
|
+
...styles
|
|
256
|
+
},
|
|
257
|
+
debugSource,
|
|
258
|
+
hasNonEditableText
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"*"
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.log("Could not notify extension:", error);
|
|
266
|
+
}
|
|
267
|
+
window.selectedComponentInfo = {
|
|
268
|
+
name: label,
|
|
269
|
+
element,
|
|
270
|
+
tagName: element.tagName
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Notify the extension about a text change
|
|
275
|
+
* @param element - The element that changed
|
|
276
|
+
* @param originalText - The original text
|
|
277
|
+
* @param newText - The new text
|
|
278
|
+
*/
|
|
279
|
+
notifyTextChange(element, originalText, newText) {
|
|
280
|
+
const label = getLabelFromSource(element);
|
|
281
|
+
const debugSource = getSourceFromDataAttributes(element);
|
|
282
|
+
try {
|
|
283
|
+
if (window.parent !== window) {
|
|
284
|
+
window.parent.postMessage(
|
|
285
|
+
{
|
|
286
|
+
type: "text-changed",
|
|
287
|
+
change: {
|
|
288
|
+
componentName: label,
|
|
289
|
+
tagName: element.tagName,
|
|
290
|
+
originalText,
|
|
291
|
+
newText,
|
|
292
|
+
debugSource
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
"*"
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
} catch (error) {
|
|
299
|
+
console.log("Could not notify extension about text change:", error);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Notify the parent window that interactions initialization is complete
|
|
304
|
+
*/
|
|
305
|
+
notifyInitializationComplete() {
|
|
306
|
+
try {
|
|
307
|
+
if (typeof window !== "undefined" && window.parent && window.parent !== window) {
|
|
308
|
+
window.parent.postMessage(
|
|
309
|
+
{
|
|
310
|
+
type: "interactions-initialized"
|
|
311
|
+
},
|
|
312
|
+
"*"
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
} catch (error) {
|
|
316
|
+
const err = error;
|
|
317
|
+
console.warn("Could not send initialization message to parent:", err.message);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
// src/design/interactions/componentMatcher.ts
|
|
323
|
+
var ComponentMatcher = class {
|
|
324
|
+
constructor(options = {}) {
|
|
325
|
+
__publicField(this, "allowlist");
|
|
326
|
+
this.allowlist = options.allowlist ?? [];
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Check whether an element contains compile-time source metadata attributes.
|
|
330
|
+
* @param element - The element to check
|
|
331
|
+
* @returns True if the data-source-file attribute is present
|
|
332
|
+
*/
|
|
333
|
+
hasSourceMetadata(element) {
|
|
334
|
+
if (!element) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return element.hasAttribute("data-source-file");
|
|
338
|
+
}
|
|
339
|
+
matchesList(element, selectors) {
|
|
340
|
+
return selectors.some((selector) => element.matches(selector));
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Checks if a label represents a component name (not a fallback like tag name, ID, or text content)
|
|
344
|
+
* @param label - The label to check
|
|
345
|
+
* @param tagName - The element's tag name (lowercase)
|
|
346
|
+
* @returns True if the label is a component name
|
|
347
|
+
*/
|
|
348
|
+
isComponentNameLabel(label, tagName) {
|
|
349
|
+
return label !== tagName && !label.includes("#") && !label.includes("(") && !label.includes('"');
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Check if an element is highlightable.
|
|
353
|
+
* @param element - The element to check
|
|
354
|
+
* @returns True if the element should be highlighted
|
|
355
|
+
*/
|
|
356
|
+
isHighlightableElement(element) {
|
|
357
|
+
if (!element || element === document.body || element === document.documentElement) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
if (!this.hasSourceMetadata(element)) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
if (this.allowlist.length > 0) {
|
|
364
|
+
return this.matchesList(element, this.allowlist);
|
|
365
|
+
}
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Find the nearest highlightable element by walking up the DOM tree
|
|
370
|
+
* @param target - The target element
|
|
371
|
+
* @returns The highlightable element or null
|
|
372
|
+
*/
|
|
373
|
+
findHighlightableElement(target) {
|
|
374
|
+
if (!target || target === document.body || target === document.documentElement) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
const closest = typeof target.closest === "function" ? target.closest("[data-source-file]") : null;
|
|
378
|
+
if (closest && closest !== document.body && closest !== document.documentElement && this.isHighlightableElement(closest)) {
|
|
379
|
+
return closest;
|
|
380
|
+
}
|
|
381
|
+
let current = target;
|
|
382
|
+
while (current && current !== document.body && current !== document.documentElement) {
|
|
383
|
+
if (this.isHighlightableElement(current)) {
|
|
384
|
+
return current;
|
|
385
|
+
}
|
|
386
|
+
current = current.parentElement;
|
|
387
|
+
}
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// src/design/interactions/eventHandlers.ts
|
|
393
|
+
var EventHandlers = class {
|
|
394
|
+
constructor(isInteractionsActive, componentMatcher, styleManager, editableManager, communicationManager) {
|
|
395
|
+
__publicField(this, "isInteractionsActive");
|
|
396
|
+
__publicField(this, "componentMatcher");
|
|
397
|
+
__publicField(this, "styleManager");
|
|
398
|
+
__publicField(this, "editableManager");
|
|
399
|
+
__publicField(this, "communicationManager");
|
|
400
|
+
__publicField(this, "currentHighlightedElements");
|
|
401
|
+
__publicField(this, "selectedElement");
|
|
402
|
+
__publicField(this, "selectedElements");
|
|
403
|
+
this.isInteractionsActive = isInteractionsActive;
|
|
404
|
+
this.componentMatcher = componentMatcher;
|
|
405
|
+
this.styleManager = styleManager;
|
|
406
|
+
this.editableManager = editableManager;
|
|
407
|
+
this.communicationManager = communicationManager;
|
|
408
|
+
this.currentHighlightedElements = [];
|
|
409
|
+
this.selectedElement = null;
|
|
410
|
+
this.selectedElements = [];
|
|
411
|
+
this.handleMouseOver = this.handleMouseOver.bind(this);
|
|
412
|
+
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
|
413
|
+
this.handleClick = this.handleClick.bind(this);
|
|
414
|
+
}
|
|
415
|
+
_findHighlightableElement(target) {
|
|
416
|
+
return this.componentMatcher.findHighlightableElement(target);
|
|
417
|
+
}
|
|
418
|
+
handleMouseOver(e) {
|
|
419
|
+
if (!this.isInteractionsActive()) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
e.stopPropagation();
|
|
423
|
+
const target = e.target;
|
|
424
|
+
if (target.nodeType !== 1 || target.tagName === "HTML" || target.tagName === "BODY") {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const element = this._findHighlightableElement(target);
|
|
428
|
+
if (!element) {
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
if (this.currentHighlightedElements.includes(element) || this.selectedElement && this.selectedElement === element) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
if (this.currentHighlightedElements.length > 0 && !this.currentHighlightedElements[0].classList.contains("design-mode-selected")) {
|
|
435
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
436
|
+
this.currentHighlightedElements = [];
|
|
437
|
+
}
|
|
438
|
+
const allElements = findElementsBySourceLocation(getSourceFromDataAttributes(element));
|
|
439
|
+
this.styleManager.highlightElements(allElements);
|
|
440
|
+
this.currentHighlightedElements = allElements;
|
|
441
|
+
}
|
|
442
|
+
handleMouseLeave() {
|
|
443
|
+
if (!this.isInteractionsActive()) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (this.currentHighlightedElements.length > 0 && !this.currentHighlightedElements[0].classList.contains("design-mode-selected")) {
|
|
447
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
448
|
+
this.currentHighlightedElements = [];
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
handleClick(e) {
|
|
452
|
+
if (!this.isInteractionsActive()) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
e.preventDefault();
|
|
456
|
+
e.stopPropagation();
|
|
457
|
+
const target = e.target;
|
|
458
|
+
if (target.nodeType !== 1 || target.tagName === "HTML" || target.tagName === "BODY") {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
const element = this._findHighlightableElement(target);
|
|
462
|
+
if (!element) {
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
if (this.selectedElement && this.selectedElement === element) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (this.selectedElement) {
|
|
469
|
+
this.styleManager.deselectElements(this.selectedElements);
|
|
470
|
+
this.editableManager.removeEditable(this.selectedElement);
|
|
471
|
+
}
|
|
472
|
+
if (this.currentHighlightedElements.length > 0) {
|
|
473
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
474
|
+
this.currentHighlightedElements = [];
|
|
475
|
+
}
|
|
476
|
+
this.selectedElement = element;
|
|
477
|
+
const allElements = findElementsBySourceLocation(getSourceFromDataAttributes(element));
|
|
478
|
+
this.selectedElements = allElements;
|
|
479
|
+
this.styleManager.selectElements(allElements);
|
|
480
|
+
this.editableManager.makeEditableIfText(element);
|
|
481
|
+
this.communicationManager.notifyComponentSelected(element);
|
|
482
|
+
}
|
|
483
|
+
clearAll() {
|
|
484
|
+
if (this.currentHighlightedElements.length > 0) {
|
|
485
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
486
|
+
this.currentHighlightedElements = [];
|
|
487
|
+
}
|
|
488
|
+
if (this.selectedElement) {
|
|
489
|
+
this.styleManager.deselectElements(this.selectedElements);
|
|
490
|
+
this.editableManager.removeEditable(this.selectedElement);
|
|
491
|
+
this.selectedElement = null;
|
|
492
|
+
this.selectedElements = [];
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
getSelectedElement() {
|
|
496
|
+
return this.selectedElement;
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// src/design/interactions/styleManager.ts
|
|
501
|
+
var StyleManager = class {
|
|
502
|
+
constructor() {
|
|
503
|
+
__publicField(this, "styleId");
|
|
504
|
+
__publicField(this, "stylesAdded");
|
|
505
|
+
this.styleId = "design-mode-styles";
|
|
506
|
+
this.stylesAdded = false;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Add CSS styles for highlighting to the document
|
|
510
|
+
*/
|
|
511
|
+
addHighlightStyles() {
|
|
512
|
+
if (this.stylesAdded) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const style = document.createElement("style");
|
|
516
|
+
style.id = this.styleId;
|
|
517
|
+
style.textContent = this._getStyles();
|
|
518
|
+
document.head.appendChild(style);
|
|
519
|
+
this.stylesAdded = true;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Remove highlight styles from the document
|
|
523
|
+
*/
|
|
524
|
+
removeHighlightStyles() {
|
|
525
|
+
const style = document.getElementById(this.styleId);
|
|
526
|
+
if (style) {
|
|
527
|
+
style.remove();
|
|
528
|
+
this.stylesAdded = false;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Get the CSS styles for highlighting
|
|
533
|
+
* @private
|
|
534
|
+
* @returns CSS styles
|
|
535
|
+
*/
|
|
536
|
+
_getStyles() {
|
|
537
|
+
return `
|
|
538
|
+
.design-mode-highlight {
|
|
539
|
+
outline: 4px dashed #007acc !important;
|
|
540
|
+
outline-offset: -4px !important;
|
|
541
|
+
transition: all 0.2s ease !important;
|
|
542
|
+
position: relative !important;
|
|
543
|
+
cursor: pointer !important;
|
|
544
|
+
}
|
|
545
|
+
.design-mode-selected {
|
|
546
|
+
outline: 5px dashed #ff6b35 !important;
|
|
547
|
+
outline-offset: -5px !important;
|
|
548
|
+
position: relative !important;
|
|
549
|
+
}
|
|
550
|
+
`;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Apply highlight class to one or more elements
|
|
554
|
+
* @param elements - The elements to highlight
|
|
555
|
+
*/
|
|
556
|
+
highlightElements(elements) {
|
|
557
|
+
for (const el of elements) {
|
|
558
|
+
el.classList.add("design-mode-highlight");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Remove highlight class from one or more elements
|
|
563
|
+
* @param elements - The elements to unhighlight
|
|
564
|
+
*/
|
|
565
|
+
unhighlightElements(elements) {
|
|
566
|
+
for (const el of elements) {
|
|
567
|
+
el.classList.remove("design-mode-highlight");
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Apply selected class to one or more elements
|
|
572
|
+
* @param elements - The elements to select
|
|
573
|
+
*/
|
|
574
|
+
selectElements(elements) {
|
|
575
|
+
for (const el of elements) {
|
|
576
|
+
el.classList.add("design-mode-selected");
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Remove selected class from one or more elements
|
|
581
|
+
* @param elements - The elements to deselect
|
|
582
|
+
*/
|
|
583
|
+
deselectElements(elements) {
|
|
584
|
+
for (const el of elements) {
|
|
585
|
+
el.classList.remove("design-mode-selected");
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// src/design/interactions/interactionsController.ts
|
|
591
|
+
var InteractionsController = class {
|
|
592
|
+
constructor(enabled = true) {
|
|
593
|
+
__publicField(this, "enabled");
|
|
594
|
+
__publicField(this, "isActive");
|
|
595
|
+
__publicField(this, "componentMatcher");
|
|
596
|
+
__publicField(this, "styleManager");
|
|
597
|
+
__publicField(this, "communicationManager");
|
|
598
|
+
__publicField(this, "editableManager");
|
|
599
|
+
__publicField(this, "eventHandlers");
|
|
600
|
+
this.enabled = enabled;
|
|
601
|
+
this.isActive = false;
|
|
602
|
+
this.componentMatcher = new ComponentMatcher({
|
|
603
|
+
allowlist: [
|
|
604
|
+
"div",
|
|
605
|
+
"p",
|
|
606
|
+
"span",
|
|
607
|
+
"h1",
|
|
608
|
+
"h2",
|
|
609
|
+
"h3",
|
|
610
|
+
"h4",
|
|
611
|
+
"h5",
|
|
612
|
+
"h6",
|
|
613
|
+
"a",
|
|
614
|
+
"button",
|
|
615
|
+
"input",
|
|
616
|
+
"select",
|
|
617
|
+
"textarea",
|
|
618
|
+
"label",
|
|
619
|
+
"section",
|
|
620
|
+
"article",
|
|
621
|
+
"main",
|
|
622
|
+
"aside",
|
|
623
|
+
"header",
|
|
624
|
+
"footer",
|
|
625
|
+
"nav",
|
|
626
|
+
"figure",
|
|
627
|
+
"figcaption",
|
|
628
|
+
"ul",
|
|
629
|
+
"ol",
|
|
630
|
+
"li",
|
|
631
|
+
"table",
|
|
632
|
+
"tr",
|
|
633
|
+
"td",
|
|
634
|
+
"th",
|
|
635
|
+
"blockquote",
|
|
636
|
+
"img"
|
|
637
|
+
]
|
|
638
|
+
});
|
|
639
|
+
this.styleManager = new StyleManager();
|
|
640
|
+
this.communicationManager = new CommunicationManager();
|
|
641
|
+
this.editableManager = new EditableManager(this.communicationManager);
|
|
642
|
+
this.eventHandlers = new EventHandlers(
|
|
643
|
+
() => this.isActive,
|
|
644
|
+
this.componentMatcher,
|
|
645
|
+
this.styleManager,
|
|
646
|
+
this.editableManager,
|
|
647
|
+
this.communicationManager
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Initialize the design mode interactions
|
|
652
|
+
*/
|
|
653
|
+
initialize() {
|
|
654
|
+
if (!this.enabled) {
|
|
655
|
+
console.log("Design Mode Interactions disabled");
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
console.log("Initializing Design Mode Interactions...");
|
|
659
|
+
this.styleManager.addHighlightStyles();
|
|
660
|
+
document.addEventListener("mouseover", this.eventHandlers.handleMouseOver);
|
|
661
|
+
document.addEventListener("mouseleave", this.eventHandlers.handleMouseLeave);
|
|
662
|
+
document.addEventListener("click", this.eventHandlers.handleClick, true);
|
|
663
|
+
console.log("Design Mode Interactions initialized!");
|
|
664
|
+
this.communicationManager.notifyInitializationComplete();
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Enable the design mode interactions
|
|
668
|
+
*/
|
|
669
|
+
enable() {
|
|
670
|
+
this.isActive = true;
|
|
671
|
+
console.log("Design Mode Interactions enabled");
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Disable the design mode interactions
|
|
675
|
+
*/
|
|
676
|
+
disable() {
|
|
677
|
+
this.isActive = false;
|
|
678
|
+
this.eventHandlers.clearAll();
|
|
679
|
+
console.log("Design Mode Interactions disabled");
|
|
680
|
+
}
|
|
681
|
+
resolveTargets(sourceLocation) {
|
|
682
|
+
let location = sourceLocation ?? null;
|
|
683
|
+
if (!location?.fileName) {
|
|
684
|
+
const selectedElement = this.eventHandlers.getSelectedElement();
|
|
685
|
+
location = getSourceFromDataAttributes(selectedElement);
|
|
686
|
+
}
|
|
687
|
+
return location ? findElementsBySourceLocation(location) : [];
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Apply a style change to all elements at the given source location.
|
|
691
|
+
* When sourceLocation is provided (undo/redo), it is used directly.
|
|
692
|
+
* Otherwise the source location is read from the currently selected element.
|
|
693
|
+
*/
|
|
694
|
+
applyStyleChange(property, value, sourceLocation) {
|
|
695
|
+
for (const el of this.resolveTargets(sourceLocation)) {
|
|
696
|
+
el.style[property] = value;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
applyTextChange(text, sourceLocation) {
|
|
700
|
+
for (const el of this.resolveTargets(sourceLocation)) {
|
|
701
|
+
el.textContent = text;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Cleanup and remove event listeners
|
|
706
|
+
*/
|
|
707
|
+
destroy() {
|
|
708
|
+
document.removeEventListener("mouseover", this.eventHandlers.handleMouseOver);
|
|
709
|
+
document.removeEventListener("mouseleave", this.eventHandlers.handleMouseLeave);
|
|
710
|
+
document.removeEventListener("click", this.eventHandlers.handleClick, true);
|
|
711
|
+
this.styleManager.removeHighlightStyles();
|
|
712
|
+
this.eventHandlers.clearAll();
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
// src/design/interactions/index.ts
|
|
717
|
+
var interactions = new InteractionsController(true);
|
|
718
|
+
if (typeof document !== "undefined") {
|
|
719
|
+
if (document.readyState === "loading") {
|
|
720
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
721
|
+
interactions.initialize();
|
|
722
|
+
});
|
|
723
|
+
} else {
|
|
724
|
+
interactions.initialize();
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (typeof window !== "undefined") {
|
|
728
|
+
window.enableInteractions = function() {
|
|
729
|
+
interactions.enable();
|
|
730
|
+
};
|
|
731
|
+
window.disableInteractions = function() {
|
|
732
|
+
interactions.disable();
|
|
733
|
+
};
|
|
734
|
+
window.addEventListener("message", function(event) {
|
|
735
|
+
const data = event.data;
|
|
736
|
+
const typed = data && typeof data === "object" ? data : null;
|
|
737
|
+
if (typed && typed.type === "style-change") {
|
|
738
|
+
interactions.applyStyleChange(
|
|
739
|
+
String(typed.property ?? ""),
|
|
740
|
+
String(typed.value ?? ""),
|
|
741
|
+
parseSourceLocation(typed.sourceLocation)
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
if (typed && typed.type === "text-change") {
|
|
745
|
+
interactions.applyTextChange(
|
|
746
|
+
String(typed.text ?? ""),
|
|
747
|
+
parseSourceLocation(typed.sourceLocation)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
if (typed && typed.type === "enable-interactions") {
|
|
751
|
+
window.enableInteractions?.();
|
|
752
|
+
}
|
|
753
|
+
if (typed && typed.type === "disable-interactions") {
|
|
754
|
+
window.disableInteractions?.();
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
})();
|
|
759
|
+
|
|
760
|
+
})();
|
|
761
|
+
|