@thinkersteps_labs/3d-viewer-angular 1.0.0 → 1.1.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-angular
2
-
3
- Official Angular component wrapper for `@thinkersteps_labs/3d-viewer-core`. Renders photorealistic 3D jewelry and customizer canvases in Angular 18+ applications (including standalone components and legacy `NgModule` architectures).
4
-
5
- ---
6
-
7
- ## Installation
8
-
9
- ```bash
10
- pnpm add @thinkersteps_labs/3d-viewer-angular @thinkersteps_labs/3d-viewer-core three
11
- # or with npm
12
- npm install @thinkersteps_labs/3d-viewer-angular @thinkersteps_labs/3d-viewer-core three
13
- ```
14
-
15
- > **Peer Dependencies**: Requires `@angular/core >= 18.0.0` and `three >= 0.150.0`.
16
-
17
- ---
18
-
19
- ## 1. Turnkey Customizer (`<td-jewelry-customizer>`)
20
-
21
- ```typescript
22
- import { Component } from '@angular/core'
23
- import { JewelryCustomizerComponent } from '@thinkersteps_labs/3d-viewer-angular'
24
-
25
- @Component({
26
- selector: 'app-product-page',
27
- standalone: true,
28
- imports: [JewelryCustomizerComponent],
29
- template: `
30
- <td-jewelry-customizer
31
- [src]="'https://cdn.example.com/models/solitaire-ring.glb'"
32
- [(metal)]="activeMetal"
33
- [(stone)]="activeStone"
34
- [height]="520"
35
- [autoRotate]="true"
36
- ></td-jewelry-customizer>
37
- `
38
- })
39
- export class ProductPageComponent {
40
- activeMetal = 'yellow-gold-18k'
41
- activeStone = 'diamond-d'
42
- }
43
- ```
44
-
45
- ---
46
-
47
- ## 2. Composable Primitives (`<td-viewer>` + Swatches)
48
-
49
- ```typescript
50
- import { Component } from '@angular/core'
51
- import {
52
- ViewerComponent,
53
- MetalSwatchesComponent,
54
- StoneSwatchesComponent,
55
- type MetalColorProp,
56
- type StoneColorProp,
57
- } from '@thinkersteps_labs/3d-viewer-angular'
58
-
59
- @Component({
60
- selector: 'app-product-detail',
61
- standalone: true,
62
- imports: [ViewerComponent, MetalSwatchesComponent, StoneSwatchesComponent],
63
- template: `
64
- <div style="max-width: 840px; margin: 0 auto;">
65
- <td-viewer
66
- [src]="modelUrl"
67
- [metalColor]="activeMetal"
68
- [stoneColor]="activeStone"
69
- [height]="500"
70
- [autoRotate]="true"
71
- ></td-viewer>
72
-
73
- <div style="margin-top: 16px; display: flex; flex-direction: column; gap: 14px;">
74
- <td-metal-swatches [(value)]="activeMetal" [variant]="'pills'"></td-metal-swatches>
75
- <td-stone-swatches [(value)]="activeStone" [variant]="'pills'"></td-stone-swatches>
76
- </div>
77
- </div>
78
- `
79
- })
80
- export class ProductDetailComponent {
81
- modelUrl = 'https://cdn.example.com/models/solitaire-ring.glb'
82
- activeMetal: MetalColorProp = 'yellow-gold-18k'
83
- activeStone: StoneColorProp = 'diamond-d'
84
- }
85
- ```
86
-
87
- ---
88
-
89
- ## 3. NgModule Apps (e.g. Angular ERP)
90
-
91
- Import `ViewerModule` in your feature module to get access to `<td-viewer>`, `<td-metal-swatches>`, `<td-stone-swatches>`, and `<td-jewelry-customizer>`:
92
-
93
- ```typescript
94
- import { NgModule } from '@angular/core'
95
- import { ViewerModule } from '@thinkersteps_labs/3d-viewer-angular'
96
-
97
- @NgModule({
98
- imports: [ViewerModule],
99
- })
100
- export class ProductModule {}
101
- ```
102
-
103
- ---
104
-
105
- ## License
106
-
107
- MIT
1
+ # @thinkersteps_labs/3d-viewer-angular
2
+
3
+ Official Angular component wrapper for `@thinkersteps_labs/3d-viewer-core`. Renders photorealistic 3D jewelry and customizer canvases in Angular 18+ applications (including standalone components and legacy `NgModule` architectures).
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pnpm add @thinkersteps_labs/3d-viewer-angular @thinkersteps_labs/3d-viewer-core three
11
+ # or with npm
12
+ npm install @thinkersteps_labs/3d-viewer-angular @thinkersteps_labs/3d-viewer-core three
13
+ ```
14
+
15
+ > **Peer Dependencies**: Requires `@angular/core >= 18.0.0` and `three >= 0.150.0`.
16
+
17
+ ---
18
+
19
+ ## 1. Turnkey Customizer (`<td-jewelry-customizer>`)
20
+
21
+ ```typescript
22
+ import { Component } from '@angular/core'
23
+ import { JewelryCustomizerComponent } from '@thinkersteps_labs/3d-viewer-angular'
24
+
25
+ @Component({
26
+ selector: 'app-product-page',
27
+ standalone: true,
28
+ imports: [JewelryCustomizerComponent],
29
+ template: `
30
+ <td-jewelry-customizer
31
+ [src]="'https://cdn.example.com/models/solitaire-ring.glb'"
32
+ [(metal)]="activeMetal"
33
+ [(stone)]="activeStone"
34
+ [height]="520"
35
+ [autoRotate]="true"
36
+ ></td-jewelry-customizer>
37
+ `
38
+ })
39
+ export class ProductPageComponent {
40
+ activeMetal = 'yellow-gold-18k'
41
+ activeStone = 'diamond-d'
42
+ }
43
+ ```
44
+
45
+ ---
46
+
47
+ ## 2. Composable Primitives (`<td-viewer>` + Swatches)
48
+
49
+ ```typescript
50
+ import { Component } from '@angular/core'
51
+ import {
52
+ ViewerComponent,
53
+ MetalSwatchesComponent,
54
+ StoneSwatchesComponent,
55
+ type MetalColorProp,
56
+ type StoneColorProp,
57
+ } from '@thinkersteps_labs/3d-viewer-angular'
58
+
59
+ @Component({
60
+ selector: 'app-product-detail',
61
+ standalone: true,
62
+ imports: [ViewerComponent, MetalSwatchesComponent, StoneSwatchesComponent],
63
+ template: `
64
+ <div style="max-width: 840px; margin: 0 auto;">
65
+ <td-viewer
66
+ [src]="modelUrl"
67
+ [metalColor]="activeMetal"
68
+ [stoneColor]="activeStone"
69
+ [height]="500"
70
+ [autoRotate]="true"
71
+ ></td-viewer>
72
+
73
+ <div style="margin-top: 16px; display: flex; flex-direction: column; gap: 14px;">
74
+ <td-metal-swatches [(value)]="activeMetal" [variant]="'pills'"></td-metal-swatches>
75
+ <td-stone-swatches [(value)]="activeStone" [variant]="'pills'"></td-stone-swatches>
76
+ </div>
77
+ </div>
78
+ `
79
+ })
80
+ export class ProductDetailComponent {
81
+ modelUrl = 'https://cdn.example.com/models/solitaire-ring.glb'
82
+ activeMetal: MetalColorProp = 'yellow-gold-18k'
83
+ activeStone: StoneColorProp = 'diamond-d'
84
+ }
85
+ ```
86
+
87
+ ---
88
+
89
+ ## 3. NgModule Apps (e.g. Angular ERP)
90
+
91
+ Import `ViewerModule` in your feature module to get access to `<td-viewer>`, `<td-metal-swatches>`, `<td-stone-swatches>`, and `<td-jewelry-customizer>`:
92
+
93
+ ```typescript
94
+ import { NgModule } from '@angular/core'
95
+ import { ViewerModule } from '@thinkersteps_labs/3d-viewer-angular'
96
+
97
+ @NgModule({
98
+ imports: [ViewerModule],
99
+ })
100
+ export class ProductModule {}
101
+ ```
102
+
103
+ ---
104
+
105
+ ## License
106
+
107
+ MIT
@@ -6,33 +6,33 @@ import { MetalSwatchesComponent, MetalSelectComponent } from './metal-swatches.c
6
6
  import { StoneSwatchesComponent, StoneSelectComponent } from './stone-swatches.component';
7
7
  import * as i0 from "@angular/core";
8
8
  import * as i1 from "@angular/common";
9
- const CUSTOMIZER_STYLES = `
10
- :host {
11
- display: block;
12
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
13
- }
14
- .td-customizer-card {
15
- display: flex;
16
- flex-direction: column;
17
- gap: 16px;
18
- background: #ffffff;
19
- border-radius: 16px;
20
- border: 1px solid #e2e8f0;
21
- padding: 16px;
22
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
23
- }
24
- .td-customizer-viewport {
25
- position: relative;
26
- width: 100%;
27
- border-radius: 12px;
28
- overflow: hidden;
29
- background: #f8fafc;
30
- }
31
- .td-customizer-controls {
32
- display: flex;
33
- flex-direction: column;
34
- gap: 14px;
35
- }
9
+ const CUSTOMIZER_STYLES = `
10
+ :host {
11
+ display: block;
12
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
13
+ }
14
+ .td-customizer-card {
15
+ display: flex;
16
+ flex-direction: column;
17
+ gap: 16px;
18
+ background: #ffffff;
19
+ border-radius: 16px;
20
+ border: 1px solid #e2e8f0;
21
+ padding: 16px;
22
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -2px rgba(0, 0, 0, 0.05);
23
+ }
24
+ .td-customizer-viewport {
25
+ position: relative;
26
+ width: 100%;
27
+ border-radius: 12px;
28
+ overflow: hidden;
29
+ background: #f8fafc;
30
+ }
31
+ .td-customizer-controls {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 14px;
35
+ }
36
36
  `;
37
37
  export class JewelryCustomizerComponent {
38
38
  constructor() {
@@ -64,51 +64,51 @@ export class JewelryCustomizerComponent {
64
64
  this.stoneChange.emit(val);
65
65
  }
66
66
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: JewelryCustomizerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
67
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: JewelryCustomizerComponent, isStandalone: true, selector: "td-jewelry-customizer", inputs: { src: "src", source: "source", metal: "metal", stone: "stone", height: "height", autoRotate: "autoRotate", quality: "quality", controlsVariant: "controlsVariant", metalOptions: "metalOptions", stoneOptions: "stoneOptions" }, outputs: { metalChange: "metalChange", stoneChange: "stoneChange", loaded: "loaded", partSelected: "partSelected" }, ngImport: i0, template: `
68
- <div class="td-customizer-card">
69
- <div class="td-customizer-viewport" [style.height]="formattedHeight">
70
- <td-viewer
71
- [src]="src"
72
- [source]="source"
73
- [metalColor]="metal"
74
- [stoneColor]="stone"
75
- [autoRotate]="autoRotate"
76
- [quality]="quality"
77
- (loaded)="loaded.emit($event)"
78
- (partSelected)="partSelected.emit($event)"
79
- ></td-viewer>
80
- </div>
81
-
82
- <div class="td-customizer-controls" *ngIf="controlsVariant !== 'none'">
83
- <ng-container *ngIf="controlsVariant === 'select'; else buttonSwatches">
84
- <td-metal-select
85
- [value]="metal"
86
- (valueChange)="onMetalChange($event)"
87
- [options]="metalOptions"
88
- ></td-metal-select>
89
- <td-stone-select
90
- [value]="stone"
91
- (valueChange)="onStoneChange($event)"
92
- [options]="stoneOptions"
93
- ></td-stone-select>
94
- </ng-container>
95
-
96
- <ng-template #buttonSwatches>
97
- <td-metal-swatches
98
- [value]="metal"
99
- (valueChange)="onMetalChange($event)"
100
- [options]="metalOptions"
101
- [variant]="buttonVariant"
102
- ></td-metal-swatches>
103
- <td-stone-swatches
104
- [value]="stone"
105
- (valueChange)="onStoneChange($event)"
106
- [options]="stoneOptions"
107
- [variant]="buttonVariant"
108
- ></td-stone-swatches>
109
- </ng-template>
110
- </div>
111
- </div>
67
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: JewelryCustomizerComponent, isStandalone: true, selector: "td-jewelry-customizer", inputs: { src: "src", source: "source", metal: "metal", stone: "stone", height: "height", autoRotate: "autoRotate", quality: "quality", controlsVariant: "controlsVariant", metalOptions: "metalOptions", stoneOptions: "stoneOptions" }, outputs: { metalChange: "metalChange", stoneChange: "stoneChange", loaded: "loaded", partSelected: "partSelected" }, ngImport: i0, template: `
68
+ <div class="td-customizer-card">
69
+ <div class="td-customizer-viewport" [style.height]="formattedHeight">
70
+ <td-viewer
71
+ [src]="src"
72
+ [source]="source"
73
+ [metalColor]="metal"
74
+ [stoneColor]="stone"
75
+ [autoRotate]="autoRotate"
76
+ [quality]="quality"
77
+ (loaded)="loaded.emit($event)"
78
+ (partSelected)="partSelected.emit($event)"
79
+ ></td-viewer>
80
+ </div>
81
+
82
+ <div class="td-customizer-controls" *ngIf="controlsVariant !== 'none'">
83
+ <ng-container *ngIf="controlsVariant === 'select'; else buttonSwatches">
84
+ <td-metal-select
85
+ [value]="metal"
86
+ (valueChange)="onMetalChange($event)"
87
+ [options]="metalOptions"
88
+ ></td-metal-select>
89
+ <td-stone-select
90
+ [value]="stone"
91
+ (valueChange)="onStoneChange($event)"
92
+ [options]="stoneOptions"
93
+ ></td-stone-select>
94
+ </ng-container>
95
+
96
+ <ng-template #buttonSwatches>
97
+ <td-metal-swatches
98
+ [value]="metal"
99
+ (valueChange)="onMetalChange($event)"
100
+ [options]="metalOptions"
101
+ [variant]="buttonVariant"
102
+ ></td-metal-swatches>
103
+ <td-stone-swatches
104
+ [value]="stone"
105
+ (valueChange)="onStoneChange($event)"
106
+ [options]="stoneOptions"
107
+ [variant]="buttonVariant"
108
+ ></td-stone-swatches>
109
+ </ng-template>
110
+ </div>
111
+ </div>
112
112
  `, isInline: true, styles: ["\n :host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif;\n }\n .td-customizer-card {\n display: flex;\n flex-direction: column;\n gap: 16px;\n background: #ffffff;\n border-radius: 16px;\n border: 1px solid #e2e8f0;\n padding: 16px;\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -2px rgba(0, 0, 0, 0.05);\n }\n .td-customizer-viewport {\n position: relative;\n width: 100%;\n border-radius: 12px;\n overflow: hidden;\n background: #f8fafc;\n }\n .td-customizer-controls {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: ViewerComponent, selector: "td-viewer", inputs: ["src", "source", "metalColor", "stoneColor", "materialColors", "partColors", "height", "minHeight", "background", "metalEnvUrl", "gemEnvUrl", "fov", "shadows", "options", "autoRotate", "quality", "state"], outputs: ["loaded", "modelLoaded", "progress", "partSelected", "error", "change"] }, { kind: "component", type: MetalSwatchesComponent, selector: "td-metal-swatches", inputs: ["value", "options", "category", "variant", "showLabel"], outputs: ["valueChange"] }, { kind: "component", type: MetalSelectComponent, selector: "td-metal-select", inputs: ["value", "options", "label"], outputs: ["valueChange"] }, { kind: "component", type: StoneSwatchesComponent, selector: "td-stone-swatches", inputs: ["value", "options", "category", "variant", "showLabel"], outputs: ["valueChange"] }, { kind: "component", type: StoneSelectComponent, selector: "td-stone-select", inputs: ["value", "options", "label"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
113
113
  }
114
114
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: JewelryCustomizerComponent, decorators: [{
@@ -120,51 +120,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
120
120
  MetalSelectComponent,
121
121
  StoneSwatchesComponent,
122
122
  StoneSelectComponent,
123
- ], template: `
124
- <div class="td-customizer-card">
125
- <div class="td-customizer-viewport" [style.height]="formattedHeight">
126
- <td-viewer
127
- [src]="src"
128
- [source]="source"
129
- [metalColor]="metal"
130
- [stoneColor]="stone"
131
- [autoRotate]="autoRotate"
132
- [quality]="quality"
133
- (loaded)="loaded.emit($event)"
134
- (partSelected)="partSelected.emit($event)"
135
- ></td-viewer>
136
- </div>
137
-
138
- <div class="td-customizer-controls" *ngIf="controlsVariant !== 'none'">
139
- <ng-container *ngIf="controlsVariant === 'select'; else buttonSwatches">
140
- <td-metal-select
141
- [value]="metal"
142
- (valueChange)="onMetalChange($event)"
143
- [options]="metalOptions"
144
- ></td-metal-select>
145
- <td-stone-select
146
- [value]="stone"
147
- (valueChange)="onStoneChange($event)"
148
- [options]="stoneOptions"
149
- ></td-stone-select>
150
- </ng-container>
151
-
152
- <ng-template #buttonSwatches>
153
- <td-metal-swatches
154
- [value]="metal"
155
- (valueChange)="onMetalChange($event)"
156
- [options]="metalOptions"
157
- [variant]="buttonVariant"
158
- ></td-metal-swatches>
159
- <td-stone-swatches
160
- [value]="stone"
161
- (valueChange)="onStoneChange($event)"
162
- [options]="stoneOptions"
163
- [variant]="buttonVariant"
164
- ></td-stone-swatches>
165
- </ng-template>
166
- </div>
167
- </div>
123
+ ], template: `
124
+ <div class="td-customizer-card">
125
+ <div class="td-customizer-viewport" [style.height]="formattedHeight">
126
+ <td-viewer
127
+ [src]="src"
128
+ [source]="source"
129
+ [metalColor]="metal"
130
+ [stoneColor]="stone"
131
+ [autoRotate]="autoRotate"
132
+ [quality]="quality"
133
+ (loaded)="loaded.emit($event)"
134
+ (partSelected)="partSelected.emit($event)"
135
+ ></td-viewer>
136
+ </div>
137
+
138
+ <div class="td-customizer-controls" *ngIf="controlsVariant !== 'none'">
139
+ <ng-container *ngIf="controlsVariant === 'select'; else buttonSwatches">
140
+ <td-metal-select
141
+ [value]="metal"
142
+ (valueChange)="onMetalChange($event)"
143
+ [options]="metalOptions"
144
+ ></td-metal-select>
145
+ <td-stone-select
146
+ [value]="stone"
147
+ (valueChange)="onStoneChange($event)"
148
+ [options]="stoneOptions"
149
+ ></td-stone-select>
150
+ </ng-container>
151
+
152
+ <ng-template #buttonSwatches>
153
+ <td-metal-swatches
154
+ [value]="metal"
155
+ (valueChange)="onMetalChange($event)"
156
+ [options]="metalOptions"
157
+ [variant]="buttonVariant"
158
+ ></td-metal-swatches>
159
+ <td-stone-swatches
160
+ [value]="stone"
161
+ (valueChange)="onStoneChange($event)"
162
+ [options]="stoneOptions"
163
+ [variant]="buttonVariant"
164
+ ></td-stone-swatches>
165
+ </ng-template>
166
+ </div>
167
+ </div>
168
168
  `, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["\n :host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif;\n }\n .td-customizer-card {\n display: flex;\n flex-direction: column;\n gap: 16px;\n background: #ffffff;\n border-radius: 16px;\n border: 1px solid #e2e8f0;\n padding: 16px;\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -2px rgba(0, 0, 0, 0.05);\n }\n .td-customizer-viewport {\n position: relative;\n width: 100%;\n border-radius: 12px;\n overflow: hidden;\n background: #f8fafc;\n }\n .td-customizer-controls {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n"] }]
169
169
  }], propDecorators: { src: [{
170
170
  type: Input