Skip to content

Commit c6c0977

Browse files
committed
example
1 parent 197104d commit c6c0977

File tree

9 files changed

+14343
-9
lines changed

9 files changed

+14343
-9
lines changed

libs/soba/.storybook/public/soba/fonts/Inter_Bold.json

Lines changed: 14108 additions & 0 deletions
Large diffs are not rendered by default.

libs/soba/.storybook/public/soba/fonts/helvetiker_regular.typeface.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

libs/soba/abstractions/src/text-3d/text-3d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class NgtsText3D {
138138

139139
geometryArgs = computed(() => {
140140
const fontData = this.fontData();
141-
if (!fontData) return null;
141+
if (!fontData || !fontData.data) return null;
142142

143143
return [
144144
this.text(),

libs/soba/misc/src/decal/decal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export class NgtsDecal {
144144
this.position(),
145145
this.rotation(),
146146
this.scale(),
147-
this.helperRef.untracked,
147+
this.helperRef.nativeElement,
148148
];
149149

150150
const parent = mesh ? (is.ref(mesh) ? mesh.nativeElement : mesh) : decal.parent;

libs/soba/misc/src/example/example.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { NgIf } from '@angular/common';
2+
import { CUSTOM_ELEMENTS_SCHEMA, Component, Input, computed, forwardRef, signal } from '@angular/core';
3+
import { createInjectionToken, extend, injectNgtRef, signalStore } from 'angular-three';
4+
import { NgtsText3D } from 'angular-three-soba/abstractions';
5+
import { NgtsCenter } from 'angular-three-soba/staging';
6+
import { Group, MeshNormalMaterial, MeshStandardMaterial } from 'three';
7+
8+
/**
9+
* define states of a component. usually acts for inputs signalStore
10+
*/
11+
export type NgtsExampleState = {
12+
font: string;
13+
color: THREE.ColorRepresentation;
14+
debug: boolean;
15+
bevelSize: number;
16+
};
17+
18+
/**
19+
* augment HTMLElementTagNameMap with the selector of the component.
20+
* the type is usually the Inputs' state and the root THREE element
21+
22+
declare global {
23+
interface HTMLElementTagNameMap {
24+
\/**
25+
* @extends ngt-group
26+
*\/
27+
'ngts-example': NgtsExampleState & NgtGroup;
28+
}
29+
}
30+
31+
*/
32+
33+
/**
34+
* make sure soba's component extends all regular THREE entities that it needs
35+
*/
36+
extend({ Group, MeshNormalMaterial, MeshStandardMaterial });
37+
38+
/**
39+
* We can setup public API for this soba component with createInjectionToken + forwardRef
40+
* the `example.api` is **usually** a computed property in the component class
41+
*
42+
* @see Bounds for example
43+
*/
44+
export const [injectNgtsExampleApi, provideNgtsExampleApi] = createInjectionToken(
45+
(example: NgtsExample) => example.api,
46+
{ isRoot: false, deps: [forwardRef(() => NgtsExample)] },
47+
);
48+
49+
@Component({
50+
selector: 'ngts-example',
51+
standalone: true,
52+
template: `
53+
<!-- ngtCompound is used by the Renderer to spread props from ngts-example down to ngt-group -->
54+
<!-- i.e: <ngts-example [position]="[1, 1, 1]" />, [1, 1, 1] will be passed down to ngt-group -->
55+
<!-- [ref] is used with the Input so that the consumer can pass an external ref and control this internal ngt-group -->
56+
<ngt-group ngtCompound [ref]="exampleRef">
57+
<ngts-center [top]="true">
58+
<ngts-text-3d
59+
[bevelEnabled]="true"
60+
[bevelSize]="bevelSize()"
61+
[font]="font()"
62+
[text]="count().toString()"
63+
>
64+
<ngt-mesh-standard-material *ngIf="!debug(); else withDebug" [color]="color()" />
65+
<ng-template #withDebug>
66+
<ngt-mesh-normal-material [wireframe]="true" />
67+
</ng-template>
68+
</ngts-text-3d>
69+
</ngts-center>
70+
<!-- use Content Projection here so consumers can pass in children for ngt-group -->
71+
<ng-content />
72+
</ngt-group>
73+
`,
74+
/**
75+
* can definitely use other Soba components
76+
*/
77+
imports: [NgtsCenter, NgtsText3D, NgIf],
78+
/**
79+
* call the API provider here to actually provide the API for the component's children
80+
*/
81+
providers: [provideNgtsExampleApi()],
82+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
83+
})
84+
/**
85+
* @description this component is only for documentation purposes.
86+
*/
87+
export class NgtsExample {
88+
/**
89+
* use signalStore to store inputs with default inputs
90+
*/
91+
private inputs = signalStore<NgtsExampleState>({ color: '#cbcbcb', bevelSize: 0.04, debug: false });
92+
93+
/**
94+
* a soba component usually has a Input for the component ref and this Input always has a default value with injectNgtRef
95+
*/
96+
@Input() exampleRef = injectNgtRef<Group>();
97+
98+
/**
99+
* setup Input for the Inputs state. Use alias to have an easier time to setup computed
100+
*
101+
* @example: this way "private font" and "set _font" won't conflict
102+
* private font = this.inputs.select('font');
103+
*/
104+
@Input({ alias: 'font', required: true }) set _font(font: string) {
105+
this.inputs.set({ font });
106+
}
107+
108+
@Input({ alias: 'color' }) set _color(color: THREE.ColorRepresentation) {
109+
this.inputs.set({ color });
110+
}
111+
112+
@Input({ alias: 'debug' }) set _debug(debug: boolean) {
113+
this.inputs.set({ debug });
114+
}
115+
116+
@Input({ alias: 'bevelSize' }) set _bevelSize(bevelSize: number) {
117+
this.inputs.set({ bevelSize });
118+
}
119+
120+
/**
121+
* exposes signals from inputs
122+
*/
123+
bevelSize = this.inputs.select('bevelSize');
124+
font = this.inputs.select('font');
125+
debug = this.inputs.select('debug');
126+
color = this.inputs.select('color');
127+
128+
/**
129+
* can have internal state
130+
*/
131+
count = signal(0);
132+
133+
/**
134+
* Expose the public API for this soba component
135+
*/
136+
api = computed(() => ({
137+
bevelSize: this.bevelSize(),
138+
increment: () => this.count.update((v) => v + 1),
139+
decrement: () => this.count.update((v) => v - 1),
140+
}));
141+
}

libs/soba/misc/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ export * from './animations/animations';
22
export * from './bake-shadows/bake-shadows';
33
export * from './decal/decal';
44
export * from './depth-buffer/depth-buffer';
5+
export * from './example/example';
56
export * from './fbo/fbo';
67
export * from './sampler/sampler';

libs/soba/src/misc/decal.stories.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,13 @@ class LoopOverInstancedBufferAttribute {
6363
6464
<decal-loop-over-instanced-buffer-attribute [buffer]="buffer()">
6565
<ng-template let-a="attribute">
66-
<ngts-decal [mesh]="ref" [position]="a.position" [rotation]="a.rotation" [scale]="a.scale">
66+
<ngts-decal
67+
[mesh]="ref"
68+
[position]="a.position"
69+
[rotation]="a.rotation"
70+
[scale]="a.scale"
71+
[debug]="debug"
72+
>
6773
<ngt-mesh-physical-material
6874
[roughness]="0.2"
6975
[transparent]="true"
@@ -85,6 +91,8 @@ class DefaultDecalStory {
8591

8692
ref = injectNgtRef<THREE.Mesh>();
8793

94+
@Input() debug = false;
95+
8896
private transformFn: NgtsSurfaceSamplerTransformFn = ({ dummy, position, normal }) => {
8997
const p = new THREE.Vector3();
9098
p.copy(position);
@@ -118,4 +126,7 @@ export default {
118126

119127
export const Default = makeStoryObject(DefaultDecalStory, {
120128
canvasOptions: { camera: { position: [0, 0, 5] } },
129+
argsOptions: {
130+
debug: false,
131+
},
121132
});

libs/soba/src/misc/example.stories.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component, Input } from '@angular/core';
2+
import { NgtArgs, type NgtThreeEvent } from 'angular-three';
3+
import { NgtsExample } from 'angular-three-soba/misc';
4+
import { color, makeDecorators, makeStoryObject, number, select } from '../setup-canvas';
5+
6+
@Component({
7+
standalone: true,
8+
template: `
9+
<ngt-color *args="['#303030']" attach="background" />
10+
<ngt-axes-helper />
11+
12+
<ngts-example
13+
#example
14+
[font]="fontUrl"
15+
[color]="color"
16+
[bevelSize]="bevelSize"
17+
[debug]="debug"
18+
(click)="onClick($event, example)"
19+
/>
20+
`,
21+
imports: [NgtsExample, NgtArgs],
22+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
23+
})
24+
class DefaultExampleStory {
25+
@Input() fontUrl = '/soba/fonts/Inter_Bold.json';
26+
@Input() bevelSize?: number;
27+
@Input() color = '#cbcbcb';
28+
@Input() debug = false;
29+
30+
/**
31+
* pointer events are also passed through to the underlying ngt-group
32+
*/
33+
onClick(event: NgtThreeEvent<PointerEvent>, example: NgtsExample) {
34+
/**
35+
* we can call the api here or we can injectNgtsExampleApi for ngts-example's children
36+
*/
37+
if (event.metaKey) {
38+
example.api().decrement();
39+
} else {
40+
example.api().increment();
41+
}
42+
}
43+
}
44+
45+
/**
46+
* always start with a default export with title and decorators: makeDecorators()
47+
* NOTE: we cannot abstract this default export due to Storybook
48+
*/
49+
export default {
50+
title: 'Misc/Example',
51+
decorators: makeDecorators(),
52+
};
53+
54+
/**
55+
* makeStoryObject is an abstraction to easily create a story with argsOptions (args + argsType)
56+
* Alternatively, we can also use makeStoryFunction if we don't need args
57+
*/
58+
export const Default = makeStoryObject(DefaultExampleStory, {
59+
/**
60+
* these get passed in <ngt-canvas />
61+
*/
62+
canvasOptions: { camera: { position: [1, 2, 4], fov: 60 } },
63+
/**
64+
* argsOptions, should match story's Inputs. Use helpers like number(), color(), and select()
65+
*/
66+
argsOptions: {
67+
fontUrl: select('/soba/fonts/Inter_Bold.json', {
68+
options: ['/soba/fonts/Inter_Bold.json', '/soba/fonts/helvetiker_regular.typeface.json'],
69+
}),
70+
bevelSize: number(0.05, { range: true, min: 0, max: 0.1, step: 0.01 }),
71+
color: color('#cbcbcb'),
72+
debug: false,
73+
},
74+
});

libs/soba/src/setup-canvas.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,10 @@ export function makeCanvasOptions(options: DeepPartial<CanvasOptions> = {}) {
216216
}
217217

218218
export function makeStoryFunction(story: Type<unknown>, canvasOptions: DeepPartial<CanvasOptions> = {}) {
219-
return (args: Args) => {
220-
return {
221-
props: { options: makeCanvasOptions(canvasOptions), inputs: args || {}, story },
222-
template: `<storybook-setup [story]="story" [inputs]="inputs" [options]="options" />`,
223-
};
224-
};
219+
return (args: Args) => ({
220+
props: { options: makeCanvasOptions(canvasOptions), inputs: args || {}, story },
221+
template: `<storybook-setup [story]="story" [inputs]="inputs" [options]="options" />`,
222+
});
225223
}
226224

227225
export function makeStoryObject(

0 commit comments

Comments
 (0)