@preference-sl/pref-viewer 2.12.0 → 2.13.0-beta.1
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/package.json +5 -5
- package/src/babylonjs-animation-controller.js +2 -2
- package/src/babylonjs-controller.js +367 -225
- package/src/index.js +2 -0
- package/src/localization/i18n.js +94 -0
- package/src/localization/translations.js +104 -0
- package/src/pref-viewer-3d-data.js +31 -2
- package/src/pref-viewer-3d.js +46 -10
- package/src/pref-viewer-menu-3d.js +557 -0
- package/src/pref-viewer-task.js +1 -0
- package/src/pref-viewer.js +532 -200
- package/src/styles.js +302 -10
package/src/index.js
CHANGED
|
@@ -3,11 +3,13 @@ import PrefViewer from "./pref-viewer.js";
|
|
|
3
3
|
import PrefViewer2D from "./pref-viewer-2d.js";
|
|
4
4
|
import PrefViewer3D from "./pref-viewer-3d.js";
|
|
5
5
|
import PrefViewerDialog from "./pref-viewer-dialog.js";
|
|
6
|
+
import PrefViewerMenu3D from "./pref-viewer-menu-3d.js";
|
|
6
7
|
|
|
7
8
|
// Defines custom elements for use as HTML tags in the application.
|
|
8
9
|
customElements.define("pref-viewer-2d", PrefViewer2D);
|
|
9
10
|
customElements.define("pref-viewer-3d", PrefViewer3D);
|
|
10
11
|
customElements.define("pref-viewer-dialog", PrefViewerDialog);
|
|
12
|
+
customElements.define("pref-viewer-menu-3d", PrefViewerMenu3D);
|
|
11
13
|
customElements.define("pref-viewer", PrefViewer);
|
|
12
14
|
|
|
13
15
|
// Exposes selected PrefViewer classes globally for external JavaScript use.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import translations from "./translations.js";
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_LOCALE = "en-EN";
|
|
4
|
+
|
|
5
|
+
export const SUPPORTED_LOCALES = Object.keys(translations);
|
|
6
|
+
const listeners = new Set();
|
|
7
|
+
let currentLocale = DEFAULT_LOCALE;
|
|
8
|
+
|
|
9
|
+
const findLocaleKey = (localeId) => {
|
|
10
|
+
if (typeof localeId !== "string") {
|
|
11
|
+
return DEFAULT_LOCALE;
|
|
12
|
+
}
|
|
13
|
+
const normalized = localeId.trim().toLowerCase();
|
|
14
|
+
const match = SUPPORTED_LOCALES.find((locale) => locale.toLowerCase() === normalized);
|
|
15
|
+
return match ?? DEFAULT_LOCALE;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getBundle = (localeId = currentLocale) => {
|
|
19
|
+
const key = findLocaleKey(localeId);
|
|
20
|
+
return translations[key] ?? translations[DEFAULT_LOCALE];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const resolveValue = (bundle, parts) => parts.reduce((acc, part) => (
|
|
24
|
+
acc && Object.prototype.hasOwnProperty.call(acc, part) ? acc[part] : undefined
|
|
25
|
+
), bundle);
|
|
26
|
+
|
|
27
|
+
export const availableLocales = () => [...SUPPORTED_LOCALES];
|
|
28
|
+
|
|
29
|
+
export const getLocale = () => currentLocale;
|
|
30
|
+
|
|
31
|
+
export const resolveLocale = (localeId) => findLocaleKey(localeId);
|
|
32
|
+
|
|
33
|
+
export const setLocale = (localeId) => {
|
|
34
|
+
const resolved = findLocaleKey(localeId);
|
|
35
|
+
if (resolved === currentLocale) {
|
|
36
|
+
return currentLocale;
|
|
37
|
+
}
|
|
38
|
+
currentLocale = resolved;
|
|
39
|
+
listeners.forEach((listener) => {
|
|
40
|
+
try {
|
|
41
|
+
listener(currentLocale);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.warn("PrefViewer i18n listener failed", error);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return currentLocale;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const onLocaleChange = (listener) => {
|
|
50
|
+
if (typeof listener !== "function") {
|
|
51
|
+
return () => {};
|
|
52
|
+
}
|
|
53
|
+
listeners.add(listener);
|
|
54
|
+
return () => listeners.delete(listener);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const translate = (key, options = {}) => {
|
|
58
|
+
if (!key) {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
const { locale, fallback } = options;
|
|
62
|
+
const parts = Array.isArray(key) ? key : key.split(".");
|
|
63
|
+
const bundle = getBundle(locale);
|
|
64
|
+
let value = resolveValue(bundle, parts);
|
|
65
|
+
if (value !== undefined) {
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
if (bundle !== translations[DEFAULT_LOCALE]) {
|
|
69
|
+
value = resolveValue(translations[DEFAULT_LOCALE], parts);
|
|
70
|
+
if (value !== undefined) {
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return fallback ?? "";
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const getNamespace = (namespace, options = {}) => {
|
|
78
|
+
if (!namespace) {
|
|
79
|
+
return getBundle(options.locale);
|
|
80
|
+
}
|
|
81
|
+
return translate(namespace, options);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default {
|
|
85
|
+
availableLocales,
|
|
86
|
+
DEFAULT_LOCALE,
|
|
87
|
+
getLocale,
|
|
88
|
+
getNamespace,
|
|
89
|
+
onLocaleChange,
|
|
90
|
+
resolveLocale,
|
|
91
|
+
setLocale,
|
|
92
|
+
SUPPORTED_LOCALES,
|
|
93
|
+
translate,
|
|
94
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export const translations = {
|
|
2
|
+
"en-EN": {
|
|
3
|
+
prefViewer: {
|
|
4
|
+
menu3d: {
|
|
5
|
+
title: "3D render settings",
|
|
6
|
+
subtitle: "Toggle visual effects on or off.",
|
|
7
|
+
pendingLabel: "Pending changes",
|
|
8
|
+
actions: {
|
|
9
|
+
apply: "Apply",
|
|
10
|
+
applying: "Applying...",
|
|
11
|
+
},
|
|
12
|
+
status: {
|
|
13
|
+
error: "Couldn't apply the changes.",
|
|
14
|
+
},
|
|
15
|
+
switches: {
|
|
16
|
+
antiAliasingEnabled: {
|
|
17
|
+
label: "Antialiasing",
|
|
18
|
+
helper: "Softens jagged edges and lines.",
|
|
19
|
+
},
|
|
20
|
+
ambientOcclusionEnabled: {
|
|
21
|
+
label: "Ambient Occlusion",
|
|
22
|
+
helper: "Reduction of ambient light in corners and areas close to surfaces.",
|
|
23
|
+
},
|
|
24
|
+
iblEnabled: {
|
|
25
|
+
label: "IBL",
|
|
26
|
+
helper: "Uses the HDR environment to light the scene.",
|
|
27
|
+
},
|
|
28
|
+
shadowsEnabled: {
|
|
29
|
+
label: "Shadows",
|
|
30
|
+
helper: "Enables shadows.",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
downloadDialog: {
|
|
35
|
+
title: "Download 3D Scene",
|
|
36
|
+
sections: {
|
|
37
|
+
content: "Content",
|
|
38
|
+
format: "Format",
|
|
39
|
+
},
|
|
40
|
+
options: {
|
|
41
|
+
model: "Model",
|
|
42
|
+
scene: "Scene",
|
|
43
|
+
both: "Both",
|
|
44
|
+
},
|
|
45
|
+
buttons: {
|
|
46
|
+
download: "Download",
|
|
47
|
+
cancel: "Cancel",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
"es-ES": {
|
|
53
|
+
prefViewer: {
|
|
54
|
+
menu3d: {
|
|
55
|
+
title: "Ajustes de render 3D",
|
|
56
|
+
subtitle: "Activa o desactiva los efectos visuales.",
|
|
57
|
+
pendingLabel: "Pendiente de aplicar",
|
|
58
|
+
actions: {
|
|
59
|
+
apply: "Aplicar",
|
|
60
|
+
applying: "Aplicando...",
|
|
61
|
+
},
|
|
62
|
+
status: {
|
|
63
|
+
error: "No se pudo aplicar los cambios.",
|
|
64
|
+
},
|
|
65
|
+
switches: {
|
|
66
|
+
antiAliasingEnabled: {
|
|
67
|
+
label: "Antialiasing",
|
|
68
|
+
helper: "Suaviza aristas y líneas.",
|
|
69
|
+
},
|
|
70
|
+
ambientOcclusionEnabled: {
|
|
71
|
+
label: "Ambient Occlusion",
|
|
72
|
+
helper: "Atenuación de la luz ambiental en rincones y zonas cercanas entre superficies.",
|
|
73
|
+
},
|
|
74
|
+
iblEnabled: {
|
|
75
|
+
label: "IBL",
|
|
76
|
+
helper: "Usa la imagen HDR de entorno para iluminar la escena.",
|
|
77
|
+
},
|
|
78
|
+
shadowsEnabled: {
|
|
79
|
+
label: "Sombras",
|
|
80
|
+
helper: "Activa sombras.",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
downloadDialog: {
|
|
85
|
+
title: "Descargar escena 3D",
|
|
86
|
+
sections: {
|
|
87
|
+
content: "Contenido",
|
|
88
|
+
format: "Formato",
|
|
89
|
+
},
|
|
90
|
+
options: {
|
|
91
|
+
model: "Modelo",
|
|
92
|
+
scene: "Escena",
|
|
93
|
+
both: "Ambos",
|
|
94
|
+
},
|
|
95
|
+
buttons: {
|
|
96
|
+
download: "Descargar",
|
|
97
|
+
cancel: "Cancelar",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default translations;
|
|
@@ -18,6 +18,7 @@ export class ContainerData {
|
|
|
18
18
|
this.show = true;
|
|
19
19
|
this.size = 0;
|
|
20
20
|
this.timeStamp = null;
|
|
21
|
+
this.storage = null;
|
|
21
22
|
this.visible = false;
|
|
22
23
|
// Set initial information about ongoing update
|
|
23
24
|
this.reset();
|
|
@@ -40,6 +41,9 @@ export class ContainerData {
|
|
|
40
41
|
this.size = this.update.size;
|
|
41
42
|
this.timeStamp = this.update.timeStamp;
|
|
42
43
|
this.metadata = { ...(this.update.metadata ?? {}) };
|
|
44
|
+
if (this.update.storage !== undefined && this.update.storage !== null) {
|
|
45
|
+
this.storage = this.update.storage;
|
|
46
|
+
}
|
|
43
47
|
} else {
|
|
44
48
|
this.update.success = false;
|
|
45
49
|
}
|
|
@@ -50,12 +54,27 @@ export class ContainerData {
|
|
|
50
54
|
this.update.storage = storage;
|
|
51
55
|
this.update.success = false;
|
|
52
56
|
this.update.show = show !== undefined ? show : this.update.show !== null ? this.update.show : this.show;
|
|
57
|
+
if (storage !== undefined && storage !== null) {
|
|
58
|
+
this.storage = storage;
|
|
59
|
+
}
|
|
53
60
|
}
|
|
54
61
|
setPendingCacheData(size = 0, timeStamp = null, metadata = {}) {
|
|
55
62
|
this.update.size = size;
|
|
56
63
|
this.update.timeStamp = timeStamp;
|
|
57
64
|
this.update.metadata = { ...(metadata ?? {}) };
|
|
58
65
|
}
|
|
66
|
+
setPendingWithCurrentStorage() {
|
|
67
|
+
const storedSource = this.storage ?? null;
|
|
68
|
+
if (!storedSource) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
const targetShow = this.update.show !== null ? this.update.show : this.show;
|
|
72
|
+
this.setPending(storedSource, targetShow);
|
|
73
|
+
this.update.size = this.size;
|
|
74
|
+
this.update.timeStamp = this.timeStamp;
|
|
75
|
+
this.update.metadata = { ...(this.metadata ?? {}) };
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
59
78
|
get isPending() {
|
|
60
79
|
return this.update.pending === true;
|
|
61
80
|
}
|
|
@@ -114,7 +133,11 @@ export class MaterialData {
|
|
|
114
133
|
this.update.value = value;
|
|
115
134
|
}
|
|
116
135
|
setPendingWithCurrent() {
|
|
136
|
+
if (this.value === undefined) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
117
139
|
this.setPending(this.value);
|
|
140
|
+
return true;
|
|
118
141
|
}
|
|
119
142
|
get isPending() {
|
|
120
143
|
return this.update.pending === true;
|
|
@@ -138,7 +161,8 @@ export class MaterialData {
|
|
|
138
161
|
* - Access status via isPending, isSuccess getters.
|
|
139
162
|
*/
|
|
140
163
|
export class CameraData {
|
|
141
|
-
|
|
164
|
+
defaultLocked = true;
|
|
165
|
+
constructor(name = "", value = null, locked = this.defaultLocked) {
|
|
142
166
|
this.name = name;
|
|
143
167
|
this.value = value;
|
|
144
168
|
this.locked = locked;
|
|
@@ -161,7 +185,7 @@ export class CameraData {
|
|
|
161
185
|
this.update.success = false;
|
|
162
186
|
}
|
|
163
187
|
}
|
|
164
|
-
setPending(value = undefined, locked =
|
|
188
|
+
setPending(value = undefined, locked = this.defaultLocked) {
|
|
165
189
|
this.reset();
|
|
166
190
|
if (value === undefined) {
|
|
167
191
|
return;
|
|
@@ -174,6 +198,11 @@ export class CameraData {
|
|
|
174
198
|
this.update.value = value;
|
|
175
199
|
this.update.locked = locked;
|
|
176
200
|
}
|
|
201
|
+
setPendingWithCurrent() {
|
|
202
|
+
const currentValue = this.value !== undefined ? this.value : null;
|
|
203
|
+
this.setPending(currentValue, this.locked);
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
177
206
|
get isPending() {
|
|
178
207
|
return this.update.pending === true;
|
|
179
208
|
}
|
package/src/pref-viewer-3d.js
CHANGED
|
@@ -8,14 +8,16 @@ import { FileStorage } from "./file-storage.js";
|
|
|
8
8
|
*
|
|
9
9
|
* Overview:
|
|
10
10
|
* - Encapsulates a Babylon.js-powered 3D viewer for displaying models, environments, and materials.
|
|
11
|
-
* - Manages internal state for containers (model, environment, materials) and options (camera, materials).
|
|
11
|
+
* - Manages internal state for containers (model, environment, materials) and options (camera, materials, IBL, render toggles).
|
|
12
12
|
* - Handles asset loading, configuration, and option updates (camera, materials, IBL) through attributes and public methods.
|
|
13
|
+
* - Exposes getters/setters for render settings so PrefViewerMenu3D or other UIs can persistently toggle AA/SSAO/IBL/shadows.
|
|
13
14
|
* - Provides API for showing/hiding the viewer, model, and environment, and for downloading assets.
|
|
14
|
-
* - Emits custom events for loading, loaded, and
|
|
15
|
+
* - Emits custom events for loading, loaded, option-setting, and render-setting workflows.
|
|
15
16
|
*
|
|
16
17
|
* Usage:
|
|
17
18
|
* - Use as a custom HTML element: <pref-viewer-3d ...>
|
|
18
19
|
* - Configure via attributes (config, options, show-model, show-scene, visible).
|
|
20
|
+
* - Call getRenderSettings()/applyRenderSettings() to synchronize UI toggles with BabylonJSController persistence.
|
|
19
21
|
* - Control viewer state and assets via public methods.
|
|
20
22
|
*
|
|
21
23
|
* Observed Attributes:
|
|
@@ -28,6 +30,8 @@ import { FileStorage } from "./file-storage.js";
|
|
|
28
30
|
* - hide(): Hides the 3D viewer component.
|
|
29
31
|
* - load(config): Loads the provided configuration into the viewer.
|
|
30
32
|
* - setOptions(options): Sets viewer options such as camera, materials, and image-based lighting (IBL).
|
|
33
|
+
* - getRenderSettings(): Returns the current anti-aliasing, SSAO, IBL, and shadow flags.
|
|
34
|
+
* - applyRenderSettings(settings): Persists incoming render toggles, reloads if needed, and reports the result.
|
|
31
35
|
* - showModel(): Shows the 3D model.
|
|
32
36
|
* - hideModel(): Hides the 3D model.
|
|
33
37
|
* - showEnvironment(): Shows the 3D environment/scene.
|
|
@@ -63,7 +67,7 @@ import { FileStorage } from "./file-storage.js";
|
|
|
63
67
|
*
|
|
64
68
|
* Notes:
|
|
65
69
|
* - Internal state and heavy initialization are handled in connectedCallback.
|
|
66
|
-
* - Designed for extensibility and integration in product configurators and visualization tools.
|
|
70
|
+
* - Designed for extensibility and integration in product configurators, PrefViewer host elements, and visualization tools.
|
|
67
71
|
* - All resource management and rendering operations are performed asynchronously for performance.
|
|
68
72
|
*/
|
|
69
73
|
export default class PrefViewer3D extends HTMLElement {
|
|
@@ -337,13 +341,12 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
337
341
|
|
|
338
342
|
if (options.ibl.url) {
|
|
339
343
|
url = options.ibl.url;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
// }
|
|
344
|
+
const fileStorage = new FileStorage("PrefViewer", "Files");
|
|
345
|
+
const newURL = await fileStorage.getURL(options.ibl.url);
|
|
346
|
+
if (newURL) {
|
|
347
|
+
url = newURL;
|
|
348
|
+
timeStamp = await fileStorage.getTimeStamp(options.ibl.url);
|
|
349
|
+
}
|
|
347
350
|
}
|
|
348
351
|
if (options.ibl.shadows !== undefined) {
|
|
349
352
|
shadows = options.ibl.shadows;
|
|
@@ -740,6 +743,39 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
740
743
|
this.#babylonJSController.downloadUSDZ(2);
|
|
741
744
|
}
|
|
742
745
|
|
|
746
|
+
/**
|
|
747
|
+
* Returns the current render settings used by the BabylonJSController.
|
|
748
|
+
* @public
|
|
749
|
+
* @returns {object|null} A shallow copy of the render settings or null if unavailable.
|
|
750
|
+
*/
|
|
751
|
+
getRenderSettings() {
|
|
752
|
+
if (!this.#babylonJSController) {
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
return this.#babylonJSController.getRenderSettings();
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Applies render settings that require reloading the Babylon.js scene.
|
|
760
|
+
* @public
|
|
761
|
+
* @param {object} settings - Partial settings for anti-aliasing, SSAO, IBL, and shadows.
|
|
762
|
+
* @returns {Promise<object>} Resolves with the reload summary.
|
|
763
|
+
*/
|
|
764
|
+
async applyRenderSettings(settings) {
|
|
765
|
+
if (!this.#babylonJSController) {
|
|
766
|
+
return { changed: false, success: false };
|
|
767
|
+
}
|
|
768
|
+
const scheduled = this.#babylonJSController.scheduleRenderSettingsReload(settings);
|
|
769
|
+
if (!scheduled.changed) {
|
|
770
|
+
return { changed: false, success: true };
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
this.#onLoading();
|
|
774
|
+
const reloadResult = await this.#babylonJSController.reloadWithCurrentSettings();
|
|
775
|
+
const detail = this.#onLoaded();
|
|
776
|
+
return { changed: true, success: reloadResult.success, detail, controller: reloadResult };
|
|
777
|
+
}
|
|
778
|
+
|
|
743
779
|
/**
|
|
744
780
|
* Indicates whether the component has completed its initialization.
|
|
745
781
|
* @public
|