@preference-sl/pref-viewer 2.16.2 → 2.17.0-beta.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.
@@ -1,6 +1,15 @@
1
1
  export const translations = {
2
2
  "en-EN": {
3
3
  prefViewer: {
4
+ viewCube: {
5
+ title: "View orientation",
6
+ front: "Inner",
7
+ back: "Outer",
8
+ top: "Top",
9
+ bottom: "Bottom",
10
+ left: "Left",
11
+ right: "Right"
12
+ },
4
13
  menu3d: {
5
14
  title: "3D render settings",
6
15
  subtitle: "Modify the visual effects.",
@@ -62,6 +71,15 @@ export const translations = {
62
71
  },
63
72
  "es-ES": {
64
73
  prefViewer: {
74
+ viewCube: {
75
+ title: "Orientación de vista",
76
+ front: "Interior",
77
+ back: "Exterior",
78
+ top: "Arriba",
79
+ bottom: "Abajo",
80
+ left: "Izquierda",
81
+ right: "Derecha"
82
+ },
65
83
  menu3d: {
66
84
  title: "Ajustes de render 3D",
67
85
  subtitle: "Modifica los efectos visuales.",
@@ -25,6 +25,7 @@ import FileStorage from "./file-storage.js";
25
25
  * - show-model: Show or hide the 3D model ("true"/"false").
26
26
  * - show-scene: Show or hide the 3D environment ("true"/"false").
27
27
  * - visible: Show or hide the entire viewer ("true"/"false").
28
+ * - show-view-cube: Enable the orientation overlay while visible; only "false" disables it (default: enabled).
28
29
  *
29
30
  * Public Methods:
30
31
  * - show(): Shows the 3D viewer component.
@@ -39,6 +40,7 @@ import FileStorage from "./file-storage.js";
39
40
  * - hideEnvironment(): Hides the 3D environment/scene.
40
41
  * - isIBLAvailable(): Reports whether a valid IBL environment map is currently available.
41
42
  * - isAnimationMenuEnabled(): Reports whether the animation control menu is currently enabled.
43
+ * - refreshViewCube(): Internally synchronizes an existing cube after a host culture change.
42
44
  * - getDebugInfo(): Returns a diagnostic snapshot of the underlying Babylon.js runtime.
43
45
  * - getAnimations(): Returns a snapshot of all available animations and their current state.
44
46
  * - playAnimation(name, action): Controls a named animation by applying the specified action.
@@ -69,6 +71,7 @@ import FileStorage from "./file-storage.js";
69
71
  * - #onLoading/#onLoaded/#onSettingOptions/#onSetOptions(): Dispatch lifecycle events and synchronize attributes.
70
72
  *
71
73
  * Events:
74
+ * - "view-cube-layout": Internal, non-bubbling event with {offset, height} in CSS pixels for host menu layout; disposal clears both.
72
75
  * - "pref-viewer-3d-loading": Dispatched when a loading operation starts. Legacy alias: "scene-loading".
73
76
  * - "pref-viewer-3d-loaded": Dispatched when a loading operation completes. Legacy alias: "scene-loaded".
74
77
  * - "pref-viewer-3d-options-applying": Dispatched when viewer options are being set. Legacy alias: "scene-setting-options".
@@ -107,7 +110,7 @@ export default class PrefViewer3D extends HTMLElement {
107
110
  * @returns {string[]} Array of attribute names that trigger attributeChangedCallback.
108
111
  */
109
112
  static get observedAttributes() {
110
- return ["show-model", "show-scene", "visible"];
113
+ return ["show-model", "show-scene", "visible", "show-view-cube"];
111
114
  }
112
115
 
113
116
  /**
@@ -120,9 +123,13 @@ export default class PrefViewer3D extends HTMLElement {
120
123
  * - "show-model": shows or hides the model according to the attribute value ("true"/"false").
121
124
  * - "show-scene": shows or hides the scene according to the attribute value ("true"/"false").
122
125
  * - "visible": shows or hides the component according to the attribute value ("true"/"false").
126
+ * - "show-view-cube": activates the cube only while visible and the attribute is not "false".
123
127
  */
124
128
  attributeChangedCallback(name, _old, value) {
125
129
  switch (name) {
130
+ case "show-view-cube":
131
+ this.#babylonJSController?.setViewCubeEnabled(this.#isVisible && value !== "false");
132
+ break;
126
133
  case "show-model":
127
134
  if (_old === value) {
128
135
  return;
@@ -237,12 +244,18 @@ export default class PrefViewer3D extends HTMLElement {
237
244
  }
238
245
 
239
246
  /**
240
- * Initializes the BabylonJSController and enables the Babylon.js engine for rendering.
247
+ * Initializes the Babylon controller with asset/option state and the visibility-aware cube contract.
248
+ * Supplies current host culture and dispatches measured view-cube-layout events for render-menu positioning.
249
+ * Starts asynchronous engine initialization without waiting for asset loading.
241
250
  * @private
242
251
  * @returns {void}
243
252
  */
244
253
  #initializeBabylonJS() {
245
- this.#babylonJSController = new BabylonJSController(this.#canvas, this.#data.containers, this.#data.options);
254
+ this.#babylonJSController = new BabylonJSController(this.#canvas, this.#data.containers, this.#data.options, {
255
+ enabled: this.#isVisible && this.getAttribute("show-view-cube") !== "false",
256
+ getCulture: () => this.getRootNode().host?.culture,
257
+ onLayout: (offset, height) => this.dispatchEvent(new CustomEvent("view-cube-layout", { detail: { offset, height } })),
258
+ });
246
259
  this.#babylonJSController.enable();
247
260
 
248
261
  }
@@ -608,22 +621,24 @@ export default class PrefViewer3D extends HTMLElement {
608
621
  */
609
622
 
610
623
  /**
611
- * Hides the 3D viewer component by updating its visibility state and DOM attribute.
624
+ * Hides the 3D viewer and releases its cube resources while retaining the requested show-view-cube attribute.
612
625
  * @public
613
626
  * @returns {void}
614
627
  */
615
628
  hide() {
616
629
  this.#isVisible = false;
630
+ this.#babylonJSController?.setViewCubeEnabled(false);
617
631
  this.setAttribute("visible", "false");
618
632
  }
619
633
 
620
634
  /**
621
- * Shows the 3D viewer component by updating its visibility state and DOM attribute.
635
+ * Shows/focuses the 3D viewer and lazily recreates its cube when show-view-cube is not "false".
622
636
  * @public
623
637
  * @returns {void}
624
638
  */
625
639
  show() {
626
640
  this.#isVisible = true;
641
+ this.#babylonJSController?.setViewCubeEnabled(this.getAttribute("show-view-cube") !== "false");
627
642
  this.setAttribute("visible", "true");
628
643
  this.#canvas.focus();
629
644
  }
@@ -900,6 +915,15 @@ export default class PrefViewer3D extends HTMLElement {
900
915
  return this.#babylonJSController.getRenderSettings();
901
916
  }
902
917
 
918
+ /**
919
+ * Forwards an internal culture/camera refresh to the existing overlay without activating a disabled cube.
920
+ * @public
921
+ * @returns {void}
922
+ */
923
+ refreshViewCube() {
924
+ this.#babylonJSController?.refreshViewCube();
925
+ }
926
+
903
927
  /**
904
928
  * Returns a snapshot of all available animations and their current state.
905
929
  * @public
@@ -61,7 +61,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
61
61
  onToggleEnter: null,
62
62
  };
63
63
 
64
- #DEFAULT_RENDER_SETTINGS = { ...BabylonJSController.DEFAULT_RENDER_SETTINGS };
64
+ #DEFAULT_RENDER_SETTINGS = { ...BabylonJSController.config.render.defaultSettings };
65
65
  // Only include boolean settings as switches; scalar settings are handled by sliders.
66
66
  #MENU_SWITCHES = Object.keys(this.#DEFAULT_RENDER_SETTINGS).filter(key =>
67
67
  typeof this.#DEFAULT_RENDER_SETTINGS[key] === "boolean"
@@ -194,7 +194,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
194
194
  /**
195
195
  * Generates the HTML fragment for a single boolean render-setting switch.
196
196
  * @private
197
- * @param {string} key - Render setting key from BabylonJSController.DEFAULT_RENDER_SETTINGS.
197
+ * @param {string} key - Render setting key from BabylonJSController.config.render.defaultSettings.
198
198
  * @param {Record<string, {label?: string, helper?: string}>} copy - Locale-aware feature copy used for labels.
199
199
  * @returns {string} HTML string representing the switch row.
200
200
  */
@@ -23,13 +23,14 @@ import { DEFAULT_LOCALE, resolveLocale, setLocale, SUPPORTED_LOCALES } from "./l
23
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
- * - config: Configuration descriptor that can bundle model/scene/materials/drawing/culture hints.
26
+ * - config: Configuration descriptor with assets, culture/styles and optional boolean showRenderMenu/showViewCube flags.
27
27
  * - model / scene / materials / drawing: Independent asset entry points (URLs, Base64, indexed data) for each subsystem.
28
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
32
  * - show-render-menu: Boolean flag to show or hide the render-settings menu (default: true).
33
+ * - show-view-cube: Enables the 3D orientation overlay unless "false"; visible cubes position the render menu below their measured height.
33
34
  * - styles: JSON style overrides. Currently supports `colorPrimary`, persisted in localStorage and exposed as `--pref-viewer-color-primary`.
34
35
  *
35
36
  * Public Methods (selection):
@@ -66,6 +67,7 @@ import { DEFAULT_LOCALE, resolveLocale, setLocale, SUPPORTED_LOCALES } from "./l
66
67
  * - Legacy `scene-*` and `drawing-*` aliases are still emitted alongside their modern equivalents for backward compatibility.
67
68
  *
68
69
  * Notes:
70
+ * - Stores the cube layout handler and subscribes only for active 3D cube layout; open dialogs render above viewer overlays.
69
71
  * - Internally wires PrefViewerMenu3D hover/apply events to PrefViewer3D render settings, ensuring persisted toggles stay in sync with BabylonJSController.
70
72
  * - Cleans up dialogs, listeners, and subcomponents on disconnect to prevent leaks when frameworks mount/unmount the custom element.
71
73
  * - Uses localStorage-backed culture persistence and attribute reflection safeguards to keep templates, menu, and global i18n aligned.
@@ -120,6 +122,7 @@ export default class PrefViewer extends HTMLElement {
120
122
  onViewerHoverEnd: null,
121
123
  on3DSceneLoaded: null,
122
124
  on3DSceneError: null,
125
+ onViewCubeLayout: null,
123
126
  };
124
127
 
125
128
  /**
@@ -146,21 +149,26 @@ export default class PrefViewer extends HTMLElement {
146
149
  * @returns {string[]} Array of attribute names to observe.
147
150
  */
148
151
  static get observedAttributes() {
149
- return ["config", "culture", "drawing", "materials", "mode", "model", "scene", "options", "show-model", "show-scene", "show-render-menu", "styles"];
152
+ return ["config", "culture", "drawing", "materials", "mode", "model", "scene", "options", "show-model", "show-scene", "show-render-menu", "show-view-cube", "styles"];
150
153
  }
151
154
 
152
155
  /**
153
156
  * Observes changes to specific attributes and triggers corresponding actions.
154
157
  * Loads configuration, drawing, model, scene, materials, or options when their attributes change.
155
158
  * 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.
159
+ * "show-view-cube" synchronizes lazy cube activation and conditional layout subscription before forwarding the attribute.
156
160
  * @public
157
161
  * @param {string} name - The name of the changed attribute.
158
- * @param {*} _old - The previous value of the attribute (unused).
159
- * @param {*} value - The new value of the attribute.
162
+ * @param {string|null} _old - The previous attribute value.
163
+ * @param {string|null} value - The new attribute value; null when removed.
160
164
  * @returns {void}
161
165
  */
162
166
  attributeChangedCallback(name, _old, value) {
163
167
  switch (name) {
168
+ case "show-view-cube":
169
+ this.#setViewCubeLayoutSubscription(this.#mode === "3d" && value !== "false");
170
+ this.#component3D?.setAttribute("show-view-cube", value === "false" ? "false" : "true");
171
+ break;
164
172
  case "config":
165
173
  this.loadConfig(value);
166
174
  break;
@@ -227,6 +235,7 @@ export default class PrefViewer extends HTMLElement {
227
235
  /**
228
236
  * Called when the element is inserted into the DOM.
229
237
  * Initializes the 3D and 2D viewer components and starts processing tasks.
238
+ * Restores the primary animation-highlight color after creating the 3D component and menu.
230
239
  * @public
231
240
  * @returns {void|boolean} Returns false if initialization fails; otherwise void.
232
241
  */
@@ -236,6 +245,7 @@ export default class PrefViewer extends HTMLElement {
236
245
  this.#createComponent3D();
237
246
  this.#createComponent2D();
238
247
  this.#createMenu3D();
248
+ this.#applyRenderSettings({ highlightColor: this.#styles.colorPrimary, persist: false });
239
249
 
240
250
  this.setMode(this.getAttribute("mode") ?? this.#mode);
241
251
 
@@ -252,7 +262,8 @@ export default class PrefViewer extends HTMLElement {
252
262
  }
253
263
 
254
264
  /**
255
- * Called when the element is removed from the DOM.
265
+ * Removes stored child/menu/dialog listeners and subcomponents when detached.
266
+ * Unsubscribes the cube layout handler before the 3D component and its overlay are torn down.
256
267
  * @public
257
268
  * @returns {void}
258
269
  */
@@ -272,6 +283,7 @@ export default class PrefViewer extends HTMLElement {
272
283
  if (this.#component3D) {
273
284
  this.#component3D.removeEventListener("pref-viewer-3d-loaded", this.#handlers.on3DSceneLoaded);
274
285
  this.#component3D.removeEventListener("pref-viewer-3d-error", this.#handlers.on3DSceneError);
286
+ this.#component3D.removeEventListener("view-cube-layout", this.#handlers.onViewCubeLayout);
275
287
  this.#component3D.remove();
276
288
  }
277
289
  if (this.#menu3D) {
@@ -302,8 +314,7 @@ export default class PrefViewer extends HTMLElement {
302
314
  }
303
315
 
304
316
  /**
305
- * Creates and appends the 2D viewer component to the shadow DOM.
306
- * Sets the "visible" attribute to true by default.
317
+ * Creates and appends the 2D viewer component, initially hidden, to the shadow DOM.
307
318
  * @private
308
319
  * @returns {void}
309
320
  */
@@ -318,14 +329,44 @@ export default class PrefViewer extends HTMLElement {
318
329
  }
319
330
 
320
331
  /**
321
- * Creates and appends the 3D viewer component to the shadow DOM.
322
- * Sets the "visible" attribute to true by default.
332
+ * Subscribes before cube activation so its first layout is received.
333
+ * Removes the subscription and menu overrides when the cube is inactive or the mode is 2D.
334
+ * @private
335
+ * @param {boolean} enabled - Whether cube layout updates are needed.
336
+ * @returns {void}
337
+ */
338
+ #setViewCubeLayoutSubscription(enabled) {
339
+ if (!this.#component3D || !this.#handlers.onViewCubeLayout) {
340
+ return;
341
+ }
342
+ this.#component3D.removeEventListener("view-cube-layout", this.#handlers.onViewCubeLayout);
343
+ if (enabled) {
344
+ this.#component3D.addEventListener("view-cube-layout", this.#handlers.onViewCubeLayout);
345
+ } else {
346
+ this.#wrapper?.style.removeProperty("--pref-viewer-render-menu-top");
347
+ this.#wrapper?.style.removeProperty("--pref-viewer-viewport-height");
348
+ }
349
+ }
350
+ /**
351
+ * Creates and appends the 3D viewer component to the shadow DOM, initially hidden.
352
+ * Forwards show-view-cube, stores its layout handler in #handlers and subscribes before activation
353
+ * so the first measured layout positions the render menu correctly.
323
354
  * @private
324
355
  * @returns {void}
325
356
  */
326
357
  #createComponent3D() {
327
358
  this.#component3D = document.createElement("pref-viewer-3d");
328
359
  this.#component3D.setAttribute("visible", "false");
360
+ this.#component3D.setAttribute("show-view-cube", this.getAttribute("show-view-cube") === "false" ? "false" : "true");
361
+ this.#handlers.onViewCubeLayout = (event) => {
362
+ if (event.detail.offset > 0) {
363
+ this.#wrapper.style.setProperty("--pref-viewer-render-menu-top", `${event.detail.offset}px`);
364
+ } else {
365
+ this.#wrapper.style.removeProperty("--pref-viewer-render-menu-top");
366
+ }
367
+ this.#wrapper.style.setProperty("--pref-viewer-viewport-height", `${event.detail.height}px`);
368
+ };
369
+ this.#setViewCubeLayoutSubscription(this.getAttribute("show-view-cube") !== "false" && (this.getAttribute("mode") ?? this.#mode) === "3d");
329
370
 
330
371
  this.#handlers.on3DSceneLoaded = () => {
331
372
  this.#menu3DSyncSettings();
@@ -609,7 +650,8 @@ export default class PrefViewer extends HTMLElement {
609
650
  }
610
651
 
611
652
  /**
612
- * Resolves the requested culture, updates internal/global state, and persists the selection.
653
+ * Resolves culture, updates internal/global state and menu/cube labels, and persists the selection.
654
+ * Refreshes only an existing cube; a culture change does not activate an inactive overlay.
613
655
  * @private
614
656
  * @param {string} [cultureId] - Desired locale to activate.
615
657
  * @param {object} [options] - Extra flags for the update.
@@ -635,6 +677,7 @@ export default class PrefViewer extends HTMLElement {
635
677
  this.#culture = resolved;
636
678
  setLocale(resolved);
637
679
  this.#menu3D?.setCulture?.(resolved);
680
+ this.#component3D?.refreshViewCube?.();
638
681
  this.#culturePersist(resolved);
639
682
  }
640
683
  return changed;
@@ -867,13 +910,24 @@ export default class PrefViewer extends HTMLElement {
867
910
  }
868
911
 
869
912
  /**
870
- * Processes a configuration object by loading it into the 3D component.
913
+ * Applies optional boolean overlay flags through their attributes, then loads the queued 3D configuration.
914
+ * Explicit flags override the current attributes; omitted/non-boolean values preserve their state.
871
915
  * Dispatches loading events and processes the next task when finished.
872
916
  * @private
873
917
  * @param {object} config - The configuration object to process.
874
- * @returns {void}
918
+ * @param {boolean} [config.showRenderMenu] - Reflects render-menu visibility before loading assets; non-booleans are ignored.
919
+ * @param {boolean} [config.showViewCube] - Reflects cube activation before loading assets; non-booleans are ignored.
920
+ * @returns {Promise<void>} Resolves after the load attempt and advances the task queue.
875
921
  */
876
922
  async #processConfig(config) {
923
+ for (const [property, attribute] of [["showRenderMenu", "show-render-menu"], ["showViewCube", "show-view-cube"]]) {
924
+ if (typeof config[property] === "boolean") {
925
+ const value = String(config[property]);
926
+ if (this.getAttribute(attribute) !== value) {
927
+ this.setAttribute(attribute, value);
928
+ }
929
+ }
930
+ }
877
931
  if (!this.#component3D) {
878
932
  this.#processNextTask();
879
933
  return;
@@ -996,14 +1050,20 @@ export default class PrefViewer extends HTMLElement {
996
1050
  }
997
1051
 
998
1052
  /**
999
- * Processes queued style overrides and advances the task queue.
1053
+ * Applies queued styles and synchronizes animation-opening highlight color without reloading assets.
1054
+ * Waits for render settings/menu synchronization before advancing the task queue.
1000
1055
  * @private
1001
1056
  * @param {object} styles - Style overrides to apply.
1002
- * @returns {void}
1057
+ * @returns {Promise<void>} Resolves after style/highlight synchronization and queue advancement.
1003
1058
  */
1004
- #processStyles(styles) {
1005
- this.#applyStyles(styles);
1006
- this.#processNextTask();
1059
+ async #processStyles(styles) {
1060
+ try {
1061
+ if (this.#applyStyles(styles)) {
1062
+ await this.#applyRenderSettings({ highlightColor: this.#styles.colorPrimary });
1063
+ }
1064
+ } finally {
1065
+ this.#processNextTask();
1066
+ }
1007
1067
  }
1008
1068
 
1009
1069
  /**
@@ -1259,10 +1319,15 @@ export default class PrefViewer extends HTMLElement {
1259
1319
 
1260
1320
  /**
1261
1321
  * Loads a configuration object or JSON string and adds it to the task queue.
1262
- * If the config contains a drawing, adds a drawing task as well.
1322
+ * Queues supplied styles/culture before the configuration and a supplied drawing afterward.
1323
+ * Boolean showRenderMenu/showViewCube flags apply when that config task is processed, before its 3D load.
1324
+ * Omitted/non-boolean flags preserve current attributes; equal values do not recreate the menu.
1263
1325
  * @public
1264
- * @param {object|string} config - Configuration object or JSON string.
1265
- * @returns {boolean|void} Returns false if config is invalid; otherwise void.
1326
+ * @param {object|string} config - Configuration object or JSON string; overlay flags apply in queue order.
1327
+ * @param {boolean} [config.showRenderMenu] - Sets show-render-menu when explicitly boolean.
1328
+ * @param {boolean} [config.showViewCube] - Sets show-view-cube when explicitly boolean.
1329
+ * @returns {false|void} Returns false for an absent/falsy payload; otherwise queues the configuration.
1330
+ * @throws {SyntaxError} If a supplied JSON string cannot be parsed.
1266
1331
  */
1267
1332
  loadConfig(config) {
1268
1333
  config = typeof config === "string" ? JSON.parse(config) : config;
@@ -1357,7 +1422,8 @@ export default class PrefViewer extends HTMLElement {
1357
1422
  }
1358
1423
 
1359
1424
  /**
1360
- * Sets the viewer mode to "2d" or "3d" and updates component visibility accordingly.
1425
+ * Sets the viewer mode and component/menu visibility; updates the conditional cube layout subscription
1426
+ * before showing 3D or releasing its overlay in 2D.
1361
1427
  * @public
1362
1428
  * @param {string} [mode=this.#mode] - The mode to set ("2d" or "3d").
1363
1429
  * @returns {void}
@@ -1369,6 +1435,7 @@ export default class PrefViewer extends HTMLElement {
1369
1435
  mode = this.#mode;
1370
1436
  }
1371
1437
  this.#mode = mode;
1438
+ this.#setViewCubeLayoutSubscription(mode === "3d" && this.getAttribute("show-view-cube") !== "false");
1372
1439
  if (mode === "2d") {
1373
1440
  this.#component3D?.hide();
1374
1441
  this.#component2D?.show();
@@ -1401,7 +1468,8 @@ export default class PrefViewer extends HTMLElement {
1401
1468
 
1402
1469
  /**
1403
1470
  * Sets supported PrefViewer style variables from an object or JSON string and adds them to the task queue.
1404
- * Currently supports `colorPrimary`, which updates `PrefViewerStyleVariables.colorPrimary`, applies the host CSS custom property, and persists the value in localStorage.
1471
+ * Currently supports `colorPrimary`, which updates `PrefViewerStyleVariables.colorPrimary`, applies the host CSS custom property,
1472
+ * synchronizes animation-opening highlight color (including active highlights), and persists the value in localStorage.
1405
1473
  * @public
1406
1474
  * @param {object|string} styles - Style overrides object or JSON string.
1407
1475
  * @returns {boolean|void} Returns false if styles are invalid; otherwise void.
@@ -1635,10 +1703,12 @@ export default class PrefViewer extends HTMLElement {
1635
1703
  * @param {string} title - The dialog title to display in the header.
1636
1704
  * @param {string} content - The HTML content to display in the dialog body.
1637
1705
  * @param {string} footer - The HTML content to display in the dialog footer (e.g., action buttons).
1638
- * @returns {HTMLElement} The created dialog element.
1706
+ * @returns {HTMLElement|null} The dialog element if opening succeeds; otherwise null.
1639
1707
  * @description
1640
1708
  * If a dialog is already open, it is closed before opening the new one.
1641
1709
  * The dialog is appended to the viewer's shadow DOM and returned for further manipulation.
1710
+ * Shared dialog styles place its modal backdrop above the render menu and ViewCube.
1711
+ * Background pointer input is intercepted; backdrop dismissal does not also activate a covered control.
1642
1712
  */
1643
1713
  openDialog(title, content, footer) {
1644
1714
  if (this.#dialog && this.#dialog.hasAttribute("open")) {
@@ -1811,9 +1881,14 @@ export default class PrefViewer extends HTMLElement {
1811
1881
  */
1812
1882
 
1813
1883
  /**
1814
- * Accepts a config object or JSON string and forwards it to `loadConfig()`.
1884
+ * Accepts a config object or JSON string and forwards it to `loadConfig()` in the existing task queue.
1885
+ * Includes optional boolean showRenderMenu/showViewCube flags with the same precedence and omission rules.
1886
+ * Supplied styles/culture and drawing retain the loadConfig ordering.
1815
1887
  * @public
1816
1888
  * @param {object|string} value - Configuration payload or serialized JSON.
1889
+ * @param {boolean} [value.showRenderMenu] - Optional render-menu visibility for an object payload.
1890
+ * @param {boolean} [value.showViewCube] - Optional cube visibility for an object payload.
1891
+ * @throws {SyntaxError} If a supplied JSON string cannot be parsed.
1817
1892
  */
1818
1893
  set config(value) {
1819
1894
  this.loadConfig(value);
package/src/styles.js CHANGED
@@ -150,6 +150,94 @@ export const PrefViewer3DStyles = `
150
150
  }
151
151
  `;
152
152
 
153
+ export const PrefViewerViewCubeStyles = `
154
+ .pref-viewer-view-cube {
155
+ --cube-fill-color: #eef0f3;
156
+ --cube-border-color: #78838f;
157
+ --cube-border-width: 1.0px;
158
+ --cube-hover-color: var(--pref-viewer-color-primary, ${PrefViewerStyleVariables.colorPrimary});
159
+ --cube-hover-opacity: 0.50;
160
+ --cube-active-color: var(--pref-viewer-color-primary, ${PrefViewerStyleVariables.colorPrimary});
161
+ --cube-active-opacity: 0.15;
162
+ --cube-text-font-family: ${PrefViewerStyleVariables.fontFamily};
163
+ --cube-text-font-size: 9px;
164
+ --cube-text-color: #29323b;
165
+ --cube-text-font-weight: 600;
166
+ --cube-text-letter-spacing: 1.0px;
167
+ }
168
+
169
+ .pref-viewer-view-cube[hidden] {
170
+ display: none;
171
+ }
172
+
173
+ .pref-viewer-view-cube svg {
174
+ width: 100%;
175
+ height: 100%;
176
+ overflow: visible;
177
+ pointer-events: none;
178
+ }
179
+
180
+ .pref-viewer-view-cube polygon {
181
+ stroke-width: var(--cube-border-width);
182
+ }
183
+
184
+ .pref-viewer-view-cube .cube-shell {
185
+ fill: var(--cube-fill-color);
186
+ stroke: var(--cube-border-color);
187
+ pointer-events: none;
188
+ }
189
+
190
+ .pref-viewer-view-cube polygon[data-region] {
191
+ fill: transparent;
192
+ stroke: transparent;
193
+ pointer-events: visiblePainted;
194
+ cursor: pointer;
195
+ }
196
+
197
+ .pref-viewer-view-cube[data-locked="true"] polygon {
198
+ cursor: default;
199
+ }
200
+
201
+ .pref-viewer-view-cube polygon[data-current] {
202
+ fill: var(--cube-active-color);
203
+ fill-opacity: var(--cube-active-opacity);
204
+ stroke: transparent;
205
+ }
206
+
207
+ .pref-viewer-view-cube polygon[data-hover] {
208
+ fill: var(--cube-hover-color);
209
+ fill-opacity: var(--cube-hover-opacity);
210
+ stroke: transparent;
211
+ }
212
+
213
+ .pref-viewer-view-cube .hover-outline {
214
+ display: none;
215
+ fill: none;
216
+ stroke: var(--cube-hover-color);
217
+ stroke-opacity: 1;
218
+ stroke-width: var(--cube-border-width);
219
+ pointer-events: none;
220
+ }
221
+
222
+ .pref-viewer-view-cube .hover-outline[data-hover] {
223
+ display: inline;
224
+ }
225
+
226
+ .pref-viewer-view-cube text {
227
+ -webkit-user-select: none;
228
+ user-select: none;
229
+ pointer-events: none;
230
+ fill: var(--cube-text-color);
231
+ font-family: var(--cube-text-font-family);
232
+ font-size: var(--cube-text-font-size);
233
+ font-weight: var(--cube-text-font-weight);
234
+ letter-spacing: var(--cube-text-letter-spacing);
235
+ text-transform: uppercase;
236
+ text-anchor: middle;
237
+ dominant-baseline: middle;
238
+ }
239
+ `;
240
+
153
241
  export const PrefViewerMenu3DStyles = `
154
242
  pref-viewer-menu-3d {
155
243
  --font-family: ${PrefViewerStyleVariables.fontFamily};
@@ -203,7 +291,7 @@ export const PrefViewerMenu3DStyles = `
203
291
  --slider-bar-border: 1px solid rgba(255, 255, 255, 0.5);
204
292
 
205
293
  position: absolute;
206
- top: var(--toggle-margin);
294
+ top: var(--pref-viewer-render-menu-top, var(--toggle-margin));
207
295
  right: var(--toggle-margin);
208
296
  z-index: 1200;
209
297
  /* The closed panel keeps its place in the flex column (opacity:0, not display:none), so
@@ -214,6 +302,8 @@ export const PrefViewerMenu3DStyles = `
214
302
  pointer-events: none;
215
303
 
216
304
  font-family: var(--font-family);
305
+ -webkit-user-select: none;
306
+ user-select: none;
217
307
  }
218
308
 
219
309
  pref-viewer-menu-3d>.menu-wrapper {
@@ -256,7 +346,7 @@ export const PrefViewerMenu3DStyles = `
256
346
 
257
347
  pref-viewer-menu-3d>.menu-wrapper>.menu-panel {
258
348
  width: var(--panel-width);
259
- max-height: 70vh;
349
+ max-height: min(70vh, calc(var(--pref-viewer-viewport-height, 100vh) - var(--pref-viewer-render-menu-top, var(--toggle-margin)) - 84px));
260
350
  overflow-y: auto;
261
351
  border-radius: var(--panel-radius);
262
352
  background: var(--panel-bg-color);
@@ -820,7 +910,8 @@ export const PrefViewerDialogStyles = `
820
910
  justify-content: center;
821
911
  background-color: var(--dialog-backdrop-color);
822
912
  position: relative;
823
- z-index: 1000;
913
+ /* Cover all viewer overlays, including the 3D render menu (1200). */
914
+ z-index: 1300;
824
915
  }
825
916
 
826
917
  pref-viewer-dialog>.dialog-wrapper {