@salesforce/webapp-experimental 1.68.0 → 1.69.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 +61 -107
- package/dist/design/interactions/componentMatcher.d.ts +0 -7
- package/dist/design/interactions/componentMatcher.d.ts.map +1 -1
- package/dist/design/interactions/componentMatcher.js +0 -20
- package/dist/design/interactions/eventHandlers.d.ts +2 -36
- package/dist/design/interactions/eventHandlers.d.ts.map +1 -1
- package/dist/design/interactions/eventHandlers.js +35 -78
- package/dist/design/interactions/index.js +13 -1
- package/dist/design/interactions/interactionsController.d.ts +5 -4
- package/dist/design/interactions/interactionsController.d.ts.map +1 -1
- package/dist/design/interactions/interactionsController.js +15 -10
- package/dist/design/interactions/utils/sourceUtils.d.ts +6 -0
- package/dist/design/interactions/utils/sourceUtils.d.ts.map +1 -1
- package/dist/design/interactions/utils/sourceUtils.js +19 -0
- package/package.json +2 -2
|
@@ -70,6 +70,17 @@
|
|
|
70
70
|
}
|
|
71
71
|
return parseSourceFileAttribute(source);
|
|
72
72
|
}
|
|
73
|
+
function findElementsBySourceLocation(location) {
|
|
74
|
+
const results = [];
|
|
75
|
+
const elements = document.querySelectorAll("[data-source-file]");
|
|
76
|
+
for (const el of elements) {
|
|
77
|
+
const elSource = getSourceFromDataAttributes(el);
|
|
78
|
+
if (elSource && elSource.fileName === location.fileName && elSource.lineNumber === location.lineNumber && elSource.columnNumber === location.columnNumber) {
|
|
79
|
+
results.push(el);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return results;
|
|
83
|
+
}
|
|
73
84
|
function getLabelFromSource(element) {
|
|
74
85
|
if (!element) {
|
|
75
86
|
return "";
|
|
@@ -233,28 +244,6 @@
|
|
|
233
244
|
}
|
|
234
245
|
return true;
|
|
235
246
|
}
|
|
236
|
-
/**
|
|
237
|
-
* Find all same elements that share the same data-source-file value.
|
|
238
|
-
* These are elements rendered by the same component at the same source location.
|
|
239
|
-
* @param element - The reference element
|
|
240
|
-
* @returns Array of same elements (excludes the element itself)
|
|
241
|
-
*/
|
|
242
|
-
findSameElements(element) {
|
|
243
|
-
const sourceFile = element.getAttribute("data-source-file");
|
|
244
|
-
if (!sourceFile) {
|
|
245
|
-
return [];
|
|
246
|
-
}
|
|
247
|
-
const all = document.querySelectorAll(
|
|
248
|
-
`[data-source-file="${CSS.escape(sourceFile)}"]`
|
|
249
|
-
);
|
|
250
|
-
const sameElements = [];
|
|
251
|
-
for (const el of all) {
|
|
252
|
-
if (el !== element) {
|
|
253
|
-
sameElements.push(el);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return sameElements;
|
|
257
|
-
}
|
|
258
247
|
/**
|
|
259
248
|
* Find the nearest highlightable element by walking up the DOM tree
|
|
260
249
|
* @param target - The target element
|
|
@@ -370,35 +359,24 @@
|
|
|
370
359
|
__publicField(this, "styleManager");
|
|
371
360
|
__publicField(this, "editableManager");
|
|
372
361
|
__publicField(this, "communicationManager");
|
|
373
|
-
__publicField(this, "
|
|
374
|
-
__publicField(this, "currentHighlightedSameElements");
|
|
362
|
+
__publicField(this, "currentHighlightedElements");
|
|
375
363
|
__publicField(this, "selectedElement");
|
|
376
|
-
__publicField(this, "
|
|
364
|
+
__publicField(this, "selectedElements");
|
|
377
365
|
this.isInteractionsActive = isInteractionsActive;
|
|
378
366
|
this.componentMatcher = componentMatcher;
|
|
379
367
|
this.styleManager = styleManager;
|
|
380
368
|
this.editableManager = editableManager;
|
|
381
369
|
this.communicationManager = communicationManager;
|
|
382
|
-
this.
|
|
383
|
-
this.currentHighlightedSameElements = [];
|
|
370
|
+
this.currentHighlightedElements = [];
|
|
384
371
|
this.selectedElement = null;
|
|
385
|
-
this.
|
|
372
|
+
this.selectedElements = [];
|
|
386
373
|
this.handleMouseOver = this.handleMouseOver.bind(this);
|
|
387
374
|
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
|
388
375
|
this.handleClick = this.handleClick.bind(this);
|
|
389
376
|
}
|
|
390
|
-
/**
|
|
391
|
-
* Find the nearest highlightable element by walking up the DOM tree
|
|
392
|
-
* @param target - The target element
|
|
393
|
-
* @returns The highlightable element or null
|
|
394
|
-
*/
|
|
395
377
|
_findHighlightableElement(target) {
|
|
396
378
|
return this.componentMatcher.findHighlightableElement(target);
|
|
397
379
|
}
|
|
398
|
-
/**
|
|
399
|
-
* Handle mouseover event
|
|
400
|
-
* @param e - The mouseover event
|
|
401
|
-
*/
|
|
402
380
|
handleMouseOver(e) {
|
|
403
381
|
if (!this.isInteractionsActive()) {
|
|
404
382
|
return;
|
|
@@ -412,41 +390,26 @@
|
|
|
412
390
|
if (!element) {
|
|
413
391
|
return;
|
|
414
392
|
}
|
|
415
|
-
if (this.
|
|
393
|
+
if (this.currentHighlightedElements.includes(element) || this.selectedElement && this.selectedElement === element) {
|
|
416
394
|
return;
|
|
417
395
|
}
|
|
418
|
-
if (this.
|
|
419
|
-
this.styleManager.unhighlightElements(
|
|
420
|
-
|
|
421
|
-
...this.currentHighlightedSameElements
|
|
422
|
-
]);
|
|
423
|
-
this.currentHighlightedSameElements = [];
|
|
396
|
+
if (this.currentHighlightedElements.length > 0 && !this.currentHighlightedElements[0].classList.contains("design-mode-selected")) {
|
|
397
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
398
|
+
this.currentHighlightedElements = [];
|
|
424
399
|
}
|
|
425
|
-
const
|
|
426
|
-
this.styleManager.highlightElements(
|
|
427
|
-
this.
|
|
428
|
-
this.currentHighlightedSameElements = sameElements;
|
|
400
|
+
const allElements = findElementsBySourceLocation(getSourceFromDataAttributes(element));
|
|
401
|
+
this.styleManager.highlightElements(allElements);
|
|
402
|
+
this.currentHighlightedElements = allElements;
|
|
429
403
|
}
|
|
430
|
-
/**
|
|
431
|
-
* Handle mouseleave event
|
|
432
|
-
*/
|
|
433
404
|
handleMouseLeave() {
|
|
434
405
|
if (!this.isInteractionsActive()) {
|
|
435
406
|
return;
|
|
436
407
|
}
|
|
437
|
-
if (this.
|
|
438
|
-
this.styleManager.unhighlightElements(
|
|
439
|
-
|
|
440
|
-
...this.currentHighlightedSameElements
|
|
441
|
-
]);
|
|
442
|
-
this.currentHighlighted = null;
|
|
443
|
-
this.currentHighlightedSameElements = [];
|
|
408
|
+
if (this.currentHighlightedElements.length > 0 && !this.currentHighlightedElements[0].classList.contains("design-mode-selected")) {
|
|
409
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
410
|
+
this.currentHighlightedElements = [];
|
|
444
411
|
}
|
|
445
412
|
}
|
|
446
|
-
/**
|
|
447
|
-
* Handle click event
|
|
448
|
-
* @param e - The click event
|
|
449
|
-
*/
|
|
450
413
|
handleClick(e) {
|
|
451
414
|
if (!this.isInteractionsActive()) {
|
|
452
415
|
return;
|
|
@@ -465,57 +428,35 @@
|
|
|
465
428
|
return;
|
|
466
429
|
}
|
|
467
430
|
if (this.selectedElement) {
|
|
468
|
-
this.styleManager.deselectElements(
|
|
431
|
+
this.styleManager.deselectElements(this.selectedElements);
|
|
469
432
|
this.editableManager.removeEditable(this.selectedElement);
|
|
470
433
|
}
|
|
471
|
-
if (this.
|
|
472
|
-
this.styleManager.unhighlightElements(
|
|
473
|
-
|
|
474
|
-
...this.currentHighlightedSameElements
|
|
475
|
-
]);
|
|
476
|
-
this.currentHighlighted = null;
|
|
477
|
-
this.currentHighlightedSameElements = [];
|
|
434
|
+
if (this.currentHighlightedElements.length > 0) {
|
|
435
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
436
|
+
this.currentHighlightedElements = [];
|
|
478
437
|
}
|
|
479
438
|
this.selectedElement = element;
|
|
480
|
-
const
|
|
481
|
-
this.
|
|
482
|
-
this.styleManager.selectElements(
|
|
439
|
+
const allElements = findElementsBySourceLocation(getSourceFromDataAttributes(element));
|
|
440
|
+
this.selectedElements = allElements;
|
|
441
|
+
this.styleManager.selectElements(allElements);
|
|
483
442
|
this.editableManager.makeEditableIfText(element);
|
|
484
443
|
this.communicationManager.notifyComponentSelected(element);
|
|
485
444
|
}
|
|
486
|
-
/**
|
|
487
|
-
* Clear all highlights and selections
|
|
488
|
-
*/
|
|
489
445
|
clearAll() {
|
|
490
|
-
if (this.
|
|
491
|
-
this.styleManager.unhighlightElements(
|
|
492
|
-
|
|
493
|
-
...this.currentHighlightedSameElements
|
|
494
|
-
]);
|
|
495
|
-
this.currentHighlighted = null;
|
|
496
|
-
this.currentHighlightedSameElements = [];
|
|
446
|
+
if (this.currentHighlightedElements.length > 0) {
|
|
447
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
448
|
+
this.currentHighlightedElements = [];
|
|
497
449
|
}
|
|
498
450
|
if (this.selectedElement) {
|
|
499
|
-
this.styleManager.deselectElements(
|
|
451
|
+
this.styleManager.deselectElements(this.selectedElements);
|
|
500
452
|
this.editableManager.removeEditable(this.selectedElement);
|
|
501
453
|
this.selectedElement = null;
|
|
502
|
-
this.
|
|
454
|
+
this.selectedElements = [];
|
|
503
455
|
}
|
|
504
456
|
}
|
|
505
|
-
/**
|
|
506
|
-
* Get the currently selected element
|
|
507
|
-
* @returns The selected element
|
|
508
|
-
*/
|
|
509
457
|
getSelectedElement() {
|
|
510
458
|
return this.selectedElement;
|
|
511
459
|
}
|
|
512
|
-
/**
|
|
513
|
-
* Get the same elements of the currently selected element
|
|
514
|
-
* @returns Array of same elements
|
|
515
|
-
*/
|
|
516
|
-
getSelectedSameElements() {
|
|
517
|
-
return this.selectedSameElements;
|
|
518
|
-
}
|
|
519
460
|
};
|
|
520
461
|
|
|
521
462
|
// src/design/interactions/styleManager.ts
|
|
@@ -700,17 +641,20 @@
|
|
|
700
641
|
console.log("Design Mode Interactions disabled");
|
|
701
642
|
}
|
|
702
643
|
/**
|
|
703
|
-
* Apply style
|
|
704
|
-
*
|
|
705
|
-
*
|
|
644
|
+
* Apply a style change to all elements at the given source location.
|
|
645
|
+
* When sourceLocation is provided (undo/redo), it is used directly.
|
|
646
|
+
* Otherwise the source location is read from the currently selected element.
|
|
706
647
|
*/
|
|
707
|
-
applyStyleChange(property, value) {
|
|
708
|
-
|
|
709
|
-
if (
|
|
710
|
-
selectedElement
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
648
|
+
applyStyleChange(property, value, sourceLocation) {
|
|
649
|
+
let location = sourceLocation ?? null;
|
|
650
|
+
if (!location?.fileName) {
|
|
651
|
+
const selectedElement = this.eventHandlers.getSelectedElement();
|
|
652
|
+
location = getSourceFromDataAttributes(selectedElement);
|
|
653
|
+
}
|
|
654
|
+
if (!location) return;
|
|
655
|
+
const targets = findElementsBySourceLocation(location);
|
|
656
|
+
for (const el of targets) {
|
|
657
|
+
el.style[property] = value;
|
|
714
658
|
}
|
|
715
659
|
}
|
|
716
660
|
/**
|
|
@@ -747,7 +691,17 @@
|
|
|
747
691
|
const data = event.data;
|
|
748
692
|
const typed = data && typeof data === "object" ? data : null;
|
|
749
693
|
if (typed && typed.type === "style-change") {
|
|
750
|
-
|
|
694
|
+
const sl = typed.sourceLocation;
|
|
695
|
+
const sourceLocation = sl && typeof sl === "object" ? {
|
|
696
|
+
fileName: String(sl.sourceFile ?? ""),
|
|
697
|
+
lineNumber: typeof sl.lineNumber === "number" ? sl.lineNumber : null,
|
|
698
|
+
columnNumber: typeof sl.columnNumber === "number" ? sl.columnNumber : null
|
|
699
|
+
} : void 0;
|
|
700
|
+
interactions.applyStyleChange(
|
|
701
|
+
String(typed.property ?? ""),
|
|
702
|
+
String(typed.value ?? ""),
|
|
703
|
+
sourceLocation
|
|
704
|
+
);
|
|
751
705
|
}
|
|
752
706
|
if (typed && typed.type === "enable-interactions") {
|
|
753
707
|
window.enableInteractions?.();
|
|
@@ -33,13 +33,6 @@ export declare class ComponentMatcher {
|
|
|
33
33
|
* @returns True if the element should be highlighted
|
|
34
34
|
*/
|
|
35
35
|
isHighlightableElement(element: HTMLElement | null | undefined): boolean;
|
|
36
|
-
/**
|
|
37
|
-
* Find all same elements that share the same data-source-file value.
|
|
38
|
-
* These are elements rendered by the same component at the same source location.
|
|
39
|
-
* @param element - The reference element
|
|
40
|
-
* @returns Array of same elements (excludes the element itself)
|
|
41
|
-
*/
|
|
42
|
-
findSameElements(element: HTMLElement): HTMLElement[];
|
|
43
36
|
/**
|
|
44
37
|
* Find the nearest highlightable element by walking up the DOM tree
|
|
45
38
|
* @param target - The target element
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"componentMatcher.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/componentMatcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AAEH,MAAM,WAAW,uBAAuB;IAEvC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,SAAS,CAAW;gBAEhB,OAAO,GAAE,uBAA4B;IAIjD;;;;OAIG;IACH,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO;IAOnE,OAAO,CAAC,WAAW;IAInB;;;;;OAKG;IACH,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAM7D;;;;OAIG;IACH,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO;IAgBxE
|
|
1
|
+
{"version":3,"file":"componentMatcher.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/componentMatcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AAEH,MAAM,WAAW,uBAAuB;IAEvC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,SAAS,CAAW;gBAEhB,OAAO,GAAE,uBAA4B;IAIjD;;;;OAIG;IACH,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO;IAOnE,OAAO,CAAC,WAAW;IAInB;;;;;OAKG;IACH,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAM7D;;;;OAIG;IACH,sBAAsB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO;IAgBxE;;;;OAIG;IACH,wBAAwB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,GAAG,IAAI;CA+BpF"}
|
|
@@ -48,26 +48,6 @@ export class ComponentMatcher {
|
|
|
48
48
|
}
|
|
49
49
|
return true;
|
|
50
50
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Find all same elements that share the same data-source-file value.
|
|
53
|
-
* These are elements rendered by the same component at the same source location.
|
|
54
|
-
* @param element - The reference element
|
|
55
|
-
* @returns Array of same elements (excludes the element itself)
|
|
56
|
-
*/
|
|
57
|
-
findSameElements(element) {
|
|
58
|
-
const sourceFile = element.getAttribute("data-source-file");
|
|
59
|
-
if (!sourceFile) {
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
const all = document.querySelectorAll(`[data-source-file="${CSS.escape(sourceFile)}"]`);
|
|
63
|
-
const sameElements = [];
|
|
64
|
-
for (const el of all) {
|
|
65
|
-
if (el !== element) {
|
|
66
|
-
sameElements.push(el);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return sameElements;
|
|
70
|
-
}
|
|
71
51
|
/**
|
|
72
52
|
* Find the nearest highlightable element by walking up the DOM tree
|
|
73
53
|
* @param target - The target element
|
|
@@ -3,13 +3,8 @@
|
|
|
3
3
|
* All rights reserved.
|
|
4
4
|
* For full license text, see the LICENSE.txt file
|
|
5
5
|
*/
|
|
6
|
-
/**
|
|
7
|
-
* Event Handlers Module
|
|
8
|
-
* Handles mouse and click events for highlighting
|
|
9
|
-
*/
|
|
10
6
|
interface ComponentMatcherLike {
|
|
11
7
|
findHighlightableElement: (target: HTMLElement) => HTMLElement | null;
|
|
12
|
-
findSameElements: (element: HTMLElement) => HTMLElement[];
|
|
13
8
|
}
|
|
14
9
|
interface StyleManagerLike {
|
|
15
10
|
highlightElements: (elements: HTMLElement[]) => void;
|
|
@@ -30,45 +25,16 @@ export declare class EventHandlers {
|
|
|
30
25
|
private styleManager;
|
|
31
26
|
private editableManager;
|
|
32
27
|
private communicationManager;
|
|
33
|
-
private
|
|
34
|
-
private currentHighlightedSameElements;
|
|
28
|
+
private currentHighlightedElements;
|
|
35
29
|
private selectedElement;
|
|
36
|
-
private
|
|
30
|
+
private selectedElements;
|
|
37
31
|
constructor(isInteractionsActive: () => boolean, componentMatcher: ComponentMatcherLike, styleManager: StyleManagerLike, editableManager: EditableManagerLike, communicationManager: CommunicationManagerLike);
|
|
38
|
-
/**
|
|
39
|
-
* Find the nearest highlightable element by walking up the DOM tree
|
|
40
|
-
* @param target - The target element
|
|
41
|
-
* @returns The highlightable element or null
|
|
42
|
-
*/
|
|
43
32
|
private _findHighlightableElement;
|
|
44
|
-
/**
|
|
45
|
-
* Handle mouseover event
|
|
46
|
-
* @param e - The mouseover event
|
|
47
|
-
*/
|
|
48
33
|
handleMouseOver(e: MouseEvent): void;
|
|
49
|
-
/**
|
|
50
|
-
* Handle mouseleave event
|
|
51
|
-
*/
|
|
52
34
|
handleMouseLeave(): void;
|
|
53
|
-
/**
|
|
54
|
-
* Handle click event
|
|
55
|
-
* @param e - The click event
|
|
56
|
-
*/
|
|
57
35
|
handleClick(e: MouseEvent): void;
|
|
58
|
-
/**
|
|
59
|
-
* Clear all highlights and selections
|
|
60
|
-
*/
|
|
61
36
|
clearAll(): void;
|
|
62
|
-
/**
|
|
63
|
-
* Get the currently selected element
|
|
64
|
-
* @returns The selected element
|
|
65
|
-
*/
|
|
66
37
|
getSelectedElement(): HTMLElement | null;
|
|
67
|
-
/**
|
|
68
|
-
* Get the same elements of the currently selected element
|
|
69
|
-
* @returns Array of same elements
|
|
70
|
-
*/
|
|
71
|
-
getSelectedSameElements(): HTMLElement[];
|
|
72
38
|
}
|
|
73
39
|
export {};
|
|
74
40
|
//# sourceMappingURL=eventHandlers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eventHandlers.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/eventHandlers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"eventHandlers.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/eventHandlers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,UAAU,oBAAoB;IAC7B,wBAAwB,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,WAAW,GAAG,IAAI,CAAC;CACtE;AAED,UAAU,gBAAgB;IACzB,iBAAiB,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IACrD,mBAAmB,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IAClD,gBAAgB,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;CACpD;AAED,UAAU,mBAAmB;IAC5B,kBAAkB,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACnD,cAAc,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CAC/C;AAED,UAAU,wBAAwB;IACjC,uBAAuB,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;CACxD;AAED,qBAAa,aAAa;IACzB,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,oBAAoB,CAA2B;IAEvD,OAAO,CAAC,0BAA0B,CAAgB;IAClD,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,gBAAgB,CAAgB;gBAGvC,oBAAoB,EAAE,MAAM,OAAO,EACnC,gBAAgB,EAAE,oBAAoB,EACtC,YAAY,EAAE,gBAAgB,EAC9B,eAAe,EAAE,mBAAmB,EACpC,oBAAoB,EAAE,wBAAwB;IAkB/C,OAAO,CAAC,yBAAyB;IAIjC,eAAe,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI;IAqCpC,gBAAgB,IAAI,IAAI;IAcxB,WAAW,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI;IA+ChC,QAAQ,IAAI,IAAI;IAchB,kBAAkB,IAAI,WAAW,GAAG,IAAI;CAGxC"}
|
|
@@ -3,43 +3,37 @@
|
|
|
3
3
|
* All rights reserved.
|
|
4
4
|
* For full license text, see the LICENSE.txt file
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* Event Handlers Module
|
|
8
|
+
* Handles mouse and click events for highlighting
|
|
9
|
+
*/
|
|
10
|
+
import { findElementsBySourceLocation, getSourceFromDataAttributes } from "./utils/sourceUtils.js";
|
|
6
11
|
export class EventHandlers {
|
|
7
12
|
isInteractionsActive;
|
|
8
13
|
componentMatcher;
|
|
9
14
|
styleManager;
|
|
10
15
|
editableManager;
|
|
11
16
|
communicationManager;
|
|
12
|
-
|
|
13
|
-
currentHighlightedSameElements;
|
|
17
|
+
currentHighlightedElements;
|
|
14
18
|
selectedElement;
|
|
15
|
-
|
|
19
|
+
selectedElements;
|
|
16
20
|
constructor(isInteractionsActive, componentMatcher, styleManager, editableManager, communicationManager) {
|
|
17
21
|
this.isInteractionsActive = isInteractionsActive;
|
|
18
22
|
this.componentMatcher = componentMatcher;
|
|
19
23
|
this.styleManager = styleManager;
|
|
20
24
|
this.editableManager = editableManager;
|
|
21
25
|
this.communicationManager = communicationManager;
|
|
22
|
-
this.
|
|
23
|
-
this.currentHighlightedSameElements = [];
|
|
26
|
+
this.currentHighlightedElements = [];
|
|
24
27
|
this.selectedElement = null;
|
|
25
|
-
this.
|
|
28
|
+
this.selectedElements = [];
|
|
26
29
|
// Bind methods to preserve 'this' context
|
|
27
30
|
this.handleMouseOver = this.handleMouseOver.bind(this);
|
|
28
31
|
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
|
29
32
|
this.handleClick = this.handleClick.bind(this);
|
|
30
33
|
}
|
|
31
|
-
/**
|
|
32
|
-
* Find the nearest highlightable element by walking up the DOM tree
|
|
33
|
-
* @param target - The target element
|
|
34
|
-
* @returns The highlightable element or null
|
|
35
|
-
*/
|
|
36
34
|
_findHighlightableElement(target) {
|
|
37
35
|
return this.componentMatcher.findHighlightableElement(target);
|
|
38
36
|
}
|
|
39
|
-
/**
|
|
40
|
-
* Handle mouseover event
|
|
41
|
-
* @param e - The mouseover event
|
|
42
|
-
*/
|
|
43
37
|
handleMouseOver(e) {
|
|
44
38
|
if (!this.isInteractionsActive()) {
|
|
45
39
|
return;
|
|
@@ -53,44 +47,29 @@ export class EventHandlers {
|
|
|
53
47
|
if (!element) {
|
|
54
48
|
return;
|
|
55
49
|
}
|
|
56
|
-
if (this.
|
|
50
|
+
if (this.currentHighlightedElements.includes(element) ||
|
|
57
51
|
(this.selectedElement && this.selectedElement === element)) {
|
|
58
52
|
return;
|
|
59
53
|
}
|
|
60
|
-
if (this.
|
|
61
|
-
!this.
|
|
62
|
-
this.styleManager.unhighlightElements(
|
|
63
|
-
|
|
64
|
-
...this.currentHighlightedSameElements,
|
|
65
|
-
]);
|
|
66
|
-
this.currentHighlightedSameElements = [];
|
|
54
|
+
if (this.currentHighlightedElements.length > 0 &&
|
|
55
|
+
!this.currentHighlightedElements[0].classList.contains("design-mode-selected")) {
|
|
56
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
57
|
+
this.currentHighlightedElements = [];
|
|
67
58
|
}
|
|
68
|
-
const
|
|
69
|
-
this.styleManager.highlightElements(
|
|
70
|
-
this.
|
|
71
|
-
this.currentHighlightedSameElements = sameElements;
|
|
59
|
+
const allElements = findElementsBySourceLocation(getSourceFromDataAttributes(element));
|
|
60
|
+
this.styleManager.highlightElements(allElements);
|
|
61
|
+
this.currentHighlightedElements = allElements;
|
|
72
62
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Handle mouseleave event
|
|
75
|
-
*/
|
|
76
63
|
handleMouseLeave() {
|
|
77
64
|
if (!this.isInteractionsActive()) {
|
|
78
65
|
return;
|
|
79
66
|
}
|
|
80
|
-
if (this.
|
|
81
|
-
!this.
|
|
82
|
-
this.styleManager.unhighlightElements(
|
|
83
|
-
|
|
84
|
-
...this.currentHighlightedSameElements,
|
|
85
|
-
]);
|
|
86
|
-
this.currentHighlighted = null;
|
|
87
|
-
this.currentHighlightedSameElements = [];
|
|
67
|
+
if (this.currentHighlightedElements.length > 0 &&
|
|
68
|
+
!this.currentHighlightedElements[0].classList.contains("design-mode-selected")) {
|
|
69
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
70
|
+
this.currentHighlightedElements = [];
|
|
88
71
|
}
|
|
89
72
|
}
|
|
90
|
-
/**
|
|
91
|
-
* Handle click event
|
|
92
|
-
* @param e - The click event
|
|
93
|
-
*/
|
|
94
73
|
handleClick(e) {
|
|
95
74
|
if (!this.isInteractionsActive()) {
|
|
96
75
|
return;
|
|
@@ -110,59 +89,37 @@ export class EventHandlers {
|
|
|
110
89
|
}
|
|
111
90
|
// Deselect previous element and its same elements
|
|
112
91
|
if (this.selectedElement) {
|
|
113
|
-
this.styleManager.deselectElements(
|
|
92
|
+
this.styleManager.deselectElements(this.selectedElements);
|
|
114
93
|
this.editableManager.removeEditable(this.selectedElement);
|
|
115
94
|
}
|
|
116
|
-
// Remove highlight from current highlighted
|
|
117
|
-
if (this.
|
|
118
|
-
this.styleManager.unhighlightElements(
|
|
119
|
-
|
|
120
|
-
...this.currentHighlightedSameElements,
|
|
121
|
-
]);
|
|
122
|
-
this.currentHighlighted = null;
|
|
123
|
-
this.currentHighlightedSameElements = [];
|
|
95
|
+
// Remove highlight from current highlighted elements
|
|
96
|
+
if (this.currentHighlightedElements.length > 0) {
|
|
97
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
98
|
+
this.currentHighlightedElements = [];
|
|
124
99
|
}
|
|
125
|
-
// Select new element and
|
|
100
|
+
// Select new element and all elements at the same source location
|
|
126
101
|
this.selectedElement = element;
|
|
127
|
-
const
|
|
128
|
-
this.
|
|
129
|
-
this.styleManager.selectElements(
|
|
102
|
+
const allElements = findElementsBySourceLocation(getSourceFromDataAttributes(element));
|
|
103
|
+
this.selectedElements = allElements;
|
|
104
|
+
this.styleManager.selectElements(allElements);
|
|
130
105
|
// Make text elements editable
|
|
131
106
|
this.editableManager.makeEditableIfText(element);
|
|
132
107
|
// Notify extension
|
|
133
108
|
this.communicationManager.notifyComponentSelected(element);
|
|
134
109
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Clear all highlights and selections
|
|
137
|
-
*/
|
|
138
110
|
clearAll() {
|
|
139
|
-
if (this.
|
|
140
|
-
this.styleManager.unhighlightElements(
|
|
141
|
-
|
|
142
|
-
...this.currentHighlightedSameElements,
|
|
143
|
-
]);
|
|
144
|
-
this.currentHighlighted = null;
|
|
145
|
-
this.currentHighlightedSameElements = [];
|
|
111
|
+
if (this.currentHighlightedElements.length > 0) {
|
|
112
|
+
this.styleManager.unhighlightElements(this.currentHighlightedElements);
|
|
113
|
+
this.currentHighlightedElements = [];
|
|
146
114
|
}
|
|
147
115
|
if (this.selectedElement) {
|
|
148
|
-
this.styleManager.deselectElements(
|
|
116
|
+
this.styleManager.deselectElements(this.selectedElements);
|
|
149
117
|
this.editableManager.removeEditable(this.selectedElement);
|
|
150
118
|
this.selectedElement = null;
|
|
151
|
-
this.
|
|
119
|
+
this.selectedElements = [];
|
|
152
120
|
}
|
|
153
121
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Get the currently selected element
|
|
156
|
-
* @returns The selected element
|
|
157
|
-
*/
|
|
158
122
|
getSelectedElement() {
|
|
159
123
|
return this.selectedElement;
|
|
160
124
|
}
|
|
161
|
-
/**
|
|
162
|
-
* Get the same elements of the currently selected element
|
|
163
|
-
* @returns Array of same elements
|
|
164
|
-
*/
|
|
165
|
-
getSelectedSameElements() {
|
|
166
|
-
return this.selectedSameElements;
|
|
167
|
-
}
|
|
168
125
|
}
|
|
@@ -33,7 +33,19 @@ if (typeof window !== "undefined") {
|
|
|
33
33
|
? data
|
|
34
34
|
: null;
|
|
35
35
|
if (typed && typed.type === "style-change") {
|
|
36
|
-
|
|
36
|
+
const sl = typed.sourceLocation;
|
|
37
|
+
const sourceLocation = sl && typeof sl === "object"
|
|
38
|
+
? {
|
|
39
|
+
fileName: String(sl.sourceFile ?? ""),
|
|
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);
|
|
37
49
|
}
|
|
38
50
|
if (typed && typed.type === "enable-interactions") {
|
|
39
51
|
window.enableInteractions?.();
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* All rights reserved.
|
|
4
4
|
* For full license text, see the LICENSE.txt file
|
|
5
5
|
*/
|
|
6
|
+
import { type SourceLocation } from "./utils/sourceUtils.js";
|
|
6
7
|
export declare class InteractionsController {
|
|
7
8
|
private enabled;
|
|
8
9
|
private isActive;
|
|
@@ -25,11 +26,11 @@ export declare class InteractionsController {
|
|
|
25
26
|
*/
|
|
26
27
|
disable(): void;
|
|
27
28
|
/**
|
|
28
|
-
* Apply style
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* Apply a style change to all elements at the given source location.
|
|
30
|
+
* When sourceLocation is provided (undo/redo), it is used directly.
|
|
31
|
+
* Otherwise the source location is read from the currently selected element.
|
|
31
32
|
*/
|
|
32
|
-
applyStyleChange(property: string, value: string): void;
|
|
33
|
+
applyStyleChange(property: string, value: string, sourceLocation?: SourceLocation): void;
|
|
33
34
|
/**
|
|
34
35
|
* Cleanup and remove event listeners
|
|
35
36
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactionsController.d.ts","sourceRoot":"","sources":["../../../src/design/interactions/interactionsController.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
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;IAgBxF;;OAEG;IACH,OAAO,IAAI,IAAI;CAOf"}
|
|
@@ -12,6 +12,7 @@ import { ComponentMatcher } from "./componentMatcher.js";
|
|
|
12
12
|
import { EditableManager } from "./editableManager.js";
|
|
13
13
|
import { EventHandlers } from "./eventHandlers.js";
|
|
14
14
|
import { StyleManager } from "./styleManager.js";
|
|
15
|
+
import { findElementsBySourceLocation, getSourceFromDataAttributes, } from "./utils/sourceUtils.js";
|
|
15
16
|
export class InteractionsController {
|
|
16
17
|
enabled;
|
|
17
18
|
isActive;
|
|
@@ -99,17 +100,21 @@ export class InteractionsController {
|
|
|
99
100
|
console.log("Design Mode Interactions disabled");
|
|
100
101
|
}
|
|
101
102
|
/**
|
|
102
|
-
* Apply style
|
|
103
|
-
*
|
|
104
|
-
*
|
|
103
|
+
* Apply a style change to all elements at the given source location.
|
|
104
|
+
* When sourceLocation is provided (undo/redo), it is used directly.
|
|
105
|
+
* Otherwise the source location is read from the currently selected element.
|
|
105
106
|
*/
|
|
106
|
-
applyStyleChange(property, value) {
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
selectedElement
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
applyStyleChange(property, value, sourceLocation) {
|
|
108
|
+
let location = sourceLocation ?? null;
|
|
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) {
|
|
117
|
+
el.style[property] = value;
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
/**
|
|
@@ -14,6 +14,12 @@ export interface SourceLocation {
|
|
|
14
14
|
* @returns Source location information, or null if missing.
|
|
15
15
|
*/
|
|
16
16
|
export declare function getSourceFromDataAttributes(element: HTMLElement | null | undefined): SourceLocation | null;
|
|
17
|
+
/**
|
|
18
|
+
* Find all DOM elements whose `data-source-file` attribute matches the given source location.
|
|
19
|
+
* @param location - The source location to match against
|
|
20
|
+
* @returns All matching elements (may be empty)
|
|
21
|
+
*/
|
|
22
|
+
export declare function findElementsBySourceLocation(location: SourceLocation): HTMLElement[];
|
|
17
23
|
/**
|
|
18
24
|
* Derive a human-readable label from the injected source file name, if present.
|
|
19
25
|
* @param element - The DOM element
|
|
@@ -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,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,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAiBlF"}
|
|
@@ -42,6 +42,25 @@ export function getSourceFromDataAttributes(element) {
|
|
|
42
42
|
}
|
|
43
43
|
return parseSourceFileAttribute(source);
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Find all DOM elements whose `data-source-file` attribute matches the given source location.
|
|
47
|
+
* @param location - The source location to match against
|
|
48
|
+
* @returns All matching elements (may be empty)
|
|
49
|
+
*/
|
|
50
|
+
export function findElementsBySourceLocation(location) {
|
|
51
|
+
const results = [];
|
|
52
|
+
const elements = document.querySelectorAll("[data-source-file]");
|
|
53
|
+
for (const el of elements) {
|
|
54
|
+
const elSource = getSourceFromDataAttributes(el);
|
|
55
|
+
if (elSource &&
|
|
56
|
+
elSource.fileName === location.fileName &&
|
|
57
|
+
elSource.lineNumber === location.lineNumber &&
|
|
58
|
+
elSource.columnNumber === location.columnNumber) {
|
|
59
|
+
results.push(el);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return results;
|
|
63
|
+
}
|
|
45
64
|
/**
|
|
46
65
|
* Derive a human-readable label from the injected source file name, if present.
|
|
47
66
|
* @param element - The DOM element
|
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.69.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.69.0",
|
|
49
49
|
"axios": "^1.7.7",
|
|
50
50
|
"micromatch": "^4.0.8",
|
|
51
51
|
"path-to-regexp": "^8.3.0"
|