@preference-sl/pref-viewer 2.4.0 → 2.5.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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/index.js +31 -47
package/package.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
+ "version": "2.5.1",
3
4
  "description": "Web Component to preview GLTF models with Babylon.js",
4
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
5
6
  "scripts": {
@@ -35,6 +36,5 @@
35
36
  },
36
37
  "devDependencies": {
37
38
  "standard-version": "^9.5.0"
38
- },
39
- "version": "2.4.0"
39
+ }
40
40
  }
package/src/index.js CHANGED
@@ -26,10 +26,10 @@
26
26
  * 3. **Listen for loading events (optional)**
27
27
  * const viewer = document.querySelector("pref-viewer");
28
28
  * viewer.addEventListener("model-loaded", (evt) => {
29
- * console.log("Loaded meshes:", evt.detail.meshes);
29
+ * xx
30
30
  * });
31
31
  * viewer.addEventListener("model-error", (evt) => {
32
- * console.error("Failed to load model:", evt.detail.error);
32
+ * xx
33
33
  * });
34
34
  *
35
35
  * 4. **Change models at runtime**
@@ -39,7 +39,6 @@
39
39
  * Implementation code below with updated preprocessUrl
40
40
  * -----------------------------------------------------------------------------
41
41
  */
42
-
43
42
  import {
44
43
  Engine,
45
44
  Scene,
@@ -59,7 +58,7 @@ class PrefViewer extends HTMLElement {
59
58
  this._createCanvas();
60
59
  this._wrapCanvas();
61
60
 
62
- // These will be set in _initializeBabylon()
61
+ // Babylon.js core objects
63
62
  this.engine = null;
64
63
  this.scene = null;
65
64
  this.camera = null;
@@ -67,10 +66,10 @@ class PrefViewer extends HTMLElement {
67
66
  this.dirLight = null;
68
67
  this._onWindowResize = null;
69
68
 
70
- // modelUrl might be provided via attribute before connectedCallback
69
+ // modelUrl will be provided via attribute
71
70
  this.modelUrl = null;
72
71
  this._hasInitialized = false;
73
- this._pluginHookSetup = false; // Track if we've already set up the plugin hook
72
+ this._pluginHookSetup = false;
74
73
  }
75
74
 
76
75
  static get observedAttributes() {
@@ -78,9 +77,8 @@ class PrefViewer extends HTMLElement {
78
77
  }
79
78
 
80
79
  attributeChangedCallback(name, _oldValue, newValue) {
81
- if (name === "model" && newValue) {
80
+ if (name === "model") {
82
81
  this.modelUrl = newValue;
83
- // Only reload if initialization has already happened
84
82
  if (this._hasInitialized) {
85
83
  this._reloadModel();
86
84
  }
@@ -88,27 +86,34 @@ class PrefViewer extends HTMLElement {
88
86
  }
89
87
 
90
88
  connectedCallback() {
91
-
92
- // Set up the plugin hook ONCE globally before any other initialization
89
+ // Set up URL preprocessing once
93
90
  if (!this._pluginHookSetup) {
94
91
  this._setupUrlPreprocessing();
95
92
  this._pluginHookSetup = true;
96
93
  }
97
94
 
98
- // 1) Determine modelUrl now that element is connected
95
+ // Require a model attribute; fail early if missing
99
96
  if (!this.hasAttribute("model")) {
100
- this.modelUrl = new URL("./models/window.gltf", import.meta.url).href;
101
- } else {
102
- this.modelUrl = this.getAttribute("model");
97
+ const errorMsg = 'PrefViewer: No "model" attribute provided.';
98
+ console.error(errorMsg);
99
+ this.dispatchEvent(
100
+ new CustomEvent("model-error", {
101
+ detail: { error: new Error(errorMsg) },
102
+ bubbles: true,
103
+ composed: true
104
+ })
105
+ );
106
+ return;
103
107
  }
104
108
 
105
- // 2) Initialize Babylon (engine + scene + camera + lights + hooks)
106
- this._initializeBabylon();
109
+ // Initialize modelUrl from attribute
110
+ this.modelUrl = this.getAttribute("model");
107
111
 
108
- // 3) Mark that initialization is done
112
+ // Initialize Babylon (engine + scene + camera + lights + hooks)
113
+ this._initializeBabylon();
109
114
  this._hasInitialized = true;
110
115
 
111
- // 4) Load whatever modelUrl we have
116
+ // Load the specified model
112
117
  this._reloadModel();
113
118
  }
114
119
 
@@ -119,38 +124,29 @@ class PrefViewer extends HTMLElement {
119
124
  }
120
125
  }
121
126
 
122
- // ====== URL Preprocessing Setup ======
127
+ // ====== URL Preprocessing ======
123
128
  _setupUrlPreprocessing() {
124
-
125
129
  const transformUrl = (url) => {
126
- // a) If the loader already prepended "blob:…", strip it out.
127
130
  const stripped = url.replace(
128
131
  /^blob:(?:http|https|file):\/\/[^\/]+\/(.+)/i,
129
132
  "$1"
130
133
  );
131
- // b) Normalize backslashes "\" → forward slashes "/"
132
134
  const fixedSlashes = stripped.replace(/\\/g, "/");
133
-
134
- // c) If it now starts with "http://" or "https://", return it as an absolute URL:
135
135
  if (/^https?:\/\//i.test(fixedSlashes)) {
136
136
  return fixedSlashes;
137
137
  }
138
- // d) Otherwise, return the relative path
139
138
  return fixedSlashes;
140
139
  };
141
140
 
142
141
  SceneLoader.OnPluginActivatedObservable.add((plugin) => {
143
142
  if (plugin.name === "gltf" || plugin.name === "gltf2") {
144
- // Support both sync and async preprocessing
145
143
  plugin.preprocessUrl = transformUrl;
146
- if (typeof plugin.preprocessUrlAsync === 'function' || plugin.preprocessUrlAsync === undefined) {
147
- plugin.preprocessUrlAsync = (url) => Promise.resolve(transformUrl(url));
148
- }
144
+ plugin.preprocessUrlAsync = (url) => Promise.resolve(transformUrl(url));
149
145
  }
150
146
  });
151
147
  }
152
148
 
153
- // ====== Private setup methods ======
149
+ // ====== Setup Helpers ======
154
150
  _createCanvas() {
155
151
  this.canvas = document.createElement("canvas");
156
152
  Object.assign(this.canvas.style, {
@@ -172,26 +168,20 @@ class PrefViewer extends HTMLElement {
172
168
  }
173
169
 
174
170
  _initializeBabylon() {
175
-
176
- // 1) Create the Babylon engine & scene
177
171
  this.engine = new Engine(this.canvas, true, { alpha: true });
178
172
  this.scene = new Scene(this.engine);
179
173
  this.scene.clearColor = new Color4(1, 1, 1, 1);
180
174
 
181
- // 2) Create camera and lights
182
175
  this._createCamera();
183
176
  this._createLights();
184
-
185
- // 3) Hook up input/event handlers (e.g. wheel-to-zoom)
186
177
  this._setupEventListeners();
187
178
 
188
- // 4) Start Babylon's render loop
189
179
  this.engine.runRenderLoop(() => {
190
180
  if (this.scene) {
191
181
  this.scene.render();
192
182
  }
193
183
  });
194
-
184
+
195
185
  this._onWindowResize = () => {
196
186
  if (this.engine) {
197
187
  this.engine.resize();
@@ -201,7 +191,6 @@ class PrefViewer extends HTMLElement {
201
191
  }
202
192
 
203
193
  _createCamera() {
204
- // ArcRotateCamera that orbits around origin
205
194
  this.camera = new ArcRotateCamera(
206
195
  "camera",
207
196
  Math.PI / 2,
@@ -231,7 +220,6 @@ class PrefViewer extends HTMLElement {
231
220
  }
232
221
 
233
222
  _setupEventListeners() {
234
- // Zoom toward point-of-interest on wheel scroll
235
223
  this.canvas.addEventListener("wheel", (evt) => {
236
224
  if (!this.scene || !this.camera) return;
237
225
  const pickResult = this.scene.pick(
@@ -248,14 +236,13 @@ class PrefViewer extends HTMLElement {
248
236
  });
249
237
  }
250
238
 
251
- // ====== Model loading / management ======
239
+ // ====== Model Management ======
252
240
  async _reloadModel() {
253
241
  if (!this.scene || !this.modelUrl) {
254
242
  console.warn("PrefViewer: _reloadModel aborted (scene or modelUrl missing)");
255
243
  return;
256
244
  }
257
245
 
258
- // Dispose previous meshes so we don't accumulate them
259
246
  this._disposePreviousMeshes();
260
247
 
261
248
  try {
@@ -267,14 +254,11 @@ class PrefViewer extends HTMLElement {
267
254
  undefined,
268
255
  ".gltf"
269
256
  );
270
- this.scene.createDefaultCameraOrLight(true, true, true);
271
257
 
258
+ this.scene.createDefaultCameraOrLight(true, true, true);
272
259
  this.dispatchEvent(
273
260
  new CustomEvent("model-loaded", {
274
- detail: {
275
- meshes: result.meshes,
276
- particleSystems: result.particleSystems
277
- },
261
+ detail: { meshes: result.meshes, particleSystems: result.particleSystems },
278
262
  bubbles: true,
279
263
  composed: true
280
264
  })
@@ -313,4 +297,4 @@ class PrefViewer extends HTMLElement {
313
297
  }
314
298
  }
315
299
 
316
- customElements.define("pref-viewer", PrefViewer);
300
+ customElements.define("pref-viewer", PrefViewer);