Skip to content

Commit ede5c0b

Browse files
committed
feat(core): add elementEvents for NGT events and THREE native events
1 parent f197487 commit ede5c0b

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

libs/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export * from './lib/store';
1818
export * from './lib/utils/apply-props';
1919
export * from './lib/utils/attach';
2020
export * from './lib/utils/before-render';
21+
export * from './lib/utils/element-events';
2122
export * from './lib/utils/is';
2223
export * from './lib/utils/make';
2324
export * from './lib/utils/object-events';
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import {
2+
computed,
3+
DestroyRef,
4+
Directive,
5+
effect,
6+
ElementRef,
7+
inject,
8+
Injector,
9+
model,
10+
output,
11+
Renderer2,
12+
} from '@angular/core';
13+
import { assertInjector } from 'ngxtension/assert-injector';
14+
import type { NgtAfterAttach } from '../types';
15+
import { is } from './is';
16+
import { resolveRef } from './resolve-ref';
17+
18+
@Directive({ selector: '[ngtElementEvents]' })
19+
/**
20+
* As host directive:
21+
* - outputs: [
22+
* 'created',
23+
* 'attached',
24+
* 'updated',
25+
* 'added', 'removed', 'childadded', 'childremoved', 'change', 'disposed'
26+
* ]
27+
* - inputs: [
28+
* 'ngtElementEvents'
29+
* ]
30+
*/
31+
export class NgtElementEvents<TElement extends object> {
32+
created = output<TElement>();
33+
attached = output<NgtAfterAttach<TElement>>();
34+
updated = output<TElement>();
35+
36+
added = output<any>();
37+
removed = output<any>();
38+
childadded = output<any>();
39+
childremoved = output<any>();
40+
change = output<any>();
41+
disposed = output<any>();
42+
43+
// NOTE: we use model here to allow for the hostDirective host to set this value
44+
ngtElementEvents = model<
45+
ElementRef<TElement> | TElement | null | undefined | (() => ElementRef<TElement> | TElement | null | undefined)
46+
>();
47+
48+
constructor() {
49+
const obj = computed(() => {
50+
const ngtObject = this.ngtElementEvents();
51+
if (typeof ngtObject === 'function') return ngtObject();
52+
return ngtObject;
53+
});
54+
55+
elementEvents(obj, {
56+
// @ts-expect-error - different type
57+
created: this.emitEvent('created'),
58+
// @ts-expect-error - different type
59+
updated: this.emitEvent('updated'),
60+
// @ts-expect-error - different type for attached
61+
attached: this.emitEvent('attached'),
62+
added: this.emitEvent('added'),
63+
removed: this.emitEvent('removed'),
64+
childadded: this.emitEvent('childadded'),
65+
childremoved: this.emitEvent('childremoved'),
66+
change: this.emitEvent('change'),
67+
disposed: this.emitEvent('disposed'),
68+
});
69+
}
70+
71+
private emitEvent<
72+
TEvent extends
73+
| 'created'
74+
| 'updated'
75+
| 'attached'
76+
| 'added'
77+
| 'removed'
78+
| 'childadded'
79+
| 'childremoved'
80+
| 'change'
81+
| 'disposed',
82+
>(eventName: TEvent) {
83+
return this[eventName].emit.bind(this[eventName]);
84+
}
85+
}
86+
87+
export function elementEvents<TElement extends object>(
88+
target: () => ElementRef<TElement> | TElement | null | undefined,
89+
events: {
90+
created?: (element: TElement) => void;
91+
updated?: (element: TElement) => void;
92+
attached?: (element: NgtAfterAttach<TElement>) => void;
93+
added?: (event: any) => void;
94+
removed?: (event: any) => void;
95+
childadded?: (event: any) => void;
96+
childremoved?: (event: any) => void;
97+
change?: (event: any) => void;
98+
disposed?: (event: any) => void;
99+
},
100+
{ injector }: { injector?: Injector } = {},
101+
) {
102+
return assertInjector(elementEvents, injector, () => {
103+
const renderer = inject(Renderer2);
104+
105+
const cleanUps: Array<() => void> = [];
106+
107+
effect((onCleanup) => {
108+
const targetObj = resolveRef(target());
109+
110+
if (!targetObj || !is.instance(targetObj)) return;
111+
112+
Object.keys(events).forEach((eventName) => {
113+
cleanUps.push(renderer.listen(targetObj, eventName, events[eventName as keyof typeof events] as any));
114+
});
115+
116+
onCleanup(() => {
117+
cleanUps.forEach((cleanUp) => cleanUp());
118+
});
119+
});
120+
121+
inject(DestroyRef).onDestroy(() => {
122+
cleanUps.forEach((cleanUp) => cleanUp());
123+
});
124+
125+
return cleanUps;
126+
});
127+
}

0 commit comments

Comments
 (0)