angular-three 2.0.0-beta.2 → 2.0.0-beta.21

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 (141) hide show
  1. package/README.md +4 -147
  2. package/esm2022/angular-three.mjs +1 -1
  3. package/esm2022/index.mjs +11 -10
  4. package/esm2022/lib/before-render.mjs +13 -0
  5. package/esm2022/lib/canvas.mjs +130 -161
  6. package/esm2022/lib/directives/args.mjs +13 -11
  7. package/esm2022/lib/directives/common.mjs +29 -27
  8. package/esm2022/lib/directives/key.mjs +29 -0
  9. package/esm2022/lib/directives/parent.mjs +13 -11
  10. package/esm2022/lib/directives/repeat.mjs +5 -6
  11. package/esm2022/lib/dom/events.mjs +6 -1
  12. package/esm2022/lib/events.mjs +75 -58
  13. package/esm2022/lib/instance.mjs +65 -0
  14. package/esm2022/lib/loader.mjs +30 -37
  15. package/esm2022/lib/loop.mjs +6 -3
  16. package/esm2022/lib/portal.mjs +91 -102
  17. package/esm2022/lib/ref.mjs +48 -0
  18. package/esm2022/lib/renderer/catalogue.mjs +7 -0
  19. package/esm2022/lib/renderer/constants.mjs +21 -0
  20. package/esm2022/lib/renderer/index.mjs +419 -0
  21. package/esm2022/lib/renderer/store.mjs +144 -108
  22. package/esm2022/lib/renderer/utils.mjs +63 -48
  23. package/esm2022/lib/roots.mjs +249 -0
  24. package/esm2022/lib/routed-scene.mjs +11 -8
  25. package/esm2022/lib/store.mjs +207 -0
  26. package/esm2022/lib/three-types.mjs +2 -2
  27. package/esm2022/lib/types.mjs +1 -1
  28. package/esm2022/lib/utils/apply-props.mjs +23 -11
  29. package/esm2022/lib/utils/assert-injection-context.mjs +14 -0
  30. package/esm2022/lib/utils/attach.mjs +2 -2
  31. package/esm2022/lib/utils/create-injection-token.mjs +47 -0
  32. package/esm2022/lib/utils/is.mjs +1 -1
  33. package/esm2022/lib/utils/make.mjs +1 -1
  34. package/esm2022/lib/utils/safe-detect-changes.mjs +15 -13
  35. package/esm2022/lib/utils/signal-store.mjs +91 -0
  36. package/esm2022/lib/utils/update.mjs +1 -1
  37. package/fesm2022/angular-three.mjs +1770 -1589
  38. package/fesm2022/angular-three.mjs.map +1 -1
  39. package/index.d.ts +10 -9
  40. package/lib/{di/before-render.d.ts → before-render.d.ts} +1 -1
  41. package/lib/canvas.d.ts +81 -11
  42. package/lib/directives/args.d.ts +2 -2
  43. package/lib/directives/common.d.ts +5 -1
  44. package/lib/directives/key.d.ts +10 -0
  45. package/lib/directives/parent.d.ts +5 -5
  46. package/lib/dom/events.d.ts +3 -2
  47. package/lib/events.d.ts +78 -2
  48. package/lib/instance.d.ts +36 -0
  49. package/lib/loader.d.ts +13 -2
  50. package/lib/loop.d.ts +64 -6
  51. package/lib/portal.d.ts +20 -12
  52. package/lib/{di/ref.d.ts → ref.d.ts} +3 -2
  53. package/lib/renderer/catalogue.d.ts +9 -0
  54. package/lib/renderer/constants.d.ts +20 -0
  55. package/lib/renderer/index.d.ts +5 -0
  56. package/lib/renderer/store.d.ts +19 -15
  57. package/lib/renderer/utils.d.ts +28 -18
  58. package/lib/roots.d.ts +11 -0
  59. package/lib/routed-scene.d.ts +1 -1
  60. package/lib/store.d.ts +143 -0
  61. package/lib/three-types.d.ts +6 -6
  62. package/lib/types.d.ts +1 -309
  63. package/lib/utils/apply-props.d.ts +4 -2
  64. package/lib/utils/attach.d.ts +5 -3
  65. package/lib/utils/create-injection-token.d.ts +27 -0
  66. package/lib/utils/is.d.ts +4 -3
  67. package/lib/utils/make.d.ts +12 -1
  68. package/lib/utils/safe-detect-changes.d.ts +2 -2
  69. package/lib/utils/signal-store.d.ts +17 -0
  70. package/lib/utils/update.d.ts +1 -1
  71. package/metadata.json +1 -1
  72. package/package.json +5 -4
  73. package/plugin/generators.json +47 -17
  74. package/plugin/package.json +2 -5
  75. package/plugin/src/generators/init/compat.d.ts +3 -1
  76. package/plugin/src/generators/init/compat.js +2 -2
  77. package/plugin/src/generators/init/compat.js.map +1 -1
  78. package/plugin/src/generators/init/files/experience/experience.component.html.__tmpl__ +4 -0
  79. package/plugin/src/generators/init/files/experience/experience.component.ts.__tmpl__ +17 -0
  80. package/plugin/src/generators/init/generator.d.ts +6 -0
  81. package/plugin/src/generators/init/generator.js +144 -0
  82. package/plugin/src/generators/init/generator.js.map +1 -0
  83. package/plugin/src/generators/init/schema.json +15 -4
  84. package/plugin/src/generators/init-cannon/compat.d.ts +2 -0
  85. package/plugin/src/generators/init-cannon/compat.js +6 -0
  86. package/plugin/src/generators/init-cannon/compat.js.map +1 -0
  87. package/plugin/src/generators/init-cannon/generator.d.ts +2 -0
  88. package/plugin/src/generators/init-cannon/generator.js +22 -0
  89. package/plugin/src/generators/init-cannon/generator.js.map +1 -0
  90. package/plugin/src/generators/init-cannon/schema.json +6 -0
  91. package/plugin/src/generators/init-postprocessing/compat.d.ts +2 -0
  92. package/plugin/src/generators/init-postprocessing/compat.js +6 -0
  93. package/plugin/src/generators/init-postprocessing/compat.js.map +1 -0
  94. package/plugin/src/generators/init-postprocessing/generator.d.ts +2 -0
  95. package/plugin/src/generators/init-postprocessing/generator.js +20 -0
  96. package/plugin/src/generators/init-postprocessing/generator.js.map +1 -0
  97. package/plugin/src/generators/init-postprocessing/schema.json +6 -0
  98. package/plugin/src/generators/init-soba/compat.d.ts +2 -0
  99. package/plugin/src/generators/init-soba/compat.js +6 -0
  100. package/plugin/src/generators/init-soba/compat.js.map +1 -0
  101. package/plugin/src/generators/init-soba/generator.d.ts +2 -0
  102. package/plugin/src/generators/init-soba/generator.js +26 -0
  103. package/plugin/src/generators/init-soba/generator.js.map +1 -0
  104. package/plugin/src/generators/init-soba/schema.json +6 -0
  105. package/plugin/src/generators/utils.d.ts +2 -0
  106. package/plugin/src/generators/utils.js +34 -0
  107. package/plugin/src/generators/utils.js.map +1 -0
  108. package/plugin/src/generators/versions.d.ts +12 -0
  109. package/plugin/src/generators/versions.js +16 -0
  110. package/plugin/src/generators/versions.js.map +1 -0
  111. package/plugin/src/index.d.ts +3 -1
  112. package/plugin/src/index.js +7 -3
  113. package/plugin/src/index.js.map +1 -1
  114. package/web-types.json +1 -1
  115. package/esm2022/lib/di/before-render.mjs +0 -13
  116. package/esm2022/lib/di/catalogue.mjs +0 -7
  117. package/esm2022/lib/di/ref.mjs +0 -49
  118. package/esm2022/lib/renderer/di.mjs +0 -3
  119. package/esm2022/lib/renderer/enums.mjs +0 -2
  120. package/esm2022/lib/renderer/provider.mjs +0 -18
  121. package/esm2022/lib/renderer/renderer.mjs +0 -365
  122. package/esm2022/lib/stores/signal.store.mjs +0 -81
  123. package/esm2022/lib/stores/store.mjs +0 -423
  124. package/esm2022/lib/utils/assert-in-injection-context.mjs +0 -14
  125. package/esm2022/lib/utils/instance.mjs +0 -63
  126. package/esm2022/lib/utils/signal.mjs +0 -24
  127. package/esm2022/lib/utils/timing.mjs +0 -21
  128. package/lib/di/catalogue.d.ts +0 -3
  129. package/lib/renderer/di.d.ts +0 -2
  130. package/lib/renderer/enums.d.ts +0 -26
  131. package/lib/renderer/provider.d.ts +0 -8
  132. package/lib/renderer/renderer.d.ts +0 -49
  133. package/lib/stores/signal.store.d.ts +0 -20
  134. package/lib/stores/store.d.ts +0 -13
  135. package/lib/utils/instance.d.ts +0 -4
  136. package/lib/utils/signal.d.ts +0 -2
  137. package/lib/utils/timing.d.ts +0 -4
  138. package/plugin/src/generators/init/init.d.ts +0 -5
  139. package/plugin/src/generators/init/init.js +0 -56
  140. package/plugin/src/generators/init/init.js.map +0 -1
  141. /package/lib/utils/{assert-in-injection-context.d.ts → assert-injection-context.d.ts} +0 -0
@@ -1,107 +1,97 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, untracked, Injectable, Optional, Inject, ElementRef, inject, Injector, assertInInjectionContext, runInInjectionContext, ChangeDetectorRef, effect, InjectionToken, EventEmitter, ViewContainerRef, NgZone, TemplateRef, Directive, Input, getDebugNode, RendererFactory2, makeEnvironmentProviders, provideZoneChangeDetection, EnvironmentInjector, DestroyRef, createEnvironmentInjector, Component, ChangeDetectionStrategy, Output, ViewChild, SkipSelf, ContentChild } from '@angular/core';
3
- import { provideNgxResizeOptions, NgxResize } from 'ngx-resize';
4
- import * as THREE from 'three';
2
+ import { untracked, computed, signal, ElementRef, inject, Injector, assertInInjectionContext, runInInjectionContext, DestroyRef, effect, InjectionToken, ChangeDetectorRef, Optional, SkipSelf, ViewContainerRef, NgZone, TemplateRef, Directive, Input, EventEmitter, getDebugNode, RendererFactory2, Injectable, makeEnvironmentProviders, provideZoneChangeDetection, EnvironmentInjector, createEnvironmentInjector, Component, ChangeDetectionStrategy, Output, ViewChild, ContentChild } from '@angular/core';
5
3
  import { DOCUMENT, NgForOf, NgIf } from '@angular/common';
4
+ import { Subject, filter } from 'rxjs';
5
+ import * as THREE from 'three';
6
+ import { provideNgxResizeOptions, NgxResize } from 'ngx-resize';
6
7
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
8
  import * as i1 from '@angular/router';
8
9
  import { ActivationEnd, RouterOutlet } from '@angular/router';
9
- import { filter } from 'rxjs';
10
10
 
11
- function createSignal(initialValue, options = {}) {
12
- const original = signal(initialValue, options);
13
- const originalSet = original.set.bind(original);
14
- const originalUpdate = original.update.bind(original);
15
- original.set = (...args) => {
16
- try {
17
- originalSet(...args);
18
- }
19
- catch {
20
- requestAnimationFrame(() => originalSet(...args));
21
- }
11
+ const STORE_COMPUTED_KEY = '__ngt_signal_store_computed__';
12
+ const setter = (_source) => (state) => {
13
+ const updater = (previous) => {
14
+ const partial = typeof state === 'function' ? state(previous) : state;
15
+ Object.keys(partial).forEach((key) => {
16
+ const typedKey = key;
17
+ if (partial[typedKey] === undefined && previous[typedKey] != null) {
18
+ partial[typedKey] = previous[typedKey];
19
+ }
20
+ });
21
+ return partial;
22
22
  };
23
- original.update = (...args) => {
24
- try {
25
- originalUpdate(...args);
26
- }
27
- catch {
28
- requestAnimationFrame(() => originalUpdate(...args));
29
- }
23
+ untracked(() => {
24
+ _source.update((previous) => ({ ...previous, ...updater(previous) }));
25
+ });
26
+ };
27
+ const patcher = (_source) => (state) => {
28
+ const updater = (previous) => {
29
+ Object.keys(state).forEach((key) => {
30
+ const typedKey = key;
31
+ if (state[typedKey] === undefined && previous[typedKey] != null) {
32
+ state[typedKey] = previous[typedKey];
33
+ }
34
+ });
35
+ return state;
30
36
  };
31
- return original;
32
- }
33
-
34
- const STORE_COMPUTED_KEY = '__ngt_store_computed__';
35
- class NgtSignalStore {
36
- #state;
37
- #computedCache = new Map();
38
- constructor(initialState = {}) {
39
- initialState ??= {};
40
- this.#state = createSignal(Object.assign(initialState, { __ngt_dummy_state__: Date.now() }));
41
- }
42
- select(...keysAndOptions) {
43
- if (keysAndOptions.length === 0)
44
- return this.#state.asReadonly();
45
- if (keysAndOptions.length === 1 && typeof keysAndOptions[0] === 'object') {
46
- if (!this.#computedCache.has(STORE_COMPUTED_KEY)) {
47
- this.#computedCache.set(STORE_COMPUTED_KEY, computed(() => this.#state(), keysAndOptions));
48
- return this.#computedCache.get(STORE_COMPUTED_KEY);
49
- }
50
- }
51
- const [keys, options] = parseOptions(keysAndOptions);
52
- const joinedKeys = keys.join('-');
53
- if (!this.#computedCache.has(joinedKeys)) {
54
- this.#computedCache.set(joinedKeys, computed(() => {
55
- const state = this.#state();
56
- return keys.reduce((value, key) => value[key], state);
57
- }, options));
58
- }
59
- return this.#computedCache.get(joinedKeys);
60
- }
61
- get(...keys) {
62
- const state = untracked(this.#state);
63
- if (keys.length === 0)
64
- return state;
65
- return keys.reduce((value, key) => value[key], state);
66
- }
67
- set(state) {
68
- const updater = (previous) => {
69
- const partial = typeof state === 'function' ? state(previous) : state;
70
- Object.keys(partial).forEach((key) => {
71
- const partialKey = key;
72
- if (partial[partialKey] === undefined && previous[partialKey] != null) {
73
- partial[partialKey] = previous[partialKey];
74
- }
75
- });
76
- return partial;
77
- };
78
- this.#state.update((previous) => ({ ...previous, ...updater(previous) }));
79
- // this.#state.update(previous => ({...previous, ...(typeof state === 'function' ? state(previous) : state)}))
80
- }
81
- patch(state) {
82
- const updater = (previous) => {
83
- Object.keys(state).forEach((key) => {
84
- const partialKey = key;
85
- if (state[partialKey] === undefined && previous[partialKey] != null) {
86
- state[partialKey] = previous[partialKey];
87
- }
88
- });
89
- return state;
90
- };
91
- this.#state.update((previous) => ({ ...updater(previous), ...previous }));
37
+ untracked(() => {
38
+ _source.update((previous) => ({ ...updater(previous), ...previous }));
39
+ });
40
+ };
41
+ const getter = (_source) => (...keys) => {
42
+ const root = untracked(_source);
43
+ if (keys.length === 0)
44
+ return root;
45
+ return keys.reduce((value, key) => value[key], root);
46
+ };
47
+ const selector = (_state, computedCache) => (...keysAndOptions) => {
48
+ if (keysAndOptions.length === 0)
49
+ return _state;
50
+ if (keysAndOptions.length === 1 && typeof keysAndOptions[0] === 'object') {
51
+ const cachedKey = STORE_COMPUTED_KEY.concat(JSON.stringify(keysAndOptions[0]));
52
+ if (!computedCache.has(cachedKey)) {
53
+ computedCache.set(cachedKey, computed(_state, keysAndOptions));
54
+ }
55
+ return computedCache.get(cachedKey);
56
+ }
57
+ const [keys, options] = parseStoreOptions(keysAndOptions);
58
+ const joinedKeys = keys.join('-');
59
+ const cachedKeys = joinedKeys.concat(options ? JSON.stringify(options) : '');
60
+ if (!computedCache.has(cachedKeys)) {
61
+ computedCache.set(cachedKeys, computed(() => keys.reduce((value, key) => value[key], _state()), options));
62
+ }
63
+ return computedCache.get(cachedKeys);
64
+ };
65
+ function signalStore(initialState = {}, options) {
66
+ let source;
67
+ let set;
68
+ let get;
69
+ let patch;
70
+ if (typeof initialState === 'function') {
71
+ source = signal({}, options);
72
+ get = getter(source);
73
+ set = setter(source);
74
+ patch = patcher(source);
75
+ source.set(initialState({ set, get, patch }));
92
76
  }
93
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtSignalStore, deps: [{ token: 'INITIAL_STATE', optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
94
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtSignalStore }); }
77
+ else {
78
+ source = signal(initialState, options);
79
+ get = getter(source);
80
+ set = setter(source);
81
+ patch = patcher(source);
82
+ }
83
+ const state = source.asReadonly();
84
+ const computedCache = new Map();
85
+ const store = { select: selector(state, computedCache), get, set, patch, state };
86
+ // NOTE: internal _snapshot to debug current state
87
+ Object.defineProperty(store, '_snapshot', {
88
+ get: state,
89
+ configurable: false,
90
+ enumerable: false,
91
+ });
92
+ return store;
95
93
  }
96
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtSignalStore, decorators: [{
97
- type: Injectable
98
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
99
- type: Optional
100
- }, {
101
- type: Inject,
102
- args: ['INITIAL_STATE']
103
- }] }]; } });
104
- function parseOptions(keysAndOptions) {
94
+ function parseStoreOptions(keysAndOptions) {
105
95
  if (typeof keysAndOptions.at(-1) === 'object') {
106
96
  return [keysAndOptions.slice(0, -1), keysAndOptions.at(-1)];
107
97
  }
@@ -203,34 +193,37 @@ function invalidateInstance(instance) {
203
193
  function prepare(object, localState) {
204
194
  const instance = object;
205
195
  if (localState?.primitive || !instance.__ngt__) {
206
- const { objects = createSignal([]), nonObjects = createSignal([]), ...rest } = localState || {};
196
+ const { objects = signal([]), nonObjects = signal([]), ...rest } = localState || {};
207
197
  instance.__ngt__ = {
208
198
  previousAttach: null,
209
199
  store: null,
210
- parent: createSignal(null),
200
+ parent: signal(null),
211
201
  memoized: {},
212
202
  eventCount: 0,
213
203
  handlers: {},
214
204
  objects,
215
205
  nonObjects,
216
- nativeProps: new NgtSignalStore({}),
206
+ nativeProps: signalStore(),
217
207
  add: (object, type) => {
218
- const current = untracked(instance.__ngt__[type]);
219
- const foundIndex = current.indexOf((obj) => obj === object);
220
- if (foundIndex > -1) {
221
- // if we add an object with the same reference, then we switch it out
222
- // and update the BehaviorSubject
223
- current.splice(foundIndex, 1, object);
224
- instance.__ngt__[type].set(current);
225
- }
226
- else {
227
- instance.__ngt__[type].update((prev) => [...prev, object]);
228
- }
229
- notifyAncestors(untracked(instance.__ngt__.parent));
208
+ untracked(() => {
209
+ const current = instance.__ngt__[type]();
210
+ const foundIndex = current.indexOf((obj) => obj === object);
211
+ if (foundIndex > -1) {
212
+ // if we add an object with the same reference, then we switch it out
213
+ current.splice(foundIndex, 1, object);
214
+ instance.__ngt__[type].set(current);
215
+ }
216
+ else {
217
+ instance.__ngt__[type].update((prev) => [...prev, object]);
218
+ }
219
+ notifyAncestors(instance.__ngt__.parent());
220
+ });
230
221
  },
231
222
  remove: (object, type) => {
232
- instance.__ngt__[type].update((prev) => prev.filter((o) => o !== object));
233
- notifyAncestors(untracked(instance.__ngt__.parent));
223
+ untracked(() => {
224
+ instance.__ngt__[type].update((prev) => prev.filter((o) => o !== object));
225
+ notifyAncestors(instance.__ngt__.parent());
226
+ });
234
227
  },
235
228
  ...rest,
236
229
  };
@@ -245,7 +238,142 @@ function notifyAncestors(instance) {
245
238
  localState.objects.update((prev) => prev);
246
239
  if (localState.nonObjects)
247
240
  localState.nonObjects.update((prev) => prev);
248
- notifyAncestors(untracked(localState.parent));
241
+ notifyAncestors(localState.parent());
242
+ }
243
+
244
+ // This function prepares a set of changes to be applied to the instance
245
+ function diffProps(instance, props) {
246
+ const propsEntries = Object.entries(props);
247
+ const changes = [];
248
+ for (const [propKey, propValue] of propsEntries) {
249
+ let key = propKey;
250
+ if (is.colorSpaceExist(instance)) {
251
+ if (propKey === 'encoding') {
252
+ key = 'colorSpace';
253
+ }
254
+ else if (propKey === 'outputEncoding') {
255
+ key = 'outputColorSpace';
256
+ }
257
+ }
258
+ if (is.equ(propValue, instance[key]))
259
+ continue;
260
+ changes.push([propKey, propValue]);
261
+ }
262
+ return changes;
263
+ }
264
+ // This function applies a set of changes to the instance
265
+ function applyProps(instance, props) {
266
+ // if props is empty
267
+ if (!Object.keys(props).length)
268
+ return instance;
269
+ // Filter equals, events and reserved props
270
+ // filter equals, events , and reserved props
271
+ const localState = getLocalState(instance);
272
+ const rootState = localState.store?.get();
273
+ const changes = diffProps(instance, props);
274
+ for (let i = 0; i < changes.length; i++) {
275
+ let [key, value] = changes[i];
276
+ // Alias (output)encoding => (output)colorSpace (since r152)
277
+ // https://github.com/pmndrs/react-three-fiber/pull/2829
278
+ if (is.colorSpaceExist(instance)) {
279
+ const sRGBEncoding = 3001;
280
+ const SRGBColorSpace = 'srgb';
281
+ const LinearSRGBColorSpace = 'srgb-linear';
282
+ if (key === 'encoding') {
283
+ key = 'colorSpace';
284
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
285
+ }
286
+ else if (key === 'outputEncoding') {
287
+ key = 'outputColorSpace';
288
+ value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
289
+ }
290
+ }
291
+ const currentInstance = instance;
292
+ const targetProp = currentInstance[key];
293
+ // special treatmen for objects with support for set/copy, and layers
294
+ if (targetProp && targetProp['set'] && (targetProp['copy'] || targetProp instanceof THREE.Layers)) {
295
+ const isColor = targetProp instanceof THREE.Color;
296
+ // if value is an array
297
+ if (Array.isArray(value)) {
298
+ if (targetProp['fromArray'])
299
+ targetProp['fromArray'](value);
300
+ else
301
+ targetProp['set'](...value);
302
+ }
303
+ // test again target.copy
304
+ else if (targetProp['copy'] &&
305
+ value &&
306
+ value.constructor &&
307
+ targetProp.constructor.name === value.constructor.name) {
308
+ targetProp['copy'](value);
309
+ if (!THREE.ColorManagement && !rootState.linear && isColor)
310
+ targetProp['convertSRGBToLinear']();
311
+ }
312
+ // if nothing else fits, just set the single value, ignore undefined
313
+ else if (value !== undefined) {
314
+ const isColor = targetProp instanceof THREE.Color;
315
+ // allow setting array scalars
316
+ if (!isColor && targetProp['setScalar'])
317
+ targetProp['setScalar'](value);
318
+ // layers have no copy function, copy the mask
319
+ else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers)
320
+ targetProp.mask = value.mask;
321
+ // otherwise just set ...
322
+ else
323
+ targetProp['set'](value);
324
+ // auto-convert srgb
325
+ if (!THREE.ColorManagement && !rootState?.linear && isColor)
326
+ targetProp.convertSRGBToLinear();
327
+ }
328
+ }
329
+ // else just overwrite the value
330
+ else {
331
+ currentInstance[key] = value;
332
+ // auto-convert srgb textures
333
+ if (currentInstance[key] instanceof THREE.Texture &&
334
+ currentInstance[key].format === THREE.RGBAFormat &&
335
+ currentInstance[key].type === THREE.UnsignedByteType) {
336
+ const texture = currentInstance[key];
337
+ if (rootState?.gl) {
338
+ if (is.colorSpaceExist(texture) && is.colorSpaceExist(rootState.gl))
339
+ texture.colorSpace = rootState.gl.outputColorSpace;
340
+ else
341
+ texture.encoding = rootState.gl.outputEncoding;
342
+ }
343
+ }
344
+ }
345
+ checkUpdate(currentInstance[key]);
346
+ checkUpdate(targetProp);
347
+ invalidateInstance(instance);
348
+ }
349
+ const instanceHandlers = localState.eventCount;
350
+ const parent = localState.parent ? untracked(localState.parent) : null;
351
+ if (parent && rootState.internal && instance['raycast'] && instanceHandlers !== localState.eventCount) {
352
+ // Pre-emptively remove the instance from the interaction manager
353
+ const index = rootState.internal.interaction.indexOf(instance);
354
+ if (index > -1)
355
+ rootState.internal.interaction.splice(index, 1);
356
+ // Add the instance to the interaction manager only when it has handlers
357
+ if (localState.eventCount)
358
+ rootState.internal.interaction.push(instance);
359
+ }
360
+ if (parent && localState.afterUpdate && localState.afterUpdate.observed && changes.length) {
361
+ localState.afterUpdate.emit(instance);
362
+ }
363
+ return instance;
364
+ }
365
+
366
+ function assertInjectionContext(fn, injector) {
367
+ try {
368
+ if (!injector) {
369
+ return inject(Injector);
370
+ }
371
+ return injector;
372
+ }
373
+ catch {
374
+ !injector && assertInInjectionContext(fn);
375
+ return null;
376
+ }
249
377
  }
250
378
 
251
379
  const idCache = {};
@@ -297,89 +425,734 @@ function makeObjectGraph(object) {
297
425
  return data;
298
426
  }
299
427
 
300
- function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
301
- const captureData = captures.get(obj);
302
- if (captureData) {
303
- captures.delete(obj);
304
- // if this was the last captured object for this pointer
305
- if (captures.size === 0) {
306
- capturedMap.delete(pointerId);
307
- captureData.target.releasePointerCapture(pointerId);
308
- }
309
- }
310
- }
311
- function removeInteractivity(store, object) {
312
- const internal = store.get('internal');
313
- // removes every trace of an object from data store
314
- internal.interaction = internal.interaction.filter((o) => o !== object);
315
- internal.initialHits = internal.initialHits.filter((o) => o !== object);
316
- internal.hovered.forEach((value, key) => {
317
- if (value.eventObject === object || value.object === object) {
318
- // clear out intersects, they are outdated by now
319
- internal.hovered.delete(key);
320
- }
321
- });
322
- internal.capturedMap.forEach((captures, pointerId) => {
323
- releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
428
+ const shallowLoose = { objects: 'shallow', strict: false };
429
+ const roots = new Map();
430
+ function injectCanvasRootInitializer(injector) {
431
+ injector = assertInjectionContext(injectCanvasRootInitializer, injector);
432
+ return runInInjectionContext(injector, () => {
433
+ const injectedStore = injectNgtStore();
434
+ const loop = injectNgtLoop();
435
+ const destroyRef = inject(DestroyRef);
436
+ return (canvas) => {
437
+ const exist = roots.has(canvas);
438
+ let store = roots.get(canvas);
439
+ if (store) {
440
+ console.warn('[NGT] Same canvas root is being created twice');
441
+ }
442
+ store ||= injectedStore;
443
+ if (!store) {
444
+ throw new Error('[NGT] No store initialized');
445
+ }
446
+ if (!exist) {
447
+ roots.set(canvas, store);
448
+ }
449
+ let isConfigured = false;
450
+ let invalidateRef;
451
+ destroyRef.onDestroy(() => invalidateRef?.destroy());
452
+ return {
453
+ isConfigured,
454
+ destroy: (timeout = 500) => {
455
+ const root = roots.get(canvas);
456
+ if (root) {
457
+ root.set((state) => ({ internal: { ...state.internal, active: false } }));
458
+ setTimeout(() => {
459
+ try {
460
+ const state = root.get();
461
+ state.events.disconnect?.();
462
+ state.gl?.renderLists?.dispose?.();
463
+ state.gl?.forceContextLoss?.();
464
+ if (state.gl?.xr)
465
+ state.xr.disconnect();
466
+ dispose(state);
467
+ roots.delete(canvas);
468
+ }
469
+ catch (e) {
470
+ console.error('[NGT] Unexpected error while destroying Canvas Root', e);
471
+ }
472
+ }, timeout);
473
+ }
474
+ },
475
+ configure: (inputs) => {
476
+ const { gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, orthographic, lookAt, shadows, linear, legacy, flat, dpr, frameloop, performance, } = inputs;
477
+ const state = store.get();
478
+ const stateToUpdate = {};
479
+ // setup renderer
480
+ let gl = state.gl;
481
+ if (!state.gl)
482
+ stateToUpdate.gl = gl = makeDefaultRenderer(glOptions, canvas);
483
+ // setup raycaster
484
+ let raycaster = state.raycaster;
485
+ if (!raycaster)
486
+ stateToUpdate.raycaster = raycaster = new THREE.Raycaster();
487
+ // set raycaster options
488
+ const { params, ...options } = raycasterOptions || {};
489
+ if (!is.equ(options, raycaster, shallowLoose))
490
+ applyProps(raycaster, { ...options });
491
+ if (!is.equ(params, raycaster.params, shallowLoose)) {
492
+ applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
493
+ }
494
+ // create default camera
495
+ if (!state.camera) {
496
+ const isCamera = is.camera(cameraOptions);
497
+ let camera = isCamera ? cameraOptions : makeDefaultCamera(orthographic || false, state.size);
498
+ if (!isCamera) {
499
+ if (cameraOptions)
500
+ applyProps(camera, cameraOptions);
501
+ // set position.z
502
+ if (!cameraOptions?.position)
503
+ camera.position.z = 5;
504
+ // always look at center or passed-in lookAt by default
505
+ if (!cameraOptions?.rotation && !cameraOptions?.quaternion) {
506
+ if (Array.isArray(lookAt))
507
+ camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
508
+ else if (lookAt instanceof THREE.Vector3)
509
+ camera.lookAt(lookAt);
510
+ else
511
+ camera.lookAt(0, 0, 0);
512
+ }
513
+ // update projection matrix after applyprops
514
+ camera.updateProjectionMatrix?.();
515
+ }
516
+ if (!is.instance(camera))
517
+ camera = prepare(camera, { store });
518
+ stateToUpdate.camera = camera;
519
+ }
520
+ // Set up scene (one time only!)
521
+ if (!state.scene) {
522
+ let scene;
523
+ if (sceneOptions instanceof THREE.Scene) {
524
+ scene = prepare(sceneOptions, { store });
525
+ }
526
+ else {
527
+ scene = prepare(new THREE.Scene(), { store });
528
+ if (sceneOptions)
529
+ applyProps(scene, sceneOptions);
530
+ }
531
+ stateToUpdate.scene = scene;
532
+ }
533
+ // Set up XR (one time only!)
534
+ if (!state.xr) {
535
+ // Handle frame behavior in WebXR
536
+ const handleXRFrame = (timestamp, frame) => {
537
+ const state = store.get();
538
+ if (state.frameloop === 'never')
539
+ return;
540
+ loop.advance(timestamp, true, store, frame);
541
+ };
542
+ // Toggle render switching on session
543
+ const handleSessionChange = () => {
544
+ const state = store.get();
545
+ state.gl.xr.enabled = state.gl.xr.isPresenting;
546
+ state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
547
+ if (!state.gl.xr.isPresenting)
548
+ loop.invalidate(store);
549
+ };
550
+ // WebXR session manager
551
+ const xr = {
552
+ connect: () => {
553
+ gl.xr.addEventListener('sessionstart', handleSessionChange);
554
+ gl.xr.addEventListener('sessionend', handleSessionChange);
555
+ },
556
+ disconnect: () => {
557
+ gl.xr.removeEventListener('sessionstart', handleSessionChange);
558
+ gl.xr.removeEventListener('sessionend', handleSessionChange);
559
+ },
560
+ };
561
+ // Subscribe to WebXR session events
562
+ if (gl.xr && typeof gl.xr.addEventListener === 'function')
563
+ xr.connect();
564
+ stateToUpdate.xr = xr;
565
+ }
566
+ // Set shadowmap
567
+ if (gl.shadowMap) {
568
+ const oldEnabled = gl.shadowMap.enabled;
569
+ const oldType = gl.shadowMap.type;
570
+ gl.shadowMap.enabled = !!shadows;
571
+ if (typeof shadows === 'boolean') {
572
+ gl.shadowMap.type = THREE.PCFSoftShadowMap;
573
+ }
574
+ else if (typeof shadows === 'string') {
575
+ const types = {
576
+ basic: THREE.BasicShadowMap,
577
+ percentage: THREE.PCFShadowMap,
578
+ soft: THREE.PCFSoftShadowMap,
579
+ variance: THREE.VSMShadowMap,
580
+ };
581
+ gl.shadowMap.type = types[shadows] ?? THREE.PCFSoftShadowMap;
582
+ }
583
+ else if (is.obj(shadows)) {
584
+ Object.assign(gl.shadowMap, shadows);
585
+ }
586
+ if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type)
587
+ checkNeedsUpdate(gl.shadowMap);
588
+ }
589
+ // Safely set color management if available.
590
+ // Avoid accessing THREE.ColorManagement to play nice with older versions
591
+ if (THREE.ColorManagement) {
592
+ const ColorManagement = THREE.ColorManagement;
593
+ if ('enabled' in ColorManagement)
594
+ ColorManagement['enabled'] = !legacy ?? false;
595
+ else if ('legacyMode' in ColorManagement)
596
+ ColorManagement['legacyMode'] = legacy ?? true;
597
+ }
598
+ // set color space and tonemapping preferences
599
+ const LinearEncoding = 3000;
600
+ const sRGBEncoding = 3001;
601
+ applyProps(gl, {
602
+ outputEncoding: linear ? LinearEncoding : sRGBEncoding,
603
+ toneMapping: flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping,
604
+ });
605
+ // Update color management state
606
+ if (state.legacy !== legacy)
607
+ stateToUpdate.legacy = legacy;
608
+ if (state.linear !== linear)
609
+ stateToUpdate.linear = linear;
610
+ if (state.flat !== flat)
611
+ stateToUpdate.flat = flat;
612
+ // Set gl props
613
+ gl.setClearAlpha(0);
614
+ gl.setPixelRatio(makeDpr(state.viewport.dpr));
615
+ gl.setSize(state.size.width, state.size.height);
616
+ if (is.obj(glOptions) &&
617
+ !(typeof glOptions === 'function') &&
618
+ !is.renderer(glOptions) &&
619
+ !is.equ(glOptions, gl, shallowLoose)) {
620
+ applyProps(gl, glOptions);
621
+ }
622
+ // Store events internally
623
+ if (events && !state.events.handlers)
624
+ stateToUpdate.events = events(store);
625
+ // Check performance
626
+ if (performance && !is.equ(performance, state.performance, shallowLoose)) {
627
+ stateToUpdate.performance = { ...state.performance, ...performance };
628
+ }
629
+ store.set(stateToUpdate);
630
+ // Check size, allow it to take on container bounds initially
631
+ const size = computeInitialSize(canvas, sizeOptions);
632
+ if (!is.equ(size, state.size, shallowLoose)) {
633
+ state.setSize(size.width, size.height, size.top, size.left);
634
+ }
635
+ // Check pixelratio
636
+ if (dpr && state.viewport.dpr !== makeDpr(dpr))
637
+ state.setDpr(dpr);
638
+ // Check frameloop
639
+ if (state.frameloop !== frameloop)
640
+ state.setFrameloop(frameloop);
641
+ isConfigured = true;
642
+ invalidateRef?.destroy();
643
+ invalidateRef = effect(() => void store.state().invalidate(), { manualCleanup: true, injector });
644
+ },
645
+ };
646
+ };
324
647
  });
325
- if (store.get('previousStore')) {
326
- removeInteractivity(store.get('previousStore'), object);
327
- }
328
648
  }
329
- function createEvents(store) {
330
- /** calculates delta **/
331
- function calculateDistance(event) {
332
- const internal = store.get('internal');
333
- const dx = event.offsetX - internal.initialClick[0];
334
- const dy = event.offsetY - internal.initialClick[1];
335
- return Math.round(Math.sqrt(dx * dx + dy * dy));
649
+ function computeInitialSize(canvas, defaultSize) {
650
+ if (defaultSize)
651
+ return defaultSize;
652
+ if (canvas instanceof HTMLCanvasElement && canvas.parentElement) {
653
+ return canvas.parentElement.getBoundingClientRect();
336
654
  }
337
- /** returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc... **/
338
- function filterPointerEvents(objects) {
339
- return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
340
- const eventName = `pointer${name}`;
341
- return getLocalState(obj).handlers?.[eventName];
342
- }));
655
+ return { width: 0, height: 0, top: 0, left: 0 };
656
+ }
657
+ // Disposes an object and all its properties
658
+ function dispose(obj) {
659
+ if (obj.dispose && obj.type !== 'Scene')
660
+ obj.dispose();
661
+ for (const p in obj) {
662
+ p.dispose?.();
663
+ delete obj[p];
343
664
  }
344
- function intersect(event, filter) {
345
- const state = store.get();
346
- const duplicates = new Set();
347
- const intersections = [];
348
- // allow callers to eliminate event objects
349
- const eventObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
350
- // reset all raycaster cameras to undefined
351
- for (let i = 0; i < eventObjects.length; i++) {
352
- const instanceState = getLocalState(eventObjects[i]).store?.get();
353
- if (instanceState) {
354
- instanceState.raycaster.camera = undefined;
355
- }
356
- }
357
- if (!state.previousStore) {
358
- // make sure root-level pointer and ray are setup
359
- state.events.compute?.(event, store);
665
+ }
666
+
667
+ function createInjectFn(token) {
668
+ return (injectOptions) => inject(token, injectOptions);
669
+ }
670
+ function createProvideFn(token, factory, deps, extraProviders) {
671
+ return (value) => {
672
+ let provider;
673
+ if (value) {
674
+ provider = { provide: token, useValue: value };
360
675
  }
361
- function handleRaycast(obj) {
362
- const objLocalState = getLocalState(obj);
363
- const objStore = objLocalState.store;
364
- const objState = objStore?.get();
365
- // skip event handling when noEvents is set, or when raycaster camera is null
366
- if (!objState || !objState.events.enabled || objState.raycaster.camera === null)
367
- return [];
368
- // when the camera is undefined, we have to call the events layers to update function
369
- if (objState.raycaster.camera === undefined) {
370
- objState.events.compute?.(event, objStore, objState.previousStore);
371
- // if the camera is still undefined, we have to skip this layer entirely
372
- if (objState.raycaster.camera === undefined)
373
- objState.raycaster.camera = null;
374
- }
375
- // intersect object by object
376
- return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
676
+ else {
677
+ provider = { provide: token, useFactory: factory, deps: (deps ?? []) };
377
678
  }
378
- // collect events
379
- let hits = eventObjects
380
- // intersect objects
679
+ return extraProviders ? [extraProviders, provider] : provider;
680
+ };
681
+ }
682
+ function createInjectionToken(factory, options) {
683
+ const opts = options ?? { isRoot: true };
684
+ opts.isRoot ??= true;
685
+ if (opts.isRoot) {
686
+ if (opts.token) {
687
+ throw new Error(`\
688
+ createInjectionToken is creating a root InjectionToken but an external token is passed in.
689
+ `);
690
+ }
691
+ const token = new InjectionToken(`Token for ${factory.name}`, {
692
+ factory: () => {
693
+ if (opts.deps && Array.isArray(opts.deps)) {
694
+ return factory(...opts.deps.map((dep) => inject(dep)));
695
+ }
696
+ return factory();
697
+ },
698
+ });
699
+ return [
700
+ createInjectFn(token),
701
+ createProvideFn(token, factory, opts.deps),
702
+ token,
703
+ ];
704
+ }
705
+ const token = opts.token || new InjectionToken(`Token for ${factory.name}`);
706
+ return [
707
+ createInjectFn(token),
708
+ createProvideFn(token, factory, opts.deps, opts.extraProviders),
709
+ token,
710
+ ];
711
+ }
712
+
713
+ function createSubs(callback, subs) {
714
+ const sub = { callback };
715
+ subs.add(sub);
716
+ return () => void subs.delete(sub);
717
+ }
718
+ const globalEffects = new Set();
719
+ const globalAfterEffects = new Set();
720
+ const globalTailEffects = new Set();
721
+ /**
722
+ * Adds a global render callback which is called each frame.
723
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
724
+ */
725
+ const addEffect = (callback) => createSubs(callback, globalEffects);
726
+ /**
727
+ * Adds a global after-render callback which is called each frame.
728
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
729
+ */
730
+ const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
731
+ /**
732
+ * Adds a global callback which is called when rendering stops.
733
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
734
+ */
735
+ const addTail = (callback) => createSubs(callback, globalTailEffects);
736
+ function run(effects, timestamp) {
737
+ if (!effects.size)
738
+ return;
739
+ for (const { callback } of effects.values()) {
740
+ callback(timestamp);
741
+ }
742
+ }
743
+ function flushGlobalEffects(type, timestamp) {
744
+ switch (type) {
745
+ case 'before':
746
+ return run(globalEffects, timestamp);
747
+ case 'after':
748
+ return run(globalAfterEffects, timestamp);
749
+ case 'tail':
750
+ return run(globalTailEffects, timestamp);
751
+ }
752
+ }
753
+ function render(timestamp, store, frame) {
754
+ const state = store.get();
755
+ // Run local effects
756
+ let delta = state.clock.getDelta();
757
+ // In frameloop='never' mode, clock times are updated using the provided timestamp
758
+ if (state.frameloop === 'never' && typeof timestamp === 'number') {
759
+ delta = timestamp - state.clock.elapsedTime;
760
+ state.clock.oldTime = state.clock.elapsedTime;
761
+ state.clock.elapsedTime = timestamp;
762
+ }
763
+ // Call subscribers (useFrame)
764
+ const subscribers = state.internal.subscribers;
765
+ for (let i = 0; i < subscribers.length; i++) {
766
+ const subscription = subscribers[i];
767
+ subscription.callback({ ...subscription.store.get(), delta, frame });
768
+ }
769
+ // Render content
770
+ if (!state.internal.priority && state.gl.render)
771
+ state.gl.render(state.scene, state.camera);
772
+ // Decrease frame count
773
+ state.internal.frames = Math.max(0, state.internal.frames - 1);
774
+ return state.frameloop === 'always' ? 1 : state.internal.frames;
775
+ }
776
+ function createLoop(roots) {
777
+ let running = false;
778
+ let repeat;
779
+ let frame;
780
+ function loop(timestamp) {
781
+ frame = requestAnimationFrame(loop);
782
+ running = true;
783
+ repeat = 0;
784
+ // Run effects
785
+ flushGlobalEffects('before', timestamp);
786
+ // Render all roots
787
+ for (const root of roots.values()) {
788
+ const state = root.get();
789
+ // If the frameloop is invalidated, do not run another frame
790
+ if (state.internal.active &&
791
+ (state.frameloop === 'always' || state.internal.frames > 0) &&
792
+ !state.gl.xr?.isPresenting) {
793
+ repeat += render(timestamp, root);
794
+ }
795
+ }
796
+ // Run after-effects
797
+ flushGlobalEffects('after', timestamp);
798
+ // Stop the loop if nothing invalidates it
799
+ if (repeat === 0) {
800
+ // Tail call effects, they are called when rendering stops
801
+ flushGlobalEffects('tail', timestamp);
802
+ // Flag end of operation
803
+ running = false;
804
+ return cancelAnimationFrame(frame);
805
+ }
806
+ }
807
+ function invalidate(store, frames = 1) {
808
+ const state = store?.get();
809
+ if (!state)
810
+ return roots.forEach((root) => invalidate(root, frames));
811
+ if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
812
+ return;
813
+ // Increase frames, do not go higher than 60
814
+ state.internal.frames = Math.min(60, state.internal.frames + frames);
815
+ // If the render-loop isn't active, start it
816
+ if (!running) {
817
+ running = true;
818
+ requestAnimationFrame(loop);
819
+ }
820
+ }
821
+ function advance(timestamp, runGlobalEffects = true, store, frame) {
822
+ if (runGlobalEffects)
823
+ flushGlobalEffects('before', timestamp);
824
+ const state = store?.get();
825
+ if (!state)
826
+ for (const root of roots.values())
827
+ render(timestamp, root);
828
+ else
829
+ render(timestamp, store, frame);
830
+ if (runGlobalEffects)
831
+ flushGlobalEffects('after', timestamp);
832
+ }
833
+ return {
834
+ loop,
835
+ /**
836
+ * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
837
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
838
+ */
839
+ invalidate,
840
+ /**
841
+ * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
842
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
843
+ */
844
+ advance,
845
+ };
846
+ }
847
+ const [injectNgtLoop, , NGT_LOOP] = createInjectionToken(() => createLoop(roots));
848
+
849
+ function safeDetectChanges(...cdrs) {
850
+ cdrs.forEach((cdr) => {
851
+ if (!cdr)
852
+ return;
853
+ try {
854
+ // dynamic created component with ViewContainerRef#createComponent does not have Context
855
+ // but it has _attachedToViewContainer
856
+ if (cdr['_attachedToViewContainer'] || !!cdr['context']) {
857
+ cdr.detectChanges();
858
+ }
859
+ }
860
+ catch (e) {
861
+ cdr.markForCheck();
862
+ }
863
+ });
864
+ }
865
+
866
+ function storeFactory(loop, document, injector, parent) {
867
+ return runInInjectionContext(injector, () => {
868
+ const window = document.defaultView;
869
+ if (!window) {
870
+ // TODO: revisit this when we need to support multiple platforms
871
+ throw new Error(`[NGT] Window is not available.`);
872
+ }
873
+ const cdr = inject(ChangeDetectorRef);
874
+ // NOTE: using Subject because we do not care about late-subscribers
875
+ const pointerMissed$ = new Subject();
876
+ const store = signalStore(({ get, set }) => {
877
+ const { invalidate, advance } = loop;
878
+ const position = new THREE.Vector3();
879
+ const defaultTarget = new THREE.Vector3();
880
+ const tempTarget = new THREE.Vector3();
881
+ function getCurrentViewport(camera = get('camera'), target = defaultTarget, size = get('size')) {
882
+ const { width, height, top, left } = size;
883
+ const aspect = width / height;
884
+ if (target instanceof THREE.Vector3)
885
+ tempTarget.copy(target);
886
+ else
887
+ tempTarget.set(...target);
888
+ const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
889
+ if (is.orthographicCamera(camera)) {
890
+ return {
891
+ width: width / camera.zoom,
892
+ height: height / camera.zoom,
893
+ top,
894
+ left,
895
+ factor: 1,
896
+ distance,
897
+ aspect,
898
+ };
899
+ }
900
+ else {
901
+ const fov = (camera.fov * Math.PI) / 180; // convert vertical fov to radians
902
+ const h = 2 * Math.tan(fov / 2) * distance; // visible height
903
+ const w = h * (width / height);
904
+ return { width: w, height: h, top, left, factor: width / w, distance, aspect };
905
+ }
906
+ }
907
+ let performanceTimeout = undefined;
908
+ const setPerformanceCurrent = (current) => set((state) => ({ performance: { ...state.performance, current } }));
909
+ const pointer = new THREE.Vector2();
910
+ return {
911
+ pointerMissed$: pointerMissed$.asObservable(),
912
+ events: { priority: 1, enabled: true, connected: false },
913
+ invalidate: (frames = 1) => invalidate(store, frames),
914
+ advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, store),
915
+ legacy: false,
916
+ linear: false,
917
+ flat: false,
918
+ controls: null,
919
+ clock: new THREE.Clock(),
920
+ pointer,
921
+ frameloop: 'always',
922
+ performance: {
923
+ current: 1,
924
+ min: 0.5,
925
+ max: 1,
926
+ debounce: 200,
927
+ regress: () => {
928
+ const state = get();
929
+ // Clear timeout
930
+ if (performanceTimeout)
931
+ clearTimeout(performanceTimeout);
932
+ // Set lower bound performance
933
+ if (state.performance.current !== state.performance.min)
934
+ setPerformanceCurrent(state.performance.min);
935
+ // Go back to upper bound performance after a while unless something regresses meanwhile
936
+ performanceTimeout = setTimeout(() => {
937
+ setPerformanceCurrent(get('performance', 'max'));
938
+ safeDetectChanges(cdr);
939
+ }, state.performance.debounce);
940
+ },
941
+ },
942
+ size: { width: 0, height: 0, top: 0, left: 0, updateStyle: false },
943
+ viewport: {
944
+ initialDpr: 0,
945
+ dpr: 0,
946
+ width: 0,
947
+ height: 0,
948
+ top: 0,
949
+ left: 0,
950
+ aspect: 0,
951
+ distance: 0,
952
+ factor: 0,
953
+ getCurrentViewport,
954
+ },
955
+ setEvents: (events) => set((state) => ({ ...state, events: { ...state.events, ...events } })),
956
+ setSize: (width, height, top, left) => {
957
+ const camera = get('camera');
958
+ const size = { width, height, top: top || 0, left: left || 0 };
959
+ set((state) => ({
960
+ size,
961
+ viewport: { ...state.viewport, ...getCurrentViewport(camera, defaultTarget, size) },
962
+ }));
963
+ },
964
+ setDpr: (dpr) => set((state) => {
965
+ const resolved = makeDpr(dpr, window);
966
+ return {
967
+ viewport: {
968
+ ...state.viewport,
969
+ dpr: resolved,
970
+ initialDpr: state.viewport.initialDpr || resolved,
971
+ },
972
+ };
973
+ }),
974
+ setFrameloop: (frameloop = 'always') => {
975
+ const clock = get('clock');
976
+ // if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
977
+ clock.stop();
978
+ clock.elapsedTime = 0;
979
+ if (frameloop !== 'never') {
980
+ clock.start();
981
+ clock.elapsedTime = 0;
982
+ }
983
+ set(() => ({ frameloop }));
984
+ },
985
+ previousRoot: parent,
986
+ internal: {
987
+ active: false,
988
+ priority: 0,
989
+ frames: 0,
990
+ lastEvent: new ElementRef(null),
991
+ interaction: [],
992
+ hovered: new Map(),
993
+ subscribers: [],
994
+ initialClick: [0, 0],
995
+ initialHits: [],
996
+ capturedMap: new Map(),
997
+ subscribe: (callback, priority = 0, _store = store) => {
998
+ const internal = get('internal');
999
+ // If this subscription was given a priority, it takes rendering into its own hands
1000
+ // For that reason we switch off automatic rendering and increase the manual flag
1001
+ // As long as this flag is positive there can be no internal rendering at all
1002
+ // because there could be multiple render subscriptions
1003
+ internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1004
+ internal.subscribers.push({ callback, priority, store });
1005
+ // Register subscriber and sort layers from lowest to highest, meaning,
1006
+ // highest priority renders last (on top of the other frames)
1007
+ internal.subscribers = internal.subscribers.sort((a, b) => (a.priority || 0) - (b.priority || 0));
1008
+ return () => {
1009
+ const internal = get('internal');
1010
+ if (internal?.subscribers) {
1011
+ // Decrease manual flag if this subscription had a priority
1012
+ internal.priority = internal.priority - (priority > 0 ? 1 : 0);
1013
+ // Remove subscriber from list
1014
+ internal.subscribers = internal.subscribers.filter((s) => s.callback !== callback);
1015
+ }
1016
+ };
1017
+ },
1018
+ },
1019
+ };
1020
+ });
1021
+ // NOTE: assign pointerMissed$ so we can use it in events
1022
+ Object.defineProperty(store, 'pointerMissed$', { get: () => pointerMissed$ });
1023
+ const state = store.get();
1024
+ let oldSize = state.size;
1025
+ let oldDpr = state.viewport.dpr;
1026
+ let oldCamera = state.camera;
1027
+ const _camera = store.select('camera');
1028
+ const _size = store.select('size');
1029
+ const _viewport = store.select('viewport');
1030
+ effect(() => {
1031
+ const [camera, size, viewport, gl] = [_camera(), _size(), _viewport(), store.get('gl')];
1032
+ // Resize camera and renderer on changes to size and pixelratio
1033
+ if (size !== oldSize || viewport.dpr !== oldDpr) {
1034
+ oldSize = size;
1035
+ oldDpr = viewport.dpr;
1036
+ // Update camera & renderer
1037
+ updateCamera(camera, size);
1038
+ gl.setPixelRatio(viewport.dpr);
1039
+ const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
1040
+ gl.setSize(size.width, size.height, updateStyle);
1041
+ }
1042
+ // Update viewport once the camera changes
1043
+ if (camera !== oldCamera) {
1044
+ oldCamera = camera;
1045
+ updateCamera(camera, size);
1046
+ // Update viewport
1047
+ store.set((state) => ({
1048
+ viewport: { ...state.viewport, ...state.viewport.getCurrentViewport(camera) },
1049
+ }));
1050
+ }
1051
+ });
1052
+ return store;
1053
+ });
1054
+ }
1055
+ const NGT_STORE = new InjectionToken('NgtStore token');
1056
+ const [injectNgtStore, provideNgtStore] = createInjectionToken(storeFactory, {
1057
+ isRoot: false,
1058
+ deps: [NGT_LOOP, DOCUMENT, Injector, [new Optional(), new SkipSelf(), NGT_STORE]],
1059
+ token: NGT_STORE,
1060
+ });
1061
+
1062
+ function injectBeforeRender(cb, { priority = 0, injector } = {}) {
1063
+ injector = assertInjectionContext(injectBeforeRender, injector);
1064
+ return runInInjectionContext(injector, () => {
1065
+ const store = injectNgtStore();
1066
+ const sub = store.get('internal').subscribe(cb, priority, store);
1067
+ inject(DestroyRef).onDestroy(() => void sub());
1068
+ return sub;
1069
+ });
1070
+ }
1071
+
1072
+ /**
1073
+ * Release pointer captures.
1074
+ * This is called by releasePointerCapture in the API, and when an object is removed.
1075
+ */
1076
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
1077
+ const captureData = captures.get(obj);
1078
+ if (captureData) {
1079
+ captures.delete(obj);
1080
+ // If this was the last capturing object for this pointer
1081
+ if (captures.size === 0) {
1082
+ capturedMap.delete(pointerId);
1083
+ captureData.target.releasePointerCapture(pointerId);
1084
+ }
1085
+ }
1086
+ }
1087
+ function removeInteractivity(store, object) {
1088
+ const { internal } = store.get();
1089
+ // Removes every trace of an object from the data store
1090
+ internal.interaction = internal.interaction.filter((o) => o !== object);
1091
+ internal.initialHits = internal.initialHits.filter((o) => o !== object);
1092
+ internal.hovered.forEach((value, key) => {
1093
+ if (value.eventObject === object || value.object === object) {
1094
+ // Clear out intersects, they are outdated by now
1095
+ internal.hovered.delete(key);
1096
+ }
1097
+ });
1098
+ internal.capturedMap.forEach((captures, pointerId) => {
1099
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
1100
+ });
1101
+ }
1102
+ function createEvents(store) {
1103
+ /** Calculates delta */
1104
+ function calculateDistance(event) {
1105
+ const internal = store.get('internal');
1106
+ const dx = event.offsetX - internal.initialClick[0];
1107
+ const dy = event.offsetY - internal.initialClick[1];
1108
+ return Math.round(Math.sqrt(dx * dx + dy * dy));
1109
+ }
1110
+ /** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
1111
+ function filterPointerEvents(objects) {
1112
+ return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
1113
+ const eventName = `pointer${name}`;
1114
+ return getLocalState(obj).handlers?.[eventName];
1115
+ }));
1116
+ }
1117
+ function intersect(event, filter) {
1118
+ const state = store.get();
1119
+ const duplicates = new Set();
1120
+ const intersections = [];
1121
+ // Allow callers to eliminate event objects
1122
+ const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
1123
+ // Reset all raycaster cameras to undefined
1124
+ for (let i = 0; i < eventsObjects.length; i++) {
1125
+ const state = getLocalState(eventsObjects[i]).store.get();
1126
+ if (state) {
1127
+ state.raycaster.camera = undefined;
1128
+ }
1129
+ }
1130
+ if (!state.previousRoot) {
1131
+ // Make sure root-level pointer and ray are set up
1132
+ state.events.compute?.(event, store, null);
1133
+ }
1134
+ function handleRaycast(obj) {
1135
+ const objLocalState = getLocalState(obj);
1136
+ const objStore = objLocalState.store;
1137
+ const objState = objStore?.get();
1138
+ // Skip event handling when noEvents is set, or when the raycasters camera is null
1139
+ if (!objState || !objState.events.enabled || objState.raycaster.camera === null)
1140
+ return [];
1141
+ // When the camera is undefined we have to call the event layers update function
1142
+ if (objState.raycaster.camera === undefined) {
1143
+ objState.events.compute?.(event, objStore, objState.previousRoot);
1144
+ // If the camera is still undefined we have to skip this layer entirely
1145
+ if (objState.raycaster.camera === undefined)
1146
+ objState.raycaster.camera = null;
1147
+ }
1148
+ // Intersect object by object
1149
+ return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
1150
+ }
1151
+ // Collect events
1152
+ let hits = eventsObjects
1153
+ // Intersect objects
381
1154
  .flatMap(handleRaycast)
382
- // sort by event priority
1155
+ // Sort by event priority and distance
383
1156
  .sort((a, b) => {
384
1157
  const aState = getLocalState(a.object).store.get();
385
1158
  const bState = getLocalState(b.object).store.get();
@@ -387,7 +1160,7 @@ function createEvents(store) {
387
1160
  return a.distance - b.distance;
388
1161
  return bState.events.priority - aState.events.priority || a.distance - b.distance;
389
1162
  })
390
- // filter out duplicates
1163
+ // Filter out duplicates
391
1164
  .filter((item) => {
392
1165
  const id = makeId(item);
393
1166
  if (duplicates.has(id))
@@ -395,10 +1168,11 @@ function createEvents(store) {
395
1168
  duplicates.add(id);
396
1169
  return true;
397
1170
  });
398
- // allow custom userland intersect sort order, this likely only makes sense on the root
1171
+ // https://github.com/mrdoob/three.js/issues/16031
1172
+ // Allow custom userland intersect sort order, this likely only makes sense on the root filter
399
1173
  if (state.events.filter)
400
1174
  hits = state.events.filter(hits, store);
401
- // bubble up the events, find the event source
1175
+ // Bubble up the events, find the event source (eventObject)
402
1176
  for (const hit of hits) {
403
1177
  let eventObject = hit.object;
404
1178
  // bubble event up
@@ -409,20 +1183,19 @@ function createEvents(store) {
409
1183
  eventObject = eventObject.parent;
410
1184
  }
411
1185
  }
412
- // if the interaction is captured, make all capturing targets part of the intersects
1186
+ // If the interaction is captured, make all capturing targets part of the intersect.
413
1187
  if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
414
- for (const capturedData of state.internal.capturedMap.get(event.pointerId).values()) {
415
- if (!duplicates.has(makeId(capturedData.intersection))) {
416
- intersections.push(capturedData.intersection);
417
- }
1188
+ for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
1189
+ if (!duplicates.has(makeId(captureData.intersection)))
1190
+ intersections.push(captureData.intersection);
418
1191
  }
419
1192
  }
420
1193
  return intersections;
421
1194
  }
422
- /** handle intersections by forwarding them to handlers */
1195
+ /** Handles intersections by forwarding them to handlers */
423
1196
  function handleIntersects(intersections, event, delta, callback) {
424
1197
  const rootState = store.get();
425
- // if anything has been found, forward it to the event listeners
1198
+ // If anything has been found, forward it to the event listeners
426
1199
  if (intersections.length) {
427
1200
  const localState = { stopped: false };
428
1201
  for (const hit of intersections) {
@@ -433,14 +1206,17 @@ function createEvents(store) {
433
1206
  const setPointerCapture = (id) => {
434
1207
  const captureData = { intersection: hit, target: event.target };
435
1208
  if (internal.capturedMap.has(id)) {
436
- // if the pointerId was previously captured, we add the hit to the event capturedMap
1209
+ // if the pointerId was previously captured, we add the hit to the
1210
+ // event capturedMap.
437
1211
  internal.capturedMap.get(id).set(hit.eventObject, captureData);
438
1212
  }
439
1213
  else {
440
- // if the pointerId was not previously captured, we create a Map containing the hitObject, and the hit. hitObject is used for faster access
1214
+ // if the pointerId was not previously captured, we create a map
1215
+ // containing the hitObject, and the hit. hitObject is used for
1216
+ // faster access.
441
1217
  internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
442
1218
  }
443
- // call the original event now
1219
+ // Call the original event now
444
1220
  event.target.setPointerCapture(id);
445
1221
  };
446
1222
  const releasePointerCapture = (id) => {
@@ -449,15 +1225,15 @@ function createEvents(store) {
449
1225
  releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
450
1226
  }
451
1227
  };
452
- // add native event props
1228
+ // Add native event props
453
1229
  const extractEventProps = {};
454
1230
  // This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
455
- for (const prop in event) {
456
- const property = event[prop];
457
- // only copy over atomics, leave functions alone as these should be called as event.nativeEvent.fn()
458
- if (typeof property !== 'function') {
1231
+ for (let prop in event) {
1232
+ let property = event[prop];
1233
+ // Only copy over atomics, leave functions alone as these should be
1234
+ // called as event.nativeEvent.fn()
1235
+ if (typeof property !== 'function')
459
1236
  extractEventProps[prop] = property;
460
- }
461
1237
  }
462
1238
  const raycastEvent = {
463
1239
  ...hit,
@@ -468,7 +1244,7 @@ function createEvents(store) {
468
1244
  delta,
469
1245
  unprojectedPoint,
470
1246
  ray: raycaster.ray,
471
- camera: camera,
1247
+ camera,
472
1248
  // Hijack stopPropagation, which just sets a flag
473
1249
  stopPropagation() {
474
1250
  // https://github.com/pmndrs/react-three-fiber/issues/596
@@ -496,9 +1272,9 @@ function createEvents(store) {
496
1272
  currentTarget: { hasPointerCapture, setPointerCapture, releasePointerCapture },
497
1273
  nativeEvent: event,
498
1274
  };
499
- // call subscribers
1275
+ // Call subscribers
500
1276
  callback(raycastEvent);
501
- // event bubbling may be interupted by stopPropagation
1277
+ // Event bubbling may be interrupted by stopPropagation
502
1278
  if (localState.stopped === true)
503
1279
  break;
504
1280
  }
@@ -506,9 +1282,9 @@ function createEvents(store) {
506
1282
  return intersections;
507
1283
  }
508
1284
  function cancelPointer(intersections) {
509
- const { internal } = store.get();
1285
+ const internal = store.get('internal');
510
1286
  for (const hoveredObj of internal.hovered.values()) {
511
- // When no objects were hit or the hovered object wasn't found underneath the cursor
1287
+ // When no objects were hit or the the hovered object wasn't found underneath the cursor
512
1288
  // we call onPointerOut and delete the object from the hovered-elements map
513
1289
  if (!intersections.length ||
514
1290
  !intersections.find((hit) => hit.object === hoveredObj.object &&
@@ -552,14 +1328,14 @@ function createEvents(store) {
552
1328
  }
553
1329
  // Any other pointer goes here ...
554
1330
  return function handleEvent(event) {
555
- const { onPointerMissed, internal } = store.get();
1331
+ const pointerMissed$ = store['pointerMissed$'];
1332
+ const internal = store.get('internal');
556
1333
  // prepareRay(event)
557
1334
  internal.lastEvent.nativeElement = event;
558
1335
  // Get fresh intersects
559
1336
  const isPointerMove = name === 'pointermove';
560
1337
  const isClickEvent = name === 'click' || name === 'contextmenu' || name === 'dblclick';
561
1338
  const filter = isPointerMove ? filterPointerEvents : undefined;
562
- // const hits = patchIntersects(intersect(filter), event)
563
1339
  const hits = intersect(event, filter);
564
1340
  const delta = isClickEvent ? calculateDistance(event) : 0;
565
1341
  // Save initial coordinates on pointer-down
@@ -572,8 +1348,7 @@ function createEvents(store) {
572
1348
  if (isClickEvent && !hits.length) {
573
1349
  if (delta <= 2) {
574
1350
  pointerMissed(event, internal.interaction);
575
- if (onPointerMissed)
576
- onPointerMissed(event);
1351
+ pointerMissed$.next(event);
577
1352
  }
578
1353
  }
579
1354
  // Take care of unhover
@@ -586,6 +1361,20 @@ function createEvents(store) {
586
1361
  // Check presence of handlers
587
1362
  if (!instance?.eventCount)
588
1363
  return;
1364
+ /*
1365
+ MAYBE TODO, DELETE IF NOT:
1366
+ Check if the object is captured, captured events should not have intersects running in parallel
1367
+ But wouldn't it be better to just replace capturedMap with a single entry?
1368
+ Also, are we OK with straight up making picking up multiple objects impossible?
1369
+
1370
+ const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
1371
+ if (pointerId !== undefined) {
1372
+ const capturedMeshSet = internal.capturedMap.get(pointerId)
1373
+ if (capturedMeshSet) {
1374
+ const captured = capturedMeshSet.get(eventObject)
1375
+ if (captured && captured.localState.stopped) return
1376
+ }
1377
+ }*/
589
1378
  if (isPointerMove) {
590
1379
  // Move event ...
591
1380
  if (handlers?.pointerover ||
@@ -680,6 +1469,11 @@ function createPointerEvents(store) {
680
1469
  handlers[supportedEventName] = handlePointer(supportedEventName);
681
1470
  return handlers;
682
1471
  }, {}),
1472
+ update: () => {
1473
+ const { events, internal } = store.get();
1474
+ if (internal.lastEvent?.nativeElement && events.handlers)
1475
+ events.handlers.pointermove(internal.lastEvent.nativeElement);
1476
+ },
683
1477
  connect: (target) => {
684
1478
  const state = store.get();
685
1479
  state.events.disconnect?.();
@@ -703,95 +1497,45 @@ function createPointerEvents(store) {
703
1497
  };
704
1498
  }
705
1499
 
706
- function assertInjectionContext(fn, injector) {
707
- try {
708
- if (!injector) {
709
- return inject(Injector);
710
- }
711
- return injector;
712
- }
713
- catch {
714
- !injector && assertInInjectionContext(fn);
715
- return null;
716
- }
717
- }
718
-
719
- function safeDetectChanges(cdr) {
720
- if (!cdr)
721
- return;
722
- try {
723
- // dynamic created component with ViewContainerRef#createComponent does not have Context
724
- // but it has _attachedToViewContainer
725
- if (cdr['_attachedToViewContainer'] || !!cdr['context']) {
726
- cdr.detectChanges();
727
- }
728
- }
729
- catch (e) {
730
- cdr.markForCheck();
731
- }
732
- }
733
-
734
- function requestAnimationInInjectionContext(cb, injector) {
735
- injector = assertInjectionContext(requestAnimationInInjectionContext, injector);
736
- return requestAnimationFrame(() => {
737
- return runInInjectionContext(injector, cb);
738
- });
739
- }
740
- function queueMicrotaskInInjectionContext(cb, injector) {
741
- injector = assertInjectionContext(requestAnimationInInjectionContext, injector);
742
- return queueMicrotask(() => {
743
- return runInInjectionContext(injector, cb);
744
- });
745
- }
746
- function queueMacrotaskInInjectionContext(cb, injector) {
747
- injector = assertInjectionContext(requestAnimationInInjectionContext, injector);
748
- return setTimeout(() => {
749
- return runInInjectionContext(injector, cb);
750
- });
751
- }
752
-
753
1500
  const cached = new Map();
1501
+ function normalizeInputs(input) {
1502
+ if (Array.isArray(input))
1503
+ return input;
1504
+ if (typeof input === 'string')
1505
+ return [input];
1506
+ return Object.values(input);
1507
+ }
754
1508
  function load(loaderConstructorFactory, inputs, { extensions, onProgress, } = {}) {
755
- const computedUrls = computed(() => {
756
- const input = inputs();
757
- if (Array.isArray(input))
758
- return input;
759
- if (typeof input === 'string')
760
- return [input];
761
- return Object.values(input);
762
- });
763
1509
  return () => {
764
- const urls = computedUrls();
765
- const loaderConstructor = loaderConstructorFactory(urls);
766
- const loader = new loaderConstructor();
1510
+ const urls = normalizeInputs(inputs());
1511
+ const loader = new (loaderConstructorFactory(urls))();
767
1512
  if (extensions)
768
1513
  extensions(loader);
769
- return urls.map((url) => new Promise((resolve, reject) => {
770
- if (cached.has(url)) {
771
- resolve(cached.get(url));
772
- }
773
- else {
774
- loader.load(url, (data) => {
775
- if ('scene' in data)
776
- Object.assign(data, makeObjectGraph(data['scene']));
777
- cached.set(url, data);
778
- resolve(data);
779
- }, onProgress, (error) => reject(new Error(`[NGT] Could not load ${url}: ${error}`)));
1514
+ // TODO: reevaluate this
1515
+ return urls.map((url) => {
1516
+ if (!cached.has(url)) {
1517
+ cached.set(url, new Promise((resolve, reject) => {
1518
+ loader.load(url, (data) => {
1519
+ if ('scene' in data)
1520
+ Object.assign(data, makeObjectGraph(data['scene']));
1521
+ resolve(data);
1522
+ }, onProgress, (error) => reject(new Error(`[NGT] Could not load ${url}: ${error}`)));
1523
+ }));
780
1524
  }
781
- }));
1525
+ return cached.get(url);
1526
+ });
782
1527
  };
783
1528
  }
784
1529
  function injectNgtLoader(loaderConstructorFactory, inputs, { extensions, onProgress, injector, } = {}) {
785
1530
  injector = assertInjectionContext(injectNgtLoader, injector);
1531
+ const response = signal(null);
786
1532
  return runInInjectionContext(injector, () => {
787
1533
  const cdr = inject(ChangeDetectorRef);
788
- const response = signal(null);
789
1534
  const effector = load(loaderConstructorFactory, inputs, { extensions, onProgress });
790
- requestAnimationInInjectionContext(() => {
791
- effect(() => {
792
- const originalUrls = untracked(inputs);
793
- Promise.all(effector())
794
- .then((results) => {
1535
+ effect(() => {
1536
+ const originalUrls = inputs();
1537
+ Promise.all(effector()).then((results) => {
1538
+ response.update(() => {
795
1539
  if (Array.isArray(originalUrls))
796
1540
  return results;
797
1541
  if (typeof originalUrls === 'string')
@@ -801,11 +1545,8 @@ function injectNgtLoader(loaderConstructorFactory, inputs, { extensions, onProgr
801
1545
  result[key] = results[keys.indexOf(key)];
802
1546
  return result;
803
1547
  }, {});
804
- })
805
- .then((value) => {
806
- response.set(value);
807
- safeDetectChanges(cdr);
808
1548
  });
1549
+ safeDetectChanges(cdr);
809
1550
  });
810
1551
  });
811
1552
  return response.asReadonly();
@@ -813,674 +1554,138 @@ function injectNgtLoader(loaderConstructorFactory, inputs, { extensions, onProgr
813
1554
  }
814
1555
  injectNgtLoader['preload'] = (loaderConstructorFactory, inputs, extensions) => {
815
1556
  Promise.all(load(loaderConstructorFactory, inputs, { extensions })());
816
- };
817
- injectNgtLoader['destroy'] = () => {
818
- cached.clear();
819
- };
820
-
821
- function createSubs(callback, subs) {
822
- const sub = { callback };
823
- subs.add(sub);
824
- return () => void subs.delete(sub);
825
- }
826
- const globalEffects = new Set();
827
- const globalAfterEffects = new Set();
828
- const globalTailEffects = new Set();
829
- /**
830
- * Adds a global render callback which is called each frame.
831
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addEffect
832
- */
833
- const addEffect = (callback) => createSubs(callback, globalEffects);
834
- /**
835
- * Adds a global after-render callback which is called each frame.
836
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addAfterEffect
837
- */
838
- const addAfterEffect = (callback) => createSubs(callback, globalAfterEffects);
839
- /**
840
- * Adds a global callback which is called when rendering stops.
841
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#addTail
842
- */
843
- const addTail = (callback) => createSubs(callback, globalTailEffects);
844
- function run(effects, timestamp) {
845
- if (!effects.size)
846
- return;
847
- for (const { callback } of effects.values()) {
848
- callback(timestamp);
849
- }
850
- }
851
- function flushGlobalEffects(type, timestamp) {
852
- switch (type) {
853
- case 'before':
854
- return run(globalEffects, timestamp);
855
- case 'after':
856
- return run(globalAfterEffects, timestamp);
857
- case 'tail':
858
- return run(globalTailEffects, timestamp);
859
- }
860
- }
861
- function render(timestamp, store, frame) {
862
- const state = store.get();
863
- // Run local effects
864
- let delta = state.clock.getDelta();
865
- // In frameloop='never' mode, clock times are updated using the provided timestamp
866
- if (state.frameloop === 'never' && typeof timestamp === 'number') {
867
- delta = timestamp - state.clock.elapsedTime;
868
- state.clock.oldTime = state.clock.elapsedTime;
869
- state.clock.elapsedTime = timestamp;
870
- }
871
- // Call subscribers (useFrame)
872
- const subscribers = state.internal.subscribers;
873
- for (let i = 0; i < subscribers.length; i++) {
874
- const subscription = subscribers[i];
875
- subscription.callback({ ...subscription.store.get(), delta, frame });
876
- }
877
- // Render content
878
- if (!state.internal.priority && state.gl.render)
879
- state.gl.render(state.scene, state.camera);
880
- // Decrease frame count
881
- state.internal.frames = Math.max(0, state.internal.frames - 1);
882
- return state.frameloop === 'always' ? 1 : state.internal.frames;
883
- }
884
- function createLoop(roots) {
885
- let running = false;
886
- let repeat;
887
- let frame;
888
- function loop(timestamp) {
889
- frame = requestAnimationFrame(loop);
890
- running = true;
891
- repeat = 0;
892
- // Run effects
893
- flushGlobalEffects('before', timestamp);
894
- // Render all roots
895
- for (const root of roots.values()) {
896
- const state = root.get();
897
- // If the frameloop is invalidated, do not run another frame
898
- if (state.internal.active &&
899
- (state.frameloop === 'always' || state.internal.frames > 0) &&
900
- !state.gl.xr?.isPresenting) {
901
- repeat += render(timestamp, root);
902
- }
903
- }
904
- // Run after-effects
905
- flushGlobalEffects('after', timestamp);
906
- // Stop the loop if nothing invalidates it
907
- if (repeat === 0) {
908
- // Tail call effects, they are called when rendering stops
909
- flushGlobalEffects('tail', timestamp);
910
- // Flag end of operation
911
- running = false;
912
- return cancelAnimationFrame(frame);
913
- }
914
- }
915
- function invalidate(store, frames = 1) {
916
- const state = store?.get();
917
- if (!state)
918
- return roots.forEach((root) => invalidate(root, frames));
919
- if (state.gl.xr?.isPresenting || !state.internal.active || state.frameloop === 'never')
920
- return;
921
- // Increase frames, do not go higher than 60
922
- state.internal.frames = Math.min(60, state.internal.frames + frames);
923
- // If the render-loop isn't active, start it
924
- if (!running) {
925
- running = true;
926
- requestAnimationFrame(loop);
927
- }
928
- }
929
- function advance(timestamp, runGlobalEffects = true, store, frame) {
930
- const state = store?.get();
931
- if (runGlobalEffects)
932
- flushGlobalEffects('before', timestamp);
933
- if (!state)
934
- for (const root of roots.values())
935
- render(timestamp, root);
936
- else
937
- render(timestamp, store, frame);
938
- if (runGlobalEffects)
939
- flushGlobalEffects('after', timestamp);
940
- }
941
- return {
942
- loop,
943
- /**
944
- * Invalidates the view, requesting a frame to be rendered. Will globally invalidate unless passed a root's state.
945
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#invalidate
946
- */
947
- invalidate,
948
- /**
949
- * Advances the frameloop and runs render effects, useful for when manually rendering via `frameloop="never"`.
950
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#advance
951
- */
952
- advance,
953
- };
954
- }
955
-
956
- function diffProps(instance, props) {
957
- const propsEntries = Object.entries(props);
958
- const changes = [];
959
- for (const [propKey, propValue] of propsEntries) {
960
- if (is.equ(propValue, instance[propKey]))
961
- continue;
962
- changes.push([propKey, propValue]);
963
- }
964
- return changes;
965
- }
966
- function applyProps(instance, props) {
967
- // if props is empty
968
- if (!Object.keys(props).length)
969
- return instance;
970
- // filter equals, events , and reserved props
971
- const localState = getLocalState(instance);
972
- const rootState = localState.store?.get();
973
- const changes = diffProps(instance, props);
974
- for (let i = 0; i < changes.length; i++) {
975
- let key = changes[i][0];
976
- const currentInstance = instance;
977
- const targetProp = currentInstance[key];
978
- let value = changes[i][1];
979
- if (is.colorSpaceExist(currentInstance)) {
980
- const sRGBEncoding = 3001;
981
- const SRGBColorSpace = 'srgb';
982
- const LinearSRGBColorSpace = 'srgb-linear';
983
- if (key === 'encoding') {
984
- key = 'colorSpace';
985
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
986
- }
987
- else if (key === 'outputEncoding') {
988
- key = 'outputColorSpace';
989
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
990
- }
991
- }
992
- // special treatmen for objects with support for set/copy, and layers
993
- if (targetProp && targetProp['set'] && (targetProp['copy'] || targetProp instanceof THREE.Layers)) {
994
- const isColor = targetProp instanceof THREE.Color;
995
- // if value is an array
996
- if (Array.isArray(value)) {
997
- if (targetProp['fromArray'])
998
- targetProp['fromArray'](value);
999
- else
1000
- targetProp['set'](...value);
1001
- }
1002
- // test again target.copy
1003
- else if (targetProp['copy'] &&
1004
- value &&
1005
- value.constructor &&
1006
- targetProp.constructor.name === value.constructor.name) {
1007
- targetProp['copy'](value);
1008
- if (!THREE.ColorManagement && !rootState.linear && isColor)
1009
- targetProp['convertSRGBToLinear']();
1010
- }
1011
- // if nothing else fits, just set the single value, ignore undefined
1012
- else if (value !== undefined) {
1013
- const isColor = targetProp instanceof THREE.Color;
1014
- // allow setting array scalars
1015
- if (!isColor && targetProp['setScalar'])
1016
- targetProp['setScalar'](value);
1017
- // layers have no copy function, copy the mask
1018
- else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers)
1019
- targetProp.mask = value.mask;
1020
- // otherwise just set ...
1021
- else
1022
- targetProp['set'](value);
1023
- // auto-convert srgb
1024
- if (!THREE.ColorManagement && !rootState?.linear && isColor)
1025
- targetProp.convertSRGBToLinear();
1557
+ };
1558
+ injectNgtLoader['destroy'] = () => {
1559
+ cached.clear();
1560
+ };
1561
+
1562
+ const catalogue = {};
1563
+ function extend(objects) {
1564
+ Object.assign(catalogue, objects);
1565
+ }
1566
+ const [injectNgtCatalogue] = createInjectionToken(() => catalogue);
1567
+
1568
+ const ROUTED_SCENE = '__ngt_renderer_is_routed_scene__';
1569
+ const HTML = '__ngt_renderer_is_html';
1570
+ const SPECIAL_INTERNAL_ADD_COMMENT = '__ngt_renderer_add_comment__';
1571
+ const SPECIAL_DOM_TAG = {
1572
+ NGT_PORTAL: 'ngt-portal',
1573
+ NGT_PRIMITIVE: 'ngt-primitive',
1574
+ NGT_VALUE: 'ngt-value',
1575
+ };
1576
+ const SPECIAL_PROPERTIES = {
1577
+ COMPOUND: 'ngtCompound',
1578
+ RENDER_PRIORITY: 'priority',
1579
+ ATTACH: 'attach',
1580
+ VALUE: 'rawValue',
1581
+ REF: 'ref',
1582
+ };
1583
+ const SPECIAL_EVENTS = {
1584
+ BEFORE_RENDER: 'beforeRender',
1585
+ AFTER_UPDATE: 'afterUpdate',
1586
+ AFTER_ATTACH: 'afterAttach',
1587
+ };
1588
+
1589
+ class NgtCommonDirective {
1590
+ static { this.processComment = true; }
1591
+ constructor() {
1592
+ this.vcr = inject(ViewContainerRef);
1593
+ this.zone = inject(NgZone);
1594
+ this.template = inject(TemplateRef);
1595
+ this.injected = false;
1596
+ this.shouldCreateView = true;
1597
+ if (NgtCommonDirective.processComment) {
1598
+ const commentNode = this.vcr.element.nativeElement;
1599
+ if (commentNode[SPECIAL_INTERNAL_ADD_COMMENT]) {
1600
+ commentNode[SPECIAL_INTERNAL_ADD_COMMENT]();
1601
+ delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
1026
1602
  }
1027
- localState?.nativeProps?.set({ [key]: targetProp });
1028
1603
  }
1029
- // else just overwrite the value
1030
- else {
1031
- currentInstance[key] = value;
1032
- // auto-convert srgb textures
1033
- if (currentInstance[key] instanceof THREE.Texture &&
1034
- currentInstance[key].format === THREE.RGBAFormat &&
1035
- currentInstance[key].type === THREE.UnsignedByteType) {
1036
- const texture = currentInstance[key];
1037
- if (rootState?.gl) {
1038
- if (is.colorSpaceExist(texture) && is.colorSpaceExist(rootState.gl))
1039
- texture.colorSpace = rootState.gl.outputColorSpace;
1040
- else
1041
- texture.encoding = rootState.gl.outputEncoding;
1604
+ inject(DestroyRef).onDestroy(() => {
1605
+ this.view?.destroy();
1606
+ });
1607
+ }
1608
+ createView() {
1609
+ this.zone.runOutsideAngular(() => {
1610
+ if (this.shouldCreateView) {
1611
+ if (this.view && !this.view.destroyed) {
1612
+ this.view.destroy();
1042
1613
  }
1614
+ this.view = this.vcr.createEmbeddedView(this.template);
1615
+ safeDetectChanges(this.view);
1043
1616
  }
1044
- localState?.nativeProps?.set({ [key]: value });
1045
- }
1046
- checkUpdate(targetProp);
1047
- invalidateInstance(instance);
1048
- }
1049
- const instanceHandlers = localState.eventCount;
1050
- const parent = localState.parent ? untracked(localState.parent) : null;
1051
- if (parent && rootState.internal && instance['raycast'] && instanceHandlers !== localState.eventCount) {
1052
- // Pre-emptively remove the instance from the interaction manager
1053
- const index = rootState.internal.interaction.indexOf(instance);
1054
- if (index > -1)
1055
- rootState.internal.interaction.splice(index, 1);
1056
- // Add the instance to the interaction manager only when it has handlers
1057
- if (localState.eventCount)
1058
- rootState.internal.interaction.push(instance);
1059
- }
1060
- if (parent && localState.afterUpdate && localState.afterUpdate.observed && changes.length) {
1061
- localState.afterUpdate.emit(instance);
1617
+ });
1062
1618
  }
1063
- return instance;
1619
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtCommonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1620
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.2", type: NgtCommonDirective, ngImport: i0 }); }
1064
1621
  }
1622
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtCommonDirective, decorators: [{
1623
+ type: Directive
1624
+ }], ctorParameters: function () { return []; } });
1065
1625
 
1066
- const rootStateMap = new Map();
1067
- const { invalidate, advance } = createLoop(rootStateMap);
1068
- const shallowLoose = { objects: 'shallow', strict: false };
1069
- class NgtStore extends NgtSignalStore {
1626
+ class NgtArgs extends NgtCommonDirective {
1070
1627
  constructor() {
1071
1628
  super(...arguments);
1072
- this.#parentStore = inject(NgtStore, { optional: true, skipSelf: true });
1073
- this.#window = inject(DOCUMENT).defaultView;
1074
- this.#injector = inject(Injector);
1075
- this.isInit = false;
1076
- }
1077
- #parentStore;
1078
- #window;
1079
- #injector;
1080
- init() {
1081
- if (!this.isInit) {
1082
- const position = new THREE.Vector3();
1083
- const defaultTarget = new THREE.Vector3();
1084
- const tempTarget = new THREE.Vector3();
1085
- const getCurrentViewport = (camera = this.get('camera'), target = defaultTarget, size = this.get('size')) => {
1086
- const { width, height, top, left } = size;
1087
- const aspect = width / height;
1088
- if (target instanceof THREE.Vector3)
1089
- tempTarget.copy(target);
1090
- else
1091
- tempTarget.set(...target);
1092
- const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1093
- if (is.orthographicCamera(camera)) {
1094
- return {
1095
- width: width / camera.zoom,
1096
- height: height / camera.zoom,
1097
- top,
1098
- left,
1099
- factor: 1,
1100
- distance,
1101
- aspect,
1102
- };
1103
- }
1104
- const fov = (camera.fov * Math.PI) / 180; // convert vertical fov to radians
1105
- const h = 2 * Math.tan(fov / 2) * distance; // visible height
1106
- const w = h * aspect;
1107
- return { width: w, height: h, top, left, factor: width / w, distance, aspect };
1108
- };
1109
- let performanceTimeout;
1110
- const setPerformanceCurrent = (current) => {
1111
- this.set((state) => ({ performance: { ...state.performance, current } }));
1112
- };
1113
- this.set({
1114
- get: this.get.bind(this),
1115
- set: this.set.bind(this),
1116
- ready: false,
1117
- events: { priority: 1, enabled: true, connected: false },
1118
- invalidate: (frames = 1) => invalidate(this, frames),
1119
- advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, this),
1120
- legacy: false,
1121
- linear: false,
1122
- flat: false,
1123
- controls: null,
1124
- clock: new THREE.Clock(),
1125
- pointer: new THREE.Vector2(),
1126
- frameloop: 'always',
1127
- performance: {
1128
- current: 1,
1129
- min: 0.5,
1130
- max: 1,
1131
- debounce: 200,
1132
- regress: () => {
1133
- const state = this.get();
1134
- // clear timeout
1135
- if (performanceTimeout)
1136
- clearTimeout(performanceTimeout);
1137
- // set lower bound
1138
- if (state.performance.current !== state.performance.min)
1139
- setPerformanceCurrent(state.performance.min);
1140
- // go back to upper bound
1141
- performanceTimeout = setTimeout(() => {
1142
- setPerformanceCurrent(this.get('performance', 'max') || 1);
1143
- }, state.performance.debounce);
1144
- },
1145
- },
1146
- size: { width: 0, height: 0, top: 0, left: 0 },
1147
- viewport: {
1148
- initialDpr: 0,
1149
- dpr: 0,
1150
- width: 0,
1151
- height: 0,
1152
- top: 0,
1153
- left: 0,
1154
- aspect: 0,
1155
- distance: 0,
1156
- factor: 0,
1157
- getCurrentViewport,
1158
- },
1159
- previousStore: this.#parentStore,
1160
- internal: {
1161
- active: false,
1162
- priority: 0,
1163
- frames: 0,
1164
- lastEvent: new ElementRef(null),
1165
- interaction: [],
1166
- hovered: new Map(),
1167
- subscribers: [],
1168
- initialClick: [0, 0],
1169
- initialHits: [],
1170
- capturedMap: new Map(),
1171
- subscribe: (callback, priority = 0, store = this) => {
1172
- const internal = this.get('internal');
1173
- // If this subscription was given a priority, it takes rendering into its own hands
1174
- // For that reason we switch off automatic rendering and increase the manual flag
1175
- // As long as this flag is positive there can be no internal rendering at all
1176
- // because there could be multiple render subscriptions
1177
- internal.priority = internal.priority + (priority > 0 ? 1 : 0);
1178
- internal.subscribers.push({ priority, store, callback });
1179
- // Register subscriber and sort layers from lowest to highest, meaning,
1180
- // highest priority renders last (on top of the other frames)
1181
- internal.subscribers.sort((a, b) => (a.priority || 0) - (b.priority || 0));
1182
- return () => {
1183
- const internal = this.get('internal');
1184
- if (internal?.subscribers) {
1185
- // Decrease manual flag if this subscription had a priority
1186
- internal.priority = internal.priority - (priority > 0 ? 1 : 0);
1187
- // Remove subscriber from list
1188
- internal.subscribers = internal.subscribers.filter((s) => s.callback !== callback);
1189
- }
1190
- };
1191
- },
1192
- },
1193
- setEvents: (events) => {
1194
- this.set((state) => ({ events: { ...state.events, ...events } }));
1195
- },
1196
- setSize: (width, height, top, left) => {
1197
- const camera = this.get('camera');
1198
- const size = { width, height, top: top || 0, left: left || 0 };
1199
- this.set((state) => ({
1200
- size,
1201
- viewport: { ...state.viewport, ...getCurrentViewport(camera, defaultTarget, size) },
1202
- }));
1203
- },
1204
- setDpr: (dpr) => {
1205
- const resolved = makeDpr(dpr, this.#window);
1206
- this.set((state) => ({
1207
- viewport: {
1208
- ...state.viewport,
1209
- dpr: resolved,
1210
- initialDpr: state.viewport.initialDpr || resolved,
1211
- },
1212
- }));
1213
- },
1214
- setFrameloop: (frameloop = 'always') => {
1215
- const clock = this.get('clock');
1216
- clock.stop();
1217
- clock.elapsedTime = 0;
1218
- if (frameloop !== 'never') {
1219
- clock.start();
1220
- clock.elapsedTime = 0;
1221
- }
1222
- this.set({ frameloop });
1223
- },
1224
- });
1225
- this.isInit = true;
1226
- this.#resize();
1227
- }
1228
- }
1229
- configure(inputs, canvasElement) {
1230
- const { gl: glOptions, size: sizeOptions, camera: cameraOptions, raycaster: raycasterOptions, scene: sceneOptions, events, orthographic, lookAt, shadows, linear, legacy, flat, dpr, frameloop, performance, } = inputs;
1231
- const state = this.get();
1232
- const stateToUpdate = {};
1233
- // setup renderer
1234
- let gl = state.gl;
1235
- if (!state.gl)
1236
- stateToUpdate.gl = gl = makeDefaultRenderer(glOptions, canvasElement);
1237
- // setup raycaster
1238
- let raycaster = state.raycaster;
1239
- if (!raycaster)
1240
- stateToUpdate.raycaster = raycaster = new THREE.Raycaster();
1241
- // set raycaster options
1242
- const { params, ...options } = raycasterOptions || {};
1243
- if (!is.equ(options, raycaster, shallowLoose))
1244
- applyProps(raycaster, { ...options });
1245
- if (!is.equ(params, raycaster.params, shallowLoose)) {
1246
- applyProps(raycaster, { params: { ...raycaster.params, ...(params || {}) } });
1247
- }
1248
- // create default camera
1249
- if (!state.camera) {
1250
- const isCamera = is.camera(cameraOptions);
1251
- let camera = isCamera ? cameraOptions : makeDefaultCamera(orthographic || false, state.size);
1252
- if (!isCamera) {
1253
- if (cameraOptions)
1254
- applyProps(camera, cameraOptions);
1255
- // set position.z
1256
- if (!cameraOptions?.position)
1257
- camera.position.z = 5;
1258
- // always look at center or passed-in lookAt by default
1259
- if (!cameraOptions?.rotation && !cameraOptions?.quaternion) {
1260
- if (Array.isArray(lookAt))
1261
- camera.lookAt(lookAt[0], lookAt[1], lookAt[2]);
1262
- else if (lookAt instanceof THREE.Vector3)
1263
- camera.lookAt(lookAt);
1264
- else
1265
- camera.lookAt(0, 0, 0);
1266
- }
1267
- // update projection matrix after applyprops
1268
- camera.updateProjectionMatrix?.();
1269
- }
1270
- if (!is.instance(camera))
1271
- camera = prepare(camera, { store: this });
1272
- stateToUpdate.camera = camera;
1273
- }
1274
- // Set up scene (one time only!)
1275
- if (!state.scene) {
1276
- let scene;
1277
- if (sceneOptions instanceof THREE.Scene) {
1278
- scene = prepare(sceneOptions, { store: this });
1279
- }
1280
- else {
1281
- scene = prepare(new THREE.Scene(), { store: this });
1282
- if (sceneOptions)
1283
- applyProps(scene, sceneOptions);
1284
- }
1285
- stateToUpdate.scene = scene;
1286
- }
1287
- // Set up XR (one time only!)
1288
- if (!state.xr) {
1289
- // Handle frame behavior in WebXR
1290
- const handleXRFrame = (timestamp, frame) => {
1291
- const state = this.get();
1292
- if (state.frameloop === 'never')
1293
- return;
1294
- advance(timestamp, true, this, frame);
1295
- };
1296
- // Toggle render switching on session
1297
- const handleSessionChange = () => {
1298
- const state = this.get();
1299
- state.gl.xr.enabled = state.gl.xr.isPresenting;
1300
- state.gl.xr.setAnimationLoop(state.gl.xr.isPresenting ? handleXRFrame : null);
1301
- if (!state.gl.xr.isPresenting)
1302
- state.invalidate();
1303
- };
1304
- // WebXR session manager
1305
- const xr = {
1306
- connect: () => {
1307
- gl.xr.addEventListener('sessionstart', handleSessionChange);
1308
- gl.xr.addEventListener('sessionend', handleSessionChange);
1309
- },
1310
- disconnect: () => {
1311
- gl.xr.removeEventListener('sessionstart', handleSessionChange);
1312
- gl.xr.removeEventListener('sessionend', handleSessionChange);
1313
- },
1314
- };
1315
- // Subscribe to WebXR session events
1316
- if (gl.xr)
1317
- xr.connect();
1318
- stateToUpdate.xr = xr;
1319
- }
1320
- // Set shadowmap
1321
- if (gl.shadowMap) {
1322
- const oldEnabled = gl.shadowMap.enabled;
1323
- const oldType = gl.shadowMap.type;
1324
- gl.shadowMap.enabled = !!shadows;
1325
- if (typeof shadows === 'boolean') {
1326
- gl.shadowMap.type = THREE.PCFSoftShadowMap;
1327
- }
1328
- else if (typeof shadows === 'string') {
1329
- const types = {
1330
- basic: THREE.BasicShadowMap,
1331
- percentage: THREE.PCFShadowMap,
1332
- soft: THREE.PCFSoftShadowMap,
1333
- variance: THREE.VSMShadowMap,
1334
- };
1335
- gl.shadowMap.type = types[shadows] ?? THREE.PCFSoftShadowMap;
1336
- }
1337
- else if (is.obj(shadows)) {
1338
- Object.assign(gl.shadowMap, shadows);
1339
- }
1340
- if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type)
1341
- checkNeedsUpdate(gl.shadowMap);
1342
- }
1343
- // Safely set color management if available.
1344
- // Avoid accessing THREE.ColorManagement to play nice with older versions
1345
- if (THREE.ColorManagement) {
1346
- const ColorManagement = THREE.ColorManagement;
1347
- if ('enabled' in ColorManagement)
1348
- ColorManagement['enabled'] = !legacy ?? false;
1349
- else if ('legacyMode' in ColorManagement)
1350
- ColorManagement['legacyMode'] = legacy ?? true;
1351
- }
1352
- // set color space and tonemapping preferences
1353
- const LinearEncoding = 3000;
1354
- const sRGBEncoding = 3001;
1355
- applyProps(gl, {
1356
- outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1357
- toneMapping: flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping,
1358
- });
1359
- // Update color management state
1360
- if (state.legacy !== legacy)
1361
- stateToUpdate.legacy = legacy;
1362
- if (state.linear !== linear)
1363
- stateToUpdate.linear = linear;
1364
- if (state.flat !== flat)
1365
- stateToUpdate.flat = flat;
1366
- // Set gl props
1367
- gl.setClearAlpha(0);
1368
- gl.setPixelRatio(makeDpr(state.viewport.dpr));
1369
- gl.setSize(state.size.width, state.size.height);
1370
- if (is.obj(glOptions) &&
1371
- !(typeof glOptions === 'function') &&
1372
- !is.renderer(glOptions) &&
1373
- !is.equ(glOptions, gl, shallowLoose)) {
1374
- applyProps(gl, glOptions);
1375
- }
1376
- // Store events internally
1377
- if (events && !state.events.handlers)
1378
- stateToUpdate.events = events(this);
1379
- // Check performance
1380
- if (performance && !is.equ(performance, state.performance, shallowLoose)) {
1381
- stateToUpdate.performance = { ...state.performance, ...performance };
1382
- }
1383
- this.set(stateToUpdate);
1384
- // Check pixelratio
1385
- if (dpr && state.viewport.dpr !== makeDpr(dpr))
1386
- state.setDpr(dpr);
1387
- // Check size, allow it to take on container bounds initially
1388
- const size = computeInitialSize(canvasElement, sizeOptions);
1389
- if (!is.equ(size, state.size, shallowLoose))
1390
- state.setSize(size.width, size.height, size.top, size.left);
1391
- // Check frameloop
1392
- if (state.frameloop !== frameloop)
1393
- state.setFrameloop(frameloop);
1394
- if (!this.get('ready'))
1395
- this.set({ ready: true });
1396
- this.#invalidate();
1397
- }
1398
- destroy(canvas) {
1399
- this.set((state) => ({ internal: { ...state.internal, active: false } }));
1400
- setTimeout(() => {
1401
- const { gl, xr, events } = this.get();
1402
- if (gl) {
1403
- if (events.disconnect) {
1404
- events.disconnect();
1405
- }
1406
- gl.renderLists.dispose();
1407
- gl.forceContextLoss();
1408
- if (gl.xr && gl.xr.enabled) {
1409
- gl.xr.setAnimationLoop(null);
1410
- xr.disconnect();
1411
- }
1412
- dispose(this.get());
1413
- rootStateMap.delete(canvas);
1414
- }
1415
- }, 500);
1629
+ this.injectedArgs = [];
1416
1630
  }
1417
- #resize() {
1418
- const state = this.get();
1419
- let oldSize = state.size;
1420
- let oldDpr = state.viewport.dpr;
1421
- let oldCamera = state.camera;
1422
- const camera = this.select('camera');
1423
- const size = this.select('size');
1424
- const viewport = this.select('viewport');
1425
- const triggers = computed(() => ({ camera: camera(), size: size(), viewport: viewport(), gl: this.get('gl') }));
1426
- effect(() => {
1427
- const { camera, size, viewport, gl } = triggers();
1428
- // resize camera and renderer on changes to size and dpr
1429
- if (size !== oldSize || viewport.dpr !== oldDpr) {
1430
- oldSize = size;
1431
- oldDpr = viewport.dpr;
1432
- // update camera
1433
- updateCamera(camera, size);
1434
- gl.setPixelRatio(viewport.dpr);
1435
- gl.setSize(size.width, size.height);
1436
- }
1437
- // update viewport when camera changes
1438
- if (camera !== oldCamera) {
1439
- oldCamera = camera;
1440
- updateCamera(camera, size);
1441
- this.set((state) => ({
1442
- viewport: { ...state.viewport, ...state.viewport.getCurrentViewport(camera) },
1443
- }));
1444
- }
1445
- }, { injector: this.#injector, allowSignalWrites: true });
1631
+ set args(args) {
1632
+ if (args == null || !Array.isArray(args) || (args.length === 1 && args[0] === null))
1633
+ return;
1634
+ this.injected = false;
1635
+ this.injectedArgs = args;
1636
+ this.createView();
1446
1637
  }
1447
- #invalidate() {
1448
- const state = this.select();
1449
- effect(() => {
1450
- state().invalidate();
1451
- }, { injector: this.#injector });
1638
+ get args() {
1639
+ if (this.validate()) {
1640
+ this.injected = true;
1641
+ return this.injectedArgs;
1642
+ }
1643
+ return null;
1452
1644
  }
1453
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtStore, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1454
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtStore }); }
1455
- }
1456
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtStore, decorators: [{
1457
- type: Injectable
1458
- }] });
1459
- function computeInitialSize(canvas, defaultSize) {
1460
- if (defaultSize)
1461
- return defaultSize;
1462
- if (canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1463
- return canvas.parentElement.getBoundingClientRect();
1645
+ validate() {
1646
+ return !this.injected && !!this.injectedArgs.length;
1464
1647
  }
1465
- return { width: 0, height: 0, top: 0, left: 0 };
1648
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtArgs, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
1649
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.2", type: NgtArgs, isStandalone: true, selector: "ng-template[args]", inputs: { args: "args" }, usesInheritance: true, ngImport: i0 }); }
1466
1650
  }
1467
- // Disposes an object and all its properties
1468
- function dispose(obj) {
1469
- if (obj.dispose && !is.scene(obj))
1470
- obj.dispose();
1471
- for (const p in obj) {
1472
- p.dispose?.();
1473
- delete obj[p];
1651
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtArgs, decorators: [{
1652
+ type: Directive,
1653
+ args: [{ selector: 'ng-template[args]', standalone: true }]
1654
+ }], propDecorators: { args: [{
1655
+ type: Input
1656
+ }] } });
1657
+
1658
+ class NgtParent extends NgtCommonDirective {
1659
+ constructor() {
1660
+ super(...arguments);
1661
+ this.injectedParent = null;
1662
+ }
1663
+ set parent(parent) {
1664
+ if (!parent)
1665
+ return;
1666
+ this.injected = false;
1667
+ this.injectedParent = parent;
1668
+ this.createView();
1669
+ }
1670
+ get parent() {
1671
+ if (this.validate()) {
1672
+ this.injected = true;
1673
+ return this.injectedParent;
1674
+ }
1675
+ return null;
1474
1676
  }
1677
+ validate() {
1678
+ return !this.injected && !!this.injectedParent;
1679
+ }
1680
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtParent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
1681
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.2", type: NgtParent, isStandalone: true, selector: "ng-template[parent]", inputs: { parent: "parent" }, usesInheritance: true, ngImport: i0 }); }
1475
1682
  }
1476
-
1477
- const NGT_COMPOUND_PREFIXES = new InjectionToken('NgtCompoundPrefixes');
1478
-
1479
- const catalogue = {};
1480
- function extend(objects) {
1481
- Object.assign(catalogue, objects);
1482
- }
1483
- const NGT_CATALOGUE = new InjectionToken('THREE Constructors Catalogue', { factory: () => catalogue });
1683
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtParent, decorators: [{
1684
+ type: Directive,
1685
+ args: [{ selector: 'ng-template[parent]', standalone: true }]
1686
+ }], propDecorators: { parent: [{
1687
+ type: Input
1688
+ }] } });
1484
1689
 
1485
1690
  function attach(object, value, paths = []) {
1486
1691
  const [base, ...remaining] = paths;
@@ -1512,25 +1717,38 @@ function createAttachFunction(cb) {
1512
1717
  return (parent, child, store) => cb({ parent, child, store });
1513
1718
  }
1514
1719
 
1515
- const ROUTED_SCENE = '__ngt_renderer_is_routed_scene__';
1516
- const SPECIAL_INTERNAL_ADD_COMMENT = '__ngt_renderer_add_comment__';
1517
- const SPECIAL_DOM_TAG = {
1518
- NGT_PORTAL: 'ngt-portal',
1519
- NGT_PRIMITIVE: 'ngt-primitive',
1520
- NGT_VALUE: 'ngt-value',
1521
- };
1522
- const SPECIAL_PROPERTIES = {
1523
- COMPOUND: 'ngtCompound',
1524
- RENDER_PRIORITY: 'priority',
1525
- ATTACH: 'attach',
1526
- VALUE: 'rawValue',
1527
- REF: 'ref',
1528
- };
1529
- const SPECIAL_EVENTS = {
1530
- BEFORE_RENDER: 'beforeRender',
1531
- AFTER_UPDATE: 'afterUpdate',
1532
- AFTER_ATTACH: 'afterAttach',
1533
- };
1720
+ // @internal
1721
+ var NgtRendererClassId;
1722
+ (function (NgtRendererClassId) {
1723
+ NgtRendererClassId[NgtRendererClassId["type"] = 0] = "type";
1724
+ NgtRendererClassId[NgtRendererClassId["parent"] = 1] = "parent";
1725
+ NgtRendererClassId[NgtRendererClassId["injectedParent"] = 2] = "injectedParent";
1726
+ NgtRendererClassId[NgtRendererClassId["children"] = 3] = "children";
1727
+ NgtRendererClassId[NgtRendererClassId["destroyed"] = 4] = "destroyed";
1728
+ NgtRendererClassId[NgtRendererClassId["compound"] = 5] = "compound";
1729
+ NgtRendererClassId[NgtRendererClassId["compoundParent"] = 6] = "compoundParent";
1730
+ NgtRendererClassId[NgtRendererClassId["compounded"] = 7] = "compounded";
1731
+ NgtRendererClassId[NgtRendererClassId["queueOps"] = 8] = "queueOps";
1732
+ NgtRendererClassId[NgtRendererClassId["attributes"] = 9] = "attributes";
1733
+ NgtRendererClassId[NgtRendererClassId["properties"] = 10] = "properties";
1734
+ NgtRendererClassId[NgtRendererClassId["rawValue"] = 11] = "rawValue";
1735
+ NgtRendererClassId[NgtRendererClassId["ref"] = 12] = "ref";
1736
+ NgtRendererClassId[NgtRendererClassId["portalContainer"] = 13] = "portalContainer";
1737
+ NgtRendererClassId[NgtRendererClassId["injectorFactory"] = 14] = "injectorFactory";
1738
+ })(NgtRendererClassId || (NgtRendererClassId = {}));
1739
+ // @internal
1740
+ var NgtCompoundClassId;
1741
+ (function (NgtCompoundClassId) {
1742
+ NgtCompoundClassId[NgtCompoundClassId["applyFirst"] = 0] = "applyFirst";
1743
+ NgtCompoundClassId[NgtCompoundClassId["props"] = 1] = "props";
1744
+ })(NgtCompoundClassId || (NgtCompoundClassId = {}));
1745
+ // @internal
1746
+ var NgtQueueOpClassId;
1747
+ (function (NgtQueueOpClassId) {
1748
+ NgtQueueOpClassId[NgtQueueOpClassId["type"] = 0] = "type";
1749
+ NgtQueueOpClassId[NgtQueueOpClassId["op"] = 1] = "op";
1750
+ NgtQueueOpClassId[NgtQueueOpClassId["done"] = 2] = "done";
1751
+ })(NgtQueueOpClassId || (NgtQueueOpClassId = {}));
1534
1752
  function attachThreeChild(parent, child) {
1535
1753
  const pLS = getLocalState(parent);
1536
1754
  const cLS = getLocalState(child);
@@ -1541,7 +1759,7 @@ function attachThreeChild(parent, child) {
1541
1759
  let added = false;
1542
1760
  // assign store on child if not already exist
1543
1761
  // or child store is the parent of parent store
1544
- if (!cLS.store || cLS.store === pLS.store.get('previousStore')) {
1762
+ if (!cLS.store || cLS.store === pLS.store.get('previousRoot')) {
1545
1763
  cLS.store = pLS.store;
1546
1764
  }
1547
1765
  if (cLS.attach) {
@@ -1567,13 +1785,15 @@ function attachThreeChild(parent, child) {
1567
1785
  }
1568
1786
  // attach
1569
1787
  if (cLS.isRaw) {
1570
- if (cLS.parent) {
1571
- cLS.parent.set(parent);
1788
+ if (cLS.parent && cLS.parent() !== parent) {
1789
+ untracked(() => {
1790
+ cLS.parent.set(parent);
1791
+ });
1572
1792
  }
1573
1793
  // at this point we don't have rawValue yet, so we bail and wait until the Renderer recalls attach
1574
- if (child.__ngt_renderer__[11 /* NgtRendererClassId.rawValue */] === undefined)
1794
+ if (child.__ngt_renderer__[NgtRendererClassId.rawValue] === undefined)
1575
1795
  return;
1576
- attach(parent, child.__ngt_renderer__[11 /* NgtRendererClassId.rawValue */], attachProp);
1796
+ attach(parent, child.__ngt_renderer__[NgtRendererClassId.rawValue], attachProp);
1577
1797
  }
1578
1798
  else {
1579
1799
  attach(parent, child, attachProp);
@@ -1587,8 +1807,10 @@ function attachThreeChild(parent, child) {
1587
1807
  added = true;
1588
1808
  }
1589
1809
  pLS.add(child, added ? 'objects' : 'nonObjects');
1590
- if (cLS.parent) {
1591
- cLS.parent.set(parent);
1810
+ if (cLS.parent && cLS.parent() !== parent) {
1811
+ untracked(() => {
1812
+ cLS.parent.set(parent);
1813
+ });
1592
1814
  }
1593
1815
  if (cLS.afterAttach)
1594
1816
  cLS.afterAttach.emit({ parent, node: child });
@@ -1599,11 +1821,13 @@ function removeThreeChild(parent, child, dispose) {
1599
1821
  const pLS = getLocalState(parent);
1600
1822
  const cLS = getLocalState(child);
1601
1823
  // clear parent ref
1602
- cLS.parent?.set(null);
1824
+ untracked(() => {
1825
+ cLS.parent?.set(null);
1826
+ });
1603
1827
  // remove child from parent
1604
- if (untracked(pLS.objects))
1828
+ if (pLS.objects && untracked(pLS.objects))
1605
1829
  pLS.remove(child, 'objects');
1606
- if (untracked(pLS.nonObjects))
1830
+ if (pLS.nonObjects && untracked(pLS.nonObjects))
1607
1831
  pLS.remove(child, 'nonObjects');
1608
1832
  if (cLS.attach) {
1609
1833
  detach(parent, child, cLS.attach);
@@ -1627,7 +1851,15 @@ function removeThreeRecursive(array, parent, dispose) {
1627
1851
  if (array)
1628
1852
  [...array].forEach((child) => removeThreeChild(parent, child, dispose));
1629
1853
  }
1630
- function processThreeEvent(instance, priority, eventName, callback, zone, cdr, targetCdr) {
1854
+ function kebabToPascal(str) {
1855
+ // split the string at each hyphen
1856
+ const parts = str.split('-');
1857
+ // map over the parts, capitalizing the first letter of each part
1858
+ const pascalParts = parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1));
1859
+ // join the parts together to create the final PascalCase string
1860
+ return pascalParts.join('');
1861
+ }
1862
+ function processThreeEvent(instance, priority, eventName, callback, zone, rootCdr, targetCdr) {
1631
1863
  const lS = getLocalState(instance);
1632
1864
  if (eventName === SPECIAL_EVENTS.BEFORE_RENDER) {
1633
1865
  return lS.store
@@ -1651,8 +1883,7 @@ function processThreeEvent(instance, priority, eventName, callback, zone, cdr, t
1651
1883
  previousHandler(event);
1652
1884
  zone.run(() => {
1653
1885
  callback(event);
1654
- safeDetectChanges(targetCdr);
1655
- safeDetectChanges(cdr);
1886
+ safeDetectChanges(targetCdr, rootCdr);
1656
1887
  });
1657
1888
  };
1658
1889
  Object.assign(lS.handlers, { [eventName]: updatedCallback });
@@ -1666,6 +1897,7 @@ function processThreeEvent(instance, priority, eventName, callback, zone, cdr, t
1666
1897
  return () => {
1667
1898
  const localState = getLocalState(instance);
1668
1899
  if (localState && localState.eventCount) {
1900
+ localState.eventCount -= 1;
1669
1901
  const index = localState.store
1670
1902
  .get('internal', 'interaction')
1671
1903
  .findIndex((obj) => obj.uuid === instance.uuid);
@@ -1674,117 +1906,12 @@ function processThreeEvent(instance, priority, eventName, callback, zone, cdr, t
1674
1906
  }
1675
1907
  };
1676
1908
  }
1677
- function eventToHandler(callback) {
1678
- return (event) => {
1679
- callback(event);
1680
- };
1681
- }
1682
- function kebabToPascal(str) {
1683
- // split the string at each hyphen
1684
- const parts = str.split('-');
1685
- // map over the parts, capitalizing the first letter of each part
1686
- const pascalParts = parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1));
1687
- // join the parts together to create the final PascalCase string
1688
- return pascalParts.join('');
1689
- }
1690
-
1691
- class NgtCommonDirective {
1692
- #vcr;
1693
- #zone;
1694
- #template;
1695
- #view;
1696
- constructor() {
1697
- this.#vcr = inject(ViewContainerRef);
1698
- this.#zone = inject(NgZone);
1699
- this.#template = inject(TemplateRef);
1700
- this.injected = false;
1701
- this.shouldCreateView = true;
1702
- const commentNode = this.#vcr.element.nativeElement;
1703
- if (commentNode[SPECIAL_INTERNAL_ADD_COMMENT]) {
1704
- commentNode[SPECIAL_INTERNAL_ADD_COMMENT]();
1705
- delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
1706
- }
1707
- }
1708
- createView() {
1709
- if (this.shouldCreateView) {
1710
- if (this.#view && !this.#view.destroyed) {
1711
- this.#view.destroy();
1712
- }
1713
- this.#zone.runOutsideAngular(() => {
1714
- this.#view = this.#vcr.createEmbeddedView(this.#template);
1715
- this.#view.detectChanges();
1716
- });
1717
- }
1718
- }
1719
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtCommonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1720
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtCommonDirective, ngImport: i0 }); }
1721
- }
1722
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtCommonDirective, decorators: [{
1723
- type: Directive
1724
- }], ctorParameters: function () { return []; } });
1725
-
1726
- class NgtArgs extends NgtCommonDirective {
1727
- #injectedArgs = [];
1728
- set args(args) {
1729
- if (args == null || !Array.isArray(args) || (args.length === 1 && args[0] === null))
1730
- return;
1731
- this.injected = false;
1732
- this.#injectedArgs = args;
1733
- this.createView();
1734
- }
1735
- get args() {
1736
- if (this.validate()) {
1737
- this.injected = true;
1738
- return this.#injectedArgs;
1739
- }
1740
- return null;
1741
- }
1742
- validate() {
1743
- return !this.injected && !!this.#injectedArgs.length;
1744
- }
1745
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtArgs, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
1746
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtArgs, isStandalone: true, selector: "[args]", inputs: { args: "args" }, usesInheritance: true, ngImport: i0 }); }
1747
- }
1748
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtArgs, decorators: [{
1749
- type: Directive,
1750
- args: [{ selector: '[args]', standalone: true }]
1751
- }], propDecorators: { args: [{
1752
- type: Input
1753
- }] } });
1754
-
1755
- class NgtParent extends NgtCommonDirective {
1756
- #injectedParent = null;
1757
- set parent(parent) {
1758
- if (!parent)
1759
- return;
1760
- this.injected = false;
1761
- this.#injectedParent = parent;
1762
- this.createView();
1763
- }
1764
- get parent() {
1765
- if (this.validate()) {
1766
- this.injected = true;
1767
- return this.#injectedParent;
1768
- }
1769
- return null;
1770
- }
1771
- validate() {
1772
- return !this.injected && !!this.#injectedParent;
1773
- }
1774
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtParent, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
1775
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtParent, isStandalone: true, selector: "[parent]", inputs: { parent: "parent" }, usesInheritance: true, ngImport: i0 }); }
1776
- }
1777
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtParent, decorators: [{
1778
- type: Directive,
1779
- args: [{ selector: '[parent]', standalone: true }]
1780
- }], propDecorators: { parent: [{
1781
- type: Input
1782
- }] } });
1783
1909
 
1910
+ const NGT_COMPOUND_PREFIXES = new InjectionToken('NgtCompoundPrefixes');
1784
1911
  class NgtRendererStore {
1785
- #comments = [];
1786
1912
  constructor(root) {
1787
1913
  this.root = root;
1914
+ this.comments = [];
1788
1915
  }
1789
1916
  createNode(type, node) {
1790
1917
  const state = [
@@ -1805,99 +1932,108 @@ class NgtRendererStore {
1805
1932
  undefined,
1806
1933
  ];
1807
1934
  const rendererNode = Object.assign(node, { __ngt_renderer__: state });
1808
- // assign ownerDocument to node so we can use HostListener in Component
1935
+ // NOTE: assign ownerDocument to node so we can use HostListener in Component
1809
1936
  if (!rendererNode['ownerDocument'])
1810
1937
  rendererNode['ownerDocument'] = this.root.document;
1811
- // assign injectorFactory on non-three type since
1938
+ // NOTE: assign injectorFactory on non-three type since
1812
1939
  // rendererNode is an instance of DOM Node
1813
- if (state[0 /* NgtRendererClassId.type */] !== 'three') {
1814
- state[14 /* NgtRendererClassId.injectorFactory */] = () => getDebugNode(rendererNode).injector;
1940
+ if (state[NgtRendererClassId.type] !== 'three') {
1941
+ state[NgtRendererClassId.injectorFactory] = () => getDebugNode(rendererNode).injector;
1815
1942
  }
1816
- if (state[0 /* NgtRendererClassId.type */] === 'comment') {
1817
- // we attach an arrow function to the Comment node
1943
+ if (state[NgtRendererClassId.type] === 'comment') {
1944
+ // NOTE: we attach an arrow function to the Comment node
1818
1945
  // In our directives, we can call this function to then start tracking the RendererNode
1819
1946
  // this is done to limit the amount of Nodes we need to process for getCreationState
1820
1947
  rendererNode[SPECIAL_INTERNAL_ADD_COMMENT] = (node) => {
1821
- if (node && node.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'portal') {
1948
+ if (node && node.__ngt_renderer__[NgtRendererClassId.type] === 'portal') {
1822
1949
  this.portals.push(node);
1823
1950
  }
1824
1951
  else {
1825
- this.#comments.push(rendererNode);
1952
+ this.comments.push(rendererNode);
1826
1953
  }
1827
1954
  };
1828
1955
  return rendererNode;
1829
1956
  }
1830
- if (state[0 /* NgtRendererClassId.type */] === 'compound') {
1831
- state[8 /* NgtRendererClassId.queueOps */] = new Set();
1832
- state[9 /* NgtRendererClassId.attributes */] = {};
1833
- state[10 /* NgtRendererClassId.properties */] = {};
1957
+ if (state[NgtRendererClassId.type] === 'compound') {
1958
+ state[NgtRendererClassId.queueOps] = new Set();
1959
+ state[NgtRendererClassId.attributes] = {};
1960
+ state[NgtRendererClassId.properties] = {};
1834
1961
  return rendererNode;
1835
1962
  }
1836
1963
  return rendererNode;
1837
1964
  }
1838
1965
  setParent(node, parent) {
1839
- if (!node.__ngt_renderer__[1 /* NgtRendererClassId.parent */]) {
1840
- node.__ngt_renderer__[1 /* NgtRendererClassId.parent */] = parent;
1966
+ if (!node.__ngt_renderer__[NgtRendererClassId.parent]) {
1967
+ node.__ngt_renderer__[NgtRendererClassId.parent] = parent;
1841
1968
  }
1842
1969
  }
1843
1970
  addChild(node, child) {
1844
- if (!node.__ngt_renderer__[3 /* NgtRendererClassId.children */].includes(child)) {
1845
- node.__ngt_renderer__[3 /* NgtRendererClassId.children */].push(child);
1971
+ if (!node.__ngt_renderer__[NgtRendererClassId.children].includes(child)) {
1972
+ node.__ngt_renderer__[NgtRendererClassId.children].push(child);
1846
1973
  }
1847
1974
  }
1848
1975
  removeChild(node, child) {
1849
- const index = node.__ngt_renderer__[3 /* NgtRendererClassId.children */].findIndex((c) => child === c);
1976
+ const index = node.__ngt_renderer__?.[NgtRendererClassId.children].findIndex((c) => child === c);
1850
1977
  if (index >= 0) {
1851
- node.__ngt_renderer__[3 /* NgtRendererClassId.children */].splice(index, 1);
1978
+ node.__ngt_renderer__[NgtRendererClassId.children].splice(index, 1);
1852
1979
  }
1853
1980
  }
1854
1981
  setCompound(compound, instance) {
1982
+ const instanceRS = instance.__ngt_renderer__;
1983
+ if (instanceRS && instanceRS[NgtRendererClassId.parent]) {
1984
+ const parentRS = instanceRS[NgtRendererClassId.parent].__ngt_renderer__;
1985
+ // NOTE: if instance is already compounded by its parent. skip
1986
+ if (parentRS[NgtRendererClassId.type] === 'compound' &&
1987
+ parentRS[NgtRendererClassId.compounded] === instance) {
1988
+ return;
1989
+ }
1990
+ }
1855
1991
  const rS = compound.__ngt_renderer__;
1856
- rS[7 /* NgtRendererClassId.compounded */] = instance;
1857
- const attributes = Object.keys(rS[9 /* NgtRendererClassId.attributes */]);
1858
- const properties = Object.keys(rS[10 /* NgtRendererClassId.properties */]);
1992
+ rS[NgtRendererClassId.compounded] = instance;
1993
+ const attributes = Object.keys(rS[NgtRendererClassId.attributes]);
1994
+ const properties = Object.keys(rS[NgtRendererClassId.properties]);
1859
1995
  for (const key of attributes) {
1860
- this.applyAttribute(instance, key, rS[9 /* NgtRendererClassId.attributes */][key]);
1996
+ this.applyAttribute(instance, key, rS[NgtRendererClassId.attributes][key]);
1861
1997
  }
1862
1998
  for (const key of properties) {
1863
- this.applyProperty(instance, key, rS[10 /* NgtRendererClassId.properties */][key]);
1999
+ this.applyProperty(instance, key, rS[NgtRendererClassId.properties][key]);
1864
2000
  }
1865
2001
  this.executeOperation(compound);
1866
2002
  }
1867
2003
  queueOperation(node, op) {
1868
- node.__ngt_renderer__[8 /* NgtRendererClassId.queueOps */].add(op);
2004
+ node.__ngt_renderer__[NgtRendererClassId.queueOps].add(op);
1869
2005
  }
1870
2006
  executeOperation(node, type = 'op') {
1871
2007
  const rS = node.__ngt_renderer__;
1872
- if (rS[8 /* NgtRendererClassId.queueOps */]?.size) {
1873
- rS[8 /* NgtRendererClassId.queueOps */].forEach((op) => {
1874
- if (op[0 /* NgtQueueOpClassId.type */] === type) {
1875
- op[1 /* NgtQueueOpClassId.op */]();
1876
- rS[8 /* NgtRendererClassId.queueOps */].delete(op);
2008
+ if (rS[NgtRendererClassId.queueOps]?.size) {
2009
+ rS[NgtRendererClassId.queueOps].forEach((op) => {
2010
+ if (op[NgtQueueOpClassId.type] === type) {
2011
+ op[NgtQueueOpClassId.op]();
2012
+ rS[NgtRendererClassId.queueOps].delete(op);
1877
2013
  }
1878
2014
  });
1879
2015
  }
1880
2016
  }
1881
2017
  processPortalContainer(portal) {
1882
- const injectorFactory = portal.__ngt_renderer__[14 /* NgtRendererClassId.injectorFactory */];
2018
+ const injectorFactory = portal.__ngt_renderer__[NgtRendererClassId.injectorFactory];
1883
2019
  const injector = injectorFactory?.();
1884
2020
  if (!injector)
1885
2021
  return;
1886
- const portalStore = injector.get(NgtStore, null);
2022
+ const portalStore = injector.get(NGT_STORE, null);
1887
2023
  if (!portalStore)
1888
2024
  return;
1889
2025
  const portalContainer = portalStore.get('scene');
1890
2026
  if (!portalContainer)
1891
2027
  return;
1892
- portal.__ngt_renderer__[13 /* NgtRendererClassId.portalContainer */] = this.createNode('three', portalContainer);
2028
+ portal.__ngt_renderer__[NgtRendererClassId.portalContainer] = this.createNode('three', portalContainer);
1893
2029
  }
1894
2030
  applyAttribute(node, name, value) {
1895
2031
  const rS = node.__ngt_renderer__;
1896
- if (rS[4 /* NgtRendererClassId.destroyed */])
2032
+ if (rS[NgtRendererClassId.destroyed])
1897
2033
  return;
1898
2034
  if (name === SPECIAL_PROPERTIES.RENDER_PRIORITY) {
1899
- // priority needs to be set as an attribute string so that they can be set as early as possible
1900
- // we convert that string to a number. if it's invalid, 0
2035
+ // NOTE: priority needs to be set as an attribute string so that they can be set as early as possible
2036
+ // we convert that string to a number. if it's invalid, default 0
1901
2037
  let priority = Number(value);
1902
2038
  if (isNaN(priority)) {
1903
2039
  priority = 0;
@@ -1906,19 +2042,19 @@ class NgtRendererStore {
1906
2042
  getLocalState(node).priority = priority;
1907
2043
  }
1908
2044
  if (name === SPECIAL_PROPERTIES.COMPOUND) {
1909
- // we set the compound property on instance node now so we know that this instance is being compounded
1910
- rS[5 /* NgtRendererClassId.compound */] = [value === '' || value === 'first', {}];
2045
+ // NOTE: we set the compound property on instance node now so we know that this instance is being compounded
2046
+ rS[NgtRendererClassId.compound] = [value === '' || value === 'first', {}];
1911
2047
  return;
1912
2048
  }
1913
2049
  if (name === SPECIAL_PROPERTIES.ATTACH) {
1914
- // handle attach as tring
2050
+ // NOTE: handle attach as tring
1915
2051
  const paths = value.split('.');
1916
2052
  if (paths.length)
1917
2053
  getLocalState(node).attach = paths;
1918
2054
  return;
1919
2055
  }
1920
2056
  if (name === SPECIAL_PROPERTIES.VALUE) {
1921
- // coercion
2057
+ // NOTE: coercion
1922
2058
  let maybeCoerced = value;
1923
2059
  if (maybeCoerced === '' || maybeCoerced === 'true' || maybeCoerced === 'false') {
1924
2060
  maybeCoerced = maybeCoerced === 'true' || maybeCoerced === '';
@@ -1926,25 +2062,26 @@ class NgtRendererStore {
1926
2062
  else if (!isNaN(Number(maybeCoerced))) {
1927
2063
  maybeCoerced = Number(maybeCoerced);
1928
2064
  }
1929
- rS[11 /* NgtRendererClassId.rawValue */] = maybeCoerced;
2065
+ rS[NgtRendererClassId.rawValue] = maybeCoerced;
1930
2066
  return;
1931
2067
  }
1932
2068
  applyProps(node, { [name]: value });
2069
+ this.updateNativeProps(node, name, value);
1933
2070
  }
1934
2071
  applyProperty(node, name, value) {
1935
2072
  const rS = node.__ngt_renderer__;
1936
- if (rS[4 /* NgtRendererClassId.destroyed */])
2073
+ if (rS[NgtRendererClassId.destroyed])
1937
2074
  return;
1938
2075
  // [ref]
1939
2076
  if (name === SPECIAL_PROPERTIES.REF && is.ref(value)) {
1940
- rS[12 /* NgtRendererClassId.ref */] = value;
2077
+ rS[NgtRendererClassId.ref] = value;
1941
2078
  value.nativeElement = node;
1942
2079
  return;
1943
2080
  }
1944
- const parent = getLocalState(node).parent() || rS[1 /* NgtRendererClassId.parent */];
2081
+ const parent = getLocalState(node).parent() || rS[NgtRendererClassId.parent];
1945
2082
  // [rawValue]
1946
2083
  if (getLocalState(node).isRaw && name === SPECIAL_PROPERTIES.VALUE) {
1947
- rS[11 /* NgtRendererClassId.rawValue */] = value;
2084
+ rS[NgtRendererClassId.rawValue] = value;
1948
2085
  if (parent)
1949
2086
  attachThreeChild(parent, node);
1950
2087
  return;
@@ -1956,13 +2093,14 @@ class NgtRendererStore {
1956
2093
  attachThreeChild(parent, node);
1957
2094
  return;
1958
2095
  }
1959
- const compound = rS[5 /* NgtRendererClassId.compound */];
1960
- if (compound?.[1 /* NgtCompoundClassId.props */] &&
1961
- name in compound[1 /* NgtCompoundClassId.props */] &&
1962
- !compound[0 /* NgtCompoundClassId.applyFirst */]) {
1963
- value = compound[1 /* NgtCompoundClassId.props */][name];
2096
+ const compound = rS[NgtRendererClassId.compound];
2097
+ if (compound?.[NgtCompoundClassId.props] &&
2098
+ name in compound[NgtCompoundClassId.props] &&
2099
+ !compound[NgtCompoundClassId.applyFirst]) {
2100
+ value = compound[NgtCompoundClassId.props][name];
1964
2101
  }
1965
2102
  applyProps(node, { [name]: value });
2103
+ this.updateNativeProps(node, name, value);
1966
2104
  }
1967
2105
  isCompound(name) {
1968
2106
  return this.root.compoundPrefixes.some((prefix) => name.startsWith(prefix));
@@ -1970,7 +2108,7 @@ class NgtRendererStore {
1970
2108
  isDOM(node) {
1971
2109
  const rS = node['__ngt_renderer__'];
1972
2110
  return (!rS ||
1973
- (rS[0 /* NgtRendererClassId.type */] !== 'compound' &&
2111
+ (rS[NgtRendererClassId.type] !== 'compound' &&
1974
2112
  (node instanceof Element || node instanceof Document || node instanceof Window)));
1975
2113
  }
1976
2114
  get rootScene() {
@@ -1980,37 +2118,37 @@ class NgtRendererStore {
1980
2118
  return this.root.portals;
1981
2119
  }
1982
2120
  getClosestParentWithInstance(node) {
1983
- let parent = node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
1984
- while (parent && parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'three') {
1985
- parent = parent.__ngt_renderer__[13 /* NgtRendererClassId.portalContainer */]
1986
- ? parent.__ngt_renderer__[13 /* NgtRendererClassId.portalContainer */]
1987
- : parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
2121
+ let parent = node.__ngt_renderer__[NgtRendererClassId.parent];
2122
+ while (parent && parent.__ngt_renderer__[NgtRendererClassId.type] !== 'three') {
2123
+ parent = parent.__ngt_renderer__[NgtRendererClassId.portalContainer]
2124
+ ? parent.__ngt_renderer__[NgtRendererClassId.portalContainer]
2125
+ : parent.__ngt_renderer__[NgtRendererClassId.parent];
1988
2126
  }
1989
2127
  return parent;
1990
2128
  }
1991
2129
  getClosestParentWithCompound(node) {
1992
- if (node.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */]) {
1993
- return node.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */];
2130
+ if (node.__ngt_renderer__[NgtRendererClassId.compoundParent]) {
2131
+ return node.__ngt_renderer__[NgtRendererClassId.compoundParent];
1994
2132
  }
1995
- let parent = node.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
2133
+ let parent = node.__ngt_renderer__[NgtRendererClassId.parent];
1996
2134
  if (parent &&
1997
- parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'compound' &&
1998
- !parent.__ngt_renderer__[7 /* NgtRendererClassId.compounded */]) {
2135
+ parent.__ngt_renderer__[NgtRendererClassId.type] === 'compound' &&
2136
+ !parent.__ngt_renderer__[NgtRendererClassId.compounded]) {
1999
2137
  return parent;
2000
2138
  }
2001
2139
  while (parent &&
2002
- (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' ||
2003
- !parent.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */] ||
2004
- parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] !== 'compound')) {
2005
- parent = parent.__ngt_renderer__[1 /* NgtRendererClassId.parent */];
2140
+ (parent.__ngt_renderer__[NgtRendererClassId.type] === 'three' ||
2141
+ !parent.__ngt_renderer__[NgtRendererClassId.compoundParent] ||
2142
+ parent.__ngt_renderer__[NgtRendererClassId.type] !== 'compound')) {
2143
+ parent = parent.__ngt_renderer__[NgtRendererClassId.parent];
2006
2144
  }
2007
2145
  if (!parent)
2008
2146
  return;
2009
- if (parent.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' &&
2010
- parent.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */]) {
2011
- return parent.__ngt_renderer__[6 /* NgtRendererClassId.compoundParent */];
2147
+ if (parent.__ngt_renderer__[NgtRendererClassId.type] === 'three' &&
2148
+ parent.__ngt_renderer__[NgtRendererClassId.compoundParent]) {
2149
+ return parent.__ngt_renderer__[NgtRendererClassId.compoundParent];
2012
2150
  }
2013
- if (!parent.__ngt_renderer__[7 /* NgtRendererClassId.compounded */]) {
2151
+ if (!parent.__ngt_renderer__[NgtRendererClassId.compounded]) {
2014
2152
  return parent;
2015
2153
  }
2016
2154
  return null;
@@ -2023,11 +2161,11 @@ class NgtRendererStore {
2023
2161
  }
2024
2162
  destroy(node, parent) {
2025
2163
  const rS = node.__ngt_renderer__;
2026
- if (rS[4 /* NgtRendererClassId.destroyed */])
2164
+ if (!rS || rS[NgtRendererClassId.destroyed])
2027
2165
  return;
2028
- if (rS[0 /* NgtRendererClassId.type */] === 'three') {
2029
- rS[5 /* NgtRendererClassId.compound */] = undefined;
2030
- rS[6 /* NgtRendererClassId.compoundParent */] = undefined;
2166
+ if (rS[NgtRendererClassId.type] === 'three') {
2167
+ rS[NgtRendererClassId.compound] = undefined;
2168
+ rS[NgtRendererClassId.compoundParent] = undefined;
2031
2169
  const localState = getLocalState(node);
2032
2170
  if (localState.objects) {
2033
2171
  untracked(localState.objects).forEach((obj) => this.destroy(obj, parent));
@@ -2052,58 +2190,73 @@ class NgtRendererStore {
2052
2190
  delete node['__ngt__'];
2053
2191
  }
2054
2192
  }
2055
- if (rS[0 /* NgtRendererClassId.type */] === 'comment') {
2056
- rS[14 /* NgtRendererClassId.injectorFactory */] = null;
2193
+ if (rS[NgtRendererClassId.type] === 'comment') {
2194
+ rS[NgtRendererClassId.injectorFactory] = null;
2057
2195
  delete node[SPECIAL_INTERNAL_ADD_COMMENT];
2058
- const index = this.#comments.findIndex((comment) => comment === node);
2196
+ const index = this.comments.findIndex((comment) => comment === node);
2059
2197
  if (index > -1) {
2060
- this.#comments.splice(index, 1);
2198
+ this.comments.splice(index, 1);
2061
2199
  }
2062
2200
  }
2063
- if (rS[0 /* NgtRendererClassId.type */] === 'portal') {
2064
- rS[14 /* NgtRendererClassId.injectorFactory */] = null;
2201
+ if (rS[NgtRendererClassId.type] === 'portal') {
2202
+ rS[NgtRendererClassId.injectorFactory] = null;
2065
2203
  const index = this.portals.findIndex((portal) => portal === node);
2066
2204
  if (index > -1) {
2067
2205
  this.portals.splice(index, 1);
2068
2206
  }
2069
2207
  }
2070
- if (rS[0 /* NgtRendererClassId.type */] === 'compound') {
2071
- rS[7 /* NgtRendererClassId.compounded */] = undefined;
2072
- rS[9 /* NgtRendererClassId.attributes */] = null;
2073
- rS[10 /* NgtRendererClassId.properties */] = null;
2208
+ if (rS[NgtRendererClassId.type] === 'compound') {
2209
+ rS[NgtRendererClassId.compounded] = undefined;
2210
+ rS[NgtRendererClassId.attributes] = null;
2211
+ rS[NgtRendererClassId.properties] = null;
2074
2212
  this.executeOperation(node, 'cleanUp');
2075
- rS[8 /* NgtRendererClassId.queueOps */].clear();
2076
- rS[8 /* NgtRendererClassId.queueOps */] = null;
2213
+ rS[NgtRendererClassId.queueOps].clear();
2214
+ rS[NgtRendererClassId.queueOps] = null;
2077
2215
  }
2078
- if (rS[12 /* NgtRendererClassId.ref */]) {
2216
+ if (rS[NgtRendererClassId.ref]) {
2079
2217
  // nullify ref
2080
- rS[12 /* NgtRendererClassId.ref */].nativeElement = null;
2081
- rS[12 /* NgtRendererClassId.ref */] = undefined;
2218
+ // but we do it later so that it doesn't hinder render
2219
+ // TODO: will this cause memory leak?
2220
+ requestAnimationFrame(() => {
2221
+ rS[NgtRendererClassId.ref].nativeElement = null;
2222
+ rS[NgtRendererClassId.ref] = undefined;
2223
+ });
2082
2224
  }
2083
2225
  // nullify parent
2084
- rS[1 /* NgtRendererClassId.parent */] = null;
2085
- for (const renderChild of rS[3 /* NgtRendererClassId.children */] || []) {
2086
- if (renderChild.__ngt_renderer__[0 /* NgtRendererClassId.type */] === 'three' && parent) {
2087
- removeThreeChild(parent, renderChild, true);
2226
+ rS[NgtRendererClassId.parent] = null;
2227
+ for (const renderChild of rS[NgtRendererClassId.children] || []) {
2228
+ if (renderChild.__ngt_renderer__?.[NgtRendererClassId.type] === 'three' && parent) {
2229
+ const closestInstance = this.getClosestParentWithInstance(parent);
2230
+ if (closestInstance) {
2231
+ removeThreeChild(closestInstance, renderChild, true);
2232
+ }
2088
2233
  }
2089
2234
  this.destroy(renderChild, parent);
2090
2235
  }
2091
- rS[3 /* NgtRendererClassId.children */] = [];
2092
- rS[4 /* NgtRendererClassId.destroyed */] = true;
2236
+ rS[NgtRendererClassId.children] = [];
2237
+ rS[NgtRendererClassId.destroyed] = true;
2093
2238
  if (parent) {
2094
2239
  this.removeChild(parent, node);
2095
2240
  }
2096
2241
  }
2242
+ updateNativeProps(node, key, value) {
2243
+ const localState = getLocalState(node);
2244
+ if (!localState || !localState.nativeProps)
2245
+ return;
2246
+ localState.nativeProps.set({ [key]: value });
2247
+ }
2097
2248
  firstNonInjectedDirective(dir) {
2098
2249
  let directive;
2099
- let i = this.#comments.length - 1;
2250
+ const destroyed = [];
2251
+ let i = this.comments.length - 1;
2100
2252
  while (i >= 0) {
2101
- const comment = this.#comments[i];
2102
- if (comment.__ngt_renderer__[4 /* NgtRendererClassId.destroyed */]) {
2253
+ const comment = this.comments[i];
2254
+ if (comment.__ngt_renderer__[NgtRendererClassId.destroyed]) {
2255
+ destroyed.push(i);
2103
2256
  i--;
2104
2257
  continue;
2105
2258
  }
2106
- const injector = comment.__ngt_renderer__[14 /* NgtRendererClassId.injectorFactory */]();
2259
+ const injector = comment.__ngt_renderer__[NgtRendererClassId.injectorFactory]();
2107
2260
  if (!injector) {
2108
2261
  i--;
2109
2262
  continue;
@@ -2115,73 +2268,85 @@ class NgtRendererStore {
2115
2268
  }
2116
2269
  i--;
2117
2270
  }
2271
+ destroyed.forEach((index) => {
2272
+ this.comments.splice(index, 1);
2273
+ });
2118
2274
  return directive;
2119
2275
  }
2120
2276
  tryGetPortalStore() {
2121
2277
  let store;
2278
+ const destroyed = [];
2122
2279
  // we only care about the portal states because NgtStore only differs per Portal
2123
2280
  let i = this.portals.length - 1;
2124
2281
  while (i >= 0) {
2125
2282
  // loop through the portal state backwards to find the closest NgtStore
2126
2283
  const portal = this.portals[i];
2127
- if (portal.__ngt_renderer__[4 /* NgtRendererClassId.destroyed */]) {
2284
+ if (portal.__ngt_renderer__[NgtRendererClassId.destroyed]) {
2285
+ destroyed.push(i);
2128
2286
  i--;
2129
2287
  continue;
2130
2288
  }
2131
- const injector = portal.__ngt_renderer__[14 /* NgtRendererClassId.injectorFactory */]();
2289
+ const injector = portal.__ngt_renderer__[NgtRendererClassId.injectorFactory]();
2132
2290
  if (!injector) {
2133
2291
  i--;
2134
2292
  continue;
2135
2293
  }
2136
- const instance = injector.get(NgtStore, null);
2137
- // only the instance with previousStore should pass
2138
- if (instance && instance.get('previousStore')) {
2294
+ const instance = injector.get(NGT_STORE, null);
2295
+ // only the instance with previousRoot should pass
2296
+ if (instance && instance.get('previousRoot')) {
2139
2297
  store = instance;
2140
2298
  break;
2141
2299
  }
2142
2300
  i--;
2143
2301
  }
2302
+ destroyed.forEach((index) => {
2303
+ this.portals.splice(index, 1);
2304
+ });
2144
2305
  return store || this.root.store;
2145
2306
  }
2146
2307
  }
2147
2308
 
2148
2309
  class NgtRendererFactory {
2149
- #delegateRendererFactory = inject(RendererFactory2, { skipSelf: true });
2150
- #catalogue = inject(NGT_CATALOGUE);
2151
- #zone = inject(NgZone);
2152
- #cdr = inject(ChangeDetectorRef);
2153
- #rendererMap = new Map();
2154
- #routedSet = new Set();
2155
- // all Renderer instances share the same Store
2156
- #rendererStore = new NgtRendererStore({
2157
- portals: [],
2158
- store: inject(NgtStore),
2159
- compoundPrefixes: inject(NGT_COMPOUND_PREFIXES),
2160
- document: inject(DOCUMENT),
2161
- });
2310
+ constructor() {
2311
+ this.delegateRendererFactory = inject(RendererFactory2, { skipSelf: true });
2312
+ this.zone = inject(NgZone);
2313
+ this.catalogue = injectNgtCatalogue();
2314
+ this.cdr = inject(ChangeDetectorRef);
2315
+ this.rendererMap = new Map();
2316
+ this.routedSet = new Set();
2317
+ // all Renderer instances share the same Store
2318
+ this.rendererStore = new NgtRendererStore({
2319
+ portals: [],
2320
+ store: injectNgtStore(),
2321
+ compoundPrefixes: inject(NGT_COMPOUND_PREFIXES),
2322
+ document: inject(DOCUMENT),
2323
+ });
2324
+ }
2162
2325
  createRenderer(hostElement, type) {
2163
- const delegateRenderer = this.#delegateRendererFactory.createRenderer(hostElement, type);
2326
+ const delegate = this.delegateRendererFactory.createRenderer(hostElement, type);
2164
2327
  if (!type)
2165
- return delegateRenderer;
2166
- // if ((type as NgtAnyRecord)['type']['isHtml']) {
2167
- // return delegateRenderer;
2168
- // }
2328
+ return delegate;
2329
+ // TODO: handle html in canvas
2330
+ if (type['type'][HTML]) {
2331
+ this.rendererMap.set(type.id, delegate);
2332
+ return delegate;
2333
+ }
2169
2334
  if (type['type'][ROUTED_SCENE]) {
2170
- this.#routedSet.add(type.id);
2335
+ this.routedSet.add(type.id);
2171
2336
  }
2172
- let renderer = this.#rendererMap.get(type.id);
2337
+ let renderer = this.rendererMap.get(type.id);
2173
2338
  if (!renderer) {
2174
- renderer = new NgtRenderer(delegateRenderer, this.#rendererStore, this.#catalogue, this.#zone, this.#cdr,
2339
+ renderer = new NgtRenderer(delegate, this.rendererStore, this.catalogue, this.zone, this.cdr,
2175
2340
  // setting root scene if there's no routed scene OR this component is the routed Scene
2176
- !hostElement && (this.#routedSet.size === 0 || this.#routedSet.has(type.id)));
2177
- this.#rendererMap.set(type.id, renderer);
2341
+ !hostElement && (this.routedSet.size === 0 || this.routedSet.has(type.id)));
2342
+ this.rendererMap.set(type.id, renderer);
2178
2343
  }
2179
2344
  return renderer;
2180
2345
  }
2181
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRendererFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2182
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRendererFactory }); }
2346
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtRendererFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2347
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtRendererFactory }); }
2183
2348
  }
2184
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRendererFactory, decorators: [{
2349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtRendererFactory, decorators: [{
2185
2350
  type: Injectable
2186
2351
  }] });
2187
2352
  /**
@@ -2213,7 +2378,7 @@ class NgtRenderer {
2213
2378
  if (this.root) {
2214
2379
  this.root = false;
2215
2380
  const node = this.store.createNode('three', this.store.rootScene);
2216
- node.__ngt_renderer__[14 /* NgtRendererClassId.injectorFactory */] = () => getDebugNode(element).injector;
2381
+ node.__ngt_renderer__[NgtRendererClassId.injectorFactory] = () => getDebugNode(element).injector;
2217
2382
  return node;
2218
2383
  }
2219
2384
  // handle compound
@@ -2226,7 +2391,10 @@ class NgtRenderer {
2226
2391
  }
2227
2392
  // handle raw value
2228
2393
  if (name === SPECIAL_DOM_TAG.NGT_VALUE) {
2229
- return this.store.createNode('three', Object.assign({ __ngt_renderer__: { rawValue: undefined } }, { __ngt__: { isRaw: true, parent: createSignal(null) } }));
2394
+ return this.store.createNode('three', Object.assign({ __ngt_renderer__: { rawValue: undefined } },
2395
+ // NOTE: we assign this manually to a raw value node
2396
+ // because we say it is a 'three' node but we're not using prepare()
2397
+ { __ngt__: { isRaw: true, parent: signal(null) } }));
2230
2398
  }
2231
2399
  const { injectedArgs, injectedParent, store } = this.store.getCreationState();
2232
2400
  let parent = injectedParent;
@@ -2242,14 +2410,14 @@ class NgtRenderer {
2242
2410
  const object = injectedArgs[0];
2243
2411
  let localState = getLocalState(object);
2244
2412
  if (!Object.keys(localState).length) {
2245
- prepare(object, { store, args: injectedArgs, primitive: true });
2246
- localState = getLocalState(object);
2413
+ // NOTE: if an object isn't already "prepared", we prepare it
2414
+ localState = getLocalState(prepare(object, { store, args: injectedArgs, primitive: true }));
2247
2415
  }
2248
2416
  if (!localState.store)
2249
2417
  localState.store = store;
2250
2418
  const node = this.store.createNode('three', object);
2251
2419
  if (parent) {
2252
- node.__ngt_renderer__[2 /* NgtRendererClassId.injectedParent */] = parent;
2420
+ node.__ngt_renderer__[NgtRendererClassId.injectedParent] = parent;
2253
2421
  }
2254
2422
  return node;
2255
2423
  }
@@ -2269,7 +2437,7 @@ class NgtRenderer {
2269
2437
  localState.attach = ['material'];
2270
2438
  }
2271
2439
  if (parent) {
2272
- node.__ngt_renderer__[2 /* NgtRendererClassId.injectedParent */] = parent;
2440
+ node.__ngt_renderer__[NgtRendererClassId.injectedParent] = parent;
2273
2441
  }
2274
2442
  return node;
2275
2443
  }
@@ -2279,24 +2447,36 @@ class NgtRenderer {
2279
2447
  return this.store.createNode('comment', this.delegate.createComment(value));
2280
2448
  }
2281
2449
  appendChild(parent, newChild) {
2282
- // TODO: just ignore text node for now
2283
- if (newChild instanceof Text)
2284
- return;
2285
- const cRS = newChild.__ngt_renderer__;
2286
2450
  const pRS = parent.__ngt_renderer__;
2287
- if (cRS[0 /* NgtRendererClassId.type */] === 'comment') {
2451
+ const cRS = newChild.__ngt_renderer__;
2452
+ if (pRS[NgtRendererClassId.type] === 'dom' &&
2453
+ (newChild instanceof Text || cRS[NgtRendererClassId.type] === 'dom')) {
2454
+ this.store.addChild(parent, newChild);
2455
+ this.delegate.appendChild(parent, newChild);
2456
+ if (cRS) {
2457
+ this.store.setParent(newChild, parent);
2458
+ if (this.shouldFindGrandparentInstance(pRS, cRS, newChild)) {
2459
+ // we'll try to get the grandparent instance here so that we can run appendChild with both instances
2460
+ const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
2461
+ if (closestGrandparentInstance)
2462
+ this.appendChild(closestGrandparentInstance, newChild);
2463
+ }
2464
+ }
2465
+ return;
2466
+ }
2467
+ if (cRS?.[NgtRendererClassId.type] === 'comment') {
2288
2468
  this.store.setParent(newChild, parent);
2289
2469
  return;
2290
2470
  }
2291
- if (cRS[2 /* NgtRendererClassId.injectedParent */]) {
2292
- if (is.ref(cRS[2 /* NgtRendererClassId.injectedParent */])) {
2293
- const injector = cRS[14 /* NgtRendererClassId.injectorFactory */]().get(Injector, null);
2471
+ if (cRS?.[NgtRendererClassId.injectedParent]) {
2472
+ if (is.ref(cRS[NgtRendererClassId.injectedParent])) {
2473
+ const injector = cRS[NgtRendererClassId.injectorFactory]().get(Injector, null);
2294
2474
  if (!injector) {
2295
2475
  console.warn(`[NGT] NgtRenderer is attempting to start an effect for injectedParent but no Injector is found.`);
2296
2476
  return;
2297
2477
  }
2298
2478
  const watcher = effect(() => {
2299
- const injectedParent = cRS[2 /* NgtRendererClassId.injectedParent */].nativeElement;
2479
+ const injectedParent = cRS[NgtRendererClassId.injectedParent].nativeElement;
2300
2480
  if (injectedParent && injectedParent !== parent) {
2301
2481
  this.appendChild(injectedParent, newChild);
2302
2482
  // only run this effect once
@@ -2306,38 +2486,38 @@ class NgtRenderer {
2306
2486
  }, { injector, manualCleanup: true });
2307
2487
  return;
2308
2488
  }
2309
- else if (parent !== cRS[2 /* NgtRendererClassId.injectedParent */]) {
2310
- this.appendChild(cRS[2 /* NgtRendererClassId.injectedParent */], newChild);
2489
+ else if (parent !== cRS[NgtRendererClassId.injectedParent]) {
2490
+ this.appendChild(cRS[NgtRendererClassId.injectedParent], newChild);
2311
2491
  return;
2312
2492
  }
2313
2493
  }
2314
2494
  this.store.setParent(newChild, parent);
2315
2495
  this.store.addChild(parent, newChild);
2316
2496
  // if new child is a portal
2317
- if (cRS[0 /* NgtRendererClassId.type */] === 'portal') {
2497
+ if (cRS?.[NgtRendererClassId.type] === 'portal') {
2318
2498
  this.store.processPortalContainer(newChild);
2319
- if (cRS[13 /* NgtRendererClassId.portalContainer */]) {
2320
- this.appendChild(parent, cRS[13 /* NgtRendererClassId.portalContainer */]);
2499
+ if (cRS[NgtRendererClassId.portalContainer]) {
2500
+ this.appendChild(parent, cRS[NgtRendererClassId.portalContainer]);
2321
2501
  }
2322
2502
  return;
2323
2503
  }
2324
2504
  // if parent is a portal
2325
- if (pRS[0 /* NgtRendererClassId.type */] === 'portal') {
2505
+ if (pRS[NgtRendererClassId.type] === 'portal') {
2326
2506
  this.store.processPortalContainer(parent);
2327
- if (pRS[13 /* NgtRendererClassId.portalContainer */]) {
2328
- this.appendChild(pRS[13 /* NgtRendererClassId.portalContainer */], newChild);
2507
+ if (pRS[NgtRendererClassId.portalContainer]) {
2508
+ this.appendChild(pRS[NgtRendererClassId.portalContainer], newChild);
2329
2509
  }
2330
2510
  return;
2331
2511
  }
2332
2512
  // if both are three instances, straightforward case
2333
- if (pRS[0 /* NgtRendererClassId.type */] === 'three' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
2513
+ if (pRS[NgtRendererClassId.type] === 'three' && cRS?.[NgtRendererClassId.type] === 'three') {
2334
2514
  // if child already attached to a parent, skip
2335
2515
  if (getLocalState(newChild).parent && untracked(getLocalState(newChild).parent))
2336
2516
  return;
2337
2517
  // attach THREE child
2338
2518
  attachThreeChild(parent, newChild);
2339
2519
  // here, we handle the special case of if the parent has a compoundParent, which means this child is part of a compound parent template
2340
- if (!cRS[5 /* NgtRendererClassId.compound */])
2520
+ if (!cRS[NgtRendererClassId.compound])
2341
2521
  return;
2342
2522
  const closestGrandparentWithCompound = this.store.getClosestParentWithCompound(parent);
2343
2523
  if (!closestGrandparentWithCompound)
@@ -2346,51 +2526,39 @@ class NgtRenderer {
2346
2526
  return;
2347
2527
  }
2348
2528
  // if only the parent is the THREE instance
2349
- if (pRS[0 /* NgtRendererClassId.type */] === 'three') {
2350
- for (const renderChild of cRS[3 /* NgtRendererClassId.children */]) {
2529
+ if (pRS[NgtRendererClassId.type] === 'three') {
2530
+ for (const renderChild of cRS?.[NgtRendererClassId.children]) {
2351
2531
  this.appendChild(parent, renderChild);
2352
2532
  }
2353
2533
  }
2354
2534
  // if parent is a compound
2355
- if (pRS[0 /* NgtRendererClassId.type */] === 'compound') {
2535
+ if (pRS[NgtRendererClassId.type] === 'compound') {
2356
2536
  // if compound doesn't have a THREE instance set yet
2357
- if (!pRS[7 /* NgtRendererClassId.compounded */] && cRS[0 /* NgtRendererClassId.type */] === 'three') {
2537
+ if (!pRS[NgtRendererClassId.compounded] && cRS[NgtRendererClassId.type] === 'three') {
2358
2538
  // if child is indeed an ngtCompound
2359
- if (cRS[5 /* NgtRendererClassId.compound */])
2539
+ if (cRS[NgtRendererClassId.compound])
2360
2540
  this.store.setCompound(parent, newChild);
2361
2541
  // if not, we track the parent (that is supposedly the compound component) on this three instance
2362
- else if (!cRS[6 /* NgtRendererClassId.compoundParent */])
2363
- cRS[6 /* NgtRendererClassId.compoundParent */] = parent;
2542
+ else if (!cRS[NgtRendererClassId.compoundParent])
2543
+ cRS[NgtRendererClassId.compoundParent] = parent;
2364
2544
  }
2365
2545
  // reset the compound if it's changed
2366
- if (pRS[7 /* NgtRendererClassId.compounded */] &&
2367
- cRS[0 /* NgtRendererClassId.type */] === 'three' &&
2368
- cRS[5 /* NgtRendererClassId.compound */] &&
2369
- pRS[7 /* NgtRendererClassId.compounded */] !== newChild) {
2546
+ if (pRS[NgtRendererClassId.compounded] &&
2547
+ cRS[NgtRendererClassId.type] === 'three' &&
2548
+ cRS[NgtRendererClassId.compound] &&
2549
+ pRS[NgtRendererClassId.compounded] !== newChild) {
2370
2550
  this.store.setCompound(parent, newChild);
2371
2551
  }
2372
2552
  }
2373
- const shouldFindGrandparentInstance =
2374
- // if child is three but haven't been attached to a parent yet
2375
- (cRS[0 /* NgtRendererClassId.type */] === 'three' && !untracked(getLocalState(newChild).parent)) ||
2376
- // or both parent and child are DOM elements
2377
- // or they are compound AND haven't had a THREE instance yet
2378
- ((pRS[0 /* NgtRendererClassId.type */] === 'dom' ||
2379
- (pRS[0 /* NgtRendererClassId.type */] === 'compound' && !pRS[7 /* NgtRendererClassId.compounded */])) &&
2380
- (cRS[0 /* NgtRendererClassId.type */] === 'dom' ||
2381
- (cRS[0 /* NgtRendererClassId.type */] === 'compound' && !cRS[7 /* NgtRendererClassId.compounded */])));
2382
- if (shouldFindGrandparentInstance) {
2553
+ if (this.shouldFindGrandparentInstance(pRS, cRS, newChild)) {
2383
2554
  // we'll try to get the grandparent instance here so that we can run appendChild with both instances
2384
2555
  const closestGrandparentInstance = this.store.getClosestParentWithInstance(parent);
2385
2556
  if (closestGrandparentInstance)
2386
2557
  this.appendChild(closestGrandparentInstance, newChild);
2558
+ return;
2387
2559
  }
2388
2560
  }
2389
- insertBefore(parent, newChild
2390
- // TODO: we might need these?
2391
- // refChild: NgtRendererNode
2392
- // isMove?: boolean | undefined
2393
- ) {
2561
+ insertBefore(parent, newChild) {
2394
2562
  if (parent == null || !parent.__ngt_renderer__ || parent === newChild)
2395
2563
  return;
2396
2564
  this.appendChild(parent, newChild);
@@ -2398,16 +2566,28 @@ class NgtRenderer {
2398
2566
  removeChild(parent, oldChild, isHostElement) {
2399
2567
  const pRS = parent.__ngt_renderer__;
2400
2568
  const cRS = oldChild.__ngt_renderer__;
2401
- if (pRS[0 /* NgtRendererClassId.type */] === 'three' && cRS[0 /* NgtRendererClassId.type */] === 'three') {
2569
+ if ((!cRS || !pRS) &&
2570
+ parent instanceof Element &&
2571
+ (oldChild instanceof Element || oldChild instanceof Text || oldChild instanceof Comment)) {
2572
+ this.delegate.removeChild(parent, oldChild);
2573
+ this.store.destroy(oldChild, parent);
2574
+ return;
2575
+ }
2576
+ if (cRS[NgtRendererClassId.type] === 'dom' && (!pRS || pRS[NgtRendererClassId.type] === 'dom')) {
2577
+ this.delegate.removeChild(parent, oldChild);
2578
+ this.store.destroy(oldChild, parent);
2579
+ return;
2580
+ }
2581
+ if (pRS[NgtRendererClassId.type] === 'three' && cRS[NgtRendererClassId.type] === 'three') {
2402
2582
  removeThreeChild(parent, oldChild, true);
2403
2583
  this.store.destroy(oldChild, parent);
2404
2584
  return;
2405
2585
  }
2406
- if (pRS[0 /* NgtRendererClassId.type */] === 'compound' && pRS[1 /* NgtRendererClassId.parent */]) {
2407
- this.removeChild(pRS[1 /* NgtRendererClassId.parent */], oldChild, isHostElement);
2586
+ if (pRS[NgtRendererClassId.type] === 'compound' && pRS[NgtRendererClassId.parent]) {
2587
+ this.removeChild(pRS[NgtRendererClassId.parent], oldChild, isHostElement);
2408
2588
  return;
2409
2589
  }
2410
- if (pRS[0 /* NgtRendererClassId.type */] === 'three') {
2590
+ if (pRS[NgtRendererClassId.type] === 'three') {
2411
2591
  this.store.destroy(oldChild, parent);
2412
2592
  return;
2413
2593
  }
@@ -2418,44 +2598,49 @@ class NgtRenderer {
2418
2598
  }
2419
2599
  parentNode(node) {
2420
2600
  const rS = node.__ngt_renderer__;
2421
- if (rS?.[1 /* NgtRendererClassId.parent */])
2422
- return rS[1 /* NgtRendererClassId.parent */];
2601
+ if (rS?.[NgtRendererClassId.parent])
2602
+ return rS[NgtRendererClassId.parent];
2423
2603
  return this.delegate.parentNode(node);
2424
2604
  }
2425
2605
  setAttribute(el, name, value, namespace) {
2426
2606
  const rS = el.__ngt_renderer__;
2427
- if (rS[0 /* NgtRendererClassId.type */] === 'compound') {
2607
+ if (rS[NgtRendererClassId.type] === 'compound') {
2428
2608
  // we don't have the compound instance yet
2429
- rS[9 /* NgtRendererClassId.attributes */][name] = value;
2430
- if (!rS[7 /* NgtRendererClassId.compounded */]) {
2609
+ rS[NgtRendererClassId.attributes][name] = value;
2610
+ if (!rS[NgtRendererClassId.compounded]) {
2431
2611
  this.store.queueOperation(el, ['op', () => this.setAttribute(el, name, value, namespace)]);
2432
2612
  return;
2433
2613
  }
2434
- this.setAttribute(rS[7 /* NgtRendererClassId.compounded */], name, value, namespace);
2614
+ this.setAttribute(rS[NgtRendererClassId.compounded], name, value, namespace);
2435
2615
  return;
2436
2616
  }
2437
- if (rS[0 /* NgtRendererClassId.type */] === 'three')
2617
+ if (rS[NgtRendererClassId.type] === 'three') {
2438
2618
  this.store.applyAttribute(el, name, value);
2619
+ return;
2620
+ }
2621
+ return this.delegate.setAttribute(el, name, value);
2439
2622
  }
2440
2623
  setProperty(el, name, value) {
2624
+ // TODO: should we support ref value
2441
2625
  const rS = el.__ngt_renderer__;
2442
- if (rS[0 /* NgtRendererClassId.type */] === 'compound') {
2626
+ if (rS[NgtRendererClassId.type] === 'compound') {
2443
2627
  // we don't have the compound instance yet
2444
- rS[10 /* NgtRendererClassId.properties */][name] = value;
2445
- if (!rS[7 /* NgtRendererClassId.compounded */]) {
2628
+ rS[NgtRendererClassId.properties][name] = value;
2629
+ if (!rS[NgtRendererClassId.compounded]) {
2446
2630
  this.store.queueOperation(el, ['op', () => this.setProperty(el, name, value)]);
2447
2631
  return;
2448
2632
  }
2449
- if (rS[7 /* NgtRendererClassId.compounded */].__ngt_renderer__[5 /* NgtRendererClassId.compound */]) {
2450
- Object.assign(rS[7 /* NgtRendererClassId.compounded */].__ngt_renderer__[5 /* NgtRendererClassId.compound */], {
2451
- props: Object.assign(rS[7 /* NgtRendererClassId.compounded */].__ngt_renderer__[5 /* NgtRendererClassId.compound */], { [name]: value }),
2452
- });
2633
+ if (rS[NgtRendererClassId.compounded].__ngt_renderer__[NgtRendererClassId.compound]) {
2634
+ Object.assign(rS[NgtRendererClassId.compounded].__ngt_renderer__[NgtRendererClassId.compound][NgtCompoundClassId.props], { [name]: value });
2453
2635
  }
2454
- this.setProperty(rS[7 /* NgtRendererClassId.compounded */], name, value);
2636
+ this.setProperty(rS[NgtRendererClassId.compounded], name, value);
2455
2637
  return;
2456
2638
  }
2457
- if (rS[0 /* NgtRendererClassId.type */] === 'three')
2639
+ if (rS[NgtRendererClassId.type] === 'three') {
2458
2640
  this.store.applyProperty(el, name, value);
2641
+ return;
2642
+ }
2643
+ return this.delegate.setProperty(el, name, value);
2459
2644
  }
2460
2645
  listen(target, eventName, callback) {
2461
2646
  const rS = target.__ngt_renderer__;
@@ -2464,15 +2649,15 @@ class NgtRenderer {
2464
2649
  if (!rS || this.store.isDOM(target)) {
2465
2650
  return this.delegate.listen(target, eventName, callback);
2466
2651
  }
2467
- if (rS[0 /* NgtRendererClassId.type */] === 'three' ||
2468
- (rS[0 /* NgtRendererClassId.type */] === 'compound' && rS[7 /* NgtRendererClassId.compounded */])) {
2469
- const instance = rS[7 /* NgtRendererClassId.compounded */] || target;
2652
+ if (rS[NgtRendererClassId.type] === 'three' ||
2653
+ (rS[NgtRendererClassId.type] === 'compound' && rS[NgtRendererClassId.compounded])) {
2654
+ const instance = rS[NgtRendererClassId.compounded] || target;
2470
2655
  const priority = getLocalState(target).priority;
2471
- const targetCdr = rS[14 /* NgtRendererClassId.injectorFactory */]?.().get(ChangeDetectorRef, null) ||
2472
- rS[1 /* NgtRendererClassId.parent */]?.__ngt_renderer__?.[14 /* NgtRendererClassId.injectorFactory */]?.().get(ChangeDetectorRef, null);
2656
+ const targetCdr = rS[NgtRendererClassId.injectorFactory]?.().get(ChangeDetectorRef, null) ||
2657
+ rS[NgtRendererClassId.parent]?.__ngt_renderer__?.[NgtRendererClassId.injectorFactory]?.().get(ChangeDetectorRef, null);
2473
2658
  return processThreeEvent(instance, priority || 0, eventName, callback, this.zone, this.cdr, targetCdr);
2474
2659
  }
2475
- if (rS[0 /* NgtRendererClassId.type */] === 'compound' && !rS[7 /* NgtRendererClassId.compounded */]) {
2660
+ if (rS[NgtRendererClassId.type] === 'compound' && !rS[NgtRendererClassId.compounded]) {
2476
2661
  this.store.queueOperation(target, [
2477
2662
  'op',
2478
2663
  () => this.store.queueOperation(target, ['cleanUp', this.listen(target, eventName, callback)]),
@@ -2493,171 +2678,165 @@ class NgtRenderer {
2493
2678
  }
2494
2679
  return () => { };
2495
2680
  }
2681
+ shouldFindGrandparentInstance(pRS, cRS, child) {
2682
+ const pType = pRS[NgtRendererClassId.type];
2683
+ const cType = cRS[NgtRendererClassId.type];
2684
+ const isParentCompounded = pRS[NgtRendererClassId.compounded];
2685
+ const isChildCompounded = cRS[NgtRendererClassId.compounded];
2686
+ // if child is three but haven't been attached to a parent yet
2687
+ const isDanglingThreeChild = cType === 'three' && !untracked(getLocalState(child).parent);
2688
+ // or both parent and child are DOM elements
2689
+ // or they are compound AND haven't had a THREE instance yet
2690
+ const isParentStillDOM = pType === 'dom' || (pType === 'compound' && !isParentCompounded);
2691
+ const isChildStillDOM = cType === 'dom' || (cType === 'compound' && !isChildCompounded);
2692
+ // and the child is a compounded compound
2693
+ const isCompoundChildCompounded = cType === 'compound' && !!isChildCompounded;
2694
+ return (isDanglingThreeChild ||
2695
+ (isParentStillDOM && isChildStillDOM) ||
2696
+ (isParentStillDOM && isCompoundChildCompounded));
2697
+ }
2496
2698
  get data() {
2497
2699
  return this.delegate.data;
2498
2700
  }
2499
2701
  }
2500
-
2501
- function provideNgtRenderer({ store, changeDetectorRef, compoundPrefixes = [] }) {
2702
+ function provideNgtRenderer(store, compoundPrefixes, cdr) {
2502
2703
  if (!compoundPrefixes.includes('ngts'))
2503
2704
  compoundPrefixes.push('ngts');
2504
2705
  if (!compoundPrefixes.includes('ngtp'))
2505
2706
  compoundPrefixes.push('ngtp');
2506
2707
  return makeEnvironmentProviders([
2507
2708
  { provide: RendererFactory2, useClass: NgtRendererFactory },
2508
- { provide: NgtStore, useValue: store },
2509
- { provide: ChangeDetectorRef, useValue: changeDetectorRef },
2510
2709
  { provide: NGT_COMPOUND_PREFIXES, useValue: compoundPrefixes },
2710
+ { provide: ChangeDetectorRef, useValue: cdr },
2711
+ provideNgtStore(store),
2511
2712
  provideZoneChangeDetection({ runCoalescing: true, eventCoalescing: true }),
2512
2713
  ]);
2513
2714
  }
2514
2715
 
2515
- class NgtCanvas extends NgtSignalStore {
2516
- #envInjector;
2517
- #injector;
2518
- #host;
2519
- #zone;
2520
- #cdr;
2521
- #store;
2522
- #isReady;
2716
+ class NgtCanvas {
2717
+ constructor() {
2718
+ this.store = injectNgtStore();
2719
+ this.initRoot = injectCanvasRootInitializer();
2720
+ this.host = inject(ElementRef);
2721
+ this.viewContainerRef = inject(ViewContainerRef);
2722
+ this.injector = inject(Injector);
2723
+ this.environmentInjector = inject(EnvironmentInjector);
2724
+ this.zone = inject(NgZone);
2725
+ this.destroyRef = inject(DestroyRef);
2726
+ this.cdr = inject(ChangeDetectorRef);
2727
+ this.inputs = signalStore({
2728
+ shadows: false,
2729
+ linear: false,
2730
+ flat: false,
2731
+ legacy: false,
2732
+ orthographic: false,
2733
+ frameloop: 'always',
2734
+ dpr: [1, 2],
2735
+ events: createPointerEvents,
2736
+ });
2737
+ this.sceneGraphInputs = {};
2738
+ this.compoundPrefixes = [];
2739
+ this.created = new EventEmitter();
2740
+ this.inputsEventSource = this.inputs.select('eventSource');
2741
+ this.hbPointerEvents = computed(() => (!!this.inputsEventSource() ? 'none' : 'auto'));
2742
+ }
2523
2743
  set linear(linear) {
2524
- this.set({ linear });
2744
+ this.inputs.set({ linear });
2525
2745
  }
2526
2746
  set legacy(legacy) {
2527
- this.set({ legacy });
2747
+ this.inputs.set({ legacy });
2528
2748
  }
2529
2749
  set flat(flat) {
2530
- this.set({ flat });
2750
+ this.inputs.set({ flat });
2531
2751
  }
2532
2752
  set orthographic(orthographic) {
2533
- this.set({ orthographic });
2753
+ this.inputs.set({ orthographic });
2534
2754
  }
2535
2755
  set frameloop(frameloop) {
2536
- this.set({ frameloop });
2756
+ this.inputs.set({ frameloop });
2537
2757
  }
2538
2758
  set dpr(dpr) {
2539
- this.set({ dpr });
2759
+ this.inputs.set({ dpr });
2540
2760
  }
2541
2761
  set raycaster(raycaster) {
2542
- this.set({ raycaster });
2762
+ this.inputs.set({ raycaster });
2543
2763
  }
2544
2764
  set shadows(shadows) {
2545
- this.set({ shadows });
2765
+ this.inputs.set({ shadows });
2546
2766
  }
2547
2767
  set camera(camera) {
2548
- this.set({ camera });
2768
+ this.inputs.set({ camera });
2549
2769
  }
2550
2770
  set scene(scene) {
2551
- this.set({ scene });
2771
+ this.inputs.set({ scene });
2552
2772
  }
2553
2773
  set gl(gl) {
2554
- this.set({ gl });
2774
+ this.inputs.set({ gl });
2555
2775
  }
2556
2776
  set eventSource(eventSource) {
2557
- this.set({ eventSource });
2777
+ this.inputs.set({ eventSource });
2558
2778
  }
2559
2779
  set eventPrefix(eventPrefix) {
2560
- this.set({ eventPrefix });
2780
+ this.inputs.set({ eventPrefix });
2561
2781
  }
2562
2782
  set lookAt(lookAt) {
2563
- this.set({ lookAt });
2783
+ this.inputs.set({ lookAt });
2564
2784
  }
2565
2785
  set performance(performance) {
2566
- this.set({ performance });
2567
- }
2568
- #glRef;
2569
- #glEnvInjector;
2570
- constructor() {
2571
- super({
2572
- shadows: false,
2573
- linear: false,
2574
- flat: false,
2575
- legacy: false,
2576
- orthographic: false,
2577
- frameloop: 'always',
2578
- dpr: [1, 2],
2579
- events: createPointerEvents,
2580
- });
2581
- this.#envInjector = inject(EnvironmentInjector);
2582
- this.#injector = inject(Injector);
2583
- this.#host = inject(ElementRef);
2584
- this.#zone = inject(NgZone);
2585
- this.#cdr = inject(ChangeDetectorRef);
2586
- this.#store = inject(NgtStore);
2587
- this.#isReady = this.#store.select('ready');
2588
- this.sceneGraphInputs = {};
2589
- this.compoundPrefixes = [];
2590
- this.created = new EventEmitter();
2591
- this.pointerMissed = new EventEmitter();
2592
- inject(DestroyRef).onDestroy(() => {
2593
- if (this.#glRef)
2594
- this.#glRef.destroy();
2595
- if (this.#glEnvInjector)
2596
- this.#glEnvInjector.destroy();
2597
- injectNgtLoader.destroy();
2598
- this.#store.destroy(this.glCanvas.nativeElement);
2599
- });
2600
- }
2601
- get hbPointerEvents() {
2602
- return this.select('eventSource')() !== this.#host.nativeElement ? 'none' : 'auto';
2786
+ this.inputs.set({ performance });
2603
2787
  }
2604
2788
  ngOnChanges(changes) {
2605
- if (changes['sceneGraphInputs'] && !changes['sceneGraphInputs'].firstChange && this.#glRef) {
2606
- this.#setSceneGraphInputs();
2789
+ if (changes['sceneGraphInputs'] && !changes['sceneGraphInputs'].firstChange && this.glRef) {
2790
+ this.setSceneGraphInputs();
2607
2791
  }
2608
2792
  }
2609
2793
  ngOnInit() {
2610
- if (!this.get('eventSource')) {
2611
- // set default event source to the host element
2612
- this.set({ eventSource: this.#host.nativeElement });
2613
- }
2614
- if (this.pointerMissed.observed) {
2615
- this.#store.set({
2616
- onPointerMissed: (event) => {
2617
- this.pointerMissed.emit(event);
2618
- },
2619
- });
2620
- }
2621
- // setup NgtStore
2622
- this.#store.init();
2623
- // set rootStateMap
2624
- rootStateMap.set(this.glCanvas.nativeElement, this.#store);
2625
- // subscribe to store to listen for ready state
2626
- effect(() => {
2627
- this.#zone.runOutsideAngular(() => {
2628
- if (this.#isReady())
2629
- this.#storeReady();
2630
- });
2631
- }, { injector: this.#injector, allowSignalWrites: true });
2632
- }
2633
- #resizeRef;
2634
- // NOTE: this is invoked outside of Angular Zone
2635
- onResize({ width, height, top, left }) {
2636
- // destroy previous effect
2637
- if (this.#resizeRef) {
2638
- this.#resizeRef.destroy();
2639
- }
2640
- if (width > 0 && height > 0) {
2641
- if (!this.#store.isInit)
2642
- this.#store.init();
2643
- const inputs = this.select();
2644
- this.#resizeRef = this.#zone.run(() => effect(() => {
2645
- const canvasInputs = inputs();
2646
- this.#zone.runOutsideAngular(() => {
2647
- this.#store.configure({ ...canvasInputs, size: { width, height, top, left } }, this.glCanvas.nativeElement);
2794
+ // NOTE: we resolve glCanvas at this point, setup the configurator
2795
+ this.configurator = this.initRoot(this.glCanvas.nativeElement);
2796
+ this.destroyRef.onDestroy(() => {
2797
+ this.glEnvironmentInjector?.destroy();
2798
+ this.glRef?.destroy();
2799
+ this.resizeEffectRef?.destroy();
2800
+ injectNgtLoader.destroy();
2801
+ this.configurator?.destroy();
2802
+ });
2803
+ }
2804
+ // NOTE: runs outside of Zone due to emitInZone: false
2805
+ onResize(result) {
2806
+ if (result.width > 0 && result.height > 0) {
2807
+ this.resizeEffectRef?.destroy();
2808
+ const inputs = this.inputs.select();
2809
+ // NOTE: go back into zone so that effect runs
2810
+ // TODO: Double-check when effect is made not depended on zone
2811
+ this.resizeEffectRef = this.zone.run(() => effect(() => {
2812
+ this.zone.runOutsideAngular(() => {
2813
+ if (!this.configurator)
2814
+ this.configurator = this.initRoot(this.glCanvas.nativeElement);
2815
+ this.configurator.configure({ ...inputs(), size: result });
2816
+ if (this.glRef) {
2817
+ this.cdr.detectChanges();
2818
+ }
2819
+ else {
2820
+ this.render();
2821
+ }
2648
2822
  });
2649
- }, { injector: this.#injector, manualCleanup: true, allowSignalWrites: true }));
2823
+ }, { manualCleanup: true, injector: this.injector }));
2650
2824
  }
2651
2825
  }
2652
- // NOTE: This is invoked outside of Angular Zone
2653
- #storeReady() {
2654
- // canvas is ready, let's activate the loop
2655
- this.#store.set((state) => ({ internal: { ...state.internal, active: true } }));
2656
- const inputs = this.get();
2657
- const state = this.#store.get();
2826
+ render() {
2827
+ this.glEnvironmentInjector?.destroy();
2828
+ this.glRef?.destroy();
2829
+ // Flag the canvas active, rendering will now begin
2830
+ this.store.set((state) => ({ internal: { ...state.internal, active: true } }));
2831
+ const inputs = this.inputs.get();
2832
+ const state = this.store.get();
2658
2833
  // connect to event source
2659
- state.events.connect?.(is.ref(inputs.eventSource) ? inputs.eventSource.nativeElement : inputs.eventSource);
2660
- // setup compute function for events
2834
+ state.events.connect?.(inputs.eventSource
2835
+ ? is.ref(inputs.eventSource)
2836
+ ? inputs.eventSource.nativeElement
2837
+ : inputs.eventSource
2838
+ : this.host.nativeElement);
2839
+ // setup compute for eventPrefix
2661
2840
  if (inputs.eventPrefix) {
2662
2841
  state.setEvents({
2663
2842
  compute: (event, store) => {
@@ -2671,70 +2850,65 @@ class NgtCanvas extends NgtSignalStore {
2671
2850
  }
2672
2851
  // emit created event if observed
2673
2852
  if (this.created.observed) {
2674
- // but go back into zone to run it
2675
- this.#zone.run(() => {
2676
- this.created.emit(this.#store.get());
2853
+ // but go back into zone to do so
2854
+ this.zone.run(() => {
2855
+ this.created.emit(this.store.get());
2677
2856
  });
2678
2857
  }
2679
- // render
2680
- if (this.#glRef)
2681
- this.#glRef.destroy();
2682
- requestAnimationFrame(() => {
2683
- this.#glEnvInjector = createEnvironmentInjector([
2684
- provideNgtRenderer({
2685
- store: this.#store,
2686
- changeDetectorRef: this.#cdr,
2687
- compoundPrefixes: this.compoundPrefixes,
2688
- }),
2689
- ], this.#envInjector);
2690
- this.#glRef = this.glAnchor.createComponent(this.sceneGraph, {
2691
- environmentInjector: this.#glEnvInjector,
2692
- });
2693
- this.#setSceneGraphInputs();
2694
- this.#overrideChangeDetectorRef();
2695
- safeDetectChanges(this.#cdr);
2858
+ if (!this.store.get('events', 'connected')) {
2859
+ this.store.get('events').connect?.(this.glCanvas.nativeElement);
2860
+ }
2861
+ this.glEnvironmentInjector = createEnvironmentInjector([provideNgtRenderer(this.store, this.compoundPrefixes, this.cdr)], this.environmentInjector);
2862
+ this.glRef = this.viewContainerRef.createComponent(this.sceneGraph, {
2863
+ environmentInjector: this.glEnvironmentInjector,
2864
+ injector: this.injector,
2696
2865
  });
2866
+ this.overrideChangeDetectorRef();
2867
+ this.setSceneGraphInputs();
2697
2868
  }
2698
- #overrideChangeDetectorRef() {
2699
- const originalDetectChanges = this.#cdr.detectChanges.bind(this.#cdr);
2700
- this.#cdr.detectChanges = () => {
2869
+ overrideChangeDetectorRef() {
2870
+ const originalDetectChanges = this.cdr.detectChanges.bind(this.cdr);
2871
+ this.cdr.detectChanges = () => {
2701
2872
  originalDetectChanges();
2702
- safeDetectChanges(this.#glRef?.changeDetectorRef);
2873
+ safeDetectChanges(this.glRef?.changeDetectorRef);
2703
2874
  };
2704
2875
  }
2705
- #setSceneGraphInputs() {
2706
- this.#zone.run(() => {
2707
- if (this.#glRef) {
2876
+ setSceneGraphInputs() {
2877
+ this.zone.run(() => {
2878
+ if (this.glRef) {
2708
2879
  for (const [key, value] of Object.entries(this.sceneGraphInputs)) {
2709
- this.#glRef.setInput(key, value);
2880
+ this.glRef.setInput(key, value);
2710
2881
  }
2711
- safeDetectChanges(this.#glRef.changeDetectorRef);
2882
+ this.glRef.changeDetectorRef.detectChanges();
2712
2883
  }
2713
2884
  });
2714
2885
  }
2715
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtCanvas, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2716
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: NgtCanvas, isStandalone: true, selector: "ngt-canvas", inputs: { sceneGraph: "sceneGraph", sceneGraphInputs: "sceneGraphInputs", compoundPrefixes: "compoundPrefixes", linear: "linear", legacy: "legacy", flat: "flat", orthographic: "orthographic", frameloop: "frameloop", dpr: "dpr", raycaster: "raycaster", shadows: "shadows", camera: "camera", scene: "scene", gl: "gl", eventSource: "eventSource", eventPrefix: "eventPrefix", lookAt: "lookAt", performance: "performance" }, outputs: { created: "created", pointerMissed: "pointerMissed" }, host: { styleAttribute: "display: block;position: relative;width: 100%;height: 100%;overflow: hidden;" }, providers: [NgtStore, provideNgxResizeOptions({ emitInZone: false, emitInitialResult: true })], viewQueries: [{ propertyName: "glCanvas", first: true, predicate: ["glCanvas"], descendants: true, static: true }, { propertyName: "glAnchor", first: true, predicate: ["glCanvas"], descendants: true, read: ViewContainerRef, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: `
2717
- <div (ngxResize)="onResize($event)" style="height: 100%; width: 100%;">
2718
- <canvas #glCanvas style="display: block;"> </canvas>
2719
- </div>
2720
- `, isInline: true, dependencies: [{ kind: "directive", type: NgxResize, selector: "[ngxResize]", inputs: ["ngxResizeOptions"], outputs: ["ngxResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2886
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtCanvas, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2887
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.2", type: NgtCanvas, isStandalone: true, selector: "ngt-canvas", inputs: { sceneGraph: "sceneGraph", sceneGraphInputs: "sceneGraphInputs", compoundPrefixes: "compoundPrefixes", linear: "linear", legacy: "legacy", flat: "flat", orthographic: "orthographic", frameloop: "frameloop", dpr: "dpr", raycaster: "raycaster", shadows: "shadows", camera: "camera", scene: "scene", gl: "gl", eventSource: "eventSource", eventPrefix: "eventPrefix", lookAt: "lookAt", performance: "performance" }, outputs: { created: "created" }, host: { properties: { "style.pointerEvents": "hbPointerEvents()" }, styleAttribute: "display: block;position: relative;width: 100%;height: 100%;overflow: hidden;" }, providers: [provideNgxResizeOptions({ emitInZone: false, emitInitialResult: true }), provideNgtStore()], viewQueries: [{ propertyName: "glCanvas", first: true, predicate: ["glCanvas"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: `
2888
+ <div (ngxResize)="onResize($event)" style="height: 100%; width: 100%;">
2889
+ <canvas #glCanvas style="display: block;"></canvas>
2890
+ </div>
2891
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgxResize, selector: "[ngxResize]", inputs: ["ngxResizeOptions"], outputs: ["ngxResize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2721
2892
  }
2722
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtCanvas, decorators: [{
2893
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtCanvas, decorators: [{
2723
2894
  type: Component,
2724
2895
  args: [{
2725
2896
  selector: 'ngt-canvas',
2726
2897
  standalone: true,
2727
2898
  template: `
2728
- <div (ngxResize)="onResize($event)" style="height: 100%; width: 100%;">
2729
- <canvas #glCanvas style="display: block;"> </canvas>
2730
- </div>
2731
- `,
2899
+ <div (ngxResize)="onResize($event)" style="height: 100%; width: 100%;">
2900
+ <canvas #glCanvas style="display: block;"></canvas>
2901
+ </div>
2902
+ `,
2732
2903
  imports: [NgxResize],
2733
- providers: [NgtStore, provideNgxResizeOptions({ emitInZone: false, emitInitialResult: true })],
2734
- host: { style: 'display: block;position: relative;width: 100%;height: 100%;overflow: hidden;' },
2904
+ providers: [provideNgxResizeOptions({ emitInZone: false, emitInitialResult: true }), provideNgtStore()],
2905
+ host: {
2906
+ style: 'display: block;position: relative;width: 100%;height: 100%;overflow: hidden;',
2907
+ '[style.pointerEvents]': 'hbPointerEvents()',
2908
+ },
2735
2909
  changeDetection: ChangeDetectionStrategy.OnPush,
2736
2910
  }]
2737
- }], ctorParameters: function () { return []; }, propDecorators: { sceneGraph: [{
2911
+ }], propDecorators: { sceneGraph: [{
2738
2912
  type: Input,
2739
2913
  args: [{ required: true }]
2740
2914
  }], sceneGraphInputs: [{
@@ -2773,34 +2947,58 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImpor
2773
2947
  type: Input
2774
2948
  }], created: [{
2775
2949
  type: Output
2776
- }], pointerMissed: [{
2777
- type: Output
2778
2950
  }], glCanvas: [{
2779
2951
  type: ViewChild,
2780
2952
  args: ['glCanvas', { static: true }]
2781
- }], glAnchor: [{
2782
- type: ViewChild,
2783
- args: ['glCanvas', { static: true, read: ViewContainerRef }]
2784
2953
  }] } });
2785
2954
 
2786
- function injectBeforeRender(cb, { priority = 0, injector } = {}) {
2787
- injector = assertInjectionContext(injectBeforeRender, injector);
2788
- return runInInjectionContext(injector, () => {
2789
- const store = inject(NgtStore);
2790
- const sub = store.get('internal').subscribe(cb, priority, store);
2791
- inject(DestroyRef).onDestroy(() => void sub());
2792
- return sub;
2793
- });
2955
+ class NgtKey extends NgtCommonDirective {
2956
+ constructor() {
2957
+ super(...arguments);
2958
+ this.lastKey = '';
2959
+ }
2960
+ static { this.processComment = false; }
2961
+ validate() {
2962
+ return false;
2963
+ }
2964
+ set key(key) {
2965
+ const normalizedKey = JSON.stringify(key);
2966
+ if (this.lastKey !== normalizedKey) {
2967
+ this.lastKey = normalizedKey;
2968
+ this.createView();
2969
+ }
2970
+ }
2971
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtKey, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
2972
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.2", type: NgtKey, isStandalone: true, selector: "ng-template[key]", inputs: { key: "key" }, usesInheritance: true, ngImport: i0 }); }
2973
+ }
2974
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtKey, decorators: [{
2975
+ type: Directive,
2976
+ args: [{ selector: 'ng-template[key]', standalone: true }]
2977
+ }], propDecorators: { key: [{
2978
+ type: Input
2979
+ }] } });
2980
+
2981
+ class NgtRepeat extends NgForOf {
2982
+ set ngForRepeat(count) {
2983
+ this.ngForOf = Number.isInteger(count) ? Array.from({ length: count }, (_, i) => i) : [];
2984
+ }
2985
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtRepeat, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
2986
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.2", type: NgtRepeat, isStandalone: true, selector: "[ngFor][ngForRepeat]", inputs: { ngForRepeat: "ngForRepeat" }, usesInheritance: true, ngImport: i0 }); }
2794
2987
  }
2988
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtRepeat, decorators: [{
2989
+ type: Directive,
2990
+ args: [{ selector: '[ngFor][ngForRepeat]', standalone: true }]
2991
+ }], propDecorators: { ngForRepeat: [{
2992
+ type: Input
2993
+ }] } });
2795
2994
 
2796
2995
  function injectNgtRef(initial = null, injector) {
2797
- injector = assertInjectionContext(injectNgtRef, injector);
2996
+ injector = assertInjectionContext(injectNgtRef);
2997
+ const ref = is.ref(initial) ? initial : new ElementRef(initial);
2998
+ const signalRef = signal(ref.nativeElement);
2999
+ const readonlySignal = signalRef.asReadonly();
3000
+ const cached = new Map();
2798
3001
  return runInInjectionContext(injector, () => {
2799
- const cdr = inject(ChangeDetectorRef);
2800
- const ref = is.ref(initial) ? initial : new ElementRef(initial);
2801
- const signalRef = createSignal(ref.nativeElement);
2802
- const readonlySignal = signalRef.asReadonly();
2803
- const cached = new Map();
2804
3002
  inject(DestroyRef).onDestroy(() => void cached.clear());
2805
3003
  const children = (type = 'objects') => {
2806
3004
  if (!cached.has(type)) {
@@ -2820,36 +3018,24 @@ function injectNgtRef(initial = null, injector) {
2820
3018
  }
2821
3019
  return cached.get(type);
2822
3020
  };
2823
- Object.defineProperty(ref, 'nativeElement', {
2824
- set: (newElement) => {
2825
- if (newElement !== untracked(signalRef)) {
2826
- signalRef.set(newElement);
2827
- safeDetectChanges(cdr);
2828
- }
3021
+ Object.defineProperties(ref, {
3022
+ nativeElement: {
3023
+ set: (newElement) => {
3024
+ untracked(() => {
3025
+ if (newElement !== signalRef()) {
3026
+ signalRef.set(newElement);
3027
+ }
3028
+ });
3029
+ },
3030
+ get: readonlySignal,
2829
3031
  },
2830
- get: () => readonlySignal(),
3032
+ untracked: { get: () => untracked(readonlySignal) },
3033
+ children: { get: () => children },
2831
3034
  });
2832
- Object.defineProperty(ref, 'untracked', {
2833
- get: () => untracked(readonlySignal),
2834
- });
2835
- return Object.assign(ref, { children });
3035
+ return ref;
2836
3036
  });
2837
3037
  }
2838
3038
 
2839
- class NgtRepeat extends NgForOf {
2840
- set ngForRepeat(count) {
2841
- this.ngForOf = Number.isInteger(count) ? Array.from({ length: count }, (_, i) => i) : [];
2842
- }
2843
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRepeat, deps: null, target: i0.ɵɵFactoryTarget.Directive }); }
2844
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtRepeat, isStandalone: true, selector: "[ngFor][ngForRepeat]", inputs: { ngForRepeat: "ngForRepeat" }, usesInheritance: true, ngImport: i0 }); }
2845
- }
2846
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRepeat, decorators: [{
2847
- type: Directive,
2848
- args: [{ selector: '[ngFor][ngForRepeat]', standalone: true }]
2849
- }], propDecorators: { ngForRepeat: [{
2850
- type: Input
2851
- }] } });
2852
-
2853
3039
  const privateKeys = [
2854
3040
  'get',
2855
3041
  'set',
@@ -2864,21 +3050,17 @@ const privateKeys = [
2864
3050
  'viewport',
2865
3051
  ];
2866
3052
  class NgtPortalBeforeRender {
2867
- #portalStore;
2868
- #subscription;
2869
3053
  constructor() {
2870
- this.#portalStore = inject(NgtStore);
3054
+ this.portalStore = injectNgtStore();
3055
+ this.injector = inject(Injector);
2871
3056
  this.renderPriority = 1;
2872
3057
  this.beforeRender = new EventEmitter();
2873
- inject(DestroyRef).onDestroy(() => {
2874
- this.#subscription?.();
2875
- });
2876
3058
  }
2877
3059
  ngOnInit() {
2878
3060
  let oldClear;
2879
- this.#subscription = this.#portalStore.get('internal').subscribe(({ delta, frame }) => {
2880
- this.beforeRender.emit({ ...this.#portalStore.get(), delta, frame });
2881
- const { gl, scene, camera } = this.#portalStore.get();
3061
+ injectBeforeRender(({ delta, frame }) => {
3062
+ this.beforeRender.emit({ ...this.portalStore.get(), delta, frame });
3063
+ const { gl, scene, camera } = this.portalStore.get();
2882
3064
  oldClear = gl.autoClear;
2883
3065
  if (this.renderPriority === 1) {
2884
3066
  // clear scene and render with default
@@ -2891,15 +3073,15 @@ class NgtPortalBeforeRender {
2891
3073
  gl.render(scene, camera);
2892
3074
  // restore
2893
3075
  gl.autoClear = oldClear;
2894
- }, this.renderPriority, this.#portalStore);
3076
+ }, { priority: this.renderPriority, injector: this.injector });
2895
3077
  }
2896
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtPortalBeforeRender, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2897
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtPortalBeforeRender, isStandalone: true, selector: "[ngtPortalBeforeRender]", inputs: { renderPriority: "renderPriority", parentScene: "parentScene", parentCamera: "parentCamera" }, outputs: { beforeRender: "beforeRender" }, ngImport: i0 }); }
3078
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtPortalBeforeRender, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3079
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.2", type: NgtPortalBeforeRender, isStandalone: true, selector: "[ngtPortalBeforeRender]", inputs: { renderPriority: "renderPriority", parentScene: "parentScene", parentCamera: "parentCamera" }, outputs: { beforeRender: "beforeRender" }, ngImport: i0 }); }
2898
3080
  }
2899
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtPortalBeforeRender, decorators: [{
3081
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtPortalBeforeRender, decorators: [{
2900
3082
  type: Directive,
2901
3083
  args: [{ selector: '[ngtPortalBeforeRender]', standalone: true }]
2902
- }], ctorParameters: function () { return []; }, propDecorators: { renderPriority: [{
3084
+ }], propDecorators: { renderPriority: [{
2903
3085
  type: Input
2904
3086
  }], parentScene: [{
2905
3087
  type: Input,
@@ -2918,96 +3100,90 @@ class NgtPortalContent {
2918
3100
  delete commentNode[SPECIAL_INTERNAL_ADD_COMMENT];
2919
3101
  }
2920
3102
  }
2921
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtPortalContent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ViewContainerRef, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive }); }
2922
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.0.0", type: NgtPortalContent, isStandalone: true, selector: "ng-template[ngtPortalContent]", ngImport: i0 }); }
3103
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtPortalContent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ViewContainerRef, skipSelf: true }], target: i0.ɵɵFactoryTarget.Directive }); }
3104
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.2", type: NgtPortalContent, isStandalone: true, selector: "ng-template[ngtPortalContent]", ngImport: i0 }); }
2923
3105
  }
2924
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtPortalContent, decorators: [{
3106
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtPortalContent, decorators: [{
2925
3107
  type: Directive,
2926
3108
  args: [{ selector: 'ng-template[ngtPortalContent]', standalone: true }]
2927
3109
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ViewContainerRef, decorators: [{
2928
3110
  type: SkipSelf
2929
3111
  }] }]; } });
2930
- class NgtPortal extends NgtSignalStore {
3112
+ class NgtPortal {
2931
3113
  set container(container) {
2932
- this.set({ container });
2933
- }
2934
- set state(state) {
2935
- this.set({ state });
2936
- }
2937
- #parentStore;
2938
- #portalStore;
2939
- #injector;
2940
- #zone;
2941
- #raycaster;
2942
- #pointer;
2943
- #portalContentView;
3114
+ this.inputs.set({ container });
3115
+ }
3116
+ set portalState(state) {
3117
+ this.inputs.set({ state });
3118
+ }
2944
3119
  constructor() {
2945
- super({ container: injectNgtRef(prepare(new THREE.Scene())) });
3120
+ this.inputs = signalStore({ container: injectNgtRef(prepare(new THREE.Scene())) });
2946
3121
  this.autoRender = true;
2947
3122
  this.autoRenderPriority = 1;
2948
3123
  this.beforeRender = new EventEmitter();
2949
- this.#parentStore = inject(NgtStore, { skipSelf: true });
2950
- this.parentScene = this.#parentStore.get('scene');
2951
- this.parentCamera = this.#parentStore.get('camera');
2952
- this.#portalStore = inject(NgtStore, { self: true });
2953
- this.#injector = inject(Injector);
2954
- this.#zone = inject(NgZone);
2955
- this.#raycaster = new THREE.Raycaster();
2956
- this.#pointer = new THREE.Vector2();
3124
+ this.parentStore = injectNgtStore({ skipSelf: true });
3125
+ this.parentScene = this.parentStore.get('scene');
3126
+ this.parentCamera = this.parentStore.get('camera');
3127
+ this.portalStore = injectNgtStore({ self: true });
3128
+ this.injector = inject(Injector);
3129
+ this.zone = inject(NgZone);
3130
+ this.raycaster = new THREE.Raycaster();
3131
+ this.pointer = new THREE.Vector2();
2957
3132
  this.portalContentRendered = false;
2958
3133
  inject(DestroyRef).onDestroy(() => {
2959
- if (this.#portalContentView && !this.#portalContentView.destroyed) {
2960
- this.#portalContentView.destroy();
3134
+ if (this.portalContentView && !this.portalContentView.destroyed) {
3135
+ this.portalContentView.destroy();
2961
3136
  }
2962
3137
  });
2963
3138
  }
2964
3139
  ngOnInit() {
2965
- const previousState = this.#parentStore.get();
2966
- const inputsState = this.get();
3140
+ const previousState = this.parentStore.get();
3141
+ const inputsState = this.inputs.get();
2967
3142
  if (!inputsState.state && this.autoRender) {
2968
3143
  inputsState.state = { events: { priority: this.autoRenderPriority + 1 } };
2969
3144
  }
2970
3145
  const { events, size, ...restInputsState } = inputsState.state || {};
2971
3146
  const containerState = inputsState.container;
2972
- const container = is.ref(containerState) ? containerState.nativeElement : containerState;
3147
+ let container = is.ref(containerState) ? containerState.nativeElement : containerState;
3148
+ if (!is.instance(container)) {
3149
+ container = prepare(container);
3150
+ }
2973
3151
  const localState = getLocalState(container);
2974
3152
  if (!localState.store) {
2975
- localState.store = this.#portalStore;
3153
+ localState.store = this.portalStore;
2976
3154
  }
2977
- this.#portalStore.set({
3155
+ this.portalStore.set({
2978
3156
  ...previousState,
2979
3157
  scene: container,
2980
- raycaster: this.#raycaster,
2981
- pointer: this.#pointer,
2982
- previousStore: this.#parentStore,
3158
+ raycaster: this.raycaster,
3159
+ pointer: this.pointer,
3160
+ previousRoot: this.parentStore,
2983
3161
  events: { ...previousState.events, ...(events || {}) },
2984
3162
  size: { ...previousState.size, ...(size || {}) },
2985
3163
  ...restInputsState,
2986
- get: this.#portalStore.get.bind(this.#portalStore),
2987
- set: this.#portalStore.set.bind(this.#portalStore),
2988
- setEvents: (events) => this.#portalStore.set((state) => ({ ...state, events: { ...state.events, ...events } })),
3164
+ setEvents: (events) => this.portalStore.set((state) => ({ ...state, events: { ...state.events, ...events } })),
2989
3165
  });
2990
- const parentState = this.#parentStore.select();
3166
+ const parentState = this.parentStore.select();
2991
3167
  effect(() => {
2992
3168
  const previous = parentState();
2993
- this.#zone.runOutsideAngular(() => {
2994
- this.#portalStore.set((state) => this.#inject(previous, state));
3169
+ this.zone.runOutsideAngular(() => {
3170
+ this.portalStore.set((state) => this.inject(previous, state));
2995
3171
  });
2996
- }, { injector: this.#injector, allowSignalWrites: true });
3172
+ }, { injector: this.injector });
2997
3173
  requestAnimationFrame(() => {
2998
- this.#portalStore.set((injectState) => this.#inject(this.#parentStore.get(), injectState));
3174
+ this.portalStore.set((injectState) => this.inject(this.parentStore.get(), injectState));
2999
3175
  });
3000
- this.#portalContentView = this.portalContentAnchor.createEmbeddedView(this.portalContentTemplate);
3001
- safeDetectChanges(this.#portalContentView);
3176
+ this.portalContentView = this.portalContentAnchor.createEmbeddedView(this.portalContentTemplate);
3177
+ safeDetectChanges(this.portalContentView);
3002
3178
  this.portalContentRendered = true;
3003
3179
  }
3004
3180
  onBeforeRender(portal) {
3005
3181
  this.beforeRender.emit({
3006
- root: { ...this.#parentStore.get(), delta: portal.delta, frame: portal.frame },
3182
+ root: { ...this.parentStore.get(), delta: portal.delta, frame: portal.frame },
3007
3183
  portal,
3008
3184
  });
3009
3185
  }
3010
- #inject(rootState, injectState) {
3186
+ inject(rootState, injectState) {
3011
3187
  const intersect = { ...rootState };
3012
3188
  Object.keys(intersect).forEach((key) => {
3013
3189
  if (privateKeys.includes(key) ||
@@ -3015,7 +3191,7 @@ class NgtPortal extends NgtSignalStore {
3015
3191
  delete intersect[key];
3016
3192
  }
3017
3193
  });
3018
- const inputs = this.get();
3194
+ const inputs = this.inputs.get();
3019
3195
  const { size, events, ...restInputsState } = inputs.state || {};
3020
3196
  let viewport = undefined;
3021
3197
  if (injectState && size) {
@@ -3027,53 +3203,54 @@ class NgtPortal extends NgtSignalStore {
3027
3203
  return {
3028
3204
  ...intersect,
3029
3205
  scene: is.ref(inputs.container) ? inputs.container.nativeElement : inputs.container,
3030
- raycaster: this.#raycaster,
3031
- pointer: this.#pointer,
3032
- previousStore: this.#parentStore,
3206
+ raycaster: this.raycaster,
3207
+ pointer: this.pointer,
3208
+ previousRoot: this.parentStore,
3033
3209
  events: { ...rootState.events, ...(injectState?.events || {}), ...events },
3034
3210
  size: { ...rootState.size, ...size },
3035
3211
  viewport: { ...rootState.viewport, ...(viewport || {}) },
3036
3212
  ...restInputsState,
3037
3213
  };
3038
3214
  }
3039
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtPortal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3040
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: NgtPortal, isStandalone: true, selector: "ngt-portal", inputs: { container: "container", state: "state", autoRender: "autoRender", autoRenderPriority: "autoRenderPriority" }, outputs: { beforeRender: "beforeRender" }, providers: [NgtStore], queries: [{ propertyName: "portalContentTemplate", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, static: true }], viewQueries: [{ propertyName: "portalContentAnchor", first: true, predicate: ["portalContentAnchor"], descendants: true, read: ViewContainerRef, static: true }], usesInheritance: true, ngImport: i0, template: `
3041
- <ng-container #portalContentAnchor>
3042
- <ng-container
3043
- *ngIf="autoRender && portalContentRendered"
3044
- ngtPortalBeforeRender
3045
- [renderPriority]="autoRenderPriority"
3046
- [parentScene]="parentScene"
3047
- [parentCamera]="parentCamera"
3048
- (beforeRender)="onBeforeRender($event)"
3049
- />
3050
- </ng-container>
3051
- `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgtPortalBeforeRender, selector: "[ngtPortalBeforeRender]", inputs: ["renderPriority", "parentScene", "parentCamera"], outputs: ["beforeRender"] }] }); }
3215
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtPortal, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3216
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.2", type: NgtPortal, isStandalone: true, selector: "ngt-portal", inputs: { container: "container", portalState: ["state", "portalState"], autoRender: "autoRender", autoRenderPriority: "autoRenderPriority" }, outputs: { beforeRender: "beforeRender" }, providers: [{ provide: NGT_STORE, useFactory: () => signalStore({}) }], queries: [{ propertyName: "portalContentTemplate", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, static: true }], viewQueries: [{ propertyName: "portalContentAnchor", first: true, predicate: ["portalContentAnchor"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
3217
+ <ng-container #portalContentAnchor>
3218
+ <ng-container
3219
+ *ngIf="autoRender && portalContentRendered"
3220
+ ngtPortalBeforeRender
3221
+ [renderPriority]="autoRenderPriority"
3222
+ [parentScene]="parentScene"
3223
+ [parentCamera]="parentCamera"
3224
+ (beforeRender)="onBeforeRender($event)"
3225
+ />
3226
+ </ng-container>
3227
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgtPortalBeforeRender, selector: "[ngtPortalBeforeRender]", inputs: ["renderPriority", "parentScene", "parentCamera"], outputs: ["beforeRender"] }] }); }
3052
3228
  }
3053
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtPortal, decorators: [{
3229
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtPortal, decorators: [{
3054
3230
  type: Component,
3055
3231
  args: [{
3056
3232
  selector: 'ngt-portal',
3057
3233
  standalone: true,
3058
3234
  template: `
3059
- <ng-container #portalContentAnchor>
3060
- <ng-container
3061
- *ngIf="autoRender && portalContentRendered"
3062
- ngtPortalBeforeRender
3063
- [renderPriority]="autoRenderPriority"
3064
- [parentScene]="parentScene"
3065
- [parentCamera]="parentCamera"
3066
- (beforeRender)="onBeforeRender($event)"
3067
- />
3068
- </ng-container>
3069
- `,
3235
+ <ng-container #portalContentAnchor>
3236
+ <ng-container
3237
+ *ngIf="autoRender && portalContentRendered"
3238
+ ngtPortalBeforeRender
3239
+ [renderPriority]="autoRenderPriority"
3240
+ [parentScene]="parentScene"
3241
+ [parentCamera]="parentCamera"
3242
+ (beforeRender)="onBeforeRender($event)"
3243
+ />
3244
+ </ng-container>
3245
+ `,
3070
3246
  imports: [NgIf, NgtPortalBeforeRender],
3071
- providers: [NgtStore],
3247
+ providers: [{ provide: NGT_STORE, useFactory: () => signalStore({}) }],
3072
3248
  }]
3073
3249
  }], ctorParameters: function () { return []; }, propDecorators: { container: [{
3074
3250
  type: Input
3075
- }], state: [{
3076
- type: Input
3251
+ }], portalState: [{
3252
+ type: Input,
3253
+ args: ['state']
3077
3254
  }], autoRender: [{
3078
3255
  type: Input
3079
3256
  }], autoRenderPriority: [{
@@ -3097,15 +3274,19 @@ class NgtRoutedScene {
3097
3274
  .pipe(filter((event) => event instanceof ActivationEnd), takeUntilDestroyed())
3098
3275
  .subscribe(() => safeDetectChanges(cdr));
3099
3276
  }
3100
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRoutedScene, deps: [{ token: i1.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
3101
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.0", type: NgtRoutedScene, isStandalone: true, selector: "ngt-routed-scene", ngImport: i0, template: `<router-outlet />`, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] }); }
3277
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtRoutedScene, deps: [{ token: i1.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
3278
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.2", type: NgtRoutedScene, isStandalone: true, selector: "ngt-routed-scene", ngImport: i0, template: `
3279
+ <router-outlet />
3280
+ `, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] }); }
3102
3281
  }
3103
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImport: i0, type: NgtRoutedScene, decorators: [{
3282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.2", ngImport: i0, type: NgtRoutedScene, decorators: [{
3104
3283
  type: Component,
3105
3284
  args: [{
3106
3285
  standalone: true,
3107
3286
  selector: 'ngt-routed-scene',
3108
- template: `<router-outlet />`,
3287
+ template: `
3288
+ <router-outlet />
3289
+ `,
3109
3290
  imports: [RouterOutlet],
3110
3291
  }]
3111
3292
  }], ctorParameters: function () { return [{ type: i1.Router }, { type: i0.ChangeDetectorRef }]; } });
@@ -3114,5 +3295,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.0", ngImpor
3114
3295
  * Generated bundle index. Do not edit.
3115
3296
  */
3116
3297
 
3117
- export { NGT_CATALOGUE, NgtArgs, NgtCanvas, NgtParent, NgtPortal, NgtPortalContent, NgtRepeat, NgtRoutedScene, NgtSignalStore, NgtStore, addAfterEffect, addEffect, addTail, applyProps, assertInjectionContext, checkNeedsUpdate, checkUpdate, createAttachFunction, createSignal, extend, getLocalState, injectBeforeRender, injectNgtLoader, injectNgtRef, invalidateInstance, is, makeDefaultCamera, makeDefaultRenderer, makeDpr, makeId, makeObjectGraph, prepare, queueMacrotaskInInjectionContext, queueMicrotaskInInjectionContext, requestAnimationInInjectionContext, rootStateMap, safeDetectChanges, updateCamera };
3298
+ export { HTML, NGT_STORE, NgtArgs, NgtCanvas, NgtKey, NgtParent, NgtPortal, NgtPortalContent, NgtRepeat, NgtRoutedScene, addAfterEffect, addEffect, addTail, applyProps, assertInjectionContext, checkNeedsUpdate, checkUpdate, createAttachFunction, createInjectionToken, diffProps, extend, getLocalState, injectBeforeRender, injectNgtLoader, injectNgtRef, injectNgtStore, invalidateInstance, is, makeDefaultCamera, makeDefaultRenderer, makeDpr, makeId, makeObjectGraph, prepare, provideNgtRenderer, provideNgtStore, safeDetectChanges, signalStore, updateCamera };
3118
3299
  //# sourceMappingURL=angular-three.mjs.map