Skip to content

Commit 346aa3d

Browse files
committed
feat: add parent directive to add "parent" slot to a 3d object
1 parent abe5ead commit 346aa3d

File tree

5 files changed

+67
-5
lines changed

5 files changed

+67
-5
lines changed

libs/angular-three/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export * from './lib/di/destroy';
55
export * from './lib/di/ref';
66
export * from './lib/di/run-in-context';
77
export * from './lib/directives/args';
8+
export * from './lib/directives/parent';
89
export * from './lib/directives/repeat';
910
export * from './lib/loader';
1011
export * from './lib/loop';
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Directive, Input } from '@angular/core';
2+
import { NgtInjectedRef } from '../di/ref';
3+
import { NgtCommonDirective } from './common';
4+
5+
@Directive({ selector: '[parent]', standalone: true })
6+
export class NgtParent extends NgtCommonDirective {
7+
private injectedParent: string | THREE.Object3D | NgtInjectedRef<THREE.Object3D> = null!;
8+
9+
@Input() set parent(parent: string | THREE.Object3D | NgtInjectedRef<THREE.Object3D>) {
10+
if (!parent) return;
11+
this.injected = false;
12+
this.injectedParent = parent;
13+
this.createView();
14+
}
15+
16+
get parent() {
17+
if (this.validate()) {
18+
this.injected = true;
19+
return this.injectedParent;
20+
}
21+
return null!;
22+
}
23+
24+
validate(): boolean {
25+
return !this.injected && !!this.injectedParent;
26+
}
27+
}

libs/angular-three/src/lib/renderer/enums.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const enum NgtRendererClassId {
22
type,
33
parent,
4+
injectedParent,
45
children,
56
destroyed,
67
compound,

libs/angular-three/src/lib/renderer/renderer.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import {
88
RendererFactory2,
99
RendererType2,
1010
} from '@angular/core';
11+
import { take } from 'rxjs';
1112
import { NGT_CATALOGUE } from '../di/catalogue';
1213
import { NgtStore } from '../stores/store';
1314
import { NgtAnyRecord } from '../types';
1415
import { getLocalState, prepare } from '../utils/instance';
1516
import { is } from '../utils/is';
1617
import { NGT_COMPOUND_PREFIXES } from './di';
1718
import { NgtRendererClassId } from './enums';
18-
import { NgtRendererNode, NgtRendererStore } from './state';
19+
import { NgtRendererNode, NgtRendererState, NgtRendererStore } from './state';
1920
import { attachThreeChild, kebabToPascal, processThreeEvent, removeThreeChild, SPECIAL_DOM_TAG } from './utils';
2021

2122
@Injectable()
@@ -99,7 +100,14 @@ export class NgtRenderer implements Renderer2 {
99100
);
100101
}
101102

102-
const { injectedArgs, store } = this.store.getCreationState();
103+
const { injectedArgs, injectedParent, store } = this.store.getCreationState();
104+
105+
let parent = injectedParent as NgtRendererState[NgtRendererClassId.injectedParent];
106+
if (typeof injectedParent === 'string') {
107+
parent = store
108+
.get('scene')
109+
.getObjectByName(injectedParent) as unknown as NgtRendererState[NgtRendererClassId.injectedParent];
110+
}
103111

104112
// handle primitive
105113
if (name === SPECIAL_DOM_TAG.NGT_PRIMITIVE) {
@@ -111,7 +119,11 @@ export class NgtRenderer implements Renderer2 {
111119
localState = getLocalState(object);
112120
}
113121
if (!localState.store) localState.store = store;
114-
return this.store.createNode('three', object);
122+
const node = this.store.createNode('three', object);
123+
if (parent) {
124+
node.__ngt_renderer__[NgtRendererClassId.injectedParent] = parent;
125+
}
126+
return node;
115127
}
116128

117129
const threeTag = name.startsWith('ngt') ? name.slice(4) : name;
@@ -127,6 +139,11 @@ export class NgtRenderer implements Renderer2 {
127139
} else if (is.material(instance)) {
128140
localState.attach = ['material'];
129141
}
142+
143+
if (parent) {
144+
node.__ngt_renderer__[NgtRendererClassId.injectedParent] = parent;
145+
}
146+
130147
return node;
131148
}
132149

@@ -148,6 +165,17 @@ export class NgtRenderer implements Renderer2 {
148165
return;
149166
}
150167

168+
if (cRS[NgtRendererClassId.injectedParent]) {
169+
if (is.ref(cRS[NgtRendererClassId.injectedParent])) {
170+
cRS[NgtRendererClassId.injectedParent].$.pipe(take(1)).subscribe((val) => {
171+
this.appendChild(val, newChild);
172+
});
173+
} else {
174+
this.appendChild(cRS[NgtRendererClassId.injectedParent], newChild);
175+
}
176+
return;
177+
}
178+
151179
this.store.setParent(newChild, parent);
152180
this.store.addChild(parent, newChild);
153181

@@ -234,7 +262,7 @@ export class NgtRenderer implements Renderer2 {
234262
// refChild: NgtRendererNode
235263
// isMove?: boolean | undefined
236264
): void {
237-
if (!parent.__ngt_renderer__ || parent === newChild) return;
265+
if (parent == null || !parent.__ngt_renderer__ || parent === newChild) return;
238266
this.appendChild(parent, newChild);
239267
}
240268

libs/angular-three/src/lib/renderer/state.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { ChangeDetectorRef, getDebugNode, Injector, Type } from '@angular/core';
2+
import { NgtInjectedRef } from '../di/ref';
23
import { NgtArgs } from '../directives/args';
34
import { NgtCommonDirective } from '../directives/common';
5+
import { NgtParent } from '../directives/parent';
46
import { NgtStore } from '../stores/store';
57
import type { NgtAnyRecord } from '../types';
68
import { applyProps } from '../utils/apply-props';
@@ -22,6 +24,7 @@ export type NgtQueueOp = [type: 'op' | 'cleanUp', op: () => void, done?: true];
2224
export type NgtRendererState = [
2325
type: 'three' | 'compound' | 'portal' | 'comment' | 'dom',
2426
parent: NgtRendererNode | null,
27+
injectedParent: NgtRendererNode | NgtInjectedRef<NgtRendererNode> | null,
2528
children: NgtRendererNode[],
2629
destroyed: boolean,
2730
compound: [applyFirst: boolean, props: Record<string, any>],
@@ -49,6 +52,7 @@ export class NgtRendererStore {
4952
const state = [
5053
type,
5154
null,
55+
null,
5256
[],
5357
false,
5458
undefined!,
@@ -326,8 +330,9 @@ export class NgtRendererStore {
326330

327331
getCreationState() {
328332
const injectedArgs = this.firstNonInjectedDirective(NgtArgs)?.args || [];
333+
const injectedParent = this.firstNonInjectedDirective(NgtParent)?.parent || null;
329334
const store = this.tryGetPortalStore();
330-
return { injectedArgs, store };
335+
return { injectedArgs, injectedParent, store };
331336
}
332337

333338
destroy(node: NgtRendererNode, parent?: NgtRendererNode) {

0 commit comments

Comments
 (0)