@salesforce/webapp-experimental 1.73.1 → 1.75.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 +179 -162
- package/dist/design/interactions/communicationManager.d.ts.map +1 -1
- package/dist/design/interactions/communicationManager.js +10 -8
- package/dist/design/interactions/editableManager.d.ts +4 -30
- package/dist/design/interactions/editableManager.d.ts.map +1 -1
- package/dist/design/interactions/editableManager.js +39 -34
- package/dist/design/interactions/index.js +6 -16
- package/dist/design/interactions/interactionsController.d.ts +2 -0
- package/dist/design/interactions/interactionsController.d.ts.map +1 -1
- package/dist/design/interactions/interactionsController.js +14 -9
- package/dist/design/interactions/utils/sourceUtils.d.ts +7 -0
- package/dist/design/interactions/utils/sourceUtils.d.ts.map +1 -1
- package/dist/design/interactions/utils/sourceUtils.js +16 -0
- package/package.json +2 -2
|
@@ -12,6 +12,154 @@
|
|
|
12
12
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
13
13
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
14
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
|
+
|
|
15
163
|
// src/design/interactions/utils/cssUtils.ts
|
|
16
164
|
function getElementStyles(element) {
|
|
17
165
|
if (!element) return {};
|
|
@@ -74,61 +222,6 @@
|
|
|
74
222
|
};
|
|
75
223
|
}
|
|
76
224
|
|
|
77
|
-
// src/design/interactions/utils/sourceUtils.ts
|
|
78
|
-
function parseOptionalInt(value) {
|
|
79
|
-
if (value === null || value === void 0 || value === "") {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
const parsed = Number.parseInt(String(value), 10);
|
|
83
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
84
|
-
}
|
|
85
|
-
function parseSourceFileAttribute(value) {
|
|
86
|
-
const match = /^(.*):(\d+):(\d+)$/.exec(value);
|
|
87
|
-
if (!match) {
|
|
88
|
-
return { fileName: value, lineNumber: null, columnNumber: null };
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
fileName: match[1] ?? value,
|
|
92
|
-
lineNumber: parseOptionalInt(match[2]),
|
|
93
|
-
columnNumber: parseOptionalInt(match[3])
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
function getSourceFromDataAttributes(element) {
|
|
97
|
-
if (!element) {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
const source = element.getAttribute("data-source-file") || null;
|
|
101
|
-
if (!source) {
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
return parseSourceFileAttribute(source);
|
|
105
|
-
}
|
|
106
|
-
function findElementsBySourceLocation(location) {
|
|
107
|
-
const results = [];
|
|
108
|
-
const elements = document.querySelectorAll("[data-source-file]");
|
|
109
|
-
for (const el of elements) {
|
|
110
|
-
const elSource = getSourceFromDataAttributes(el);
|
|
111
|
-
if (elSource && elSource.fileName === location.fileName && elSource.lineNumber === location.lineNumber && elSource.columnNumber === location.columnNumber) {
|
|
112
|
-
results.push(el);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return results;
|
|
116
|
-
}
|
|
117
|
-
function getLabelFromSource(element) {
|
|
118
|
-
if (!element) {
|
|
119
|
-
return "";
|
|
120
|
-
}
|
|
121
|
-
const source = element.getAttribute("data-source-file");
|
|
122
|
-
if (!source) {
|
|
123
|
-
return element.tagName ? element.tagName.toLowerCase() : "";
|
|
124
|
-
}
|
|
125
|
-
const { fileName } = parseSourceFileAttribute(source);
|
|
126
|
-
const parts = fileName.split(/[/\\]/);
|
|
127
|
-
const baseName = parts[parts.length - 1] || fileName;
|
|
128
|
-
console.log("baseName", baseName);
|
|
129
|
-
return baseName;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
225
|
// src/design/interactions/communicationManager.ts
|
|
133
226
|
var CommunicationManager = class {
|
|
134
227
|
constructor() {
|
|
@@ -148,6 +241,8 @@
|
|
|
148
241
|
element.classList.add("design-mode-selected");
|
|
149
242
|
}
|
|
150
243
|
const debugSource = getSourceFromDataAttributes(element);
|
|
244
|
+
const textType = element.dataset?.textType ?? "none";
|
|
245
|
+
const hasNonEditableText = TEXT_TAGS.includes(element.tagName) && (textType === "dynamic" || textType === "mixed");
|
|
151
246
|
try {
|
|
152
247
|
if (window.parent !== window) {
|
|
153
248
|
window.parent.postMessage(
|
|
@@ -163,7 +258,8 @@
|
|
|
163
258
|
styles: {
|
|
164
259
|
...styles
|
|
165
260
|
},
|
|
166
|
-
debugSource
|
|
261
|
+
debugSource,
|
|
262
|
+
hasNonEditableText
|
|
167
263
|
}
|
|
168
264
|
},
|
|
169
265
|
"*"
|
|
@@ -188,20 +284,18 @@
|
|
|
188
284
|
*/
|
|
189
285
|
notifyTextChange(element, originalText, newText) {
|
|
190
286
|
const label = getLabelFromSource(element);
|
|
287
|
+
const debugSource = getSourceFromDataAttributes(element);
|
|
191
288
|
try {
|
|
192
289
|
if (window.parent !== window) {
|
|
193
290
|
window.parent.postMessage(
|
|
194
291
|
{
|
|
195
292
|
type: "text-changed",
|
|
196
293
|
change: {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
originalText,
|
|
203
|
-
newText
|
|
204
|
-
}
|
|
294
|
+
componentName: label,
|
|
295
|
+
tagName: element.tagName,
|
|
296
|
+
originalText,
|
|
297
|
+
newText,
|
|
298
|
+
debugSource
|
|
205
299
|
}
|
|
206
300
|
},
|
|
207
301
|
"*"
|
|
@@ -301,89 +395,6 @@
|
|
|
301
395
|
}
|
|
302
396
|
};
|
|
303
397
|
|
|
304
|
-
// src/design/interactions/editableManager.ts
|
|
305
|
-
var EditableManager = class {
|
|
306
|
-
constructor(communicationManager) {
|
|
307
|
-
__publicField(this, "communicationManager");
|
|
308
|
-
__publicField(this, "textTags");
|
|
309
|
-
__publicField(this, "boundHandleBlur");
|
|
310
|
-
__publicField(this, "boundHandleKeydown");
|
|
311
|
-
this.communicationManager = communicationManager;
|
|
312
|
-
this.textTags = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "SPAN", "A", "BUTTON", "LABEL"];
|
|
313
|
-
this.boundHandleBlur = this._handleBlur.bind(this);
|
|
314
|
-
this.boundHandleKeydown = this._handleKeydown.bind(this);
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Make an element editable if it's a text element
|
|
318
|
-
* @param element - The element to make editable
|
|
319
|
-
*/
|
|
320
|
-
makeEditableIfText(element) {
|
|
321
|
-
if (!this._isTextElement(element)) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
element.contentEditable = "true";
|
|
325
|
-
element.dataset.originalText = element.textContent ?? "";
|
|
326
|
-
element.addEventListener("blur", this.boundHandleBlur);
|
|
327
|
-
element.addEventListener("keydown", this.boundHandleKeydown);
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* Remove editable state from an element
|
|
331
|
-
* @param element - The element to make non-editable
|
|
332
|
-
*/
|
|
333
|
-
removeEditable(element) {
|
|
334
|
-
if (element.contentEditable === "true") {
|
|
335
|
-
element.contentEditable = "false";
|
|
336
|
-
}
|
|
337
|
-
delete element.dataset.originalText;
|
|
338
|
-
element.removeEventListener("blur", this.boundHandleBlur);
|
|
339
|
-
element.removeEventListener("keydown", this.boundHandleKeydown);
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Check if an element is a text element that can be made editable
|
|
343
|
-
* @private
|
|
344
|
-
* @param element - The element to check
|
|
345
|
-
* @returns True if the element can be made editable
|
|
346
|
-
*/
|
|
347
|
-
_isTextElement(element) {
|
|
348
|
-
return this.textTags.includes(element.tagName) && (element.textContent ?? "").trim().length > 0 && element.dataset.textType === "static";
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Handle blur event on editable element
|
|
352
|
-
* @private
|
|
353
|
-
* @param e - The blur event
|
|
354
|
-
*/
|
|
355
|
-
_handleBlur(e) {
|
|
356
|
-
const element = e.target;
|
|
357
|
-
const newText = element.textContent ?? "";
|
|
358
|
-
const originalText = element.dataset.originalText ?? "";
|
|
359
|
-
if (newText !== originalText) {
|
|
360
|
-
element.dataset.originalText = newText;
|
|
361
|
-
if (this.communicationManager) {
|
|
362
|
-
this.communicationManager.notifyTextChange(element, originalText, newText);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
this.removeEditable(element);
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Handle keydown event on editable element
|
|
369
|
-
* @private
|
|
370
|
-
* @param e - The keydown event
|
|
371
|
-
*/
|
|
372
|
-
_handleKeydown(e) {
|
|
373
|
-
const element = e.target;
|
|
374
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
375
|
-
e.preventDefault();
|
|
376
|
-
element.blur();
|
|
377
|
-
}
|
|
378
|
-
if (e.key === "Escape") {
|
|
379
|
-
if (element.dataset.originalText) {
|
|
380
|
-
element.textContent = element.dataset.originalText;
|
|
381
|
-
}
|
|
382
|
-
element.blur();
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
};
|
|
386
|
-
|
|
387
398
|
// src/design/interactions/eventHandlers.ts
|
|
388
399
|
var EventHandlers = class {
|
|
389
400
|
constructor(isInteractionsActive, componentMatcher, styleManager, editableManager, communicationManager) {
|
|
@@ -673,23 +684,29 @@
|
|
|
673
684
|
this.eventHandlers.clearAll();
|
|
674
685
|
console.log("Design Mode Interactions disabled");
|
|
675
686
|
}
|
|
687
|
+
resolveTargets(sourceLocation) {
|
|
688
|
+
let location = sourceLocation ?? null;
|
|
689
|
+
if (!location?.fileName) {
|
|
690
|
+
const selectedElement = this.eventHandlers.getSelectedElement();
|
|
691
|
+
location = getSourceFromDataAttributes(selectedElement);
|
|
692
|
+
}
|
|
693
|
+
return location ? findElementsBySourceLocation(location) : [];
|
|
694
|
+
}
|
|
676
695
|
/**
|
|
677
696
|
* Apply a style change to all elements at the given source location.
|
|
678
697
|
* When sourceLocation is provided (undo/redo), it is used directly.
|
|
679
698
|
* Otherwise the source location is read from the currently selected element.
|
|
680
699
|
*/
|
|
681
700
|
applyStyleChange(property, value, sourceLocation) {
|
|
682
|
-
|
|
683
|
-
if (!location?.fileName) {
|
|
684
|
-
const selectedElement = this.eventHandlers.getSelectedElement();
|
|
685
|
-
location = getSourceFromDataAttributes(selectedElement);
|
|
686
|
-
}
|
|
687
|
-
if (!location) return;
|
|
688
|
-
const targets = findElementsBySourceLocation(location);
|
|
689
|
-
for (const el of targets) {
|
|
701
|
+
for (const el of this.resolveTargets(sourceLocation)) {
|
|
690
702
|
el.style[property] = value;
|
|
691
703
|
}
|
|
692
704
|
}
|
|
705
|
+
applyTextChange(text, sourceLocation) {
|
|
706
|
+
for (const el of this.resolveTargets(sourceLocation)) {
|
|
707
|
+
el.textContent = text;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
693
710
|
/**
|
|
694
711
|
* Cleanup and remove event listeners
|
|
695
712
|
*/
|
|
@@ -724,16 +741,16 @@
|
|
|
724
741
|
const data = event.data;
|
|
725
742
|
const typed = data && typeof data === "object" ? data : null;
|
|
726
743
|
if (typed && typed.type === "style-change") {
|
|
727
|
-
const sl = typed.sourceLocation;
|
|
728
|
-
const sourceLocation = sl && typeof sl === "object" ? {
|
|
729
|
-
fileName: String(sl.sourceFile ?? ""),
|
|
730
|
-
lineNumber: typeof sl.lineNumber === "number" ? sl.lineNumber : null,
|
|
731
|
-
columnNumber: typeof sl.columnNumber === "number" ? sl.columnNumber : null
|
|
732
|
-
} : void 0;
|
|
733
744
|
interactions.applyStyleChange(
|
|
734
745
|
String(typed.property ?? ""),
|
|
735
746
|
String(typed.value ?? ""),
|
|
736
|
-
sourceLocation
|
|
747
|
+
parseSourceLocation(typed.sourceLocation)
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
if (typed && typed.type === "text-change") {
|
|
751
|
+
interactions.applyTextChange(
|
|
752
|
+
String(typed.text ?? ""),
|
|
753
|
+
parseSourceLocation(typed.sourceLocation)
|
|
737
754
|
);
|
|
738
755
|
}
|
|
739
756
|
if (typed && typed.type === "enable-interactions") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"communicationManager.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/communicationManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"communicationManager.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/communicationManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmBH,qBAAa,oBAAoB;;IAKhC;;;OAGG;IACH,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IA6DnD;;;;;OAKG;IACH,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAyBnF;;OAEG;IACH,4BAA4B,IAAI,IAAI;CAepC"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Communication Manager Module
|
|
8
8
|
* Handles communication with the parent window (VS Code extension)
|
|
9
9
|
*/
|
|
10
|
+
import { TEXT_TAGS } from "./editableManager.js";
|
|
10
11
|
import { getElementStyles } from "./utils/cssUtils.js";
|
|
11
12
|
import { getLabelFromSource, getSourceFromDataAttributes } from "./utils/sourceUtils.js";
|
|
12
13
|
export class CommunicationManager {
|
|
@@ -31,6 +32,8 @@ export class CommunicationManager {
|
|
|
31
32
|
}
|
|
32
33
|
// Source location metadata injected at compile time (babel-plugin-enhanced-locator)
|
|
33
34
|
const debugSource = getSourceFromDataAttributes(element);
|
|
35
|
+
const textType = element.dataset?.textType ?? "none";
|
|
36
|
+
const hasNonEditableText = TEXT_TAGS.includes(element.tagName) && (textType === "dynamic" || textType === "mixed");
|
|
34
37
|
// Send message to parent window
|
|
35
38
|
try {
|
|
36
39
|
if (window.parent !== window) {
|
|
@@ -47,6 +50,7 @@ export class CommunicationManager {
|
|
|
47
50
|
...styles,
|
|
48
51
|
},
|
|
49
52
|
debugSource: debugSource,
|
|
53
|
+
hasNonEditableText,
|
|
50
54
|
},
|
|
51
55
|
}, "*");
|
|
52
56
|
}
|
|
@@ -72,19 +76,17 @@ export class CommunicationManager {
|
|
|
72
76
|
*/
|
|
73
77
|
notifyTextChange(element, originalText, newText) {
|
|
74
78
|
const label = getLabelFromSource(element);
|
|
79
|
+
const debugSource = getSourceFromDataAttributes(element);
|
|
75
80
|
try {
|
|
76
81
|
if (window.parent !== window) {
|
|
77
82
|
window.parent.postMessage({
|
|
78
83
|
type: "text-changed",
|
|
79
84
|
change: {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
originalText: originalText,
|
|
86
|
-
newText: newText,
|
|
87
|
-
},
|
|
85
|
+
componentName: label,
|
|
86
|
+
tagName: element.tagName,
|
|
87
|
+
originalText,
|
|
88
|
+
newText,
|
|
89
|
+
debugSource,
|
|
88
90
|
},
|
|
89
91
|
}, "*");
|
|
90
92
|
}
|
|
@@ -3,48 +3,22 @@
|
|
|
3
3
|
* All rights reserved.
|
|
4
4
|
* For full license text, see the LICENSE.txt file
|
|
5
5
|
*/
|
|
6
|
-
/**
|
|
7
|
-
* Editable Manager Module
|
|
8
|
-
* Handles making elements editable and notifying the extension of text changes.
|
|
9
|
-
* History/undo for style changes is owned by the design property panel (host); this module does not use history.
|
|
10
|
-
*/
|
|
11
6
|
interface CommunicationManagerLike {
|
|
12
7
|
notifyTextChange: (element: HTMLElement, originalText: string, newText: string) => void;
|
|
13
8
|
}
|
|
9
|
+
export declare const TEXT_TAGS: string[];
|
|
14
10
|
export declare class EditableManager {
|
|
15
11
|
private communicationManager?;
|
|
16
|
-
private textTags;
|
|
17
12
|
private boundHandleBlur;
|
|
18
13
|
private boundHandleKeydown;
|
|
14
|
+
private boundHandleInput;
|
|
15
|
+
private editableGroup;
|
|
19
16
|
constructor(communicationManager?: CommunicationManagerLike);
|
|
20
|
-
/**
|
|
21
|
-
* Make an element editable if it's a text element
|
|
22
|
-
* @param element - The element to make editable
|
|
23
|
-
*/
|
|
24
17
|
makeEditableIfText(element: HTMLElement): void;
|
|
25
|
-
/**
|
|
26
|
-
* Remove editable state from an element
|
|
27
|
-
* @param element - The element to make non-editable
|
|
28
|
-
*/
|
|
29
18
|
removeEditable(element: HTMLElement): void;
|
|
30
|
-
/**
|
|
31
|
-
* Check if an element is a text element that can be made editable
|
|
32
|
-
* @private
|
|
33
|
-
* @param element - The element to check
|
|
34
|
-
* @returns True if the element can be made editable
|
|
35
|
-
*/
|
|
36
19
|
private _isTextElement;
|
|
37
|
-
|
|
38
|
-
* Handle blur event on editable element
|
|
39
|
-
* @private
|
|
40
|
-
* @param e - The blur event
|
|
41
|
-
*/
|
|
20
|
+
private _handleInput;
|
|
42
21
|
private _handleBlur;
|
|
43
|
-
/**
|
|
44
|
-
* Handle keydown event on editable element
|
|
45
|
-
* @private
|
|
46
|
-
* @param e - The keydown event
|
|
47
|
-
*/
|
|
48
22
|
private _handleKeydown;
|
|
49
23
|
}
|
|
50
24
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editableManager.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/editableManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"editableManager.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/editableManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,UAAU,wBAAwB;IACjC,gBAAgB,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACxF;AAED,eAAO,MAAM,SAAS,UAA4E,CAAC;AAEnG,qBAAa,eAAe;IAC3B,OAAO,CAAC,oBAAoB,CAAC,CAA2B;IACxD,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,aAAa,CAAgB;gBAEzB,oBAAoB,CAAC,EAAE,wBAAwB;IAQ3D,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAoB9C,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAe1C,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,cAAc;CAiBtB"}
|
|
@@ -3,77 +3,80 @@
|
|
|
3
3
|
* All rights reserved.
|
|
4
4
|
* For full license text, see the LICENSE.txt file
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* Editable Manager Module
|
|
8
|
+
* Handles making elements editable and notifying the extension of text changes.
|
|
9
|
+
* History/undo for style changes is owned by the design property panel (host); this module does not use history.
|
|
10
|
+
*/
|
|
11
|
+
import { findElementsBySourceLocation, getSourceFromDataAttributes } from "./utils/sourceUtils.js";
|
|
12
|
+
export const TEXT_TAGS = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "SPAN", "A", "BUTTON", "LABEL"];
|
|
6
13
|
export class EditableManager {
|
|
7
14
|
communicationManager;
|
|
8
|
-
textTags;
|
|
9
15
|
boundHandleBlur;
|
|
10
16
|
boundHandleKeydown;
|
|
17
|
+
boundHandleInput;
|
|
18
|
+
editableGroup;
|
|
11
19
|
constructor(communicationManager) {
|
|
12
20
|
this.communicationManager = communicationManager;
|
|
13
|
-
this.textTags = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "SPAN", "A", "BUTTON", "LABEL"];
|
|
14
21
|
this.boundHandleBlur = this._handleBlur.bind(this);
|
|
15
22
|
this.boundHandleKeydown = this._handleKeydown.bind(this);
|
|
23
|
+
this.boundHandleInput = this._handleInput.bind(this);
|
|
24
|
+
this.editableGroup = [];
|
|
16
25
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Make an element editable if it's a text element
|
|
19
|
-
* @param element - The element to make editable
|
|
20
|
-
*/
|
|
21
26
|
makeEditableIfText(element) {
|
|
22
27
|
if (!this._isTextElement(element)) {
|
|
23
28
|
return;
|
|
24
29
|
}
|
|
30
|
+
const source = getSourceFromDataAttributes(element);
|
|
31
|
+
const siblings = source ? findElementsBySourceLocation(source) : [];
|
|
32
|
+
const others = siblings.filter((el) => el !== element);
|
|
33
|
+
this.editableGroup = [element, ...others];
|
|
34
|
+
for (const el of this.editableGroup) {
|
|
35
|
+
el.dataset.originalText = el.textContent ?? "";
|
|
36
|
+
}
|
|
25
37
|
element.contentEditable = "true";
|
|
26
|
-
element.dataset.originalText = element.textContent ?? "";
|
|
27
38
|
element.addEventListener("blur", this.boundHandleBlur);
|
|
28
39
|
element.addEventListener("keydown", this.boundHandleKeydown);
|
|
40
|
+
element.addEventListener("input", this.boundHandleInput);
|
|
29
41
|
}
|
|
30
|
-
/**
|
|
31
|
-
* Remove editable state from an element
|
|
32
|
-
* @param element - The element to make non-editable
|
|
33
|
-
*/
|
|
34
42
|
removeEditable(element) {
|
|
35
43
|
if (element.contentEditable === "true") {
|
|
36
44
|
element.contentEditable = "false";
|
|
37
45
|
}
|
|
38
|
-
delete element.dataset.originalText;
|
|
39
46
|
element.removeEventListener("blur", this.boundHandleBlur);
|
|
40
47
|
element.removeEventListener("keydown", this.boundHandleKeydown);
|
|
48
|
+
element.removeEventListener("input", this.boundHandleInput);
|
|
49
|
+
for (const el of this.editableGroup) {
|
|
50
|
+
delete el.dataset.originalText;
|
|
51
|
+
}
|
|
52
|
+
this.editableGroup = [];
|
|
41
53
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Check if an element is a text element that can be made editable
|
|
44
|
-
* @private
|
|
45
|
-
* @param element - The element to check
|
|
46
|
-
* @returns True if the element can be made editable
|
|
47
|
-
*/
|
|
48
54
|
_isTextElement(element) {
|
|
49
|
-
return (
|
|
55
|
+
return (TEXT_TAGS.includes(element.tagName) &&
|
|
50
56
|
(element.textContent ?? "").trim().length > 0 &&
|
|
51
57
|
element.dataset.textType === "static");
|
|
52
58
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
_handleInput(e) {
|
|
60
|
+
const primary = e.target;
|
|
61
|
+
const text = primary.textContent ?? "";
|
|
62
|
+
for (let i = 1; i < this.editableGroup.length; i++) {
|
|
63
|
+
this.editableGroup[i].textContent = text;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
58
66
|
_handleBlur(e) {
|
|
59
67
|
const element = e.target;
|
|
60
68
|
const newText = element.textContent ?? "";
|
|
61
69
|
const originalText = element.dataset.originalText ?? "";
|
|
62
70
|
if (newText !== originalText) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
for (const el of this.editableGroup) {
|
|
72
|
+
el.dataset.originalText = newText;
|
|
73
|
+
}
|
|
66
74
|
if (this.communicationManager) {
|
|
67
75
|
this.communicationManager.notifyTextChange(element, originalText, newText);
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
78
|
this.removeEditable(element);
|
|
71
79
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Handle keydown event on editable element
|
|
74
|
-
* @private
|
|
75
|
-
* @param e - The keydown event
|
|
76
|
-
*/
|
|
77
80
|
_handleKeydown(e) {
|
|
78
81
|
const element = e.target;
|
|
79
82
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
@@ -81,8 +84,10 @@ export class EditableManager {
|
|
|
81
84
|
element.blur();
|
|
82
85
|
}
|
|
83
86
|
if (e.key === "Escape") {
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
for (const el of this.editableGroup) {
|
|
88
|
+
if (el.dataset.originalText) {
|
|
89
|
+
el.textContent = el.dataset.originalText;
|
|
90
|
+
}
|
|
86
91
|
}
|
|
87
92
|
element.blur();
|
|
88
93
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* This file is used by esbuild to create the bundled version
|
|
9
9
|
*/
|
|
10
10
|
import { InteractionsController } from "./interactionsController.js";
|
|
11
|
+
import { parseSourceLocation } from "./utils/sourceUtils.js";
|
|
11
12
|
const interactions = new InteractionsController(true);
|
|
12
13
|
if (typeof document !== "undefined") {
|
|
13
14
|
if (document.readyState === "loading") {
|
|
@@ -29,23 +30,12 @@ if (typeof window !== "undefined") {
|
|
|
29
30
|
};
|
|
30
31
|
window.addEventListener("message", function (event) {
|
|
31
32
|
const data = event.data;
|
|
32
|
-
const typed = data && typeof data === "object"
|
|
33
|
-
? data
|
|
34
|
-
: null;
|
|
33
|
+
const typed = data && typeof data === "object" ? data : null;
|
|
35
34
|
if (typed && typed.type === "style-change") {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
lineNumber: typeof sl.lineNumber === "number"
|
|
41
|
-
? sl.lineNumber
|
|
42
|
-
: null,
|
|
43
|
-
columnNumber: typeof sl.columnNumber === "number"
|
|
44
|
-
? sl.columnNumber
|
|
45
|
-
: null,
|
|
46
|
-
}
|
|
47
|
-
: undefined;
|
|
48
|
-
interactions.applyStyleChange(String(typed.property ?? ""), String(typed.value ?? ""), sourceLocation);
|
|
35
|
+
interactions.applyStyleChange(String(typed.property ?? ""), String(typed.value ?? ""), parseSourceLocation(typed.sourceLocation));
|
|
36
|
+
}
|
|
37
|
+
if (typed && typed.type === "text-change") {
|
|
38
|
+
interactions.applyTextChange(String(typed.text ?? ""), parseSourceLocation(typed.sourceLocation));
|
|
49
39
|
}
|
|
50
40
|
if (typed && typed.type === "enable-interactions") {
|
|
51
41
|
window.enableInteractions?.();
|
|
@@ -25,12 +25,14 @@ export declare class InteractionsController {
|
|
|
25
25
|
* Disable the design mode interactions
|
|
26
26
|
*/
|
|
27
27
|
disable(): void;
|
|
28
|
+
private resolveTargets;
|
|
28
29
|
/**
|
|
29
30
|
* Apply a style change to all elements at the given source location.
|
|
30
31
|
* When sourceLocation is provided (undo/redo), it is used directly.
|
|
31
32
|
* Otherwise the source location is read from the currently selected element.
|
|
32
33
|
*/
|
|
33
34
|
applyStyleChange(property: string, value: string, sourceLocation?: SourceLocation): void;
|
|
35
|
+
applyTextChange(text: string, sourceLocation?: SourceLocation): void;
|
|
34
36
|
/**
|
|
35
37
|
* Cleanup and remove event listeners
|
|
36
38
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactionsController.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/interactionsController.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,wBAAwB,CAAC;AAEhC,qBAAa,sBAAsB;IAClC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAU;IAE1B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,OAAO,UAAO;IAwD1B;;OAEG;IACH,UAAU,IAAI,IAAI;IAiBlB;;OAEG;IACH,MAAM,IAAI,IAAI;IAKd;;OAEG;IACH,OAAO,IAAI,IAAI;IAMf;;;;OAIG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"interactionsController.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/interactionsController.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,wBAAwB,CAAC;AAEhC,qBAAa,sBAAsB;IAClC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAU;IAE1B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,OAAO,UAAO;IAwD1B;;OAEG;IACH,UAAU,IAAI,IAAI;IAiBlB;;OAEG;IACH,MAAM,IAAI,IAAI;IAKd;;OAEG;IACH,OAAO,IAAI,IAAI;IAMf,OAAO,CAAC,cAAc;IAWtB;;;;OAIG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI;IAMxF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI;IAMpE;;OAEG;IACH,OAAO,IAAI,IAAI;CAOf"}
|
|
@@ -99,24 +99,29 @@ export class InteractionsController {
|
|
|
99
99
|
this.eventHandlers.clearAll();
|
|
100
100
|
console.log("Design Mode Interactions disabled");
|
|
101
101
|
}
|
|
102
|
+
resolveTargets(sourceLocation) {
|
|
103
|
+
let location = sourceLocation ?? null;
|
|
104
|
+
if (!location?.fileName) {
|
|
105
|
+
const selectedElement = this.eventHandlers.getSelectedElement();
|
|
106
|
+
location = getSourceFromDataAttributes(selectedElement);
|
|
107
|
+
}
|
|
108
|
+
return location ? findElementsBySourceLocation(location) : [];
|
|
109
|
+
}
|
|
102
110
|
/**
|
|
103
111
|
* Apply a style change to all elements at the given source location.
|
|
104
112
|
* When sourceLocation is provided (undo/redo), it is used directly.
|
|
105
113
|
* Otherwise the source location is read from the currently selected element.
|
|
106
114
|
*/
|
|
107
115
|
applyStyleChange(property, value, sourceLocation) {
|
|
108
|
-
|
|
109
|
-
if (!location?.fileName) {
|
|
110
|
-
const selectedElement = this.eventHandlers.getSelectedElement();
|
|
111
|
-
location = getSourceFromDataAttributes(selectedElement);
|
|
112
|
-
}
|
|
113
|
-
if (!location)
|
|
114
|
-
return;
|
|
115
|
-
const targets = findElementsBySourceLocation(location);
|
|
116
|
-
for (const el of targets) {
|
|
116
|
+
for (const el of this.resolveTargets(sourceLocation)) {
|
|
117
117
|
el.style[property] = value;
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
|
+
applyTextChange(text, sourceLocation) {
|
|
121
|
+
for (const el of this.resolveTargets(sourceLocation)) {
|
|
122
|
+
el.textContent = text;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
120
125
|
/**
|
|
121
126
|
* Cleanup and remove event listeners
|
|
122
127
|
*/
|
|
@@ -25,5 +25,12 @@ export declare function findElementsBySourceLocation(location: SourceLocation):
|
|
|
25
25
|
* @param element - The DOM element
|
|
26
26
|
* @returns A label suitable for UI display
|
|
27
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* Parse an untyped message payload object into a SourceLocation.
|
|
30
|
+
* Used by the entry point to convert raw postMessage data into typed values.
|
|
31
|
+
* @param sl - The raw source location object from a message payload
|
|
32
|
+
* @returns A SourceLocation, or undefined if the input is not a valid object
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseSourceLocation(sl: unknown): SourceLocation | undefined;
|
|
28
35
|
export declare function getLabelFromSource(element: HTMLElement | null | undefined): string;
|
|
29
36
|
//# sourceMappingURL=sourceUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sourceUtils.d.ts","sourceRoot":"","sources":["../../../../src/design/interactions/utils/sourceUtils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkCH,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAC1C,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GACrC,cAAc,GAAG,IAAI,CAWvB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,cAAc,GAAG,WAAW,EAAE,CAepF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAiBlF"}
|
|
1
|
+
{"version":3,"file":"sourceUtils.d.ts","sourceRoot":"","sources":["../../../../src/design/interactions/utils/sourceUtils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkCH,MAAM,WAAW,cAAc;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAC1C,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GACrC,cAAc,GAAG,IAAI,CAWvB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,cAAc,GAAG,WAAW,EAAE,CAepF;AAED;;;;GAIG;AACH;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CAQ3E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAiBlF"}
|
|
@@ -66,6 +66,22 @@ export function findElementsBySourceLocation(location) {
|
|
|
66
66
|
* @param element - The DOM element
|
|
67
67
|
* @returns A label suitable for UI display
|
|
68
68
|
*/
|
|
69
|
+
/**
|
|
70
|
+
* Parse an untyped message payload object into a SourceLocation.
|
|
71
|
+
* Used by the entry point to convert raw postMessage data into typed values.
|
|
72
|
+
* @param sl - The raw source location object from a message payload
|
|
73
|
+
* @returns A SourceLocation, or undefined if the input is not a valid object
|
|
74
|
+
*/
|
|
75
|
+
export function parseSourceLocation(sl) {
|
|
76
|
+
if (!sl || typeof sl !== "object")
|
|
77
|
+
return undefined;
|
|
78
|
+
const obj = sl;
|
|
79
|
+
return {
|
|
80
|
+
fileName: String(obj.sourceFile ?? ""),
|
|
81
|
+
lineNumber: typeof obj.lineNumber === "number" ? obj.lineNumber : null,
|
|
82
|
+
columnNumber: typeof obj.columnNumber === "number" ? obj.columnNumber : null,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
69
85
|
export function getLabelFromSource(element) {
|
|
70
86
|
if (!element) {
|
|
71
87
|
return "";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/webapp-experimental",
|
|
3
3
|
"description": "[experimental] Core package for Salesforce Web Applications",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.75.0",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@salesforce/core": "^8.23.4",
|
|
48
|
-
"@salesforce/sdk-data": "^1.
|
|
48
|
+
"@salesforce/sdk-data": "^1.75.0",
|
|
49
49
|
"axios": "^1.7.7",
|
|
50
50
|
"micromatch": "^4.0.8",
|
|
51
51
|
"path-to-regexp": "^8.3.0"
|