@preference-sl/pref-viewer 2.11.0-beta.6 → 2.11.0-beta.8
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 +3 -2
- package/src/babylonjs-controller.js +295 -58
- package/src/index.js +101 -9
- package/src/pref-viewer-3d.js +72 -7
- package/src/pref-viewer-dialog.js +121 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@preference-sl/pref-viewer",
|
|
3
|
-
"version": "2.11.0-beta.
|
|
3
|
+
"version": "2.11.0-beta.8",
|
|
4
4
|
"description": "Web Component to preview GLTF models with Babylon.js",
|
|
5
5
|
"author": "Alex Moreno Palacio <amoreno@preference.es>",
|
|
6
6
|
"scripts": {
|
|
@@ -40,7 +40,8 @@
|
|
|
40
40
|
"@panzoom/panzoom": "^4.6.0",
|
|
41
41
|
"babylonjs-gltf2interface": "^8.36.1",
|
|
42
42
|
"idb": "^8.0.3",
|
|
43
|
-
"is-svg": "^6.1.0"
|
|
43
|
+
"is-svg": "^6.1.0",
|
|
44
|
+
"jszip": "^3.10.1"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"esbuild": "^0.25.10",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { ArcRotateCamera, AssetContainer, Color4, DirectionalLight, Engine, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, LoadAssetContainerAsync, MeshBuilder, PointLight, Scene, ShadowGenerator, Tools, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager } from "@babylonjs/core";
|
|
1
|
+
import { ArcRotateCamera, AssetContainer, Camera, Color4, DirectionalLight, Engine, HDRCubeTexture, HemisphericLight, IblShadowsRenderPipeline, LoadAssetContainerAsync, MeshBuilder, PointLight, Scene, ShadowGenerator, Tools, Vector3, WebXRDefaultExperience, WebXRFeatureName, WebXRSessionManager } from "@babylonjs/core";
|
|
2
2
|
import { DracoCompression } from "@babylonjs/core/Meshes/Compression/dracoCompression";
|
|
3
3
|
import "@babylonjs/loaders";
|
|
4
4
|
import "@babylonjs/loaders/glTF/2.0/Extensions/KHR_draco_mesh_compression";
|
|
5
5
|
import { USDZExportAsync, GLTF2Export } from "@babylonjs/serializers";
|
|
6
6
|
|
|
7
7
|
import GLTFResolver from "./gltf-resolver.js";
|
|
8
|
-
import {
|
|
8
|
+
import { MaterialData } from "./pref-viewer-3d-data.js";
|
|
9
9
|
import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -16,7 +16,7 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
16
16
|
* - Handles loading, replacing, and disposing of 3D models, environments, and materials.
|
|
17
17
|
* - Configures advanced rendering features such as Draco mesh compression, shadows, and image-based lighting (IBL).
|
|
18
18
|
* - Integrates Babylon.js WebXR experience for augmented reality (AR) support.
|
|
19
|
-
* - Provides methods for downloading models and scenes in GLB and USDZ formats.
|
|
19
|
+
* - Provides methods for downloading models and scenes in GLB, GLTF, and USDZ formats.
|
|
20
20
|
* - Manages camera and material options, container visibility, and user interactions.
|
|
21
21
|
* - Observes canvas resize events and updates the engine accordingly.
|
|
22
22
|
*
|
|
@@ -26,7 +26,7 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
26
26
|
* - Load assets: await controller.load();
|
|
27
27
|
* - Set camera/material options: controller.setCameraOptions(), controller.setMaterialOptions();
|
|
28
28
|
* - Control visibility: controller.setContainerVisibility(name, show);
|
|
29
|
-
* - Download assets: controller.downloadModelGLB(), controller.downloadModelUSDZ(),
|
|
29
|
+
* - Download assets: controller.downloadModelGLB(), controller.downloadModelGLTF(), controller.downloadModelUSDZ(), controller.downloadModelAndSceneGLB(), controller.downloadModelAndSceneGLTF(), controller.downloadModelAndSceneUSDZ();
|
|
30
30
|
* - Disable rendering: controller.disable();
|
|
31
31
|
*
|
|
32
32
|
* Public Methods:
|
|
@@ -36,20 +36,29 @@ import BabylonJSAnimationController from "./babylonjs-animation-controller.js";
|
|
|
36
36
|
* - setCameraOptions(): Applies camera options from configuration.
|
|
37
37
|
* - setMaterialOptions(): Applies material options from configuration.
|
|
38
38
|
* - setContainerVisibility(name, show): Shows or hides a container by name.
|
|
39
|
-
* -
|
|
39
|
+
* - downloadGLB(content): Downloads the current scene, model, or environment as a GLB file.
|
|
40
|
+
* - downloadGLTF(content): Downloads the current scene, model, or environment as a glTF ZIP file.
|
|
41
|
+
* - downloadUSDZ(content): Downloads the current scene, model, or environment as a USDZ file.
|
|
40
42
|
*
|
|
41
43
|
* Private Methods:
|
|
42
44
|
* - #configureDracoCompression(): Sets up Draco mesh compression.
|
|
45
|
+
* - #renderLoop(): Babylon.js render loop callback.
|
|
46
|
+
* - #addStylesToARButton(): Styles AR button.
|
|
47
|
+
* - #createXRExperience(): Initializes WebXR AR experience.
|
|
43
48
|
* - #createCamera(), #createLights(), #initializeEnvironmentTexture(), #initializeIBLShadows(), #initializeShadows(): Scene setup.
|
|
44
|
-
* - #
|
|
49
|
+
* - #setMaxSimultaneousLights(): Updates max simultaneous lights for materials.
|
|
50
|
+
* - #enableInteraction(), #disableInteraction(): Canvas interaction handlers.
|
|
45
51
|
* - #disposeEngine(): Disposes engine and resources.
|
|
52
|
+
* - #onMouseWheel(event), #onKeyUp(event): Canvas event handlers.
|
|
46
53
|
* - #setOptionsMaterial(), #setOptions_Materials(), #setOptions_Camera(): Applies material/camera options.
|
|
47
54
|
* - #findContainerByName(), #addContainer(), #removeContainer(), #replaceContainer(): Container management.
|
|
55
|
+
* - #getPrefViewer3DComponent(), #getPrefViewerComponent(): Custom element references.
|
|
56
|
+
* - #updateVisibilityAttributeInComponents(): Updates parent visibility attributes.
|
|
48
57
|
* - #setVisibilityOfWallAndFloorInModel(): Controls wall/floor mesh visibility.
|
|
49
58
|
* - #stopRender(), #startRender(): Render loop control.
|
|
50
59
|
* - #loadAssetContainer(), #loadContainers(): Asset loading.
|
|
51
|
-
* - #
|
|
52
|
-
* - #
|
|
60
|
+
* - #downloadZip(): Generates and downloads a ZIP file.
|
|
61
|
+
* - #openDownloadDialog(): Opens the modal download dialog.
|
|
53
62
|
*
|
|
54
63
|
* Notes:
|
|
55
64
|
* - Designed for integration with PrefViewer and GLTFResolver.
|
|
@@ -63,6 +72,7 @@ export default class BabylonJSController {
|
|
|
63
72
|
// References to parent custom elements
|
|
64
73
|
#prefViewer3D = undefined;
|
|
65
74
|
#prefViewer = undefined;
|
|
75
|
+
#prefViewerDialog = null;
|
|
66
76
|
|
|
67
77
|
// Babylon.js core objects
|
|
68
78
|
#engine = null;
|
|
@@ -393,23 +403,22 @@ export default class BabylonJSController {
|
|
|
393
403
|
|
|
394
404
|
/**
|
|
395
405
|
* Sets up interaction handlers for the Babylon.js canvas.
|
|
396
|
-
* Adds a wheel event listener to control camera zoom based on mouse wheel input.
|
|
397
|
-
* Prevents zoom if the active camera is locked.
|
|
398
406
|
* @private
|
|
399
407
|
* @returns {void}
|
|
400
408
|
*/
|
|
401
|
-
#
|
|
402
|
-
this.#canvas.addEventListener("wheel", (
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
409
|
+
#enableInteraction() {
|
|
410
|
+
this.#canvas.addEventListener("wheel", this.#onMouseWheel.bind(this));
|
|
411
|
+
this.#canvas.addEventListener("keyup", this.#onKeyUp.bind(this));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Removes interaction event listeners from the Babylon.js canvas.
|
|
416
|
+
* @private
|
|
417
|
+
* @returns {void}
|
|
418
|
+
*/
|
|
419
|
+
#disableInteraction() {
|
|
420
|
+
this.#canvas.removeEventListener("wheel", this.#onMouseWheel.bind(this));
|
|
421
|
+
this.#canvas.removeEventListener("keyup", this.#onKeyUp.bind(this));
|
|
413
422
|
}
|
|
414
423
|
|
|
415
424
|
/**
|
|
@@ -428,6 +437,43 @@ export default class BabylonJSController {
|
|
|
428
437
|
this.#XRExperience = null;
|
|
429
438
|
}
|
|
430
439
|
|
|
440
|
+
/**
|
|
441
|
+
* Handles mouse wheel events on the Babylon.js canvas for zooming the camera.
|
|
442
|
+
* @private
|
|
443
|
+
* @param {WheelEvent} event - The mouse wheel event.
|
|
444
|
+
* @returns {void|false}
|
|
445
|
+
*/
|
|
446
|
+
#onMouseWheel(event) {
|
|
447
|
+
if (!this.#scene || !this.#camera) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
//const pick = this.#scene.pick(this.#scene.pointerX, this.#scene.pointerY);
|
|
451
|
+
//this.#camera.target = pick.hit ? pick.pickedPoint.clone() : this.#camera.target;
|
|
452
|
+
if (!this.#scene.activeCamera.metadata?.locked) {
|
|
453
|
+
this.#scene.activeCamera.inertialRadiusOffset -= event.deltaY * this.#scene.activeCamera.wheelPrecision * 0.001;
|
|
454
|
+
}
|
|
455
|
+
event.preventDefault();
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Handles keyup events on the Babylon.js canvas for triggering model and scene downloads.
|
|
460
|
+
* @private
|
|
461
|
+
* @param {KeyboardEvent} event - The keyup event.
|
|
462
|
+
* @returns {void}
|
|
463
|
+
*/
|
|
464
|
+
#onKeyUp(event) {
|
|
465
|
+
// CTRL + ALT + letter
|
|
466
|
+
if (event.ctrlKey && event.altKey && event.key !== undefined) {
|
|
467
|
+
switch (event.key.toLowerCase()) {
|
|
468
|
+
case "d":
|
|
469
|
+
this.#openDownloadDialog();
|
|
470
|
+
break;
|
|
471
|
+
default:
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
431
477
|
/**
|
|
432
478
|
* Applies material options from the configuration to the relevant meshes.
|
|
433
479
|
* @private
|
|
@@ -452,7 +498,7 @@ export default class BabylonJSController {
|
|
|
452
498
|
if (container.state.name === "materials") {
|
|
453
499
|
return;
|
|
454
500
|
}
|
|
455
|
-
if (container.assetContainer && (container.isPending || materialContainer.isPending || optionMaterial.isPending)) {
|
|
501
|
+
if (container.assetContainer && (container.state.isPending || materialContainer.state.isPending || optionMaterial.isPending)) {
|
|
456
502
|
assetContainersToProcess.push(container.assetContainer);
|
|
457
503
|
}
|
|
458
504
|
});
|
|
@@ -504,7 +550,7 @@ export default class BabylonJSController {
|
|
|
504
550
|
const modelContainer = this.#containers.model;
|
|
505
551
|
const environmentContainer = this.#containers.environment;
|
|
506
552
|
|
|
507
|
-
if (!cameraState.isPending && !modelContainer.isPending && !environmentContainer.isPending) {
|
|
553
|
+
if (!cameraState.isPending && !modelContainer.state.isPending && !environmentContainer.state.isPending) {
|
|
508
554
|
return false;
|
|
509
555
|
}
|
|
510
556
|
|
|
@@ -559,7 +605,7 @@ export default class BabylonJSController {
|
|
|
559
605
|
* Finds and returns the asset container object by its name.
|
|
560
606
|
* @private
|
|
561
607
|
* @param {string} name - The name of the container to find.
|
|
562
|
-
* @returns {
|
|
608
|
+
* @returns {Object|null} The matching container object, or null if not found.
|
|
563
609
|
*/
|
|
564
610
|
#findContainerByName(name) {
|
|
565
611
|
return Object.values(this.#containers).find((container) => container.state.name === name) || null;
|
|
@@ -584,6 +630,13 @@ export default class BabylonJSController {
|
|
|
584
630
|
*/
|
|
585
631
|
#getPrefViewerComponent() {
|
|
586
632
|
if (this.#prefViewer === undefined) {
|
|
633
|
+
if (this.#prefViewer3D === undefined) {
|
|
634
|
+
this.#getPrefViewer3DComponent();
|
|
635
|
+
}
|
|
636
|
+
if (!this.#prefViewer3D) {
|
|
637
|
+
this.#prefViewer = null;
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
587
640
|
const rootNode = this.#prefViewer3D ? this.#prefViewer3D.getRootNode().host : null;
|
|
588
641
|
this.#prefViewer = rootNode && rootNode.nodeName === "PREF-VIEWER" ? rootNode : null;
|
|
589
642
|
}
|
|
@@ -612,7 +665,7 @@ export default class BabylonJSController {
|
|
|
612
665
|
/**
|
|
613
666
|
* Adds the asset container to the Babylon.js scene if it should be shown and is not already visible.
|
|
614
667
|
* @private
|
|
615
|
-
* @param {
|
|
668
|
+
* @param {object} container - The container object containing asset state and metadata.
|
|
616
669
|
* @param {boolean} [updateVisibility=true] - If true, updates the visibility attribute in parent components.
|
|
617
670
|
* @returns {boolean} True if the container was added, false otherwise.
|
|
618
671
|
*/
|
|
@@ -631,7 +684,7 @@ export default class BabylonJSController {
|
|
|
631
684
|
/**
|
|
632
685
|
* Removes the asset container from the Babylon.js scene if it is currently visible.
|
|
633
686
|
* @private
|
|
634
|
-
* @param {
|
|
687
|
+
* @param {object} container - The container object containing asset state and metadata.
|
|
635
688
|
* @param {boolean} [updateVisibility=true] - If true, updates the visibility attribute in parent components.
|
|
636
689
|
* @returns {boolean} True if the container was removed, false otherwise.
|
|
637
690
|
*/
|
|
@@ -650,7 +703,7 @@ export default class BabylonJSController {
|
|
|
650
703
|
/**
|
|
651
704
|
* Replaces the asset container in the Babylon.js scene with a new one.
|
|
652
705
|
* @private
|
|
653
|
-
* @param {
|
|
706
|
+
* @param {object} container - The container object containing asset state and metadata.
|
|
654
707
|
* @param {AssetContainer} newAssetContainer - The new asset container to add to the scene.
|
|
655
708
|
* @returns {boolean} True if the container was replaced and added, false otherwise.
|
|
656
709
|
*/
|
|
@@ -703,8 +756,8 @@ export default class BabylonJSController {
|
|
|
703
756
|
/**
|
|
704
757
|
* Loads an asset container (model, environment, materials, etc.) using the provided container state.
|
|
705
758
|
* @private
|
|
706
|
-
* @param {
|
|
707
|
-
* @returns {Promise<[
|
|
759
|
+
* @param {object} container - The container object containing asset state and metadata.
|
|
760
|
+
* @returns {Promise<[object, AssetContainer|boolean]>} Resolves to an array with the container and the loaded asset container, or false if loading fails.
|
|
708
761
|
* @description
|
|
709
762
|
* Resolves the asset source using GLTFResolver, prepares plugin options, and loads the asset into the Babylon.js scene.
|
|
710
763
|
* Updates the container's cache data and returns the container along with the loaded asset container or false if loading fails.
|
|
@@ -771,7 +824,7 @@ export default class BabylonJSController {
|
|
|
771
824
|
if (this.#babylonJSAnimationController) {
|
|
772
825
|
this.#babylonJSAnimationController.dispose();
|
|
773
826
|
this.#babylonJSAnimationController = null;
|
|
774
|
-
}
|
|
827
|
+
}
|
|
775
828
|
values.forEach((result) => {
|
|
776
829
|
const container = result.value ? result.value[0] : null;
|
|
777
830
|
const assetContainer = result.value ? result.value[1] : null;
|
|
@@ -788,7 +841,7 @@ export default class BabylonJSController {
|
|
|
788
841
|
container.state.setSuccess(false);
|
|
789
842
|
}
|
|
790
843
|
});
|
|
791
|
-
|
|
844
|
+
|
|
792
845
|
this.#setOptions_Materials();
|
|
793
846
|
this.#setOptions_Camera();
|
|
794
847
|
this.#setVisibilityOfWallAndFloorInModel();
|
|
@@ -809,6 +862,132 @@ export default class BabylonJSController {
|
|
|
809
862
|
return detail;
|
|
810
863
|
}
|
|
811
864
|
|
|
865
|
+
/**
|
|
866
|
+
* Generates and downloads a ZIP file with the specified folder name and files.
|
|
867
|
+
* @private
|
|
868
|
+
* @param {Object} files - An object where keys are file names and values are file contents.
|
|
869
|
+
* @param {string} [name="files"] - The name for the root folder inside the ZIP.
|
|
870
|
+
* @param {string} [comment=""] - A comment to include in the ZIP file.
|
|
871
|
+
* @param {boolean} [addDateInName=false] - Whether to append the current date/time to the folder name.
|
|
872
|
+
* @returns {void}
|
|
873
|
+
* @description
|
|
874
|
+
* Uses JSZip to create the ZIP and triggers a browser download.
|
|
875
|
+
*/
|
|
876
|
+
#downloadZip(files, name = "files", comment = "", addDateInName = false) {
|
|
877
|
+
if (addDateInName) {
|
|
878
|
+
const now = new Date();
|
|
879
|
+
const year = now.getFullYear();
|
|
880
|
+
const month = (now.getMonth() + 1).toString().padStart(2, "0");
|
|
881
|
+
const day = now.getDate().toString().padStart(2, "0");
|
|
882
|
+
const hours = now.getHours().toString().padStart(2, "0");
|
|
883
|
+
const minutes = now.getMinutes().toString().padStart(2, "0");
|
|
884
|
+
const seconds = now.getSeconds().toString().padStart(2, "0");
|
|
885
|
+
name = `${name}_${year}-${month}-${day}_${hours}.${minutes}.${seconds}`;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
const JSZip = require("jszip");
|
|
889
|
+
const zip = new JSZip();
|
|
890
|
+
zip.comment = comment;
|
|
891
|
+
|
|
892
|
+
const rootFolder = zip.folder(name);
|
|
893
|
+
Object.keys(files).forEach((key) => {
|
|
894
|
+
rootFolder.file(key, files[key]);
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
zip.generateAsync({ type: "blob" }).then((content) => {
|
|
898
|
+
const zipName = `${name}.zip`;
|
|
899
|
+
const a = document.createElement("a");
|
|
900
|
+
const url = window.URL.createObjectURL(content);
|
|
901
|
+
a.href = url;
|
|
902
|
+
a.download = zipName;
|
|
903
|
+
a.click();
|
|
904
|
+
window.URL.revokeObjectURL(url);
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Opens a modal dialog for downloading the 3D scene, model, or environment.
|
|
910
|
+
* @private
|
|
911
|
+
* @returns {void}
|
|
912
|
+
*/
|
|
913
|
+
#openDownloadDialog() {
|
|
914
|
+
this.#getPrefViewerComponent();
|
|
915
|
+
if (!this.#prefViewer) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
this.#prefViewerDialog = document.createElement("pref-viewer-dialog");
|
|
919
|
+
this.#prefViewer.shadowRoot.appendChild(this.#prefViewerDialog);
|
|
920
|
+
|
|
921
|
+
const content = document.createElement("div");
|
|
922
|
+
content.innerHTML = `
|
|
923
|
+
<form id="download-dialog-form" style="display:flex;flex-direction:column;gap:15px;">
|
|
924
|
+
<h3 style="margin: 0 0 5px; 0">Download 3D Scene</h3>
|
|
925
|
+
<h4 style="margin:0;">Content</h4>
|
|
926
|
+
<div style="display:flex;flex-direction:row;gap:15px;margin:0 10px 0 10px;">
|
|
927
|
+
<label><input type="radio" name="content" value="1"> Model</label>
|
|
928
|
+
<label><input type="radio" name="content" value="2"> Scene</label>
|
|
929
|
+
<label><input type="radio" name="content" value="0" checked> Both</label>
|
|
930
|
+
</div>
|
|
931
|
+
<h4 style="margin:0;">Format</h4>
|
|
932
|
+
<div style="display:flex;flex-direction:row;gap:15px;margin: 0 10px 0 10px;">
|
|
933
|
+
<label><input type="radio" name="format" value="gltf" checked> glTF (ZIP)</label>
|
|
934
|
+
<label><input type="radio" name="format" value="glb"> GLB</label>
|
|
935
|
+
<label><input type="radio" name="format" value="usdz"> USDZ</label>
|
|
936
|
+
</div>
|
|
937
|
+
<div style="display:flex;gap:15px;justify-content:stretch;margin-top:10px;">
|
|
938
|
+
<button type="button" id="download-dialog-download">Download</button>
|
|
939
|
+
<button type="button" id="download-dialog-cancel">Cancel</button>
|
|
940
|
+
</div>
|
|
941
|
+
<style>
|
|
942
|
+
#download-dialog-download,
|
|
943
|
+
#download-dialog-cancel {
|
|
944
|
+
width: 100%;
|
|
945
|
+
padding: 8px 20px;
|
|
946
|
+
font-size: 1.1rem;
|
|
947
|
+
border-radius: 6px;
|
|
948
|
+
border: none;
|
|
949
|
+
background: #eee;
|
|
950
|
+
cursor: pointer;
|
|
951
|
+
transition: background 0.2s;
|
|
952
|
+
}
|
|
953
|
+
#download-dialog-download:hover,
|
|
954
|
+
#download-dialog-cancel:hover {
|
|
955
|
+
background: #e0e0e0;
|
|
956
|
+
}
|
|
957
|
+
</style>
|
|
958
|
+
</form>`;
|
|
959
|
+
|
|
960
|
+
this.#prefViewerDialog.open(content);
|
|
961
|
+
|
|
962
|
+
// Button event handlers
|
|
963
|
+
const form = content.querySelector("#download-dialog-form");
|
|
964
|
+
const downloadBtn = content.querySelector("#download-dialog-download");
|
|
965
|
+
const cancelBtn = content.querySelector("#download-dialog-cancel");
|
|
966
|
+
|
|
967
|
+
downloadBtn.onclick = () => {
|
|
968
|
+
const contentValue = form.content.value;
|
|
969
|
+
const formatValue = form.format.value;
|
|
970
|
+
switch (formatValue) {
|
|
971
|
+
case "glb":
|
|
972
|
+
this.downloadGLB(Number(contentValue));
|
|
973
|
+
break;
|
|
974
|
+
case "gltf":
|
|
975
|
+
this.downloadGLTF(Number(contentValue));
|
|
976
|
+
break;
|
|
977
|
+
case "usdz":
|
|
978
|
+
this.downloadUSDZ(Number(contentValue));
|
|
979
|
+
break;
|
|
980
|
+
}
|
|
981
|
+
this.#prefViewerDialog.close();
|
|
982
|
+
};
|
|
983
|
+
|
|
984
|
+
cancelBtn.onclick = () => {
|
|
985
|
+
this.#prefViewerDialog.close();
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
this.#prefViewerDialog.open(content);
|
|
989
|
+
}
|
|
990
|
+
|
|
812
991
|
/**
|
|
813
992
|
* ---------------------------
|
|
814
993
|
* Public methods
|
|
@@ -830,7 +1009,7 @@ export default class BabylonJSController {
|
|
|
830
1009
|
this.#scene.clearColor = new Color4(1, 1, 1, 1);
|
|
831
1010
|
this.#createCamera();
|
|
832
1011
|
this.#createLights();
|
|
833
|
-
this.#
|
|
1012
|
+
this.#enableInteraction();
|
|
834
1013
|
await this.#createXRExperience();
|
|
835
1014
|
this.#startRender();
|
|
836
1015
|
this.#canvasResizeObserver = new ResizeObserver(() => this.#engine && this.#engine.resize());
|
|
@@ -844,8 +1023,13 @@ export default class BabylonJSController {
|
|
|
844
1023
|
* @returns {void}
|
|
845
1024
|
*/
|
|
846
1025
|
disable() {
|
|
847
|
-
this.#disposeEngine();
|
|
848
1026
|
this.#canvasResizeObserver.disconnect();
|
|
1027
|
+
this.#disableInteraction();
|
|
1028
|
+
if (this.#babylonJSAnimationController) {
|
|
1029
|
+
this.#babylonJSAnimationController.dispose();
|
|
1030
|
+
this.#babylonJSAnimationController = null;
|
|
1031
|
+
}
|
|
1032
|
+
this.#disposeEngine();
|
|
849
1033
|
}
|
|
850
1034
|
|
|
851
1035
|
/**
|
|
@@ -909,50 +1093,103 @@ export default class BabylonJSController {
|
|
|
909
1093
|
}
|
|
910
1094
|
|
|
911
1095
|
/**
|
|
912
|
-
*
|
|
1096
|
+
* Downloads the current scene, model, or environment as a GLB file.
|
|
913
1097
|
* @public
|
|
1098
|
+
* @param {number} content - 0 for full scene, 1 for model, 2 for environment.
|
|
914
1099
|
* @returns {void}
|
|
915
1100
|
*/
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1101
|
+
downloadGLB(content = 0) {
|
|
1102
|
+
let fileName = "";
|
|
1103
|
+
let container = null;
|
|
1104
|
+
switch (content) {
|
|
1105
|
+
case 0:
|
|
1106
|
+
fileName = "full-scene";
|
|
1107
|
+
container = this.#scene;
|
|
1108
|
+
break;
|
|
1109
|
+
case 1:
|
|
1110
|
+
fileName = "model";
|
|
1111
|
+
container = this.#containers.model.assetContainer;
|
|
1112
|
+
break;
|
|
1113
|
+
case 2:
|
|
1114
|
+
fileName = "scene";
|
|
1115
|
+
container = this.#containers.environment.assetContainer;
|
|
1116
|
+
break;
|
|
1117
|
+
default:
|
|
1118
|
+
break;
|
|
1119
|
+
}
|
|
1120
|
+
GLTF2Export.GLBAsync(container, fileName, { exportWithoutWaitingForScene: true }).then((glb) => {
|
|
1121
|
+
if (glb) {
|
|
1122
|
+
glb.downloadFiles();
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
919
1125
|
}
|
|
920
1126
|
|
|
921
1127
|
/**
|
|
922
|
-
*
|
|
1128
|
+
* Downloads the current scene, model, or environment as a glTF ZIP file.
|
|
923
1129
|
* @public
|
|
1130
|
+
* @param {number} content - 0 for full scene, 1 for model, 2 for environment.
|
|
924
1131
|
* @returns {void}
|
|
925
1132
|
*/
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1133
|
+
downloadGLTF(content = 0) {
|
|
1134
|
+
let fileName = "";
|
|
1135
|
+
let container = null;
|
|
1136
|
+
let comment = "";
|
|
1137
|
+
switch (content) {
|
|
1138
|
+
case 0:
|
|
1139
|
+
fileName = "full-scene";
|
|
1140
|
+
comment = "Export of the complete scene in glTF format, including the model and the environment.";
|
|
1141
|
+
container = this.#scene;
|
|
1142
|
+
break;
|
|
1143
|
+
case 1:
|
|
1144
|
+
fileName = "model";
|
|
1145
|
+
comment = "Export of model scene in glTF format.";
|
|
1146
|
+
container = this.#containers.model.assetContainer;
|
|
1147
|
+
break;
|
|
1148
|
+
case 2:
|
|
1149
|
+
fileName = "scene";
|
|
1150
|
+
comment = "Export of the environment scene in glTF format.";
|
|
1151
|
+
container = this.#containers.environment.assetContainer;
|
|
1152
|
+
break;
|
|
1153
|
+
default:
|
|
1154
|
+
break;
|
|
1155
|
+
}
|
|
1156
|
+
comment += "\nPrefViewer by Preference, S.L.\npreference.com\n";
|
|
1157
|
+
GLTF2Export.GLTFAsync(container, fileName, { exportWithoutWaitingForScene: true }).then((gltf) => {
|
|
1158
|
+
if (gltf?.files) {
|
|
1159
|
+
this.#downloadZip(gltf.files, fileName, comment, true);
|
|
931
1160
|
}
|
|
932
1161
|
});
|
|
933
1162
|
}
|
|
934
1163
|
|
|
935
1164
|
/**
|
|
936
|
-
*
|
|
1165
|
+
* Downloads the current scene, model, or environment as a USDZ file.
|
|
937
1166
|
* @public
|
|
1167
|
+
* @param {number} content - 0 for full scene, 1 for model, 2 for environment.
|
|
938
1168
|
* @returns {void}
|
|
939
1169
|
*/
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1170
|
+
downloadUSDZ(content = 0) {
|
|
1171
|
+
let fileName = "";
|
|
1172
|
+
let container = null;
|
|
1173
|
+
switch (content) {
|
|
1174
|
+
case 0:
|
|
1175
|
+
fileName = "full-scene";
|
|
1176
|
+
container = this.#scene;
|
|
1177
|
+
break;
|
|
1178
|
+
case 1:
|
|
1179
|
+
fileName = "model";
|
|
1180
|
+
container = this.#containers.model.assetContainer;
|
|
1181
|
+
break;
|
|
1182
|
+
case 2:
|
|
1183
|
+
fileName = "scene";
|
|
1184
|
+
container = this.#containers.environment.assetContainer;
|
|
1185
|
+
break;
|
|
1186
|
+
default:
|
|
1187
|
+
break;
|
|
1188
|
+
}
|
|
1189
|
+
USDZExportAsync(container).then((response) => {
|
|
943
1190
|
if (response) {
|
|
944
1191
|
Tools.Download(new Blob([response], { type: "model/vnd.usdz+zip" }), `${fileName}.usdz`);
|
|
945
1192
|
}
|
|
946
1193
|
});
|
|
947
1194
|
}
|
|
948
|
-
|
|
949
|
-
/**
|
|
950
|
-
* Initiates download of the entire scene (model and environment) as a GLB file.
|
|
951
|
-
* @public
|
|
952
|
-
* @returns {void}
|
|
953
|
-
*/
|
|
954
|
-
downloadModelAndSceneGLB() {
|
|
955
|
-
const fileName = "scene";
|
|
956
|
-
GLTF2Export.GLBAsync(this.#scene, fileName, { exportWithoutWaitingForScene: true }).then((glb) => glb.downloadFiles());
|
|
957
|
-
}
|
|
958
1195
|
}
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PrefViewer2D } from "./pref-viewer-2d.js";
|
|
2
2
|
import { PrefViewer3D } from "./pref-viewer-3d.js";
|
|
3
|
+
import { PrefViewerDialog } from "./pref-viewer-dialog.js";
|
|
3
4
|
import { PrefViewerTask } from "./pref-viewer-task.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -29,12 +30,30 @@ import { PrefViewerTask } from "./pref-viewer-task.js";
|
|
|
29
30
|
* - mode: Viewer mode ("2d" or "3d").
|
|
30
31
|
*
|
|
31
32
|
* Public Methods:
|
|
32
|
-
* - loadConfig(config)
|
|
33
|
-
* -
|
|
33
|
+
* - loadConfig(config): Loads a configuration object or JSON string.
|
|
34
|
+
* - loadModel(model): Loads a model object or JSON string.
|
|
35
|
+
* - loadScene(scene): Loads a scene/environment object or JSON string.
|
|
36
|
+
* - loadMaterials(materials): Loads materials object or JSON string.
|
|
37
|
+
* - loadDrawing(drawing): Loads a drawing object or JSON string.
|
|
38
|
+
* - setOptions(options): Sets viewer options from an object or JSON string.
|
|
34
39
|
* - setMode(mode): Sets the viewer mode to "2d" or "3d" and updates component visibility.
|
|
35
|
-
* - showModel()
|
|
36
|
-
* -
|
|
37
|
-
* -
|
|
40
|
+
* - showModel(): Shows the 3D model.
|
|
41
|
+
* - hideModel(): Hides the 3D model.
|
|
42
|
+
* - showScene(): Shows the 3D environment/scene.
|
|
43
|
+
* - hideScene(): Hides the 3D environment/scene.
|
|
44
|
+
* - zoomCenter(): Centers the 2D drawing view.
|
|
45
|
+
* - zoomExtentsAll(): Zooms the 2D drawing to fit all content.
|
|
46
|
+
* - zoomIn(): Zooms in on the 2D drawing.
|
|
47
|
+
* - zoomOut(): Zooms out of the 2D drawing.
|
|
48
|
+
* - downloadModelGLB(): Downloads the current 3D model as a GLB file.
|
|
49
|
+
* - downloadModelGLTF(): Downloads the current 3D model as a glTF ZIP file.
|
|
50
|
+
* - downloadModelUSDZ(): Downloads the current 3D model as a USDZ file.
|
|
51
|
+
* - downloadModelAndSceneGLB(): Downloads both the model and scene as a GLB file.
|
|
52
|
+
* - downloadModelAndSceneGLTF(): Downloads both the model and scene as a glTF ZIP file.
|
|
53
|
+
* - downloadModelAndSceneUSDZ(): Downloads both the model and scene as a USDZ file.
|
|
54
|
+
* - downloadSceneGLB(): Downloads the environment as a GLB file.
|
|
55
|
+
* - downloadSceneGLTF(): Downloads the environment as a glTF ZIP file.
|
|
56
|
+
* - downloadSceneUSDZ(): Downloads the environment as a USDZ file.
|
|
38
57
|
*
|
|
39
58
|
* Public Properties:
|
|
40
59
|
* - isInitialized: Indicates if the viewer is initialized.
|
|
@@ -65,6 +84,7 @@ class PrefViewer extends HTMLElement {
|
|
|
65
84
|
|
|
66
85
|
#component2D = null;
|
|
67
86
|
#component3D = null;
|
|
87
|
+
#dialog = null;
|
|
68
88
|
|
|
69
89
|
/**
|
|
70
90
|
* Creates a new PrefViewer instance and attaches a shadow DOM.
|
|
@@ -689,6 +709,19 @@ class PrefViewer extends HTMLElement {
|
|
|
689
709
|
this.#component3D.downloadModelGLB();
|
|
690
710
|
}
|
|
691
711
|
|
|
712
|
+
/**
|
|
713
|
+
* Initiates download of the current 3D model in GLTF format.
|
|
714
|
+
* @public
|
|
715
|
+
* @returns {void}
|
|
716
|
+
*/
|
|
717
|
+
downloadModelGLTF() {
|
|
718
|
+
if (!this.#component3D) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
this.#component3D.downloadModelGLTF();
|
|
723
|
+
}
|
|
724
|
+
|
|
692
725
|
/**
|
|
693
726
|
* Initiates download of the current 3D model in USDZ format.
|
|
694
727
|
* @public
|
|
@@ -701,9 +734,35 @@ class PrefViewer extends HTMLElement {
|
|
|
701
734
|
|
|
702
735
|
this.#component3D.downloadModelUSDZ();
|
|
703
736
|
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Initiates download of the current complete scene (3D model and environment) in GLB format.
|
|
740
|
+
* @public
|
|
741
|
+
* @returns {void}
|
|
742
|
+
*/
|
|
743
|
+
downloadModelAndSceneGLB() {
|
|
744
|
+
if (!this.#component3D) {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
this.#component3D.downloadModelAndSceneGLB();
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Initiates download of the current complete scene (3D model and environment) in GLTF format.
|
|
753
|
+
* @public
|
|
754
|
+
* @returns {void}
|
|
755
|
+
*/
|
|
756
|
+
downloadModelAndSceneGLTF() {
|
|
757
|
+
if (!this.#component3D) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
this.#component3D.downloadModelAndSceneGLTF();
|
|
762
|
+
}
|
|
704
763
|
|
|
705
764
|
/**
|
|
706
|
-
* Initiates download of
|
|
765
|
+
* Initiates download of the current complete scene (3D model and environment) in USDZ format.
|
|
707
766
|
* @public
|
|
708
767
|
* @returns {void}
|
|
709
768
|
*/
|
|
@@ -716,18 +775,50 @@ class PrefViewer extends HTMLElement {
|
|
|
716
775
|
}
|
|
717
776
|
|
|
718
777
|
/**
|
|
719
|
-
* Initiates download of
|
|
778
|
+
* Initiates download of the current 3D environment in GLB format.
|
|
720
779
|
* @public
|
|
721
780
|
* @returns {void}
|
|
722
781
|
*/
|
|
723
|
-
|
|
782
|
+
downloadSceneGLB() {
|
|
724
783
|
if (!this.#component3D) {
|
|
725
784
|
return;
|
|
726
785
|
}
|
|
727
786
|
|
|
728
|
-
this.#component3D.
|
|
787
|
+
this.#component3D.downloadSceneGLB();
|
|
729
788
|
}
|
|
730
789
|
|
|
790
|
+
/**
|
|
791
|
+
* Initiates download of the current 3D environment in GLTF format.
|
|
792
|
+
* @public
|
|
793
|
+
* @returns {void}
|
|
794
|
+
*/
|
|
795
|
+
downloadSceneGLTF() {
|
|
796
|
+
if (!this.#component3D) {
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
this.#component3D.downloadSceneGLTF();
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* Initiates download of the current 3D environment in USDZ format.
|
|
805
|
+
* @public
|
|
806
|
+
* @returns {void}
|
|
807
|
+
*/
|
|
808
|
+
downloadSceneUSDZ() {
|
|
809
|
+
if (!this.#component3D) {
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
this.#component3D.downloadSceneUSDZ();
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* ---------------------------
|
|
818
|
+
* Public properties
|
|
819
|
+
* ---------------------------
|
|
820
|
+
*/
|
|
821
|
+
|
|
731
822
|
/**
|
|
732
823
|
* Indicates whether the viewer has been initialized.
|
|
733
824
|
* @public
|
|
@@ -776,4 +867,5 @@ class PrefViewer extends HTMLElement {
|
|
|
776
867
|
|
|
777
868
|
customElements.define("pref-viewer-2d", PrefViewer2D);
|
|
778
869
|
customElements.define("pref-viewer-3d", PrefViewer3D);
|
|
870
|
+
customElements.define("pref-viewer-dialog", PrefViewerDialog);
|
|
779
871
|
customElements.define("pref-viewer", PrefViewer);
|
package/src/pref-viewer-3d.js
CHANGED
|
@@ -31,9 +31,14 @@ import BabylonJSController from "./babylonjs-controller.js";
|
|
|
31
31
|
* - showEnvironment(): Shows the 3D environment/scene.
|
|
32
32
|
* - hideEnvironment(): Hides the 3D environment/scene.
|
|
33
33
|
* - downloadModelGLB(): Downloads the current 3D model as a GLB file.
|
|
34
|
+
* - downloadModelGLTF(): Downloads the current 3D model as a glTF ZIP file.
|
|
34
35
|
* - downloadModelUSDZ(): Downloads the current 3D model as a USDZ file.
|
|
35
36
|
* - downloadModelAndSceneGLB(): Downloads both the model and scene as a GLB file.
|
|
37
|
+
* - downloadModelAndSceneGLTF(): Downloads both the model and scene as a glTF ZIP file.
|
|
36
38
|
* - downloadModelAndSceneUSDZ(): Downloads both the model and scene as a USDZ file.
|
|
39
|
+
* - downloadSceneGLB(): Downloads the environment as a GLB file.
|
|
40
|
+
* - downloadSceneGLTF(): Downloads the environment as a glTF ZIP file.
|
|
41
|
+
* - downloadSceneUSDZ(): Downloads the environment as a USDZ file.
|
|
37
42
|
*
|
|
38
43
|
* Public Properties:
|
|
39
44
|
* - isInitialized: Indicates whether the component has completed initialization.
|
|
@@ -567,7 +572,19 @@ export class PrefViewer3D extends HTMLElement {
|
|
|
567
572
|
if (!this.#babylonJSController) {
|
|
568
573
|
return;
|
|
569
574
|
}
|
|
570
|
-
this.#babylonJSController.
|
|
575
|
+
this.#babylonJSController.downloadGLB(1);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Downloads the current 3D model as a glTF file (ZIP with gltf and all resources (textures, buffers, etc.)).
|
|
580
|
+
* @public
|
|
581
|
+
* @returns {void}
|
|
582
|
+
*/
|
|
583
|
+
downloadModelGLTF() {
|
|
584
|
+
if (!this.#babylonJSController) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
this.#babylonJSController.downloadGLTF(1);
|
|
571
588
|
}
|
|
572
589
|
|
|
573
590
|
/**
|
|
@@ -579,11 +596,35 @@ export class PrefViewer3D extends HTMLElement {
|
|
|
579
596
|
if (!this.#babylonJSController) {
|
|
580
597
|
return;
|
|
581
598
|
}
|
|
582
|
-
this.#babylonJSController.
|
|
599
|
+
this.#babylonJSController.downloadUSDZ(1);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Downloads the current 3D complete scene (model and environment) as a GLB file.
|
|
604
|
+
* @public
|
|
605
|
+
* @returns {void}
|
|
606
|
+
*/
|
|
607
|
+
downloadModelAndSceneGLB() {
|
|
608
|
+
if (!this.#babylonJSController) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
this.#babylonJSController.downloadModelGLB(0);
|
|
583
612
|
}
|
|
584
613
|
|
|
585
614
|
/**
|
|
586
|
-
* Downloads
|
|
615
|
+
* Downloads the current 3D complete scene (model and environment) as a glTF file (ZIP with gltf and all resources (textures, buffers, etc.)).
|
|
616
|
+
* @public
|
|
617
|
+
* @returns {void}
|
|
618
|
+
*/
|
|
619
|
+
downloadModelAndSceneGLTF() {
|
|
620
|
+
if (!this.#babylonJSController) {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
this.#babylonJSController.downloadGLTF(0);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Downloads the current 3D complete scene (model and environment) as a USDZ file.
|
|
587
628
|
* @public
|
|
588
629
|
* @returns {void}
|
|
589
630
|
*/
|
|
@@ -591,19 +632,43 @@ export class PrefViewer3D extends HTMLElement {
|
|
|
591
632
|
if (!this.#babylonJSController) {
|
|
592
633
|
return;
|
|
593
634
|
}
|
|
594
|
-
this.#babylonJSController.
|
|
635
|
+
this.#babylonJSController.downloadUSDZ(0);
|
|
595
636
|
}
|
|
596
637
|
|
|
597
638
|
/**
|
|
598
|
-
* Downloads
|
|
639
|
+
* Downloads the current 3D environment as a GLB file.
|
|
599
640
|
* @public
|
|
600
641
|
* @returns {void}
|
|
601
642
|
*/
|
|
602
|
-
|
|
643
|
+
downloadSceneGLB() {
|
|
644
|
+
if (!this.#babylonJSController) {
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
this.#babylonJSController.downloadGLB(2);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Downloads the current 3D environment as a glTF file (ZIP with gltf and all resources (textures, buffers, etc.)).
|
|
652
|
+
* @public
|
|
653
|
+
* @returns {void}
|
|
654
|
+
*/
|
|
655
|
+
downloadSceneGLTF() {
|
|
656
|
+
if (!this.#babylonJSController) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
this.#babylonJSController.downloadGLTF(2);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Downloads the current 3D environment as a USDZ file.
|
|
664
|
+
* @public
|
|
665
|
+
* @returns {void}
|
|
666
|
+
*/
|
|
667
|
+
downloadSceneUSDZ() {
|
|
603
668
|
if (!this.#babylonJSController) {
|
|
604
669
|
return;
|
|
605
670
|
}
|
|
606
|
-
this.#babylonJSController.
|
|
671
|
+
this.#babylonJSController.downloadUSDZ(2);
|
|
607
672
|
}
|
|
608
673
|
|
|
609
674
|
/**
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PrefViewerDialog - Custom Web Component for displaying modal dialogs in PrefViewer.
|
|
3
|
+
*
|
|
4
|
+
* Overview:
|
|
5
|
+
* - Provides a modal dialog overlay for user interactions such as downloads or confirmations.
|
|
6
|
+
* - Handles DOM creation, styling, and open/close logic.
|
|
7
|
+
* - Ensures dialog content is centered and styled appropriately.
|
|
8
|
+
* - Automatically focuses the canvas when closed for improved accessibility.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* - Use as a custom HTML element: <pref-viewer-dialog></pref-viewer-dialog>
|
|
12
|
+
* - Call open(content) to display the dialog with custom HTML content.
|
|
13
|
+
* - Call close() to hide and remove the dialog.
|
|
14
|
+
*
|
|
15
|
+
* Methods:
|
|
16
|
+
* - constructor(): Initializes the custom element.
|
|
17
|
+
* - connectedCallback(): Called when the element is added to the DOM; sets up DOM and styles.
|
|
18
|
+
* - disconnectedCallback(): Called when the element is removed from the DOM; cleans up resources.
|
|
19
|
+
* - open(content): Displays the dialog and appends the provided content.
|
|
20
|
+
* - close(): Hides and removes the dialog, refocuses the canvas if available.
|
|
21
|
+
* - #createDOMElements(): Creates the dialog structure and applies styles.
|
|
22
|
+
*/
|
|
23
|
+
export class PrefViewerDialog extends HTMLElement {
|
|
24
|
+
#wrapper = null;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Initializes the custom dialog element.
|
|
28
|
+
* Only calls super(); heavy initialization is deferred to connectedCallback.
|
|
29
|
+
*/
|
|
30
|
+
constructor() {
|
|
31
|
+
super();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Called when the element is inserted into the DOM.
|
|
36
|
+
* Sets up the dialog's DOM structure and styles.
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
connectedCallback() {
|
|
40
|
+
this.#createDOMElements();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Called when the element is removed from the DOM.
|
|
45
|
+
* Cleans up resources and event listeners.
|
|
46
|
+
* @returns {void}
|
|
47
|
+
*/
|
|
48
|
+
disconnectedCallback() {}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates the dialog's DOM structure and applies CSS styles.
|
|
52
|
+
* Adds a click handler to close the dialog when clicking outside the content.
|
|
53
|
+
* @private
|
|
54
|
+
* @returns {void}
|
|
55
|
+
*/
|
|
56
|
+
#createDOMElements() {
|
|
57
|
+
this.#wrapper = document.createElement("div");
|
|
58
|
+
this.append(this.#wrapper);
|
|
59
|
+
const style = document.createElement("style");
|
|
60
|
+
style.textContent = `
|
|
61
|
+
pref-viewer-dialog {
|
|
62
|
+
display: none;
|
|
63
|
+
}
|
|
64
|
+
pref-viewer-dialog[open] {
|
|
65
|
+
font-family: ui-sans-serif, system-ui, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
|
66
|
+
width: 100%;
|
|
67
|
+
height: 100%;
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
justify-content: center;
|
|
71
|
+
background-color: rgba(0, 0, 0, 0.25);
|
|
72
|
+
position: fixed;
|
|
73
|
+
top: 0;
|
|
74
|
+
left: 0;
|
|
75
|
+
z-index: 1000;
|
|
76
|
+
}
|
|
77
|
+
pref-viewer-dialog > div:first-child {
|
|
78
|
+
display: inline-block;
|
|
79
|
+
background: #fff;
|
|
80
|
+
border-radius: 10px;
|
|
81
|
+
box-shadow: 0 4px 24px rgba(0,0,0,0.25);
|
|
82
|
+
padding: 15px;
|
|
83
|
+
max-width: 90%;
|
|
84
|
+
max-height: 90%;
|
|
85
|
+
overflow: auto;
|
|
86
|
+
}`;
|
|
87
|
+
this.append(style);
|
|
88
|
+
this.addEventListener("click", (event) => {
|
|
89
|
+
if (event.target === this) {
|
|
90
|
+
this.close();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Opens the dialog and appends the provided content.
|
|
97
|
+
* @param {HTMLElement} content - The HTML content to display inside the dialog.
|
|
98
|
+
* @returns {void}
|
|
99
|
+
*/
|
|
100
|
+
open(content) {
|
|
101
|
+
this.setAttribute("open", "");
|
|
102
|
+
this.#wrapper.appendChild(content);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Closes and removes the dialog from the DOM.
|
|
107
|
+
* @returns {void}
|
|
108
|
+
*/
|
|
109
|
+
close() {
|
|
110
|
+
this.removeAttribute("open");
|
|
111
|
+
const parent = this.getRootNode().host;
|
|
112
|
+
if (parent) {
|
|
113
|
+
// Refocus canvas for accessibility
|
|
114
|
+
const canvas = parent.shadowRoot.querySelector("canvas");
|
|
115
|
+
if (canvas) {
|
|
116
|
+
canvas.focus();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
this.remove();
|
|
120
|
+
}
|
|
121
|
+
}
|