Skip to content

Commit a8176a5

Browse files
committed
feat: add renderer
1 parent 9d584c3 commit a8176a5

File tree

5 files changed

+228
-0
lines changed

5 files changed

+228
-0
lines changed

libs/angular-three/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './lib/canvas';

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Component } from '@angular/core';
2+
3+
@Component({
4+
selector: 'ngt-canvas',
5+
standalone: true,
6+
template: `text`,
7+
})
8+
export class NgtCanvas {}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export const enum NgtRendererClassId {
2+
type,
3+
parent,
4+
children,
5+
removed,
6+
compound,
7+
compoundParent,
8+
compounded,
9+
queueOps,
10+
attributes,
11+
properties,
12+
rawValue,
13+
ref,
14+
portalContainer,
15+
}
16+
17+
export const enum NgtCompoundClassId {
18+
applyFirst,
19+
props,
20+
}
21+
22+
export const enum NgtQueueOpClassId {
23+
type,
24+
op,
25+
done,
26+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { inject, Injectable, Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2 } from '@angular/core';
2+
import { ɵDomRendererFactory2 as DomRendererFactory2 } from '@angular/platform-browser';
3+
4+
@Injectable()
5+
export class NgtRendererFactory implements RendererFactory2 {
6+
private readonly domRendererFactory = inject(DomRendererFactory2);
7+
8+
createRenderer(hostElement: any, type: RendererType2 | null): Renderer2 {
9+
const domRenderer = this.domRendererFactory.createRenderer(hostElement, type);
10+
const renderer = new NgtRenderer(domRenderer);
11+
return renderer;
12+
}
13+
}
14+
15+
export class NgtRenderer implements Renderer2 {
16+
constructor(private domRenderer: Renderer2) {}
17+
18+
createElement(name: string, namespace?: string | null | undefined) {
19+
throw new Error('Method not implemented.');
20+
}
21+
22+
createComment(value: string) {
23+
throw new Error('Method not implemented.');
24+
}
25+
26+
appendChild(parent: any, newChild: any): void {
27+
throw new Error('Method not implemented.');
28+
}
29+
30+
insertBefore(parent: any, newChild: any, refChild: any, isMove?: boolean | undefined): void {
31+
throw new Error('Method not implemented.');
32+
}
33+
34+
removeChild(parent: any, oldChild: any, isHostElement?: boolean | undefined): void {
35+
throw new Error('Method not implemented.');
36+
}
37+
38+
parentNode(node: any) {
39+
throw new Error('Method not implemented.');
40+
}
41+
42+
setAttribute(el: any, name: string, value: string, namespace?: string | null | undefined): void {
43+
throw new Error('Method not implemented.');
44+
}
45+
46+
setProperty(el: any, name: string, value: any): void {
47+
throw new Error('Method not implemented.');
48+
}
49+
50+
listen(target: any, eventName: string, callback: (event: any) => boolean | void): () => void {
51+
throw new Error('Method not implemented.');
52+
}
53+
54+
get data(): { [key: string]: any } {
55+
return this.domRenderer.data;
56+
}
57+
createText = this.domRenderer.createText.bind(this.domRenderer);
58+
destroy = this.domRenderer.destroy.bind(this.domRenderer);
59+
destroyNode: ((node: any) => void) | null = null;
60+
selectRootElement = this.domRenderer.selectRootElement.bind(this.domRenderer);
61+
nextSibling = this.domRenderer.nextSibling.bind(this.domRenderer);
62+
removeAttribute = this.domRenderer.removeAttribute.bind(this.domRenderer);
63+
addClass = this.domRenderer.addClass.bind(this.domRenderer);
64+
removeClass = this.domRenderer.removeClass.bind(this.domRenderer);
65+
setStyle = this.domRenderer.setStyle.bind(this.domRenderer);
66+
removeStyle = this.domRenderer.removeStyle.bind(this.domRenderer);
67+
setValue = this.domRenderer.setValue.bind(this.domRenderer);
68+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { ChangeDetectorRef, getDebugNode, Injector } from '@angular/core';
2+
import { NgtQueueOpClassId, NgtRendererClassId } from './enums';
3+
4+
export type NgtRendererRootState = {
5+
cdr: ChangeDetectorRef;
6+
compoundPrefixes: string[];
7+
};
8+
9+
export type NgtQueueOp = [type: 'op' | 'cleanUp', op: () => void, done?: true];
10+
11+
export type NgtRendererState = [
12+
type: 'three' | 'compound' | 'portal' | 'comment' | 'dom',
13+
parent: NgtRendererNode | null,
14+
children: NgtRendererNode[],
15+
removed: boolean,
16+
compound: [applyFirst: boolean, props: Record<string, any>],
17+
compoundParent: NgtRendererNode,
18+
compounded: NgtRendererNode,
19+
queueOps: Set<NgtQueueOp>,
20+
attributes: Record<string, any>,
21+
properties: Record<string, any>,
22+
rawValue: any,
23+
ref: any,
24+
portalContainer: NgtRendererNode
25+
];
26+
27+
export type NgtRendererNode = {
28+
__ngt_renderer__: NgtRendererState;
29+
};
30+
31+
export class NgtRendererStore {
32+
private readonly comments = new Set<() => Injector>();
33+
private readonly portals = new Map<NgtRendererNode, () => Injector>();
34+
35+
constructor(private readonly root: NgtRendererRootState) {}
36+
37+
createNode(type: NgtRendererState[NgtRendererClassId.type], node: any) {
38+
const state = [
39+
type,
40+
null,
41+
[],
42+
false,
43+
undefined!,
44+
undefined!,
45+
undefined!,
46+
undefined!,
47+
undefined!,
48+
undefined!,
49+
undefined!,
50+
undefined!,
51+
undefined!,
52+
] as NgtRendererState;
53+
54+
const rendererNode = Object.assign(node, { __ngt_renderer__: state });
55+
56+
if (state[NgtRendererClassId.type] === 'comment') {
57+
rendererNode['__ngt_renderer_add_comment__'] = () => {
58+
this.comments.add(() => getDebugNode(rendererNode)!.injector);
59+
};
60+
return rendererNode;
61+
}
62+
63+
if (state[NgtRendererClassId.type] === 'portal') {
64+
this.portals.set(rendererNode, () => getDebugNode(rendererNode)!.injector);
65+
return rendererNode;
66+
}
67+
68+
if (state[NgtRendererClassId.type] === 'compound') {
69+
state[NgtRendererClassId.queueOps] = new Set();
70+
state[NgtRendererClassId.attributes] = {};
71+
state[NgtRendererClassId.properties] = {};
72+
return rendererNode;
73+
}
74+
75+
return rendererNode;
76+
}
77+
78+
setParent(node: NgtRendererNode, parent: NgtRendererNode) {
79+
if (!node.__ngt_renderer__[NgtRendererClassId.parent]) {
80+
node.__ngt_renderer__[NgtRendererClassId.parent] = parent;
81+
}
82+
}
83+
84+
addChild(node: NgtRendererNode, child: NgtRendererNode) {
85+
if (!node.__ngt_renderer__[NgtRendererClassId.children].includes(child)) {
86+
node.__ngt_renderer__[NgtRendererClassId.children].push(child);
87+
}
88+
}
89+
90+
removeChild(node: NgtRendererNode, child: NgtRendererNode) {
91+
const index = node.__ngt_renderer__[NgtRendererClassId.children].findIndex((c) => child === c);
92+
if (index >= 0) {
93+
node.__ngt_renderer__[NgtRendererClassId.children].splice(index, 1);
94+
}
95+
}
96+
97+
queueOperation(node: NgtRendererNode, op: NgtQueueOp) {
98+
node.__ngt_renderer__[NgtRendererClassId.queueOps].add(op);
99+
}
100+
101+
executeOperation(node: NgtRendererNode, type: NgtQueueOp[NgtQueueOpClassId.type] = 'op') {
102+
if (node.__ngt_renderer__[NgtRendererClassId.queueOps]?.size) {
103+
node.__ngt_renderer__[NgtRendererClassId.queueOps].forEach((op) => {
104+
if (op[NgtQueueOpClassId.type] === type) {
105+
op[NgtQueueOpClassId.op]();
106+
node.__ngt_renderer__[NgtRendererClassId.queueOps].delete(op);
107+
}
108+
});
109+
}
110+
}
111+
112+
processPortalContainer(portal: NgtRendererNode) {
113+
const injectorFactory = this.portals.get(portal);
114+
const injector = injectorFactory?.();
115+
if (!injector) return;
116+
117+
const portalStore = injector.get('', null);
118+
if (!portalStore) return;
119+
120+
const portalContainer = portalStore.get('scene');
121+
if (!portalContainer) return;
122+
123+
portal.__ngt_renderer__[NgtRendererClassId.portalContainer] = this.createNode('three', portalContainer);
124+
}
125+
}

0 commit comments

Comments
 (0)