angular-three 2.0.0-beta.242 → 2.0.0-beta.244

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.
Files changed (50) hide show
  1. package/esm2022/index.mjs +2 -2
  2. package/esm2022/lib/canvas.mjs +19 -27
  3. package/esm2022/lib/directives/args.mjs +52 -14
  4. package/esm2022/lib/instance.mjs +10 -9
  5. package/esm2022/lib/loop.mjs +2 -2
  6. package/esm2022/lib/portal.mjs +119 -111
  7. package/esm2022/lib/renderer/catalogue.mjs +2 -2
  8. package/esm2022/lib/renderer/constants.mjs +3 -5
  9. package/esm2022/lib/renderer/index.mjs +276 -194
  10. package/esm2022/lib/renderer/state.mjs +49 -0
  11. package/esm2022/lib/renderer/utils.mjs +40 -24
  12. package/esm2022/lib/roots.mjs +25 -25
  13. package/esm2022/lib/routed-scene.mjs +3 -3
  14. package/esm2022/lib/store.mjs +14 -12
  15. package/esm2022/lib/three-types.mjs +1 -1
  16. package/esm2022/lib/utils/apply-props.mjs +3 -3
  17. package/esm2022/lib/utils/before-render.mjs +4 -3
  18. package/esm2022/lib/utils/parameters.mjs +33 -28
  19. package/esm2022/lib/utils/resolve-ref.mjs +8 -0
  20. package/esm2022/lib/utils/signal-store.mjs +3 -14
  21. package/fesm2022/angular-three.mjs +720 -1089
  22. package/fesm2022/angular-three.mjs.map +1 -1
  23. package/index.d.ts +2 -2
  24. package/lib/canvas.d.ts +278 -1001
  25. package/lib/directives/args.d.ts +11 -4
  26. package/lib/instance.d.ts +8 -13
  27. package/lib/loop.d.ts +1 -1
  28. package/lib/portal.d.ts +41 -31
  29. package/lib/renderer/catalogue.d.ts +1 -1
  30. package/lib/renderer/constants.d.ts +2 -4
  31. package/lib/renderer/index.d.ts +15 -5
  32. package/lib/renderer/state.d.ts +23 -0
  33. package/lib/renderer/utils.d.ts +5 -22
  34. package/lib/roots.d.ts +5 -0
  35. package/lib/store.d.ts +12 -12
  36. package/lib/three-types.d.ts +8 -5
  37. package/lib/utils/parameters.d.ts +6 -9
  38. package/lib/utils/resolve-ref.d.ts +2 -0
  39. package/lib/utils/signal-store.d.ts +1 -5
  40. package/metadata.json +1 -1
  41. package/package.json +71 -72
  42. package/web-types.json +1 -1
  43. package/esm2022/lib/directives/common.mjs +0 -67
  44. package/esm2022/lib/directives/parent.mjs +0 -20
  45. package/esm2022/lib/ref.mjs +0 -50
  46. package/esm2022/lib/renderer/store.mjs +0 -439
  47. package/lib/directives/common.d.ts +0 -31
  48. package/lib/directives/parent.d.ts +0 -11
  49. package/lib/ref.d.ts +0 -7
  50. package/lib/renderer/store.d.ts +0 -65
@@ -1,12 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { untracked, computed, signal, ElementRef, inject, effect, InjectionToken, Optional, SkipSelf, ViewContainerRef, NgZone, TemplateRef, afterNextRender, DestroyRef, Directive, input, getDebugNode, RendererFactory2, Injectable, Injector, makeEnvironmentProviders, EnvironmentInjector, booleanAttribute, output, viewChild, createEnvironmentInjector, Component, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, contentChild, forwardRef } from '@angular/core';
2
+ import { untracked, computed, signal, ElementRef, input, inject, ViewContainerRef, NgZone, TemplateRef, afterNextRender, DestroyRef, Directive, effect, InjectionToken, getDebugNode, RendererFactory2, Injectable, makeEnvironmentProviders, EnvironmentInjector, Injector, booleanAttribute, output, viewChild, createEnvironmentInjector, Component, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, contentChild, forwardRef } from '@angular/core';
3
3
  import { outputFromObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { injectAutoEffect } from 'ngxtension/auto-effect';
5
5
  import { provideResizeOptions, NgxResize } from 'ngxtension/resize';
6
6
  import { MathUtils, WebGLRenderer, OrthographicCamera, PerspectiveCamera, Vector3, Layers, Color, ColorManagement, Texture, RGBAFormat, UnsignedByteType, Raycaster, Scene, PCFSoftShadowMap, BasicShadowMap, PCFShadowMap, VSMShadowMap, NoToneMapping, ACESFilmicToneMapping, Vector2, Clock } from 'three';
7
7
  import { DOCUMENT } from '@angular/common';
8
+ import { Subject, filter } from 'rxjs';
8
9
  import { createInjectionToken } from 'ngxtension/create-injection-token';
9
- import { Subject, ReplaySubject, filter } from 'rxjs';
10
10
  import { assertInjector } from 'ngxtension/assert-injector';
11
11
  import * as i1 from '@angular/router';
12
12
  import { ActivationEnd, RouterOutlet } from '@angular/router';
@@ -32,14 +32,6 @@ function updater(_source) {
32
32
  });
33
33
  };
34
34
  }
35
- function patcher(_source) {
36
- return (state) => {
37
- const updater = reducer(state);
38
- untracked(() => {
39
- _source.update((previous) => ({ ...updater(previous), ...previous }));
40
- });
41
- };
42
- }
43
35
  function getter(_source) {
44
36
  return (...keys) => {
45
37
  const root = untracked(_source);
@@ -78,7 +70,6 @@ function signalStore(initialState = {}, options) {
78
70
  let source;
79
71
  let update;
80
72
  let get;
81
- let patch;
82
73
  let select;
83
74
  let state;
84
75
  const computedCache = new Map();
@@ -90,19 +81,17 @@ function signalStore(initialState = {}, options) {
90
81
  state = source.asReadonly();
91
82
  get = getter(source);
92
83
  update = updater(source);
93
- patch = patcher(source);
94
84
  select = selector(state, computedCache);
95
- source.set(initialState({ update, get, patch, select }));
85
+ source.set(initialState({ update, get, select }));
96
86
  }
97
87
  else {
98
88
  source = signal(initialState, options);
99
89
  state = source.asReadonly();
100
90
  get = getter(source);
101
91
  update = updater(source);
102
- patch = patcher(source);
103
92
  select = selector(state, computedCache);
104
93
  }
105
- const store = { select, get, update, patch, state };
94
+ const store = { select, get, update, state };
106
95
  Object.defineProperty(store, 'snapshot', {
107
96
  get: untracked.bind({}, state),
108
97
  configurable: false,
@@ -201,16 +190,21 @@ function getLocalState(obj) {
201
190
  return obj['__ngt__'];
202
191
  }
203
192
  function invalidateInstance(instance) {
204
- const state = getLocalState(instance)?.store.snapshot;
205
- if (state && state.internal.frames === 0)
206
- state.invalidate();
193
+ let store = getLocalState(instance)?.store;
194
+ if (store) {
195
+ while (store.snapshot.previousRoot) {
196
+ store = store.snapshot.previousRoot;
197
+ }
198
+ if (store.snapshot.internal.frames === 0) {
199
+ store.snapshot.invalidate();
200
+ }
201
+ }
207
202
  checkUpdate(instance);
208
203
  }
209
204
  function prepare(object, localState) {
210
205
  const instance = object;
211
206
  if (localState?.primitive || !instance.__ngt__) {
212
207
  const { instanceStore = signalStore({
213
- nativeProps: {},
214
208
  parent: null,
215
209
  objects: [],
216
210
  nonObjects: [],
@@ -225,7 +219,6 @@ function prepare(object, localState) {
225
219
  parent: instanceStore.select('parent'),
226
220
  objects: instanceStore.select('objects'),
227
221
  nonObjects: instanceStore.select('nonObjects'),
228
- nativeProps: instanceStore.select('nativeProps'),
229
222
  add(object, type) {
230
223
  const current = instance.__ngt__.instanceStore.get(type);
231
224
  const foundIndex = current.indexOf((node) => object === node);
@@ -242,9 +235,6 @@ function prepare(object, localState) {
242
235
  instance.__ngt__.instanceStore.update((prev) => ({ [type]: prev[type].filter((node) => node !== object) }));
243
236
  notifyAncestors(instance.__ngt__.instanceStore.get('parent'));
244
237
  },
245
- setNativeProps(key, value) {
246
- instance.__ngt__.instanceStore.update((prev) => ({ nativeProps: { ...prev.nativeProps, [key]: value } }));
247
- },
248
238
  setParent(parent) {
249
239
  instance.__ngt__.instanceStore.update({ parent });
250
240
  },
@@ -743,6 +733,83 @@ function createPointerEvents(store) {
743
733
  };
744
734
  }
745
735
 
736
+ const ROUTED_SCENE = '__ngt_renderer_is_routed_scene__';
737
+ const HTML = '__ngt_renderer_is_html';
738
+ const SPECIAL_INTERNAL_ADD_COMMENT = '__ngt_renderer_add_comment__';
739
+ const SPECIAL_DOM_TAG = {
740
+ NGT_PORTAL: 'ngt-portal',
741
+ NGT_PRIMITIVE: 'ngt-primitive',
742
+ NGT_VALUE: 'ngt-value',
743
+ };
744
+ const SPECIAL_PROPERTIES = {
745
+ RENDER_PRIORITY: 'priority',
746
+ ATTACH: 'attach',
747
+ RAW_VALUE: 'rawValue',
748
+ PARAMETERS: 'parameters',
749
+ };
750
+ const SPECIAL_EVENTS = {
751
+ BEFORE_RENDER: 'beforeRender',
752
+ UPDATED: 'updated',
753
+ ATTACHED: 'attached',
754
+ };
755
+
756
+ class NgtArgs {
757
+ constructor() {
758
+ this.args = input.required();
759
+ this.vcr = inject(ViewContainerRef);
760
+ this.zone = inject(NgZone);
761
+ this.template = inject(TemplateRef);
762
+ this.autoEffect = injectAutoEffect();
763
+ this.injected = false;
764
+ this.injectedArgs = null;
765
+ const commentNode = this.vcr.element.nativeElement;
766
+ if (commentNode[SPECIAL_INTERNAL_ADD_COMMENT]) {
767
+ commentNode[SPECIAL_INTERNAL_ADD_COMMENT]('args');
768
+ delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
769
+ }
770
+ afterNextRender(() => {
771
+ this.autoEffect(() => {
772
+ const value = this.args();
773
+ if (value == null || !Array.isArray(value) || (value.length === 1 && value[0] === null))
774
+ return;
775
+ this.injected = false;
776
+ this.injectedArgs = value;
777
+ untracked(() => {
778
+ this.createView();
779
+ });
780
+ });
781
+ });
782
+ inject(DestroyRef).onDestroy(() => {
783
+ this.view?.destroy();
784
+ });
785
+ }
786
+ get value() {
787
+ if (this.validate()) {
788
+ this.injected = true;
789
+ return this.injectedArgs;
790
+ }
791
+ return null;
792
+ }
793
+ validate() {
794
+ return !this.injected && !!this.injectedArgs?.length;
795
+ }
796
+ createView() {
797
+ this.zone.runOutsideAngular(() => {
798
+ if (this.view && !this.view.destroyed) {
799
+ this.view.destroy();
800
+ }
801
+ this.view = this.vcr.createEmbeddedView(this.template);
802
+ this.view.detectChanges();
803
+ });
804
+ }
805
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtArgs, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
806
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.0.6", type: NgtArgs, isStandalone: true, selector: "ng-template[args]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0 }); }
807
+ }
808
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtArgs, decorators: [{
809
+ type: Directive,
810
+ args: [{ selector: 'ng-template[args]', standalone: true }]
811
+ }], ctorParameters: () => [] });
812
+
746
813
  // This function prepares a set of changes to be applied to the instance
747
814
  function diffProps(instance, props) {
748
815
  const propsEntries = Object.entries(props);
@@ -856,8 +923,8 @@ function applyProps(instance, props) {
856
923
  if (localState?.eventCount)
857
924
  rootState.internal.interaction.push(instance);
858
925
  }
859
- if (parent && localState?.afterUpdate && localState.afterUpdate.observed && changes.length) {
860
- localState.afterUpdate.next(instance);
926
+ if (parent && localState?.onUpdate && changes.length) {
927
+ localState.onUpdate(instance);
861
928
  }
862
929
  return instance;
863
930
  }
@@ -866,8 +933,8 @@ const shallowLoose = { objects: 'shallow', strict: false };
866
933
  const roots = new Map();
867
934
  function injectCanvasRootInitializer(injector) {
868
935
  return assertInjector(injectCanvasRootInitializer, injector, () => {
869
- const injectedStore = injectNgtStore();
870
- const loop = injectNgtLoop();
936
+ const injectedStore = injectStore();
937
+ const loop = injectLoop();
871
938
  return (canvas) => {
872
939
  const exist = roots.has(canvas);
873
940
  let store = roots.get(canvas);
@@ -889,21 +956,19 @@ function injectCanvasRootInitializer(injector) {
889
956
  const root = roots.get(canvas);
890
957
  if (root) {
891
958
  root.update((state) => ({ internal: { ...state.internal, active: false } }));
892
- setTimeout(() => {
893
- try {
894
- const state = root.get();
895
- state.events.disconnect?.();
896
- state.gl?.renderLists?.dispose?.();
897
- state.gl?.forceContextLoss?.();
898
- if (state.gl?.xr)
899
- state.xr.disconnect();
900
- dispose(state);
901
- roots.delete(canvas);
902
- }
903
- catch (e) {
904
- console.error('[NGT] Unexpected error while destroying Canvas Root', e);
905
- }
906
- }, timeout);
959
+ try {
960
+ const state = root.get();
961
+ state.events.disconnect?.();
962
+ state.gl?.renderLists?.dispose?.();
963
+ state.gl?.forceContextLoss?.();
964
+ if (state.gl?.xr)
965
+ state.xr.disconnect();
966
+ dispose(state);
967
+ roots.delete(canvas);
968
+ }
969
+ catch (e) {
970
+ console.error('[NGT] Unexpected error while destroying Canvas Root', e);
971
+ }
907
972
  }
908
973
  },
909
974
  configure: (inputs) => {
@@ -1057,9 +1122,9 @@ function injectCanvasRootInitializer(injector) {
1057
1122
  if (state.flat !== flat)
1058
1123
  stateToUpdate.flat = flat;
1059
1124
  // Set gl props
1060
- gl.setClearAlpha(0);
1061
- gl.setPixelRatio(makeDpr(state.viewport.dpr));
1062
- gl.setSize(state.size.width, state.size.height);
1125
+ // gl.setClearAlpha(0);
1126
+ // gl.setPixelRatio(makeDpr(state.viewport.dpr));
1127
+ // gl.setSize(state.size.width, state.size.height);
1063
1128
  if (is.obj(glOptions) &&
1064
1129
  !(typeof glOptions === 'function') &&
1065
1130
  !is.renderer(glOptions) &&
@@ -1073,7 +1138,9 @@ function injectCanvasRootInitializer(injector) {
1073
1138
  if (performance && !is.equ(performance, state.performance, shallowLoose)) {
1074
1139
  stateToUpdate.performance = { ...state.performance, ...performance };
1075
1140
  }
1076
- store.update(stateToUpdate);
1141
+ if (Object.keys(stateToUpdate).length) {
1142
+ store.update(stateToUpdate);
1143
+ }
1077
1144
  // Check size, allow it to take on container bounds initially
1078
1145
  const size = computeInitialSize(canvas, sizeOptions);
1079
1146
  if (!is.equ(size, state.size, shallowLoose)) {
@@ -1250,7 +1317,7 @@ function createLoop(roots) {
1250
1317
  }
1251
1318
  return { loop, invalidate, advance };
1252
1319
  }
1253
- const [injectNgtLoop] = createInjectionToken(() => createLoop(roots));
1320
+ const [injectLoop] = createInjectionToken(() => createLoop(roots));
1254
1321
 
1255
1322
  function storeFactory(previousStore) {
1256
1323
  const document = inject(DOCUMENT);
@@ -1259,7 +1326,7 @@ function storeFactory(previousStore) {
1259
1326
  // TODO: revisit this when we need to support multiple platforms
1260
1327
  throw new Error(`[NGT] Window is not available.`);
1261
1328
  }
1262
- const loop = injectNgtLoop();
1329
+ const loop = injectLoop();
1263
1330
  // NOTE: using Subject because we do not care about late-subscribers
1264
1331
  const pointerMissed$ = new Subject();
1265
1332
  const store = signalStore(({ get, update }) => {
@@ -1393,9 +1460,8 @@ function storeFactory(previousStore) {
1393
1460
  });
1394
1461
  Object.defineProperty(store, 'pointerMissed$', { get: () => pointerMissed$ });
1395
1462
  let { size: oldSize, viewport: { dpr: oldDpr }, camera: oldCamera, } = store.snapshot;
1396
- const [camera, size, viewportDpr] = [store.select('camera'), store.select('size'), store.select('viewport', 'dpr')];
1397
1463
  effect(() => {
1398
- const [newCamera, newSize, newDpr, gl] = [camera(), size(), viewportDpr(), store.snapshot.gl];
1464
+ const { camera: newCamera, size: newSize, viewport: { dpr: newDpr }, gl, } = store.state();
1399
1465
  // Resize camera and renderer on changes to size and pixel-ratio
1400
1466
  if (newSize !== oldSize || newDpr !== oldDpr) {
1401
1467
  oldSize = newSize;
@@ -1417,138 +1483,69 @@ function storeFactory(previousStore) {
1417
1483
  return store;
1418
1484
  }
1419
1485
  const NGT_STORE = new InjectionToken('NgtStore Token');
1420
- const [injectNgtStore, provideNgtStore] = createInjectionToken(storeFactory, {
1421
- isRoot: false,
1422
- deps: [[new Optional(), new SkipSelf(), NGT_STORE]],
1423
- token: NGT_STORE,
1424
- });
1486
+ function provideStore(store) {
1487
+ if (store) {
1488
+ return { provide: NGT_STORE, useFactory: store };
1489
+ }
1490
+ return { provide: NGT_STORE, useFactory: storeFactory };
1491
+ }
1492
+ function injectStore(options) {
1493
+ return inject(NGT_STORE, options);
1494
+ }
1425
1495
 
1426
1496
  const catalogue = {};
1427
1497
  function extend(objects) {
1428
1498
  Object.assign(catalogue, objects);
1429
1499
  }
1430
- const [injectNgtCatalogue] = createInjectionToken(() => catalogue);
1431
-
1432
- const ROUTED_SCENE = '__ngt_renderer_is_routed_scene__';
1433
- const HTML = '__ngt_renderer_is_html';
1434
- const SPECIAL_INTERNAL_ADD_COMMENT = '__ngt_renderer_add_comment__';
1435
- const SPECIAL_DOM_TAG = {
1436
- NGT_PORTAL: 'ngt-portal',
1437
- NGT_PRIMITIVE: 'ngt-primitive',
1438
- NGT_VALUE: 'ngt-value',
1439
- };
1440
- const SPECIAL_PROPERTIES = {
1441
- COMPOUND: 'ngtCompound',
1442
- RENDER_PRIORITY: 'priority',
1443
- ATTACH: 'attach',
1444
- RAW_VALUE: 'rawValue',
1445
- PARAMETERS: 'parameters',
1446
- REF: 'ref',
1447
- };
1448
- const SPECIAL_EVENTS = {
1449
- BEFORE_RENDER: 'beforeRender',
1450
- AFTER_UPDATE: 'afterUpdate',
1451
- AFTER_ATTACH: 'afterAttach',
1452
- };
1500
+ const [injectCatalogue] = createInjectionToken(() => catalogue);
1453
1501
 
1454
- const [injectNodeType, provideNodeType] = createInjectionToken(() => '', {
1455
- isRoot: false,
1456
- });
1457
- class NgtCommonDirective {
1458
- constructor() {
1459
- this.vcr = inject(ViewContainerRef);
1460
- this.zone = inject(NgZone);
1461
- this.template = inject(TemplateRef);
1462
- this.nodeType = injectNodeType();
1463
- this.autoEffect = injectAutoEffect();
1464
- this.injected = false;
1465
- this.injectedValue = null;
1466
- this.shouldCreateView = true;
1467
- const commentNode = this.vcr.element.nativeElement;
1468
- if (commentNode[SPECIAL_INTERNAL_ADD_COMMENT]) {
1469
- commentNode[SPECIAL_INTERNAL_ADD_COMMENT](this.nodeType);
1470
- delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
1471
- }
1472
- afterNextRender(() => {
1473
- this.autoEffect(() => {
1474
- const value = this.inputValue();
1475
- if (this.shouldSkipCreateView(value))
1476
- return;
1477
- this.injected = false;
1478
- this.injectedValue = value;
1479
- untracked(() => {
1480
- this.createView();
1481
- });
1482
- });
1483
- });
1484
- inject(DestroyRef).onDestroy(() => {
1485
- this.view?.destroy();
1486
- });
1487
- }
1488
- shouldSkipCreateView(value) {
1489
- return !value;
1490
- }
1491
- get value() {
1492
- if (this.validate()) {
1493
- this.injected = true;
1494
- return this.injectedValue;
1495
- }
1496
- return null;
1497
- }
1498
- createView() {
1499
- this.zone.runOutsideAngular(() => {
1500
- if (this.shouldCreateView) {
1501
- if (this.view && !this.view.destroyed) {
1502
- this.view.destroy();
1503
- }
1504
- this.view = this.vcr.createEmbeddedView(this.template);
1505
- this.view.detectChanges();
1506
- }
1507
- });
1502
+ function createNode(type, node, document) {
1503
+ const state = [type, null, [], false, undefined, undefined, undefined];
1504
+ const rendererNode = Object.assign(node, { __ngt_renderer__: state });
1505
+ // NOTE: assign ownerDocument to node so we can use HostListener in Component
1506
+ if (!rendererNode['ownerDocument'])
1507
+ rendererNode['ownerDocument'] = document;
1508
+ // NOTE: assign injectorFactory on non-three type since
1509
+ // rendererNode is an instance of DOM Node
1510
+ if (state[0 /* NgtRendererClassId.type */] !== 'three') {
1511
+ state[6 /* NgtRendererClassId.injectorFactory */] = () => getDebugNode(rendererNode)?.injector;
1508
1512
  }
1509
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtCommonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1510
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.3", type: NgtCommonDirective, ngImport: i0 }); }
1513
+ return rendererNode;
1511
1514
  }
1512
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtCommonDirective, decorators: [{
1513
- type: Directive
1514
- }], ctorParameters: () => [] });
1515
-
1516
- class NgtArgs extends NgtCommonDirective {
1517
- constructor() {
1518
- super(...arguments);
1519
- this.args = input.required();
1520
- this.inputValue = this.args;
1515
+ function isDOM(node) {
1516
+ const rS = node['__ngt_renderer__'];
1517
+ return !rS || node instanceof Element || node instanceof Document || node instanceof Window;
1518
+ }
1519
+ function getClosestParentWithInstance(node) {
1520
+ let parent = node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1521
+ if (parent &&
1522
+ parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'portal' &&
1523
+ parent.__ngt_renderer__[5 /* NgtRendererClassId.portalContainer */]?.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
1524
+ return parent.__ngt_renderer__[5 /* NgtRendererClassId.portalContainer */];
1521
1525
  }
1522
- shouldSkipCreateView(value) {
1523
- return value == null || !Array.isArray(value) || (value.length === 1 && value[0] === null);
1526
+ while (parent && parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'three') {
1527
+ parent = parent.__ngt_renderer__[5 /* NgtRendererClassId.portalContainer */]
1528
+ ? parent.__ngt_renderer__[5 /* NgtRendererClassId.portalContainer */]
1529
+ : parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1524
1530
  }
1525
- validate() {
1526
- return !this.injected && !!this.injectedValue?.length;
1531
+ return parent;
1532
+ }
1533
+ function setParent(node, parent) {
1534
+ if (!node.__ngt_renderer__[1 /* NgtRendererClassId.parent */]) {
1535
+ node.__ngt_renderer__[1 /* NgtRendererClassId.parent */] = parent;
1527
1536
  }
1528
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtArgs, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
1529
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.0.3", type: NgtArgs, isStandalone: true, selector: "ng-template[args]", inputs: { args: { classPropertyName: "args", publicName: "args", isSignal: true, isRequired: true, transformFunction: null } }, providers: [provideNodeType('args')], usesInheritance: true, ngImport: i0 }); }
1530
1537
  }
1531
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtArgs, decorators: [{
1532
- type: Directive,
1533
- args: [{ selector: 'ng-template[args]', standalone: true, providers: [provideNodeType('args')] }]
1534
- }] });
1535
-
1536
- class NgtParent extends NgtCommonDirective {
1537
- constructor() {
1538
- super(...arguments);
1539
- this.parent = input.required();
1540
- this.inputValue = this.parent;
1538
+ function addChild(node, child) {
1539
+ if (!node.__ngt_renderer__[2 /* NgtRendererClassId.children */].includes(child)) {
1540
+ node.__ngt_renderer__[2 /* NgtRendererClassId.children */].push(child);
1541
1541
  }
1542
- validate() {
1543
- return !this.injected && !!this.injectedValue;
1542
+ }
1543
+ function removeChild(node, child) {
1544
+ const index = node.__ngt_renderer__?.[2 /* NgtRendererClassId.children */].findIndex((c) => child === c);
1545
+ if (index >= 0) {
1546
+ node.__ngt_renderer__[2 /* NgtRendererClassId.children */].splice(index, 1);
1544
1547
  }
1545
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtParent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
1546
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "18.0.3", type: NgtParent, isStandalone: true, selector: "ng-template[parent]", inputs: { parent: { classPropertyName: "parent", publicName: "parent", isSignal: true, isRequired: true, transformFunction: null } }, providers: [provideNodeType('parent')], usesInheritance: true, ngImport: i0 }); }
1547
1548
  }
1548
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtParent, decorators: [{
1549
- type: Directive,
1550
- args: [{ selector: 'ng-template[parent]', standalone: true, providers: [provideNodeType('parent')] }]
1551
- }] });
1552
1549
 
1553
1550
  function attach(object, value, paths = []) {
1554
1551
  const [base, ...remaining] = paths;
@@ -1596,10 +1593,22 @@ function attachThreeChild(parent, child) {
1596
1593
  }
1597
1594
  // whether the child is added to the parent with parent.add()
1598
1595
  let added = false;
1596
+ let attached = false;
1599
1597
  // assign store on child if not already exist
1598
+ // or child store is not the same as parent store
1600
1599
  // or child store is the parent of parent store
1601
- if (!cLS.store || cLS.store === pLS.store.get('previousRoot')) {
1600
+ if (!cLS.store || cLS.store !== pLS.store || cLS.store === pLS.store.get('previousRoot')) {
1602
1601
  cLS.store = pLS.store;
1602
+ const grandchildren = [
1603
+ ...(cLS.objects ? untracked(cLS.objects) : []),
1604
+ ...(cLS.nonObjects ? untracked(cLS.nonObjects) : []),
1605
+ ];
1606
+ for (const grandchild of grandchildren) {
1607
+ const grandChildLS = getLocalState(grandchild);
1608
+ if (!grandChildLS)
1609
+ continue;
1610
+ grandChildLS.store = cLS.store;
1611
+ }
1603
1612
  }
1604
1613
  if (cLS.attach) {
1605
1614
  const attachProp = cLS.attach;
@@ -1628,9 +1637,9 @@ function attachThreeChild(parent, child) {
1628
1637
  cLS.setParent(parent);
1629
1638
  }
1630
1639
  // at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
1631
- if (child.__ngt_renderer__[11 /* NgtRendererClassId.rawValue */] === undefined)
1640
+ if (child.__ngt_renderer__[4 /* NgtRendererClassId.rawValue */] === undefined)
1632
1641
  return;
1633
- attach(parent, child.__ngt_renderer__[11 /* NgtRendererClassId.rawValue */], attachProp);
1642
+ attach(parent, child.__ngt_renderer__[4 /* NgtRendererClassId.rawValue */], attachProp);
1634
1643
  }
1635
1644
  else {
1636
1645
  attach(parent, child, attachProp);
@@ -1644,11 +1653,13 @@ function attachThreeChild(parent, child) {
1644
1653
  added = true;
1645
1654
  }
1646
1655
  pLS.add(child, added ? 'objects' : 'nonObjects');
1647
- if (cLS.instanceStore.get('parent') !== parent) {
1656
+ if (cLS.parent && untracked(cLS.parent) !== parent) {
1648
1657
  cLS.setParent(parent);
1649
1658
  }
1650
- if (cLS.afterAttach)
1651
- cLS.afterAttach.next({ parent, node: child });
1659
+ // NOTE: this does not mean that the child is actually attached to the parent on the scenegraph.
1660
+ // a child on the Angular template can also emit onAttach
1661
+ if (cLS.onAttach)
1662
+ cLS.onAttach({ parent, node: child });
1652
1663
  invalidateInstance(child);
1653
1664
  invalidateInstance(parent);
1654
1665
  }
@@ -1695,22 +1706,19 @@ function processThreeEvent(instance, priority, eventName, callback) {
1695
1706
  .get('internal')
1696
1707
  .subscribe((state) => callback({ state, object: instance }), priority || lS.priority || 0);
1697
1708
  }
1698
- if (eventName === SPECIAL_EVENTS.AFTER_UPDATE || eventName === SPECIAL_EVENTS.AFTER_ATTACH) {
1699
- let emitter = lS[eventName];
1700
- if (!emitter)
1701
- emitter = lS[eventName] = new ReplaySubject(1);
1702
- const sub = emitter.subscribe(callback);
1703
- // NOTE: for afterAttach event, if the instance already has a parent,
1704
- // then we'll emit the event immediately
1705
- if (eventName === SPECIAL_EVENTS.AFTER_ATTACH && untracked(lS.parent)) {
1706
- emitter.next({ parent: untracked(lS.parent), node: instance });
1709
+ if (eventName === SPECIAL_EVENTS.ATTACHED) {
1710
+ lS.onAttach = callback;
1711
+ if (untracked(lS.parent)) {
1712
+ lS.onAttach({ parent: untracked(lS.parent), node: instance });
1707
1713
  }
1708
1714
  return () => {
1709
- sub.unsubscribe();
1710
- const emitter = lS[eventName];
1711
- if (emitter && !emitter.observed) {
1712
- emitter.complete();
1713
- }
1715
+ lS.onAttach = undefined;
1716
+ };
1717
+ }
1718
+ if (eventName === SPECIAL_EVENTS.UPDATED) {
1719
+ lS.onUpdate = callback;
1720
+ return () => {
1721
+ lS.onUpdate = undefined;
1714
1722
  };
1715
1723
  }
1716
1724
  if (!lS.handlers)
@@ -1728,8 +1736,14 @@ function processThreeEvent(instance, priority, eventName, callback) {
1728
1736
  lS.eventCount += 1;
1729
1737
  // but only add the instance (target) to the interaction array (so that it is handled by the EventManager with Raycast)
1730
1738
  // the first time eventCount is incremented
1731
- if (lS.eventCount === 1 && instance['raycast'])
1732
- lS.store.get('internal', 'interaction').push(instance);
1739
+ if (lS.eventCount === 1 && instance['raycast']) {
1740
+ let root = lS.store;
1741
+ while (root.get('previousRoot')) {
1742
+ root = root.get('previousRoot');
1743
+ }
1744
+ const interactions = root.get('internal', 'interaction') || [];
1745
+ interactions.push(instance);
1746
+ }
1733
1747
  // clean up the event listener by removing the target from the interaction array
1734
1748
  return () => {
1735
1749
  const lS = getLocalState(instance);
@@ -1743,590 +1757,146 @@ function processThreeEvent(instance, priority, eventName, callback) {
1743
1757
  };
1744
1758
  }
1745
1759
 
1746
- const NGT_COMPOUND_PREFIXES = new InjectionToken('NgtCompoundPrefixes');
1747
- class NgtRendererStore {
1748
- constructor(rootState) {
1749
- this.rootState = rootState;
1750
- this.argsCommentNodes = [];
1751
- this.parentCommentNodes = [];
1760
+ class NgtRendererFactory {
1761
+ constructor() {
1762
+ this.delegateRendererFactory = inject(RendererFactory2, { skipSelf: true });
1763
+ this.document = inject(DOCUMENT);
1764
+ this.catalogue = injectCatalogue();
1765
+ this.rootStore = injectStore();
1752
1766
  this.portalCommentsNodes = [];
1767
+ this.rendererMap = new Map();
1768
+ this.routedSet = new Set();
1753
1769
  }
1754
- createNode(type, node) {
1755
- const state = [
1756
- type,
1757
- null,
1758
- null,
1759
- [],
1760
- false,
1761
- undefined,
1762
- undefined,
1763
- undefined,
1764
- undefined,
1765
- undefined,
1766
- undefined,
1767
- undefined,
1768
- undefined,
1769
- undefined,
1770
- undefined,
1771
- ];
1772
- const rendererNode = Object.assign(node, { __ngt_renderer__: state });
1773
- // NOTE: assign ownerDocument to node so we can use HostListener in Component
1774
- if (!rendererNode['ownerDocument'])
1775
- rendererNode['ownerDocument'] = this.rootState.document;
1776
- // NOTE: assign injectorFactory on non-three type since
1777
- // rendererNode is an instance of DOM Node
1778
- if (state[0 /* NgtRendererClassId.type */] !== 'three') {
1779
- state[14 /* NgtRendererClassId.injectorFactory */] = () => getDebugNode(rendererNode)?.injector;
1780
- }
1781
- if (state[0 /* NgtRendererClassId.type */] === 'comment') {
1782
- // NOTE: we attach an arrow function to the Comment node
1783
- // In our directives, we can call this function to then start tracking the RendererNode
1784
- // this is done to limit the amount of Nodes we need to process for getCreationState
1785
- rendererNode[SPECIAL_INTERNAL_ADD_COMMENT] = (node) => {
1786
- if (node === 'args') {
1787
- this.argsCommentNodes.push(rendererNode);
1788
- }
1789
- else if (node === 'parent') {
1790
- this.parentCommentNodes.push(rendererNode);
1791
- }
1792
- else if (typeof node === 'object') {
1793
- this.portalCommentsNodes.push(node);
1794
- }
1795
- };
1796
- return rendererNode;
1797
- }
1798
- if (state[0 /* NgtRendererClassId.type */] === 'compound') {
1799
- state[8 /* NgtRendererClassId.queueOps */] = new Set();
1800
- state[9 /* NgtRendererClassId.attributes */] = state[10 /* NgtRendererClassId.properties */] = {};
1801
- return rendererNode;
1802
- }
1803
- return rendererNode;
1804
- }
1805
- isCompound(name) {
1806
- return this.rootState.compoundPrefixes.some((prefix) => name.startsWith(prefix));
1807
- }
1808
- isDOM(node) {
1809
- const rS = node['__ngt_renderer__'];
1810
- return (!rS ||
1811
- (rS[0 /* NgtRendererClassId.type */] !== 'compound' &&
1812
- (node instanceof Element || node instanceof Document || node instanceof Window)));
1813
- }
1814
- getClosestParentWithInstance(node) {
1815
- let parent = node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1816
- if (parent &&
1817
- parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound' &&
1818
- parent.__ngt_renderer__[7 /* NgtRendererClassId.compounded */] &&
1819
- parent.__ngt_renderer__[7 /* NgtRendererClassId.compounded */].__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
1820
- return parent.__ngt_renderer__[7 /* NgtRendererClassId.compounded */];
1821
- }
1822
- if (parent &&
1823
- parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'portal' &&
1824
- parent.__ngt_renderer__[13 /* NgtRendererClassId.portalContainer */]?.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
1825
- return parent.__ngt_renderer__[13 /* NgtRendererClassId.portalContainer */];
1826
- }
1827
- while (parent && parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'three') {
1828
- parent = parent.__ngt_renderer__[13 /* NgtRendererClassId.portalContainer */]
1829
- ? parent.__ngt_renderer__[13 /* NgtRendererClassId.portalContainer */]
1830
- : parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1831
- }
1832
- return parent;
1833
- }
1834
- getClosestParentWithCompound(node) {
1835
- if (node.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */]) {
1836
- return node.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */];
1837
- }
1838
- let parent = node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1839
- if (parent &&
1840
- parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound' &&
1841
- !parent.__ngt_renderer__[7 /* NgtRendererClassId.compounded */]) {
1842
- return parent;
1843
- }
1844
- while (parent &&
1845
- (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' ||
1846
- !parent.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */] ||
1847
- parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'compound')) {
1848
- parent = parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1849
- }
1850
- if (!parent)
1851
- return null;
1852
- if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' &&
1853
- parent.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */]) {
1854
- return parent.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */];
1770
+ createRenderer(hostElement, type) {
1771
+ const delegateRenderer = this.delegateRendererFactory.createRenderer(hostElement, type);
1772
+ if (!type)
1773
+ return delegateRenderer;
1774
+ // NOTE: might need to revisit this
1775
+ if (type['type'][HTML]) {
1776
+ this.rendererMap.set(type.id, delegateRenderer);
1777
+ return delegateRenderer;
1778
+ }
1779
+ if (type['type'][ROUTED_SCENE]) {
1780
+ this.routedSet.add(type.id);
1855
1781
  }
1856
- if (!parent.__ngt_renderer__[7 /* NgtRendererClassId.compounded */]) {
1857
- return parent;
1782
+ let renderer = this.rendererMap.get(type.id);
1783
+ if (!renderer) {
1784
+ this.rendererMap.set(type.id, (renderer = new NgtRenderer(delegateRenderer, this.rootStore, this.document, this.portalCommentsNodes, this.catalogue,
1785
+ // setting root scene if there's no routed scene OR this component is the routed Scene
1786
+ !hostElement && (this.routedSet.size === 0 || this.routedSet.has(type.id)))));
1858
1787
  }
1859
- return null;
1860
- }
1861
- processPortalContainer(portal) {
1862
- const injector = portal.__ngt_renderer__[14 /* NgtRendererClassId.injectorFactory */]?.();
1863
- if (!injector)
1864
- return;
1865
- const portalStore = injector.get(NGT_STORE, null);
1866
- if (!portalStore)
1867
- return;
1868
- const portalContainer = portalStore.get('scene');
1869
- if (!portalContainer)
1870
- return;
1871
- portal.__ngt_renderer__[13 /* NgtRendererClassId.portalContainer */] = this.createNode('three', portalContainer);
1788
+ return renderer;
1872
1789
  }
1873
- getCreationState() {
1874
- return [
1875
- this.firstNonInjectedDirective('argsCommentNodes', NgtArgs)?.value || [],
1876
- this.firstNonInjectedDirective('parentCommentNodes', NgtParent)?.value || null,
1877
- this.tryGetPortalStore(),
1878
- ];
1790
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtRendererFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1791
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtRendererFactory }); }
1792
+ }
1793
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtRendererFactory, decorators: [{
1794
+ type: Injectable
1795
+ }] });
1796
+ class NgtRenderer {
1797
+ constructor(delegate, rootStore, document, portalCommentsNodes, catalogue, isRoot = true) {
1798
+ this.delegate = delegate;
1799
+ this.rootStore = rootStore;
1800
+ this.document = document;
1801
+ this.portalCommentsNodes = portalCommentsNodes;
1802
+ this.catalogue = catalogue;
1803
+ this.isRoot = isRoot;
1804
+ this.argsCommentNodes = [];
1805
+ this.createText = this.delegate.createText.bind(this.delegate);
1806
+ this.destroy = this.delegate.destroy.bind(this.delegate);
1807
+ this.destroyNode = null;
1808
+ this.selectRootElement = this.delegate.selectRootElement.bind(this.delegate);
1809
+ this.nextSibling = this.delegate.nextSibling.bind(this.delegate);
1810
+ this.addClass = this.delegate.addClass.bind(this.delegate);
1811
+ this.removeClass = this.delegate.removeClass.bind(this.delegate);
1812
+ this.setStyle = this.delegate.setStyle.bind(this.delegate);
1813
+ this.removeStyle = this.delegate.removeStyle.bind(this.delegate);
1814
+ this.setValue = this.delegate.setValue.bind(this.delegate);
1879
1815
  }
1880
- setParent(node, parent) {
1881
- if (!node.__ngt_renderer__[1 /* NgtRendererClassId.parent */]) {
1882
- node.__ngt_renderer__[1 /* NgtRendererClassId.parent */] = parent;
1816
+ createElement(name, namespace) {
1817
+ const element = this.delegate.createElement(name, namespace);
1818
+ // on first pass, we return the Root Scene as the root node
1819
+ if (this.isRoot) {
1820
+ this.isRoot = false;
1821
+ const node = createNode('three', this.rootStore.snapshot.scene, this.document);
1822
+ node.__ngt_renderer__[6 /* NgtRendererClassId.injectorFactory */] = () => getDebugNode(element)?.injector;
1823
+ return node;
1883
1824
  }
1884
- }
1885
- addChild(node, child) {
1886
- if (!node.__ngt_renderer__[3 /* NgtRendererClassId.children */].includes(child)) {
1887
- node.__ngt_renderer__[3 /* NgtRendererClassId.children */].push(child);
1825
+ if (name === SPECIAL_DOM_TAG.NGT_PORTAL) {
1826
+ return createNode('portal', element, this.document);
1888
1827
  }
1889
- }
1890
- removeChild(node, child) {
1891
- const index = node.__ngt_renderer__?.[3 /* NgtRendererClassId.children */].findIndex((c) => child === c);
1892
- if (index >= 0) {
1893
- node.__ngt_renderer__[3 /* NgtRendererClassId.children */].splice(index, 1);
1828
+ if (name === SPECIAL_DOM_TAG.NGT_VALUE) {
1829
+ const instanceStore = signalStore({ parent: null, objects: [], nonObjects: [] });
1830
+ return createNode('three', Object.assign({ __ngt_renderer__: { rawValue: undefined } },
1831
+ // NOTE: we assign this manually to a raw value node
1832
+ // because we say it is a 'three' node but we're not using prepare()
1833
+ {
1834
+ __ngt__: {
1835
+ isRaw: true,
1836
+ instanceStore,
1837
+ setParent(parent) {
1838
+ instanceStore.update({ parent });
1839
+ },
1840
+ },
1841
+ }), this.document);
1894
1842
  }
1895
- }
1896
- setCompound(compound, instance) {
1897
- const instanceRS = instance.__ngt_renderer__;
1898
- if (instanceRS && instanceRS[1 /* NgtRendererClassId.parent */]) {
1899
- const parentRS = instanceRS[1 /* NgtRendererClassId.parent */].__ngt_renderer__;
1900
- // NOTE: if instance is already compounded by its parent. skip
1901
- if (parentRS[0 /* NgtRendererClassId.type */] === 'compound' && parentRS[7 /* NgtRendererClassId.compounded */] === instance) {
1902
- return;
1843
+ const [injectedArgs] = [this.getNgtArgs()?.value || []];
1844
+ if (name === SPECIAL_DOM_TAG.NGT_PRIMITIVE) {
1845
+ if (!injectedArgs[0])
1846
+ throw new Error(`[NGT] ngt-primitive without args is invalid`);
1847
+ const object = injectedArgs[0];
1848
+ let localState = getLocalState(object);
1849
+ if (!localState) {
1850
+ // NOTE: if an object isn't already "prepared", we prepare it
1851
+ prepare(object, { store: this.rootStore, primitive: true });
1903
1852
  }
1853
+ return createNode('three', object, this.document);
1904
1854
  }
1905
- const rS = compound.__ngt_renderer__;
1906
- rS[7 /* NgtRendererClassId.compounded */] = instance;
1907
- for (const key of Object.keys(rS[9 /* NgtRendererClassId.attributes */])) {
1908
- this.applyAttribute(instance, key, rS[9 /* NgtRendererClassId.attributes */][key]);
1909
- }
1910
- for (const key of Object.keys(rS[10 /* NgtRendererClassId.properties */])) {
1911
- this.applyProperty(instance, key, rS[10 /* NgtRendererClassId.properties */][key]);
1855
+ const threeName = kebabToPascal(name.startsWith('ngt-') ? name.slice(4) : name);
1856
+ const threeTarget = this.catalogue[threeName];
1857
+ // we have the THREE constructor here, handle it
1858
+ if (threeTarget) {
1859
+ const instance = prepare(new threeTarget(...injectedArgs), { store: this.rootStore });
1860
+ const node = createNode('three', instance, this.document);
1861
+ const localState = getLocalState(instance);
1862
+ // auto-attach for geometry and material
1863
+ if (is.geometry(instance)) {
1864
+ localState.attach = ['geometry'];
1865
+ }
1866
+ else if (is.material(instance)) {
1867
+ localState.attach = ['material'];
1868
+ }
1869
+ return node;
1912
1870
  }
1913
- this.executeOperation(compound);
1871
+ return createNode('dom', element, this.document);
1914
1872
  }
1915
- queueOperation(node, op) {
1916
- node.__ngt_renderer__[8 /* NgtRendererClassId.queueOps */].add(op);
1917
- }
1918
- executeOperation(node, type = 'op') {
1919
- const rS = node.__ngt_renderer__;
1920
- // TODO: maybe an array with pop() would work better
1921
- if (rS[8 /* NgtRendererClassId.queueOps */]?.size) {
1922
- rS[8 /* NgtRendererClassId.queueOps */].forEach((op) => {
1923
- if (op[0 /* NgtQueueOpClassId.type */] === type) {
1924
- op[1 /* NgtQueueOpClassId.op */]();
1925
- rS[8 /* NgtRendererClassId.queueOps */].delete(op);
1926
- }
1927
- });
1928
- }
1929
- }
1930
- applyAttribute(node, name, value) {
1931
- const rS = node.__ngt_renderer__;
1932
- if (rS[4 /* NgtRendererClassId.destroyed */])
1933
- return;
1934
- if (name === SPECIAL_PROPERTIES.RENDER_PRIORITY) {
1935
- // NOTE: priority needs to be set as an attribute string so that they can be set as early as possible
1936
- // we convert that string to a number. if it's invalid, default 0
1937
- let priority = Number(value);
1938
- if (isNaN(priority)) {
1939
- priority = 0;
1940
- console.warn(`[NGT] "priority" is an invalid number, default to 0`);
1941
- }
1942
- const localState = getLocalState(node);
1943
- if (localState) {
1944
- localState.priority = priority;
1945
- }
1946
- }
1947
- if (name === SPECIAL_PROPERTIES.COMPOUND) {
1948
- // NOTE: we set the compound property on instance node now so we know that this instance is being compounded
1949
- rS[5 /* NgtRendererClassId.compound */] = [value === '' || value === 'first', {}];
1950
- return;
1951
- }
1952
- if (name === SPECIAL_PROPERTIES.ATTACH) {
1953
- // NOTE: handle attach as tring
1954
- const paths = value.split('.');
1955
- if (paths.length) {
1956
- const localState = getLocalState(node);
1957
- if (localState) {
1958
- localState.attach = paths;
1959
- }
1960
- }
1961
- return;
1962
- }
1963
- if (name === SPECIAL_PROPERTIES.RAW_VALUE) {
1964
- // NOTE: coercion
1965
- let maybeCoerced = value;
1966
- if (maybeCoerced === '' || maybeCoerced === 'true' || maybeCoerced === 'false') {
1967
- maybeCoerced = maybeCoerced === 'true' || maybeCoerced === '';
1968
- }
1969
- else if (!isNaN(Number(maybeCoerced))) {
1970
- maybeCoerced = Number(maybeCoerced);
1971
- }
1972
- rS[11 /* NgtRendererClassId.rawValue */] = maybeCoerced;
1973
- return;
1974
- }
1975
- applyProps(node, { [name]: value });
1976
- this.updateNativeProps(node, name, value);
1977
- }
1978
- applyProperty(node, name, value) {
1979
- const rS = node.__ngt_renderer__;
1980
- if (rS[4 /* NgtRendererClassId.destroyed */])
1981
- return;
1982
- // [ref]
1983
- if (name === SPECIAL_PROPERTIES.REF && is.ref(value)) {
1984
- rS[12 /* NgtRendererClassId.ref */] = value;
1985
- value.nativeElement = node;
1986
- return;
1987
- }
1988
- const localState = getLocalState(node);
1989
- const parent = localState?.instanceStore.get('parent') || rS[1 /* NgtRendererClassId.parent */];
1990
- // [rawValue]
1991
- if (localState?.isRaw && name === SPECIAL_PROPERTIES.RAW_VALUE) {
1992
- rS[11 /* NgtRendererClassId.rawValue */] = value;
1993
- if (parent)
1994
- attachThreeChild(parent, node);
1995
- return;
1996
- }
1997
- // [attach]
1998
- if (name === SPECIAL_PROPERTIES.ATTACH) {
1999
- if (localState)
2000
- localState.attach = Array.isArray(value) ? value.map((v) => v.toString()) : value;
2001
- if (parent)
2002
- attachThreeChild(parent, node);
2003
- return;
2004
- }
2005
- const compound = rS[5 /* NgtRendererClassId.compound */];
2006
- if (compound?.[1 /* NgtCompoundClassId.props */] &&
2007
- name in compound[1 /* NgtCompoundClassId.props */] &&
2008
- !compound[0 /* NgtCompoundClassId.applyFirst */]) {
2009
- value = compound[1 /* NgtCompoundClassId.props */][name];
2010
- }
2011
- applyProps(node, { [name]: value });
2012
- this.updateNativeProps(node, name, value);
2013
- }
2014
- applyParameters(node, parameters) {
2015
- const rS = node.__ngt_renderer__;
2016
- if (rS[4 /* NgtRendererClassId.destroyed */])
2017
- return;
2018
- applyProps(node, parameters);
2019
- this.updateNativeProps(node, 'parameters', parameters);
2020
- }
2021
- get rootScene() {
2022
- return this.rootState.store.get('scene');
2023
- }
2024
- destroy(node, parent) {
2025
- const rS = node.__ngt_renderer__;
2026
- if (!rS || rS[4 /* NgtRendererClassId.destroyed */])
2027
- return;
2028
- if (rS[0 /* NgtRendererClassId.type */] === 'three') {
2029
- rS[5 /* NgtRendererClassId.compound */] = undefined;
2030
- rS[6 /* NgtRendererClassId.compoundParent */] = undefined;
2031
- const localState = getLocalState(node);
2032
- if (localState?.instanceStore) {
2033
- localState.instanceStore.get('objects').forEach((obj) => this.destroy(obj, parent));
2034
- localState.instanceStore.get('nonObjects').forEach((obj) => this.destroy(obj, parent));
2035
- }
2036
- if (localState?.afterUpdate)
2037
- localState.afterUpdate.complete();
2038
- if (localState?.afterAttach)
2039
- localState.afterAttach.complete();
2040
- delete localState['objects'];
2041
- delete localState['nonObjects'];
2042
- delete localState['parent'];
2043
- delete localState['nativeProps'];
2044
- delete localState['add'];
2045
- delete localState['remove'];
2046
- delete localState['afterUpdate'];
2047
- delete localState['afterAttach'];
2048
- delete localState['store'];
2049
- delete localState['handlers'];
2050
- if (!localState?.primitive) {
2051
- delete node['__ngt__'];
2052
- }
2053
- }
2054
- if (rS[0 /* NgtRendererClassId.type */] === 'comment') {
2055
- rS[14 /* NgtRendererClassId.injectorFactory */] = null;
2056
- delete node[SPECIAL_INTERNAL_ADD_COMMENT];
2057
- if (!this.removeCommentNode(node, this.argsCommentNodes)) {
2058
- this.removeCommentNode(node, this.parentCommentNodes);
2059
- }
2060
- }
2061
- if (rS[0 /* NgtRendererClassId.type */] === 'portal') {
2062
- rS[14 /* NgtRendererClassId.injectorFactory */] = null;
2063
- this.removeCommentNode(node, this.portalCommentsNodes);
2064
- }
2065
- if (rS[0 /* NgtRendererClassId.type */] === 'compound') {
2066
- rS[7 /* NgtRendererClassId.compounded */] = undefined;
2067
- rS[9 /* NgtRendererClassId.attributes */] = null;
2068
- rS[10 /* NgtRendererClassId.properties */] = null;
2069
- this.executeOperation(node, 'cleanUp');
2070
- rS[8 /* NgtRendererClassId.queueOps */].clear();
2071
- rS[8 /* NgtRendererClassId.queueOps */] = null;
2072
- }
2073
- if (rS[12 /* NgtRendererClassId.ref */]) {
2074
- // nullify ref
2075
- // but we do it later so that it doesn't hinder render
2076
- // TODO: will this cause memory leak?
2077
- requestAnimationFrame(() => {
2078
- rS[12 /* NgtRendererClassId.ref */].nativeElement = null;
2079
- rS[12 /* NgtRendererClassId.ref */] = undefined;
2080
- });
2081
- }
2082
- // nullify parent
2083
- rS[1 /* NgtRendererClassId.parent */] = null;
2084
- for (const renderChild of rS[3 /* NgtRendererClassId.children */] || []) {
2085
- if (renderChild.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three' && parent) {
2086
- if (parent.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three') {
2087
- removeThreeChild(parent, renderChild, true);
2088
- continue;
2089
- }
2090
- const closestInstance = this.getClosestParentWithInstance(parent);
2091
- if (closestInstance) {
2092
- removeThreeChild(closestInstance, renderChild, true);
2093
- }
2094
- }
2095
- this.destroy(renderChild, parent);
2096
- }
2097
- rS[3 /* NgtRendererClassId.children */] = [];
2098
- rS[4 /* NgtRendererClassId.destroyed */] = true;
2099
- if (parent) {
2100
- this.removeChild(parent, node);
2101
- }
2102
- }
2103
- removeCommentNode(node, nodes) {
2104
- const index = nodes.findIndex((comment) => comment === node);
2105
- if (index > -1) {
2106
- nodes.splice(index, 1);
2107
- return true;
2108
- }
2109
- return false;
2110
- }
2111
- updateNativeProps(node, key, value) {
2112
- const localState = getLocalState(node);
2113
- localState?.setNativeProps(key, value);
2114
- }
2115
- firstNonInjectedDirective(listProperty, dir) {
2116
- let directive;
2117
- const destroyed = [];
2118
- let i = this[listProperty].length - 1;
2119
- while (i >= 0) {
2120
- const comment = this[listProperty][i];
2121
- if (comment.__ngt_renderer__[4 /* NgtRendererClassId.destroyed */]) {
2122
- destroyed.push(i);
2123
- i--;
2124
- continue;
2125
- }
2126
- const injector = comment.__ngt_renderer__[14 /* NgtRendererClassId.injectorFactory */]();
2127
- if (!injector) {
2128
- i--;
2129
- continue;
2130
- }
2131
- const instance = injector.get(dir, null);
2132
- if (instance && instance.validate()) {
2133
- directive = instance;
2134
- break;
2135
- }
2136
- i--;
2137
- }
2138
- destroyed.forEach((index) => {
2139
- this[listProperty].splice(index, 1);
2140
- });
2141
- return directive;
2142
- }
2143
- tryGetPortalStore() {
2144
- let store;
2145
- const destroyed = [];
2146
- // we only care about the portal states because NgtStore only differs per Portal
2147
- let i = this.portalCommentsNodes.length - 1;
2148
- while (i >= 0) {
2149
- // loop through the portal state backwards to find the closest NgtStore
2150
- const portal = this.portalCommentsNodes[i];
2151
- if (portal.__ngt_renderer__[4 /* NgtRendererClassId.destroyed */]) {
2152
- destroyed.push(i);
2153
- i--;
2154
- continue;
2155
- }
2156
- const injector = portal.__ngt_renderer__[14 /* NgtRendererClassId.injectorFactory */]();
2157
- if (!injector) {
2158
- i--;
2159
- continue;
2160
- }
2161
- const instance = injector.get(NGT_STORE, null);
2162
- // only the instance with previousRoot should pass
2163
- if (instance && instance.get('previousRoot')) {
2164
- store = instance;
2165
- break;
2166
- }
2167
- i--;
2168
- }
2169
- destroyed.forEach((index) => {
2170
- this.portalCommentsNodes.splice(index, 1);
2171
- });
2172
- return store || this.rootState.store;
2173
- }
2174
- }
2175
-
2176
- class NgtRendererFactory {
2177
- constructor() {
2178
- this.delegateRendererFactory = inject(RendererFactory2, { skipSelf: true });
2179
- this.catalogue = injectNgtCatalogue();
2180
- this.rendererMap = new Map();
2181
- this.routedSet = new Set();
2182
- // NOTE: all Renderer instances under the same NgtCanvas share the same Store
2183
- this.rendererStore = new NgtRendererStore({
2184
- store: injectNgtStore(),
2185
- compoundPrefixes: inject(NGT_COMPOUND_PREFIXES),
2186
- document: inject(DOCUMENT),
2187
- });
2188
- }
2189
- createRenderer(hostElement, type) {
2190
- const delegateRenderer = this.delegateRendererFactory.createRenderer(hostElement, type);
2191
- if (!type)
2192
- return delegateRenderer;
2193
- // NOTE: might need to revisit this
2194
- if (type['type'][HTML]) {
2195
- this.rendererMap.set(type.id, delegateRenderer);
2196
- return delegateRenderer;
2197
- }
2198
- if (type['type'][ROUTED_SCENE]) {
2199
- this.routedSet.add(type.id);
2200
- }
2201
- let renderer = this.rendererMap.get(type.id);
2202
- if (!renderer) {
2203
- this.rendererMap.set(type.id, (renderer = new NgtRenderer(delegateRenderer, this.rendererStore, this.catalogue,
2204
- // setting root scene if there's no routed scene OR this component is the routed Scene
2205
- !hostElement && (this.routedSet.size === 0 || this.routedSet.has(type.id)))));
2206
- }
2207
- return renderer;
2208
- }
2209
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtRendererFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2210
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtRendererFactory }); }
2211
- }
2212
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtRendererFactory, decorators: [{
2213
- type: Injectable
2214
- }] });
2215
- class NgtRenderer {
2216
- constructor(delegate, store, catalogue, isRoot = true) {
2217
- this.delegate = delegate;
2218
- this.store = store;
2219
- this.catalogue = catalogue;
2220
- this.isRoot = isRoot;
2221
- this.createText = this.delegate.createText.bind(this.delegate);
2222
- this.destroy = this.delegate.destroy.bind(this.delegate);
2223
- this.destroyNode = null;
2224
- this.selectRootElement = this.delegate.selectRootElement.bind(this.delegate);
2225
- this.nextSibling = this.delegate.nextSibling.bind(this.delegate);
2226
- this.addClass = this.delegate.addClass.bind(this.delegate);
2227
- this.removeClass = this.delegate.removeClass.bind(this.delegate);
2228
- this.setStyle = this.delegate.setStyle.bind(this.delegate);
2229
- this.removeStyle = this.delegate.removeStyle.bind(this.delegate);
2230
- this.setValue = this.delegate.setValue.bind(this.delegate);
2231
- }
2232
- createElement(name, namespace) {
2233
- const element = this.delegate.createElement(name, namespace);
2234
- // on first pass, we return the Root Scene as the root node
2235
- if (this.isRoot) {
2236
- this.isRoot = false;
2237
- const node = this.store.createNode('three', this.store.rootScene);
2238
- node.__ngt_renderer__[14 /* NgtRendererClassId.injectorFactory */] = () => getDebugNode(element)?.injector;
2239
- return node;
2240
- }
2241
- if (this.store.isCompound(name)) {
2242
- return this.store.createNode('compound', element);
2243
- }
2244
- if (name === SPECIAL_DOM_TAG.NGT_PORTAL) {
2245
- return this.store.createNode('portal', element);
2246
- }
2247
- if (name === SPECIAL_DOM_TAG.NGT_VALUE) {
2248
- const instanceStore = signalStore({
2249
- nativeProps: {},
2250
- parent: null,
2251
- objects: [],
2252
- nonObjects: [],
2253
- });
2254
- return this.store.createNode('three', Object.assign({ __ngt_renderer__: { rawValue: undefined } },
2255
- // NOTE: we assign this manually to a raw value node
2256
- // because we say it is a 'three' node but we're not using prepare()
2257
- {
2258
- __ngt__: {
2259
- isRaw: true,
2260
- instanceStore,
2261
- setNativeProps(key, value) {
2262
- instanceStore.update((prev) => ({ nativeProps: { ...prev.nativeProps, [key]: value } }));
2263
- },
2264
- setParent(parent) {
2265
- instanceStore.update({ parent });
2266
- },
2267
- },
2268
- }));
2269
- }
2270
- const [injectedArgs, injectedParent, store] = this.store.getCreationState();
2271
- let parent = injectedParent;
2272
- if (typeof injectedParent === 'string') {
2273
- parent = store
2274
- .get('scene')
2275
- .getObjectByName(injectedParent);
2276
- }
2277
- if (name === SPECIAL_DOM_TAG.NGT_PRIMITIVE) {
2278
- if (!injectedArgs[0])
2279
- throw new Error(`[NGT] ngt-primitive without args is invalid`);
2280
- const object = injectedArgs[0];
2281
- let localState = getLocalState(object);
2282
- if (!localState) {
2283
- // NOTE: if an object isn't already "prepared", we prepare it
2284
- localState = getLocalState(prepare(object, { store, args: injectedArgs, primitive: true }));
2285
- }
2286
- if (!localState.store)
2287
- localState.store = store;
2288
- const node = this.store.createNode('three', object);
2289
- if (parent) {
2290
- node.__ngt_renderer__[2 /* NgtRendererClassId.injectedParent */] = parent;
2291
- }
2292
- return node;
2293
- }
2294
- const threeName = kebabToPascal(name.startsWith('ngt') ? name.slice(4) : name);
2295
- const threeTarget = this.catalogue[threeName];
2296
- // we have the THREE constructor here, handle it
2297
- if (threeTarget) {
2298
- const instance = prepare(new threeTarget(...injectedArgs), { store, args: injectedArgs });
2299
- const node = this.store.createNode('three', instance);
2300
- const localState = getLocalState(instance);
2301
- // auto-attach for geometry and material
2302
- if (is.geometry(instance)) {
2303
- localState.attach = ['geometry'];
2304
- }
2305
- else if (is.material(instance)) {
2306
- localState.attach = ['material'];
1873
+ createComment(value) {
1874
+ const comment = this.delegate.createComment(value);
1875
+ // NOTE: we attach an arrow function to the Comment node
1876
+ // In our directives, we can call this function to then start tracking the RendererNode
1877
+ // this is done to limit the amount of Nodes we need to process for getCreationState
1878
+ comment[SPECIAL_INTERNAL_ADD_COMMENT] = (node) => {
1879
+ if (node === 'args') {
1880
+ this.argsCommentNodes.push(comment);
2307
1881
  }
2308
- if (parent) {
2309
- node.__ngt_renderer__[2 /* NgtRendererClassId.injectedParent */] = parent;
1882
+ else if (typeof node === 'object') {
1883
+ this.portalCommentsNodes.push(node);
2310
1884
  }
2311
- return node;
2312
- }
2313
- return this.store.createNode('dom', element);
2314
- }
2315
- createComment(value) {
2316
- return this.store.createNode('comment', this.delegate.createComment(value));
1885
+ };
1886
+ return createNode('comment', comment, this.document);
2317
1887
  }
2318
1888
  appendChild(parent, newChild) {
2319
1889
  const pRS = parent.__ngt_renderer__;
2320
1890
  const cRS = newChild.__ngt_renderer__;
2321
1891
  if (pRS[0 /* NgtRendererClassId.type */] === 'dom' &&
2322
1892
  (newChild instanceof Text || cRS[0 /* NgtRendererClassId.type */] === 'dom')) {
2323
- this.store.addChild(parent, newChild);
1893
+ addChild(parent, newChild);
2324
1894
  this.delegate.appendChild(parent, newChild);
2325
1895
  if (cRS) {
2326
- this.store.setParent(newChild, parent);
1896
+ setParent(newChild, parent);
2327
1897
  if (this.shouldFindGrandparentInstance(pRS, cRS, newChild)) {
2328
1898
  // we'll try to get the grandparent instance here so that we can run appendChild with both instances
2329
- const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
1899
+ const closestGrandparentInstance = getClosestParentWithInstance(parent);
2330
1900
  if (closestGrandparentInstance)
2331
1901
  this.appendChild(closestGrandparentInstance, newChild);
2332
1902
  }
@@ -2334,48 +1904,26 @@ class NgtRenderer {
2334
1904
  return;
2335
1905
  }
2336
1906
  if (cRS?.[0 /* NgtRendererClassId.type */] === 'comment') {
2337
- this.store.setParent(newChild, parent);
1907
+ setParent(newChild, parent);
2338
1908
  return;
2339
1909
  }
2340
- if (cRS?.[2 /* NgtRendererClassId.injectedParent */]) {
2341
- if (is.ref(cRS[2 /* NgtRendererClassId.injectedParent */])) {
2342
- const injector = cRS[14 /* NgtRendererClassId.injectorFactory */]()?.get(Injector, null);
2343
- if (!injector) {
2344
- console.warn(`[NGT] NgtRenderer is attempting to start an effect for injectedParent but no Injector is found.`);
2345
- return;
2346
- }
2347
- const watcher = effect(() => {
2348
- const injectedParent = cRS[2 /* NgtRendererClassId.injectedParent */]
2349
- .nativeElement;
2350
- if (injectedParent && injectedParent !== parent) {
2351
- this.appendChild(injectedParent, newChild);
2352
- // only run this effect once
2353
- // as soon as we re-run appendChild with the injectedParent, we stop the effect
2354
- watcher.destroy();
2355
- }
2356
- }, { injector, manualCleanup: true });
2357
- return;
2358
- }
2359
- else if (parent !== cRS[2 /* NgtRendererClassId.injectedParent */]) {
2360
- this.appendChild(cRS[2 /* NgtRendererClassId.injectedParent */], newChild);
2361
- return;
2362
- }
2363
- }
2364
- this.store.setParent(newChild, parent);
2365
- this.store.addChild(parent, newChild);
1910
+ setParent(newChild, parent);
1911
+ addChild(parent, newChild);
2366
1912
  // if new child is a portal
2367
1913
  if (cRS?.[0 /* NgtRendererClassId.type */] === 'portal') {
2368
- this.store.processPortalContainer(newChild);
2369
- if (cRS[13 /* NgtRendererClassId.portalContainer */]) {
2370
- this.appendChild(parent, cRS[13 /* NgtRendererClassId.portalContainer */]);
1914
+ if (!cRS[5 /* NgtRendererClassId.portalContainer */])
1915
+ this.processPortalContainer(newChild);
1916
+ if (cRS[5 /* NgtRendererClassId.portalContainer */]) {
1917
+ this.appendChild(parent, cRS[5 /* NgtRendererClassId.portalContainer */]);
2371
1918
  }
2372
1919
  return;
2373
1920
  }
2374
1921
  // if parent is a portal
2375
1922
  if (pRS[0 /* NgtRendererClassId.type */] === 'portal') {
2376
- this.store.processPortalContainer(parent);
2377
- if (pRS[13 /* NgtRendererClassId.portalContainer */]) {
2378
- this.appendChild(pRS[13 /* NgtRendererClassId.portalContainer */], newChild);
1923
+ if (!pRS[5 /* NgtRendererClassId.portalContainer */])
1924
+ this.processPortalContainer(parent);
1925
+ if (pRS[5 /* NgtRendererClassId.portalContainer */]) {
1926
+ this.appendChild(pRS[5 /* NgtRendererClassId.portalContainer */], newChild);
2379
1927
  }
2380
1928
  return;
2381
1929
  }
@@ -2389,43 +1937,17 @@ class NgtRenderer {
2389
1937
  return;
2390
1938
  // attach THREE child
2391
1939
  attachThreeChild(parent, newChild);
2392
- // here, we handle the special case of if the parent has a compoundParent, which means this child is part of a compound parent template
2393
- if (!cRS[5 /* NgtRendererClassId.compound */])
2394
- return;
2395
- const closestGrandparentWithCompound = this.store.getClosestParentWithCompound(parent);
2396
- if (!closestGrandparentWithCompound)
2397
- return;
2398
- this.appendChild(closestGrandparentWithCompound, newChild);
2399
1940
  return;
2400
1941
  }
2401
1942
  // if only the parent is the THREE instance
2402
1943
  if (pRS[0 /* NgtRendererClassId.type */] === 'three') {
2403
- for (const renderChild of cRS?.[3 /* NgtRendererClassId.children */] || []) {
1944
+ for (const renderChild of cRS?.[2 /* NgtRendererClassId.children */] || []) {
2404
1945
  this.appendChild(parent, renderChild);
2405
1946
  }
2406
1947
  }
2407
- // if parent is a compound
2408
- if (pRS[0 /* NgtRendererClassId.type */] === 'compound') {
2409
- // if compound doesn't have a THREE instance set yet
2410
- if (!pRS[7 /* NgtRendererClassId.compounded */] && cRS[0 /* NgtRendererClassId.type */] === 'three') {
2411
- // if child is indeed an ngtCompound
2412
- if (cRS[5 /* NgtRendererClassId.compound */])
2413
- this.store.setCompound(parent, newChild);
2414
- // if not, we track the parent (that is supposedly the compound component) on this three instance
2415
- else if (!cRS[6 /* NgtRendererClassId.compoundParent */])
2416
- cRS[6 /* NgtRendererClassId.compoundParent */] = parent;
2417
- }
2418
- // reset the compound if it's changed
2419
- if (pRS[7 /* NgtRendererClassId.compounded */] &&
2420
- cRS[0 /* NgtRendererClassId.type */] === 'three' &&
2421
- cRS[5 /* NgtRendererClassId.compound */] &&
2422
- pRS[7 /* NgtRendererClassId.compounded */] !== newChild) {
2423
- this.store.setCompound(parent, newChild);
2424
- }
2425
- }
2426
1948
  if (this.shouldFindGrandparentInstance(pRS, cRS, newChild)) {
2427
1949
  // we'll try to get the grandparent instance here so that we can run appendChild with both instances
2428
- const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
1950
+ const closestGrandparentInstance = getClosestParentWithInstance(parent);
2429
1951
  if (closestGrandparentInstance)
2430
1952
  this.appendChild(closestGrandparentInstance, newChild);
2431
1953
  return;
@@ -2443,42 +1965,32 @@ class NgtRenderer {
2443
1965
  parent instanceof Element &&
2444
1966
  (oldChild instanceof Element || oldChild instanceof Text || oldChild instanceof Comment)) {
2445
1967
  this.delegate.removeChild(parent, oldChild);
2446
- this.store.destroy(oldChild, parent);
1968
+ this.destroyInternal(oldChild, parent);
2447
1969
  return;
2448
1970
  }
2449
1971
  if (cRS[0 /* NgtRendererClassId.type */] === 'dom' && (!pRS || pRS[0 /* NgtRendererClassId.type */] === 'dom')) {
2450
1972
  this.delegate.removeChild(parent, oldChild);
2451
- this.store.destroy(oldChild, parent);
1973
+ this.destroyInternal(oldChild, parent);
2452
1974
  return;
2453
1975
  }
2454
1976
  if (pRS[0 /* NgtRendererClassId.type */] === 'three' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
2455
1977
  removeThreeChild(parent, oldChild, true);
2456
- this.store.destroy(oldChild, parent);
1978
+ this.destroyInternal(oldChild, parent);
2457
1979
  return;
2458
1980
  }
2459
- if (pRS[0 /* NgtRendererClassId.type */] === 'compound') {
2460
- if (pRS[7 /* NgtRendererClassId.compounded */]) {
2461
- this.removeChild(pRS[7 /* NgtRendererClassId.compounded */], oldChild, isHostElement);
2462
- return;
2463
- }
2464
- if (pRS[1 /* NgtRendererClassId.parent */]) {
2465
- this.removeChild(pRS[1 /* NgtRendererClassId.parent */], oldChild, isHostElement);
2466
- return;
2467
- }
2468
- }
2469
1981
  if (pRS[0 /* NgtRendererClassId.type */] === 'portal' &&
2470
- pRS[13 /* NgtRendererClassId.portalContainer */]?.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
2471
- this.removeChild(pRS[13 /* NgtRendererClassId.portalContainer */], oldChild, isHostElement);
1982
+ pRS[5 /* NgtRendererClassId.portalContainer */]?.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three') {
1983
+ this.removeChild(pRS[5 /* NgtRendererClassId.portalContainer */], oldChild, isHostElement);
2472
1984
  return;
2473
1985
  }
2474
1986
  if (pRS[0 /* NgtRendererClassId.type */] === 'three') {
2475
- this.store.destroy(oldChild, parent);
1987
+ this.destroyInternal(oldChild, parent);
2476
1988
  return;
2477
1989
  }
2478
- const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
1990
+ const closestGrandparentInstance = getClosestParentWithInstance(parent);
2479
1991
  if (closestGrandparentInstance)
2480
1992
  this.removeChild(closestGrandparentInstance, oldChild, isHostElement);
2481
- this.store.destroy(oldChild, closestGrandparentInstance);
1993
+ this.destroyInternal(oldChild, closestGrandparentInstance);
2482
1994
  }
2483
1995
  parentNode(node) {
2484
1996
  const rS = node.__ngt_renderer__;
@@ -2488,17 +2000,42 @@ class NgtRenderer {
2488
2000
  }
2489
2001
  setAttributeInternal(el, name, value, namespace) {
2490
2002
  const rS = el.__ngt_renderer__;
2491
- if (rS[0 /* NgtRendererClassId.type */] === 'compound') {
2492
- // we don't have the compound instance yet
2493
- rS[9 /* NgtRendererClassId.attributes */][name] = value;
2494
- if (!rS[7 /* NgtRendererClassId.compounded */]) {
2495
- this.store.queueOperation(el, ['op', () => this.setAttribute(el, name, value, namespace)]);
2496
- return false;
2497
- }
2498
- return this.setAttributeInternal(rS[7 /* NgtRendererClassId.compounded */], name, value, namespace);
2499
- }
2003
+ if (rS[3 /* NgtRendererClassId.destroyed */])
2004
+ return false;
2500
2005
  if (rS[0 /* NgtRendererClassId.type */] === 'three') {
2501
- this.store.applyAttribute(el, name, value);
2006
+ if (name === SPECIAL_PROPERTIES.RENDER_PRIORITY) {
2007
+ // NOTE: priority needs to be set as an attribute string so that they can be set as early as possible
2008
+ // we convert that string to a number. if it's invalid, default 0
2009
+ let priority = Number(value);
2010
+ if (isNaN(priority)) {
2011
+ priority = 0;
2012
+ console.warn(`[NGT] "priority" is an invalid number, default to 0`);
2013
+ }
2014
+ const localState = getLocalState(el);
2015
+ if (localState)
2016
+ localState.priority = priority;
2017
+ }
2018
+ else if (name === SPECIAL_PROPERTIES.ATTACH) {
2019
+ // NOTE: handle attach as string
2020
+ const paths = value.split('.');
2021
+ if (paths.length) {
2022
+ const localState = getLocalState(el);
2023
+ if (localState)
2024
+ localState.attach = paths;
2025
+ }
2026
+ }
2027
+ else if (name === SPECIAL_PROPERTIES.RAW_VALUE) {
2028
+ // NOTE: coercion
2029
+ let maybeCoerced = value;
2030
+ if (maybeCoerced === '' || maybeCoerced === 'true' || maybeCoerced === 'false') {
2031
+ maybeCoerced = maybeCoerced === 'true' || maybeCoerced === '';
2032
+ }
2033
+ else if (!isNaN(Number(maybeCoerced))) {
2034
+ maybeCoerced = Number(maybeCoerced);
2035
+ }
2036
+ rS[4 /* NgtRendererClassId.rawValue */] = maybeCoerced;
2037
+ }
2038
+ applyProps(el, { [name]: value });
2502
2039
  return false;
2503
2040
  }
2504
2041
  return true;
@@ -2516,28 +2053,36 @@ class NgtRenderer {
2516
2053
  }
2517
2054
  }
2518
2055
  setProperty(el, name, value) {
2519
- // TODO: should we support ref value
2520
2056
  const rS = el.__ngt_renderer__;
2521
- if (rS[0 /* NgtRendererClassId.type */] === 'compound') {
2522
- // we don't have the compound instance yet
2523
- rS[10 /* NgtRendererClassId.properties */][name] = value;
2524
- if (!rS[7 /* NgtRendererClassId.compounded */]) {
2525
- this.store.queueOperation(el, ['op', () => this.setProperty(el, name, value)]);
2526
- return;
2527
- }
2528
- if (rS[7 /* NgtRendererClassId.compounded */].__ngt_renderer__[5 /* NgtRendererClassId.compound */]) {
2529
- Object.assign(rS[7 /* NgtRendererClassId.compounded */].__ngt_renderer__[5 /* NgtRendererClassId.compound */][1 /* NgtCompoundClassId.props */], { [name]: value });
2530
- }
2531
- this.setProperty(rS[7 /* NgtRendererClassId.compounded */], name, value);
2057
+ if (rS[3 /* NgtRendererClassId.destroyed */])
2532
2058
  return;
2533
- }
2534
2059
  if (rS[0 /* NgtRendererClassId.type */] === 'three') {
2535
2060
  if (name === SPECIAL_PROPERTIES.PARAMETERS) {
2536
- this.store.applyParameters(el, value);
2061
+ applyProps(el, value);
2062
+ return;
2537
2063
  }
2538
- else {
2539
- this.store.applyProperty(el, name, value);
2064
+ const localState = getLocalState(el);
2065
+ const parent = localState?.instanceStore.get('parent') || rS[1 /* NgtRendererClassId.parent */];
2066
+ // [rawValue]
2067
+ if (localState?.isRaw && name === SPECIAL_PROPERTIES.RAW_VALUE) {
2068
+ rS[4 /* NgtRendererClassId.rawValue */] = value;
2069
+ if (parent)
2070
+ attachThreeChild(parent, el);
2071
+ return;
2540
2072
  }
2073
+ // [attach]
2074
+ if (name === SPECIAL_PROPERTIES.ATTACH) {
2075
+ if (localState)
2076
+ localState.attach = Array.isArray(value)
2077
+ ? value.map((v) => v.toString())
2078
+ : typeof value === 'function'
2079
+ ? value
2080
+ : [value];
2081
+ if (parent)
2082
+ attachThreeChild(parent, el);
2083
+ return;
2084
+ }
2085
+ applyProps(el, { [name]: value });
2541
2086
  return;
2542
2087
  }
2543
2088
  return this.delegate.setProperty(el, name, value);
@@ -2546,25 +2091,17 @@ class NgtRenderer {
2546
2091
  const rS = target.__ngt_renderer__;
2547
2092
  // if the target doesn't have __ngt_renderer__, we delegate
2548
2093
  // if target is DOM node, we delegate
2549
- if (!rS || this.store.isDOM(target)) {
2094
+ if (!rS || isDOM(target)) {
2550
2095
  return this.delegate.listen(target, eventName, callback);
2551
2096
  }
2552
- if (rS[0 /* NgtRendererClassId.type */] === 'three' ||
2553
- (rS[0 /* NgtRendererClassId.type */] === 'compound' && rS[7 /* NgtRendererClassId.compounded */])) {
2554
- const instance = rS[7 /* NgtRendererClassId.compounded */] || target;
2097
+ if (rS[0 /* NgtRendererClassId.type */] === 'three') {
2098
+ const instance = target;
2555
2099
  const localState = getLocalState(instance);
2556
2100
  const priority = localState?.priority ?? 0;
2557
2101
  return processThreeEvent(instance, priority, eventName, callback);
2558
2102
  }
2559
- if (rS[0 /* NgtRendererClassId.type */] === 'compound' && !rS[7 /* NgtRendererClassId.compounded */]) {
2560
- this.store.queueOperation(target, [
2561
- 'op',
2562
- () => this.store.queueOperation(target, ['cleanUp', this.listen(target, eventName, callback)]),
2563
- ]);
2564
- return () => { };
2565
- }
2566
2103
  // @ts-expect-error - we know that target is not DOM node
2567
- if (target === this.store.rootScene) {
2104
+ if (target === this.rootStore.snapshot.scene) {
2568
2105
  let [domTarget, event] = eventName.split(':');
2569
2106
  if (event == null) {
2570
2107
  event = domTarget;
@@ -2577,43 +2114,170 @@ class NgtRenderer {
2577
2114
  }
2578
2115
  return () => { };
2579
2116
  }
2117
+ destroyInternal(node, parent) {
2118
+ const rS = node.__ngt_renderer__;
2119
+ if (!rS || rS[3 /* NgtRendererClassId.destroyed */])
2120
+ return;
2121
+ if (rS[0 /* NgtRendererClassId.type */] === 'three') {
2122
+ const localState = getLocalState(node);
2123
+ if (localState?.instanceStore) {
2124
+ localState.instanceStore.get('objects').forEach((obj) => this.destroyInternal(obj, parent));
2125
+ localState.instanceStore.get('nonObjects').forEach((obj) => this.destroyInternal(obj, parent));
2126
+ }
2127
+ if (localState?.onUpdate)
2128
+ delete localState.onUpdate;
2129
+ if (localState?.onAttach)
2130
+ delete localState.onAttach;
2131
+ delete localState['objects'];
2132
+ delete localState['nonObjects'];
2133
+ delete localState['parent'];
2134
+ delete localState['add'];
2135
+ delete localState['remove'];
2136
+ delete localState['store'];
2137
+ delete localState['handlers'];
2138
+ if (!localState?.primitive) {
2139
+ delete node['__ngt__'];
2140
+ }
2141
+ }
2142
+ if (rS[0 /* NgtRendererClassId.type */] === 'comment') {
2143
+ rS[6 /* NgtRendererClassId.injectorFactory */] = null;
2144
+ delete node[SPECIAL_INTERNAL_ADD_COMMENT];
2145
+ this.removeCommentNode(node, this.argsCommentNodes);
2146
+ }
2147
+ if (rS[0 /* NgtRendererClassId.type */] === 'portal') {
2148
+ rS[6 /* NgtRendererClassId.injectorFactory */] = null;
2149
+ this.removeCommentNode(node, this.portalCommentsNodes);
2150
+ }
2151
+ // nullify parent
2152
+ rS[1 /* NgtRendererClassId.parent */] = null;
2153
+ for (const renderChild of rS[2 /* NgtRendererClassId.children */] || []) {
2154
+ if (renderChild.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three' && parent) {
2155
+ if (parent.__ngt_renderer__?.[0 /* NgtRendererClassId.type */] === 'three') {
2156
+ removeThreeChild(parent, renderChild, true);
2157
+ continue;
2158
+ }
2159
+ const closestInstance = getClosestParentWithInstance(parent);
2160
+ if (closestInstance) {
2161
+ removeThreeChild(closestInstance, renderChild, true);
2162
+ }
2163
+ }
2164
+ this.destroyInternal(renderChild, parent);
2165
+ }
2166
+ rS[2 /* NgtRendererClassId.children */] = [];
2167
+ rS[3 /* NgtRendererClassId.destroyed */] = true;
2168
+ if (parent) {
2169
+ removeChild(parent, node);
2170
+ }
2171
+ }
2172
+ removeCommentNode(node, nodes) {
2173
+ const index = nodes.findIndex((comment) => comment === node);
2174
+ if (index > -1) {
2175
+ nodes.splice(index, 1);
2176
+ }
2177
+ }
2178
+ processPortalContainer(portal) {
2179
+ const injector = portal.__ngt_renderer__[6 /* NgtRendererClassId.injectorFactory */]?.();
2180
+ if (!injector)
2181
+ return;
2182
+ const portalStore = injector.get(NGT_STORE, null);
2183
+ if (!portalStore)
2184
+ return;
2185
+ const portalContainer = portalStore.get('scene');
2186
+ if (!portalContainer)
2187
+ return;
2188
+ const localState = getLocalState(portalContainer);
2189
+ if (localState) {
2190
+ localState.store = portalStore;
2191
+ }
2192
+ portal.__ngt_renderer__[5 /* NgtRendererClassId.portalContainer */] = createNode('three', portalContainer, this.document);
2193
+ }
2580
2194
  shouldFindGrandparentInstance(pRS, cRS, child) {
2581
2195
  const pType = pRS[0 /* NgtRendererClassId.type */];
2582
2196
  const cType = cRS[0 /* NgtRendererClassId.type */];
2583
- const isParentCompounded = pRS[7 /* NgtRendererClassId.compounded */];
2584
- const isChildCompounded = cRS[7 /* NgtRendererClassId.compounded */];
2585
2197
  const cLS = getLocalState(child);
2586
2198
  // if child is three but haven't been attached to a parent yet
2587
2199
  const isDanglingThreeChild = cType === 'three' && !cLS?.instanceStore?.get('parent');
2588
2200
  // or both parent and child are DOM elements
2589
2201
  // or they are compound AND haven't had a THREE instance yet
2590
- const isParentStillDOM = pType === 'dom' || (pType === 'compound' && !isParentCompounded);
2591
- const isChildStillDOM = cType === 'dom' || (cType === 'compound' && !isChildCompounded);
2592
- // and the child is a compounded compound
2593
- const isCompoundChildCompounded = cType === 'compound' && !!isChildCompounded;
2594
- return (isDanglingThreeChild || (isParentStillDOM && isChildStillDOM) || (isParentStillDOM && isCompoundChildCompounded));
2202
+ const isParentStillDOM = pType === 'dom';
2203
+ const isChildStillDOM = cType === 'dom';
2204
+ return isDanglingThreeChild || (isParentStillDOM && isChildStillDOM) || isParentStillDOM;
2205
+ }
2206
+ getNgtArgs() {
2207
+ let directive;
2208
+ const destroyed = [];
2209
+ let i = this.argsCommentNodes.length - 1;
2210
+ while (i >= 0) {
2211
+ const comment = this.argsCommentNodes[i];
2212
+ if (comment.__ngt_renderer__[3 /* NgtRendererClassId.destroyed */]) {
2213
+ destroyed.push(i);
2214
+ i--;
2215
+ continue;
2216
+ }
2217
+ const injector = comment.__ngt_renderer__[6 /* NgtRendererClassId.injectorFactory */]();
2218
+ if (!injector) {
2219
+ i--;
2220
+ continue;
2221
+ }
2222
+ const instance = injector.get(NgtArgs, null);
2223
+ if (instance && instance.validate()) {
2224
+ directive = instance;
2225
+ break;
2226
+ }
2227
+ i--;
2228
+ }
2229
+ destroyed.forEach((index) => {
2230
+ this.argsCommentNodes.splice(index, 1);
2231
+ });
2232
+ return directive;
2233
+ }
2234
+ tryGetPortalStore() {
2235
+ let store;
2236
+ const destroyed = [];
2237
+ // we only care about the portal states because NgtStore only differs per Portal
2238
+ let i = this.portalCommentsNodes.length - 1;
2239
+ while (i >= 0) {
2240
+ // loop through the portal state backwards to find the closest NgtStore
2241
+ const portal = this.portalCommentsNodes[i];
2242
+ if (portal.__ngt_renderer__[3 /* NgtRendererClassId.destroyed */]) {
2243
+ destroyed.push(i);
2244
+ i--;
2245
+ continue;
2246
+ }
2247
+ const injector = portal.__ngt_renderer__[6 /* NgtRendererClassId.injectorFactory */]();
2248
+ if (!injector) {
2249
+ i--;
2250
+ continue;
2251
+ }
2252
+ const instance = injector.get(NGT_STORE, null);
2253
+ // only the instance with previousRoot should pass
2254
+ if (instance && instance.get('previousRoot')) {
2255
+ store = instance;
2256
+ break;
2257
+ }
2258
+ i--;
2259
+ }
2260
+ destroyed.forEach((index) => {
2261
+ this.portalCommentsNodes.splice(index, 1);
2262
+ });
2263
+ return store || this.rootStore;
2595
2264
  }
2596
2265
  get data() {
2597
2266
  return this.delegate.data;
2598
2267
  }
2599
2268
  }
2600
- function provideNgtRenderer(store, compoundPrefixes) {
2601
- if (!compoundPrefixes.includes('ngts'))
2602
- compoundPrefixes.push('ngts');
2603
- if (!compoundPrefixes.includes('ngtp'))
2604
- compoundPrefixes.push('ngtp');
2269
+ function provideNgtRenderer(store) {
2605
2270
  const providers = [
2606
2271
  NgtRendererFactory,
2607
2272
  { provide: RendererFactory2, useExisting: NgtRendererFactory },
2608
- { provide: NGT_COMPOUND_PREFIXES, useValue: compoundPrefixes },
2609
- provideNgtStore(store),
2273
+ provideStore(() => store),
2610
2274
  ];
2611
2275
  return makeEnvironmentProviders(providers);
2612
2276
  }
2613
2277
 
2614
2278
  class NgtCanvas {
2615
2279
  constructor() {
2616
- this.store = injectNgtStore();
2280
+ this.store = injectStore();
2617
2281
  this.initRoot = injectCanvasRootInitializer();
2618
2282
  this.autoEffect = injectAutoEffect();
2619
2283
  this.host = inject(ElementRef);
@@ -2622,8 +2286,6 @@ class NgtCanvas {
2622
2286
  this.environmentInjector = inject(EnvironmentInjector);
2623
2287
  this.injector = inject(Injector);
2624
2288
  this.sceneGraph = input.required();
2625
- this.compoundPrefixes = input([]);
2626
- this.sceneGraphInputs = input({});
2627
2289
  this.gl = input();
2628
2290
  this.size = input();
2629
2291
  this.shadows = input(false, {
@@ -2658,7 +2320,6 @@ class NgtCanvas {
2658
2320
  this.zone.runOutsideAngular(() => {
2659
2321
  this.configurator = this.initRoot(this.glCanvas().nativeElement);
2660
2322
  this.noZoneResizeEffect();
2661
- this.noZoneSceneGraphInputsEffect();
2662
2323
  });
2663
2324
  });
2664
2325
  inject(DestroyRef).onDestroy(() => {
@@ -2734,38 +2395,29 @@ class NgtCanvas {
2734
2395
  this.store.get('events').connect?.(untracked(this.glCanvas).nativeElement);
2735
2396
  }
2736
2397
  untracked(() => {
2737
- this.glEnvironmentInjector = createEnvironmentInjector([provideNgtRenderer(this.store, untracked(this.compoundPrefixes))], this.environmentInjector);
2398
+ this.glEnvironmentInjector = createEnvironmentInjector([provideNgtRenderer(this.store)], this.environmentInjector);
2738
2399
  this.glRef = this.viewContainerRef.createComponent(untracked(this.sceneGraph), {
2739
2400
  environmentInjector: this.glEnvironmentInjector,
2740
2401
  injector: this.injector,
2741
2402
  });
2742
2403
  this.glRef.changeDetectorRef.detectChanges();
2743
- this.setSceneGraphInputs(this.sceneGraphInputs());
2744
2404
  });
2745
2405
  }
2746
- noZoneSceneGraphInputsEffect() {
2747
- this.autoEffect(() => {
2748
- this.setSceneGraphInputs(this.sceneGraphInputs());
2749
- });
2750
- }
2751
- setSceneGraphInputs(sceneGraphInputs) {
2752
- if (this.glRef) {
2753
- for (const [key, value] of Object.entries(sceneGraphInputs)) {
2754
- this.glRef.setInput(key, value);
2755
- }
2756
- }
2757
- }
2758
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtCanvas, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2759
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "18.0.3", type: NgtCanvas, isStandalone: true, selector: "ngt-canvas", inputs: { sceneGraph: { classPropertyName: "sceneGraph", publicName: "sceneGraph", isSignal: true, isRequired: true, transformFunction: null }, compoundPrefixes: { classPropertyName: "compoundPrefixes", publicName: "compoundPrefixes", isSignal: true, isRequired: false, transformFunction: null }, sceneGraphInputs: { classPropertyName: "sceneGraphInputs", publicName: "sceneGraphInputs", isSignal: true, isRequired: false, transformFunction: null }, gl: { classPropertyName: "gl", publicName: "gl", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shadows: { classPropertyName: "shadows", publicName: "shadows", isSignal: true, isRequired: false, transformFunction: null }, legacy: { classPropertyName: "legacy", publicName: "legacy", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, flat: { classPropertyName: "flat", publicName: "flat", isSignal: true, isRequired: false, transformFunction: null }, orthographic: { classPropertyName: "orthographic", publicName: "orthographic", isSignal: true, isRequired: false, transformFunction: null }, frameloop: { classPropertyName: "frameloop", publicName: "frameloop", isSignal: true, isRequired: false, transformFunction: null }, performance: { classPropertyName: "performance", publicName: "performance", isSignal: true, isRequired: false, transformFunction: null }, dpr: { classPropertyName: "dpr", publicName: "dpr", isSignal: true, isRequired: false, transformFunction: null }, raycaster: { classPropertyName: "raycaster", publicName: "raycaster", isSignal: true, isRequired: false, transformFunction: null }, scene: { classPropertyName: "scene", publicName: "scene", isSignal: true, isRequired: false, transformFunction: null }, camera: { classPropertyName: "camera", publicName: "camera", isSignal: true, isRequired: false, transformFunction: null }, events: { classPropertyName: "events", publicName: "events", isSignal: true, isRequired: false, transformFunction: null }, eventSource: { classPropertyName: "eventSource", publicName: "eventSource", isSignal: true, isRequired: false, transformFunction: null }, eventPrefix: { classPropertyName: "eventPrefix", publicName: "eventPrefix", isSignal: true, isRequired: false, transformFunction: null }, lookAt: { classPropertyName: "lookAt", publicName: "lookAt", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { created: "created", pointerMissed: "pointerMissed" }, host: { properties: { "style.pointerEvents": "hbPointerEvents()" }, styleAttribute: "display: block;position: relative;width: 100%;height: 100%;overflow: hidden;" }, providers: [
2760
- provideResizeOptions({ emitInZone: false, emitInitialResult: true, debounce: 250 }),
2761
- provideNgtStore(),
2406
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtCanvas, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2407
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "18.0.6", type: NgtCanvas, isStandalone: true, selector: "ngt-canvas", inputs: { sceneGraph: { classPropertyName: "sceneGraph", publicName: "sceneGraph", isSignal: true, isRequired: true, transformFunction: null }, gl: { classPropertyName: "gl", publicName: "gl", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shadows: { classPropertyName: "shadows", publicName: "shadows", isSignal: true, isRequired: false, transformFunction: null }, legacy: { classPropertyName: "legacy", publicName: "legacy", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, flat: { classPropertyName: "flat", publicName: "flat", isSignal: true, isRequired: false, transformFunction: null }, orthographic: { classPropertyName: "orthographic", publicName: "orthographic", isSignal: true, isRequired: false, transformFunction: null }, frameloop: { classPropertyName: "frameloop", publicName: "frameloop", isSignal: true, isRequired: false, transformFunction: null }, performance: { classPropertyName: "performance", publicName: "performance", isSignal: true, isRequired: false, transformFunction: null }, dpr: { classPropertyName: "dpr", publicName: "dpr", isSignal: true, isRequired: false, transformFunction: null }, raycaster: { classPropertyName: "raycaster", publicName: "raycaster", isSignal: true, isRequired: false, transformFunction: null }, scene: { classPropertyName: "scene", publicName: "scene", isSignal: true, isRequired: false, transformFunction: null }, camera: { classPropertyName: "camera", publicName: "camera", isSignal: true, isRequired: false, transformFunction: null }, events: { classPropertyName: "events", publicName: "events", isSignal: true, isRequired: false, transformFunction: null }, eventSource: { classPropertyName: "eventSource", publicName: "eventSource", isSignal: true, isRequired: false, transformFunction: null }, eventPrefix: { classPropertyName: "eventPrefix", publicName: "eventPrefix", isSignal: true, isRequired: false, transformFunction: null }, lookAt: { classPropertyName: "lookAt", publicName: "lookAt", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { created: "created", pointerMissed: "pointerMissed" }, host: { properties: { "style.pointerEvents": "hbPointerEvents()" }, styleAttribute: "display: block;position: relative;width: 100%;height: 100%;overflow: hidden;" }, providers: [
2408
+ provideResizeOptions({
2409
+ emitInZone: false,
2410
+ emitInitialResult: true,
2411
+ debounce: { scroll: 50, resize: 0 },
2412
+ }),
2413
+ provideStore(),
2762
2414
  ], viewQueries: [{ propertyName: "glCanvas", first: true, predicate: ["glCanvas"], descendants: true, isSignal: true }, { propertyName: "glCanvasViewContainerRef", first: true, predicate: ["glCanvas"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
2763
2415
  <div (ngxResize)="resizeResult.set($event)" style="height: 100%; width: 100%;">
2764
2416
  <canvas #glCanvas style="display: block;"></canvas>
2765
2417
  </div>
2766
2418
  `, isInline: true, dependencies: [{ kind: "directive", type: NgxResize, selector: "[ngxResize]", inputs: ["ngxResizeOptions"], outputs: ["ngxResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2767
2419
  }
2768
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtCanvas, decorators: [{
2420
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtCanvas, decorators: [{
2769
2421
  type: Component,
2770
2422
  args: [{
2771
2423
  selector: 'ngt-canvas',
@@ -2777,8 +2429,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImpor
2777
2429
  `,
2778
2430
  imports: [NgxResize],
2779
2431
  providers: [
2780
- provideResizeOptions({ emitInZone: false, emitInitialResult: true, debounce: 250 }),
2781
- provideNgtStore(),
2432
+ provideResizeOptions({
2433
+ emitInZone: false,
2434
+ emitInitialResult: true,
2435
+ debounce: { scroll: 50, resize: 0 },
2436
+ }),
2437
+ provideStore(),
2782
2438
  ],
2783
2439
  host: {
2784
2440
  style: 'display: block;position: relative;width: 100%;height: 100%;overflow: hidden;',
@@ -2870,12 +2526,13 @@ const injectLoader = _injectLoader;
2870
2526
 
2871
2527
  function injectBeforeRender(cb, { priority = 0, injector } = {}) {
2872
2528
  return assertInjector(injectBeforeRender, injector, () => {
2873
- const store = injectNgtStore();
2529
+ const store = injectStore();
2874
2530
  const sub = store.get('internal').subscribe(cb, priority, store);
2875
2531
  inject(DestroyRef).onDestroy(() => void sub());
2876
2532
  return sub;
2877
2533
  });
2878
2534
  }
2535
+ // TODO (chau): clean this up
2879
2536
  function injectNextBeforeRender(cb, { priority = 0, injector } = {}) {
2880
2537
  return assertInjector(injectNextBeforeRender, injector, () => {
2881
2538
  const assertedInjector = inject(Injector);
@@ -2887,30 +2544,19 @@ function injectNextBeforeRender(cb, { priority = 0, injector } = {}) {
2887
2544
  });
2888
2545
  }
2889
2546
 
2890
- const privateKeys = [
2891
- 'get',
2892
- 'set',
2893
- 'select',
2894
- 'setSize',
2895
- 'setDpr',
2896
- 'setFrameloop',
2897
- 'events',
2898
- 'invalidate',
2899
- 'advance',
2900
- 'size',
2901
- 'viewport',
2902
- ];
2903
2547
  class NgtPortalBeforeRender {
2904
2548
  constructor() {
2905
- this.portalStore = injectNgtStore();
2906
- this.injector = inject(Injector);
2549
+ this.portalStore = injectStore();
2907
2550
  this.renderPriority = input(1);
2908
2551
  this.parentScene = input.required();
2909
2552
  this.parentCamera = input.required();
2910
- afterNextRender(() => {
2553
+ injectAutoEffect()((injector) => {
2554
+ // track state
2555
+ this.portalStore.state();
2556
+ const priority = this.renderPriority();
2911
2557
  let oldClear;
2912
- injectBeforeRender(() => {
2913
- const { gl, scene, camera } = this.portalStore.get();
2558
+ return injectBeforeRender(() => {
2559
+ const { gl, scene, camera } = this.portalStore.snapshot;
2914
2560
  oldClear = gl.autoClear;
2915
2561
  if (this.renderPriority() === 1) {
2916
2562
  // clear scene and render with default
@@ -2923,19 +2569,19 @@ class NgtPortalBeforeRender {
2923
2569
  gl.render(scene, camera);
2924
2570
  // restore
2925
2571
  gl.autoClear = oldClear;
2926
- }, { priority: this.renderPriority(), injector: this.injector });
2572
+ }, { priority, injector });
2927
2573
  });
2928
2574
  }
2929
2575
  onPointerOver() {
2930
2576
  /* noop */
2931
2577
  }
2932
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtPortalBeforeRender, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2933
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.0.3", type: NgtPortalBeforeRender, isStandalone: true, selector: "ngt-portal-before-render", inputs: { renderPriority: { classPropertyName: "renderPriority", publicName: "renderPriority", isSignal: true, isRequired: false, transformFunction: null }, parentScene: { classPropertyName: "parentScene", publicName: "parentScene", isSignal: true, isRequired: true, transformFunction: null }, parentCamera: { classPropertyName: "parentCamera", publicName: "parentCamera", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
2578
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtPortalBeforeRender, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2579
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.0.6", type: NgtPortalBeforeRender, isStandalone: true, selector: "ngt-portal-before-render", inputs: { renderPriority: { classPropertyName: "renderPriority", publicName: "renderPriority", isSignal: true, isRequired: false, transformFunction: null }, parentScene: { classPropertyName: "parentScene", publicName: "parentScene", isSignal: true, isRequired: true, transformFunction: null }, parentCamera: { classPropertyName: "parentCamera", publicName: "parentCamera", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
2934
2580
  <!-- Without an element that receives pointer events state.pointer will always be 0/0 -->
2935
2581
  <ngt-group (pointerover)="onPointerOver()" attach="none" />
2936
2582
  `, isInline: true }); }
2937
2583
  }
2938
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtPortalBeforeRender, decorators: [{
2584
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtPortalBeforeRender, decorators: [{
2939
2585
  type: Component,
2940
2586
  args: [{
2941
2587
  selector: 'ngt-portal-before-render',
@@ -2948,194 +2594,167 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImpor
2948
2594
  }]
2949
2595
  }], ctorParameters: () => [] });
2950
2596
  class NgtPortalContent {
2951
- constructor(vcr, parentVcr) {
2952
- const commentNode = vcr.element.nativeElement;
2597
+ constructor() {
2598
+ const { element: comment } = inject(ViewContainerRef);
2599
+ const { element: parentComment } = inject(ViewContainerRef, { skipSelf: true });
2600
+ const commentNode = comment.nativeElement;
2953
2601
  if (commentNode[SPECIAL_INTERNAL_ADD_COMMENT]) {
2954
- commentNode[SPECIAL_INTERNAL_ADD_COMMENT](parentVcr.element.nativeElement);
2602
+ commentNode[SPECIAL_INTERNAL_ADD_COMMENT](parentComment.nativeElement);
2955
2603
  delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
2956
2604
  }
2957
2605
  }
2958
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtPortalContent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ViewContainerRef, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive }); }
2959
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.3", type: NgtPortalContent, isStandalone: true, selector: "ng-template[portalContent]", ngImport: i0 }); }
2606
+ static ngTemplateContextGuard(_, ctx) {
2607
+ return true;
2608
+ }
2609
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtPortalContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2610
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.0.6", type: NgtPortalContent, isStandalone: true, selector: "ng-template[portalContent]", ngImport: i0 }); }
2960
2611
  }
2961
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtPortalContent, decorators: [{
2612
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtPortalContent, decorators: [{
2962
2613
  type: Directive,
2963
2614
  args: [{ selector: 'ng-template[portalContent]', standalone: true }]
2964
- }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.ViewContainerRef, decorators: [{
2965
- type: SkipSelf
2966
- }] }] });
2615
+ }], ctorParameters: () => [] });
2616
+ // Keys that shouldn't be copied between stores
2617
+ const privateKeys = [
2618
+ 'setSize',
2619
+ 'setFrameloop',
2620
+ 'setDpr',
2621
+ 'events',
2622
+ 'setEvents',
2623
+ 'invalidate',
2624
+ 'advance',
2625
+ 'size',
2626
+ 'viewport',
2627
+ ];
2967
2628
  class NgtPortal {
2968
2629
  constructor() {
2969
- this.container = input(prepare(new Scene()));
2970
- this.camera = input();
2971
- this.state = input();
2630
+ this.container = input.required();
2631
+ this.state = input({});
2632
+ /**
2633
+ * @decsription turn this on to enable "HUD" like rendering
2634
+ */
2972
2635
  this.autoRender = input(false);
2973
2636
  this.autoRenderPriority = input(1);
2974
- this.portalContentTemplate = contentChild.required(NgtPortalContent, { read: TemplateRef });
2975
- this.portalContentAnchor = viewChild.required('portalContentAnchor', { read: ViewContainerRef });
2976
- this.destroyRef = inject(DestroyRef);
2977
- this.autoEffect = injectAutoEffect();
2978
- this.parentStore = injectNgtStore({ skipSelf: true });
2979
- this.portalStore = injectNgtStore({ self: true });
2980
- this.portalRendered = signal(false);
2981
- this.renderAutoBeforeRender = computed(() => this.portalRendered() && this.autoRender());
2982
- this.parentScene = this.parentStore.get('scene');
2983
- this.parentCamera = this.parentStore.get('camera');
2637
+ this.portalContent = contentChild.required(NgtPortalContent, { read: TemplateRef });
2638
+ this.portalAnchor = viewChild.required('anchor', { read: ViewContainerRef });
2639
+ this.injector = inject(Injector);
2640
+ this.portalStore = injectStore({ self: true });
2641
+ this.parentStore = injectStore({ skipSelf: true });
2642
+ this.parentScene = this.parentStore.select('scene');
2643
+ this.parentCamera = this.parentStore.select('camera');
2984
2644
  this.raycaster = new Raycaster();
2985
2645
  this.pointer = new Vector2();
2646
+ this.portalRendered = signal(false);
2647
+ this.renderAutoBeforeRender = computed(() => this.portalRendered() && this.autoRender());
2648
+ const autoEffect = injectAutoEffect();
2649
+ const parentState = this.parentStore.select();
2986
2650
  afterNextRender(() => {
2987
- const parentState = this.parentStore.snapshot;
2988
- let [container, state, autoRender, autoRenderPriority] = [
2989
- this.container(),
2990
- this.state(),
2991
- this.autoRender(),
2992
- this.autoRenderPriority(),
2993
- ];
2994
- let stateFromInput = state;
2995
- if (!stateFromInput && autoRender) {
2996
- stateFromInput = { events: { priority: autoRenderPriority + 1 } };
2997
- }
2998
- const { events = {}, size = {}, ...rest } = stateFromInput || {};
2651
+ const previousState = this.parentStore.snapshot;
2652
+ const { events = {}, size = {}, ...rest } = this.state();
2653
+ let container = this.container();
2999
2654
  if (!is.instance(container)) {
3000
- container = prepare(container);
2655
+ container = prepare(container, { store: this.portalStore });
3001
2656
  }
3002
2657
  const localState = getLocalState(container);
3003
2658
  if (localState && !localState.store) {
3004
2659
  localState.store = this.portalStore;
3005
2660
  }
3006
2661
  this.portalStore.update({
3007
- ...parentState,
2662
+ ...previousState,
3008
2663
  scene: container,
3009
2664
  raycaster: this.raycaster,
3010
2665
  pointer: this.pointer,
2666
+ events: { ...previousState.events, ...events },
2667
+ size: { ...previousState.size, ...size },
3011
2668
  previousRoot: this.parentStore,
3012
- events: { ...parentState.events, ...events },
3013
- size: { ...parentState.size, ...size },
3014
2669
  ...rest,
3015
2670
  setEvents: (events) => this.portalStore.update((state) => ({ ...state, events: { ...state.events, ...events } })),
3016
2671
  });
3017
- this.autoEffect(() => {
3018
- const previous = this.parentStore.state();
3019
- this.portalStore.update((state) => this.inject(previous, state));
3020
- });
3021
- untracked(() => {
3022
- const portalView = this.portalContentAnchor().createEmbeddedView(this.portalContentTemplate());
3023
- portalView.detectChanges();
3024
- this.destroyRef.onDestroy(() => {
3025
- portalView.destroy();
2672
+ autoEffect(() => {
2673
+ const state = this.state();
2674
+ const _parentState = parentState();
2675
+ this.portalStore.update((prev) => this.inject(_parentState, prev, state, untracked(this.container)));
2676
+ untracked(() => {
2677
+ if (this.portalView) {
2678
+ this.portalView.detectChanges();
2679
+ return;
2680
+ }
2681
+ this.portalView = this.portalAnchor().createEmbeddedView(this.portalContent(), { container: this.container(), injector: this.injector }, { injector: this.injector });
2682
+ this.portalView.detectChanges();
2683
+ this.portalRendered.set(true);
3026
2684
  });
3027
2685
  });
3028
- this.portalRendered.set(true);
2686
+ });
2687
+ inject(DestroyRef).onDestroy(() => {
2688
+ this.portalView?.destroy();
3029
2689
  });
3030
2690
  }
3031
- inject(rootState, injectState) {
3032
- const intersect = { ...rootState };
3033
- Object.keys(intersect).forEach((key) => {
2691
+ inject(parentState, portalState, injectedState, container) {
2692
+ const { events = {}, size, ...rest } = injectedState;
2693
+ const intersect = { ...parentState }; // all prev state props
2694
+ Object.keys(parentState).forEach((key) => {
3034
2695
  if (privateKeys.includes(key) ||
3035
- rootState[key] !== injectState[key]) {
2696
+ (parentState[key] !== portalState[key] &&
2697
+ portalState[key])) {
3036
2698
  delete intersect[key];
3037
2699
  }
3038
2700
  });
3039
- const container = untracked(this.container);
3040
- const { size, events, ...restInputsState } = untracked(this.state) || {};
3041
2701
  let viewport = undefined;
3042
- if (injectState && size) {
3043
- const camera = injectState.camera;
3044
- viewport = rootState.viewport.getCurrentViewport(camera, new Vector3(), size);
3045
- if (camera !== rootState.camera)
2702
+ if (portalState && size) {
2703
+ const camera = portalState.camera;
2704
+ // Calculate the override viewport, if present
2705
+ viewport = parentState.viewport.getCurrentViewport(camera, new Vector3(), size);
2706
+ // Update the portal camera, if it differs from the previous layer
2707
+ if (camera !== parentState.camera) {
3046
2708
  updateCamera(camera, size);
2709
+ }
3047
2710
  }
3048
2711
  return {
3049
2712
  ...intersect,
3050
2713
  scene: container,
3051
- previousRoot: this.parentStore,
3052
- events: { ...rootState.events, ...(injectState?.events || {}), ...events },
3053
- size: { ...rootState.size, ...size },
3054
- viewport: { ...rootState.viewport, ...(viewport || {}) },
3055
- ...restInputsState,
2714
+ raycaster: this.raycaster,
2715
+ pointer: this.pointer,
2716
+ events: { ...parentState.events, ...(portalState.events || {}), ...(events || {}) },
2717
+ size: { ...parentState.size, ...(size || {}) },
2718
+ viewport: { ...parentState.viewport, ...(viewport || {}) },
2719
+ ...rest,
3056
2720
  };
3057
2721
  }
3058
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtPortal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3059
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.3", type: NgtPortal, isStandalone: true, selector: "ngt-portal", inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: false, transformFunction: null }, camera: { classPropertyName: "camera", publicName: "camera", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, autoRender: { classPropertyName: "autoRender", publicName: "autoRender", isSignal: true, isRequired: false, transformFunction: null }, autoRenderPriority: { classPropertyName: "autoRenderPriority", publicName: "autoRenderPriority", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideNgtStore(signalStore({}))], queries: [{ propertyName: "portalContentTemplate", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "portalContentAnchor", first: true, predicate: ["portalContentAnchor"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
3060
- <ng-container #portalContentAnchor>
3061
- @if (renderAutoBeforeRender()) {
3062
- <ngt-portal-before-render
3063
- [renderPriority]="autoRenderPriority()"
3064
- [parentScene]="parentScene"
3065
- [parentCamera]="parentCamera"
3066
- />
3067
- }
3068
- </ng-container>
2722
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtPortal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2723
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.0.6", type: NgtPortal, isStandalone: true, selector: "ngt-portal", inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: true, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null }, autoRender: { classPropertyName: "autoRender", publicName: "autoRender", isSignal: true, isRequired: false, transformFunction: null }, autoRenderPriority: { classPropertyName: "autoRenderPriority", publicName: "autoRenderPriority", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideStore(() => signalStore({}))], queries: [{ propertyName: "portalContent", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "portalAnchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
2724
+ <ng-container #anchor />
2725
+
2726
+ @if (renderAutoBeforeRender()) {
2727
+ <ngt-portal-before-render
2728
+ [renderPriority]="autoRenderPriority()"
2729
+ [parentScene]="parentScene()"
2730
+ [parentCamera]="parentCamera()"
2731
+ />
2732
+ }
3069
2733
  `, isInline: true, dependencies: [{ kind: "component", type: NgtPortalBeforeRender, selector: "ngt-portal-before-render", inputs: ["renderPriority", "parentScene", "parentCamera"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3070
2734
  }
3071
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtPortal, decorators: [{
2735
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtPortal, decorators: [{
3072
2736
  type: Component,
3073
2737
  args: [{
3074
2738
  selector: 'ngt-portal',
3075
2739
  standalone: true,
3076
2740
  template: `
3077
- <ng-container #portalContentAnchor>
3078
- @if (renderAutoBeforeRender()) {
3079
- <ngt-portal-before-render
3080
- [renderPriority]="autoRenderPriority()"
3081
- [parentScene]="parentScene"
3082
- [parentCamera]="parentCamera"
3083
- />
3084
- }
3085
- </ng-container>
2741
+ <ng-container #anchor />
2742
+
2743
+ @if (renderAutoBeforeRender()) {
2744
+ <ngt-portal-before-render
2745
+ [renderPriority]="autoRenderPriority()"
2746
+ [parentScene]="parentScene()"
2747
+ [parentCamera]="parentCamera()"
2748
+ />
2749
+ }
3086
2750
  `,
3087
2751
  imports: [NgtPortalBeforeRender],
3088
- providers: [provideNgtStore(signalStore({}))],
2752
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
3089
2753
  changeDetection: ChangeDetectionStrategy.OnPush,
2754
+ providers: [provideStore(() => signalStore({}))],
3090
2755
  }]
3091
2756
  }], ctorParameters: () => [] });
3092
2757
 
3093
- function injectNgtRef(initial = null, injector) {
3094
- if (initial && Reflect.get(initial, '__ngtType')) {
3095
- return initial;
3096
- }
3097
- return assertInjector(injectNgtRef, injector, () => {
3098
- const ref = is.ref(initial) ? initial : new ElementRef(initial);
3099
- const refSignal = signal(ref.nativeElement, { equal: Object.is });
3100
- const readonlyRef = refSignal.asReadonly();
3101
- const computedCached = new Map();
3102
- inject(DestroyRef).onDestroy(() => void computedCached.clear());
3103
- const children = (type = 'objects') => {
3104
- if (!computedCached.has(type)) {
3105
- computedCached.set(type, computed(() => {
3106
- const instance = readonlyRef();
3107
- if (!instance)
3108
- return [];
3109
- const localState = getLocalState(instance);
3110
- if (!localState?.instanceStore)
3111
- return [];
3112
- if (type === 'objects')
3113
- return localState.objects();
3114
- if (type === 'nonObjects')
3115
- return localState.nonObjects();
3116
- return [...localState.objects(), ...localState.nonObjects()];
3117
- }));
3118
- }
3119
- return computedCached.get(type);
3120
- };
3121
- Object.defineProperties(ref, {
3122
- nativeElement: {
3123
- set: (newElement) => {
3124
- untracked(() => {
3125
- if (newElement !== readonlyRef()) {
3126
- refSignal.set(newElement);
3127
- }
3128
- });
3129
- },
3130
- get: readonlyRef,
3131
- },
3132
- children: { value: children },
3133
- __ngtType: { value: 'ngtRef' },
3134
- });
3135
- return ref;
3136
- });
3137
- }
3138
-
3139
2758
  var _a;
3140
2759
  class NgtRoutedScene {
3141
2760
  static { _a = ROUTED_SCENE; }
@@ -3145,12 +2764,12 @@ class NgtRoutedScene {
3145
2764
  .pipe(filter((event) => event instanceof ActivationEnd), takeUntilDestroyed())
3146
2765
  .subscribe(cdr.detectChanges.bind(cdr));
3147
2766
  }
3148
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtRoutedScene, deps: [{ token: i1.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
3149
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.3", type: NgtRoutedScene, isStandalone: true, selector: "ngt-routed-scene", ngImport: i0, template: `
2767
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtRoutedScene, deps: [{ token: i1.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
2768
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.0.6", type: NgtRoutedScene, isStandalone: true, selector: "ngt-routed-scene", ngImport: i0, template: `
3150
2769
  <router-outlet />
3151
2770
  `, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] }); }
3152
2771
  }
3153
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.3", ngImport: i0, type: NgtRoutedScene, decorators: [{
2772
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.6", ngImport: i0, type: NgtRoutedScene, decorators: [{
3154
2773
  type: Component,
3155
2774
  args: [{
3156
2775
  standalone: true,
@@ -3173,63 +2792,75 @@ function createApiToken(forwardedObject) {
3173
2792
  return [injectFn, () => provideFn()];
3174
2793
  }
3175
2794
 
3176
- function exclude(options, keysToExclude) {
2795
+ function omit(objFn, keysToOmit) {
3177
2796
  return computed(() => {
3178
- const opts = options();
3179
- return Object.keys(opts).reduce((acc, key) => {
3180
- const optKey = key;
3181
- if (!keysToExclude.includes(optKey)) {
3182
- acc[optKey] = opts[optKey];
3183
- }
3184
- return acc;
3185
- }, {});
2797
+ const obj = objFn();
2798
+ const result = {};
2799
+ for (const key of Object.keys(obj)) {
2800
+ if (keysToOmit.includes(key))
2801
+ continue;
2802
+ Object.assign(result, { [key]: obj[key] });
2803
+ }
2804
+ return result;
3186
2805
  });
3187
2806
  }
3188
- function pick(options, keys) {
3189
- if (Array.isArray(keys)) {
2807
+ function pick(objFn, keyOrKeys) {
2808
+ if (Array.isArray(keyOrKeys)) {
3190
2809
  return computed(() => {
3191
- const opts = options();
3192
- return keys.reduce((acc, key) => {
3193
- // @ts-expect-error - fix this later
3194
- acc[key] = opts[key];
3195
- return acc;
3196
- }, {});
2810
+ const obj = objFn();
2811
+ const result = {};
2812
+ for (const key of keyOrKeys) {
2813
+ if (!(key in obj))
2814
+ continue;
2815
+ Object.assign(result, { [key]: obj[key] });
2816
+ }
2817
+ return result;
3197
2818
  });
3198
2819
  }
3199
- return computed(() => options()[keys]);
2820
+ return computed(() => objFn()[keyOrKeys]);
3200
2821
  }
3201
- function merge(options, toMerge, mode = 'override') {
3202
- return computed(() => {
3203
- const opts = options();
3204
- return mode === 'override' ? { ...opts, ...toMerge } : { ...toMerge, ...opts };
3205
- });
2822
+ function merge(objFn, toMerge, mode = 'override') {
2823
+ if (mode === 'override')
2824
+ return computed(() => ({ ...objFn(), ...toMerge }));
2825
+ return computed(() => ({ ...toMerge, ...objFn() }));
3206
2826
  }
3207
- function vector2(options, key) {
2827
+ function vector2(options, key, keepUndefined = false) {
3208
2828
  return computed(() => {
3209
2829
  const value = options()[key];
2830
+ if (keepUndefined && value == undefined)
2831
+ return undefined;
3210
2832
  if (typeof value === 'number')
3211
2833
  return new Vector2(value, value);
3212
2834
  else if (value)
3213
2835
  return new Vector2(...value);
3214
2836
  else
3215
2837
  return new Vector2();
3216
- });
2838
+ }, { equal: (a, b) => !!a && !!b && a.equals(b) });
3217
2839
  }
3218
- function vector3(options, key) {
2840
+ function vector3(options, key, keepUndefined = false) {
3219
2841
  return computed(() => {
3220
2842
  const value = options()[key];
2843
+ if (keepUndefined && value == undefined)
2844
+ return undefined;
3221
2845
  if (typeof value === 'number')
3222
2846
  return new Vector3(value, value, value);
3223
2847
  else if (value)
3224
2848
  return new Vector3(...value);
3225
2849
  else
3226
2850
  return new Vector3();
3227
- });
2851
+ }, { equal: (a, b) => !!a && !!b && a.equals(b) });
2852
+ }
2853
+
2854
+ function resolveRef(ref) {
2855
+ if (is.ref(ref)) {
2856
+ return ref.nativeElement;
2857
+ }
2858
+ return ref;
3228
2859
  }
3229
2860
 
3230
2861
  /**
3231
2862
  * Generated bundle index. Do not edit.
3232
2863
  */
3233
2864
 
3234
- export { HTML, NGT_STORE, NgtArgs, NgtCanvas, NgtPortal, NgtPortalContent, NgtRenderer, NgtRendererFactory, NgtRoutedScene, ROUTED_SCENE, addAfterEffect, addEffect, addTail, applyProps, checkNeedsUpdate, checkUpdate, createApiToken, createAttachFunction, exclude, extend, getLocalState, injectBeforeRender, injectLoader, injectNextBeforeRender, injectNgtRef, injectNgtStore, invalidateInstance, is, makeCameraInstance, makeDpr, makeId, makeObjectGraph, makeRendererInstance, merge, pick, prepare, provideNgtRenderer, provideNgtStore, signalStore, updateCamera, vector2, vector3 };
2865
+ export { HTML, NGT_STORE, NgtArgs, NgtCanvas, NgtPortal, NgtPortalContent, NgtRenderer, NgtRendererFactory, NgtRoutedScene, ROUTED_SCENE, addAfterEffect, addEffect, addTail, applyProps, checkNeedsUpdate, checkUpdate, createApiToken, createAttachFunction, extend, getLocalState, injectBeforeRender, injectLoader, injectNextBeforeRender, injectStore, invalidateInstance, is, makeCameraInstance, makeDpr, makeId, makeObjectGraph, makeRendererInstance, merge, omit, pick, prepare, provideNgtRenderer, provideStore, resolveRef, signalStore, updateCamera, vector2, vector3 };
3235
2866
  //# sourceMappingURL=angular-three.mjs.map