@salesforce/webapp-experimental 1.47.0 → 1.48.0
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/design/design-mode-interactions.js +649 -0
- package/dist/design/index.d.ts +12 -0
- package/dist/design/index.d.ts.map +1 -0
- package/dist/design/index.js +21 -0
- package/dist/design/interactions/communicationManager.d.ts +25 -0
- package/dist/design/interactions/communicationManager.d.ts.map +1 -0
- package/dist/design/interactions/communicationManager.js +112 -0
- package/dist/design/interactions/componentMatcher.d.ts +37 -0
- package/dist/design/interactions/componentMatcher.d.ts.map +1 -0
- package/dist/design/interactions/componentMatcher.js +68 -0
- package/dist/design/interactions/editableManager.d.ts +51 -0
- package/dist/design/interactions/editableManager.d.ts.map +1 -0
- package/dist/design/interactions/editableManager.js +90 -0
- package/dist/design/interactions/eventHandlers.d.ts +66 -0
- package/dist/design/interactions/eventHandlers.d.ts.map +1 -0
- package/dist/design/interactions/eventHandlers.js +136 -0
- package/dist/design/interactions/index.d.ts +7 -0
- package/dist/design/interactions/index.d.ts.map +1 -0
- package/dist/design/interactions/index.js +45 -0
- package/dist/design/interactions/interactionsController.d.ts +38 -0
- package/dist/design/interactions/interactionsController.d.ts.map +1 -0
- package/dist/design/interactions/interactionsController.js +86 -0
- package/dist/design/interactions/styleManager.d.ts +49 -0
- package/dist/design/interactions/styleManager.d.ts.map +1 -0
- package/dist/design/interactions/styleManager.js +89 -0
- package/dist/design/interactions/utils/cssUtils.d.ts +22 -0
- package/dist/design/interactions/utils/cssUtils.d.ts.map +1 -0
- package/dist/design/interactions/utils/cssUtils.js +46 -0
- package/dist/design/interactions/utils/sourceUtils.d.ts +23 -0
- package/dist/design/interactions/utils/sourceUtils.d.ts.map +1 -0
- package/dist/design/interactions/utils/sourceUtils.js +64 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +10 -4
|
@@ -0,0 +1,649 @@
|
|
|
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/cssUtils.ts
|
|
16
|
+
function parsePixelValue(value) {
|
|
17
|
+
if (!value || typeof value !== "string") {
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
const match = value.match(/^(\d+(?:\.\d+)?)px$/);
|
|
21
|
+
const numberPart = match?.[1];
|
|
22
|
+
return numberPart ? Number.parseFloat(numberPart) : 0;
|
|
23
|
+
}
|
|
24
|
+
function getElementStyles(element) {
|
|
25
|
+
if (!element) {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
const computed = window.getComputedStyle(element);
|
|
29
|
+
const px = (prop) => parsePixelValue(computed[prop] || "");
|
|
30
|
+
return {
|
|
31
|
+
paddingTop: px("paddingTop"),
|
|
32
|
+
paddingRight: px("paddingRight"),
|
|
33
|
+
paddingBottom: px("paddingBottom"),
|
|
34
|
+
paddingLeft: px("paddingLeft"),
|
|
35
|
+
marginTop: px("marginTop"),
|
|
36
|
+
marginRight: px("marginRight"),
|
|
37
|
+
marginBottom: px("marginBottom"),
|
|
38
|
+
marginLeft: px("marginLeft"),
|
|
39
|
+
backgroundColor: computed.backgroundColor,
|
|
40
|
+
fontFamily: computed.fontFamily
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/design/interactions/utils/sourceUtils.ts
|
|
45
|
+
function parseOptionalInt(value) {
|
|
46
|
+
if (value === null || value === void 0 || value === "") {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
50
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
51
|
+
}
|
|
52
|
+
function parseSourceFileAttribute(value) {
|
|
53
|
+
const match = /^(.*):(\d+):(\d+)$/.exec(value);
|
|
54
|
+
if (!match) {
|
|
55
|
+
return { fileName: value, lineNumber: null, columnNumber: null };
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
fileName: match[1] ?? value,
|
|
59
|
+
lineNumber: parseOptionalInt(match[2]),
|
|
60
|
+
columnNumber: parseOptionalInt(match[3])
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function getSourceFromDataAttributes(element) {
|
|
64
|
+
if (!element) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const source = element.getAttribute("data-source-file") || null;
|
|
68
|
+
if (!source) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return parseSourceFileAttribute(source);
|
|
72
|
+
}
|
|
73
|
+
function getLabelFromSource(element) {
|
|
74
|
+
if (!element) {
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
const source = element.getAttribute("data-source-file");
|
|
78
|
+
if (!source) {
|
|
79
|
+
return element.tagName ? element.tagName.toLowerCase() : "";
|
|
80
|
+
}
|
|
81
|
+
const { fileName } = parseSourceFileAttribute(source);
|
|
82
|
+
const parts = fileName.split(/[/\\]/);
|
|
83
|
+
const baseName = parts[parts.length - 1] || fileName;
|
|
84
|
+
console.log("baseName", baseName);
|
|
85
|
+
return baseName;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/design/interactions/communicationManager.ts
|
|
89
|
+
var CommunicationManager = class {
|
|
90
|
+
constructor() {
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Notify the extension about a selected component
|
|
94
|
+
* @param element - The selected element
|
|
95
|
+
*/
|
|
96
|
+
notifyComponentSelected(element) {
|
|
97
|
+
const label = getLabelFromSource(element);
|
|
98
|
+
const wasSelected = element.classList.contains("design-mode-selected");
|
|
99
|
+
if (wasSelected) {
|
|
100
|
+
element.classList.remove("design-mode-selected");
|
|
101
|
+
}
|
|
102
|
+
const styles = getElementStyles(element);
|
|
103
|
+
if (wasSelected) {
|
|
104
|
+
element.classList.add("design-mode-selected");
|
|
105
|
+
}
|
|
106
|
+
const debugSource = getSourceFromDataAttributes(element);
|
|
107
|
+
try {
|
|
108
|
+
if (window.parent !== window) {
|
|
109
|
+
window.parent.postMessage(
|
|
110
|
+
{
|
|
111
|
+
type: "component-selected",
|
|
112
|
+
component: {
|
|
113
|
+
name: label,
|
|
114
|
+
element: {
|
|
115
|
+
tagName: element.tagName,
|
|
116
|
+
classList: Array.from(element.classList),
|
|
117
|
+
id: element.id || ""
|
|
118
|
+
},
|
|
119
|
+
styles: {
|
|
120
|
+
...styles
|
|
121
|
+
},
|
|
122
|
+
debugSource
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"*"
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.log("Could not notify extension:", error);
|
|
130
|
+
}
|
|
131
|
+
window.selectedComponentInfo = {
|
|
132
|
+
name: label,
|
|
133
|
+
element,
|
|
134
|
+
classList: Array.from(element.classList),
|
|
135
|
+
id: element.id || "no-id",
|
|
136
|
+
tagName: element.tagName
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Notify the extension about a text change
|
|
141
|
+
* @param element - The element that changed
|
|
142
|
+
* @param originalText - The original text
|
|
143
|
+
* @param newText - The new text
|
|
144
|
+
*/
|
|
145
|
+
notifyTextChange(element, originalText, newText) {
|
|
146
|
+
const label = getLabelFromSource(element);
|
|
147
|
+
try {
|
|
148
|
+
if (window.parent !== window) {
|
|
149
|
+
window.parent.postMessage(
|
|
150
|
+
{
|
|
151
|
+
type: "text-changed",
|
|
152
|
+
change: {
|
|
153
|
+
component: { name: label },
|
|
154
|
+
element: {
|
|
155
|
+
tagName: element.tagName,
|
|
156
|
+
classList: Array.from(element.classList),
|
|
157
|
+
id: element.id || "",
|
|
158
|
+
originalText,
|
|
159
|
+
newText
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"*"
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.log("Could not notify extension about text change:", error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Notify the parent window that interactions initialization is complete
|
|
172
|
+
*/
|
|
173
|
+
notifyInitializationComplete() {
|
|
174
|
+
try {
|
|
175
|
+
if (typeof window !== "undefined" && window.parent && window.parent !== window) {
|
|
176
|
+
window.parent.postMessage(
|
|
177
|
+
{
|
|
178
|
+
type: "interactions-initialized"
|
|
179
|
+
},
|
|
180
|
+
"*"
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
const err = error;
|
|
185
|
+
console.warn("Could not send initialization message to parent:", err.message);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/design/interactions/componentMatcher.ts
|
|
191
|
+
var ComponentMatcher = class {
|
|
192
|
+
/**
|
|
193
|
+
* Check whether an element contains compile-time source metadata attributes.
|
|
194
|
+
* @param element - The element to check
|
|
195
|
+
* @returns True if the data-source-file attribute is present
|
|
196
|
+
*/
|
|
197
|
+
hasSourceMetadata(element) {
|
|
198
|
+
if (!element) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return element.hasAttribute("data-source-file");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Checks if a label represents a component name (not a fallback like tag name, ID, or text content)
|
|
205
|
+
* @param label - The label to check
|
|
206
|
+
* @param tagName - The element's tag name (lowercase)
|
|
207
|
+
* @returns True if the label is a component name
|
|
208
|
+
*/
|
|
209
|
+
isComponentNameLabel(label, tagName) {
|
|
210
|
+
return label !== tagName && !label.includes("#") && !label.includes("(") && !label.includes('"');
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Check if an element is highlightable.
|
|
214
|
+
* @param element - The element to check
|
|
215
|
+
* @returns True if the element should be highlighted
|
|
216
|
+
*/
|
|
217
|
+
isHighlightableElement(element) {
|
|
218
|
+
if (!element || element === document.body || element === document.documentElement) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
return this.hasSourceMetadata(element);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Find the nearest highlightable element by walking up the DOM tree
|
|
225
|
+
* @param target - The target element
|
|
226
|
+
* @returns The highlightable element or null
|
|
227
|
+
*/
|
|
228
|
+
findHighlightableElement(target) {
|
|
229
|
+
if (!target || target === document.body || target === document.documentElement) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
const closest = typeof target.closest === "function" ? target.closest("[data-source-file]") : null;
|
|
233
|
+
if (closest && closest !== document.body && closest !== document.documentElement) {
|
|
234
|
+
return closest;
|
|
235
|
+
}
|
|
236
|
+
let current = target;
|
|
237
|
+
while (current && current !== document.body && current !== document.documentElement) {
|
|
238
|
+
if (this.isHighlightableElement(current)) {
|
|
239
|
+
return current;
|
|
240
|
+
}
|
|
241
|
+
current = current.parentElement;
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
// src/design/interactions/editableManager.ts
|
|
248
|
+
var EditableManager = class {
|
|
249
|
+
constructor(communicationManager) {
|
|
250
|
+
__publicField(this, "communicationManager");
|
|
251
|
+
__publicField(this, "textTags");
|
|
252
|
+
__publicField(this, "boundHandleBlur");
|
|
253
|
+
__publicField(this, "boundHandleKeydown");
|
|
254
|
+
this.communicationManager = communicationManager;
|
|
255
|
+
this.textTags = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "SPAN", "A", "BUTTON", "LABEL"];
|
|
256
|
+
this.boundHandleBlur = this._handleBlur.bind(this);
|
|
257
|
+
this.boundHandleKeydown = this._handleKeydown.bind(this);
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Make an element editable if it's a text element
|
|
261
|
+
* @param element - The element to make editable
|
|
262
|
+
*/
|
|
263
|
+
makeEditableIfText(element) {
|
|
264
|
+
if (!this._isTextElement(element)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
element.contentEditable = "true";
|
|
268
|
+
element.dataset.originalText = element.textContent ?? "";
|
|
269
|
+
element.addEventListener("blur", this.boundHandleBlur);
|
|
270
|
+
element.addEventListener("keydown", this.boundHandleKeydown);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Remove editable state from an element
|
|
274
|
+
* @param element - The element to make non-editable
|
|
275
|
+
*/
|
|
276
|
+
removeEditable(element) {
|
|
277
|
+
if (element.contentEditable === "true") {
|
|
278
|
+
element.contentEditable = "false";
|
|
279
|
+
}
|
|
280
|
+
delete element.dataset.originalText;
|
|
281
|
+
element.removeEventListener("blur", this.boundHandleBlur);
|
|
282
|
+
element.removeEventListener("keydown", this.boundHandleKeydown);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Check if an element is a text element that can be made editable
|
|
286
|
+
* @private
|
|
287
|
+
* @param element - The element to check
|
|
288
|
+
* @returns True if the element can be made editable
|
|
289
|
+
*/
|
|
290
|
+
_isTextElement(element) {
|
|
291
|
+
return this.textTags.includes(element.tagName) && (element.textContent ?? "").trim().length > 0 && element.dataset.textType === "static";
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Handle blur event on editable element
|
|
295
|
+
* @private
|
|
296
|
+
* @param e - The blur event
|
|
297
|
+
*/
|
|
298
|
+
_handleBlur(e) {
|
|
299
|
+
const element = e.target;
|
|
300
|
+
const newText = element.textContent ?? "";
|
|
301
|
+
const originalText = element.dataset.originalText ?? "";
|
|
302
|
+
if (newText !== originalText) {
|
|
303
|
+
element.dataset.originalText = newText;
|
|
304
|
+
if (this.communicationManager) {
|
|
305
|
+
this.communicationManager.notifyTextChange(element, originalText, newText);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
this.removeEditable(element);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Handle keydown event on editable element
|
|
312
|
+
* @private
|
|
313
|
+
* @param e - The keydown event
|
|
314
|
+
*/
|
|
315
|
+
_handleKeydown(e) {
|
|
316
|
+
const element = e.target;
|
|
317
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
318
|
+
e.preventDefault();
|
|
319
|
+
element.blur();
|
|
320
|
+
}
|
|
321
|
+
if (e.key === "Escape") {
|
|
322
|
+
if (element.dataset.originalText) {
|
|
323
|
+
element.textContent = element.dataset.originalText;
|
|
324
|
+
}
|
|
325
|
+
element.blur();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// src/design/interactions/eventHandlers.ts
|
|
331
|
+
var EventHandlers = class {
|
|
332
|
+
constructor(isInteractionsActive, componentMatcher, styleManager, editableManager, communicationManager) {
|
|
333
|
+
__publicField(this, "isInteractionsActive");
|
|
334
|
+
__publicField(this, "componentMatcher");
|
|
335
|
+
__publicField(this, "styleManager");
|
|
336
|
+
__publicField(this, "editableManager");
|
|
337
|
+
__publicField(this, "communicationManager");
|
|
338
|
+
__publicField(this, "currentHighlighted");
|
|
339
|
+
__publicField(this, "selectedElement");
|
|
340
|
+
this.isInteractionsActive = isInteractionsActive;
|
|
341
|
+
this.componentMatcher = componentMatcher;
|
|
342
|
+
this.styleManager = styleManager;
|
|
343
|
+
this.editableManager = editableManager;
|
|
344
|
+
this.communicationManager = communicationManager;
|
|
345
|
+
this.currentHighlighted = null;
|
|
346
|
+
this.selectedElement = null;
|
|
347
|
+
this.handleMouseOver = this.handleMouseOver.bind(this);
|
|
348
|
+
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
|
349
|
+
this.handleClick = this.handleClick.bind(this);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Find the nearest highlightable element by walking up the DOM tree
|
|
353
|
+
* @param target - The target element
|
|
354
|
+
* @returns The highlightable element or null
|
|
355
|
+
*/
|
|
356
|
+
_findHighlightableElement(target) {
|
|
357
|
+
return this.componentMatcher.findHighlightableElement(target);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Handle mouseover event
|
|
361
|
+
* @param e - The mouseover event
|
|
362
|
+
*/
|
|
363
|
+
handleMouseOver(e) {
|
|
364
|
+
if (!this.isInteractionsActive()) {
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
e.stopPropagation();
|
|
368
|
+
const target = e.target;
|
|
369
|
+
if (target.nodeType !== 1 || target.tagName === "HTML" || target.tagName === "BODY") {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const element = this._findHighlightableElement(target);
|
|
373
|
+
if (!element) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (this.currentHighlighted === element || this.selectedElement && this.selectedElement === element) {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (this.currentHighlighted && !this.currentHighlighted.classList.contains("design-mode-selected")) {
|
|
380
|
+
this.styleManager.unhighlightElement(this.currentHighlighted);
|
|
381
|
+
}
|
|
382
|
+
this.styleManager.highlightElement(element);
|
|
383
|
+
this.currentHighlighted = element;
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Handle mouseleave event
|
|
387
|
+
*/
|
|
388
|
+
handleMouseLeave() {
|
|
389
|
+
if (!this.isInteractionsActive()) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (this.currentHighlighted && !this.currentHighlighted.classList.contains("design-mode-selected")) {
|
|
393
|
+
this.styleManager.unhighlightElement(this.currentHighlighted);
|
|
394
|
+
this.currentHighlighted = null;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Handle click event
|
|
399
|
+
* @param e - The click event
|
|
400
|
+
*/
|
|
401
|
+
handleClick(e) {
|
|
402
|
+
if (!this.isInteractionsActive()) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
e.preventDefault();
|
|
406
|
+
e.stopPropagation();
|
|
407
|
+
const target = e.target;
|
|
408
|
+
if (target.nodeType !== 1 || target.tagName === "HTML" || target.tagName === "BODY") {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const element = this._findHighlightableElement(target);
|
|
412
|
+
if (!element) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (this.selectedElement && this.selectedElement === element) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
if (this.selectedElement) {
|
|
419
|
+
this.styleManager.deselectElement(this.selectedElement);
|
|
420
|
+
this.editableManager.removeEditable(this.selectedElement);
|
|
421
|
+
}
|
|
422
|
+
if (this.currentHighlighted) {
|
|
423
|
+
this.styleManager.unhighlightElement(this.currentHighlighted);
|
|
424
|
+
this.currentHighlighted = null;
|
|
425
|
+
}
|
|
426
|
+
this.selectedElement = element;
|
|
427
|
+
this.styleManager.selectElement(element);
|
|
428
|
+
this.editableManager.makeEditableIfText(element);
|
|
429
|
+
this.communicationManager.notifyComponentSelected(element);
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Clear all highlights and selections
|
|
433
|
+
*/
|
|
434
|
+
clearAll() {
|
|
435
|
+
if (this.currentHighlighted) {
|
|
436
|
+
this.styleManager.unhighlightElement(this.currentHighlighted);
|
|
437
|
+
this.currentHighlighted = null;
|
|
438
|
+
}
|
|
439
|
+
if (this.selectedElement) {
|
|
440
|
+
this.styleManager.deselectElement(this.selectedElement);
|
|
441
|
+
this.editableManager.removeEditable(this.selectedElement);
|
|
442
|
+
this.selectedElement = null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Get the currently selected element
|
|
447
|
+
* @returns The selected element
|
|
448
|
+
*/
|
|
449
|
+
getSelectedElement() {
|
|
450
|
+
return this.selectedElement;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
// src/design/interactions/styleManager.ts
|
|
455
|
+
var StyleManager = class {
|
|
456
|
+
constructor() {
|
|
457
|
+
__publicField(this, "styleId");
|
|
458
|
+
__publicField(this, "stylesAdded");
|
|
459
|
+
this.styleId = "design-mode-styles";
|
|
460
|
+
this.stylesAdded = false;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Add CSS styles for highlighting to the document
|
|
464
|
+
*/
|
|
465
|
+
addHighlightStyles() {
|
|
466
|
+
if (this.stylesAdded) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const style = document.createElement("style");
|
|
470
|
+
style.id = this.styleId;
|
|
471
|
+
style.textContent = this._getStyles();
|
|
472
|
+
document.head.appendChild(style);
|
|
473
|
+
this.stylesAdded = true;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Remove highlight styles from the document
|
|
477
|
+
*/
|
|
478
|
+
removeHighlightStyles() {
|
|
479
|
+
const style = document.getElementById(this.styleId);
|
|
480
|
+
if (style) {
|
|
481
|
+
style.remove();
|
|
482
|
+
this.stylesAdded = false;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Get the CSS styles for highlighting
|
|
487
|
+
* @private
|
|
488
|
+
* @returns CSS styles
|
|
489
|
+
*/
|
|
490
|
+
_getStyles() {
|
|
491
|
+
return `
|
|
492
|
+
.design-mode-highlight {
|
|
493
|
+
outline: 4px dashed #007acc !important;
|
|
494
|
+
outline-offset: -4px !important;
|
|
495
|
+
transition: all 0.2s ease !important;
|
|
496
|
+
position: relative !important;
|
|
497
|
+
cursor: pointer !important;
|
|
498
|
+
}
|
|
499
|
+
.design-mode-selected {
|
|
500
|
+
outline: 5px dashed #ff6b35 !important;
|
|
501
|
+
outline-offset: -5px !important;
|
|
502
|
+
position: relative !important;
|
|
503
|
+
}
|
|
504
|
+
`;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Apply highlight class to an element
|
|
508
|
+
* @param element - The element to highlight
|
|
509
|
+
*/
|
|
510
|
+
highlightElement(element) {
|
|
511
|
+
element.classList.add("design-mode-highlight");
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Remove highlight class from an element
|
|
515
|
+
* @param element - The element to unhighlight
|
|
516
|
+
*/
|
|
517
|
+
unhighlightElement(element) {
|
|
518
|
+
element.classList.remove("design-mode-highlight");
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Apply selected class to an element
|
|
522
|
+
* @param element - The element to select
|
|
523
|
+
*/
|
|
524
|
+
selectElement(element) {
|
|
525
|
+
element.classList.add("design-mode-selected");
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Remove selected class from an element
|
|
529
|
+
* @param element - The element to deselect
|
|
530
|
+
*/
|
|
531
|
+
deselectElement(element) {
|
|
532
|
+
element.classList.remove("design-mode-selected");
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// src/design/interactions/interactionsController.ts
|
|
537
|
+
var InteractionsController = class {
|
|
538
|
+
constructor(enabled = true) {
|
|
539
|
+
__publicField(this, "enabled");
|
|
540
|
+
__publicField(this, "isActive");
|
|
541
|
+
__publicField(this, "componentMatcher");
|
|
542
|
+
__publicField(this, "styleManager");
|
|
543
|
+
__publicField(this, "communicationManager");
|
|
544
|
+
__publicField(this, "editableManager");
|
|
545
|
+
__publicField(this, "eventHandlers");
|
|
546
|
+
this.enabled = enabled;
|
|
547
|
+
this.isActive = false;
|
|
548
|
+
this.componentMatcher = new ComponentMatcher();
|
|
549
|
+
this.styleManager = new StyleManager();
|
|
550
|
+
this.communicationManager = new CommunicationManager();
|
|
551
|
+
this.editableManager = new EditableManager(this.communicationManager);
|
|
552
|
+
this.eventHandlers = new EventHandlers(
|
|
553
|
+
() => this.isActive,
|
|
554
|
+
this.componentMatcher,
|
|
555
|
+
this.styleManager,
|
|
556
|
+
this.editableManager,
|
|
557
|
+
this.communicationManager
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Initialize the design mode interactions
|
|
562
|
+
*/
|
|
563
|
+
initialize() {
|
|
564
|
+
if (!this.enabled) {
|
|
565
|
+
console.log("Design Mode Interactions disabled");
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
console.log("Initializing Design Mode Interactions...");
|
|
569
|
+
this.styleManager.addHighlightStyles();
|
|
570
|
+
document.addEventListener("mouseover", this.eventHandlers.handleMouseOver);
|
|
571
|
+
document.addEventListener("mouseleave", this.eventHandlers.handleMouseLeave);
|
|
572
|
+
document.addEventListener("click", this.eventHandlers.handleClick, true);
|
|
573
|
+
console.log("Design Mode Interactions initialized!");
|
|
574
|
+
this.communicationManager.notifyInitializationComplete();
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Enable the design mode interactions
|
|
578
|
+
*/
|
|
579
|
+
enable() {
|
|
580
|
+
this.isActive = true;
|
|
581
|
+
console.log("Design Mode Interactions enabled");
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Disable the design mode interactions
|
|
585
|
+
*/
|
|
586
|
+
disable() {
|
|
587
|
+
this.isActive = false;
|
|
588
|
+
this.eventHandlers.clearAll();
|
|
589
|
+
console.log("Design Mode Interactions disabled");
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Apply style changes to the selected element
|
|
593
|
+
* @param property - CSS property name
|
|
594
|
+
* @param value - CSS property value
|
|
595
|
+
*/
|
|
596
|
+
applyStyleChange(property, value) {
|
|
597
|
+
const selectedElement = this.eventHandlers.getSelectedElement();
|
|
598
|
+
if (selectedElement) {
|
|
599
|
+
selectedElement.style[property] = value;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Cleanup and remove event listeners
|
|
604
|
+
*/
|
|
605
|
+
destroy() {
|
|
606
|
+
document.removeEventListener("mouseover", this.eventHandlers.handleMouseOver);
|
|
607
|
+
document.removeEventListener("mouseleave", this.eventHandlers.handleMouseLeave);
|
|
608
|
+
document.removeEventListener("click", this.eventHandlers.handleClick, true);
|
|
609
|
+
this.styleManager.removeHighlightStyles();
|
|
610
|
+
this.eventHandlers.clearAll();
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
// src/design/interactions/index.ts
|
|
615
|
+
var interactions = new InteractionsController(true);
|
|
616
|
+
if (typeof document !== "undefined") {
|
|
617
|
+
if (document.readyState === "loading") {
|
|
618
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
619
|
+
interactions.initialize();
|
|
620
|
+
});
|
|
621
|
+
} else {
|
|
622
|
+
interactions.initialize();
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (typeof window !== "undefined") {
|
|
626
|
+
window.enableInteractions = function() {
|
|
627
|
+
interactions.enable();
|
|
628
|
+
};
|
|
629
|
+
window.disableInteractions = function() {
|
|
630
|
+
interactions.disable();
|
|
631
|
+
};
|
|
632
|
+
window.addEventListener("message", function(event) {
|
|
633
|
+
const data = event.data;
|
|
634
|
+
const typed = data && typeof data === "object" ? data : null;
|
|
635
|
+
if (typed && typed.type === "style-change") {
|
|
636
|
+
interactions.applyStyleChange(String(typed.property ?? ""), String(typed.value ?? ""));
|
|
637
|
+
}
|
|
638
|
+
if (typed && typed.type === "enable-interactions") {
|
|
639
|
+
window.enableInteractions?.();
|
|
640
|
+
}
|
|
641
|
+
if (typed && typed.type === "disable-interactions") {
|
|
642
|
+
window.disableInteractions?.();
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
})();
|
|
647
|
+
|
|
648
|
+
})();
|
|
649
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Read the bundled design mode interactions script content.
|
|
8
|
+
* @returns The script content, or null if the file does not exist (e.g. build:design not run)
|
|
9
|
+
* @throws Error if the file exists but cannot be read
|
|
10
|
+
*/
|
|
11
|
+
export declare function getDesignModeScriptContent(): string | null;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/design/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,GAAG,IAAI,CAK1D"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
7
|
+
import { join, dirname } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const DESIGN_MODE_SCRIPT_PATH = join(__dirname, "../../dist/design/design-mode-interactions.js");
|
|
11
|
+
/**
|
|
12
|
+
* Read the bundled design mode interactions script content.
|
|
13
|
+
* @returns The script content, or null if the file does not exist (e.g. build:design not run)
|
|
14
|
+
* @throws Error if the file exists but cannot be read
|
|
15
|
+
*/
|
|
16
|
+
export function getDesignModeScriptContent() {
|
|
17
|
+
if (!existsSync(DESIGN_MODE_SCRIPT_PATH)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return readFileSync(DESIGN_MODE_SCRIPT_PATH, "utf-8");
|
|
21
|
+
}
|