@preference-sl/pref-viewer 2.16.0 → 2.16.2-beta.0

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/index.d.ts CHANGED
@@ -1,24 +1,385 @@
1
1
  import type React from "react";
2
2
 
3
+ /** Render mode. `loadConfig`/`loadDrawing` pick the matching one on their own. */
4
+ export type PrefViewerMode = "2d" | "3d";
5
+
6
+ /** Animation state as reported by `getAnimations()` and `prefviewer-animation-changed`. */
7
+ export type PrefViewerAnimationState = 0 | 1 | 2 | 3 | 4;
8
+
9
+ /** Action accepted by `playAnimation`. */
10
+ export type PrefViewerAnimationAction = "open" | "close" | "pause" | "goToOpened" | "goToClosed";
11
+
12
+ /**
13
+ * An IndexedDB pointer. The SDK writes the asset and hands you the record id;
14
+ * the viewer reads it back itself, so you never fetch or decode the bytes.
15
+ */
16
+ export interface PrefViewerStoragePointer {
17
+ db: string;
18
+ table: string;
19
+ id: string;
20
+ }
21
+
22
+ /** A storage pointer or a plain URL — every asset slot accepts either. */
23
+ export type PrefViewerSource = PrefViewerStoragePointer | { url: string };
24
+
25
+ export interface PrefViewerAsset {
26
+ storage: PrefViewerSource;
27
+ visible?: boolean;
28
+ }
29
+
30
+ /** Payload for `loadConfig`. Pass the object; a JSON string is also accepted. */
31
+ export interface PrefViewerConfig {
32
+ model?: PrefViewerAsset;
33
+ scene?: PrefViewerAsset;
34
+ materials?: PrefViewerAsset;
35
+ drawing?: PrefViewerAsset;
36
+ options?: Record<string, unknown>;
37
+ }
38
+
39
+ export interface PrefViewerAnimation {
40
+ name: string;
41
+ state: PrefViewerAnimationState;
42
+ progress: number;
43
+ loop: boolean;
44
+ nodes: string[];
45
+ }
46
+
47
+ /**
48
+ * `<pref-viewer>` shadows the standard `Element.getAnimations()` with its own snapshot of the
49
+ * model's opening animations. The two signatures are incompatible, so the method is not declared
50
+ * on `PrefViewer` — reach it through this view:
51
+ *
52
+ * ```ts
53
+ * const animations = (viewer as unknown as PrefViewerAnimationsApi).getAnimations()
54
+ * ```
55
+ */
56
+ export interface PrefViewerAnimationsApi {
57
+ getAnimations(): PrefViewerAnimation[];
58
+ }
59
+
60
+ // ── Semantic selection and turnable state ──
61
+
62
+ export interface PrefViewerSelectionItem {
63
+ id: string;
64
+ highlights: string[];
65
+ }
66
+
67
+ export interface PrefViewerSelectionDescriptor {
68
+ id?: string;
69
+ Id?: string;
70
+ highlights?: string[];
71
+ Highlights?: string[];
72
+ }
73
+
74
+ export interface PrefViewerSelectionState {
75
+ selectableIds: string[];
76
+ selectedIds: string[];
77
+ selectedItems: PrefViewerSelectionItem[];
78
+ }
79
+
80
+ export type PrefViewerElementDescriptor = string | { id?: string; Id?: string };
81
+
82
+ export interface PrefViewerSelectionInput {
83
+ selectableIds?: PrefViewerElementDescriptor[];
84
+ selectables?: PrefViewerElementDescriptor[];
85
+ selectedItems?: Array<string | PrefViewerSelectionDescriptor>;
86
+ selectedIds?: PrefViewerElementDescriptor[];
87
+ selected?: Array<string | PrefViewerSelectionDescriptor>;
88
+ }
89
+
90
+ export interface PrefViewerElementClickDetail {
91
+ ids: string[];
92
+ }
93
+
94
+ /** Optional speed/direction for `startTurnable` / `toggleTurnable`. */
95
+ export interface PrefViewerTurnableOptions {
96
+ speed?: number;
97
+ direction?: string;
98
+ }
99
+
100
+ export interface PrefViewerTurnableState {
101
+ running: boolean;
102
+ speed: number;
103
+ direction: string;
104
+ }
105
+
106
+ // ── Event details ──
107
+
108
+ /** Which containers a load touched, and which of them succeeded. */
109
+ export interface PrefViewerContainerFlags {
110
+ model?: boolean;
111
+ scene?: boolean;
112
+ materials?: boolean;
113
+ }
114
+
115
+ export interface PrefViewerSceneLoadedDetail {
116
+ tried: PrefViewerContainerFlags;
117
+ success: PrefViewerContainerFlags;
118
+ }
119
+
120
+ export interface PrefViewerSceneOptionsDetail {
121
+ success: boolean;
122
+ detail: PrefViewerSceneLoadedDetail;
123
+ }
124
+
125
+ export interface PrefViewerDrawingLoadedDetail {
126
+ success: boolean;
127
+ error: Error | null;
128
+ }
129
+
130
+ export interface PrefViewerErrorDetail {
131
+ error: Error;
132
+ }
133
+
134
+ export interface PrefViewerAnimationChangedDetail {
135
+ name: string;
136
+ state: PrefViewerAnimationState;
137
+ progress: number;
138
+ loop: boolean;
139
+ }
140
+
141
+ export interface PrefViewerInitializedDetail {
142
+ isInitialized: boolean;
143
+ }
144
+
145
+ /** The 2D pan/zoom viewport, as reported after every pan or zoom. */
146
+ export interface PrefViewerViewportChangedDetail {
147
+ x: number;
148
+ y: number;
149
+ scale: number;
150
+ moved: boolean;
151
+ scaled: boolean;
152
+ maximized: boolean;
153
+ minimized: boolean;
154
+ }
155
+
156
+ /**
157
+ * Root-scoped events (`pref-viewer-loading` / `-loaded` / `-error`) fire for both modes and
158
+ * carry the mode they happened in; the 2D- and 3D-scoped events do not.
159
+ */
160
+ export interface PrefViewerRootLoadingDetail {
161
+ mode: PrefViewerMode;
162
+ }
163
+
164
+ export interface PrefViewerRootLoadedDetail {
165
+ mode: PrefViewerMode;
166
+ result: PrefViewerSceneLoadedDetail | PrefViewerDrawingLoadedDetail | null;
167
+ }
168
+
169
+ export interface PrefViewerRootErrorDetail {
170
+ mode: PrefViewerMode;
171
+ error: Error | null;
172
+ }
173
+
174
+ /**
175
+ * Events dispatched by `<pref-viewer>`. All bubble and are composed, so a listener
176
+ * on an ancestor outside the shadow root still receives them.
177
+ *
178
+ * The `scene-*` / `drawing-*` names are the legacy aliases: each is still emitted
179
+ * alongside its `pref-viewer-3d-*` / `pref-viewer-2d-*` equivalent, with the same detail.
180
+ */
181
+ export interface PrefViewerEventMap {
182
+ "pref-viewer-initialized": CustomEvent<PrefViewerInitializedDetail>;
183
+
184
+ // Root scope — either mode, and the detail says which.
185
+ "pref-viewer-loading": CustomEvent<PrefViewerRootLoadingDetail>;
186
+ "pref-viewer-loaded": CustomEvent<PrefViewerRootLoadedDetail>;
187
+ "pref-viewer-error": CustomEvent<PrefViewerRootErrorDetail>;
188
+
189
+ // 3D scope (and its legacy aliases).
190
+ "pref-viewer-3d-loading": CustomEvent<undefined>;
191
+ "pref-viewer-3d-loaded": CustomEvent<PrefViewerSceneLoadedDetail | Record<string, never>>;
192
+ "pref-viewer-3d-error": CustomEvent<PrefViewerErrorDetail | Record<string, never>>;
193
+ "pref-viewer-3d-turnable-changed": CustomEvent<{ state: PrefViewerTurnableState }>;
194
+ "scene-loading": CustomEvent<undefined>;
195
+ "scene-loaded": CustomEvent<PrefViewerSceneLoadedDetail | undefined>;
196
+ "scene-error": CustomEvent<PrefViewerErrorDetail>;
197
+
198
+ // 2D scope (and its legacy aliases).
199
+ "pref-viewer-2d-loading": CustomEvent<undefined>;
200
+ "pref-viewer-2d-loaded": CustomEvent<PrefViewerDrawingLoadedDetail | Record<string, never>>;
201
+ "pref-viewer-2d-error": CustomEvent<PrefViewerErrorDetail | Record<string, never>>;
202
+ "pref-viewer-2d-viewport-changed": CustomEvent<PrefViewerViewportChangedDetail>;
203
+ "pref-viewer-2d-element-click": CustomEvent<PrefViewerElementClickDetail>;
204
+ "drawing-loading": CustomEvent<undefined>;
205
+ "drawing-loaded": CustomEvent<PrefViewerDrawingLoadedDetail>;
206
+ "drawing-error": CustomEvent<PrefViewerErrorDetail>;
207
+ "drawing-zoom-changed": CustomEvent<{ zoom: number }>;
208
+
209
+ // Semantic selection.
210
+ "pref-viewer-element-click": CustomEvent<PrefViewerElementClickDetail>;
211
+ "pref-viewer-selection-state-changed": CustomEvent<PrefViewerSelectionState>;
212
+
213
+ /** Re-dispatched from the 3D component; not emitted by the root element. */
214
+ "prefviewer-animation-changed": CustomEvent<PrefViewerAnimationChangedDetail>;
215
+ }
216
+
3
217
  export declare class PrefViewer extends HTMLElement {
4
- /**
5
- * The URL or blob string of the GLTF model
6
- */
7
- set model(v: string | null);
218
+ // ── Property setters (each mirrors the attribute of the same name) ──
219
+ set config(v: PrefViewerConfig | string | null);
220
+ set culture(v: string | null);
221
+ set drawing(v: PrefViewerAsset | string | null);
222
+ set materials(v: PrefViewerAsset | string | null);
223
+ set mode(v: PrefViewerMode | null);
224
+ /** The URL or storage pointer of the glTF model. */
225
+ set model(v: PrefViewerAsset | string | null);
226
+ set options(v: Record<string, unknown> | string | null);
227
+ set scene(v: PrefViewerAsset | string | null);
228
+ /** Supported style overrides; currently `{ colorPrimary }`. */
229
+ set styles(v: Record<string, unknown> | string | null);
230
+
231
+ // ── Read-only state ──
232
+ get culture(): string;
233
+ get isInitialized(): boolean;
234
+ get isLoaded(): boolean;
235
+ get isLoading(): boolean;
236
+
237
+ // ── Loading ──
238
+ /** Returns false when the config is unusable; otherwise queues the work. */
239
+ loadConfig(config: PrefViewerConfig | string): boolean | void;
240
+ loadDrawing(drawing: PrefViewerAsset | string): boolean | void;
241
+ loadMaterials(materials: PrefViewerAsset | string): boolean | void;
242
+ loadModel(model: PrefViewerAsset | string): boolean | void;
243
+ loadScene(scene: PrefViewerAsset | string): boolean | void;
244
+
245
+ // ── Runtime ──
246
+ setCulture(cultureId: string): void;
247
+ setMode(mode?: PrefViewerMode): void;
248
+ /** Cheaper than a full `loadConfig` when only options changed — no visible reload. */
249
+ setOptions(options: Record<string, unknown> | string): void;
250
+ /** Convenience wrapper for `setOptions({ ibl })`. False when the payload is unusable. */
251
+ setIBL(ibl: Record<string, unknown> | string): boolean | void;
252
+ /** Runtime style overrides; currently `{ colorPrimary }`. False when unusable. */
253
+ setStyles(styles: Record<string, unknown> | string): boolean | void;
254
+ /** 3D runtime diagnostics, or null before the 3D component exists. */
255
+ getDebugInfo(): Record<string, unknown> | null;
256
+
257
+ // ── Visibility and framing ──
258
+ showModel(): void;
259
+ hideModel(): void;
260
+ showScene(): void;
261
+ hideScene(): void;
262
+ zoomCenter(): void;
263
+ zoomExtentsAll(): void;
264
+ zoomIn(): void;
265
+ zoomOut(): void;
266
+
267
+ // ── Semantic selection ──
268
+ setSelectableElements(elements?: PrefViewerElementDescriptor[]): PrefViewerSelectionState;
269
+ setSelectedElements(elements?: Array<string | PrefViewerSelectionDescriptor>): PrefViewerSelectionState;
270
+ applySelectionState(state?: PrefViewerSelectionInput): PrefViewerSelectionState;
271
+ clearSelectedElements(): PrefViewerSelectionState;
272
+ clearSelectableElements(): PrefViewerSelectionState;
273
+ clearSelectionState(): PrefViewerSelectionState;
274
+ getSelectionState(): PrefViewerSelectionState;
275
+ notifyElementClick(
276
+ element: PrefViewerElementDescriptor | PrefViewerElementDescriptor[] | { ids: PrefViewerElementDescriptor[] },
277
+ ): void;
278
+ appendSvgNode(id: string, svgId: string, svg: SVGElement): boolean;
279
+ removeAppendedSvgNodes(): void;
280
+
281
+ // ── Turnable (auto-rotate) state ──
282
+ startTurnable(options?: PrefViewerTurnableOptions): PrefViewerTurnableState;
283
+ stopTurnable(): PrefViewerTurnableState;
284
+ toggleTurnable(options?: PrefViewerTurnableOptions): PrefViewerTurnableState;
285
+ getTurnableState(): PrefViewerTurnableState;
286
+
287
+ // ── Dialog ──
288
+ openDialog(title: string, content: string | Node, footer?: string | Node): void;
289
+ closeDialog(): void;
290
+
291
+ // ── Animations (3D only) ──
292
+ // `getAnimations()` is deliberately absent here: the component shadows the standard
293
+ // `Element.getAnimations()` with an incompatible return, and TypeScript cannot model an
294
+ // incompatible override without making PrefViewer unassignable to HTMLElement. Read it
295
+ // through PrefViewerAnimationsApi instead. Renaming the runtime method would fix this
296
+ // properly, but that is a breaking change for the component's API (TEC-3490).
297
+ /** False when no animation of that name exists. */
298
+ playAnimation(name: string, action: PrefViewerAnimationAction): boolean;
299
+ setAnimationProgress(name: string, progress: number): boolean;
300
+ setAnimationLoop(name: string, loop: boolean): boolean;
301
+
302
+ // ── Downloads ──
303
+ downloadModelGLB(): void;
304
+ downloadModelGLTF(): void;
305
+ downloadModelUSDZ(): void;
306
+ downloadSceneGLB(): void;
307
+ downloadSceneGLTF(): void;
308
+ downloadSceneUSDZ(): void;
309
+ downloadModelAndSceneGLB(): void;
310
+ downloadModelAndSceneGLTF(): void;
311
+ downloadModelAndSceneUSDZ(): void;
312
+
313
+ addEventListener<K extends keyof PrefViewerEventMap>(
314
+ type: K,
315
+ listener: (this: PrefViewer, ev: PrefViewerEventMap[K]) => unknown,
316
+ options?: boolean | AddEventListenerOptions,
317
+ ): void;
318
+ addEventListener(
319
+ type: string,
320
+ listener: EventListenerOrEventListenerObject,
321
+ options?: boolean | AddEventListenerOptions,
322
+ ): void;
323
+ removeEventListener<K extends keyof PrefViewerEventMap>(
324
+ type: K,
325
+ listener: (this: PrefViewer, ev: PrefViewerEventMap[K]) => unknown,
326
+ options?: boolean | EventListenerOptions,
327
+ ): void;
328
+ removeEventListener(
329
+ type: string,
330
+ listener: EventListenerOrEventListenerObject,
331
+ options?: boolean | EventListenerOptions,
332
+ ): void;
333
+ }
334
+
335
+ /**
336
+ * The observed attributes. Structured payloads (`config`, `model`, `scene`,
337
+ * `materials`, `drawing`, `options`, `styles`) are JSON strings when written as attributes —
338
+ * pass the objects through the matching method or property instead.
339
+ */
340
+ export interface PrefViewerAttributes {
341
+ config?: string;
342
+ culture?: string;
343
+ drawing?: string;
344
+ materials?: string;
345
+ mode?: PrefViewerMode;
346
+ model?: string;
347
+ scene?: string;
348
+ options?: string;
349
+ styles?: string;
350
+ "show-model"?: boolean | "true" | "false";
351
+ "show-scene"?: boolean | "true" | "false";
352
+ "show-render-menu"?: boolean | "true" | "false";
8
353
  }
9
354
 
355
+ /** <pref-viewer mode="3d" style="display:block; width:100%; height:600px" /> */
356
+ type PrefViewerJsxProps = React.DetailedHTMLProps<
357
+ React.HTMLAttributes<PrefViewer> & PrefViewerAttributes,
358
+ PrefViewer
359
+ >;
360
+
10
361
  declare global {
362
+ interface HTMLElementTagNameMap {
363
+ "pref-viewer": PrefViewer;
364
+ }
365
+
366
+ // React <= 18 (@types/react 18) resolves intrinsics through the global JSX namespace.
367
+ namespace JSX {
368
+ interface IntrinsicElements {
369
+ "pref-viewer": PrefViewerJsxProps;
370
+ }
371
+ }
372
+ }
373
+
374
+ // React 19 (@types/react 19) dropped the global JSX namespace: intrinsics resolve through
375
+ // React.JSX instead. Declaring only the global one above leaves the element unknown on any
376
+ // modern setup, which is why `<pref-viewer mode="3d" />` failed to compile (TEC-3490).
377
+ declare module "react" {
11
378
  namespace JSX {
12
379
  interface IntrinsicElements {
13
- /**
14
- * <pref-viewer model="…" />
15
- */
16
- "pref-viewer": React.DetailedHTMLProps<
17
- React.HTMLAttributes<HTMLElement> & { model?: string },
18
- HTMLElement
19
- >;
380
+ "pref-viewer": PrefViewerJsxProps;
20
381
  }
21
382
  }
22
383
  }
23
384
 
24
- export {};
385
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@preference-sl/pref-viewer",
3
- "version": "2.16.0",
3
+ "version": "2.16.2-beta.0",
4
4
  "description": "Web Component to preview GLTF models with Babylon.js",
5
5
  "author": "Alex Moreno Palacio <amoreno@preference.es>",
6
6
  "scripts": {
@@ -25,6 +25,7 @@
25
25
  "types": "index.d.ts",
26
26
  "exports": {
27
27
  ".": {
28
+ "types": "./index.d.ts",
28
29
  "import": "./src/index.js",
29
30
  "require": "./src/index.js"
30
31
  }
@@ -8,7 +8,7 @@ import OpeningAnimationMenu from "./babylonjs-animation-opening-menu.js";
8
8
  * - Tracks animation state (paused, closed, opened, opening, closing).
9
9
  * - Synchronizes animation progress and UI controls.
10
10
  * - Handles loop mode and progress threshold logic.
11
- * - Dispatches `prefviewer-animation-update` events with animation state snapshots.
11
+ * - Dispatches `pref-viewer-3d-animation-changed` events with animation state snapshots.
12
12
  * - Provides methods for play, pause, go to opened/closed, and progress control.
13
13
  * - Manages the animation control menu (OpeningAnimationMenu) and its callbacks.
14
14
  *
@@ -20,12 +20,17 @@ import OpeningAnimationMenu from "./babylonjs-animation-opening-menu.js";
20
20
  * - pause(): Pauses the current animation.
21
21
  * - goToOpened(): Moves animation to the fully opened state.
22
22
  * - goToClosed(): Moves animation to the fully closed state.
23
+ * - setProgress(progress): Seeks the animation to a normalized position (0–1), snapping to ends when within the threshold.
24
+ * - setLoop(loop): Enables or disables loop mode and syncs the control menu if visible.
23
25
  * - showControls(canvas, openingAnimations): Displays the animation control menu and sets up callbacks.
24
26
  * - hideControls(): Hides the animation control menu.
25
27
  * - isControlsVisible(): Returns true if the control menu is visible for this animation.
26
28
  *
27
29
  * Public Properties:
28
- * - state: Returns the current animation state.
30
+ * - state: Returns the current animation state (one of OpeningAnimation.states).
31
+ * - progress: Returns the normalized progress (0–1) derived from the current frame position.
32
+ * - loop: Returns whether loop mode is currently enabled.
33
+ * - nodes: Returns a copy of the node ID array affected by this animation.
29
34
  *
30
35
  * Private Methods:
31
36
  * - #getNodesFromAnimationGroups(): Collects node IDs affected by the animation groups.
@@ -39,7 +44,7 @@ import OpeningAnimationMenu from "./babylonjs-animation-opening-menu.js";
39
44
  * - #getProgress(): Calculates progress (0-1) from current frame.
40
45
  * - #getPrefViewer3DComponent(): Resolves and caches the nearest `pref-viewer-3d` host element.
41
46
  * - #checkProgress(progress): Applies threshold logic to progress.
42
- * - #notifyStateChange(): Emits `prefviewer-animation-update` with state/progress/loop metadata.
47
+ * - #notifyStateChange(): Emits `pref-viewer-3d-animation-changed` with state/progress/loop metadata.
43
48
  * - #updateControlsSlider(): Updates the slider in the control menu.
44
49
  * - #updateControls(): Emits state updates and syncs controls when the menu is visible.
45
50
  */
@@ -315,7 +320,7 @@ export default class OpeningAnimation {
315
320
  }
316
321
 
317
322
  /**
318
- * Dispatches a `prefviewer-animation-update` CustomEvent from the nearest `pref-viewer-3d` host.
323
+ * Dispatches a `pref-viewer-3d-animation-changed` CustomEvent from the nearest `pref-viewer-3d` host.
319
324
  * The event bubbles and crosses shadow DOM boundaries, exposing the current animation snapshot
320
325
  * (`name`, `state`, `progress`, and `loop`) in `event.detail`.
321
326
  * @private
@@ -329,6 +334,7 @@ export default class OpeningAnimation {
329
334
 
330
335
  const customEventOptions = {
331
336
  bubbles: true,
337
+ cancelable: false,
332
338
  composed: true,
333
339
  detail: {
334
340
  name: this.name,
@@ -337,7 +343,7 @@ export default class OpeningAnimation {
337
343
  loop: this.#loop,
338
344
  },
339
345
  };
340
- this.#prefViewer3D.dispatchEvent(new CustomEvent("prefviewer-animation-changed", customEventOptions));
346
+ this.#prefViewer3D.dispatchEvent(new CustomEvent("pref-viewer-3d-animation-changed", customEventOptions));
341
347
  return true;
342
348
  }
343
349