@preference-sl/pref-viewer 2.16.0-beta.1 → 2.16.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +13 -0
- package/README.md +486 -0
- package/package.json +7 -7
- package/src/babylonjs-animation-opening.js +11 -5
- package/src/babylonjs-controller.js +455 -903
- package/src/file-storage.js +5 -126
- package/src/gltf-resolver.js +24 -58
- package/src/localization/translations.js +5 -17
- package/src/material-utils.js +72 -0
- package/src/pref-viewer-2d.js +14 -21
- package/src/pref-viewer-3d.js +160 -193
- package/src/pref-viewer-menu-3d.js +185 -268
- package/src/pref-viewer-task.js +7 -2
- package/src/pref-viewer.js +549 -333
- package/src/render-math.js +127 -0
- package/src/styles.js +163 -179
- package/Readme.md +0 -8
package/src/pref-viewer.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import PrefViewerTask from "./pref-viewer-task.js";
|
|
2
|
-
import { PrefViewerStyles } from "./styles.js";
|
|
2
|
+
import { PREF_VIEWER_STYLES_STORAGE_KEY, PrefViewerStyles, PrefViewerStyleVariables } from "./styles.js";
|
|
3
3
|
import { DEFAULT_LOCALE, resolveLocale, setLocale, SUPPORTED_LOCALES } from "./localization/i18n.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -9,10 +9,10 @@ import { DEFAULT_LOCALE, resolveLocale, setLocale, SUPPORTED_LOCALES } from "./l
|
|
|
9
9
|
* - Hosts synchronized 2D (SVG) and 3D (Babylon.js) canvases plus a render-settings menu inside a single shadow DOM wrapper.
|
|
10
10
|
* - Queues mutations (config/model/scene/materials/drawing/options/culture) to guarantee deterministic execution and avoid race conditions.
|
|
11
11
|
* - Normalizes remote URLs, Base64 payloads, and IndexedDB lookups before forwarding jobs to the BabylonJSController stack.
|
|
12
|
-
* - Persists the active locale to localStorage, mirrors
|
|
12
|
+
* - Persists the active locale and supported style variables to localStorage, mirrors locale to PrefViewerMenu3D, and keeps the shared i18n module in sync with the chosen culture.
|
|
13
13
|
* - Surfaces a dialog API that powers built-in download flows (GLB, glTF+ZIP, USDZ) for model-only, scene-only, or combined asset exports.
|
|
14
|
-
* - Emits granular lifecycle custom events so host applications can react to loading progress, drawing zoom changes, and menu interactions.
|
|
15
|
-
* - Exposes imperative methods for toggling visibility, updating options, switching modes, and requesting downloads without attribute juggling.
|
|
14
|
+
* - Emits granular lifecycle custom events so host applications can react to loading progress, drawing zoom changes, selection changes, and menu interactions.
|
|
15
|
+
* - Exposes imperative methods for toggling visibility, updating options, switching modes, controlling animations, managing selection state, and requesting downloads without attribute juggling.
|
|
16
16
|
* - Automatically reflects supported attributes back to the DOM so templates, frameworks, and manual scripting stay aligned with viewer state.
|
|
17
17
|
*
|
|
18
18
|
* Usage:
|
|
@@ -20,29 +20,49 @@ import { DEFAULT_LOCALE, resolveLocale, setLocale, SUPPORTED_LOCALES } from "./l
|
|
|
20
20
|
* - Feed serialized JSON strings or plain objects to `loadConfig`, `loadModel`, `loadScene`, `loadMaterials`, `loadDrawing`, or `setOptions`.
|
|
21
21
|
* - Drive localization by setting the `culture` attribute/property or invoking `setCulture("es-ES")`, which propagates to the menu and dialog copy.
|
|
22
22
|
* - Trigger downloads through the provided public methods or open the interactive dialog with `openDialog()` for user-driven selection.
|
|
23
|
-
* - Listen for `
|
|
23
|
+
* - Listen for `pref-viewer-initialized`, `pref-viewer-loading`, `pref-viewer-loaded`, `pref-viewer-error`, `pref-viewer-3d-loaded`, `pref-viewer-2d-loaded`, and `pref-viewer-2d-viewport-changed` to integrate with surrounding UX.
|
|
24
24
|
*
|
|
25
25
|
* Reactive Attributes:
|
|
26
26
|
* - config: Configuration descriptor that can bundle model/scene/materials/drawing/culture hints.
|
|
27
27
|
* - model / scene / materials / drawing: Independent asset entry points (URLs, Base64, indexed data) for each subsystem.
|
|
28
|
-
* - options: JSON describing render toggles (AA, SSAO, IBL, shadows)
|
|
28
|
+
* - options: JSON describing render toggles (AA, SSAO, IBL, shadows), animation menu, highlight, lighting, and misc viewer knobs.
|
|
29
29
|
* - culture: Locale identifier (RFC-5646 style) that resolves against `SUPPORTED_LOCALES` before applying.
|
|
30
30
|
* - mode: Switches between `2d` and `3d`, automatically hiding/showing the relevant subcomponents and menu states.
|
|
31
31
|
* - show-model / show-scene: Boolean flags to toggle visibility without reloading the underlying resources.
|
|
32
|
+
* - show-render-menu: Boolean flag to show or hide the render-settings menu (default: true).
|
|
33
|
+
* - styles: JSON style overrides. Currently supports `colorPrimary`, persisted in localStorage and exposed as `--pref-viewer-color-primary`.
|
|
32
34
|
*
|
|
33
35
|
* Public Methods (selection):
|
|
34
36
|
* - loadConfig / loadModel / loadScene / loadMaterials / loadDrawing: Queue asset ingestion jobs.
|
|
35
|
-
* - setOptions / setMode / setCulture: Update runtime behavior, render mode, or
|
|
37
|
+
* - setOptions / setIBL / setMode / setCulture / setStyles: Update runtime behavior, IBL lighting, render mode, locale, or supported visual variables.
|
|
36
38
|
* - showModel / hideModel / showScene / hideScene: Toggle visibility using the internal task queue for consistent sequencing.
|
|
37
39
|
* - zoomCenter / zoomExtentsAll / zoomIn / zoomOut: Forward zoom commands to the 2D controller when active.
|
|
38
|
-
* -
|
|
40
|
+
* - downloadModelGLB / downloadModelGLTF / downloadModelUSDZ: Export the loaded model in the requested format.
|
|
41
|
+
* - downloadSceneGLB / downloadSceneGLTF / downloadSceneUSDZ: Export the loaded environment in the requested format.
|
|
42
|
+
* - downloadModelAndSceneGLB / downloadModelAndSceneGLTF / downloadModelAndSceneUSDZ: Export the combined scene in the requested format.
|
|
39
43
|
* - openDialog / closeDialog: Manage PrefViewerDialog instances rendered inside the shadow tree.
|
|
44
|
+
* - setSelectableElements / setSelectedElements / applySelectionState: Drive the semantic selection contract.
|
|
45
|
+
* - clearSelectedElements / clearSelectableElements / clearSelectionState: Reset selection state fully or partially.
|
|
46
|
+
* - getSelectionState: Return a snapshot of the current selection.
|
|
47
|
+
* - notifyElementClick: Forward a host-side element click into the viewer event stream.
|
|
48
|
+
* - startTurnable / stopTurnable / toggleTurnable / getTurnableState: Control and inspect the auto-rotate state.
|
|
49
|
+
* - getAnimations / playAnimation / setAnimationProgress / setAnimationLoop: Inspect and control named glTF animations.
|
|
50
|
+
* - getDebugInfo: Return a diagnostic snapshot of the active 3D runtime.
|
|
40
51
|
*
|
|
41
52
|
* Public Properties:
|
|
42
|
-
* - culture, isInitialized, isLoaded, isLoading, isMode2D, isMode3D expose runtime state for host inspection.
|
|
53
|
+
* - Getters: culture, isInitialized, isLoaded, isLoading, isMode2D, isMode3D expose runtime state for host inspection.
|
|
54
|
+
* - Setters: config, culture, drawing, materials, mode, model, options, scene, and styles mirror the corresponding load/set methods for reactive bindings.
|
|
43
55
|
*
|
|
44
56
|
* Events:
|
|
45
|
-
* - `
|
|
57
|
+
* - `pref-viewer-initialized`: emitted after the host and internal components are mounted and public methods/properties are available.
|
|
58
|
+
* - `pref-viewer-loading` / `pref-viewer-loaded` / `pref-viewer-error`: generic lifecycle events that fire for both 2D and 3D pipelines.
|
|
59
|
+
* - `pref-viewer-3d-loading` / `pref-viewer-3d-loaded` / `pref-viewer-3d-error`: announce Babylon.js work start, completion, and failure.
|
|
60
|
+
* - `pref-viewer-2d-loading` / `pref-viewer-2d-loaded` / `pref-viewer-2d-error`: announce SVG pipeline start, completion, and failure.
|
|
61
|
+
* - `pref-viewer-2d-viewport-changed`: proxies pan/zoom delta from the 2D controller.
|
|
62
|
+
* - `pref-viewer-selection-state-changed`: emitted whenever the selectable or selected element sets change.
|
|
63
|
+
* - `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
|
+
* - Legacy `scene-*` and `drawing-*` aliases are still emitted alongside their modern equivalents for backward compatibility.
|
|
46
66
|
*
|
|
47
67
|
* Notes:
|
|
48
68
|
* - Internally wires PrefViewerMenu3D hover/apply events to PrefViewer3D render settings, ensuring persisted toggles stay in sync with BabylonJSController.
|
|
@@ -56,6 +76,16 @@ export default class PrefViewer extends HTMLElement {
|
|
|
56
76
|
#isLoaded = false;
|
|
57
77
|
#isLoading = false;
|
|
58
78
|
#mode = "3d";
|
|
79
|
+
#selectionState = {
|
|
80
|
+
selectableIds: [],
|
|
81
|
+
selectedIds: [],
|
|
82
|
+
selectedItems: [],
|
|
83
|
+
};
|
|
84
|
+
#turnableState = {
|
|
85
|
+
running: false,
|
|
86
|
+
speed: 1,
|
|
87
|
+
direction: "clockwise",
|
|
88
|
+
};
|
|
59
89
|
|
|
60
90
|
// Task queue for sequential processing
|
|
61
91
|
#taskQueue = [];
|
|
@@ -66,7 +96,12 @@ export default class PrefViewer extends HTMLElement {
|
|
|
66
96
|
#cultureList = SUPPORTED_LOCALES;
|
|
67
97
|
#culture = "";
|
|
68
98
|
#cultureSuppressAttribute = false;
|
|
69
|
-
|
|
99
|
+
|
|
100
|
+
// Runtime style management
|
|
101
|
+
#STYLES_STORAGE_KEY = PREF_VIEWER_STYLES_STORAGE_KEY;
|
|
102
|
+
#styles = {
|
|
103
|
+
colorPrimary: PrefViewerStyleVariables.colorPrimary,
|
|
104
|
+
};
|
|
70
105
|
|
|
71
106
|
// References to internal components
|
|
72
107
|
#wrapper = null;
|
|
@@ -99,6 +134,8 @@ export default class PrefViewer extends HTMLElement {
|
|
|
99
134
|
} else {
|
|
100
135
|
this.#culture = setLocale(this.#cultureInitial);
|
|
101
136
|
}
|
|
137
|
+
|
|
138
|
+
this.#applyStyles(this.#stylesReadPersisted(), { persist: false, applyCssVariables: false });
|
|
102
139
|
}
|
|
103
140
|
|
|
104
141
|
/**
|
|
@@ -107,13 +144,13 @@ export default class PrefViewer extends HTMLElement {
|
|
|
107
144
|
* @returns {string[]} Array of attribute names to observe.
|
|
108
145
|
*/
|
|
109
146
|
static get observedAttributes() {
|
|
110
|
-
return ["config", "culture", "drawing", "materials", "mode", "model", "scene", "options", "show-model", "show-scene", "show-
|
|
147
|
+
return ["config", "culture", "drawing", "materials", "mode", "model", "scene", "options", "show-model", "show-scene", "show-render-menu", "styles"];
|
|
111
148
|
}
|
|
112
149
|
|
|
113
150
|
/**
|
|
114
151
|
* Observes changes to specific attributes and triggers corresponding actions.
|
|
115
152
|
* Loads configuration, drawing, model, scene, materials, or options when their attributes change.
|
|
116
|
-
* Toggles model or scene visibility when "show-model" or "show-scene" attributes change.
|
|
153
|
+
* Toggles model or scene visibility when "show-model" or "show-scene" attributes change, recreates the 3D menu when "show-render-menu" changes, and queues style overrides when "styles" changes.
|
|
117
154
|
* @public
|
|
118
155
|
* @param {string} name - The name of the changed attribute.
|
|
119
156
|
* @param {*} _old - The previous value of the attribute (unused).
|
|
@@ -121,9 +158,6 @@ export default class PrefViewer extends HTMLElement {
|
|
|
121
158
|
* @returns {void}
|
|
122
159
|
*/
|
|
123
160
|
attributeChangedCallback(name, _old, value) {
|
|
124
|
-
if (this.#suppressVisibilityAttributeCallback && (name === "show-model" || name === "show-scene")) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
161
|
switch (name) {
|
|
128
162
|
case "config":
|
|
129
163
|
this.loadConfig(value);
|
|
@@ -178,16 +212,12 @@ export default class PrefViewer extends HTMLElement {
|
|
|
178
212
|
const showScene = value.toLowerCase() === "true";
|
|
179
213
|
showScene ? this.showScene() : this.hideScene();
|
|
180
214
|
break;
|
|
181
|
-
case "show-menu":
|
|
215
|
+
case "show-render-menu":
|
|
182
216
|
// Recreate menu to apply show/hide setting
|
|
183
217
|
this.#createMenu3D();
|
|
184
218
|
break;
|
|
185
|
-
case "
|
|
186
|
-
|
|
187
|
-
case "highlight-color":
|
|
188
|
-
case "highlight-enabled":
|
|
189
|
-
// Pass these attributes to the 3D component and menu
|
|
190
|
-
this.#updateComponentAttributes(name, value);
|
|
219
|
+
case "styles":
|
|
220
|
+
this.setStyles(value);
|
|
191
221
|
break;
|
|
192
222
|
}
|
|
193
223
|
}
|
|
@@ -199,24 +229,24 @@ export default class PrefViewer extends HTMLElement {
|
|
|
199
229
|
* @returns {void|boolean} Returns false if initialization fails; otherwise void.
|
|
200
230
|
*/
|
|
201
231
|
connectedCallback() {
|
|
202
|
-
this.#
|
|
203
|
-
this.#
|
|
204
|
-
this.shadowRoot.append(this.#wrapper);
|
|
205
|
-
|
|
206
|
-
const style = document.createElement("style");
|
|
207
|
-
style.textContent = PrefViewerStyles;
|
|
208
|
-
this.shadowRoot.append(style);
|
|
209
|
-
|
|
232
|
+
this.#createShadowDOM();
|
|
233
|
+
this.#stylesApplyCssVariables();
|
|
210
234
|
this.#createComponent3D();
|
|
211
235
|
this.#createComponent2D();
|
|
212
236
|
this.#createMenu3D();
|
|
213
237
|
|
|
214
|
-
|
|
215
|
-
this.setMode();
|
|
216
|
-
}
|
|
238
|
+
this.setMode(this.getAttribute("mode") ?? this.#mode);
|
|
217
239
|
|
|
218
240
|
this.#isInitialized = true;
|
|
219
|
-
|
|
241
|
+
Promise.resolve().then(() => {
|
|
242
|
+
if (!this.isConnected || !this.#isInitialized) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this.#dispatchPrefViewerEvent("pref-viewer-initialized", {
|
|
246
|
+
isInitialized: this.#isInitialized,
|
|
247
|
+
});
|
|
248
|
+
this.#processNextTask();
|
|
249
|
+
});
|
|
220
250
|
}
|
|
221
251
|
|
|
222
252
|
/**
|
|
@@ -233,21 +263,41 @@ export default class PrefViewer extends HTMLElement {
|
|
|
233
263
|
this.#dialog?.remove();
|
|
234
264
|
}
|
|
235
265
|
if (this.#component2D) {
|
|
236
|
-
this.#component2D.removeEventListener("
|
|
266
|
+
this.#component2D.removeEventListener("pref-viewer-2d-viewport-changed", this.#handlers.on2DZoomChanged);
|
|
237
267
|
this.#component2D.remove();
|
|
238
268
|
}
|
|
239
269
|
if (this.#component3D) {
|
|
240
|
-
this.#component3D.removeEventListener("
|
|
241
|
-
this.#component3D.removeEventListener("
|
|
270
|
+
this.#component3D.removeEventListener("pref-viewer-3d-loaded", this.#handlers.on3DSceneLoaded);
|
|
271
|
+
this.#component3D.removeEventListener("pref-viewer-3d-error", this.#handlers.on3DSceneError);
|
|
242
272
|
this.#component3D.remove();
|
|
243
273
|
}
|
|
244
274
|
if (this.#menu3D) {
|
|
245
|
-
this.#menu3D.removeEventListener("pref-viewer-
|
|
275
|
+
this.#menu3D.removeEventListener("pref-viewer-3d-render-settings-change-requested", this.#handlers.onMenuApply);
|
|
246
276
|
this.#menu3D.remove();
|
|
247
277
|
this.#menu3D = null;
|
|
248
278
|
}
|
|
249
279
|
}
|
|
250
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Creates the root shadow DOM structure and host styles.
|
|
283
|
+
* @private
|
|
284
|
+
* @returns {void}
|
|
285
|
+
*/
|
|
286
|
+
#createShadowDOM() {
|
|
287
|
+
this.#wrapper = document.createElement("div");
|
|
288
|
+
this.#wrapper.classList.add("pref-viewer-wrapper");
|
|
289
|
+
this.shadowRoot.append(this.#wrapper);
|
|
290
|
+
|
|
291
|
+
const link = document.createElement("link");
|
|
292
|
+
link.setAttribute("rel", "stylesheet");
|
|
293
|
+
link.setAttribute("href", "https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900;1,100..900&display=swap");
|
|
294
|
+
this.shadowRoot.append(link);
|
|
295
|
+
|
|
296
|
+
const style = document.createElement("style");
|
|
297
|
+
style.textContent = PrefViewerStyles;
|
|
298
|
+
this.shadowRoot.append(style);
|
|
299
|
+
}
|
|
300
|
+
|
|
251
301
|
/**
|
|
252
302
|
* Creates and appends the 2D viewer component to the shadow DOM.
|
|
253
303
|
* Sets the "visible" attribute to true by default.
|
|
@@ -258,7 +308,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
258
308
|
this.#handlers.on2DZoomChanged = this.#on2DZoomChanged.bind(this);
|
|
259
309
|
this.#component2D = document.createElement("pref-viewer-2d");
|
|
260
310
|
this.#component2D.setAttribute("visible", "false");
|
|
261
|
-
this.#component2D.addEventListener("
|
|
311
|
+
this.#component2D.addEventListener("pref-viewer-2d-viewport-changed", this.#handlers.on2DZoomChanged);
|
|
262
312
|
this.#wrapper.appendChild(this.#component2D);
|
|
263
313
|
}
|
|
264
314
|
|
|
@@ -271,15 +321,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
271
321
|
#createComponent3D() {
|
|
272
322
|
this.#component3D = document.createElement("pref-viewer-3d");
|
|
273
323
|
this.#component3D.setAttribute("visible", "false");
|
|
274
|
-
|
|
275
|
-
// Pass new configuration attributes to 3D component
|
|
276
|
-
const newAttrs = ["show-animation-menu", "highlight-color", "highlight-enabled"];
|
|
277
|
-
for (const attr of newAttrs) {
|
|
278
|
-
if (this.hasAttribute(attr)) {
|
|
279
|
-
this.#component3D.setAttribute(attr, this.getAttribute(attr));
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
324
|
+
|
|
283
325
|
this.#handlers.on3DSceneLoaded = () => {
|
|
284
326
|
this.#menu3DSyncSettings();
|
|
285
327
|
this.#menu3D?.setApplying(false);
|
|
@@ -287,15 +329,15 @@ export default class PrefViewer extends HTMLElement {
|
|
|
287
329
|
this.#handlers.on3DSceneError = () => {
|
|
288
330
|
this.#menu3D?.setApplying(false, true);
|
|
289
331
|
};
|
|
290
|
-
this.#component3D.addEventListener("
|
|
291
|
-
this.#component3D.addEventListener("
|
|
332
|
+
this.#component3D.addEventListener("pref-viewer-3d-loaded", this.#handlers.on3DSceneLoaded);
|
|
333
|
+
this.#component3D.addEventListener("pref-viewer-3d-error", this.#handlers.on3DSceneError);
|
|
292
334
|
this.#wrapper.appendChild(this.#component3D);
|
|
293
335
|
}
|
|
294
336
|
|
|
295
337
|
/**
|
|
296
338
|
* Creates (or recreates) the PrefViewerMenu3D element, wires hover/apply handlers, and syncs it with the 3D component.
|
|
297
339
|
* When a menu already exists it is removed so the DOM stays clean.
|
|
298
|
-
* Respects the "show-menu" attribute to determine if menu should be visible.
|
|
340
|
+
* Respects the "show-render-menu" attribute to determine if the menu should be visible.
|
|
299
341
|
* @private
|
|
300
342
|
* @returns {void}
|
|
301
343
|
*/
|
|
@@ -304,7 +346,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
304
346
|
return;
|
|
305
347
|
}
|
|
306
348
|
if (this.#menu3D) {
|
|
307
|
-
this.#menu3D.removeEventListener("pref-viewer-
|
|
349
|
+
this.#menu3D.removeEventListener("pref-viewer-3d-render-settings-change-requested", this.#handlers.onMenuApply);
|
|
308
350
|
this.#menu3D.remove();
|
|
309
351
|
this.#menu3D = null;
|
|
310
352
|
}
|
|
@@ -318,13 +360,13 @@ export default class PrefViewer extends HTMLElement {
|
|
|
318
360
|
}
|
|
319
361
|
|
|
320
362
|
// Check if menu should be shown (default: true if not specified)
|
|
321
|
-
const
|
|
322
|
-
const
|
|
363
|
+
const show3DMenuAttr = this.getAttribute("show-render-menu");
|
|
364
|
+
const show3DMenu = show3DMenuAttr === null || show3DMenuAttr === "true";
|
|
323
365
|
|
|
324
|
-
if (!
|
|
366
|
+
if (!show3DMenu) {
|
|
325
367
|
return;
|
|
326
368
|
}
|
|
327
|
-
|
|
369
|
+
|
|
328
370
|
this.#menu3D = document.createElement("pref-viewer-menu-3d");
|
|
329
371
|
if (typeof this.#menu3D.setCulture === "function") {
|
|
330
372
|
this.#menu3D.setCulture(this.#culture);
|
|
@@ -332,57 +374,22 @@ export default class PrefViewer extends HTMLElement {
|
|
|
332
374
|
this.#menu3D.setAttribute("culture", this.#culture);
|
|
333
375
|
}
|
|
334
376
|
|
|
335
|
-
// Pass illumination-related attributes to menu
|
|
336
|
-
const illumAttrs = ["show-illumination", "illumination-color"];
|
|
337
|
-
for (const attr of illumAttrs) {
|
|
338
|
-
if (this.hasAttribute(attr)) {
|
|
339
|
-
this.#menu3D.setAttribute(attr, this.getAttribute(attr));
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
377
|
if (!this.#handlers.onMenuApply) {
|
|
344
378
|
this.#handlers.onMenuApply = this.#onMenuApply.bind(this);
|
|
345
379
|
}
|
|
346
380
|
this.#handlers.onViewerHoverStart = () => this.#menu3D.setViewerHover(true);
|
|
347
381
|
this.#handlers.onViewerHoverEnd = () => this.#menu3D.setViewerHover(false);
|
|
348
382
|
|
|
349
|
-
this.#menu3D.addEventListener("pref-viewer-
|
|
383
|
+
this.#menu3D.addEventListener("pref-viewer-3d-render-settings-change-requested", this.#handlers.onMenuApply);
|
|
350
384
|
this.#wrapper.addEventListener("mouseenter", this.#handlers.onViewerHoverStart);
|
|
351
385
|
this.#wrapper.addEventListener("mouseleave", this.#handlers.onViewerHoverEnd);
|
|
352
386
|
|
|
353
387
|
this.#wrapper.appendChild(this.#menu3D);
|
|
354
388
|
this.#menu3DSyncSettings();
|
|
355
|
-
this.#
|
|
389
|
+
this.#menu3DUpdateElementAvailability();
|
|
356
390
|
this.#menu3DUpdateAvailability();
|
|
357
391
|
}
|
|
358
392
|
|
|
359
|
-
/**
|
|
360
|
-
* Updates attributes on child components (3D viewer and menu) based on parent attributes.
|
|
361
|
-
* @private
|
|
362
|
-
* @param {string} name - Attribute name.
|
|
363
|
-
* @param {*} value - Attribute value.
|
|
364
|
-
* @returns {void}
|
|
365
|
-
*/
|
|
366
|
-
#updateComponentAttributes(name, value) {
|
|
367
|
-
// Pass to 3D component
|
|
368
|
-
if (this.#component3D) {
|
|
369
|
-
if (value === null) {
|
|
370
|
-
this.#component3D.removeAttribute(name);
|
|
371
|
-
} else {
|
|
372
|
-
this.#component3D.setAttribute(name, value);
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// Pass to menu
|
|
377
|
-
if (this.#menu3D) {
|
|
378
|
-
if (value === null) {
|
|
379
|
-
this.#menu3D.removeAttribute(name);
|
|
380
|
-
} else {
|
|
381
|
-
this.#menu3D.setAttribute(name, value);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
393
|
/**
|
|
387
394
|
* Reads the last persisted locale from localStorage so the viewer restores user preference.
|
|
388
395
|
* @private
|
|
@@ -432,6 +439,84 @@ export default class PrefViewer extends HTMLElement {
|
|
|
432
439
|
this.#cultureSuppressAttribute = false;
|
|
433
440
|
}
|
|
434
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Reads persisted style overrides from localStorage.
|
|
444
|
+
* @private
|
|
445
|
+
* @returns {object} Persisted style overrides, or an empty object when unavailable.
|
|
446
|
+
*/
|
|
447
|
+
#stylesReadPersisted() {
|
|
448
|
+
if (typeof window === "undefined") {
|
|
449
|
+
return {};
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
const serialized = window.localStorage?.getItem(this.#STYLES_STORAGE_KEY);
|
|
453
|
+
return serialized ? JSON.parse(serialized) : {};
|
|
454
|
+
} catch (_error) {
|
|
455
|
+
return {};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Persists current style overrides in localStorage.
|
|
461
|
+
* @private
|
|
462
|
+
* @returns {void}
|
|
463
|
+
*/
|
|
464
|
+
#stylesPersist() {
|
|
465
|
+
if (typeof window === "undefined") {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
window.localStorage?.setItem(this.#STYLES_STORAGE_KEY, JSON.stringify(this.#styles));
|
|
470
|
+
} catch (_error) {}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Keeps the host CSS custom properties in sync with the style variables.
|
|
475
|
+
* @private
|
|
476
|
+
* @returns {void}
|
|
477
|
+
*/
|
|
478
|
+
#stylesApplyCssVariables() {
|
|
479
|
+
this.style.setProperty("--pref-viewer-color-primary", this.#styles.colorPrimary);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Applies supported style overrides to PrefViewerStyleVariables and the host CSS variables.
|
|
484
|
+
* @private
|
|
485
|
+
* @param {object} styles - Style overrides.
|
|
486
|
+
* @param {object} [options] - Apply options.
|
|
487
|
+
* @param {boolean} [options.persist=true] - Whether to persist the resolved styles.
|
|
488
|
+
* @param {boolean} [options.applyCssVariables=true] - Whether to update host CSS custom properties immediately.
|
|
489
|
+
* @returns {boolean} True when at least one supported style was applied.
|
|
490
|
+
*/
|
|
491
|
+
#applyStyles(styles, options = {}) {
|
|
492
|
+
const { persist = true, applyCssVariables = true } = options;
|
|
493
|
+
if (!styles || typeof styles !== "object") {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const nextStyles = {};
|
|
498
|
+
if (typeof styles.colorPrimary === "string" && styles.colorPrimary.trim().length > 0) {
|
|
499
|
+
nextStyles.colorPrimary = styles.colorPrimary.trim();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (!Object.keys(nextStyles).length) {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
this.#styles = {
|
|
507
|
+
...this.#styles,
|
|
508
|
+
...nextStyles,
|
|
509
|
+
};
|
|
510
|
+
PrefViewerStyleVariables.colorPrimary = this.#styles.colorPrimary;
|
|
511
|
+
if (applyCssVariables) {
|
|
512
|
+
this.#stylesApplyCssVariables();
|
|
513
|
+
}
|
|
514
|
+
if (persist) {
|
|
515
|
+
this.#stylesPersist();
|
|
516
|
+
}
|
|
517
|
+
return true;
|
|
518
|
+
}
|
|
519
|
+
|
|
435
520
|
/**
|
|
436
521
|
* Synchronizes the menu switches with the current render settings reported by PrefViewer3D.
|
|
437
522
|
* Skips the update when either the menu or the getter API is unavailable.
|
|
@@ -471,37 +556,45 @@ export default class PrefViewer extends HTMLElement {
|
|
|
471
556
|
* @private
|
|
472
557
|
* @returns {void}
|
|
473
558
|
*/
|
|
474
|
-
#
|
|
559
|
+
#menu3DUpdateElementAvailability() {
|
|
475
560
|
if (!this.#menu3D) {
|
|
476
561
|
return;
|
|
477
562
|
}
|
|
478
563
|
const iblAvailable = this.#component3D?.isIBLAvailable?.() === true;
|
|
479
|
-
this.#
|
|
564
|
+
const animationMenuEnabled = this.#component3D?.isAnimationMenuEnabled?.() === true;
|
|
565
|
+
const lightingTimeOfDayEnabled = this.#component3D?.isLightingTimeOfDayAvailable?.() === true;
|
|
566
|
+
this.#menu3D.setElementEnabled?.("iblEnabled", iblAvailable);
|
|
567
|
+
this.#menu3D.setElementVisible?.("showAnimationMenu", false);
|
|
568
|
+
this.#menu3D.setElementVisible?.("highlightEnabled", animationMenuEnabled);
|
|
569
|
+
this.#menu3D.setElementVisible?.("highlightColor", animationMenuEnabled);
|
|
570
|
+
this.#menu3D.setElementVisible?.("showLightingTimeOfDay", false);
|
|
571
|
+
this.#menu3D.setElementVisible?.("lightingTimeOfDay", lightingTimeOfDayEnabled);
|
|
480
572
|
}
|
|
481
573
|
|
|
482
574
|
/**
|
|
483
|
-
* Applies render
|
|
484
|
-
*
|
|
575
|
+
* Applies render settings via PrefViewer3D.
|
|
576
|
+
* Reload-capable toggles lock the menu while they run; partial highlight/lighting updates are forwarded immediately.
|
|
485
577
|
* @private
|
|
486
|
-
* @param {object} settings - Partial render settings
|
|
578
|
+
* @param {object} settings - Partial render settings requested by the menu.
|
|
487
579
|
* @returns {Promise<void>}
|
|
488
580
|
*/
|
|
489
581
|
async #applyRenderSettings(settings) {
|
|
490
582
|
if (!this.#component3D) {
|
|
491
583
|
return;
|
|
492
584
|
}
|
|
493
|
-
const { highlightEnabled: _he, highlightColor: _hc,
|
|
585
|
+
const { showAnimationMenu: _sam, highlightEnabled: _he, highlightColor: _hc, showLightingTimeOfDay: _slt, lightingTimeOfDay: _lt, persist: _persist, ...reloadSettings } = settings ?? {};
|
|
494
586
|
const hasReloadSettings = Object.keys(reloadSettings).length > 0;
|
|
495
587
|
if (hasReloadSettings) {
|
|
496
588
|
this.#menu3D?.setApplying(true);
|
|
497
589
|
}
|
|
498
590
|
try {
|
|
499
591
|
const result = await this.#component3D.applyRenderSettings(settings);
|
|
592
|
+
const controllerSettings = this.#component3D.getRenderSettings();
|
|
593
|
+
if (controllerSettings) {
|
|
594
|
+
this.#menu3D?.setSettings(controllerSettings);
|
|
595
|
+
}
|
|
596
|
+
this.#menu3DUpdateElementAvailability();
|
|
500
597
|
if (!result?.changed) {
|
|
501
|
-
const controllerSettings = this.#component3D.getRenderSettings();
|
|
502
|
-
if (controllerSettings) {
|
|
503
|
-
this.#menu3D?.setSettings(controllerSettings);
|
|
504
|
-
}
|
|
505
598
|
this.#menu3D?.setApplying(false);
|
|
506
599
|
}
|
|
507
600
|
} catch (error) {
|
|
@@ -552,10 +645,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
552
645
|
*/
|
|
553
646
|
#addTaskToQueue(value, type) {
|
|
554
647
|
const task = new PrefViewerTask(value, type);
|
|
555
|
-
if (
|
|
556
|
-
task.type === PrefViewerTask.Types.Options ||
|
|
557
|
-
task.type === PrefViewerTask.Types.Drawing
|
|
558
|
-
) {
|
|
648
|
+
if (task.type === PrefViewerTask.Types.Options || task.type === PrefViewerTask.Types.Drawing || task.type === PrefViewerTask.Types.Styles) {
|
|
559
649
|
this.#taskQueue = this.#taskQueue.filter((queuedTask) => queuedTask.type !== task.type);
|
|
560
650
|
}
|
|
561
651
|
this.#taskQueue.push(task);
|
|
@@ -601,6 +691,9 @@ export default class PrefViewer extends HTMLElement {
|
|
|
601
691
|
case PrefViewerTask.Types.Options:
|
|
602
692
|
this.#processOptions(task.value);
|
|
603
693
|
break;
|
|
694
|
+
case PrefViewerTask.Types.Styles:
|
|
695
|
+
this.#processStyles(task.value);
|
|
696
|
+
break;
|
|
604
697
|
case PrefViewerTask.Types.Visibility:
|
|
605
698
|
this.#processVisibility(task.value);
|
|
606
699
|
break;
|
|
@@ -620,24 +713,16 @@ export default class PrefViewer extends HTMLElement {
|
|
|
620
713
|
this.removeAttribute("loaded-3d");
|
|
621
714
|
this.setAttribute("loading-3d", "");
|
|
622
715
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
composed: true,
|
|
627
|
-
};
|
|
628
|
-
this.dispatchEvent(new CustomEvent("scene-loading", customEventOptions));
|
|
716
|
+
this.#dispatchPrefViewerEvent("pref-viewer-3d-loading");
|
|
717
|
+
this.#dispatchPrefViewerEvent("scene-loading"); // Legacy event for backward compatibility
|
|
718
|
+
this.#dispatchPrefViewerEvent("pref-viewer-loading");
|
|
629
719
|
}
|
|
630
720
|
|
|
631
721
|
/**
|
|
632
722
|
* Handles the completion of a 3D loading operation.
|
|
633
723
|
* Updates loading state, sets attributes, dispatches a "scene-loaded" event, and processes the next task.
|
|
634
|
-
* Dispatches granular container events ("model-loaded", "environment-loaded", "materials-loaded")
|
|
635
|
-
* for each successfully loaded container before firing "scene-loaded".
|
|
636
|
-
* The independent model path may additionally emit "model-first-painted" when the first painted
|
|
637
|
-
* frame is acknowledged by the Babylon controller.
|
|
638
724
|
* @private
|
|
639
725
|
* @param {object} [detail] - Optional details to include in the event.
|
|
640
|
-
* @param {string[]} [detail.loadedContainers] - Names of containers that loaded successfully.
|
|
641
726
|
* @returns {void}
|
|
642
727
|
*/
|
|
643
728
|
#on3DLoaded(detail) {
|
|
@@ -645,42 +730,16 @@ export default class PrefViewer extends HTMLElement {
|
|
|
645
730
|
this.#isLoading = false;
|
|
646
731
|
|
|
647
732
|
this.#menu3DSyncSettings();
|
|
648
|
-
this.#
|
|
733
|
+
this.#menu3DUpdateElementAvailability();
|
|
649
734
|
|
|
650
735
|
this.removeAttribute("loading-3d");
|
|
651
736
|
this.setAttribute("loaded-3d", "");
|
|
652
737
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
environment: "environment-loaded",
|
|
659
|
-
materials: "materials-loaded",
|
|
660
|
-
};
|
|
661
|
-
if (Array.isArray(detail?.loadedContainers)) {
|
|
662
|
-
const granularEventOptions = {
|
|
663
|
-
bubbles: true,
|
|
664
|
-
cancelable: false,
|
|
665
|
-
composed: true,
|
|
666
|
-
};
|
|
667
|
-
detail.loadedContainers.forEach((containerName) => {
|
|
668
|
-
const eventName = granularEventMap[containerName];
|
|
669
|
-
if (eventName) {
|
|
670
|
-
this.dispatchEvent(new CustomEvent(eventName, granularEventOptions));
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
const customEventOptions = {
|
|
676
|
-
bubbles: true,
|
|
677
|
-
cancelable: true,
|
|
678
|
-
composed: true,
|
|
679
|
-
};
|
|
680
|
-
if (detail) {
|
|
681
|
-
customEventOptions.detail = detail;
|
|
682
|
-
}
|
|
683
|
-
this.dispatchEvent(new CustomEvent("scene-loaded", customEventOptions));
|
|
738
|
+
this.#dispatchPrefViewerEvent("pref-viewer-3d-loaded", detail ?? {});
|
|
739
|
+
this.#dispatchPrefViewerEvent("scene-loaded", detail ?? {}); // Legacy event for backward compatibility
|
|
740
|
+
this.#dispatchPrefViewerEvent("pref-viewer-loaded", {
|
|
741
|
+
result: detail ?? null,
|
|
742
|
+
});
|
|
684
743
|
}
|
|
685
744
|
|
|
686
745
|
/**
|
|
@@ -698,15 +757,9 @@ export default class PrefViewer extends HTMLElement {
|
|
|
698
757
|
this.removeAttribute("loading-3d");
|
|
699
758
|
this.removeAttribute("loaded-3d");
|
|
700
759
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
composed: true,
|
|
705
|
-
};
|
|
706
|
-
if (detail) {
|
|
707
|
-
customEventOptions.detail = detail;
|
|
708
|
-
}
|
|
709
|
-
this.dispatchEvent(new CustomEvent("scene-error", customEventOptions));
|
|
760
|
+
this.#dispatchPrefViewerEvent("pref-viewer-3d-error", detail ?? {});
|
|
761
|
+
this.#dispatchPrefViewerEvent("scene-error", detail ?? {}); // Legacy event for backward compatibility
|
|
762
|
+
this.#dispatchPrefViewerEvent("pref-viewer-error", { error: detail?.error ?? detail ?? null });
|
|
710
763
|
}
|
|
711
764
|
|
|
712
765
|
/**
|
|
@@ -722,12 +775,9 @@ export default class PrefViewer extends HTMLElement {
|
|
|
722
775
|
this.removeAttribute("loaded-2d");
|
|
723
776
|
this.setAttribute("loading-2d", "");
|
|
724
777
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
composed: true,
|
|
729
|
-
};
|
|
730
|
-
this.dispatchEvent(new CustomEvent("drawing-loading", customEventOptions));
|
|
778
|
+
this.#dispatchPrefViewerEvent("pref-viewer-2d-loading");
|
|
779
|
+
this.#dispatchPrefViewerEvent("drawing-loading"); // Legacy event for backward compatibility
|
|
780
|
+
this.#dispatchPrefViewerEvent("pref-viewer-loading");
|
|
731
781
|
}
|
|
732
782
|
|
|
733
783
|
/**
|
|
@@ -744,15 +794,9 @@ export default class PrefViewer extends HTMLElement {
|
|
|
744
794
|
this.removeAttribute("loading-2d");
|
|
745
795
|
this.setAttribute("loaded-2d", "");
|
|
746
796
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
composed: true,
|
|
751
|
-
};
|
|
752
|
-
if (detail) {
|
|
753
|
-
customEventOptions.detail = detail;
|
|
754
|
-
}
|
|
755
|
-
this.dispatchEvent(new CustomEvent("drawing-loaded", customEventOptions));
|
|
797
|
+
this.#dispatchPrefViewerEvent("pref-viewer-2d-loaded", detail ?? {});
|
|
798
|
+
this.#dispatchPrefViewerEvent("drawing-loaded", detail ?? {}); // Legacy event for backward compatibility
|
|
799
|
+
this.#dispatchPrefViewerEvent("pref-viewer-loaded", { result: detail ?? null });
|
|
756
800
|
}
|
|
757
801
|
|
|
758
802
|
/**
|
|
@@ -769,20 +813,14 @@ export default class PrefViewer extends HTMLElement {
|
|
|
769
813
|
this.removeAttribute("loading-2d");
|
|
770
814
|
this.removeAttribute("loaded-2d");
|
|
771
815
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
composed: true,
|
|
776
|
-
};
|
|
777
|
-
if (detail) {
|
|
778
|
-
customEventOptions.detail = detail;
|
|
779
|
-
}
|
|
780
|
-
this.dispatchEvent(new CustomEvent("drawing-error", customEventOptions));
|
|
816
|
+
this.#dispatchPrefViewerEvent("pref-viewer-2d-error", detail ?? {});
|
|
817
|
+
this.#dispatchPrefViewerEvent("drawing-error", detail ?? {}); // Legacy event for backward compatibility
|
|
818
|
+
this.#dispatchPrefViewerEvent("pref-viewer-error", { error: detail?.error ?? detail ?? null });
|
|
781
819
|
}
|
|
782
820
|
|
|
783
821
|
/**
|
|
784
|
-
* Handles the "
|
|
785
|
-
* Dispatches
|
|
822
|
+
* Handles the "pref-viewer-2d-viewport-changed" event from the 2D viewer component.
|
|
823
|
+
* Dispatches the public 2D viewport event, forwarding the event detail to external listeners.
|
|
786
824
|
* @private
|
|
787
825
|
* @param {CustomEvent} event - The original zoom change event from the 2D viewer.
|
|
788
826
|
* @returns {void}
|
|
@@ -790,29 +828,24 @@ export default class PrefViewer extends HTMLElement {
|
|
|
790
828
|
#on2DZoomChanged(event) {
|
|
791
829
|
event.stopPropagation();
|
|
792
830
|
event.preventDefault();
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
cancelable: true,
|
|
796
|
-
composed: true,
|
|
797
|
-
detail: event.detail,
|
|
798
|
-
};
|
|
799
|
-
this.dispatchEvent(new CustomEvent("drawing-zoom-changed", customEventOptions));
|
|
831
|
+
const detail = { ...(event.detail ?? {}) };
|
|
832
|
+
this.#dispatchPrefViewerEvent("pref-viewer-2d-viewport-changed", detail);
|
|
800
833
|
}
|
|
801
834
|
|
|
802
835
|
/**
|
|
803
|
-
* Handles the custom "pref-viewer-
|
|
836
|
+
* Handles the custom "pref-viewer-3d-render-settings-change-requested" event emitted by the menu component.
|
|
804
837
|
* Forwards the requested settings to the render-application pipeline.
|
|
805
838
|
* @private
|
|
806
839
|
* @param {CustomEvent} event - Menu event containing the `settings` payload.
|
|
807
840
|
* @returns {void}
|
|
808
841
|
*/
|
|
809
|
-
#onMenuApply(event) {
|
|
842
|
+
async #onMenuApply(event) {
|
|
810
843
|
event.stopPropagation();
|
|
811
844
|
const settings = event.detail?.settings;
|
|
812
845
|
if (!settings) {
|
|
813
846
|
return;
|
|
814
847
|
}
|
|
815
|
-
this.#applyRenderSettings(settings);
|
|
848
|
+
await this.#applyRenderSettings(settings);
|
|
816
849
|
}
|
|
817
850
|
|
|
818
851
|
/**
|
|
@@ -944,6 +977,17 @@ export default class PrefViewer extends HTMLElement {
|
|
|
944
977
|
}
|
|
945
978
|
}
|
|
946
979
|
|
|
980
|
+
/**
|
|
981
|
+
* Processes queued style overrides and advances the task queue.
|
|
982
|
+
* @private
|
|
983
|
+
* @param {object} styles - Style overrides to apply.
|
|
984
|
+
* @returns {void}
|
|
985
|
+
*/
|
|
986
|
+
#processStyles(styles) {
|
|
987
|
+
this.#applyStyles(styles);
|
|
988
|
+
this.#processNextTask();
|
|
989
|
+
}
|
|
990
|
+
|
|
947
991
|
/**
|
|
948
992
|
* Processes visibility configuration for the model and scene.
|
|
949
993
|
* Shows or hides the model and/or scene based on the config, then processes the next task.
|
|
@@ -967,6 +1011,107 @@ export default class PrefViewer extends HTMLElement {
|
|
|
967
1011
|
this.#processNextTask();
|
|
968
1012
|
}
|
|
969
1013
|
|
|
1014
|
+
/**
|
|
1015
|
+
* Constructs and dispatches a CustomEvent from the host element.
|
|
1016
|
+
* Strips any incoming `mode` field from the detail to avoid leaking stale values,
|
|
1017
|
+
* then re-attaches `mode` only for generic (non-scoped) event types.
|
|
1018
|
+
* Always appends a `timestamp` (Unix ms) so consumers can correlate events.
|
|
1019
|
+
* @private
|
|
1020
|
+
* @param {string} type - The event type string, e.g. "pref-viewer-loaded".
|
|
1021
|
+
* @param {object} [detail={}] - Arbitrary key/value pairs to include in `event.detail`.
|
|
1022
|
+
* @returns {void}
|
|
1023
|
+
*/
|
|
1024
|
+
#dispatchPrefViewerEvent(type, detail = {}) {
|
|
1025
|
+
const eventDetail = { ...(detail ?? {}) };
|
|
1026
|
+
delete eventDetail.mode;
|
|
1027
|
+
|
|
1028
|
+
const hasExplicitModeScope = type.startsWith("pref-viewer-2d-")
|
|
1029
|
+
|| type.startsWith("pref-viewer-3d-")
|
|
1030
|
+
|| type.startsWith("drawing-")
|
|
1031
|
+
|| type.startsWith("scene-");
|
|
1032
|
+
|
|
1033
|
+
if (!hasExplicitModeScope) {
|
|
1034
|
+
eventDetail.mode = this.#mode;
|
|
1035
|
+
}
|
|
1036
|
+
eventDetail.timestamp = Date.now();
|
|
1037
|
+
|
|
1038
|
+
this.dispatchEvent(
|
|
1039
|
+
new CustomEvent(type, {
|
|
1040
|
+
bubbles: true,
|
|
1041
|
+
cancelable: false,
|
|
1042
|
+
composed: true,
|
|
1043
|
+
detail: eventDetail,
|
|
1044
|
+
}),
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Normalizes a mixed array of element descriptors into a plain string-id list.
|
|
1050
|
+
* Accepts raw strings, objects with an `id` property, or objects with an `Id` property.
|
|
1051
|
+
* Non-string and empty-string results are filtered out.
|
|
1052
|
+
* @private
|
|
1053
|
+
* @param {Array<string|{id?: string, Id?: string}>} value - Array of id-like items to normalize.
|
|
1054
|
+
* @returns {string[]} Deduplicated, non-empty string ids.
|
|
1055
|
+
*/
|
|
1056
|
+
#normalizeIdList(value) {
|
|
1057
|
+
if (!Array.isArray(value)) {
|
|
1058
|
+
return [];
|
|
1059
|
+
}
|
|
1060
|
+
return value.map((item) => (typeof item === "string" ? item : (item?.id ?? item?.Id))).filter((id) => typeof id === "string" && id.length > 0);
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Normalizes a mixed array of selection descriptors into a canonical list of selection items.
|
|
1065
|
+
* Each output item contains an `id` string and a `highlights` string array.
|
|
1066
|
+
* Accepts raw id strings (expanded to `{ id, highlights: [id] }`), or objects with
|
|
1067
|
+
* `id`/`Id` and optional `highlights`/`Highlights` fields.
|
|
1068
|
+
* Items that cannot be resolved to a non-empty string id are discarded.
|
|
1069
|
+
* @private
|
|
1070
|
+
* @param {Array<string|{id?: string, Id?: string, highlights?: string[], Highlights?: string[]}>} value - Raw selection descriptors.
|
|
1071
|
+
* @returns {Array<{id: string, highlights: string[]}>} Normalized, non-null selection items.
|
|
1072
|
+
*/
|
|
1073
|
+
#normalizeSelectionItems(value) {
|
|
1074
|
+
if (!Array.isArray(value)) {
|
|
1075
|
+
return [];
|
|
1076
|
+
}
|
|
1077
|
+
return value
|
|
1078
|
+
.map((item) => {
|
|
1079
|
+
if (typeof item === "string") {
|
|
1080
|
+
return { id: item, highlights: [item] };
|
|
1081
|
+
}
|
|
1082
|
+
const id = item?.id ?? item?.Id;
|
|
1083
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
1084
|
+
return null;
|
|
1085
|
+
}
|
|
1086
|
+
const highlights = item?.highlights ?? item?.Highlights ?? [id];
|
|
1087
|
+
return {
|
|
1088
|
+
id,
|
|
1089
|
+
highlights: this.#normalizeIdList(highlights),
|
|
1090
|
+
};
|
|
1091
|
+
})
|
|
1092
|
+
.filter(Boolean);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Dispatches a `pref-viewer-selection-state-changed` event carrying the current selection snapshot.
|
|
1097
|
+
* Called whenever `selectableIds`, `selectedIds`, or `selectedItems` change.
|
|
1098
|
+
* @private
|
|
1099
|
+
* @returns {void}
|
|
1100
|
+
*/
|
|
1101
|
+
#emitSelectionStateChanged() {
|
|
1102
|
+
this.#dispatchPrefViewerEvent("pref-viewer-selection-state-changed", this.getSelectionState());
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* Dispatches a `pref-viewer-3d-turnable-changed` event carrying the current turnable snapshot.
|
|
1107
|
+
* Called after `startTurnable`, `stopTurnable`, or `toggleTurnable` mutates the internal state.
|
|
1108
|
+
* @private
|
|
1109
|
+
* @returns {void}
|
|
1110
|
+
*/
|
|
1111
|
+
#emitTurnableChanged() {
|
|
1112
|
+
this.#dispatchPrefViewerEvent("pref-viewer-3d-turnable-changed", { state: this.getTurnableState() });
|
|
1113
|
+
}
|
|
1114
|
+
|
|
970
1115
|
/**
|
|
971
1116
|
* ---------------------------
|
|
972
1117
|
* Public methods
|
|
@@ -1101,13 +1246,25 @@ export default class PrefViewer extends HTMLElement {
|
|
|
1101
1246
|
if (!config) {
|
|
1102
1247
|
return false;
|
|
1103
1248
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
this.#addTaskToQueue(config.drawing, "drawing");
|
|
1249
|
+
if (config.styles) {
|
|
1250
|
+
this.#addTaskToQueue(config.styles, "styles");
|
|
1107
1251
|
}
|
|
1108
1252
|
if (config.culture) {
|
|
1109
1253
|
this.#addTaskToQueue(config.culture, "culture");
|
|
1110
1254
|
}
|
|
1255
|
+
this.#addTaskToQueue(config, "config");
|
|
1256
|
+
if (config.drawing) {
|
|
1257
|
+
this.#addTaskToQueue(config.drawing, "drawing");
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/**
|
|
1262
|
+
* Returns a diagnostic snapshot for the active 3D runtime.
|
|
1263
|
+
* @public
|
|
1264
|
+
* @returns {object|null} Debug information, or null when the 3D component is not initialized.
|
|
1265
|
+
*/
|
|
1266
|
+
getDebugInfo() {
|
|
1267
|
+
return this.#component3D?.getDebugInfo?.() ?? null;
|
|
1111
1268
|
}
|
|
1112
1269
|
|
|
1113
1270
|
/**
|
|
@@ -1152,78 +1309,6 @@ export default class PrefViewer extends HTMLElement {
|
|
|
1152
1309
|
this.#addTaskToQueue(model, "model");
|
|
1153
1310
|
}
|
|
1154
1311
|
|
|
1155
|
-
/**
|
|
1156
|
-
* Loads the model container in parallel with any ongoing scene load, bypassing the task queue.
|
|
1157
|
-
* Dispatches "model-loading", "model-first-painted", and "model-loaded" events directly.
|
|
1158
|
-
* @public
|
|
1159
|
-
* @param {object|string} model - Model payload with storage pointer.
|
|
1160
|
-
* @returns {Promise<void>}
|
|
1161
|
-
*/
|
|
1162
|
-
async loadModelParallel(model) {
|
|
1163
|
-
model = typeof model === "string" ? JSON.parse(model) : model;
|
|
1164
|
-
if (!model || !this.#component3D) return;
|
|
1165
|
-
const eventOptions = { bubbles: true, cancelable: false, composed: true };
|
|
1166
|
-
this.dispatchEvent(new CustomEvent("model-loading", eventOptions));
|
|
1167
|
-
try {
|
|
1168
|
-
const detail = await this.#component3D.loadModelIndependent(model);
|
|
1169
|
-
if (detail?.success) {
|
|
1170
|
-
this.dispatchEvent(new CustomEvent("model-loaded", { ...eventOptions, detail }));
|
|
1171
|
-
} else {
|
|
1172
|
-
this.dispatchEvent(new CustomEvent("model-error", { ...eventOptions, detail }));
|
|
1173
|
-
}
|
|
1174
|
-
} catch (error) {
|
|
1175
|
-
this.dispatchEvent(new CustomEvent("model-error", { ...eventOptions, detail: { error } }));
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
/**
|
|
1180
|
-
* Loads the environment/scene container in parallel with any ongoing model load, bypassing the
|
|
1181
|
-
* task queue. Dispatches "scene-loading" and "scene-loaded" (or "scene-error") events directly.
|
|
1182
|
-
* @public
|
|
1183
|
-
* @param {object|string} scene - Scene payload: { storage, ibl?, visible? }
|
|
1184
|
-
* @returns {Promise<void>}
|
|
1185
|
-
*/
|
|
1186
|
-
async loadSceneParallel(scene) {
|
|
1187
|
-
scene = typeof scene === "string" ? JSON.parse(scene) : scene;
|
|
1188
|
-
if (!scene || !this.#component3D) return;
|
|
1189
|
-
const eventOptions = { bubbles: true, cancelable: false, composed: true };
|
|
1190
|
-
this.dispatchEvent(new CustomEvent("scene-loading", eventOptions));
|
|
1191
|
-
try {
|
|
1192
|
-
const detail = await this.#component3D.loadSceneIndependent(scene);
|
|
1193
|
-
if (detail?.success) {
|
|
1194
|
-
this.dispatchEvent(new CustomEvent("scene-loaded", { ...eventOptions, detail }));
|
|
1195
|
-
} else {
|
|
1196
|
-
this.dispatchEvent(new CustomEvent("scene-error", { ...eventOptions, detail }));
|
|
1197
|
-
}
|
|
1198
|
-
} catch (error) {
|
|
1199
|
-
this.dispatchEvent(new CustomEvent("scene-error", { ...eventOptions, detail: { error } }));
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
/**
|
|
1204
|
-
* Loads the materials container in parallel with any ongoing model or environment load,
|
|
1205
|
-
* bypassing the task queue. Dispatches "materials-loading" and "materials-loaded" events.
|
|
1206
|
-
* @public
|
|
1207
|
-
* @param {object|string} materials - Materials payload: { storage }
|
|
1208
|
-
* @returns {Promise<void>}
|
|
1209
|
-
*/
|
|
1210
|
-
async loadMaterialsParallel(materials) {
|
|
1211
|
-
materials = typeof materials === "string" ? JSON.parse(materials) : materials;
|
|
1212
|
-
if (!materials || !this.#component3D) return;
|
|
1213
|
-
const eventOptions = { bubbles: true, cancelable: false, composed: true };
|
|
1214
|
-
this.dispatchEvent(new CustomEvent("materials-loading", eventOptions));
|
|
1215
|
-
try {
|
|
1216
|
-
const detail = await this.#component3D.loadMaterialsIndependent(materials);
|
|
1217
|
-
if (detail?.success) {
|
|
1218
|
-
this.dispatchEvent(new CustomEvent("materials-loaded", { ...eventOptions, detail }));
|
|
1219
|
-
} else {
|
|
1220
|
-
this.dispatchEvent(new CustomEvent("materials-error", { ...eventOptions, detail }));
|
|
1221
|
-
}
|
|
1222
|
-
} catch (error) {
|
|
1223
|
-
this.dispatchEvent(new CustomEvent("materials-error", { ...eventOptions, detail: { error } }));
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
1312
|
/**
|
|
1228
1313
|
* Loads a scene/environment object or JSON string and adds it to the task queue.
|
|
1229
1314
|
* @public
|
|
@@ -1291,6 +1376,191 @@ export default class PrefViewer extends HTMLElement {
|
|
|
1291
1376
|
this.#addTaskToQueue(options, "options");
|
|
1292
1377
|
}
|
|
1293
1378
|
|
|
1379
|
+
/**
|
|
1380
|
+
* Sets supported PrefViewer style variables from an object or JSON string and adds them to the task queue.
|
|
1381
|
+
* Currently supports `colorPrimary`, which updates `PrefViewerStyleVariables.colorPrimary`, applies the host CSS custom property, and persists the value in localStorage.
|
|
1382
|
+
* @public
|
|
1383
|
+
* @param {object|string} styles - Style overrides object or JSON string.
|
|
1384
|
+
* @returns {boolean|void} Returns false if styles are invalid; otherwise void.
|
|
1385
|
+
*/
|
|
1386
|
+
setStyles(styles) {
|
|
1387
|
+
styles = typeof styles === "string" ? JSON.parse(styles) : styles;
|
|
1388
|
+
if (!styles || typeof styles !== "object") {
|
|
1389
|
+
return false;
|
|
1390
|
+
}
|
|
1391
|
+
this.#addTaskToQueue(styles, "styles");
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
/**
|
|
1395
|
+
* Sets image-based lighting options using the stable contract entry point.
|
|
1396
|
+
* @public
|
|
1397
|
+
* @param {object|string} ibl - IBL options payload.
|
|
1398
|
+
* @returns {boolean|void} Returns false if options are invalid; otherwise void.
|
|
1399
|
+
*/
|
|
1400
|
+
setIBL(ibl) {
|
|
1401
|
+
ibl = typeof ibl === "string" ? JSON.parse(ibl) : ibl;
|
|
1402
|
+
if (!ibl) {
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
return this.setOptions({ ibl });
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
/**
|
|
1409
|
+
* 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.
|
|
1411
|
+
* @public
|
|
1412
|
+
* @param {Array<string|{id?: string, Id?: string}>} elements - Element ids or objects containing id/Id.
|
|
1413
|
+
* @returns {object} Current selection state.
|
|
1414
|
+
*/
|
|
1415
|
+
setSelectableElements(elements = []) {
|
|
1416
|
+
this.#selectionState.selectableIds = this.#normalizeIdList(elements);
|
|
1417
|
+
this.#emitSelectionStateChanged();
|
|
1418
|
+
return this.getSelectionState();
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
/**
|
|
1422
|
+
* Defines the currently selected elements using the semantic PrefViewer contract.
|
|
1423
|
+
* @public
|
|
1424
|
+
* @param {Array<string|{id?: string, Id?: string, highlights?: string[], Highlights?: string[]}>} elements - Element ids or objects containing id/Id and optional highlights/Highlights.
|
|
1425
|
+
* @returns {object} Current selection state.
|
|
1426
|
+
*/
|
|
1427
|
+
setSelectedElements(elements = []) {
|
|
1428
|
+
const selectedItems = this.#normalizeSelectionItems(elements);
|
|
1429
|
+
this.#selectionState.selectedItems = selectedItems;
|
|
1430
|
+
this.#selectionState.selectedIds = selectedItems.map((item) => item.id);
|
|
1431
|
+
this.#emitSelectionStateChanged();
|
|
1432
|
+
return this.getSelectionState();
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
/**
|
|
1436
|
+
* Applies a complete host selection state in one call.
|
|
1437
|
+
* @public
|
|
1438
|
+
* @param {{selectableIds?: string[], selectables?: string[], selectedItems?: object[], selectedIds?: string[], selected?: string[]}} state - Selection state with selectableIds/selectables and selectedItems/selectedIds/selected.
|
|
1439
|
+
* @returns {object} Current selection state.
|
|
1440
|
+
*/
|
|
1441
|
+
applySelectionState(state = {}) {
|
|
1442
|
+
this.#selectionState.selectableIds = this.#normalizeIdList(state.selectableIds ?? state.selectables ?? []);
|
|
1443
|
+
this.setSelectedElements(state.selectedItems ?? state.selectedIds ?? state.selected ?? []);
|
|
1444
|
+
return this.getSelectionState();
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/**
|
|
1448
|
+
* Clears selected elements while keeping the selectable set.
|
|
1449
|
+
* @public
|
|
1450
|
+
* @returns {object} Current selection state.
|
|
1451
|
+
*/
|
|
1452
|
+
clearSelectedElements() {
|
|
1453
|
+
this.#selectionState.selectedIds = [];
|
|
1454
|
+
this.#selectionState.selectedItems = [];
|
|
1455
|
+
this.#emitSelectionStateChanged();
|
|
1456
|
+
return this.getSelectionState();
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* Clears selectable elements while keeping the current selected state.
|
|
1461
|
+
* @public
|
|
1462
|
+
* @returns {object} Current selection state.
|
|
1463
|
+
*/
|
|
1464
|
+
clearSelectableElements() {
|
|
1465
|
+
this.#selectionState.selectableIds = [];
|
|
1466
|
+
this.#emitSelectionStateChanged();
|
|
1467
|
+
return this.getSelectionState();
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* Clears all semantic selection state.
|
|
1472
|
+
* @public
|
|
1473
|
+
* @returns {object} Current selection state.
|
|
1474
|
+
*/
|
|
1475
|
+
clearSelectionState() {
|
|
1476
|
+
this.#selectionState = {
|
|
1477
|
+
selectableIds: [],
|
|
1478
|
+
selectedIds: [],
|
|
1479
|
+
selectedItems: [],
|
|
1480
|
+
};
|
|
1481
|
+
this.#emitSelectionStateChanged();
|
|
1482
|
+
return this.getSelectionState();
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* Returns the current semantic selection state.
|
|
1487
|
+
* @public
|
|
1488
|
+
* @returns {{selectableIds: string[], selectedIds: string[], selectedItems: object[]}}
|
|
1489
|
+
*/
|
|
1490
|
+
getSelectionState() {
|
|
1491
|
+
return {
|
|
1492
|
+
selectableIds: [...this.#selectionState.selectableIds],
|
|
1493
|
+
selectedIds: [...this.#selectionState.selectedIds],
|
|
1494
|
+
selectedItems: this.#selectionState.selectedItems.map((item) => ({
|
|
1495
|
+
id: item.id,
|
|
1496
|
+
highlights: [...item.highlights],
|
|
1497
|
+
})),
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* Contract hook for host-driven element click forwarding.
|
|
1503
|
+
* Internal 2D/3D hit-testing can call this once it exposes stable element ids.
|
|
1504
|
+
* @public
|
|
1505
|
+
* @param {object|string} element - Element id or detail object.
|
|
1506
|
+
* @returns {void}
|
|
1507
|
+
*/
|
|
1508
|
+
notifyElementClick(element) {
|
|
1509
|
+
const detail = typeof element === "string" ? { id: element } : { ...(element ?? {}) };
|
|
1510
|
+
this.#dispatchPrefViewerEvent("pref-viewer-element-click", {
|
|
1511
|
+
element: detail,
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
/**
|
|
1516
|
+
* Starts the semantic turnable state.
|
|
1517
|
+
* @public
|
|
1518
|
+
* @param {{speed?: number, direction?: string}} [options] - Optional speed/direction values.
|
|
1519
|
+
* @returns {object} Current turnable state.
|
|
1520
|
+
*/
|
|
1521
|
+
startTurnable(options = {}) {
|
|
1522
|
+
this.#turnableState = {
|
|
1523
|
+
...this.#turnableState,
|
|
1524
|
+
...options,
|
|
1525
|
+
running: true,
|
|
1526
|
+
};
|
|
1527
|
+
this.#emitTurnableChanged();
|
|
1528
|
+
return this.getTurnableState();
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/**
|
|
1532
|
+
* Stops the semantic turnable state.
|
|
1533
|
+
* @public
|
|
1534
|
+
* @returns {object} Current turnable state.
|
|
1535
|
+
*/
|
|
1536
|
+
stopTurnable() {
|
|
1537
|
+
this.#turnableState = {
|
|
1538
|
+
...this.#turnableState,
|
|
1539
|
+
running: false,
|
|
1540
|
+
};
|
|
1541
|
+
this.#emitTurnableChanged();
|
|
1542
|
+
return this.getTurnableState();
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* Toggles the semantic turnable state.
|
|
1547
|
+
* @public
|
|
1548
|
+
* @param {object} [options] - Optional speed/direction values used when starting.
|
|
1549
|
+
* @returns {object} Current turnable state.
|
|
1550
|
+
*/
|
|
1551
|
+
toggleTurnable(options = {}) {
|
|
1552
|
+
return this.#turnableState.running ? this.stopTurnable() : this.startTurnable(options);
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/**
|
|
1556
|
+
* Returns the current semantic turnable state.
|
|
1557
|
+
* @public
|
|
1558
|
+
* @returns {{running: boolean, speed: number, direction: string}}
|
|
1559
|
+
*/
|
|
1560
|
+
getTurnableState() {
|
|
1561
|
+
return { ...this.#turnableState };
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1294
1564
|
/**
|
|
1295
1565
|
* Opens a modal dialog with the specified title, content, and footer.
|
|
1296
1566
|
* @public
|
|
@@ -1368,31 +1638,6 @@ export default class PrefViewer extends HTMLElement {
|
|
|
1368
1638
|
this.#addTaskToQueue(config, "visibility");
|
|
1369
1639
|
}
|
|
1370
1640
|
|
|
1371
|
-
/**
|
|
1372
|
-
* Reflects the current visibility of a container without feeding the task queue.
|
|
1373
|
-
* Used by the Babylon controller to keep host attributes in sync with renderer state.
|
|
1374
|
-
* @public
|
|
1375
|
-
* @param {string} name - Container name ("model" or "environment").
|
|
1376
|
-
* @param {boolean} isVisible - Whether the container should be reflected as visible.
|
|
1377
|
-
* @returns {void}
|
|
1378
|
-
*/
|
|
1379
|
-
syncVisibility(name, isVisible) {
|
|
1380
|
-
const attrName = name === "model" ? "show-model" : name === "environment" ? "show-scene" : null;
|
|
1381
|
-
if (!attrName) {
|
|
1382
|
-
return;
|
|
1383
|
-
}
|
|
1384
|
-
const nextValue = isVisible ? "true" : "false";
|
|
1385
|
-
if (this.getAttribute(attrName) === nextValue) {
|
|
1386
|
-
return;
|
|
1387
|
-
}
|
|
1388
|
-
this.#suppressVisibilityAttributeCallback = true;
|
|
1389
|
-
try {
|
|
1390
|
-
this.setAttribute(attrName, nextValue);
|
|
1391
|
-
} finally {
|
|
1392
|
-
this.#suppressVisibilityAttributeCallback = false;
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
1641
|
/**
|
|
1397
1642
|
* Centers the 2D drawing view in the viewer.
|
|
1398
1643
|
* @public
|
|
@@ -1561,31 +1806,20 @@ export default class PrefViewer extends HTMLElement {
|
|
|
1561
1806
|
}
|
|
1562
1807
|
|
|
1563
1808
|
/**
|
|
1564
|
-
*
|
|
1809
|
+
* Sends style updates to `setStyles()` enabling reactive bindings.
|
|
1565
1810
|
* @public
|
|
1566
|
-
* @param {object|string} value -
|
|
1811
|
+
* @param {object|string} value - Style overrides object or JSON string.
|
|
1567
1812
|
*/
|
|
1568
|
-
set
|
|
1569
|
-
this.
|
|
1813
|
+
set styles(value) {
|
|
1814
|
+
this.setStyles(value);
|
|
1570
1815
|
}
|
|
1571
|
-
|
|
1572
|
-
/**
|
|
1573
|
-
* Enables or disables the hover highlight effect at runtime.
|
|
1574
|
-
* Accepts boolean or string "true"/"false".
|
|
1575
|
-
* @public
|
|
1576
|
-
* @param {boolean|string} value
|
|
1577
|
-
*/
|
|
1578
|
-
set highlightEnabled(value) {
|
|
1579
|
-
this.setAttribute("highlight-enabled", String(value));
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
1816
|
/**
|
|
1583
|
-
*
|
|
1817
|
+
* Redirects scene assignments to `loadScene()` for normalized handling.
|
|
1584
1818
|
* @public
|
|
1585
|
-
* @param {string} value -
|
|
1819
|
+
* @param {object|string} value - Scene payload or serialized JSON.
|
|
1586
1820
|
*/
|
|
1587
|
-
set
|
|
1588
|
-
this.
|
|
1821
|
+
set scene(value) {
|
|
1822
|
+
this.loadScene(value);
|
|
1589
1823
|
}
|
|
1590
1824
|
|
|
1591
1825
|
/**
|
|
@@ -1647,22 +1881,4 @@ export default class PrefViewer extends HTMLElement {
|
|
|
1647
1881
|
get isMode3D() {
|
|
1648
1882
|
return this.#mode === "3d";
|
|
1649
1883
|
}
|
|
1650
|
-
|
|
1651
|
-
/**
|
|
1652
|
-
* Forces the 3D camera to frame the current model, when supported.
|
|
1653
|
-
* @public
|
|
1654
|
-
* @returns {boolean}
|
|
1655
|
-
*/
|
|
1656
|
-
focusModel() {
|
|
1657
|
-
return this.#component3D?.focusModel?.() ?? false;
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
/**
|
|
1661
|
-
* Exposes a debug snapshot of the 3D runtime when available.
|
|
1662
|
-
* @public
|
|
1663
|
-
* @returns {object|null}
|
|
1664
|
-
*/
|
|
1665
|
-
getDebugState() {
|
|
1666
|
-
return this.#component3D?.getDebugState?.() ?? null;
|
|
1667
|
-
}
|
|
1668
1884
|
}
|