@preference-sl/pref-viewer 2.16.0-beta.10 → 2.16.0-beta.12

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.
@@ -10,7 +10,7 @@ import { DEFAULT_LOCALE, resolveLocale, translate } from "./localization/i18n.js
10
10
  * - A hue slider for the highlight color.
11
11
  * - A time-of-day slider with play/pause continuous lighting-cycle animation.
12
12
  *
13
- * Every interaction emits `pref-viewer-menu-3d-apply` immediately; there is no Apply button or
13
+ * Every interaction emits `pref-viewer-3d-render-settings-change-requested` immediately; there is no Apply button or
14
14
  * draft/pending state. Switch changes emit the full settings snapshot, while highlight color and
15
15
  * lighting changes emit partial payloads to avoid unnecessary scene reloads.
16
16
  *
@@ -19,13 +19,13 @@ import { DEFAULT_LOCALE, resolveLocale, translate } from "./localization/i18n.js
19
19
  * - `setApplying(isApplying)`: locks interactions while an async apply operation is running.
20
20
  * - `setViewerHover(isHovering)`: drives panel visibility from the 3D viewport hover state.
21
21
  * - `setEnabled(isEnabled)`: hides the menu when 3D mode is unavailable.
22
- * - `setSwitchEnabled(settingKey, isEnabled)`: enables/disables a specific toggle at runtime.
22
+ * - `setElementEnabled(settingKey, isEnabled)`: enables/disables a specific switch or slider at runtime.
23
+ * - `setElementVisible(settingKey, isVisible)`: shows/hides a specific switch or slider at runtime.
23
24
  * - `setCulture(cultureId)`: switches the display locale.
24
25
  *
25
- * Observed attributes: `culture`, `show-illumination`, `illumination-color`, `lighting-time-of-day`.
26
- * The illumination attributes are mirrored for compatibility with hosts that still set them declaratively.
26
+ * Observed attributes: `culture`. Runtime render values are synchronized through `setSettings()`.
27
27
  *
28
- * Events emitted: `pref-viewer-menu-3d-apply` - `{ detail: { settings: object } }`.
28
+ * Events emitted: `pref-viewer-3d-render-settings-change-requested` - `{ detail: { settings: object } }`.
29
29
  */
30
30
  export default class PrefViewerMenu3D extends HTMLElement {
31
31
  #elements = {
@@ -41,6 +41,8 @@ export default class PrefViewerMenu3D extends HTMLElement {
41
41
  lightingSlider: null,
42
42
  lightingValue: null,
43
43
  panel: null,
44
+ sliders: {},
45
+ sliderWrappers: {},
44
46
  subtitle: null,
45
47
  switches: {},
46
48
  switchCopyNodes: {},
@@ -65,7 +67,8 @@ export default class PrefViewerMenu3D extends HTMLElement {
65
67
  typeof this.#DEFAULT_RENDER_SETTINGS[key] === "boolean"
66
68
  );
67
69
  #settings = { ...this.#DEFAULT_RENDER_SETTINGS };
68
- #switchAvailability = {};
70
+ #elementAvailability = {};
71
+ #elementVisibility = {};
69
72
  #isApplying = false;
70
73
  #isEnabled = true;
71
74
  #isLightingPlaying = false;
@@ -92,35 +95,22 @@ export default class PrefViewerMenu3D extends HTMLElement {
92
95
  * @returns {string[]} Array of attribute names to observe.
93
96
  */
94
97
  static get observedAttributes() {
95
- return ["culture", "show-illumination", "illumination-color", "lighting-time-of-day"];
98
+ return ["culture"];
96
99
  }
97
100
 
98
101
  /**
99
102
  * Reacts to observed attribute mutations and mirrors them into the internal settings/UI state.
100
103
  * @public
101
- * @param {"culture"|"show-illumination"|"illumination-color"|"lighting-time-of-day"} name - Attribute name that changed.
104
+ * @param {"culture"} name - Attribute name that changed.
102
105
  * @param {string|null} _old - Previous attribute value (unused).
103
106
  * @param {string|null} value - New attribute value.
104
107
  * @returns {void}
105
108
  */
106
109
  attributeChangedCallback(name, _old, value) {
107
- if (name === "culture") {
108
- this.#setCultureInternal(value || DEFAULT_LOCALE);
109
- } else if (name === "show-illumination") {
110
- this.#settings.illuminationEnabled = value === "true" || value === true;
111
- const switchEl = this.#elements.switches?.illuminationEnabled;
112
- if (switchEl) {
113
- switchEl.checked = this.#settings.illuminationEnabled;
114
- }
115
- } else if (name === "illumination-color") {
116
- this.#settings.illuminationColor = value;
117
- this.#updateColorPickerPreview(value);
118
- } else if (name === "lighting-time-of-day") {
119
- const parsed = Number(value);
120
- if (Number.isFinite(parsed)) {
121
- this.#settings.lightingTimeOfDay = this.#clampLightingTime(parsed);
122
- this.#updateLightingSliderUI(this.#settings.lightingTimeOfDay);
123
- }
110
+ switch (name) {
111
+ case "culture":
112
+ this.#setCultureInternal(value || DEFAULT_LOCALE);
113
+ break;
124
114
  }
125
115
  }
126
116
 
@@ -171,19 +161,19 @@ export default class PrefViewerMenu3D extends HTMLElement {
171
161
  <div class="menu-switches">
172
162
  ${this.#MENU_SWITCHES.map((key) => this.#renderSwitchRow(key, this.#texts.features)).join("")}
173
163
  </div>
174
- <div class="menu-color-picker" data-setting="highlightColor">
164
+ <div class="menu-color-picker menu-slider" data-setting="highlightColor">
175
165
  <div class="color-slider-row">
176
166
  <span class="color-picker-label">${this.#texts.features?.highlightColor?.label || "Highlight Color"}</span>
177
167
  <span class="color-preview" style="background-color: ${this.#settings.highlightColor || '#ff6700'}"></span>
178
168
  </div>
179
- <input type="range" class="color-slider" min="0" max="359" value="${PrefViewerMenu3D.#hexToHue(this.#settings.highlightColor || '#ff6700')}">
169
+ <input type="range" data-setting="highlightColor" class="color-slider" min="0" max="359" value="${PrefViewerMenu3D.#hexToHue(this.#settings.highlightColor || '#ff6700')}">
180
170
  </div>
181
- <div class="menu-lighting-slider" data-setting="lightingTimeOfDay">
171
+ <div class="menu-lighting-slider menu-slider" data-setting="lightingTimeOfDay">
182
172
  <div class="lighting-slider-row">
183
- <span class="lighting-slider-label">${this.#texts.features?.lightingTimeOfDay?.label || "Time of day"}</span>
173
+ <span class="lighting-slider-label menu-slider-label">${this.#texts.features?.lightingTimeOfDay?.label || "Time of day"}</span>
184
174
  <span class="lighting-slider-value">${PrefViewerMenu3D.#formatLightingTime(this.#settings.lightingTimeOfDay ?? 0.5)}</span>
185
175
  </div>
186
- <input type="range" class="lighting-slider" min="0" max="1000" step="1" value="${Math.round((this.#settings.lightingTimeOfDay ?? 0.5) * 1000)}">
176
+ <input type="range" data-setting="lightingTimeOfDay" class="lighting-slider" min="0" max="1000" step="1" value="${Math.round((this.#settings.lightingTimeOfDay ?? 0.5) * 1000)}">
187
177
  <div class="lighting-slider-markers" aria-hidden="true">
188
178
  <span class="lighting-slider-start">${this.#texts.features?.lightingTimeOfDay?.startLabel || "Night"}</span>
189
179
  <span class="lighting-slider-mid">${this.#texts.features?.lightingTimeOfDay?.midLabel || "Day"}</span>
@@ -325,7 +315,8 @@ export default class PrefViewerMenu3D extends HTMLElement {
325
315
  this.#elements.panel = this.querySelector(".menu-panel");
326
316
  this.#elements.title = this.querySelector(".menu-title");
327
317
  this.#elements.subtitle = this.querySelector(".menu-subtitle");
328
- // Cache color picker elements
318
+
319
+ // Cache slider elements
329
320
  this.#elements.colorPicker = this.querySelector(".menu-color-picker");
330
321
  this.#elements.colorSlider = this.querySelector(".color-slider");
331
322
  this.#elements.colorPickerPreview = this.querySelector(".color-preview");
@@ -337,6 +328,11 @@ export default class PrefViewerMenu3D extends HTMLElement {
337
328
  this.#elements.lightingSliderEnd = this.querySelector(".lighting-slider-end");
338
329
  this.#elements.lightingSlider = this.querySelector(".lighting-slider");
339
330
  this.#elements.lightingValue = this.querySelector(".lighting-slider-value");
331
+
332
+ this.#elements.sliders["highlightColor"] = this.querySelector("input[data-setting='highlightColor']");
333
+ this.#elements.sliderWrappers["highlightColor"] = this.querySelector(".menu-slider[data-setting='highlightColor']");
334
+ this.#elements.sliders["lightingTimeOfDay"] = this.querySelector("input[data-setting='lightingTimeOfDay']");
335
+ this.#elements.sliderWrappers["lightingTimeOfDay"] = this.querySelector(".menu-slider[data-setting='lightingTimeOfDay']");
340
336
 
341
337
  this.#MENU_SWITCHES.forEach((key) => {
342
338
  this.#elements.switches[key] = this.querySelector(`input[data-setting="${key}"]`);
@@ -668,12 +664,13 @@ export default class PrefViewerMenu3D extends HTMLElement {
668
664
  return;
669
665
  }
670
666
  const detail = { settings: options.partial ? { ...extraSettings } : { ...this.#settings, ...extraSettings } };
671
- this.dispatchEvent(new CustomEvent("pref-viewer-menu-3d-apply", {
667
+ const eventOptions = {
672
668
  bubbles: true,
673
669
  cancelable: true,
674
670
  composed: true,
675
671
  detail,
676
- }));
672
+ };
673
+ this.dispatchEvent(new CustomEvent("pref-viewer-3d-render-settings-change-requested", eventOptions));
677
674
  }
678
675
 
679
676
  /**
@@ -684,10 +681,29 @@ export default class PrefViewerMenu3D extends HTMLElement {
684
681
  #updateSwitches() {
685
682
  Object.entries(this.#elements.switches).forEach(([key, input]) => {
686
683
  if (input) {
687
- const isEnabled = this.#switchAvailability[key] !== false;
684
+ const isEnabled = this.#elementAvailability[key] !== false;
685
+ const isVisible = this.#elementVisibility[key] !== false;
688
686
  input.checked = Boolean(this.#settings[key]);
689
687
  input.disabled = !isEnabled;
690
688
  this.#elements.switchWrappers[key]?.toggleAttribute("data-disabled", !isEnabled);
689
+ this.#elements.switchWrappers[key]?.toggleAttribute("data-hidden", !isVisible);
690
+ }
691
+ });
692
+ }
693
+
694
+ /**
695
+ * Synchronizes slider states with the current settings snapshot.
696
+ * @private
697
+ * @returns {void}
698
+ */
699
+ #updateSliders() {
700
+ Object.entries(this.#elements.sliders).forEach(([key, input]) => {
701
+ if (input) {
702
+ const isEnabled = this.#elementAvailability[key] !== false;
703
+ const isVisible = this.#elementVisibility[key] !== false;
704
+ input.disabled = !isEnabled;
705
+ this.#elements.sliderWrappers[key]?.toggleAttribute("data-disabled", !isEnabled);
706
+ this.#elements.sliderWrappers[key]?.toggleAttribute("data-hidden", !isVisible);
691
707
  }
692
708
  });
693
709
  }
@@ -754,6 +770,7 @@ export default class PrefViewerMenu3D extends HTMLElement {
754
770
  if (this.#settings.lightingTimeOfDay !== undefined) {
755
771
  this.#updateLightingSliderUI(this.#settings.lightingTimeOfDay);
756
772
  }
773
+ this.#updateSliders();
757
774
  this.#updateLightingPlayButtonUI();
758
775
  this.setApplying(false);
759
776
  }
@@ -777,17 +794,44 @@ export default class PrefViewerMenu3D extends HTMLElement {
777
794
  }
778
795
 
779
796
  /**
780
- * Enables or disables a specific render-setting switch in the menu.
797
+ * Enables or disables a specific render-setting switch or slider in the menu.
781
798
  * @public
782
- * @param {string} settingKey - Switch key (e.g. "iblEnabled").
799
+ * @param {string} settingKey - Setting key (e.g. "iblEnabled", "highlightColor", or "lightingTimeOfDay").
783
800
  * @param {boolean} [isEnabled=true] - True to allow interaction, false to disable it.
784
801
  * @returns {void}
785
802
  */
786
- setSwitchEnabled(settingKey, isEnabled = true) {
787
- if (!settingKey || !this.#MENU_SWITCHES.includes(settingKey)) {
803
+ setElementEnabled(settingKey, isEnabled = true) {
804
+ if (!settingKey) {
788
805
  return;
789
806
  }
790
- this.#switchAvailability[settingKey] = Boolean(isEnabled);
791
- this.#updateSwitches();
807
+
808
+ if (this.#MENU_SWITCHES.includes(settingKey)) {
809
+ this.#elementAvailability[settingKey] = Boolean(isEnabled);
810
+ this.#updateSwitches();
811
+ } else if (this.#elements.sliders[settingKey]) {
812
+ this.#elementAvailability[settingKey] = Boolean(isEnabled);
813
+ this.#updateSliders();
814
+ }
815
+ }
816
+
817
+ /**
818
+ * Shows or hides a specific render-setting switch or slider in the menu.
819
+ * @public
820
+ * @param {string} settingKey - Setting key (e.g. "iblEnabled", "highlightColor", or "lightingTimeOfDay").
821
+ * @param {boolean} [isVisible=true] - True to show the switch, false to hide it.
822
+ * @returns {void}
823
+ */
824
+ setElementVisible(settingKey, isVisible = true) {
825
+ if (!settingKey) {
826
+ return;
827
+ }
828
+
829
+ if (this.#MENU_SWITCHES.includes(settingKey)) {
830
+ this.#elementVisibility[settingKey] = Boolean(isVisible);
831
+ this.#updateSwitches();
832
+ } else if (this.#elements.sliders[settingKey]) {
833
+ this.#elementVisibility[settingKey] = Boolean(isVisible);
834
+ this.#updateSliders();
835
+ }
792
836
  }
793
837
  }
@@ -2,7 +2,7 @@
2
2
  * PrefViewerTask - Represents a single task or operation to be processed by the PrefViewer.
3
3
  *
4
4
  * Responsibilities:
5
- * - Encapsulates the payload and type of a viewer task (e.g., loading a model, updating materials).
5
+ * - Encapsulates the payload and type of a viewer task (e.g., loading a model, updating materials, applying styles).
6
6
  * - Validates the task type against a predefined set of allowed types.
7
7
  * - Provides an immutable instance to ensure task integrity.
8
8
  *
@@ -12,13 +12,17 @@
12
12
  * - Access the payload via the `value` property and the normalized type via the `type` property.
13
13
  *
14
14
  * Static Properties:
15
- * - Types: An object containing all allowed task types as string constants.
15
+ * - Types: An object containing all allowed task types as string constants, including `Styles` for queued runtime style overrides.
16
16
  *
17
17
  * Constructor:
18
18
  * - Validates the type (case-insensitive) and throws a TypeError if invalid.
19
19
  * - Freezes the instance to prevent further modification.
20
20
  */
21
21
  export default class PrefViewerTask {
22
+ /**
23
+ * Allowed task type constants used by the PrefViewer queue.
24
+ * @type {Readonly<{Config: string, Culture: string, Drawing: string, Environment: string, Materials: string, Model: string, Options: string, Styles: string, Visibility: string}>}
25
+ */
22
26
  static Types = Object.freeze({
23
27
  Config: "config",
24
28
  Culture: "culture",
@@ -27,6 +31,7 @@ export default class PrefViewerTask {
27
31
  Materials: "materials",
28
32
  Model: "model",
29
33
  Options: "options",
34
+ Styles: "styles",
30
35
  Visibility: "visibility",
31
36
  });
32
37