@thinkersteps_labs/3d-viewer-react 1.0.1 → 1.2.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/README.md CHANGED
@@ -1,107 +1,107 @@
1
- # @thinkersteps_labs/3d-viewer-react
2
-
3
- Official React component wrapper for `@thinkersteps_labs/3d-viewer-core`. Renders photorealistic 3D jewelry viewers, swatches, and customizers in Next.js, Remix, Vite, and standard React applications.
4
-
5
- ---
6
-
7
- ## Installation
8
-
9
- ```bash
10
- pnpm add @thinkersteps_labs/3d-viewer-react @thinkersteps_labs/3d-viewer-core three
11
- # or with npm
12
- npm install @thinkersteps_labs/3d-viewer-react @thinkersteps_labs/3d-viewer-core three
13
- ```
14
-
15
- ---
16
-
17
- ## 1. Turnkey Customizer (5-Line Embed)
18
-
19
- The `<JewelryCustomizer />` provides a complete, responsive 3D canvas and luxury swatch controls out of the box:
20
-
21
- ```tsx
22
- import { JewelryCustomizer } from '@thinkersteps_labs/3d-viewer-react'
23
-
24
- export function ProductPage() {
25
- return (
26
- <JewelryCustomizer
27
- src="https://cdn.example.com/models/solitaire-ring.glb"
28
- defaultMetal="yellow-gold-18k"
29
- defaultStone="diamond-d"
30
- height={520}
31
- autoRotate
32
- onMetalChange={(metal) => console.log('Selected metal:', metal)}
33
- onStoneChange={(stone) => console.log('Selected stone:', stone)}
34
- />
35
- )
36
- }
37
- ```
38
-
39
- ---
40
-
41
- ## 2. Composable Primitives (`<Viewer />` + Swatches)
42
-
43
- Combine `<Viewer />` with pre-built `<MetalSwatches />` and `<StoneSwatches />` for custom layouts:
44
-
45
- ```tsx
46
- import { useState } from 'react'
47
- import {
48
- Viewer,
49
- MetalSwatches,
50
- StoneSwatches,
51
- type MetalColorProp,
52
- type StoneColorProp,
53
- } from '@thinkersteps_labs/3d-viewer-react'
54
-
55
- export function Customizer() {
56
- const [metal, setMetal] = useState<MetalColorProp>('yellow-gold-18k')
57
- const [stone, setStone] = useState<StoneColorProp>('diamond-d')
58
-
59
- return (
60
- <div style={{ maxWidth: '840px', margin: '0 auto' }}>
61
- <Viewer
62
- src="https://cdn.example.com/models/solitaire-ring.glb"
63
- metalColor={metal}
64
- stoneColor={stone}
65
- height={500}
66
- autoRotate
67
- />
68
-
69
- <div style={{ marginTop: '16px', display: 'flex', flexDirection: 'column', gap: '14px' }}>
70
- <MetalSwatches value={metal} onChange={setMetal} variant="pills" />
71
- <StoneSwatches value={stone} onChange={setStone} variant="pills" />
72
- </div>
73
- </div>
74
- )
75
- }
76
- ```
77
-
78
- ---
79
-
80
- ## 3. Headless Hook (`useJewelryCustomizer`)
81
-
82
- For custom UI layouts with pre-wired state and preset lookup:
83
-
84
- ```tsx
85
- import { Viewer, useJewelryCustomizer } from '@thinkersteps_labs/3d-viewer-react'
86
-
87
- export function HeadlessCustomizer() {
88
- const { metal, setMetal, stone, setStone, metalOptions, stoneOptions, viewerProps } =
89
- useJewelryCustomizer({
90
- defaultMetal: 'yellow-gold-18k',
91
- defaultStone: 'diamond-d',
92
- })
93
-
94
- return (
95
- <div>
96
- <Viewer src="/models/ring.glb" {...viewerProps} height={480} />
97
- {/* Build your bespoke buttons, dropdowns, or swatches */}
98
- </div>
99
- )
100
- }
101
- ```
102
-
103
- ---
104
-
105
- ## License
106
-
107
- MIT
1
+ # @thinkersteps_labs/3d-viewer-react
2
+
3
+ Official React component wrapper for `@thinkersteps_labs/3d-viewer-core`. Renders photorealistic 3D jewelry viewers, swatches, and customizers in Next.js, Remix, Vite, and standard React applications.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pnpm add @thinkersteps_labs/3d-viewer-react @thinkersteps_labs/3d-viewer-core three
11
+ # or with npm
12
+ npm install @thinkersteps_labs/3d-viewer-react @thinkersteps_labs/3d-viewer-core three
13
+ ```
14
+
15
+ ---
16
+
17
+ ## 1. Turnkey Customizer (5-Line Embed)
18
+
19
+ The `<JewelryCustomizer />` provides a complete, responsive 3D canvas and luxury swatch controls out of the box:
20
+
21
+ ```tsx
22
+ import { JewelryCustomizer } from '@thinkersteps_labs/3d-viewer-react'
23
+
24
+ export function ProductPage() {
25
+ return (
26
+ <JewelryCustomizer
27
+ src="https://cdn.example.com/models/solitaire-ring.glb"
28
+ defaultMetal="yellow-gold-18k"
29
+ defaultStone="diamond-d"
30
+ height={520}
31
+ autoRotate
32
+ onMetalChange={(metal) => console.log('Selected metal:', metal)}
33
+ onStoneChange={(stone) => console.log('Selected stone:', stone)}
34
+ />
35
+ )
36
+ }
37
+ ```
38
+
39
+ ---
40
+
41
+ ## 2. Composable Primitives (`<Viewer />` + Swatches)
42
+
43
+ Combine `<Viewer />` with pre-built `<MetalSwatches />` and `<StoneSwatches />` for custom layouts:
44
+
45
+ ```tsx
46
+ import { useState } from 'react'
47
+ import {
48
+ Viewer,
49
+ MetalSwatches,
50
+ StoneSwatches,
51
+ type MetalColorProp,
52
+ type StoneColorProp,
53
+ } from '@thinkersteps_labs/3d-viewer-react'
54
+
55
+ export function Customizer() {
56
+ const [metal, setMetal] = useState<MetalColorProp>('yellow-gold-18k')
57
+ const [stone, setStone] = useState<StoneColorProp>('diamond-d')
58
+
59
+ return (
60
+ <div style={{ maxWidth: '840px', margin: '0 auto' }}>
61
+ <Viewer
62
+ src="https://cdn.example.com/models/solitaire-ring.glb"
63
+ metalColor={metal}
64
+ stoneColor={stone}
65
+ height={500}
66
+ autoRotate
67
+ />
68
+
69
+ <div style={{ marginTop: '16px', display: 'flex', flexDirection: 'column', gap: '14px' }}>
70
+ <MetalSwatches value={metal} onChange={setMetal} variant="pills" />
71
+ <StoneSwatches value={stone} onChange={setStone} variant="pills" />
72
+ </div>
73
+ </div>
74
+ )
75
+ }
76
+ ```
77
+
78
+ ---
79
+
80
+ ## 3. Headless Hook (`useJewelryCustomizer`)
81
+
82
+ For custom UI layouts with pre-wired state and preset lookup:
83
+
84
+ ```tsx
85
+ import { Viewer, useJewelryCustomizer } from '@thinkersteps_labs/3d-viewer-react'
86
+
87
+ export function HeadlessCustomizer() {
88
+ const { metal, setMetal, stone, setStone, metalOptions, stoneOptions, viewerProps } =
89
+ useJewelryCustomizer({
90
+ defaultMetal: 'yellow-gold-18k',
91
+ defaultStone: 'diamond-d',
92
+ })
93
+
94
+ return (
95
+ <div>
96
+ <Viewer src="/models/ring.glb" {...viewerProps} height={480} />
97
+ {/* Build your bespoke buttons, dropdowns, or swatches */}
98
+ </div>
99
+ )
100
+ }
101
+ ```
102
+
103
+ ---
104
+
105
+ ## License
106
+
107
+ MIT
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/Viewer.tsx","../src/swatches/MetalSwatches.tsx","../src/swatches/StoneSwatches.tsx","../src/swatches/JewelryCustomizer.tsx","../src/swatches/useJewelryCustomizer.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport { useRef, useEffect, forwardRef, useImperativeHandle, type CSSProperties } from 'react'\nimport {\n createViewer,\n normalizeModelSource,\n type Viewer as CoreViewer,\n type ModelSource,\n type ViewerSourceInput,\n type MetalColorProp,\n type StoneColorProp,\n type ViewerOptions,\n type ViewerState,\n type PartInfo,\n type MaterialSlotInfo,\n type PartType,\n} from '@thinkersteps_labs/3d-viewer-core'\n\nexport interface ViewerRef extends CoreViewer { }\n\nexport interface ViewerProps {\n /** 3D model URL or source object. Takes precedence over `source` if both provided. */\n src?: string\n /** 3D model source (URL string, ModelSource payload, or null). */\n source?: ViewerSourceInput\n /** Metal preset ID or hex code. IDE autocompletes standard presets. */\n metalColor?: MetalColorProp\n /** Stone preset ID or hex code. IDE autocompletes standard presets. */\n stoneColor?: StoneColorProp\n /** Bulk material name or slot ID color overrides. */\n materialColors?: Record<string, string>\n /** Part color overrides keyed by partId or partId:slotIndex. */\n partColors?: Record<string, string>\n background?: string | number\n metalEnvUrl?: string | ArrayBuffer\n gemEnvUrl?: string | ArrayBuffer\n options?: ViewerOptions\n fov?: number\n shadows?: boolean\n autoRotate?: boolean\n quality?: 'low' | 'medium' | 'high'\n /** Explicit container height (e.g. 520 or '520px'). Prevents zero-height collapse. */\n height?: number | string\n /** Explicit container min-height. */\n minHeight?: number | string\n /** Aspect ratio for responsive sizing (e.g. '16/9' or '4/3'). */\n aspectRatio?: string\n onLoaded?: (parts: PartInfo[], materials?: MaterialSlotInfo[]) => void\n onPartSelected?: (info: {\n meshId: string\n partId?: string\n type: PartType\n slotIndex?: number\n slotId?: string\n materialName?: string\n }) => void\n onChange?: (state: ViewerState) => void\n onError?: (err: Error) => void\n onProgress?: (progress: number) => void\n className?: string\n style?: CSSProperties\n}\n\nexport const Viewer = forwardRef<ViewerRef, ViewerProps>(function Viewer(props, ref) {\n const {\n src,\n source,\n metalColor,\n stoneColor,\n materialColors,\n partColors,\n background,\n metalEnvUrl,\n gemEnvUrl,\n options,\n fov,\n shadows,\n autoRotate,\n quality,\n height,\n minHeight,\n aspectRatio,\n className,\n style,\n } = props\n const containerRef = useRef<HTMLDivElement>(null)\n const viewerRef = useRef<ViewerRef | null>(null)\n const cb = useRef(props)\n cb.current = props\n\n useImperativeHandle(ref, () => viewerRef.current!, [])\n\n useEffect(() => {\n if (!containerRef.current) return\n const viewer = createViewer(containerRef.current, {\n ...options,\n ...(background !== undefined && { background }),\n ...(fov !== undefined && { fov }),\n ...(shadows !== undefined && { shadows }),\n ...(autoRotate !== undefined && { autoRotate }),\n ...(quality !== undefined && { quality }),\n ...(metalEnvUrl !== undefined && { metalEnvUrl }),\n ...(gemEnvUrl !== undefined && { gemEnvUrl }),\n })\n viewerRef.current = viewer\n if (import.meta.env.DEV && typeof window !== 'undefined') (window as any).__viewer = viewer\n if (ref) {\n if (typeof ref === 'function') ref(viewer)\n else (ref as any).current = viewer\n }\n viewer.on('loaded', ({ parts, materials }) => cb.current.onLoaded?.(parts, materials))\n viewer.on('partSelected', (info) => cb.current.onPartSelected?.(info))\n viewer.on('change', (state) => cb.current.onChange?.(state))\n viewer.on('error', (err) => cb.current.onError?.(err))\n viewer.on('loading', ({ progress }) => cb.current.onProgress?.(progress))\n return () => {\n viewer.dispose()\n if (ref) {\n if (typeof ref === 'function') ref(null)\n else (ref as any).current = null\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n useEffect(() => {\n if (!viewerRef.current) return\n const resolved = normalizeModelSource(src ?? source)\n if (!resolved) {\n viewerRef.current.unload()\n return\n }\n const ac = new AbortController()\n viewerRef.current.load(resolved, { signal: ac.signal })\n return () => ac.abort()\n }, [src, source])\n\n useEffect(() => {\n if (metalColor) viewerRef.current?.setMetalColor(metalColor)\n }, [metalColor])\n\n useEffect(() => {\n if (stoneColor) viewerRef.current?.setStoneColor(stoneColor)\n }, [stoneColor])\n\n const prevMaterialColorsRef = useRef<Record<string, string>>({})\n useEffect(() => {\n if (!viewerRef.current) return\n const prev = prevMaterialColorsRef.current\n const next = materialColors ?? {}\n\n for (const key of Object.keys(prev)) {\n if (!(key in next)) {\n viewerRef.current.clearMaterialColor(key)\n }\n }\n for (const [key, color] of Object.entries(next)) {\n if (prev[key] !== color) {\n viewerRef.current.setMaterialColor(key, color)\n }\n }\n prevMaterialColorsRef.current = { ...next }\n }, [materialColors])\n\n const prevPartColorsRef = useRef<Record<string, string>>({})\n useEffect(() => {\n if (!viewerRef.current) return\n const prev = prevPartColorsRef.current\n const next = partColors ?? {}\n\n for (const key of Object.keys(prev)) {\n if (!(key in next)) {\n if (key.includes(':')) {\n const [partId, slotIdxStr] = key.split(':')\n const slotIndex = parseInt(slotIdxStr ?? '', 10)\n viewerRef.current.clearPartColor(partId ?? '', isNaN(slotIndex) ? undefined : slotIndex)\n } else {\n viewerRef.current.clearPartColor(key)\n }\n }\n }\n for (const [key, color] of Object.entries(next)) {\n if (prev[key] !== color) {\n if (key.includes(':')) {\n const [partId, slotIdxStr] = key.split(':')\n const slotIndex = parseInt(slotIdxStr ?? '', 10)\n viewerRef.current.setPartColor(partId ?? '', color, isNaN(slotIndex) ? undefined : slotIndex)\n } else {\n viewerRef.current.setPartColor(key, color)\n }\n }\n }\n prevPartColorsRef.current = { ...next }\n }, [partColors])\n\n useEffect(() => {\n if (!viewerRef.current) return\n if (autoRotate) viewerRef.current.startRotation()\n else viewerRef.current.stopRotation()\n }, [autoRotate])\n\n useEffect(() => {\n if (quality) viewerRef.current?.setQuality(quality)\n }, [quality])\n\n const containerStyle: CSSProperties = {\n width: '100%',\n height: height !== undefined ? (typeof height === 'number' ? `${height}px` : height) : '100%',\n minHeight: minHeight !== undefined ? (typeof minHeight === 'number' ? `${minHeight}px` : minHeight) : undefined,\n aspectRatio,\n position: 'relative',\n ...style,\n }\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={containerStyle}\n />\n )\n})\n\nexport default Viewer\n","import React, { type CSSProperties } from 'react'\nimport {\n CANONICAL_METAL_PRESETS,\n getMetalPreset,\n type MetalPreset,\n type MetalColorProp,\n} from '@thinkersteps_labs/3d-viewer-core'\nimport './swatches.css'\n\nexport interface MetalSwatchesProps {\n value?: MetalColorProp\n onChange?: (metalId: string, preset?: MetalPreset) => void\n options?: MetalPreset[]\n category?: 'all' | 'gold' | 'platinum-silver' | 'alternative'\n variant?: 'pills' | 'dots'\n showLabel?: boolean\n className?: string\n style?: CSSProperties\n}\n\nexport function MetalSwatches({\n value,\n onChange,\n options = CANONICAL_METAL_PRESETS,\n category = 'all',\n variant = 'pills',\n showLabel = true,\n className = '',\n style,\n}: MetalSwatchesProps) {\n const filteredOptions = category === 'all'\n ? options\n : options.filter((m) => m.category === category)\n\n const activePreset = value ? getMetalPreset(value) : undefined\n const activeId = activePreset?.id ?? value\n\n return (\n <div className={`td-swatch-group ${className}`} style={style}>\n {showLabel && (\n <div className=\"td-swatch-header\">\n <span className=\"td-swatch-title\">Metal</span>\n {activePreset && (\n <span className=\"td-swatch-selected-name\">{activePreset.label}</span>\n )}\n </div>\n )}\n <div className=\"td-swatch-row\" role=\"radiogroup\" aria-label=\"Metal Selection\">\n {filteredOptions.map((m) => {\n const isSelected = activeId === m.id || value === m.hex\n return (\n <button\n key={m.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n title={m.label}\n className={`td-swatch-${variant === 'dots' ? 'dot' : 'pill'} ${isSelected ? 'active' : ''}`}\n onClick={() => onChange?.(m.id, m)}\n >\n <span\n className=\"td-swatch-color-circle\"\n style={{ backgroundColor: m.hex }}\n />\n {variant === 'pills' && <span>{m.label}</span>}\n </button>\n )\n })}\n </div>\n </div>\n )\n}\n\nexport interface MetalSelectProps {\n value?: MetalColorProp\n onChange?: (metalId: string, preset?: MetalPreset) => void\n options?: MetalPreset[]\n label?: string\n className?: string\n style?: CSSProperties\n}\n\nexport function MetalSelect({\n value,\n onChange,\n options = CANONICAL_METAL_PRESETS,\n label = 'Select Metal',\n className = '',\n style,\n}: MetalSelectProps) {\n const activePreset = value ? getMetalPreset(value) : undefined\n const activeValue = activePreset?.id ?? value ?? options[0]?.id\n\n return (\n <div className={`td-select-wrapper ${className}`} style={style}>\n {label && <label className=\"td-swatch-title\">{label}</label>}\n <select\n className=\"td-select\"\n value={activeValue}\n onChange={(e) => {\n const val = e.target.value\n const preset = getMetalPreset(val)\n onChange?.(val, preset)\n }}\n >\n {options.map((m) => (\n <option key={m.id} value={m.id}>\n {m.label}\n </option>\n ))}\n </select>\n </div>\n )\n}\n","import React, { type CSSProperties } from 'react'\nimport {\n CANONICAL_STONE_PRESETS,\n getStonePreset,\n type StonePreset,\n type StoneColorProp,\n} from '@thinkersteps_labs/3d-viewer-core'\nimport './swatches.css'\n\nexport interface StoneSwatchesProps {\n value?: StoneColorProp\n onChange?: (stoneId: string, preset?: StonePreset) => void\n options?: StonePreset[]\n category?: 'all' | 'diamond' | 'gemstone'\n variant?: 'pills' | 'dots'\n showLabel?: boolean\n className?: string\n style?: CSSProperties\n}\n\nexport function StoneSwatches({\n value,\n onChange,\n options = CANONICAL_STONE_PRESETS,\n category = 'all',\n variant = 'pills',\n showLabel = true,\n className = '',\n style,\n}: StoneSwatchesProps) {\n const filteredOptions = category === 'all'\n ? options\n : options.filter((s) => s.category === category)\n\n const activePreset = value ? getStonePreset(value) : undefined\n const activeId = activePreset?.id ?? value\n\n return (\n <div className={`td-swatch-group ${className}`} style={style}>\n {showLabel && (\n <div className=\"td-swatch-header\">\n <span className=\"td-swatch-title\">Gemstone</span>\n {activePreset && (\n <span className=\"td-swatch-selected-name\">\n {activePreset.label}\n {activePreset.grade ? ` (${activePreset.grade})` : ''}\n </span>\n )}\n </div>\n )}\n <div className=\"td-swatch-row\" role=\"radiogroup\" aria-label=\"Gemstone Selection\">\n {filteredOptions.map((s) => {\n const isSelected = activeId === s.id || value === s.hex\n return (\n <button\n key={s.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n title={`${s.label}${s.grade ? ` - ${s.grade}` : ''}`}\n className={`td-swatch-${variant === 'dots' ? 'dot' : 'pill'} ${isSelected ? 'active' : ''}`}\n onClick={() => onChange?.(s.id, s)}\n >\n <span\n className=\"td-swatch-color-circle\"\n style={{ backgroundColor: s.hex }}\n />\n {variant === 'pills' && <span>{s.label}</span>}\n </button>\n )\n })}\n </div>\n </div>\n )\n}\n\nexport interface StoneSelectProps {\n value?: StoneColorProp\n onChange?: (stoneId: string, preset?: StonePreset) => void\n options?: StonePreset[]\n label?: string\n className?: string\n style?: CSSProperties\n}\n\nexport function StoneSelect({\n value,\n onChange,\n options = CANONICAL_STONE_PRESETS,\n label = 'Select Gemstone',\n className = '',\n style,\n}: StoneSelectProps) {\n const activePreset = value ? getStonePreset(value) : undefined\n const activeValue = activePreset?.id ?? value ?? options[0]?.id\n\n return (\n <div className={`td-select-wrapper ${className}`} style={style}>\n {label && <label className=\"td-swatch-title\">{label}</label>}\n <select\n className=\"td-select\"\n value={activeValue}\n onChange={(e) => {\n const val = e.target.value\n const preset = getStonePreset(val)\n onChange?.(val, preset)\n }}\n >\n {options.map((s) => (\n <option key={s.id} value={s.id}>\n {s.label} {s.grade ? `(${s.grade})` : ''}\n </option>\n ))}\n </select>\n </div>\n )\n}\n","import React, { useState, type CSSProperties } from 'react'\nimport {\n type MetalColorProp,\n type StoneColorProp,\n type ViewerSourceInput,\n type MetalPreset,\n type StonePreset,\n type PartInfo,\n type PartType,\n} from '@thinkersteps_labs/3d-viewer-core'\nimport { Viewer, type ViewerRef } from '../Viewer'\nimport { MetalSwatches, MetalSelect } from './MetalSwatches'\nimport { StoneSwatches, StoneSelect } from './StoneSwatches'\nimport './swatches.css'\n\nexport interface JewelryCustomizerProps {\n src?: string\n source?: ViewerSourceInput\n defaultMetal?: MetalColorProp\n defaultStone?: StoneColorProp\n metal?: MetalColorProp\n onMetalChange?: (metal: string, preset?: MetalPreset) => void\n stone?: StoneColorProp\n onStoneChange?: (stone: string, preset?: StonePreset) => void\n metalOptions?: MetalPreset[]\n stoneOptions?: StonePreset[]\n controlsVariant?: 'pills' | 'dots' | 'select' | 'none'\n height?: number | string\n autoRotate?: boolean\n quality?: 'low' | 'medium' | 'high'\n fov?: number\n shadows?: boolean\n onLoaded?: (parts: PartInfo[]) => void\n onPartSelected?: (info: { meshId: string; type: PartType }) => void\n className?: string\n style?: CSSProperties\n viewerRef?: React.Ref<ViewerRef>\n}\n\nexport function JewelryCustomizer({\n src,\n source,\n defaultMetal = 'yellow-gold-18k',\n defaultStone = 'diamond-d',\n metal: controlledMetal,\n onMetalChange,\n stone: controlledStone,\n onStoneChange,\n metalOptions,\n stoneOptions,\n controlsVariant = 'pills',\n height = 520,\n autoRotate = true,\n quality = 'high',\n fov,\n shadows,\n onLoaded,\n onPartSelected,\n className = '',\n style,\n viewerRef,\n}: JewelryCustomizerProps) {\n const [internalMetal, setInternalMetal] = useState<MetalColorProp>(defaultMetal)\n const [internalStone, setInternalStone] = useState<StoneColorProp>(defaultStone)\n\n const currentMetal = controlledMetal !== undefined ? controlledMetal : internalMetal\n const currentStone = controlledStone !== undefined ? controlledStone : internalStone\n\n const handleMetalChange = (val: string, preset?: MetalPreset) => {\n if (controlledMetal === undefined) setInternalMetal(val)\n onMetalChange?.(val, preset)\n }\n\n const handleStoneChange = (val: string, preset?: StonePreset) => {\n if (controlledStone === undefined) setInternalStone(val)\n onStoneChange?.(val, preset)\n }\n\n const viewportHeight = typeof height === 'number' ? `${height}px` : height\n\n return (\n <div className={`td-customizer-card ${className}`} style={style}>\n <div className=\"td-customizer-viewport\" style={{ height: viewportHeight }}>\n <Viewer\n ref={viewerRef}\n src={src}\n source={source}\n metalColor={currentMetal}\n stoneColor={currentStone}\n autoRotate={autoRotate}\n quality={quality}\n fov={fov}\n shadows={shadows}\n onLoaded={onLoaded}\n onPartSelected={onPartSelected}\n />\n </div>\n\n {controlsVariant !== 'none' && (\n <div className=\"td-customizer-controls\">\n {controlsVariant === 'select' ? (\n <>\n <MetalSelect\n value={currentMetal}\n onChange={handleMetalChange}\n options={metalOptions}\n />\n <StoneSelect\n value={currentStone}\n onChange={handleStoneChange}\n options={stoneOptions}\n />\n </>\n ) : (\n <>\n <MetalSwatches\n value={currentMetal}\n onChange={handleMetalChange}\n options={metalOptions}\n variant={controlsVariant}\n />\n <StoneSwatches\n value={currentStone}\n onChange={handleStoneChange}\n options={stoneOptions}\n variant={controlsVariant}\n />\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n","import { useState, useMemo } from 'react'\nimport {\n CANONICAL_METAL_PRESETS,\n CANONICAL_STONE_PRESETS,\n getMetalPreset,\n getStonePreset,\n type MetalColorProp,\n type StoneColorProp,\n type MetalPreset,\n type StonePreset,\n} from '@thinkersteps_labs/3d-viewer-core'\n\nexport interface UseJewelryCustomizerOptions {\n defaultMetal?: MetalColorProp\n defaultStone?: StoneColorProp\n metalOptions?: MetalPreset[]\n stoneOptions?: StonePreset[]\n}\n\nexport interface UseJewelryCustomizerReturn {\n metal: MetalColorProp\n setMetal: (val: MetalColorProp) => void\n stone: StoneColorProp\n setStone: (val: StoneColorProp) => void\n metalPreset?: MetalPreset\n stonePreset?: StonePreset\n metalOptions: MetalPreset[]\n stoneOptions: StonePreset[]\n viewerProps: {\n metalColor: MetalColorProp\n stoneColor: StoneColorProp\n }\n}\n\nexport function useJewelryCustomizer(\n options: UseJewelryCustomizerOptions = {}\n): UseJewelryCustomizerReturn {\n const {\n defaultMetal = 'yellow-gold-18k',\n defaultStone = 'diamond-d',\n metalOptions = CANONICAL_METAL_PRESETS,\n stoneOptions = CANONICAL_STONE_PRESETS,\n } = options\n\n const [metal, setMetal] = useState<MetalColorProp>(defaultMetal)\n const [stone, setStone] = useState<StoneColorProp>(defaultStone)\n\n const metalPreset = useMemo(() => getMetalPreset(metal), [metal])\n const stonePreset = useMemo(() => getStonePreset(stone), [stone])\n\n const viewerProps = useMemo(\n () => ({\n metalColor: metal,\n stoneColor: stone,\n }),\n [metal, stone]\n )\n\n return {\n metal,\n setMetal,\n stone,\n setStone,\n metalPreset,\n stonePreset,\n metalOptions,\n stoneOptions,\n viewerProps,\n }\n}\n"],"names":["Viewer","forwardRef","props","ref","src","source","metalColor","stoneColor","materialColors","partColors","background","metalEnvUrl","gemEnvUrl","options","fov","shadows","autoRotate","quality","height","minHeight","aspectRatio","className","style","containerRef","useRef","viewerRef","cb","useImperativeHandle","useEffect","viewer","createViewer","parts","materials","_b","_a","info","state","err","progress","resolved","normalizeModelSource","ac","prevMaterialColorsRef","prev","next","key","color","prevPartColorsRef","partId","slotIdxStr","slotIndex","containerStyle","jsx","MetalSwatches","value","onChange","CANONICAL_METAL_PRESETS","category","variant","showLabel","filteredOptions","m","activePreset","getMetalPreset","activeId","jsxs","isSelected","MetalSelect","label","activeValue","e","val","preset","StoneSwatches","CANONICAL_STONE_PRESETS","s","getStonePreset","StoneSelect","JewelryCustomizer","defaultMetal","defaultStone","controlledMetal","onMetalChange","controlledStone","onStoneChange","metalOptions","stoneOptions","controlsVariant","onLoaded","onPartSelected","internalMetal","setInternalMetal","useState","internalStone","setInternalStone","currentMetal","currentStone","handleMetalChange","handleStoneChange","viewportHeight","Fragment","useJewelryCustomizer","metal","setMetal","stone","setStone","metalPreset","useMemo","stonePreset","viewerProps"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/Viewer.tsx","../src/swatches/MetalSwatches.tsx","../src/swatches/StoneSwatches.tsx","../src/swatches/JewelryCustomizer.tsx","../src/swatches/useJewelryCustomizer.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\r\nimport { useRef, useEffect, forwardRef, useImperativeHandle, type CSSProperties } from 'react'\r\nimport {\r\n createViewer,\r\n normalizeModelSource,\r\n type Viewer as CoreViewer,\r\n type ModelSource,\r\n type ViewerSourceInput,\r\n type MetalColorProp,\r\n type StoneColorProp,\r\n type ViewerOptions,\r\n type ViewerState,\r\n type PartInfo,\r\n type MaterialSlotInfo,\r\n type PartType,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\n\r\nexport interface ViewerRef extends CoreViewer { }\r\n\r\nexport interface ViewerProps {\r\n /** 3D model URL or source object. Takes precedence over `source` if both provided. */\r\n src?: string\r\n /** 3D model source (URL string, ModelSource payload, or null). */\r\n source?: ViewerSourceInput\r\n /** Metal preset ID or hex code. IDE autocompletes standard presets. */\r\n metalColor?: MetalColorProp\r\n /** Stone preset ID or hex code. IDE autocompletes standard presets. */\r\n stoneColor?: StoneColorProp\r\n /** Bulk material name or slot ID color overrides. */\r\n materialColors?: Record<string, string>\r\n /** Part color overrides keyed by partId or partId:slotIndex. */\r\n partColors?: Record<string, string>\r\n background?: string | number\r\n metalEnvUrl?: string | ArrayBuffer\r\n gemEnvUrl?: string | ArrayBuffer\r\n options?: ViewerOptions\r\n fov?: number\r\n shadows?: boolean\r\n autoRotate?: boolean\r\n quality?: 'low' | 'medium' | 'high'\r\n /** Explicit container height (e.g. 520 or '520px'). Prevents zero-height collapse. */\r\n height?: number | string\r\n /** Explicit container min-height. */\r\n minHeight?: number | string\r\n /** Aspect ratio for responsive sizing (e.g. '16/9' or '4/3'). */\r\n aspectRatio?: string\r\n onLoaded?: (parts: PartInfo[], materials?: MaterialSlotInfo[]) => void\r\n onPartSelected?: (info: {\r\n meshId: string\r\n partId?: string\r\n type: PartType\r\n slotIndex?: number\r\n slotId?: string\r\n materialName?: string\r\n }) => void\r\n onChange?: (state: ViewerState) => void\r\n onError?: (err: Error) => void\r\n onProgress?: (progress: number) => void\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport const Viewer = forwardRef<ViewerRef, ViewerProps>(function Viewer(props, ref) {\r\n const {\r\n src,\r\n source,\r\n metalColor,\r\n stoneColor,\r\n materialColors,\r\n partColors,\r\n background,\r\n metalEnvUrl,\r\n gemEnvUrl,\r\n options,\r\n fov,\r\n shadows,\r\n autoRotate,\r\n quality,\r\n height,\r\n minHeight,\r\n aspectRatio,\r\n className,\r\n style,\r\n } = props\r\n const containerRef = useRef<HTMLDivElement>(null)\r\n const viewerRef = useRef<ViewerRef | null>(null)\r\n const cb = useRef(props)\r\n cb.current = props\r\n\r\n useImperativeHandle(ref, () => viewerRef.current!, [])\r\n\r\n useEffect(() => {\r\n if (!containerRef.current) return\r\n const viewer = createViewer(containerRef.current, {\r\n ...options,\r\n ...(background !== undefined && { background }),\r\n ...(fov !== undefined && { fov }),\r\n ...(shadows !== undefined && { shadows }),\r\n ...(autoRotate !== undefined && { autoRotate }),\r\n ...(quality !== undefined && { quality }),\r\n ...(metalEnvUrl !== undefined && { metalEnvUrl }),\r\n ...(gemEnvUrl !== undefined && { gemEnvUrl }),\r\n })\r\n viewerRef.current = viewer\r\n if (import.meta.env.DEV && typeof window !== 'undefined') (window as any).__viewer = viewer\r\n if (ref) {\r\n if (typeof ref === 'function') ref(viewer)\r\n else (ref as any).current = viewer\r\n }\r\n viewer.on('loaded', ({ parts, materials }) => cb.current.onLoaded?.(parts, materials))\r\n viewer.on('partSelected', (info) => cb.current.onPartSelected?.(info))\r\n viewer.on('change', (state) => cb.current.onChange?.(state))\r\n viewer.on('error', (err) => cb.current.onError?.(err))\r\n viewer.on('loading', ({ progress }) => cb.current.onProgress?.(progress))\r\n return () => {\r\n viewer.dispose()\r\n if (ref) {\r\n if (typeof ref === 'function') ref(null)\r\n else (ref as any).current = null\r\n }\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [])\r\n\r\n useEffect(() => {\r\n if (!viewerRef.current) return\r\n const resolved = normalizeModelSource(src ?? source)\r\n if (!resolved) {\r\n viewerRef.current.unload()\r\n return\r\n }\r\n const ac = new AbortController()\r\n viewerRef.current.load(resolved, { signal: ac.signal })\r\n return () => ac.abort()\r\n }, [src, source])\r\n\r\n useEffect(() => {\r\n if (metalColor) viewerRef.current?.setMetalColor(metalColor)\r\n }, [metalColor])\r\n\r\n useEffect(() => {\r\n if (stoneColor) viewerRef.current?.setStoneColor(stoneColor)\r\n }, [stoneColor])\r\n\r\n const prevMaterialColorsRef = useRef<Record<string, string>>({})\r\n useEffect(() => {\r\n if (!viewerRef.current) return\r\n const prev = prevMaterialColorsRef.current\r\n const next = materialColors ?? {}\r\n\r\n for (const key of Object.keys(prev)) {\r\n if (!(key in next)) {\r\n viewerRef.current.clearMaterialColor(key)\r\n }\r\n }\r\n for (const [key, color] of Object.entries(next)) {\r\n if (prev[key] !== color) {\r\n viewerRef.current.setMaterialColor(key, color)\r\n }\r\n }\r\n prevMaterialColorsRef.current = { ...next }\r\n }, [materialColors])\r\n\r\n const prevPartColorsRef = useRef<Record<string, string>>({})\r\n useEffect(() => {\r\n if (!viewerRef.current) return\r\n const prev = prevPartColorsRef.current\r\n const next = partColors ?? {}\r\n\r\n for (const key of Object.keys(prev)) {\r\n if (!(key in next)) {\r\n if (key.includes(':')) {\r\n const [partId, slotIdxStr] = key.split(':')\r\n const slotIndex = parseInt(slotIdxStr ?? '', 10)\r\n viewerRef.current.clearPartColor(partId ?? '', isNaN(slotIndex) ? undefined : slotIndex)\r\n } else {\r\n viewerRef.current.clearPartColor(key)\r\n }\r\n }\r\n }\r\n for (const [key, color] of Object.entries(next)) {\r\n if (prev[key] !== color) {\r\n if (key.includes(':')) {\r\n const [partId, slotIdxStr] = key.split(':')\r\n const slotIndex = parseInt(slotIdxStr ?? '', 10)\r\n viewerRef.current.setPartColor(partId ?? '', color, isNaN(slotIndex) ? undefined : slotIndex)\r\n } else {\r\n viewerRef.current.setPartColor(key, color)\r\n }\r\n }\r\n }\r\n prevPartColorsRef.current = { ...next }\r\n }, [partColors])\r\n\r\n useEffect(() => {\r\n if (!viewerRef.current) return\r\n if (autoRotate) viewerRef.current.startRotation()\r\n else viewerRef.current.stopRotation()\r\n }, [autoRotate])\r\n\r\n useEffect(() => {\r\n if (quality) viewerRef.current?.setQuality(quality)\r\n }, [quality])\r\n\r\n const containerStyle: CSSProperties = {\r\n width: '100%',\r\n height: height !== undefined ? (typeof height === 'number' ? `${height}px` : height) : '100%',\r\n minHeight: minHeight !== undefined ? (typeof minHeight === 'number' ? `${minHeight}px` : minHeight) : undefined,\r\n aspectRatio,\r\n position: 'relative',\r\n ...style,\r\n }\r\n\r\n return (\r\n <div\r\n ref={containerRef}\r\n className={className}\r\n style={containerStyle}\r\n />\r\n )\r\n})\r\n\r\nexport default Viewer\r\n","import React, { type CSSProperties } from 'react'\r\nimport {\r\n CANONICAL_METAL_PRESETS,\r\n getMetalPreset,\r\n type MetalPreset,\r\n type MetalColorProp,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\nimport './swatches.css'\r\n\r\nexport interface MetalSwatchesProps {\r\n value?: MetalColorProp\r\n onChange?: (metalId: string, preset?: MetalPreset) => void\r\n options?: MetalPreset[]\r\n category?: 'all' | 'gold' | 'platinum-silver' | 'alternative'\r\n variant?: 'pills' | 'dots'\r\n showLabel?: boolean\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport function MetalSwatches({\r\n value,\r\n onChange,\r\n options = CANONICAL_METAL_PRESETS,\r\n category = 'all',\r\n variant = 'pills',\r\n showLabel = true,\r\n className = '',\r\n style,\r\n}: MetalSwatchesProps) {\r\n const filteredOptions = category === 'all'\r\n ? options\r\n : options.filter((m) => m.category === category)\r\n\r\n const activePreset = value ? getMetalPreset(value) : undefined\r\n const activeId = activePreset?.id ?? value\r\n\r\n return (\r\n <div className={`td-swatch-group ${className}`} style={style}>\r\n {showLabel && (\r\n <div className=\"td-swatch-header\">\r\n <span className=\"td-swatch-title\">Metal</span>\r\n {activePreset && (\r\n <span className=\"td-swatch-selected-name\">{activePreset.label}</span>\r\n )}\r\n </div>\r\n )}\r\n <div className=\"td-swatch-row\" role=\"radiogroup\" aria-label=\"Metal Selection\">\r\n {filteredOptions.map((m) => {\r\n const isSelected = activeId === m.id || value === m.hex\r\n return (\r\n <button\r\n key={m.id}\r\n type=\"button\"\r\n role=\"radio\"\r\n aria-checked={isSelected}\r\n title={m.label}\r\n className={`td-swatch-${variant === 'dots' ? 'dot' : 'pill'} ${isSelected ? 'active' : ''}`}\r\n onClick={() => onChange?.(m.id, m)}\r\n >\r\n <span\r\n className=\"td-swatch-color-circle\"\r\n style={{ backgroundColor: m.hex }}\r\n />\r\n {variant === 'pills' && <span>{m.label}</span>}\r\n </button>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport interface MetalSelectProps {\r\n value?: MetalColorProp\r\n onChange?: (metalId: string, preset?: MetalPreset) => void\r\n options?: MetalPreset[]\r\n label?: string\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport function MetalSelect({\r\n value,\r\n onChange,\r\n options = CANONICAL_METAL_PRESETS,\r\n label = 'Select Metal',\r\n className = '',\r\n style,\r\n}: MetalSelectProps) {\r\n const activePreset = value ? getMetalPreset(value) : undefined\r\n const activeValue = activePreset?.id ?? value ?? options[0]?.id\r\n\r\n return (\r\n <div className={`td-select-wrapper ${className}`} style={style}>\r\n {label && <label className=\"td-swatch-title\">{label}</label>}\r\n <select\r\n className=\"td-select\"\r\n value={activeValue}\r\n onChange={(e) => {\r\n const val = e.target.value\r\n const preset = getMetalPreset(val)\r\n onChange?.(val, preset)\r\n }}\r\n >\r\n {options.map((m) => (\r\n <option key={m.id} value={m.id}>\r\n {m.label}\r\n </option>\r\n ))}\r\n </select>\r\n </div>\r\n )\r\n}\r\n","import React, { type CSSProperties } from 'react'\r\nimport {\r\n CANONICAL_STONE_PRESETS,\r\n getStonePreset,\r\n type StonePreset,\r\n type StoneColorProp,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\nimport './swatches.css'\r\n\r\nexport interface StoneSwatchesProps {\r\n value?: StoneColorProp\r\n onChange?: (stoneId: string, preset?: StonePreset) => void\r\n options?: StonePreset[]\r\n category?: 'all' | 'diamond' | 'gemstone'\r\n variant?: 'pills' | 'dots'\r\n showLabel?: boolean\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport function StoneSwatches({\r\n value,\r\n onChange,\r\n options = CANONICAL_STONE_PRESETS,\r\n category = 'all',\r\n variant = 'pills',\r\n showLabel = true,\r\n className = '',\r\n style,\r\n}: StoneSwatchesProps) {\r\n const filteredOptions = category === 'all'\r\n ? options\r\n : options.filter((s) => s.category === category)\r\n\r\n const activePreset = value ? getStonePreset(value) : undefined\r\n const activeId = activePreset?.id ?? value\r\n\r\n return (\r\n <div className={`td-swatch-group ${className}`} style={style}>\r\n {showLabel && (\r\n <div className=\"td-swatch-header\">\r\n <span className=\"td-swatch-title\">Gemstone</span>\r\n {activePreset && (\r\n <span className=\"td-swatch-selected-name\">\r\n {activePreset.label}\r\n {activePreset.grade ? ` (${activePreset.grade})` : ''}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n <div className=\"td-swatch-row\" role=\"radiogroup\" aria-label=\"Gemstone Selection\">\r\n {filteredOptions.map((s) => {\r\n const isSelected = activeId === s.id || value === s.hex\r\n return (\r\n <button\r\n key={s.id}\r\n type=\"button\"\r\n role=\"radio\"\r\n aria-checked={isSelected}\r\n title={`${s.label}${s.grade ? ` - ${s.grade}` : ''}`}\r\n className={`td-swatch-${variant === 'dots' ? 'dot' : 'pill'} ${isSelected ? 'active' : ''}`}\r\n onClick={() => onChange?.(s.id, s)}\r\n >\r\n <span\r\n className=\"td-swatch-color-circle\"\r\n style={{ backgroundColor: s.hex }}\r\n />\r\n {variant === 'pills' && <span>{s.label}</span>}\r\n </button>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport interface StoneSelectProps {\r\n value?: StoneColorProp\r\n onChange?: (stoneId: string, preset?: StonePreset) => void\r\n options?: StonePreset[]\r\n label?: string\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport function StoneSelect({\r\n value,\r\n onChange,\r\n options = CANONICAL_STONE_PRESETS,\r\n label = 'Select Gemstone',\r\n className = '',\r\n style,\r\n}: StoneSelectProps) {\r\n const activePreset = value ? getStonePreset(value) : undefined\r\n const activeValue = activePreset?.id ?? value ?? options[0]?.id\r\n\r\n return (\r\n <div className={`td-select-wrapper ${className}`} style={style}>\r\n {label && <label className=\"td-swatch-title\">{label}</label>}\r\n <select\r\n className=\"td-select\"\r\n value={activeValue}\r\n onChange={(e) => {\r\n const val = e.target.value\r\n const preset = getStonePreset(val)\r\n onChange?.(val, preset)\r\n }}\r\n >\r\n {options.map((s) => (\r\n <option key={s.id} value={s.id}>\r\n {s.label} {s.grade ? `(${s.grade})` : ''}\r\n </option>\r\n ))}\r\n </select>\r\n </div>\r\n )\r\n}\r\n","import React, { useState, type CSSProperties } from 'react'\r\nimport {\r\n type MetalColorProp,\r\n type StoneColorProp,\r\n type ViewerSourceInput,\r\n type MetalPreset,\r\n type StonePreset,\r\n type PartInfo,\r\n type PartType,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\nimport { Viewer, type ViewerRef } from '../Viewer'\r\nimport { MetalSwatches, MetalSelect } from './MetalSwatches'\r\nimport { StoneSwatches, StoneSelect } from './StoneSwatches'\r\nimport './swatches.css'\r\n\r\nexport interface JewelryCustomizerProps {\r\n src?: string\r\n source?: ViewerSourceInput\r\n defaultMetal?: MetalColorProp\r\n defaultStone?: StoneColorProp\r\n metal?: MetalColorProp\r\n onMetalChange?: (metal: string, preset?: MetalPreset) => void\r\n stone?: StoneColorProp\r\n onStoneChange?: (stone: string, preset?: StonePreset) => void\r\n metalOptions?: MetalPreset[]\r\n stoneOptions?: StonePreset[]\r\n controlsVariant?: 'pills' | 'dots' | 'select' | 'none'\r\n height?: number | string\r\n autoRotate?: boolean\r\n quality?: 'low' | 'medium' | 'high'\r\n fov?: number\r\n shadows?: boolean\r\n onLoaded?: (parts: PartInfo[]) => void\r\n onPartSelected?: (info: { meshId: string; type: PartType }) => void\r\n className?: string\r\n style?: CSSProperties\r\n viewerRef?: React.Ref<ViewerRef>\r\n}\r\n\r\nexport function JewelryCustomizer({\r\n src,\r\n source,\r\n defaultMetal = 'yellow-gold-18k',\r\n defaultStone = 'diamond-d',\r\n metal: controlledMetal,\r\n onMetalChange,\r\n stone: controlledStone,\r\n onStoneChange,\r\n metalOptions,\r\n stoneOptions,\r\n controlsVariant = 'pills',\r\n height = 520,\r\n autoRotate = true,\r\n quality = 'high',\r\n fov,\r\n shadows,\r\n onLoaded,\r\n onPartSelected,\r\n className = '',\r\n style,\r\n viewerRef,\r\n}: JewelryCustomizerProps) {\r\n const [internalMetal, setInternalMetal] = useState<MetalColorProp>(defaultMetal)\r\n const [internalStone, setInternalStone] = useState<StoneColorProp>(defaultStone)\r\n\r\n const currentMetal = controlledMetal !== undefined ? controlledMetal : internalMetal\r\n const currentStone = controlledStone !== undefined ? controlledStone : internalStone\r\n\r\n const handleMetalChange = (val: string, preset?: MetalPreset) => {\r\n if (controlledMetal === undefined) setInternalMetal(val)\r\n onMetalChange?.(val, preset)\r\n }\r\n\r\n const handleStoneChange = (val: string, preset?: StonePreset) => {\r\n if (controlledStone === undefined) setInternalStone(val)\r\n onStoneChange?.(val, preset)\r\n }\r\n\r\n const viewportHeight = typeof height === 'number' ? `${height}px` : height\r\n\r\n return (\r\n <div className={`td-customizer-card ${className}`} style={style}>\r\n <div className=\"td-customizer-viewport\" style={{ height: viewportHeight }}>\r\n <Viewer\r\n ref={viewerRef}\r\n src={src}\r\n source={source}\r\n metalColor={currentMetal}\r\n stoneColor={currentStone}\r\n autoRotate={autoRotate}\r\n quality={quality}\r\n fov={fov}\r\n shadows={shadows}\r\n onLoaded={onLoaded}\r\n onPartSelected={onPartSelected}\r\n />\r\n </div>\r\n\r\n {controlsVariant !== 'none' && (\r\n <div className=\"td-customizer-controls\">\r\n {controlsVariant === 'select' ? (\r\n <>\r\n <MetalSelect\r\n value={currentMetal}\r\n onChange={handleMetalChange}\r\n options={metalOptions}\r\n />\r\n <StoneSelect\r\n value={currentStone}\r\n onChange={handleStoneChange}\r\n options={stoneOptions}\r\n />\r\n </>\r\n ) : (\r\n <>\r\n <MetalSwatches\r\n value={currentMetal}\r\n onChange={handleMetalChange}\r\n options={metalOptions}\r\n variant={controlsVariant}\r\n />\r\n <StoneSwatches\r\n value={currentStone}\r\n onChange={handleStoneChange}\r\n options={stoneOptions}\r\n variant={controlsVariant}\r\n />\r\n </>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","import { useState, useMemo } from 'react'\r\nimport {\r\n CANONICAL_METAL_PRESETS,\r\n CANONICAL_STONE_PRESETS,\r\n getMetalPreset,\r\n getStonePreset,\r\n type MetalColorProp,\r\n type StoneColorProp,\r\n type MetalPreset,\r\n type StonePreset,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\n\r\nexport interface UseJewelryCustomizerOptions {\r\n defaultMetal?: MetalColorProp\r\n defaultStone?: StoneColorProp\r\n metalOptions?: MetalPreset[]\r\n stoneOptions?: StonePreset[]\r\n}\r\n\r\nexport interface UseJewelryCustomizerReturn {\r\n metal: MetalColorProp\r\n setMetal: (val: MetalColorProp) => void\r\n stone: StoneColorProp\r\n setStone: (val: StoneColorProp) => void\r\n metalPreset?: MetalPreset\r\n stonePreset?: StonePreset\r\n metalOptions: MetalPreset[]\r\n stoneOptions: StonePreset[]\r\n viewerProps: {\r\n metalColor: MetalColorProp\r\n stoneColor: StoneColorProp\r\n }\r\n}\r\n\r\nexport function useJewelryCustomizer(\r\n options: UseJewelryCustomizerOptions = {}\r\n): UseJewelryCustomizerReturn {\r\n const {\r\n defaultMetal = 'yellow-gold-18k',\r\n defaultStone = 'diamond-d',\r\n metalOptions = CANONICAL_METAL_PRESETS,\r\n stoneOptions = CANONICAL_STONE_PRESETS,\r\n } = options\r\n\r\n const [metal, setMetal] = useState<MetalColorProp>(defaultMetal)\r\n const [stone, setStone] = useState<StoneColorProp>(defaultStone)\r\n\r\n const metalPreset = useMemo(() => getMetalPreset(metal), [metal])\r\n const stonePreset = useMemo(() => getStonePreset(stone), [stone])\r\n\r\n const viewerProps = useMemo(\r\n () => ({\r\n metalColor: metal,\r\n stoneColor: stone,\r\n }),\r\n [metal, stone]\r\n )\r\n\r\n return {\r\n metal,\r\n setMetal,\r\n stone,\r\n setStone,\r\n metalPreset,\r\n stonePreset,\r\n metalOptions,\r\n stoneOptions,\r\n viewerProps,\r\n }\r\n}\r\n"],"names":["Viewer","forwardRef","props","ref","src","source","metalColor","stoneColor","materialColors","partColors","background","metalEnvUrl","gemEnvUrl","options","fov","shadows","autoRotate","quality","height","minHeight","aspectRatio","className","style","containerRef","useRef","viewerRef","cb","useImperativeHandle","useEffect","viewer","createViewer","parts","materials","_b","_a","info","state","err","progress","resolved","normalizeModelSource","ac","prevMaterialColorsRef","prev","next","key","color","prevPartColorsRef","partId","slotIdxStr","slotIndex","containerStyle","jsx","MetalSwatches","value","onChange","CANONICAL_METAL_PRESETS","category","variant","showLabel","filteredOptions","m","activePreset","getMetalPreset","activeId","jsxs","isSelected","MetalSelect","label","activeValue","e","val","preset","StoneSwatches","CANONICAL_STONE_PRESETS","s","getStonePreset","StoneSelect","JewelryCustomizer","defaultMetal","defaultStone","controlledMetal","onMetalChange","controlledStone","onStoneChange","metalOptions","stoneOptions","controlsVariant","onLoaded","onPartSelected","internalMetal","setInternalMetal","useState","internalStone","setInternalStone","currentMetal","currentStone","handleMetalChange","handleStoneChange","viewportHeight","Fragment","useJewelryCustomizer","metal","setMetal","stone","setStone","metalPreset","useMemo","stonePreset","viewerProps"],"mappings":"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"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/Viewer.tsx","../src/swatches/MetalSwatches.tsx","../src/swatches/StoneSwatches.tsx","../src/swatches/JewelryCustomizer.tsx","../src/swatches/useJewelryCustomizer.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\nimport { useRef, useEffect, forwardRef, useImperativeHandle, type CSSProperties } from 'react'\nimport {\n createViewer,\n normalizeModelSource,\n type Viewer as CoreViewer,\n type ModelSource,\n type ViewerSourceInput,\n type MetalColorProp,\n type StoneColorProp,\n type ViewerOptions,\n type ViewerState,\n type PartInfo,\n type MaterialSlotInfo,\n type PartType,\n} from '@thinkersteps_labs/3d-viewer-core'\n\nexport interface ViewerRef extends CoreViewer { }\n\nexport interface ViewerProps {\n /** 3D model URL or source object. Takes precedence over `source` if both provided. */\n src?: string\n /** 3D model source (URL string, ModelSource payload, or null). */\n source?: ViewerSourceInput\n /** Metal preset ID or hex code. IDE autocompletes standard presets. */\n metalColor?: MetalColorProp\n /** Stone preset ID or hex code. IDE autocompletes standard presets. */\n stoneColor?: StoneColorProp\n /** Bulk material name or slot ID color overrides. */\n materialColors?: Record<string, string>\n /** Part color overrides keyed by partId or partId:slotIndex. */\n partColors?: Record<string, string>\n background?: string | number\n metalEnvUrl?: string | ArrayBuffer\n gemEnvUrl?: string | ArrayBuffer\n options?: ViewerOptions\n fov?: number\n shadows?: boolean\n autoRotate?: boolean\n quality?: 'low' | 'medium' | 'high'\n /** Explicit container height (e.g. 520 or '520px'). Prevents zero-height collapse. */\n height?: number | string\n /** Explicit container min-height. */\n minHeight?: number | string\n /** Aspect ratio for responsive sizing (e.g. '16/9' or '4/3'). */\n aspectRatio?: string\n onLoaded?: (parts: PartInfo[], materials?: MaterialSlotInfo[]) => void\n onPartSelected?: (info: {\n meshId: string\n partId?: string\n type: PartType\n slotIndex?: number\n slotId?: string\n materialName?: string\n }) => void\n onChange?: (state: ViewerState) => void\n onError?: (err: Error) => void\n onProgress?: (progress: number) => void\n className?: string\n style?: CSSProperties\n}\n\nexport const Viewer = forwardRef<ViewerRef, ViewerProps>(function Viewer(props, ref) {\n const {\n src,\n source,\n metalColor,\n stoneColor,\n materialColors,\n partColors,\n background,\n metalEnvUrl,\n gemEnvUrl,\n options,\n fov,\n shadows,\n autoRotate,\n quality,\n height,\n minHeight,\n aspectRatio,\n className,\n style,\n } = props\n const containerRef = useRef<HTMLDivElement>(null)\n const viewerRef = useRef<ViewerRef | null>(null)\n const cb = useRef(props)\n cb.current = props\n\n useImperativeHandle(ref, () => viewerRef.current!, [])\n\n useEffect(() => {\n if (!containerRef.current) return\n const viewer = createViewer(containerRef.current, {\n ...options,\n ...(background !== undefined && { background }),\n ...(fov !== undefined && { fov }),\n ...(shadows !== undefined && { shadows }),\n ...(autoRotate !== undefined && { autoRotate }),\n ...(quality !== undefined && { quality }),\n ...(metalEnvUrl !== undefined && { metalEnvUrl }),\n ...(gemEnvUrl !== undefined && { gemEnvUrl }),\n })\n viewerRef.current = viewer\n if (import.meta.env.DEV && typeof window !== 'undefined') (window as any).__viewer = viewer\n if (ref) {\n if (typeof ref === 'function') ref(viewer)\n else (ref as any).current = viewer\n }\n viewer.on('loaded', ({ parts, materials }) => cb.current.onLoaded?.(parts, materials))\n viewer.on('partSelected', (info) => cb.current.onPartSelected?.(info))\n viewer.on('change', (state) => cb.current.onChange?.(state))\n viewer.on('error', (err) => cb.current.onError?.(err))\n viewer.on('loading', ({ progress }) => cb.current.onProgress?.(progress))\n return () => {\n viewer.dispose()\n if (ref) {\n if (typeof ref === 'function') ref(null)\n else (ref as any).current = null\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n useEffect(() => {\n if (!viewerRef.current) return\n const resolved = normalizeModelSource(src ?? source)\n if (!resolved) {\n viewerRef.current.unload()\n return\n }\n const ac = new AbortController()\n viewerRef.current.load(resolved, { signal: ac.signal })\n return () => ac.abort()\n }, [src, source])\n\n useEffect(() => {\n if (metalColor) viewerRef.current?.setMetalColor(metalColor)\n }, [metalColor])\n\n useEffect(() => {\n if (stoneColor) viewerRef.current?.setStoneColor(stoneColor)\n }, [stoneColor])\n\n const prevMaterialColorsRef = useRef<Record<string, string>>({})\n useEffect(() => {\n if (!viewerRef.current) return\n const prev = prevMaterialColorsRef.current\n const next = materialColors ?? {}\n\n for (const key of Object.keys(prev)) {\n if (!(key in next)) {\n viewerRef.current.clearMaterialColor(key)\n }\n }\n for (const [key, color] of Object.entries(next)) {\n if (prev[key] !== color) {\n viewerRef.current.setMaterialColor(key, color)\n }\n }\n prevMaterialColorsRef.current = { ...next }\n }, [materialColors])\n\n const prevPartColorsRef = useRef<Record<string, string>>({})\n useEffect(() => {\n if (!viewerRef.current) return\n const prev = prevPartColorsRef.current\n const next = partColors ?? {}\n\n for (const key of Object.keys(prev)) {\n if (!(key in next)) {\n if (key.includes(':')) {\n const [partId, slotIdxStr] = key.split(':')\n const slotIndex = parseInt(slotIdxStr ?? '', 10)\n viewerRef.current.clearPartColor(partId ?? '', isNaN(slotIndex) ? undefined : slotIndex)\n } else {\n viewerRef.current.clearPartColor(key)\n }\n }\n }\n for (const [key, color] of Object.entries(next)) {\n if (prev[key] !== color) {\n if (key.includes(':')) {\n const [partId, slotIdxStr] = key.split(':')\n const slotIndex = parseInt(slotIdxStr ?? '', 10)\n viewerRef.current.setPartColor(partId ?? '', color, isNaN(slotIndex) ? undefined : slotIndex)\n } else {\n viewerRef.current.setPartColor(key, color)\n }\n }\n }\n prevPartColorsRef.current = { ...next }\n }, [partColors])\n\n useEffect(() => {\n if (!viewerRef.current) return\n if (autoRotate) viewerRef.current.startRotation()\n else viewerRef.current.stopRotation()\n }, [autoRotate])\n\n useEffect(() => {\n if (quality) viewerRef.current?.setQuality(quality)\n }, [quality])\n\n const containerStyle: CSSProperties = {\n width: '100%',\n height: height !== undefined ? (typeof height === 'number' ? `${height}px` : height) : '100%',\n minHeight: minHeight !== undefined ? (typeof minHeight === 'number' ? `${minHeight}px` : minHeight) : undefined,\n aspectRatio,\n position: 'relative',\n ...style,\n }\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={containerStyle}\n />\n )\n})\n\nexport default Viewer\n","import React, { type CSSProperties } from 'react'\nimport {\n CANONICAL_METAL_PRESETS,\n getMetalPreset,\n type MetalPreset,\n type MetalColorProp,\n} from '@thinkersteps_labs/3d-viewer-core'\nimport './swatches.css'\n\nexport interface MetalSwatchesProps {\n value?: MetalColorProp\n onChange?: (metalId: string, preset?: MetalPreset) => void\n options?: MetalPreset[]\n category?: 'all' | 'gold' | 'platinum-silver' | 'alternative'\n variant?: 'pills' | 'dots'\n showLabel?: boolean\n className?: string\n style?: CSSProperties\n}\n\nexport function MetalSwatches({\n value,\n onChange,\n options = CANONICAL_METAL_PRESETS,\n category = 'all',\n variant = 'pills',\n showLabel = true,\n className = '',\n style,\n}: MetalSwatchesProps) {\n const filteredOptions = category === 'all'\n ? options\n : options.filter((m) => m.category === category)\n\n const activePreset = value ? getMetalPreset(value) : undefined\n const activeId = activePreset?.id ?? value\n\n return (\n <div className={`td-swatch-group ${className}`} style={style}>\n {showLabel && (\n <div className=\"td-swatch-header\">\n <span className=\"td-swatch-title\">Metal</span>\n {activePreset && (\n <span className=\"td-swatch-selected-name\">{activePreset.label}</span>\n )}\n </div>\n )}\n <div className=\"td-swatch-row\" role=\"radiogroup\" aria-label=\"Metal Selection\">\n {filteredOptions.map((m) => {\n const isSelected = activeId === m.id || value === m.hex\n return (\n <button\n key={m.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n title={m.label}\n className={`td-swatch-${variant === 'dots' ? 'dot' : 'pill'} ${isSelected ? 'active' : ''}`}\n onClick={() => onChange?.(m.id, m)}\n >\n <span\n className=\"td-swatch-color-circle\"\n style={{ backgroundColor: m.hex }}\n />\n {variant === 'pills' && <span>{m.label}</span>}\n </button>\n )\n })}\n </div>\n </div>\n )\n}\n\nexport interface MetalSelectProps {\n value?: MetalColorProp\n onChange?: (metalId: string, preset?: MetalPreset) => void\n options?: MetalPreset[]\n label?: string\n className?: string\n style?: CSSProperties\n}\n\nexport function MetalSelect({\n value,\n onChange,\n options = CANONICAL_METAL_PRESETS,\n label = 'Select Metal',\n className = '',\n style,\n}: MetalSelectProps) {\n const activePreset = value ? getMetalPreset(value) : undefined\n const activeValue = activePreset?.id ?? value ?? options[0]?.id\n\n return (\n <div className={`td-select-wrapper ${className}`} style={style}>\n {label && <label className=\"td-swatch-title\">{label}</label>}\n <select\n className=\"td-select\"\n value={activeValue}\n onChange={(e) => {\n const val = e.target.value\n const preset = getMetalPreset(val)\n onChange?.(val, preset)\n }}\n >\n {options.map((m) => (\n <option key={m.id} value={m.id}>\n {m.label}\n </option>\n ))}\n </select>\n </div>\n )\n}\n","import React, { type CSSProperties } from 'react'\nimport {\n CANONICAL_STONE_PRESETS,\n getStonePreset,\n type StonePreset,\n type StoneColorProp,\n} from '@thinkersteps_labs/3d-viewer-core'\nimport './swatches.css'\n\nexport interface StoneSwatchesProps {\n value?: StoneColorProp\n onChange?: (stoneId: string, preset?: StonePreset) => void\n options?: StonePreset[]\n category?: 'all' | 'diamond' | 'gemstone'\n variant?: 'pills' | 'dots'\n showLabel?: boolean\n className?: string\n style?: CSSProperties\n}\n\nexport function StoneSwatches({\n value,\n onChange,\n options = CANONICAL_STONE_PRESETS,\n category = 'all',\n variant = 'pills',\n showLabel = true,\n className = '',\n style,\n}: StoneSwatchesProps) {\n const filteredOptions = category === 'all'\n ? options\n : options.filter((s) => s.category === category)\n\n const activePreset = value ? getStonePreset(value) : undefined\n const activeId = activePreset?.id ?? value\n\n return (\n <div className={`td-swatch-group ${className}`} style={style}>\n {showLabel && (\n <div className=\"td-swatch-header\">\n <span className=\"td-swatch-title\">Gemstone</span>\n {activePreset && (\n <span className=\"td-swatch-selected-name\">\n {activePreset.label}\n {activePreset.grade ? ` (${activePreset.grade})` : ''}\n </span>\n )}\n </div>\n )}\n <div className=\"td-swatch-row\" role=\"radiogroup\" aria-label=\"Gemstone Selection\">\n {filteredOptions.map((s) => {\n const isSelected = activeId === s.id || value === s.hex\n return (\n <button\n key={s.id}\n type=\"button\"\n role=\"radio\"\n aria-checked={isSelected}\n title={`${s.label}${s.grade ? ` - ${s.grade}` : ''}`}\n className={`td-swatch-${variant === 'dots' ? 'dot' : 'pill'} ${isSelected ? 'active' : ''}`}\n onClick={() => onChange?.(s.id, s)}\n >\n <span\n className=\"td-swatch-color-circle\"\n style={{ backgroundColor: s.hex }}\n />\n {variant === 'pills' && <span>{s.label}</span>}\n </button>\n )\n })}\n </div>\n </div>\n )\n}\n\nexport interface StoneSelectProps {\n value?: StoneColorProp\n onChange?: (stoneId: string, preset?: StonePreset) => void\n options?: StonePreset[]\n label?: string\n className?: string\n style?: CSSProperties\n}\n\nexport function StoneSelect({\n value,\n onChange,\n options = CANONICAL_STONE_PRESETS,\n label = 'Select Gemstone',\n className = '',\n style,\n}: StoneSelectProps) {\n const activePreset = value ? getStonePreset(value) : undefined\n const activeValue = activePreset?.id ?? value ?? options[0]?.id\n\n return (\n <div className={`td-select-wrapper ${className}`} style={style}>\n {label && <label className=\"td-swatch-title\">{label}</label>}\n <select\n className=\"td-select\"\n value={activeValue}\n onChange={(e) => {\n const val = e.target.value\n const preset = getStonePreset(val)\n onChange?.(val, preset)\n }}\n >\n {options.map((s) => (\n <option key={s.id} value={s.id}>\n {s.label} {s.grade ? `(${s.grade})` : ''}\n </option>\n ))}\n </select>\n </div>\n )\n}\n","import React, { useState, type CSSProperties } from 'react'\nimport {\n type MetalColorProp,\n type StoneColorProp,\n type ViewerSourceInput,\n type MetalPreset,\n type StonePreset,\n type PartInfo,\n type PartType,\n} from '@thinkersteps_labs/3d-viewer-core'\nimport { Viewer, type ViewerRef } from '../Viewer'\nimport { MetalSwatches, MetalSelect } from './MetalSwatches'\nimport { StoneSwatches, StoneSelect } from './StoneSwatches'\nimport './swatches.css'\n\nexport interface JewelryCustomizerProps {\n src?: string\n source?: ViewerSourceInput\n defaultMetal?: MetalColorProp\n defaultStone?: StoneColorProp\n metal?: MetalColorProp\n onMetalChange?: (metal: string, preset?: MetalPreset) => void\n stone?: StoneColorProp\n onStoneChange?: (stone: string, preset?: StonePreset) => void\n metalOptions?: MetalPreset[]\n stoneOptions?: StonePreset[]\n controlsVariant?: 'pills' | 'dots' | 'select' | 'none'\n height?: number | string\n autoRotate?: boolean\n quality?: 'low' | 'medium' | 'high'\n fov?: number\n shadows?: boolean\n onLoaded?: (parts: PartInfo[]) => void\n onPartSelected?: (info: { meshId: string; type: PartType }) => void\n className?: string\n style?: CSSProperties\n viewerRef?: React.Ref<ViewerRef>\n}\n\nexport function JewelryCustomizer({\n src,\n source,\n defaultMetal = 'yellow-gold-18k',\n defaultStone = 'diamond-d',\n metal: controlledMetal,\n onMetalChange,\n stone: controlledStone,\n onStoneChange,\n metalOptions,\n stoneOptions,\n controlsVariant = 'pills',\n height = 520,\n autoRotate = true,\n quality = 'high',\n fov,\n shadows,\n onLoaded,\n onPartSelected,\n className = '',\n style,\n viewerRef,\n}: JewelryCustomizerProps) {\n const [internalMetal, setInternalMetal] = useState<MetalColorProp>(defaultMetal)\n const [internalStone, setInternalStone] = useState<StoneColorProp>(defaultStone)\n\n const currentMetal = controlledMetal !== undefined ? controlledMetal : internalMetal\n const currentStone = controlledStone !== undefined ? controlledStone : internalStone\n\n const handleMetalChange = (val: string, preset?: MetalPreset) => {\n if (controlledMetal === undefined) setInternalMetal(val)\n onMetalChange?.(val, preset)\n }\n\n const handleStoneChange = (val: string, preset?: StonePreset) => {\n if (controlledStone === undefined) setInternalStone(val)\n onStoneChange?.(val, preset)\n }\n\n const viewportHeight = typeof height === 'number' ? `${height}px` : height\n\n return (\n <div className={`td-customizer-card ${className}`} style={style}>\n <div className=\"td-customizer-viewport\" style={{ height: viewportHeight }}>\n <Viewer\n ref={viewerRef}\n src={src}\n source={source}\n metalColor={currentMetal}\n stoneColor={currentStone}\n autoRotate={autoRotate}\n quality={quality}\n fov={fov}\n shadows={shadows}\n onLoaded={onLoaded}\n onPartSelected={onPartSelected}\n />\n </div>\n\n {controlsVariant !== 'none' && (\n <div className=\"td-customizer-controls\">\n {controlsVariant === 'select' ? (\n <>\n <MetalSelect\n value={currentMetal}\n onChange={handleMetalChange}\n options={metalOptions}\n />\n <StoneSelect\n value={currentStone}\n onChange={handleStoneChange}\n options={stoneOptions}\n />\n </>\n ) : (\n <>\n <MetalSwatches\n value={currentMetal}\n onChange={handleMetalChange}\n options={metalOptions}\n variant={controlsVariant}\n />\n <StoneSwatches\n value={currentStone}\n onChange={handleStoneChange}\n options={stoneOptions}\n variant={controlsVariant}\n />\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n","import { useState, useMemo } from 'react'\nimport {\n CANONICAL_METAL_PRESETS,\n CANONICAL_STONE_PRESETS,\n getMetalPreset,\n getStonePreset,\n type MetalColorProp,\n type StoneColorProp,\n type MetalPreset,\n type StonePreset,\n} from '@thinkersteps_labs/3d-viewer-core'\n\nexport interface UseJewelryCustomizerOptions {\n defaultMetal?: MetalColorProp\n defaultStone?: StoneColorProp\n metalOptions?: MetalPreset[]\n stoneOptions?: StonePreset[]\n}\n\nexport interface UseJewelryCustomizerReturn {\n metal: MetalColorProp\n setMetal: (val: MetalColorProp) => void\n stone: StoneColorProp\n setStone: (val: StoneColorProp) => void\n metalPreset?: MetalPreset\n stonePreset?: StonePreset\n metalOptions: MetalPreset[]\n stoneOptions: StonePreset[]\n viewerProps: {\n metalColor: MetalColorProp\n stoneColor: StoneColorProp\n }\n}\n\nexport function useJewelryCustomizer(\n options: UseJewelryCustomizerOptions = {}\n): UseJewelryCustomizerReturn {\n const {\n defaultMetal = 'yellow-gold-18k',\n defaultStone = 'diamond-d',\n metalOptions = CANONICAL_METAL_PRESETS,\n stoneOptions = CANONICAL_STONE_PRESETS,\n } = options\n\n const [metal, setMetal] = useState<MetalColorProp>(defaultMetal)\n const [stone, setStone] = useState<StoneColorProp>(defaultStone)\n\n const metalPreset = useMemo(() => getMetalPreset(metal), [metal])\n const stonePreset = useMemo(() => getStonePreset(stone), [stone])\n\n const viewerProps = useMemo(\n () => ({\n metalColor: metal,\n stoneColor: stone,\n }),\n [metal, stone]\n )\n\n return {\n metal,\n setMetal,\n stone,\n setStone,\n metalPreset,\n stonePreset,\n metalOptions,\n stoneOptions,\n viewerProps,\n }\n}\n"],"names":["Viewer","forwardRef","props","ref","src","source","metalColor","stoneColor","materialColors","partColors","background","metalEnvUrl","gemEnvUrl","options","fov","shadows","autoRotate","quality","height","minHeight","aspectRatio","className","style","containerRef","useRef","viewerRef","cb","useImperativeHandle","useEffect","viewer","createViewer","parts","materials","_b","_a","info","state","err","progress","resolved","normalizeModelSource","ac","prevMaterialColorsRef","prev","next","key","color","prevPartColorsRef","partId","slotIdxStr","slotIndex","containerStyle","jsx","MetalSwatches","value","onChange","CANONICAL_METAL_PRESETS","category","variant","showLabel","filteredOptions","m","activePreset","getMetalPreset","activeId","jsxs","isSelected","MetalSelect","label","activeValue","e","val","preset","StoneSwatches","CANONICAL_STONE_PRESETS","s","getStonePreset","StoneSelect","JewelryCustomizer","defaultMetal","defaultStone","controlledMetal","onMetalChange","controlledStone","onStoneChange","metalOptions","stoneOptions","controlsVariant","onLoaded","onPartSelected","internalMetal","setInternalMetal","useState","internalStone","setInternalStone","currentMetal","currentStone","handleMetalChange","handleStoneChange","viewportHeight","Fragment","useJewelryCustomizer","metal","setMetal","stone","setStone","metalPreset","useMemo","stonePreset","viewerProps"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/Viewer.tsx","../src/swatches/MetalSwatches.tsx","../src/swatches/StoneSwatches.tsx","../src/swatches/JewelryCustomizer.tsx","../src/swatches/useJewelryCustomizer.ts"],"sourcesContent":["/// <reference types=\"vite/client\" />\r\nimport { useRef, useEffect, forwardRef, useImperativeHandle, type CSSProperties } from 'react'\r\nimport {\r\n createViewer,\r\n normalizeModelSource,\r\n type Viewer as CoreViewer,\r\n type ModelSource,\r\n type ViewerSourceInput,\r\n type MetalColorProp,\r\n type StoneColorProp,\r\n type ViewerOptions,\r\n type ViewerState,\r\n type PartInfo,\r\n type MaterialSlotInfo,\r\n type PartType,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\n\r\nexport interface ViewerRef extends CoreViewer { }\r\n\r\nexport interface ViewerProps {\r\n /** 3D model URL or source object. Takes precedence over `source` if both provided. */\r\n src?: string\r\n /** 3D model source (URL string, ModelSource payload, or null). */\r\n source?: ViewerSourceInput\r\n /** Metal preset ID or hex code. IDE autocompletes standard presets. */\r\n metalColor?: MetalColorProp\r\n /** Stone preset ID or hex code. IDE autocompletes standard presets. */\r\n stoneColor?: StoneColorProp\r\n /** Bulk material name or slot ID color overrides. */\r\n materialColors?: Record<string, string>\r\n /** Part color overrides keyed by partId or partId:slotIndex. */\r\n partColors?: Record<string, string>\r\n background?: string | number\r\n metalEnvUrl?: string | ArrayBuffer\r\n gemEnvUrl?: string | ArrayBuffer\r\n options?: ViewerOptions\r\n fov?: number\r\n shadows?: boolean\r\n autoRotate?: boolean\r\n quality?: 'low' | 'medium' | 'high'\r\n /** Explicit container height (e.g. 520 or '520px'). Prevents zero-height collapse. */\r\n height?: number | string\r\n /** Explicit container min-height. */\r\n minHeight?: number | string\r\n /** Aspect ratio for responsive sizing (e.g. '16/9' or '4/3'). */\r\n aspectRatio?: string\r\n onLoaded?: (parts: PartInfo[], materials?: MaterialSlotInfo[]) => void\r\n onPartSelected?: (info: {\r\n meshId: string\r\n partId?: string\r\n type: PartType\r\n slotIndex?: number\r\n slotId?: string\r\n materialName?: string\r\n }) => void\r\n onChange?: (state: ViewerState) => void\r\n onError?: (err: Error) => void\r\n onProgress?: (progress: number) => void\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport const Viewer = forwardRef<ViewerRef, ViewerProps>(function Viewer(props, ref) {\r\n const {\r\n src,\r\n source,\r\n metalColor,\r\n stoneColor,\r\n materialColors,\r\n partColors,\r\n background,\r\n metalEnvUrl,\r\n gemEnvUrl,\r\n options,\r\n fov,\r\n shadows,\r\n autoRotate,\r\n quality,\r\n height,\r\n minHeight,\r\n aspectRatio,\r\n className,\r\n style,\r\n } = props\r\n const containerRef = useRef<HTMLDivElement>(null)\r\n const viewerRef = useRef<ViewerRef | null>(null)\r\n const cb = useRef(props)\r\n cb.current = props\r\n\r\n useImperativeHandle(ref, () => viewerRef.current!, [])\r\n\r\n useEffect(() => {\r\n if (!containerRef.current) return\r\n const viewer = createViewer(containerRef.current, {\r\n ...options,\r\n ...(background !== undefined && { background }),\r\n ...(fov !== undefined && { fov }),\r\n ...(shadows !== undefined && { shadows }),\r\n ...(autoRotate !== undefined && { autoRotate }),\r\n ...(quality !== undefined && { quality }),\r\n ...(metalEnvUrl !== undefined && { metalEnvUrl }),\r\n ...(gemEnvUrl !== undefined && { gemEnvUrl }),\r\n })\r\n viewerRef.current = viewer\r\n if (import.meta.env.DEV && typeof window !== 'undefined') (window as any).__viewer = viewer\r\n if (ref) {\r\n if (typeof ref === 'function') ref(viewer)\r\n else (ref as any).current = viewer\r\n }\r\n viewer.on('loaded', ({ parts, materials }) => cb.current.onLoaded?.(parts, materials))\r\n viewer.on('partSelected', (info) => cb.current.onPartSelected?.(info))\r\n viewer.on('change', (state) => cb.current.onChange?.(state))\r\n viewer.on('error', (err) => cb.current.onError?.(err))\r\n viewer.on('loading', ({ progress }) => cb.current.onProgress?.(progress))\r\n return () => {\r\n viewer.dispose()\r\n if (ref) {\r\n if (typeof ref === 'function') ref(null)\r\n else (ref as any).current = null\r\n }\r\n }\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [])\r\n\r\n useEffect(() => {\r\n if (!viewerRef.current) return\r\n const resolved = normalizeModelSource(src ?? source)\r\n if (!resolved) {\r\n viewerRef.current.unload()\r\n return\r\n }\r\n const ac = new AbortController()\r\n viewerRef.current.load(resolved, { signal: ac.signal })\r\n return () => ac.abort()\r\n }, [src, source])\r\n\r\n useEffect(() => {\r\n if (metalColor) viewerRef.current?.setMetalColor(metalColor)\r\n }, [metalColor])\r\n\r\n useEffect(() => {\r\n if (stoneColor) viewerRef.current?.setStoneColor(stoneColor)\r\n }, [stoneColor])\r\n\r\n const prevMaterialColorsRef = useRef<Record<string, string>>({})\r\n useEffect(() => {\r\n if (!viewerRef.current) return\r\n const prev = prevMaterialColorsRef.current\r\n const next = materialColors ?? {}\r\n\r\n for (const key of Object.keys(prev)) {\r\n if (!(key in next)) {\r\n viewerRef.current.clearMaterialColor(key)\r\n }\r\n }\r\n for (const [key, color] of Object.entries(next)) {\r\n if (prev[key] !== color) {\r\n viewerRef.current.setMaterialColor(key, color)\r\n }\r\n }\r\n prevMaterialColorsRef.current = { ...next }\r\n }, [materialColors])\r\n\r\n const prevPartColorsRef = useRef<Record<string, string>>({})\r\n useEffect(() => {\r\n if (!viewerRef.current) return\r\n const prev = prevPartColorsRef.current\r\n const next = partColors ?? {}\r\n\r\n for (const key of Object.keys(prev)) {\r\n if (!(key in next)) {\r\n if (key.includes(':')) {\r\n const [partId, slotIdxStr] = key.split(':')\r\n const slotIndex = parseInt(slotIdxStr ?? '', 10)\r\n viewerRef.current.clearPartColor(partId ?? '', isNaN(slotIndex) ? undefined : slotIndex)\r\n } else {\r\n viewerRef.current.clearPartColor(key)\r\n }\r\n }\r\n }\r\n for (const [key, color] of Object.entries(next)) {\r\n if (prev[key] !== color) {\r\n if (key.includes(':')) {\r\n const [partId, slotIdxStr] = key.split(':')\r\n const slotIndex = parseInt(slotIdxStr ?? '', 10)\r\n viewerRef.current.setPartColor(partId ?? '', color, isNaN(slotIndex) ? undefined : slotIndex)\r\n } else {\r\n viewerRef.current.setPartColor(key, color)\r\n }\r\n }\r\n }\r\n prevPartColorsRef.current = { ...next }\r\n }, [partColors])\r\n\r\n useEffect(() => {\r\n if (!viewerRef.current) return\r\n if (autoRotate) viewerRef.current.startRotation()\r\n else viewerRef.current.stopRotation()\r\n }, [autoRotate])\r\n\r\n useEffect(() => {\r\n if (quality) viewerRef.current?.setQuality(quality)\r\n }, [quality])\r\n\r\n const containerStyle: CSSProperties = {\r\n width: '100%',\r\n height: height !== undefined ? (typeof height === 'number' ? `${height}px` : height) : '100%',\r\n minHeight: minHeight !== undefined ? (typeof minHeight === 'number' ? `${minHeight}px` : minHeight) : undefined,\r\n aspectRatio,\r\n position: 'relative',\r\n ...style,\r\n }\r\n\r\n return (\r\n <div\r\n ref={containerRef}\r\n className={className}\r\n style={containerStyle}\r\n />\r\n )\r\n})\r\n\r\nexport default Viewer\r\n","import React, { type CSSProperties } from 'react'\r\nimport {\r\n CANONICAL_METAL_PRESETS,\r\n getMetalPreset,\r\n type MetalPreset,\r\n type MetalColorProp,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\nimport './swatches.css'\r\n\r\nexport interface MetalSwatchesProps {\r\n value?: MetalColorProp\r\n onChange?: (metalId: string, preset?: MetalPreset) => void\r\n options?: MetalPreset[]\r\n category?: 'all' | 'gold' | 'platinum-silver' | 'alternative'\r\n variant?: 'pills' | 'dots'\r\n showLabel?: boolean\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport function MetalSwatches({\r\n value,\r\n onChange,\r\n options = CANONICAL_METAL_PRESETS,\r\n category = 'all',\r\n variant = 'pills',\r\n showLabel = true,\r\n className = '',\r\n style,\r\n}: MetalSwatchesProps) {\r\n const filteredOptions = category === 'all'\r\n ? options\r\n : options.filter((m) => m.category === category)\r\n\r\n const activePreset = value ? getMetalPreset(value) : undefined\r\n const activeId = activePreset?.id ?? value\r\n\r\n return (\r\n <div className={`td-swatch-group ${className}`} style={style}>\r\n {showLabel && (\r\n <div className=\"td-swatch-header\">\r\n <span className=\"td-swatch-title\">Metal</span>\r\n {activePreset && (\r\n <span className=\"td-swatch-selected-name\">{activePreset.label}</span>\r\n )}\r\n </div>\r\n )}\r\n <div className=\"td-swatch-row\" role=\"radiogroup\" aria-label=\"Metal Selection\">\r\n {filteredOptions.map((m) => {\r\n const isSelected = activeId === m.id || value === m.hex\r\n return (\r\n <button\r\n key={m.id}\r\n type=\"button\"\r\n role=\"radio\"\r\n aria-checked={isSelected}\r\n title={m.label}\r\n className={`td-swatch-${variant === 'dots' ? 'dot' : 'pill'} ${isSelected ? 'active' : ''}`}\r\n onClick={() => onChange?.(m.id, m)}\r\n >\r\n <span\r\n className=\"td-swatch-color-circle\"\r\n style={{ backgroundColor: m.hex }}\r\n />\r\n {variant === 'pills' && <span>{m.label}</span>}\r\n </button>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport interface MetalSelectProps {\r\n value?: MetalColorProp\r\n onChange?: (metalId: string, preset?: MetalPreset) => void\r\n options?: MetalPreset[]\r\n label?: string\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport function MetalSelect({\r\n value,\r\n onChange,\r\n options = CANONICAL_METAL_PRESETS,\r\n label = 'Select Metal',\r\n className = '',\r\n style,\r\n}: MetalSelectProps) {\r\n const activePreset = value ? getMetalPreset(value) : undefined\r\n const activeValue = activePreset?.id ?? value ?? options[0]?.id\r\n\r\n return (\r\n <div className={`td-select-wrapper ${className}`} style={style}>\r\n {label && <label className=\"td-swatch-title\">{label}</label>}\r\n <select\r\n className=\"td-select\"\r\n value={activeValue}\r\n onChange={(e) => {\r\n const val = e.target.value\r\n const preset = getMetalPreset(val)\r\n onChange?.(val, preset)\r\n }}\r\n >\r\n {options.map((m) => (\r\n <option key={m.id} value={m.id}>\r\n {m.label}\r\n </option>\r\n ))}\r\n </select>\r\n </div>\r\n )\r\n}\r\n","import React, { type CSSProperties } from 'react'\r\nimport {\r\n CANONICAL_STONE_PRESETS,\r\n getStonePreset,\r\n type StonePreset,\r\n type StoneColorProp,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\nimport './swatches.css'\r\n\r\nexport interface StoneSwatchesProps {\r\n value?: StoneColorProp\r\n onChange?: (stoneId: string, preset?: StonePreset) => void\r\n options?: StonePreset[]\r\n category?: 'all' | 'diamond' | 'gemstone'\r\n variant?: 'pills' | 'dots'\r\n showLabel?: boolean\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport function StoneSwatches({\r\n value,\r\n onChange,\r\n options = CANONICAL_STONE_PRESETS,\r\n category = 'all',\r\n variant = 'pills',\r\n showLabel = true,\r\n className = '',\r\n style,\r\n}: StoneSwatchesProps) {\r\n const filteredOptions = category === 'all'\r\n ? options\r\n : options.filter((s) => s.category === category)\r\n\r\n const activePreset = value ? getStonePreset(value) : undefined\r\n const activeId = activePreset?.id ?? value\r\n\r\n return (\r\n <div className={`td-swatch-group ${className}`} style={style}>\r\n {showLabel && (\r\n <div className=\"td-swatch-header\">\r\n <span className=\"td-swatch-title\">Gemstone</span>\r\n {activePreset && (\r\n <span className=\"td-swatch-selected-name\">\r\n {activePreset.label}\r\n {activePreset.grade ? ` (${activePreset.grade})` : ''}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n <div className=\"td-swatch-row\" role=\"radiogroup\" aria-label=\"Gemstone Selection\">\r\n {filteredOptions.map((s) => {\r\n const isSelected = activeId === s.id || value === s.hex\r\n return (\r\n <button\r\n key={s.id}\r\n type=\"button\"\r\n role=\"radio\"\r\n aria-checked={isSelected}\r\n title={`${s.label}${s.grade ? ` - ${s.grade}` : ''}`}\r\n className={`td-swatch-${variant === 'dots' ? 'dot' : 'pill'} ${isSelected ? 'active' : ''}`}\r\n onClick={() => onChange?.(s.id, s)}\r\n >\r\n <span\r\n className=\"td-swatch-color-circle\"\r\n style={{ backgroundColor: s.hex }}\r\n />\r\n {variant === 'pills' && <span>{s.label}</span>}\r\n </button>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport interface StoneSelectProps {\r\n value?: StoneColorProp\r\n onChange?: (stoneId: string, preset?: StonePreset) => void\r\n options?: StonePreset[]\r\n label?: string\r\n className?: string\r\n style?: CSSProperties\r\n}\r\n\r\nexport function StoneSelect({\r\n value,\r\n onChange,\r\n options = CANONICAL_STONE_PRESETS,\r\n label = 'Select Gemstone',\r\n className = '',\r\n style,\r\n}: StoneSelectProps) {\r\n const activePreset = value ? getStonePreset(value) : undefined\r\n const activeValue = activePreset?.id ?? value ?? options[0]?.id\r\n\r\n return (\r\n <div className={`td-select-wrapper ${className}`} style={style}>\r\n {label && <label className=\"td-swatch-title\">{label}</label>}\r\n <select\r\n className=\"td-select\"\r\n value={activeValue}\r\n onChange={(e) => {\r\n const val = e.target.value\r\n const preset = getStonePreset(val)\r\n onChange?.(val, preset)\r\n }}\r\n >\r\n {options.map((s) => (\r\n <option key={s.id} value={s.id}>\r\n {s.label} {s.grade ? `(${s.grade})` : ''}\r\n </option>\r\n ))}\r\n </select>\r\n </div>\r\n )\r\n}\r\n","import React, { useState, type CSSProperties } from 'react'\r\nimport {\r\n type MetalColorProp,\r\n type StoneColorProp,\r\n type ViewerSourceInput,\r\n type MetalPreset,\r\n type StonePreset,\r\n type PartInfo,\r\n type PartType,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\nimport { Viewer, type ViewerRef } from '../Viewer'\r\nimport { MetalSwatches, MetalSelect } from './MetalSwatches'\r\nimport { StoneSwatches, StoneSelect } from './StoneSwatches'\r\nimport './swatches.css'\r\n\r\nexport interface JewelryCustomizerProps {\r\n src?: string\r\n source?: ViewerSourceInput\r\n defaultMetal?: MetalColorProp\r\n defaultStone?: StoneColorProp\r\n metal?: MetalColorProp\r\n onMetalChange?: (metal: string, preset?: MetalPreset) => void\r\n stone?: StoneColorProp\r\n onStoneChange?: (stone: string, preset?: StonePreset) => void\r\n metalOptions?: MetalPreset[]\r\n stoneOptions?: StonePreset[]\r\n controlsVariant?: 'pills' | 'dots' | 'select' | 'none'\r\n height?: number | string\r\n autoRotate?: boolean\r\n quality?: 'low' | 'medium' | 'high'\r\n fov?: number\r\n shadows?: boolean\r\n onLoaded?: (parts: PartInfo[]) => void\r\n onPartSelected?: (info: { meshId: string; type: PartType }) => void\r\n className?: string\r\n style?: CSSProperties\r\n viewerRef?: React.Ref<ViewerRef>\r\n}\r\n\r\nexport function JewelryCustomizer({\r\n src,\r\n source,\r\n defaultMetal = 'yellow-gold-18k',\r\n defaultStone = 'diamond-d',\r\n metal: controlledMetal,\r\n onMetalChange,\r\n stone: controlledStone,\r\n onStoneChange,\r\n metalOptions,\r\n stoneOptions,\r\n controlsVariant = 'pills',\r\n height = 520,\r\n autoRotate = true,\r\n quality = 'high',\r\n fov,\r\n shadows,\r\n onLoaded,\r\n onPartSelected,\r\n className = '',\r\n style,\r\n viewerRef,\r\n}: JewelryCustomizerProps) {\r\n const [internalMetal, setInternalMetal] = useState<MetalColorProp>(defaultMetal)\r\n const [internalStone, setInternalStone] = useState<StoneColorProp>(defaultStone)\r\n\r\n const currentMetal = controlledMetal !== undefined ? controlledMetal : internalMetal\r\n const currentStone = controlledStone !== undefined ? controlledStone : internalStone\r\n\r\n const handleMetalChange = (val: string, preset?: MetalPreset) => {\r\n if (controlledMetal === undefined) setInternalMetal(val)\r\n onMetalChange?.(val, preset)\r\n }\r\n\r\n const handleStoneChange = (val: string, preset?: StonePreset) => {\r\n if (controlledStone === undefined) setInternalStone(val)\r\n onStoneChange?.(val, preset)\r\n }\r\n\r\n const viewportHeight = typeof height === 'number' ? `${height}px` : height\r\n\r\n return (\r\n <div className={`td-customizer-card ${className}`} style={style}>\r\n <div className=\"td-customizer-viewport\" style={{ height: viewportHeight }}>\r\n <Viewer\r\n ref={viewerRef}\r\n src={src}\r\n source={source}\r\n metalColor={currentMetal}\r\n stoneColor={currentStone}\r\n autoRotate={autoRotate}\r\n quality={quality}\r\n fov={fov}\r\n shadows={shadows}\r\n onLoaded={onLoaded}\r\n onPartSelected={onPartSelected}\r\n />\r\n </div>\r\n\r\n {controlsVariant !== 'none' && (\r\n <div className=\"td-customizer-controls\">\r\n {controlsVariant === 'select' ? (\r\n <>\r\n <MetalSelect\r\n value={currentMetal}\r\n onChange={handleMetalChange}\r\n options={metalOptions}\r\n />\r\n <StoneSelect\r\n value={currentStone}\r\n onChange={handleStoneChange}\r\n options={stoneOptions}\r\n />\r\n </>\r\n ) : (\r\n <>\r\n <MetalSwatches\r\n value={currentMetal}\r\n onChange={handleMetalChange}\r\n options={metalOptions}\r\n variant={controlsVariant}\r\n />\r\n <StoneSwatches\r\n value={currentStone}\r\n onChange={handleStoneChange}\r\n options={stoneOptions}\r\n variant={controlsVariant}\r\n />\r\n </>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n )\r\n}\r\n","import { useState, useMemo } from 'react'\r\nimport {\r\n CANONICAL_METAL_PRESETS,\r\n CANONICAL_STONE_PRESETS,\r\n getMetalPreset,\r\n getStonePreset,\r\n type MetalColorProp,\r\n type StoneColorProp,\r\n type MetalPreset,\r\n type StonePreset,\r\n} from '@thinkersteps_labs/3d-viewer-core'\r\n\r\nexport interface UseJewelryCustomizerOptions {\r\n defaultMetal?: MetalColorProp\r\n defaultStone?: StoneColorProp\r\n metalOptions?: MetalPreset[]\r\n stoneOptions?: StonePreset[]\r\n}\r\n\r\nexport interface UseJewelryCustomizerReturn {\r\n metal: MetalColorProp\r\n setMetal: (val: MetalColorProp) => void\r\n stone: StoneColorProp\r\n setStone: (val: StoneColorProp) => void\r\n metalPreset?: MetalPreset\r\n stonePreset?: StonePreset\r\n metalOptions: MetalPreset[]\r\n stoneOptions: StonePreset[]\r\n viewerProps: {\r\n metalColor: MetalColorProp\r\n stoneColor: StoneColorProp\r\n }\r\n}\r\n\r\nexport function useJewelryCustomizer(\r\n options: UseJewelryCustomizerOptions = {}\r\n): UseJewelryCustomizerReturn {\r\n const {\r\n defaultMetal = 'yellow-gold-18k',\r\n defaultStone = 'diamond-d',\r\n metalOptions = CANONICAL_METAL_PRESETS,\r\n stoneOptions = CANONICAL_STONE_PRESETS,\r\n } = options\r\n\r\n const [metal, setMetal] = useState<MetalColorProp>(defaultMetal)\r\n const [stone, setStone] = useState<StoneColorProp>(defaultStone)\r\n\r\n const metalPreset = useMemo(() => getMetalPreset(metal), [metal])\r\n const stonePreset = useMemo(() => getStonePreset(stone), [stone])\r\n\r\n const viewerProps = useMemo(\r\n () => ({\r\n metalColor: metal,\r\n stoneColor: stone,\r\n }),\r\n [metal, stone]\r\n )\r\n\r\n return {\r\n metal,\r\n setMetal,\r\n stone,\r\n setStone,\r\n metalPreset,\r\n stonePreset,\r\n metalOptions,\r\n stoneOptions,\r\n viewerProps,\r\n }\r\n}\r\n"],"names":["Viewer","forwardRef","props","ref","src","source","metalColor","stoneColor","materialColors","partColors","background","metalEnvUrl","gemEnvUrl","options","fov","shadows","autoRotate","quality","height","minHeight","aspectRatio","className","style","containerRef","useRef","viewerRef","cb","useImperativeHandle","useEffect","viewer","createViewer","parts","materials","_b","_a","info","state","err","progress","resolved","normalizeModelSource","ac","prevMaterialColorsRef","prev","next","key","color","prevPartColorsRef","partId","slotIdxStr","slotIndex","containerStyle","jsx","MetalSwatches","value","onChange","CANONICAL_METAL_PRESETS","category","variant","showLabel","filteredOptions","m","activePreset","getMetalPreset","activeId","jsxs","isSelected","MetalSelect","label","activeValue","e","val","preset","StoneSwatches","CANONICAL_STONE_PRESETS","s","getStonePreset","StoneSelect","JewelryCustomizer","defaultMetal","defaultStone","controlledMetal","onMetalChange","controlledStone","onStoneChange","metalOptions","stoneOptions","controlsVariant","onLoaded","onPartSelected","internalMetal","setInternalMetal","useState","internalStone","setInternalStone","currentMetal","currentStone","handleMetalChange","handleStoneChange","viewportHeight","Fragment","useJewelryCustomizer","metal","setMetal","stone","setStone","metalPreset","useMemo","stonePreset","viewerProps"],"mappings":"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"}
package/package.json CHANGED
@@ -1,46 +1,46 @@
1
- {
2
- "name": "@thinkersteps_labs/3d-viewer-react",
3
- "version": "1.0.1",
4
- "license": "MIT",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/lalitthinker/3d-jewelry-viewer.git",
8
- "directory": "packages/react"
9
- },
10
- "publishConfig": {
11
- "access": "public"
12
- },
13
- "type": "module",
14
- "main": "./dist/index.cjs",
15
- "module": "./dist/index.js",
16
- "types": "./dist/index.d.ts",
17
- "exports": {
18
- ".": {
19
- "types": "./dist/index.d.ts",
20
- "import": "./dist/index.js",
21
- "require": "./dist/index.cjs"
22
- }
23
- },
24
- "files": [
25
- "dist"
26
- ],
27
- "peerDependencies": {
28
- "react": ">=18.0.0",
29
- "three": ">=0.150.0",
30
- "@thinkersteps_labs/3d-viewer-core": "1.0.1"
31
- },
32
- "devDependencies": {
33
- "@types/node": "^20.11.0",
34
- "@types/react": "^18.2.0",
35
- "@vitejs/plugin-react": "^4.2.0",
36
- "react": "^18.2.0",
37
- "three": "^0.160.0",
38
- "vite": "^5.0.0",
39
- "vite-plugin-dts": "^3.7.0",
40
- "@thinkersteps_labs/3d-viewer-core": "1.0.1"
41
- },
42
- "scripts": {
43
- "build": "vite build",
44
- "typecheck": "tsc --noEmit"
45
- }
1
+ {
2
+ "name": "@thinkersteps_labs/3d-viewer-react",
3
+ "version": "1.2.0",
4
+ "license": "MIT",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/lalitthinker/3d-jewelry-viewer.git",
8
+ "directory": "packages/react"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "type": "module",
14
+ "main": "./dist/index.cjs",
15
+ "module": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "require": "./dist/index.cjs"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "scripts": {
28
+ "build": "vite build",
29
+ "typecheck": "tsc --noEmit"
30
+ },
31
+ "peerDependencies": {
32
+ "@thinkersteps_labs/3d-viewer-core": "workspace:*",
33
+ "react": ">=18.0.0",
34
+ "three": ">=0.150.0"
35
+ },
36
+ "devDependencies": {
37
+ "@thinkersteps_labs/3d-viewer-core": "workspace:*",
38
+ "@types/node": "^20.11.0",
39
+ "@types/react": "^18.2.0",
40
+ "@vitejs/plugin-react": "^4.2.0",
41
+ "react": "^18.2.0",
42
+ "three": "^0.160.0",
43
+ "vite": "^5.0.0",
44
+ "vite-plugin-dts": "^3.7.0"
45
+ }
46
46
  }