@preference-sl/pref-viewer 2.16.0-beta.14 → 2.16.0-beta.19

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.
@@ -45,6 +45,7 @@ import { DEFAULT_LOCALE, resolveLocale, setLocale, SUPPORTED_LOCALES } from "./l
45
45
  * - clearSelectedElements / clearSelectableElements / clearSelectionState: Reset selection state fully or partially.
46
46
  * - getSelectionState: Return a snapshot of the current selection.
47
47
  * - notifyElementClick: Forward a host-side element click into the viewer event stream.
48
+ * - appendSvgNode / removeAppendedSvgNodes: Manage host-provided SVG nodes through the root facade.
48
49
  * - startTurnable / stopTurnable / toggleTurnable / getTurnableState: Control and inspect the auto-rotate state.
49
50
  * - getAnimations / playAnimation / setAnimationProgress / setAnimationLoop: Inspect and control named glTF animations.
50
51
  * - getDebugInfo: Return a diagnostic snapshot of the active 3D runtime.
@@ -61,7 +62,7 @@ import { DEFAULT_LOCALE, resolveLocale, setLocale, SUPPORTED_LOCALES } from "./l
61
62
  * - `pref-viewer-2d-viewport-changed`: proxies pan/zoom delta from the 2D controller.
62
63
  * - `pref-viewer-selection-state-changed`: emitted whenever the selectable or selected element sets change.
63
64
  * - `pref-viewer-3d-turnable-changed`: emitted after the auto-rotate state starts, stops, or is toggled.
64
- * - `pref-viewer-element-click`: emitted when the host forwards an element click via `notifyElementClick`.
65
+ * - `pref-viewer-element-click`: emitted with `{ ids: string[] }` for 2D or host-forwarded semantic clicks.
65
66
  * - Legacy `scene-*` and `drawing-*` aliases are still emitted alongside their modern equivalents for backward compatibility.
66
67
  *
67
68
  * Notes:
@@ -113,6 +114,7 @@ export default class PrefViewer extends HTMLElement {
113
114
  // Event handlers
114
115
  #handlers = {
115
116
  on2DZoomChanged: null,
117
+ on2DElementClick: null,
116
118
  onMenuApply: null,
117
119
  onViewerHoverStart: null,
118
120
  onViewerHoverEnd: null,
@@ -264,6 +266,7 @@ export default class PrefViewer extends HTMLElement {
264
266
  }
265
267
  if (this.#component2D) {
266
268
  this.#component2D.removeEventListener("pref-viewer-2d-viewport-changed", this.#handlers.on2DZoomChanged);
269
+ this.#component2D.removeEventListener("pref-viewer-2d-element-click", this.#handlers.on2DElementClick);
267
270
  this.#component2D.remove();
268
271
  }
269
272
  if (this.#component3D) {
@@ -306,9 +309,11 @@ export default class PrefViewer extends HTMLElement {
306
309
  */
307
310
  #createComponent2D() {
308
311
  this.#handlers.on2DZoomChanged = this.#on2DZoomChanged.bind(this);
312
+ this.#handlers.on2DElementClick = this.#on2DElementClick.bind(this);
309
313
  this.#component2D = document.createElement("pref-viewer-2d");
310
314
  this.#component2D.setAttribute("visible", "false");
311
315
  this.#component2D.addEventListener("pref-viewer-2d-viewport-changed", this.#handlers.on2DZoomChanged);
316
+ this.#component2D.addEventListener("pref-viewer-2d-element-click", this.#handlers.on2DElementClick);
312
317
  this.#wrapper.appendChild(this.#component2D);
313
318
  }
314
319
 
@@ -794,6 +799,8 @@ export default class PrefViewer extends HTMLElement {
794
799
  this.removeAttribute("loading-2d");
795
800
  this.setAttribute("loaded-2d", "");
796
801
 
802
+ this.#applySelectionStateTo2D();
803
+
797
804
  this.#dispatchPrefViewerEvent("pref-viewer-2d-loaded", detail ?? {});
798
805
  this.#dispatchPrefViewerEvent("drawing-loaded", detail ?? {}); // Legacy event for backward compatibility
799
806
  this.#dispatchPrefViewerEvent("pref-viewer-loaded", { result: detail ?? null });
@@ -832,6 +839,17 @@ export default class PrefViewer extends HTMLElement {
832
839
  this.#dispatchPrefViewerEvent("pref-viewer-2d-viewport-changed", detail);
833
840
  }
834
841
 
842
+ /**
843
+ * Proxy semantic click ids from the encapsulated 2D component.
844
+ * @private
845
+ * @param {CustomEvent} event - Internal component event.
846
+ * @returns {void}
847
+ */
848
+ #on2DElementClick(event) {
849
+ event.stopPropagation();
850
+ this.notifyElementClick(event.detail?.ids ?? []);
851
+ }
852
+
835
853
  /**
836
854
  * Handles the custom "pref-viewer-3d-render-settings-change-requested" event emitted by the menu component.
837
855
  * Forwards the requested settings to the render-application pipeline.
@@ -1102,6 +1120,11 @@ export default class PrefViewer extends HTMLElement {
1102
1120
  this.#dispatchPrefViewerEvent("pref-viewer-selection-state-changed", this.getSelectionState());
1103
1121
  }
1104
1122
 
1123
+ /** Apply the current semantic snapshot to the 2D renderer when available. */
1124
+ #applySelectionStateTo2D() {
1125
+ this.#component2D?.applySelectionState?.(this.getSelectionState());
1126
+ }
1127
+
1105
1128
  /**
1106
1129
  * Dispatches a `pref-viewer-3d-turnable-changed` event carrying the current turnable snapshot.
1107
1130
  * Called after `startTurnable`, `stopTurnable`, or `toggleTurnable` mutates the internal state.
@@ -1407,13 +1430,14 @@ export default class PrefViewer extends HTMLElement {
1407
1430
 
1408
1431
  /**
1409
1432
  * Defines the elements that the host application considers selectable.
1410
- * This method stores the contract state; visual selection rendering is handled by host-specific follow-up work.
1433
+ * The normalized state is rendered by the 2D component as non-destructive yellow SVG clones.
1411
1434
  * @public
1412
1435
  * @param {Array<string|{id?: string, Id?: string}>} elements - Element ids or objects containing id/Id.
1413
1436
  * @returns {object} Current selection state.
1414
1437
  */
1415
1438
  setSelectableElements(elements = []) {
1416
1439
  this.#selectionState.selectableIds = this.#normalizeIdList(elements);
1440
+ this.#applySelectionStateTo2D();
1417
1441
  this.#emitSelectionStateChanged();
1418
1442
  return this.getSelectionState();
1419
1443
  }
@@ -1428,6 +1452,7 @@ export default class PrefViewer extends HTMLElement {
1428
1452
  const selectedItems = this.#normalizeSelectionItems(elements);
1429
1453
  this.#selectionState.selectedItems = selectedItems;
1430
1454
  this.#selectionState.selectedIds = selectedItems.map((item) => item.id);
1455
+ this.#applySelectionStateTo2D();
1431
1456
  this.#emitSelectionStateChanged();
1432
1457
  return this.getSelectionState();
1433
1458
  }
@@ -1435,12 +1460,16 @@ export default class PrefViewer extends HTMLElement {
1435
1460
  /**
1436
1461
  * Applies a complete host selection state in one call.
1437
1462
  * @public
1438
- * @param {{selectableIds?: string[], selectables?: string[], selectedItems?: object[], selectedIds?: string[], selected?: string[]}} state - Selection state with selectableIds/selectables and selectedItems/selectedIds/selected.
1463
+ * @param {{selectableIds?: Array<string|object>, selectables?: Array<string|object>, selectedItems?: Array<string|object>, selectedIds?: Array<string|object>, selected?: Array<string|object>}} state - Selection state with selectableIds/selectables and selectedItems/selectedIds/selected.
1439
1464
  * @returns {object} Current selection state.
1440
1465
  */
1441
1466
  applySelectionState(state = {}) {
1442
1467
  this.#selectionState.selectableIds = this.#normalizeIdList(state.selectableIds ?? state.selectables ?? []);
1443
- this.setSelectedElements(state.selectedItems ?? state.selectedIds ?? state.selected ?? []);
1468
+ const selectedItems = this.#normalizeSelectionItems(state.selectedItems ?? state.selectedIds ?? state.selected ?? []);
1469
+ this.#selectionState.selectedItems = selectedItems;
1470
+ this.#selectionState.selectedIds = selectedItems.map((item) => item.id);
1471
+ this.#applySelectionStateTo2D();
1472
+ this.#emitSelectionStateChanged();
1444
1473
  return this.getSelectionState();
1445
1474
  }
1446
1475
 
@@ -1452,6 +1481,7 @@ export default class PrefViewer extends HTMLElement {
1452
1481
  clearSelectedElements() {
1453
1482
  this.#selectionState.selectedIds = [];
1454
1483
  this.#selectionState.selectedItems = [];
1484
+ this.#applySelectionStateTo2D();
1455
1485
  this.#emitSelectionStateChanged();
1456
1486
  return this.getSelectionState();
1457
1487
  }
@@ -1463,6 +1493,7 @@ export default class PrefViewer extends HTMLElement {
1463
1493
  */
1464
1494
  clearSelectableElements() {
1465
1495
  this.#selectionState.selectableIds = [];
1496
+ this.#applySelectionStateTo2D();
1466
1497
  this.#emitSelectionStateChanged();
1467
1498
  return this.getSelectionState();
1468
1499
  }
@@ -1478,6 +1509,7 @@ export default class PrefViewer extends HTMLElement {
1478
1509
  selectedIds: [],
1479
1510
  selectedItems: [],
1480
1511
  };
1512
+ this.#applySelectionStateTo2D();
1481
1513
  this.#emitSelectionStateChanged();
1482
1514
  return this.getSelectionState();
1483
1515
  }
@@ -1499,17 +1531,53 @@ export default class PrefViewer extends HTMLElement {
1499
1531
  }
1500
1532
 
1501
1533
  /**
1502
- * Contract hook for host-driven element click forwarding.
1503
- * Internal 2D/3D hit-testing can call this once it exposes stable element ids.
1534
+ * Publishes host-forwarded semantic ids through the same exact event contract as 2D hit-testing.
1504
1535
  * @public
1505
- * @param {object|string} element - Element id or detail object.
1536
+ * @param {object|string|Array<object|string>} element - Element id, descriptor, collection, or `{ ids }` detail.
1506
1537
  * @returns {void}
1507
1538
  */
1508
1539
  notifyElementClick(element) {
1509
- const detail = typeof element === "string" ? { id: element } : { ...(element ?? {}) };
1510
- this.#dispatchPrefViewerEvent("pref-viewer-element-click", {
1511
- element: detail,
1512
- });
1540
+ let candidates;
1541
+ if (Array.isArray(element)) {
1542
+ candidates = element;
1543
+ } else if (Array.isArray(element?.ids)) {
1544
+ candidates = element.ids;
1545
+ } else {
1546
+ candidates = [element?.element ?? element];
1547
+ }
1548
+ const ids = candidates
1549
+ .map((candidate) => typeof candidate === "string" ? candidate : (candidate?.id ?? candidate?.Id))
1550
+ .filter((id) => typeof id === "string" && id.length > 0);
1551
+ if (ids.length === 0) {
1552
+ return;
1553
+ }
1554
+ this.dispatchEvent(new CustomEvent("pref-viewer-element-click", {
1555
+ bubbles: true,
1556
+ cancelable: true,
1557
+ composed: true,
1558
+ detail: { ids },
1559
+ }));
1560
+ }
1561
+
1562
+ /**
1563
+ * Add a host-owned SVG node through the public root facade.
1564
+ * @public
1565
+ * @param {string} id - Owner id.
1566
+ * @param {string} svgId - Stable selector/id.
1567
+ * @param {SVGElement} svg - SVG node.
1568
+ * @returns {boolean} True when appended.
1569
+ */
1570
+ appendSvgNode(id, svgId, svg) {
1571
+ return this.#component2D?.appendSvgNode?.(id, svgId, svg) ?? false;
1572
+ }
1573
+
1574
+ /**
1575
+ * Remove every SVG node previously added through appendSvgNode.
1576
+ * @public
1577
+ * @returns {void}
1578
+ * */
1579
+ removeAppendedSvgNodes() {
1580
+ this.#component2D?.removeAppendedSvgNodes?.();
1513
1581
  }
1514
1582
 
1515
1583
  /**
package/src/styles.js CHANGED
@@ -206,6 +206,12 @@ export const PrefViewerMenu3DStyles = `
206
206
  top: var(--toggle-margin);
207
207
  right: var(--toggle-margin);
208
208
  z-index: 1200;
209
+ /* The closed panel keeps its place in the flex column (opacity:0, not display:none), so
210
+ this host spans the panel's full 300px x 70vh even when only the toggle is showing.
211
+ Left clickable, that empty space is an invisible catcher over the right edge of the
212
+ viewer: it swallowed the XR entry button and orbiting alike. The toggle and the panel
213
+ each flip back to auto when they become usable, so nothing else is needed. */
214
+ pointer-events: none;
209
215
 
210
216
  font-family: var(--font-family);
211
217
  }