@preference-sl/pref-viewer 2.13.0-beta.13 → 2.13.0-beta.15
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 +1 -1
- package/src/babylonjs-controller.js +11 -9
- package/src/gltf-storage.js +48 -55
- package/src/pref-viewer-3d.js +30 -6
- package/src/pref-viewer.js +113 -13
package/package.json
CHANGED
|
@@ -2087,7 +2087,7 @@ export default class BabylonJSController {
|
|
|
2087
2087
|
* Applies material and camera options, sets wall/floor visibility, and initializes lights and shadows.
|
|
2088
2088
|
* Returns an object with success status and error details.
|
|
2089
2089
|
*/
|
|
2090
|
-
async #loadContainers() {
|
|
2090
|
+
async #loadContainers(force = false) {
|
|
2091
2091
|
this.#detachAnimationChangedListener();
|
|
2092
2092
|
await this.#stopRender();
|
|
2093
2093
|
|
|
@@ -2096,7 +2096,7 @@ export default class BabylonJSController {
|
|
|
2096
2096
|
|
|
2097
2097
|
const promiseArray = [];
|
|
2098
2098
|
Object.values(this.#containers).forEach((container) => {
|
|
2099
|
-
promiseArray.push(this.#loadAssetContainer(container));
|
|
2099
|
+
promiseArray.push(this.#loadAssetContainer(container, force));
|
|
2100
2100
|
});
|
|
2101
2101
|
|
|
2102
2102
|
let detail = {
|
|
@@ -2594,8 +2594,8 @@ export default class BabylonJSController {
|
|
|
2594
2594
|
* @public
|
|
2595
2595
|
* @returns {Promise<boolean>} Resolves to true if loading succeeds, false otherwise.
|
|
2596
2596
|
*/
|
|
2597
|
-
async load() {
|
|
2598
|
-
return await this.#loadContainers();
|
|
2597
|
+
async load(force = false) {
|
|
2598
|
+
return await this.#loadContainers(force);
|
|
2599
2599
|
}
|
|
2600
2600
|
|
|
2601
2601
|
/**
|
|
@@ -2630,11 +2630,13 @@ export default class BabylonJSController {
|
|
|
2630
2630
|
* @public
|
|
2631
2631
|
* @returns {boolean} True if IBL options were set successfully, false otherwise.
|
|
2632
2632
|
*/
|
|
2633
|
-
setIBLOptions() {
|
|
2634
|
-
this.#stopRender();
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2633
|
+
async setIBLOptions() {
|
|
2634
|
+
await this.#stopRender();
|
|
2635
|
+
try {
|
|
2636
|
+
return await this.#setOptions_IBL();
|
|
2637
|
+
} finally {
|
|
2638
|
+
await this.#startRender();
|
|
2639
|
+
}
|
|
2638
2640
|
}
|
|
2639
2641
|
|
|
2640
2642
|
/**
|
package/src/gltf-storage.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
const PC = (globalThis.PrefConfigurator ??= {});
|
|
5
5
|
PC.version = PC.version ?? "1.0.1"; // bump if your schema changes!!
|
|
6
6
|
PC.db = PC.db ?? null;
|
|
7
|
+
PC.dbName = PC.dbName ?? null;
|
|
8
|
+
PC.storeName = PC.storeName ?? null;
|
|
7
9
|
|
|
8
10
|
function _getDbOrThrow() {
|
|
9
11
|
const db = PC.db;
|
|
@@ -23,22 +25,50 @@ function _openEnsuringStore(dbName, storeName) {
|
|
|
23
25
|
}
|
|
24
26
|
open.onupgradeneeded = (ev) => {
|
|
25
27
|
const upgradeDb = ev.target.result;
|
|
28
|
+
const tx = ev.target.transaction;
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
if (upgradeDb.objectStoreNames.contains(storeName)) {
|
|
29
|
-
upgradeDb.
|
|
30
|
+
let store;
|
|
31
|
+
if (!upgradeDb.objectStoreNames.contains(storeName)) {
|
|
32
|
+
store = upgradeDb.createObjectStore(storeName, { keyPath: 'id' });
|
|
33
|
+
} else if (tx) {
|
|
34
|
+
store = tx.objectStore(storeName);
|
|
30
35
|
}
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
if (store && !store.indexNames.contains('expirationTimeStamp')) {
|
|
38
|
+
store.createIndex('expirationTimeStamp', 'expirationTimeStamp', { unique: false });
|
|
39
|
+
}
|
|
34
40
|
};
|
|
35
41
|
});
|
|
36
42
|
}
|
|
37
43
|
|
|
44
|
+
function _isRecoverableDbError(err) {
|
|
45
|
+
const msg = String(err?.message ?? err ?? "");
|
|
46
|
+
if (!msg) return false;
|
|
47
|
+
return msg.includes("Database not initialized") ||
|
|
48
|
+
msg.includes("The database connection is closing") ||
|
|
49
|
+
msg.includes("InvalidStateError") ||
|
|
50
|
+
msg.includes("NotFoundError");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function _withDbRetry(work) {
|
|
54
|
+
try {
|
|
55
|
+
return await work(_getDbOrThrow());
|
|
56
|
+
} catch (e) {
|
|
57
|
+
if (!_isRecoverableDbError(e) || !PC.dbName || !PC.storeName) {
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
await initDb(PC.dbName, PC.storeName);
|
|
61
|
+
return await work(_getDbOrThrow());
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
38
65
|
// --- public API -------------------------------------------------------------
|
|
39
66
|
|
|
40
67
|
// Inicializar IndexedDB y dejar el handle en PrefConfigurator.db (público)
|
|
41
68
|
export async function initDb(dbName, storeName) {
|
|
69
|
+
PC.dbName = dbName;
|
|
70
|
+
PC.storeName = storeName;
|
|
71
|
+
|
|
42
72
|
const db = await _openEnsuringStore(dbName, storeName);
|
|
43
73
|
// Close any previous handle to avoid versionchange blocking
|
|
44
74
|
try { PC.db?.close?.(); } catch { }
|
|
@@ -54,10 +84,7 @@ export async function initDb(dbName, storeName) {
|
|
|
54
84
|
|
|
55
85
|
// Guardar modelo
|
|
56
86
|
export async function saveModel(modelDataStr, storeName) {
|
|
57
|
-
return new Promise((resolve, reject) => {
|
|
58
|
-
let db;
|
|
59
|
-
try { db = _getDbOrThrow(); } catch (e) { reject(e); return; }
|
|
60
|
-
|
|
87
|
+
return _withDbRetry((db) => new Promise((resolve, reject) => {
|
|
61
88
|
let modelData;
|
|
62
89
|
try { modelData = JSON.parse(modelDataStr); }
|
|
63
90
|
catch (e) { reject(new Error("saveModel: modelDataStr is not valid JSON")); return; }
|
|
@@ -74,59 +101,34 @@ export async function saveModel(modelDataStr, storeName) {
|
|
|
74
101
|
|
|
75
102
|
req.onerror = () => reject(req.error);
|
|
76
103
|
req.onsuccess = () => resolve();
|
|
77
|
-
});
|
|
104
|
+
}));
|
|
78
105
|
}
|
|
79
106
|
|
|
80
107
|
// Cargar modelo
|
|
81
108
|
export function loadModel(modelId, storeName) {
|
|
82
|
-
return new Promise((resolve, reject) => {
|
|
83
|
-
let db;
|
|
84
|
-
try { db = _getDbOrThrow(); } catch (e) { reject(e); return; }
|
|
85
|
-
|
|
109
|
+
return _withDbRetry((db) => new Promise((resolve, reject) => {
|
|
86
110
|
const tx = db.transaction([storeName], "readonly");
|
|
87
111
|
const store = tx.objectStore(storeName);
|
|
88
112
|
const req = store.get(modelId);
|
|
89
113
|
|
|
90
114
|
req.onerror = () => reject(req.error);
|
|
91
115
|
req.onsuccess = () => resolve(req.result ?? null);
|
|
92
|
-
});
|
|
116
|
+
}));
|
|
93
117
|
}
|
|
94
118
|
|
|
95
119
|
// Descargar archivo desde base64
|
|
96
120
|
export function downloadFileFromBytes(fileName, bytesBase64, mimeType) {
|
|
97
|
-
let objectURL = null;
|
|
98
121
|
const link = document.createElement("a");
|
|
99
122
|
link.download = fileName;
|
|
100
|
-
|
|
101
|
-
const decoded = atob(bytesBase64);
|
|
102
|
-
const bytes = new Uint8Array(decoded.length);
|
|
103
|
-
for (let i = 0; i < decoded.length; i++) {
|
|
104
|
-
bytes[i] = decoded.charCodeAt(i);
|
|
105
|
-
}
|
|
106
|
-
const blob = new Blob([bytes], { type: mimeType || "application/octet-stream" });
|
|
107
|
-
objectURL = URL.createObjectURL(blob);
|
|
108
|
-
link.href = objectURL;
|
|
109
|
-
} catch {
|
|
110
|
-
// Fallback if base64 decoding fails for any reason.
|
|
111
|
-
link.href = `data:${mimeType};base64,${bytesBase64}`;
|
|
112
|
-
}
|
|
123
|
+
link.href = `data:${mimeType};base64,${bytesBase64}`;
|
|
113
124
|
document.body.appendChild(link);
|
|
114
125
|
link.click();
|
|
115
|
-
link.href = "";
|
|
116
126
|
link.remove();
|
|
117
|
-
if (objectURL) {
|
|
118
|
-
setTimeout(() => {
|
|
119
|
-
URL.revokeObjectURL(objectURL);
|
|
120
|
-
}, 0);
|
|
121
|
-
}
|
|
122
127
|
}
|
|
123
128
|
|
|
124
129
|
// Obtener todos los modelos (solo metadata)
|
|
125
130
|
export async function getAllModels(storeName) {
|
|
126
|
-
return new Promise((resolve, reject) => {
|
|
127
|
-
let db;
|
|
128
|
-
try { db = _getDbOrThrow(); } catch (e) { reject(e); return; }
|
|
129
|
-
|
|
131
|
+
return _withDbRetry((db) => new Promise((resolve, reject) => {
|
|
130
132
|
const tx = db.transaction([storeName], "readonly");
|
|
131
133
|
const store = tx.objectStore(storeName);
|
|
132
134
|
const req = store.getAll();
|
|
@@ -143,45 +145,36 @@ export async function getAllModels(storeName) {
|
|
|
143
145
|
// keep old behavior: return JSON string
|
|
144
146
|
resolve(JSON.stringify(results));
|
|
145
147
|
};
|
|
146
|
-
});
|
|
148
|
+
}));
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
// Eliminar modelo por id
|
|
150
152
|
export async function deleteModel(modelId, storeName) {
|
|
151
|
-
return new Promise((resolve, reject) => {
|
|
152
|
-
let db;
|
|
153
|
-
try { db = _getDbOrThrow(); } catch (e) { reject(e); return; }
|
|
154
|
-
|
|
153
|
+
return _withDbRetry((db) => new Promise((resolve, reject) => {
|
|
155
154
|
const tx = db.transaction([storeName], "readwrite");
|
|
156
155
|
const store = tx.objectStore(storeName);
|
|
157
156
|
const req = store.delete(modelId);
|
|
158
157
|
|
|
159
158
|
req.onerror = () => reject(req.error);
|
|
160
159
|
req.onsuccess = () => resolve();
|
|
161
|
-
});
|
|
160
|
+
}));
|
|
162
161
|
}
|
|
163
162
|
|
|
164
163
|
// Limpiar toda la store
|
|
165
164
|
export async function clearAll(storeName) {
|
|
166
|
-
return new Promise((resolve, reject) => {
|
|
167
|
-
let db;
|
|
168
|
-
try { db = _getDbOrThrow(); } catch (e) { reject(e); return; }
|
|
169
|
-
|
|
165
|
+
return _withDbRetry((db) => new Promise((resolve, reject) => {
|
|
170
166
|
const tx = db.transaction([storeName], "readwrite");
|
|
171
167
|
const store = tx.objectStore(storeName);
|
|
172
168
|
const req = store.clear();
|
|
173
169
|
|
|
174
170
|
req.onerror = () => reject(req.error);
|
|
175
171
|
req.onsuccess = () => resolve();
|
|
176
|
-
});
|
|
172
|
+
}));
|
|
177
173
|
}
|
|
178
174
|
|
|
179
175
|
// Borrar modelos expirados usando el índice "expirationTimeStamp"
|
|
180
176
|
export async function cleanExpiredModels(storeName) {
|
|
181
|
-
return new Promise((resolve, reject) => {
|
|
182
|
-
let db;
|
|
183
|
-
try { db = _getDbOrThrow(); } catch (e) { reject(e); return; }
|
|
184
|
-
|
|
177
|
+
return _withDbRetry((db) => new Promise((resolve, reject) => {
|
|
185
178
|
const tx = db.transaction([storeName], "readwrite");
|
|
186
179
|
const store = tx.objectStore(storeName);
|
|
187
180
|
|
|
@@ -201,7 +194,7 @@ export async function cleanExpiredModels(storeName) {
|
|
|
201
194
|
|
|
202
195
|
tx.oncomplete = () => resolve();
|
|
203
196
|
tx.onerror = () => reject(tx.error);
|
|
204
|
-
});
|
|
197
|
+
}));
|
|
205
198
|
}
|
|
206
199
|
|
|
207
200
|
// Utilidades opcionales y visibles (por si las quieres usar en consola)
|
package/src/pref-viewer-3d.js
CHANGED
|
@@ -75,6 +75,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
75
75
|
#isInitialized = false;
|
|
76
76
|
#isLoaded = false;
|
|
77
77
|
#isVisible = false;
|
|
78
|
+
#lastLoadTimestamp = undefined;
|
|
78
79
|
|
|
79
80
|
#wrapper = null;
|
|
80
81
|
#canvas = null;
|
|
@@ -230,6 +231,27 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
230
231
|
this.#babylonJSController.enable();
|
|
231
232
|
}
|
|
232
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Determines whether the next 3D load should bypass cache checks.
|
|
236
|
+
* @private
|
|
237
|
+
* @param {object} config - Incoming config payload.
|
|
238
|
+
* @returns {boolean} True when force reload should be enabled.
|
|
239
|
+
*/
|
|
240
|
+
#shouldForceReload(config) {
|
|
241
|
+
if (!config || typeof config !== "object") {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
if (config.forceReload === true) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
if (config.timestamp === undefined || config.timestamp === null) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
const changed = config.timestamp !== this.#lastLoadTimestamp;
|
|
251
|
+
this.#lastLoadTimestamp = config.timestamp;
|
|
252
|
+
return changed;
|
|
253
|
+
}
|
|
254
|
+
|
|
233
255
|
/**
|
|
234
256
|
* Resets update flags for all containers and material/camera options after loading or setting options.
|
|
235
257
|
* @private
|
|
@@ -558,6 +580,7 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
558
580
|
}
|
|
559
581
|
|
|
560
582
|
this.#onLoading();
|
|
583
|
+
const forceReload = this.#shouldForceReload(config);
|
|
561
584
|
|
|
562
585
|
// Containers
|
|
563
586
|
this.#checkNeedToUpdateContainers(config);
|
|
@@ -566,10 +589,10 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
566
589
|
if (config.options) {
|
|
567
590
|
this.#checkNeedToUpdateCamera(config.options);
|
|
568
591
|
this.#checkNeedToUpdateMaterials(config.options);
|
|
569
|
-
this.#checkNeedToUpdateIBL(config.options);
|
|
592
|
+
await this.#checkNeedToUpdateIBL(config.options);
|
|
570
593
|
}
|
|
571
594
|
|
|
572
|
-
const loadDetail = await this.#babylonJSController.load();
|
|
595
|
+
const loadDetail = await this.#babylonJSController.load(forceReload);
|
|
573
596
|
|
|
574
597
|
return { ...loadDetail, load: this.#onLoaded() };
|
|
575
598
|
}
|
|
@@ -579,9 +602,9 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
579
602
|
* Updates internal states, triggers option setting events, and returns the result.
|
|
580
603
|
* @public
|
|
581
604
|
* @param {object} options - Options object containing camera and material settings.
|
|
582
|
-
* @returns {object} Object containing success status and details of set options.
|
|
605
|
+
* @returns {Promise<object>} Object containing success status and details of set options.
|
|
583
606
|
*/
|
|
584
|
-
setOptions(options) {
|
|
607
|
+
async setOptions(options) {
|
|
585
608
|
if (!this.#babylonJSController) {
|
|
586
609
|
return;
|
|
587
610
|
}
|
|
@@ -595,8 +618,9 @@ export default class PrefViewer3D extends HTMLElement {
|
|
|
595
618
|
if (this.#checkNeedToUpdateMaterials(options)) {
|
|
596
619
|
someSetted = someSetted || this.#babylonJSController.setMaterialOptions();
|
|
597
620
|
}
|
|
598
|
-
|
|
599
|
-
|
|
621
|
+
const needUpdateIBL = await this.#checkNeedToUpdateIBL(options);
|
|
622
|
+
if (needUpdateIBL) {
|
|
623
|
+
someSetted = someSetted || (await this.#babylonJSController.setIBLOptions());
|
|
600
624
|
}
|
|
601
625
|
const detail = this.#onSetOptions();
|
|
602
626
|
return { success: someSetted, detail: detail };
|
package/src/pref-viewer.js
CHANGED
|
@@ -81,6 +81,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
81
81
|
onViewerHoverStart: null,
|
|
82
82
|
onViewerHoverEnd: null,
|
|
83
83
|
on3DSceneLoaded: null,
|
|
84
|
+
on3DSceneError: null,
|
|
84
85
|
};
|
|
85
86
|
|
|
86
87
|
/**
|
|
@@ -136,6 +137,9 @@ export default class PrefViewer extends HTMLElement {
|
|
|
136
137
|
this.loadMaterials(value);
|
|
137
138
|
break;
|
|
138
139
|
case "mode":
|
|
140
|
+
if (typeof value !== "string") {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
139
143
|
if (_old === value || value.toLowerCase() === this.#mode) {
|
|
140
144
|
return;
|
|
141
145
|
}
|
|
@@ -151,6 +155,9 @@ export default class PrefViewer extends HTMLElement {
|
|
|
151
155
|
this.setOptions(value);
|
|
152
156
|
break;
|
|
153
157
|
case "show-model":
|
|
158
|
+
if (typeof value !== "string") {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
154
161
|
if (_old === value) {
|
|
155
162
|
return;
|
|
156
163
|
}
|
|
@@ -158,6 +165,9 @@ export default class PrefViewer extends HTMLElement {
|
|
|
158
165
|
showModel ? this.showModel() : this.hideModel();
|
|
159
166
|
break;
|
|
160
167
|
case "show-scene":
|
|
168
|
+
if (typeof value !== "string") {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
161
171
|
if (_old === value) {
|
|
162
172
|
return;
|
|
163
173
|
}
|
|
@@ -213,6 +223,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
213
223
|
}
|
|
214
224
|
if (this.#component3D) {
|
|
215
225
|
this.#component3D.removeEventListener("scene-loaded", this.#handlers.on3DSceneLoaded);
|
|
226
|
+
this.#component3D.removeEventListener("scene-error", this.#handlers.on3DSceneError);
|
|
216
227
|
this.#component3D.remove();
|
|
217
228
|
}
|
|
218
229
|
if (this.#menu3D) {
|
|
@@ -249,7 +260,11 @@ export default class PrefViewer extends HTMLElement {
|
|
|
249
260
|
this.#menu3DSyncSettings();
|
|
250
261
|
this.#menu3D?.setApplying(false);
|
|
251
262
|
};
|
|
263
|
+
this.#handlers.on3DSceneError = () => {
|
|
264
|
+
this.#menu3D?.setApplying(false, true);
|
|
265
|
+
};
|
|
252
266
|
this.#component3D.addEventListener("scene-loaded", this.#handlers.on3DSceneLoaded);
|
|
267
|
+
this.#component3D.addEventListener("scene-error", this.#handlers.on3DSceneError);
|
|
253
268
|
this.#wrapper.appendChild(this.#component3D);
|
|
254
269
|
}
|
|
255
270
|
|
|
@@ -454,7 +469,14 @@ export default class PrefViewer extends HTMLElement {
|
|
|
454
469
|
* @returns {void}
|
|
455
470
|
*/
|
|
456
471
|
#addTaskToQueue(value, type) {
|
|
457
|
-
|
|
472
|
+
const task = new PrefViewerTask(value, type);
|
|
473
|
+
if (
|
|
474
|
+
task.type === PrefViewerTask.Types.Options ||
|
|
475
|
+
task.type === PrefViewerTask.Types.Drawing
|
|
476
|
+
) {
|
|
477
|
+
this.#taskQueue = this.#taskQueue.filter((queuedTask) => queuedTask.type !== task.type);
|
|
478
|
+
}
|
|
479
|
+
this.#taskQueue.push(task);
|
|
458
480
|
if (this.#isInitialized && !this.#isLoading) {
|
|
459
481
|
this.#processNextTask();
|
|
460
482
|
}
|
|
@@ -552,6 +574,32 @@ export default class PrefViewer extends HTMLElement {
|
|
|
552
574
|
this.dispatchEvent(new CustomEvent("scene-loaded", customEventOptions));
|
|
553
575
|
}
|
|
554
576
|
|
|
577
|
+
/**
|
|
578
|
+
* Handles 3D load errors.
|
|
579
|
+
* Clears loading state and dispatches a "scene-error" event.
|
|
580
|
+
* @private
|
|
581
|
+
* @param {object} [detail] - Optional details about the failure.
|
|
582
|
+
* @returns {void}
|
|
583
|
+
*/
|
|
584
|
+
#on3DError(detail) {
|
|
585
|
+
this.#isLoaded = false;
|
|
586
|
+
this.#isLoading = false;
|
|
587
|
+
this.#menu3D?.setApplying(false, true);
|
|
588
|
+
|
|
589
|
+
this.removeAttribute("loading-3d");
|
|
590
|
+
this.removeAttribute("loaded-3d");
|
|
591
|
+
|
|
592
|
+
const customEventOptions = {
|
|
593
|
+
bubbles: true,
|
|
594
|
+
cancelable: true,
|
|
595
|
+
composed: true,
|
|
596
|
+
};
|
|
597
|
+
if (detail) {
|
|
598
|
+
customEventOptions.detail = detail;
|
|
599
|
+
}
|
|
600
|
+
this.dispatchEvent(new CustomEvent("scene-error", customEventOptions));
|
|
601
|
+
}
|
|
602
|
+
|
|
555
603
|
/**
|
|
556
604
|
* Handles the start of a 2D loading operation.
|
|
557
605
|
* Updates loading state, sets attributes, and dispatches a "drawing-loading" event.
|
|
@@ -598,6 +646,31 @@ export default class PrefViewer extends HTMLElement {
|
|
|
598
646
|
this.dispatchEvent(new CustomEvent("drawing-loaded", customEventOptions));
|
|
599
647
|
}
|
|
600
648
|
|
|
649
|
+
/**
|
|
650
|
+
* Handles 2D load errors.
|
|
651
|
+
* Clears loading state and dispatches a "drawing-error" event.
|
|
652
|
+
* @private
|
|
653
|
+
* @param {object} [detail] - Optional details about the failure.
|
|
654
|
+
* @returns {void}
|
|
655
|
+
*/
|
|
656
|
+
#on2DError(detail) {
|
|
657
|
+
this.#isLoaded = false;
|
|
658
|
+
this.#isLoading = false;
|
|
659
|
+
|
|
660
|
+
this.removeAttribute("loading-2d");
|
|
661
|
+
this.removeAttribute("loaded-2d");
|
|
662
|
+
|
|
663
|
+
const customEventOptions = {
|
|
664
|
+
bubbles: true,
|
|
665
|
+
cancelable: true,
|
|
666
|
+
composed: true,
|
|
667
|
+
};
|
|
668
|
+
if (detail) {
|
|
669
|
+
customEventOptions.detail = detail;
|
|
670
|
+
}
|
|
671
|
+
this.dispatchEvent(new CustomEvent("drawing-error", customEventOptions));
|
|
672
|
+
}
|
|
673
|
+
|
|
601
674
|
/**
|
|
602
675
|
* Handles the "drawing-zoom-changed" event from the 2D viewer component.
|
|
603
676
|
* Dispatches a custom "drawing-zoom-changed" event from the PrefViewer element, forwarding the event detail to external listeners.
|
|
@@ -640,16 +713,27 @@ export default class PrefViewer extends HTMLElement {
|
|
|
640
713
|
* @param {object} config - The configuration object to process.
|
|
641
714
|
* @returns {void}
|
|
642
715
|
*/
|
|
643
|
-
#processConfig(config) {
|
|
716
|
+
async #processConfig(config) {
|
|
644
717
|
if (!this.#component3D) {
|
|
718
|
+
this.#processNextTask();
|
|
645
719
|
return;
|
|
646
720
|
}
|
|
647
721
|
|
|
648
722
|
this.#on3DLoading();
|
|
649
|
-
|
|
650
|
-
this.#
|
|
723
|
+
try {
|
|
724
|
+
const detail = await this.#component3D.load(config);
|
|
725
|
+
if (detail?.success === false) {
|
|
726
|
+
this.#on3DError(detail);
|
|
727
|
+
} else {
|
|
728
|
+
this.#on3DLoaded(detail);
|
|
729
|
+
}
|
|
730
|
+
} catch (error) {
|
|
731
|
+
this.#on3DError({
|
|
732
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
733
|
+
});
|
|
734
|
+
} finally {
|
|
651
735
|
this.#processNextTask();
|
|
652
|
-
}
|
|
736
|
+
}
|
|
653
737
|
}
|
|
654
738
|
|
|
655
739
|
/**
|
|
@@ -670,16 +754,23 @@ export default class PrefViewer extends HTMLElement {
|
|
|
670
754
|
* @param {object} drawing - The drawing object to process.
|
|
671
755
|
* @returns {void}
|
|
672
756
|
*/
|
|
673
|
-
#processDrawing(drawing) {
|
|
757
|
+
async #processDrawing(drawing) {
|
|
674
758
|
if (!this.#component2D) {
|
|
759
|
+
this.#processNextTask();
|
|
675
760
|
return;
|
|
676
761
|
}
|
|
677
762
|
|
|
678
763
|
this.#on2DLoading();
|
|
679
|
-
|
|
764
|
+
try {
|
|
765
|
+
const detail = await this.#component2D.load(drawing);
|
|
680
766
|
this.#on2DLoaded(detail);
|
|
767
|
+
} catch (error) {
|
|
768
|
+
this.#on2DError({
|
|
769
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
770
|
+
});
|
|
771
|
+
} finally {
|
|
681
772
|
this.#processNextTask();
|
|
682
|
-
}
|
|
773
|
+
}
|
|
683
774
|
}
|
|
684
775
|
|
|
685
776
|
/**
|
|
@@ -725,15 +816,23 @@ export default class PrefViewer extends HTMLElement {
|
|
|
725
816
|
* @param {object} options - The options object to process.
|
|
726
817
|
* @returns {void}
|
|
727
818
|
*/
|
|
728
|
-
#processOptions(options) {
|
|
819
|
+
async #processOptions(options) {
|
|
729
820
|
if (!this.#component3D) {
|
|
821
|
+
this.#processNextTask();
|
|
730
822
|
return;
|
|
731
823
|
}
|
|
732
824
|
|
|
733
825
|
this.#on3DLoading();
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
826
|
+
try {
|
|
827
|
+
const detail = await this.#component3D.setOptions(options);
|
|
828
|
+
this.#on3DLoaded(detail);
|
|
829
|
+
} catch (error) {
|
|
830
|
+
this.#on3DError({
|
|
831
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
832
|
+
});
|
|
833
|
+
} finally {
|
|
834
|
+
this.#processNextTask();
|
|
835
|
+
}
|
|
737
836
|
}
|
|
738
837
|
|
|
739
838
|
/**
|
|
@@ -745,6 +844,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
745
844
|
*/
|
|
746
845
|
#processVisibility(config) {
|
|
747
846
|
if (!this.#component3D) {
|
|
847
|
+
this.#processNextTask();
|
|
748
848
|
return;
|
|
749
849
|
}
|
|
750
850
|
const showModel = config.model?.visible;
|
|
@@ -974,7 +1074,7 @@ export default class PrefViewer extends HTMLElement {
|
|
|
974
1074
|
* @returns {void}
|
|
975
1075
|
*/
|
|
976
1076
|
setMode(mode = this.#mode) {
|
|
977
|
-
mode = mode.toLowerCase();
|
|
1077
|
+
mode = typeof mode === "string" ? mode.toLowerCase() : this.#mode;
|
|
978
1078
|
if (mode !== "2d" && mode !== "3d") {
|
|
979
1079
|
console.warn(`PrefViewer: invalid mode "${mode}". Allowed modes are "2d" and "3d".`);
|
|
980
1080
|
mode = this.#mode;
|