angular-three 4.0.0-next.96 → 4.0.0-next.98
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.
- package/fesm2022/angular-three-dom.mjs +2 -2
- package/fesm2022/angular-three-dom.mjs.map +1 -1
- package/fesm2022/angular-three-testing.mjs +2 -2
- package/fesm2022/angular-three-testing.mjs.map +1 -1
- package/fesm2022/angular-three.mjs +934 -826
- package/fesm2022/angular-three.mjs.map +1 -1
- package/index.d.ts +2 -1
- package/lib/loader-resource.d.ts +14 -0
- package/lib/loader.d.ts +11 -3
- package/lib/renderer/renderer.d.ts +1 -1
- package/lib/roots.d.ts +2 -2
- package/lib/utils/before-render.d.ts +6 -1
- package/lib/utils/object-events.d.ts +6 -1
- package/package.json +1 -1
- package/testing/lib/utils/web-gl-rendering-context.d.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ViewContainerRef, TemplateRef, Injector, effect, DestroyRef, Directive, input, linkedSignal, computed, InjectionToken, untracked, isSignal, signal, ElementRef, booleanAttribute, Pipe, numberAttribute, contentChild, SkipSelf, Component, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Injectable, output, model, Renderer2 } from '@angular/core';
|
|
2
|
+
import { inject, ViewContainerRef, TemplateRef, Injector, effect, DestroyRef, Directive, input, linkedSignal, computed, InjectionToken, untracked, isSignal, signal, ElementRef, booleanAttribute, resource, Pipe, numberAttribute, contentChild, SkipSelf, Component, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Injectable, output, model, Renderer2 } from '@angular/core';
|
|
3
3
|
import { DOCUMENT } from '@angular/common';
|
|
4
4
|
import { Subject, filter } from 'rxjs';
|
|
5
5
|
import * as THREE from 'three';
|
|
@@ -903,880 +903,978 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImpor
|
|
|
903
903
|
}], ctorParameters: () => [] });
|
|
904
904
|
const NgtSelection = [NgtSelectionApi, NgtSelect];
|
|
905
905
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
class NgtHTML {
|
|
919
|
-
static { _a = NGT_HTML_FLAG; }
|
|
920
|
-
static { this[_a] = true; }
|
|
921
|
-
constructor() {
|
|
922
|
-
this.domElement = inject(NGT_HTML_DOM_ELEMENT, { self: true, optional: true });
|
|
923
|
-
const host = inject(ElementRef);
|
|
924
|
-
const store = injectStore();
|
|
925
|
-
if (this.domElement === 'gl') {
|
|
926
|
-
Object.assign(host.nativeElement, {
|
|
927
|
-
[NGT_DOM_PARENT_FLAG]: store.snapshot.gl.domElement.parentElement,
|
|
928
|
-
});
|
|
929
|
-
}
|
|
930
|
-
else if (this.domElement) {
|
|
931
|
-
Object.assign(host.nativeElement, { [NGT_DOM_PARENT_FLAG]: this.domElement });
|
|
906
|
+
/**
|
|
907
|
+
* Release pointer captures.
|
|
908
|
+
* This is called by releasePointerCapture in the API, and when an object is removed.
|
|
909
|
+
*/
|
|
910
|
+
function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
|
|
911
|
+
const captureData = captures.get(obj);
|
|
912
|
+
if (captureData) {
|
|
913
|
+
captures.delete(obj);
|
|
914
|
+
// If this was the last capturing object for this pointer
|
|
915
|
+
if (captures.size === 0) {
|
|
916
|
+
capturedMap.delete(pointerId);
|
|
917
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
932
918
|
}
|
|
933
|
-
inject(DestroyRef).onDestroy(() => {
|
|
934
|
-
host.nativeElement[NGT_DOM_PARENT_FLAG] = null;
|
|
935
|
-
delete host.nativeElement[NGT_DOM_PARENT_FLAG];
|
|
936
|
-
});
|
|
937
|
-
}
|
|
938
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtHTML, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
939
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: NgtHTML, isStandalone: true, ngImport: i0 }); }
|
|
940
|
-
}
|
|
941
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtHTML, decorators: [{
|
|
942
|
-
type: Directive
|
|
943
|
-
}], ctorParameters: () => [] });
|
|
944
|
-
|
|
945
|
-
const cached = new Map();
|
|
946
|
-
const memoizedLoaders = new WeakMap();
|
|
947
|
-
function normalizeInputs(input) {
|
|
948
|
-
let urls = [];
|
|
949
|
-
if (Array.isArray(input)) {
|
|
950
|
-
urls = input;
|
|
951
919
|
}
|
|
952
|
-
else if (typeof input === 'string') {
|
|
953
|
-
urls = [input];
|
|
954
|
-
}
|
|
955
|
-
else {
|
|
956
|
-
urls = Object.values(input);
|
|
957
|
-
}
|
|
958
|
-
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
959
920
|
}
|
|
960
|
-
function
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
921
|
+
function removeInteractivity(store, object) {
|
|
922
|
+
const { internal } = store.snapshot;
|
|
923
|
+
// Removes every trace of an object from the data store
|
|
924
|
+
internal.interaction = internal.interaction.filter((o) => o !== object);
|
|
925
|
+
internal.initialHits = internal.initialHits.filter((o) => o !== object);
|
|
926
|
+
internal.hovered.forEach((value, key) => {
|
|
927
|
+
if (value.eventObject === object || value.object === object) {
|
|
928
|
+
// Clear out intersects, they are outdated by now
|
|
929
|
+
internal.hovered.delete(key);
|
|
967
930
|
}
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
if (url === '')
|
|
972
|
-
return Promise.resolve(null);
|
|
973
|
-
if (!cached.has(url)) {
|
|
974
|
-
cached.set(url, new Promise((resolve, reject) => {
|
|
975
|
-
loader.load(url, (data) => {
|
|
976
|
-
if ('scene' in data) {
|
|
977
|
-
Object.assign(data, makeObjectGraph(data['scene']));
|
|
978
|
-
}
|
|
979
|
-
if (onLoad) {
|
|
980
|
-
onLoad(data);
|
|
981
|
-
}
|
|
982
|
-
resolve(data);
|
|
983
|
-
}, onProgress, (error) => reject(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
984
|
-
}));
|
|
985
|
-
}
|
|
986
|
-
return cached.get(url);
|
|
987
|
-
});
|
|
988
|
-
};
|
|
989
|
-
}
|
|
990
|
-
function _injectLoader(loaderConstructorFactory, inputs, { extensions, onProgress, onLoad, injector, } = {}) {
|
|
991
|
-
return assertInjector(_injectLoader, injector, () => {
|
|
992
|
-
const response = signal(null);
|
|
993
|
-
const cachedResultPromisesEffect = load(loaderConstructorFactory, inputs, {
|
|
994
|
-
extensions,
|
|
995
|
-
onProgress,
|
|
996
|
-
onLoad: onLoad,
|
|
997
|
-
});
|
|
998
|
-
effect(() => {
|
|
999
|
-
const originalUrls = inputs();
|
|
1000
|
-
const cachedResultPromises = cachedResultPromisesEffect();
|
|
1001
|
-
Promise.all(cachedResultPromises).then((results) => {
|
|
1002
|
-
response.update(() => {
|
|
1003
|
-
if (Array.isArray(originalUrls))
|
|
1004
|
-
return results;
|
|
1005
|
-
if (typeof originalUrls === 'string')
|
|
1006
|
-
return results[0];
|
|
1007
|
-
const keys = Object.keys(originalUrls);
|
|
1008
|
-
return keys.reduce((result, key) => {
|
|
1009
|
-
// @ts-ignore
|
|
1010
|
-
result[key] = results[keys.indexOf(key)];
|
|
1011
|
-
return result;
|
|
1012
|
-
}, {});
|
|
1013
|
-
});
|
|
1014
|
-
});
|
|
1015
|
-
});
|
|
1016
|
-
return response.asReadonly();
|
|
931
|
+
});
|
|
932
|
+
internal.capturedMap.forEach((captures, pointerId) => {
|
|
933
|
+
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
1017
934
|
});
|
|
1018
935
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
936
|
+
function createEvents(store) {
|
|
937
|
+
/** Calculates delta */
|
|
938
|
+
function calculateDistance(event) {
|
|
939
|
+
const internal = store.snapshot.internal;
|
|
940
|
+
const dx = event.offsetX - internal.initialClick[0];
|
|
941
|
+
const dy = event.offsetY - internal.initialClick[1];
|
|
942
|
+
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
1023
943
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
urlToClear.forEach((url) => {
|
|
1031
|
-
cached.delete(url);
|
|
1032
|
-
});
|
|
1033
|
-
};
|
|
1034
|
-
const injectLoader = _injectLoader;
|
|
1035
|
-
|
|
1036
|
-
const RGBA_REGEX = /rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*(\d*\.?\d+)?\)/;
|
|
1037
|
-
const DEFAULT_COLOR = 0x000000;
|
|
1038
|
-
class NgtHexify {
|
|
1039
|
-
constructor() {
|
|
1040
|
-
this.document = inject(DOCUMENT, { optional: true });
|
|
1041
|
-
this.cache = {};
|
|
944
|
+
/** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
|
|
945
|
+
function filterPointerEvents(objects) {
|
|
946
|
+
return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
|
|
947
|
+
const eventName = `pointer${name}`;
|
|
948
|
+
return getInstanceState(obj)?.handlers?.[eventName];
|
|
949
|
+
}));
|
|
1042
950
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
if (value == null)
|
|
1055
|
-
return DEFAULT_COLOR;
|
|
1056
|
-
if (value.startsWith('#')) {
|
|
1057
|
-
if (!this.cache[value]) {
|
|
1058
|
-
this.cache[value] = this.hexStringToNumber(value);
|
|
951
|
+
function intersect(event, filter) {
|
|
952
|
+
const state = store.snapshot;
|
|
953
|
+
const duplicates = new Set();
|
|
954
|
+
const intersections = [];
|
|
955
|
+
// Allow callers to eliminate event objects
|
|
956
|
+
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
|
|
957
|
+
// Reset all raycaster cameras to undefined
|
|
958
|
+
for (let i = 0; i < eventsObjects.length; i++) {
|
|
959
|
+
const objectRootState = getInstanceState(eventsObjects[i])?.store?.snapshot;
|
|
960
|
+
if (objectRootState) {
|
|
961
|
+
objectRootState.raycaster.camera = undefined;
|
|
1059
962
|
}
|
|
1060
|
-
return this.cache[value];
|
|
1061
963
|
}
|
|
1062
|
-
if (!
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
if (!this.ctx) {
|
|
1066
|
-
console.warn('[NGT] hexify: canvas context is not available');
|
|
1067
|
-
return DEFAULT_COLOR;
|
|
964
|
+
if (!state.previousRoot) {
|
|
965
|
+
// Make sure root-level pointer and ray are set up
|
|
966
|
+
state.events.compute?.(event, store, null);
|
|
1068
967
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
968
|
+
function handleRaycast(obj) {
|
|
969
|
+
const objStore = getInstanceState(obj)?.store;
|
|
970
|
+
const objState = objStore?.snapshot;
|
|
971
|
+
// Skip event handling when noEvents is set, or when the raycasters camera is null
|
|
972
|
+
if (!objState || !objState.events.enabled || objState.raycaster.camera === null)
|
|
973
|
+
return [];
|
|
974
|
+
// When the camera is undefined we have to call the event layers update function
|
|
975
|
+
if (objState.raycaster.camera === undefined) {
|
|
976
|
+
objState.events.compute?.(event, objStore, objState.previousRoot);
|
|
977
|
+
// If the camera is still undefined we have to skip this layer entirely
|
|
978
|
+
if (objState.raycaster.camera === undefined)
|
|
979
|
+
objState.raycaster.camera = null;
|
|
1074
980
|
}
|
|
1075
|
-
|
|
981
|
+
// Intersect object by object
|
|
982
|
+
return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
|
|
1076
983
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
984
|
+
// Collect events
|
|
985
|
+
let hits = eventsObjects
|
|
986
|
+
// Intersect objects
|
|
987
|
+
.flatMap(handleRaycast)
|
|
988
|
+
// Sort by event priority and distance
|
|
989
|
+
.sort((a, b) => {
|
|
990
|
+
const aState = getInstanceState(a.object)?.store?.snapshot;
|
|
991
|
+
const bState = getInstanceState(b.object)?.store?.snapshot;
|
|
992
|
+
if (!aState || !bState)
|
|
993
|
+
return a.distance - b.distance;
|
|
994
|
+
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
995
|
+
})
|
|
996
|
+
// Filter out duplicates
|
|
997
|
+
.filter((item) => {
|
|
998
|
+
const id = makeId(item);
|
|
999
|
+
if (duplicates.has(id))
|
|
1000
|
+
return false;
|
|
1001
|
+
duplicates.add(id);
|
|
1002
|
+
return true;
|
|
1003
|
+
});
|
|
1004
|
+
// https://github.com/mrdoob/three.js/issues/16031
|
|
1005
|
+
// Allow custom userland intersect sort order, this likely only makes sense on the root filter
|
|
1006
|
+
if (state.events.filter)
|
|
1007
|
+
hits = state.events.filter(hits, store);
|
|
1008
|
+
// Bubble up the events, find the event source (eventObject)
|
|
1009
|
+
for (const hit of hits) {
|
|
1010
|
+
let eventObject = hit.object;
|
|
1011
|
+
// bubble event up
|
|
1012
|
+
while (eventObject) {
|
|
1013
|
+
if (getInstanceState(eventObject)?.eventCount)
|
|
1014
|
+
intersections.push({ ...hit, eventObject });
|
|
1015
|
+
eventObject = eventObject.parent;
|
|
1016
|
+
}
|
|
1080
1017
|
}
|
|
1081
|
-
|
|
1082
|
-
if (
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
const g = parseInt(match[2], 10);
|
|
1088
|
-
const b = parseInt(match[3], 10);
|
|
1089
|
-
const a = match[4] ? parseFloat(match[4]) : 1.0;
|
|
1090
|
-
const cacheKey = `${r}:${g}:${b}:${a}`;
|
|
1091
|
-
// check result from cache
|
|
1092
|
-
if (!this.cache[cacheKey]) {
|
|
1093
|
-
// Convert the components to hex strings
|
|
1094
|
-
const hexR = this.componentToHex(r);
|
|
1095
|
-
const hexG = this.componentToHex(g);
|
|
1096
|
-
const hexB = this.componentToHex(b);
|
|
1097
|
-
const hexA = this.componentToHex(Math.round(a * 255));
|
|
1098
|
-
// Combine the hex components into a single hex string
|
|
1099
|
-
const hex = `#${hexR}${hexG}${hexB}${hexA}`;
|
|
1100
|
-
this.cache[cacheKey] = this.hexStringToNumber(hex);
|
|
1018
|
+
// If the interaction is captured, make all capturing targets part of the intersect.
|
|
1019
|
+
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
1020
|
+
for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
1021
|
+
if (!duplicates.has(makeId(captureData.intersection)))
|
|
1022
|
+
intersections.push(captureData.intersection);
|
|
1023
|
+
}
|
|
1101
1024
|
}
|
|
1102
|
-
return
|
|
1103
|
-
}
|
|
1104
|
-
hexStringToNumber(hexString) {
|
|
1105
|
-
return parseInt(hexString.replace('#', ''), 16);
|
|
1025
|
+
return intersections;
|
|
1106
1026
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1027
|
+
/** Handles intersections by forwarding them to handlers */
|
|
1028
|
+
function handleIntersects(intersections, event, delta, callback) {
|
|
1029
|
+
const rootState = store.snapshot;
|
|
1030
|
+
// If anything has been found, forward it to the event listeners
|
|
1031
|
+
if (intersections.length) {
|
|
1032
|
+
const localState = { stopped: false };
|
|
1033
|
+
for (const hit of intersections) {
|
|
1034
|
+
let instanceState = getInstanceState(hit.object);
|
|
1035
|
+
// If the object is not managed by NGT, it might be parented to an element which is.
|
|
1036
|
+
// Traverse upwards until we find a managed parent and use its state instead.
|
|
1037
|
+
if (!instanceState) {
|
|
1038
|
+
hit.object.traverseAncestors((ancestor) => {
|
|
1039
|
+
const parentInstanceState = getInstanceState(ancestor);
|
|
1040
|
+
if (parentInstanceState) {
|
|
1041
|
+
instanceState = parentInstanceState;
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
return;
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
const { raycaster, pointer, camera, internal } = instanceState?.store?.snapshot || rootState;
|
|
1048
|
+
const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
1049
|
+
const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
|
|
1050
|
+
const setPointerCapture = (id) => {
|
|
1051
|
+
const captureData = { intersection: hit, target: event.target };
|
|
1052
|
+
if (internal.capturedMap.has(id)) {
|
|
1053
|
+
// if the pointerId was previously captured, we add the hit to the
|
|
1054
|
+
// event capturedMap.
|
|
1055
|
+
internal.capturedMap.get(id).set(hit.eventObject, captureData);
|
|
1056
|
+
}
|
|
1057
|
+
else {
|
|
1058
|
+
// if the pointerId was not previously captured, we create a map
|
|
1059
|
+
// containing the hitObject, and the hit. hitObject is used for
|
|
1060
|
+
// faster access.
|
|
1061
|
+
internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
|
|
1062
|
+
}
|
|
1063
|
+
// Call the original event now
|
|
1064
|
+
event.target.setPointerCapture(id);
|
|
1065
|
+
};
|
|
1066
|
+
const releasePointerCapture = (id) => {
|
|
1067
|
+
const captures = internal.capturedMap.get(id);
|
|
1068
|
+
if (captures) {
|
|
1069
|
+
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
// Add native event props
|
|
1073
|
+
const extractEventProps = {};
|
|
1074
|
+
// 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.
|
|
1075
|
+
for (const prop in event) {
|
|
1076
|
+
const property = event[prop];
|
|
1077
|
+
// Only copy over atomics, leave functions alone as these should be
|
|
1078
|
+
// called as event.nativeEvent.fn()
|
|
1079
|
+
if (typeof property !== 'function')
|
|
1080
|
+
extractEventProps[prop] = property;
|
|
1081
|
+
}
|
|
1082
|
+
const raycastEvent = {
|
|
1083
|
+
...hit,
|
|
1084
|
+
...extractEventProps,
|
|
1085
|
+
pointer,
|
|
1086
|
+
intersections,
|
|
1087
|
+
stopped: localState.stopped,
|
|
1088
|
+
delta,
|
|
1089
|
+
unprojectedPoint,
|
|
1090
|
+
ray: raycaster.ray,
|
|
1091
|
+
camera,
|
|
1092
|
+
// Hijack stopPropagation, which just sets a flag
|
|
1093
|
+
stopPropagation() {
|
|
1094
|
+
// https://github.com/pmndrs/react-three-fiber/issues/596
|
|
1095
|
+
// Events are not allowed to stop propagation if the pointer has been captured
|
|
1096
|
+
const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
|
|
1097
|
+
// We only authorize stopPropagation...
|
|
1098
|
+
if (
|
|
1099
|
+
// ...if this pointer hasn't been captured
|
|
1100
|
+
!capturesForPointer ||
|
|
1101
|
+
// ... or if the hit object is capturing the pointer
|
|
1102
|
+
capturesForPointer.has(hit.eventObject)) {
|
|
1103
|
+
raycastEvent.stopped = localState.stopped = true;
|
|
1104
|
+
// Propagation is stopped, remove all other hover records
|
|
1105
|
+
// An event handler is only allowed to flush other handlers if it is hovered itself
|
|
1106
|
+
if (internal.hovered.size &&
|
|
1107
|
+
Array.from(internal.hovered.values()).find((i) => i.eventObject === hit.eventObject)) {
|
|
1108
|
+
// Objects cannot flush out higher up objects that have already caught the event
|
|
1109
|
+
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
1110
|
+
cancelPointer([...higher, hit]);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
},
|
|
1114
|
+
// there should be a distinction between target and currentTarget
|
|
1115
|
+
target: { hasPointerCapture, setPointerCapture, releasePointerCapture },
|
|
1116
|
+
currentTarget: { hasPointerCapture, setPointerCapture, releasePointerCapture },
|
|
1117
|
+
nativeEvent: event,
|
|
1118
|
+
};
|
|
1119
|
+
// Call subscribers
|
|
1120
|
+
callback(raycastEvent);
|
|
1121
|
+
// Event bubbling may be interrupted by stopPropagation
|
|
1122
|
+
if (localState.stopped)
|
|
1123
|
+
break;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
return intersections;
|
|
1110
1127
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
const result = {};
|
|
1132
|
-
for (const key of Object.keys(obj)) {
|
|
1133
|
-
if (keysToOmit.includes(key))
|
|
1134
|
-
continue;
|
|
1135
|
-
Object.assign(result, { [key]: obj[key] });
|
|
1128
|
+
function cancelPointer(intersections) {
|
|
1129
|
+
const internal = store.snapshot.internal;
|
|
1130
|
+
for (const hoveredObj of internal.hovered.values()) {
|
|
1131
|
+
// When no objects were hit or the the hovered object wasn't found underneath the cursor
|
|
1132
|
+
// we call onPointerOut and delete the object from the hovered-elements map
|
|
1133
|
+
if (!intersections.length ||
|
|
1134
|
+
!intersections.find((hit) => hit.object === hoveredObj.object &&
|
|
1135
|
+
hit.index === hoveredObj.index &&
|
|
1136
|
+
hit.instanceId === hoveredObj.instanceId)) {
|
|
1137
|
+
const eventObject = hoveredObj.eventObject;
|
|
1138
|
+
const instance = getInstanceState(eventObject);
|
|
1139
|
+
const handlers = instance?.handlers;
|
|
1140
|
+
internal.hovered.delete(makeId(hoveredObj));
|
|
1141
|
+
if (instance?.eventCount) {
|
|
1142
|
+
// Clear out intersects, they are outdated by now
|
|
1143
|
+
const data = { ...hoveredObj, intersections };
|
|
1144
|
+
handlers?.pointerout?.(data);
|
|
1145
|
+
handlers?.pointerleave?.(data);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1136
1148
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
if (!equal) {
|
|
1143
|
-
equal = (a, b) => is.equ(a, b, { objects: 'shallow', arrays: 'shallow' });
|
|
1149
|
+
}
|
|
1150
|
+
function pointerMissed(event, objects) {
|
|
1151
|
+
for (let i = 0; i < objects.length; i++) {
|
|
1152
|
+
const instance = getInstanceState(objects[i]);
|
|
1153
|
+
instance?.handlers.pointermissed?.(event);
|
|
1144
1154
|
}
|
|
1145
|
-
return computed(() => {
|
|
1146
|
-
const obj = objFn();
|
|
1147
|
-
const result = {};
|
|
1148
|
-
for (const key of keyOrKeys) {
|
|
1149
|
-
if (!(key in obj))
|
|
1150
|
-
continue;
|
|
1151
|
-
Object.assign(result, { [key]: obj[key] });
|
|
1152
|
-
}
|
|
1153
|
-
return result;
|
|
1154
|
-
}, { equal });
|
|
1155
1155
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
return new vectorCtor();
|
|
1180
|
-
}, { equal: (a, b) => !!a && !!b && a.equals(b) });
|
|
1156
|
+
function handlePointer(name) {
|
|
1157
|
+
// Deal with cancelation
|
|
1158
|
+
switch (name) {
|
|
1159
|
+
case 'pointerleave':
|
|
1160
|
+
case 'pointercancel':
|
|
1161
|
+
return () => cancelPointer([]);
|
|
1162
|
+
case 'lostpointercapture':
|
|
1163
|
+
return (event) => {
|
|
1164
|
+
const { internal } = store.snapshot;
|
|
1165
|
+
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
1166
|
+
// If the object event interface had lostpointercapture, we'd call it here on every
|
|
1167
|
+
// object that's getting removed. We call it on the next frame because lostpointercapture
|
|
1168
|
+
// fires before pointerup. Otherwise pointerUp would never be called if the event didn't
|
|
1169
|
+
// happen in the object it originated from, leaving components in a in-between state.
|
|
1170
|
+
requestAnimationFrame(() => {
|
|
1171
|
+
// Only release if pointer-up didn't do it already
|
|
1172
|
+
if (internal.capturedMap.has(event.pointerId)) {
|
|
1173
|
+
internal.capturedMap.delete(event.pointerId);
|
|
1174
|
+
cancelPointer([]);
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1181
1179
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1180
|
+
// Any other pointer goes here ...
|
|
1181
|
+
return function handleEvent(event) {
|
|
1182
|
+
// NOTE: __pointerMissed$ on NgtStore is private subject since we only expose the Observable
|
|
1183
|
+
const pointerMissed$ = store['__pointerMissed$'];
|
|
1184
|
+
const internal = store.snapshot.internal;
|
|
1185
|
+
// prepareRay(event)
|
|
1186
|
+
internal.lastEvent.nativeElement = event;
|
|
1187
|
+
// Get fresh intersects
|
|
1188
|
+
const isPointerMove = name === 'pointermove';
|
|
1189
|
+
const isClickEvent = name === 'click' || name === 'contextmenu' || name === 'dblclick';
|
|
1190
|
+
const filter = isPointerMove ? filterPointerEvents : undefined;
|
|
1191
|
+
const hits = intersect(event, filter);
|
|
1192
|
+
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
1193
|
+
// Save initial coordinates on pointer-down
|
|
1194
|
+
if (name === 'pointerdown') {
|
|
1195
|
+
internal.initialClick = [event.offsetX, event.offsetY];
|
|
1196
|
+
internal.initialHits = hits.map((hit) => hit.eventObject);
|
|
1197
|
+
}
|
|
1198
|
+
// If a click yields no results, pass it back to the user as a miss
|
|
1199
|
+
// Missed events have to come first in order to establish user-land side-effect clean up
|
|
1200
|
+
if (isClickEvent && !hits.length) {
|
|
1201
|
+
if (delta <= 2) {
|
|
1202
|
+
pointerMissed(event, internal.interaction);
|
|
1203
|
+
pointerMissed$.next(event);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
// Take care of unhover
|
|
1207
|
+
if (isPointerMove)
|
|
1208
|
+
cancelPointer(hits);
|
|
1209
|
+
function onIntersect(data) {
|
|
1210
|
+
const eventObject = data.eventObject;
|
|
1211
|
+
const instance = getInstanceState(eventObject);
|
|
1212
|
+
const handlers = instance?.handlers;
|
|
1213
|
+
// Check presence of handlers
|
|
1214
|
+
if (!instance?.eventCount)
|
|
1215
|
+
return;
|
|
1216
|
+
/*
|
|
1217
|
+
MAYBE TODO, DELETE IF NOT:
|
|
1218
|
+
Check if the object is captured, captured events should not have intersects running in parallel
|
|
1219
|
+
But wouldn't it be better to just replace capturedMap with a single entry?
|
|
1220
|
+
Also, are we OK with straight up making picking up multiple objects impossible?
|
|
1202
1221
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1222
|
+
const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
|
|
1223
|
+
if (pointerId !== undefined) {
|
|
1224
|
+
const capturedMeshSet = internal.capturedMap.get(pointerId)
|
|
1225
|
+
if (capturedMeshSet) {
|
|
1226
|
+
const captured = capturedMeshSet.get(eventObject)
|
|
1227
|
+
if (captured && captured.localState.stopped) return
|
|
1228
|
+
}
|
|
1229
|
+
}*/
|
|
1230
|
+
if (isPointerMove) {
|
|
1231
|
+
// Move event ...
|
|
1232
|
+
if (handlers?.pointerover ||
|
|
1233
|
+
handlers?.pointerenter ||
|
|
1234
|
+
handlers?.pointerout ||
|
|
1235
|
+
handlers?.pointerleave) {
|
|
1236
|
+
// When enter or out is present take care of hover-state
|
|
1237
|
+
const id = makeId(data);
|
|
1238
|
+
const hoveredItem = internal.hovered.get(id);
|
|
1239
|
+
if (!hoveredItem) {
|
|
1240
|
+
// If the object wasn't previously hovered, book it and call its handler
|
|
1241
|
+
internal.hovered.set(id, data);
|
|
1242
|
+
handlers.pointerover?.(data);
|
|
1243
|
+
handlers.pointerenter?.(data);
|
|
1244
|
+
}
|
|
1245
|
+
else if (hoveredItem.stopped) {
|
|
1246
|
+
// If the object was previously hovered and stopped, we shouldn't allow other items to proceed
|
|
1247
|
+
data.stopPropagation();
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
// Call mouse move
|
|
1251
|
+
handlers?.pointermove?.(data);
|
|
1252
|
+
}
|
|
1253
|
+
else {
|
|
1254
|
+
// All other events ...
|
|
1255
|
+
const handler = handlers?.[name];
|
|
1256
|
+
if (handler) {
|
|
1257
|
+
// Forward all events back to their respective handlers with the exception of click events,
|
|
1258
|
+
// which must use the initial target
|
|
1259
|
+
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
1260
|
+
// Missed events have to come first
|
|
1261
|
+
pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
|
|
1262
|
+
// Now call the handler
|
|
1263
|
+
handler(data);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
else {
|
|
1267
|
+
// Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
|
|
1268
|
+
if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
1269
|
+
pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1229
1272
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
gl.render(scene, camera);
|
|
1234
|
-
// restore
|
|
1235
|
-
gl.autoClear = oldClean;
|
|
1236
|
-
}, renderPriority, this.portalStore);
|
|
1237
|
-
onCleanup(() => cleanup());
|
|
1238
|
-
});
|
|
1273
|
+
}
|
|
1274
|
+
handleIntersects(hits, event, delta, onIntersect);
|
|
1275
|
+
};
|
|
1239
1276
|
}
|
|
1240
|
-
|
|
1241
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.1", type: NgtPortalAutoRender, isStandalone: true, selector: "ngt-portal[autoRender]", inputs: { renderPriority: { classPropertyName: "renderPriority", publicName: "autoRender", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
1277
|
+
return { handlePointer };
|
|
1242
1278
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
return
|
|
1279
|
+
|
|
1280
|
+
var _a;
|
|
1281
|
+
//
|
|
1282
|
+
const NGT_HTML_DOM_ELEMENT = new InjectionToken('NGT_HTML_DOM_ELEMENT');
|
|
1283
|
+
function provideHTMLDomElement(...args) {
|
|
1284
|
+
if (args.length === 0) {
|
|
1285
|
+
return { provide: NGT_HTML_DOM_ELEMENT, useFactory: () => 'gl' };
|
|
1286
|
+
}
|
|
1287
|
+
if (args.length === 1) {
|
|
1288
|
+
return { provide: NGT_HTML_DOM_ELEMENT, useFactory: args[0] };
|
|
1250
1289
|
}
|
|
1290
|
+
return { provide: NGT_HTML_DOM_ELEMENT, useFactory: args.pop(), deps: args };
|
|
1291
|
+
}
|
|
1292
|
+
class NgtHTML {
|
|
1293
|
+
static { _a = NGT_HTML_FLAG; }
|
|
1294
|
+
static { this[_a] = true; }
|
|
1251
1295
|
constructor() {
|
|
1252
|
-
|
|
1253
|
-
const
|
|
1254
|
-
const injector = inject(Injector);
|
|
1255
|
-
const commentNode = element.nativeElement;
|
|
1296
|
+
this.domElement = inject(NGT_HTML_DOM_ELEMENT, { self: true, optional: true });
|
|
1297
|
+
const host = inject(ElementRef);
|
|
1256
1298
|
const store = injectStore();
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1299
|
+
if (this.domElement === 'gl') {
|
|
1300
|
+
Object.assign(host.nativeElement, {
|
|
1301
|
+
[NGT_DOM_PARENT_FLAG]: store.snapshot.gl.domElement.parentElement,
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
else if (this.domElement) {
|
|
1305
|
+
Object.assign(host.nativeElement, { [NGT_DOM_PARENT_FLAG]: this.domElement });
|
|
1263
1306
|
}
|
|
1307
|
+
inject(DestroyRef).onDestroy(() => {
|
|
1308
|
+
host.nativeElement[NGT_DOM_PARENT_FLAG] = null;
|
|
1309
|
+
delete host.nativeElement[NGT_DOM_PARENT_FLAG];
|
|
1310
|
+
});
|
|
1264
1311
|
}
|
|
1265
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type:
|
|
1266
|
-
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type:
|
|
1312
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtHTML, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1313
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: NgtHTML, isStandalone: true, ngImport: i0 }); }
|
|
1267
1314
|
}
|
|
1268
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type:
|
|
1269
|
-
type: Directive
|
|
1270
|
-
args: [{ selector: 'ng-template[portalContent]' }]
|
|
1315
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtHTML, decorators: [{
|
|
1316
|
+
type: Directive
|
|
1271
1317
|
}], ctorParameters: () => [] });
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
let
|
|
1277
|
-
if (
|
|
1278
|
-
|
|
1279
|
-
// calculate the override viewport, if present
|
|
1280
|
-
viewport = previousState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
|
|
1281
|
-
// update the portal camera, if it differs from the previous layer
|
|
1282
|
-
if (camera !== previousState.camera)
|
|
1283
|
-
updateCamera(camera, size);
|
|
1318
|
+
|
|
1319
|
+
const cached$1 = new Map();
|
|
1320
|
+
const memoizedLoaders$1 = new WeakMap();
|
|
1321
|
+
function normalizeInputs$1(input) {
|
|
1322
|
+
let urls = [];
|
|
1323
|
+
if (Array.isArray(input)) {
|
|
1324
|
+
urls = input;
|
|
1284
1325
|
}
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
raycaster,
|
|
1293
|
-
// their previous root is the layer before it
|
|
1294
|
-
previousRoot,
|
|
1295
|
-
events: { ...previousState.events, ...state.events, ...events },
|
|
1296
|
-
size: { ...previousState.size, ...size },
|
|
1297
|
-
viewport: { ...previousState.viewport, ...viewport },
|
|
1298
|
-
// layers are allowed to override events
|
|
1299
|
-
setEvents: (events) => store.update((state) => ({ ...state, events: { ...state.events, ...events } })),
|
|
1300
|
-
};
|
|
1326
|
+
else if (typeof input === 'string') {
|
|
1327
|
+
urls = [input];
|
|
1328
|
+
}
|
|
1329
|
+
else {
|
|
1330
|
+
urls = Object.values(input);
|
|
1331
|
+
}
|
|
1332
|
+
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
1301
1333
|
}
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
}
|
|
1328
|
-
const instanceState = getInstanceState(container);
|
|
1329
|
-
if (instanceState && instanceState.store !== this.portalStore) {
|
|
1330
|
-
instanceState.store = this.portalStore;
|
|
1331
|
-
}
|
|
1332
|
-
this.portalStore.update(restState, mergeState(this.previousStore, this.portalStore, container, this.portalStore.snapshot.pointer, this.portalStore.snapshot.raycaster, events, size));
|
|
1333
|
-
if (this.portalViewRef) {
|
|
1334
|
-
this.portalViewRef.detectChanges();
|
|
1335
|
-
return;
|
|
1334
|
+
function load(loaderConstructorFactory, inputs, { extensions, onLoad, onProgress, } = {}) {
|
|
1335
|
+
return () => {
|
|
1336
|
+
const urls = normalizeInputs$1(inputs());
|
|
1337
|
+
let loader = memoizedLoaders$1.get(loaderConstructorFactory(urls));
|
|
1338
|
+
if (!loader) {
|
|
1339
|
+
loader = new (loaderConstructorFactory(urls))();
|
|
1340
|
+
memoizedLoaders$1.set(loaderConstructorFactory(urls), loader);
|
|
1341
|
+
}
|
|
1342
|
+
if (extensions)
|
|
1343
|
+
extensions(loader);
|
|
1344
|
+
return urls.map((url) => {
|
|
1345
|
+
if (url === '')
|
|
1346
|
+
return Promise.resolve(null);
|
|
1347
|
+
if (!cached$1.has(url)) {
|
|
1348
|
+
cached$1.set(url, new Promise((resolve, reject) => {
|
|
1349
|
+
loader.load(url, (data) => {
|
|
1350
|
+
if ('scene' in data) {
|
|
1351
|
+
Object.assign(data, makeObjectGraph(data['scene']));
|
|
1352
|
+
}
|
|
1353
|
+
if (onLoad) {
|
|
1354
|
+
onLoad(data);
|
|
1355
|
+
}
|
|
1356
|
+
resolve(data);
|
|
1357
|
+
}, onProgress, (error) => reject(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
1358
|
+
}));
|
|
1336
1359
|
}
|
|
1337
|
-
|
|
1338
|
-
this.portalViewRef.detectChanges();
|
|
1339
|
-
this.portalContentRendered.set(true);
|
|
1360
|
+
return cached$1.get(url);
|
|
1340
1361
|
});
|
|
1341
|
-
}
|
|
1342
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtPortalImpl, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1343
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.1", type: NgtPortalImpl, isStandalone: true, selector: "ngt-portal", inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: true, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
1344
|
-
{
|
|
1345
|
-
provide: NGT_STORE,
|
|
1346
|
-
useFactory: (previousStore) => {
|
|
1347
|
-
const pointer = new THREE.Vector2();
|
|
1348
|
-
const raycaster = new THREE.Raycaster();
|
|
1349
|
-
const { id: _skipId, ...previousState } = previousStore.snapshot;
|
|
1350
|
-
const store = signalState({
|
|
1351
|
-
id: makeId(),
|
|
1352
|
-
...previousState,
|
|
1353
|
-
scene: null,
|
|
1354
|
-
previousRoot: previousStore,
|
|
1355
|
-
pointer,
|
|
1356
|
-
raycaster,
|
|
1357
|
-
});
|
|
1358
|
-
store.update(mergeState(previousStore, store, null, pointer, raycaster));
|
|
1359
|
-
return store;
|
|
1360
|
-
},
|
|
1361
|
-
deps: [[new SkipSelf(), NGT_STORE]],
|
|
1362
|
-
},
|
|
1363
|
-
], queries: [{ propertyName: "contentRef", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "anchorRef", first: true, predicate: NgtPortalContent, descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
|
|
1364
|
-
@if (portalRendered()) {
|
|
1365
|
-
<!-- Without an element that receives pointer events state.pointer will always be 0/0 -->
|
|
1366
|
-
<ngt-group (pointerover)="(undefined)" attach="none" />
|
|
1367
|
-
}
|
|
1368
|
-
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1362
|
+
};
|
|
1369
1363
|
}
|
|
1370
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtPortalImpl, decorators: [{
|
|
1371
|
-
type: Component,
|
|
1372
|
-
args: [{
|
|
1373
|
-
selector: 'ngt-portal',
|
|
1374
|
-
template: `
|
|
1375
|
-
@if (portalRendered()) {
|
|
1376
|
-
<!-- Without an element that receives pointer events state.pointer will always be 0/0 -->
|
|
1377
|
-
<ngt-group (pointerover)="(undefined)" attach="none" />
|
|
1378
|
-
}
|
|
1379
|
-
`,
|
|
1380
|
-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
1381
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1382
|
-
providers: [
|
|
1383
|
-
{
|
|
1384
|
-
provide: NGT_STORE,
|
|
1385
|
-
useFactory: (previousStore) => {
|
|
1386
|
-
const pointer = new THREE.Vector2();
|
|
1387
|
-
const raycaster = new THREE.Raycaster();
|
|
1388
|
-
const { id: _skipId, ...previousState } = previousStore.snapshot;
|
|
1389
|
-
const store = signalState({
|
|
1390
|
-
id: makeId(),
|
|
1391
|
-
...previousState,
|
|
1392
|
-
scene: null,
|
|
1393
|
-
previousRoot: previousStore,
|
|
1394
|
-
pointer,
|
|
1395
|
-
raycaster,
|
|
1396
|
-
});
|
|
1397
|
-
store.update(mergeState(previousStore, store, null, pointer, raycaster));
|
|
1398
|
-
return store;
|
|
1399
|
-
},
|
|
1400
|
-
deps: [[new SkipSelf(), NGT_STORE]],
|
|
1401
|
-
},
|
|
1402
|
-
],
|
|
1403
|
-
}]
|
|
1404
|
-
}], ctorParameters: () => [] });
|
|
1405
|
-
const NgtPortal = [NgtPortalImpl, NgtPortalContent];
|
|
1406
|
-
|
|
1407
1364
|
/**
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1365
|
+
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
1366
|
+
* @since v4.0.0~
|
|
1367
|
+
*/
|
|
1368
|
+
function _injectLoader(loaderConstructorFactory, inputs, { extensions, onProgress, onLoad, injector, } = {}) {
|
|
1369
|
+
return assertInjector(_injectLoader, injector, () => {
|
|
1370
|
+
const response = signal(null);
|
|
1371
|
+
const cachedResultPromisesEffect = load(loaderConstructorFactory, inputs, {
|
|
1372
|
+
extensions,
|
|
1373
|
+
onProgress,
|
|
1374
|
+
onLoad: onLoad,
|
|
1375
|
+
});
|
|
1376
|
+
effect(() => {
|
|
1377
|
+
const originalUrls = inputs();
|
|
1378
|
+
const cachedResultPromises = cachedResultPromisesEffect();
|
|
1379
|
+
Promise.all(cachedResultPromises).then((results) => {
|
|
1380
|
+
response.update(() => {
|
|
1381
|
+
if (Array.isArray(originalUrls))
|
|
1382
|
+
return results;
|
|
1383
|
+
if (typeof originalUrls === 'string')
|
|
1384
|
+
return results[0];
|
|
1385
|
+
const keys = Object.keys(originalUrls);
|
|
1386
|
+
return keys.reduce((result, key) => {
|
|
1387
|
+
// @ts-ignore
|
|
1388
|
+
result[key] = results[keys.indexOf(key)];
|
|
1389
|
+
return result;
|
|
1390
|
+
}, {});
|
|
1391
|
+
});
|
|
1392
|
+
});
|
|
1393
|
+
});
|
|
1394
|
+
return response.asReadonly();
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
_injectLoader.preload = (loaderConstructorFactory, inputs, extensions, onLoad) => {
|
|
1398
|
+
const effects = load(loaderConstructorFactory, inputs, { extensions, onLoad })();
|
|
1399
|
+
if (effects) {
|
|
1400
|
+
void Promise.all(effects);
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
_injectLoader.destroy = () => {
|
|
1404
|
+
cached$1.clear();
|
|
1405
|
+
};
|
|
1406
|
+
_injectLoader.clear = (urls) => {
|
|
1407
|
+
const urlToClear = Array.isArray(urls) ? urls : [urls];
|
|
1408
|
+
urlToClear.forEach((url) => {
|
|
1409
|
+
cached$1.delete(url);
|
|
1410
|
+
});
|
|
1411
|
+
};
|
|
1412
|
+
/**
|
|
1413
|
+
* @deprecated Use loaderResource instead. Will be removed in v5.0.0
|
|
1414
|
+
* @since v4.0.0~
|
|
1410
1415
|
*/
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
capturedMap.delete(pointerId);
|
|
1418
|
-
captureData.target.releasePointerCapture(pointerId);
|
|
1419
|
-
}
|
|
1416
|
+
const injectLoader = _injectLoader;
|
|
1417
|
+
|
|
1418
|
+
function normalizeInputs(input) {
|
|
1419
|
+
let urls = [];
|
|
1420
|
+
if (Array.isArray(input)) {
|
|
1421
|
+
urls = input;
|
|
1420
1422
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
const { internal } = store.snapshot;
|
|
1424
|
-
// Removes every trace of an object from the data store
|
|
1425
|
-
internal.interaction = internal.interaction.filter((o) => o !== object);
|
|
1426
|
-
internal.initialHits = internal.initialHits.filter((o) => o !== object);
|
|
1427
|
-
internal.hovered.forEach((value, key) => {
|
|
1428
|
-
if (value.eventObject === object || value.object === object) {
|
|
1429
|
-
// Clear out intersects, they are outdated by now
|
|
1430
|
-
internal.hovered.delete(key);
|
|
1431
|
-
}
|
|
1432
|
-
});
|
|
1433
|
-
internal.capturedMap.forEach((captures, pointerId) => {
|
|
1434
|
-
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
|
-
function createEvents(store) {
|
|
1438
|
-
/** Calculates delta */
|
|
1439
|
-
function calculateDistance(event) {
|
|
1440
|
-
const internal = store.snapshot.internal;
|
|
1441
|
-
const dx = event.offsetX - internal.initialClick[0];
|
|
1442
|
-
const dy = event.offsetY - internal.initialClick[1];
|
|
1443
|
-
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
1423
|
+
else if (typeof input === 'string') {
|
|
1424
|
+
urls = [input];
|
|
1444
1425
|
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
return objects.filter((obj) => ['move', 'over', 'enter', 'out', 'leave'].some((name) => {
|
|
1448
|
-
const eventName = `pointer${name}`;
|
|
1449
|
-
return getInstanceState(obj)?.handlers?.[eventName];
|
|
1450
|
-
}));
|
|
1426
|
+
else {
|
|
1427
|
+
urls = Object.values(input);
|
|
1451
1428
|
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
return objState.raycaster.camera ? objState.raycaster.intersectObject(obj, true) : [];
|
|
1484
|
-
}
|
|
1485
|
-
// Collect events
|
|
1486
|
-
let hits = eventsObjects
|
|
1487
|
-
// Intersect objects
|
|
1488
|
-
.flatMap(handleRaycast)
|
|
1489
|
-
// Sort by event priority and distance
|
|
1490
|
-
.sort((a, b) => {
|
|
1491
|
-
const aState = getInstanceState(a.object)?.store?.snapshot;
|
|
1492
|
-
const bState = getInstanceState(b.object)?.store?.snapshot;
|
|
1493
|
-
if (!aState || !bState)
|
|
1494
|
-
return a.distance - b.distance;
|
|
1495
|
-
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
1496
|
-
})
|
|
1497
|
-
// Filter out duplicates
|
|
1498
|
-
.filter((item) => {
|
|
1499
|
-
const id = makeId(item);
|
|
1500
|
-
if (duplicates.has(id))
|
|
1501
|
-
return false;
|
|
1502
|
-
duplicates.add(id);
|
|
1503
|
-
return true;
|
|
1429
|
+
return urls.map((url) => (url.includes('undefined') || url.includes('null') || !url ? '' : url));
|
|
1430
|
+
}
|
|
1431
|
+
const cached = new Map();
|
|
1432
|
+
const memoizedLoaders = new WeakMap();
|
|
1433
|
+
function getLoaderRequestParams(input, loaderConstructorFactory, extensions) {
|
|
1434
|
+
const urls = input();
|
|
1435
|
+
const LoaderConstructor = loaderConstructorFactory(urls);
|
|
1436
|
+
const normalizedUrls = normalizeInputs(urls);
|
|
1437
|
+
let loader = memoizedLoaders.get(LoaderConstructor);
|
|
1438
|
+
if (!loader) {
|
|
1439
|
+
loader = new LoaderConstructor();
|
|
1440
|
+
memoizedLoaders.set(LoaderConstructor, loader);
|
|
1441
|
+
}
|
|
1442
|
+
if (extensions)
|
|
1443
|
+
extensions(loader);
|
|
1444
|
+
return { urls, normalizedUrls, loader };
|
|
1445
|
+
}
|
|
1446
|
+
function getLoaderPromises(request, onProgress) {
|
|
1447
|
+
return request.normalizedUrls.map((url) => {
|
|
1448
|
+
if (url === '')
|
|
1449
|
+
return Promise.resolve(null);
|
|
1450
|
+
const cachedPromise = cached.get(url);
|
|
1451
|
+
if (cachedPromise)
|
|
1452
|
+
return cachedPromise;
|
|
1453
|
+
const promise = new Promise((res, rej) => {
|
|
1454
|
+
request.loader.load(url, (data) => {
|
|
1455
|
+
if ('scene' in data) {
|
|
1456
|
+
Object.assign(data, makeObjectGraph(data['scene']));
|
|
1457
|
+
}
|
|
1458
|
+
res(data);
|
|
1459
|
+
}, onProgress, (error) => rej(new Error(`[NGT] Could not load ${url}: ${error?.message}`)));
|
|
1504
1460
|
});
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
}
|
|
1519
|
-
// If the interaction is captured, make all capturing targets part of the intersect.
|
|
1520
|
-
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
1521
|
-
for (const captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
1522
|
-
if (!duplicates.has(makeId(captureData.intersection)))
|
|
1523
|
-
intersections.push(captureData.intersection);
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
return intersections;
|
|
1527
|
-
}
|
|
1528
|
-
/** Handles intersections by forwarding them to handlers */
|
|
1529
|
-
function handleIntersects(intersections, event, delta, callback) {
|
|
1530
|
-
const rootState = store.snapshot;
|
|
1531
|
-
// If anything has been found, forward it to the event listeners
|
|
1532
|
-
if (intersections.length) {
|
|
1533
|
-
const localState = { stopped: false };
|
|
1534
|
-
for (const hit of intersections) {
|
|
1535
|
-
let instanceState = getInstanceState(hit.object);
|
|
1536
|
-
// If the object is not managed by NGT, it might be parented to an element which is.
|
|
1537
|
-
// Traverse upwards until we find a managed parent and use its state instead.
|
|
1538
|
-
if (!instanceState) {
|
|
1539
|
-
hit.object.traverseAncestors((ancestor) => {
|
|
1540
|
-
const parentInstanceState = getInstanceState(ancestor);
|
|
1541
|
-
if (parentInstanceState) {
|
|
1542
|
-
instanceState = parentInstanceState;
|
|
1543
|
-
return false;
|
|
1544
|
-
}
|
|
1545
|
-
return;
|
|
1546
|
-
});
|
|
1461
|
+
cached.set(url, promise);
|
|
1462
|
+
return promise;
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
function loaderResource(loaderConstructorFactory, input, { extensions, onLoad, onProgress, injector, } = {}) {
|
|
1466
|
+
return assertInjector(loaderResource, injector, () => {
|
|
1467
|
+
return resource({
|
|
1468
|
+
request: () => getLoaderRequestParams(input, loaderConstructorFactory, extensions),
|
|
1469
|
+
loader: async ({ request }) => {
|
|
1470
|
+
const loadedResults = await Promise.all(getLoaderPromises(request, onProgress));
|
|
1471
|
+
let results;
|
|
1472
|
+
if (Array.isArray(request.urls)) {
|
|
1473
|
+
results = loadedResults;
|
|
1547
1474
|
}
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
const hasPointerCapture = (id) => internal.capturedMap.get(id)?.has(hit.eventObject) ?? false;
|
|
1551
|
-
const setPointerCapture = (id) => {
|
|
1552
|
-
const captureData = { intersection: hit, target: event.target };
|
|
1553
|
-
if (internal.capturedMap.has(id)) {
|
|
1554
|
-
// if the pointerId was previously captured, we add the hit to the
|
|
1555
|
-
// event capturedMap.
|
|
1556
|
-
internal.capturedMap.get(id).set(hit.eventObject, captureData);
|
|
1557
|
-
}
|
|
1558
|
-
else {
|
|
1559
|
-
// if the pointerId was not previously captured, we create a map
|
|
1560
|
-
// containing the hitObject, and the hit. hitObject is used for
|
|
1561
|
-
// faster access.
|
|
1562
|
-
internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
|
|
1563
|
-
}
|
|
1564
|
-
// Call the original event now
|
|
1565
|
-
event.target.setPointerCapture(id);
|
|
1566
|
-
};
|
|
1567
|
-
const releasePointerCapture = (id) => {
|
|
1568
|
-
const captures = internal.capturedMap.get(id);
|
|
1569
|
-
if (captures) {
|
|
1570
|
-
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
1571
|
-
}
|
|
1572
|
-
};
|
|
1573
|
-
// Add native event props
|
|
1574
|
-
const extractEventProps = {};
|
|
1575
|
-
// 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.
|
|
1576
|
-
for (const prop in event) {
|
|
1577
|
-
const property = event[prop];
|
|
1578
|
-
// Only copy over atomics, leave functions alone as these should be
|
|
1579
|
-
// called as event.nativeEvent.fn()
|
|
1580
|
-
if (typeof property !== 'function')
|
|
1581
|
-
extractEventProps[prop] = property;
|
|
1475
|
+
else if (typeof request.urls === 'string') {
|
|
1476
|
+
results = loadedResults[0];
|
|
1582
1477
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1478
|
+
else {
|
|
1479
|
+
const keys = Object.keys(request.urls);
|
|
1480
|
+
results = keys.reduce((result, key) => {
|
|
1481
|
+
// @ts-ignore
|
|
1482
|
+
result[key] = loadedResults[keys.indexOf(key)];
|
|
1483
|
+
return result;
|
|
1484
|
+
}, {});
|
|
1485
|
+
}
|
|
1486
|
+
if (onLoad) {
|
|
1487
|
+
onLoad(results);
|
|
1488
|
+
}
|
|
1489
|
+
return results;
|
|
1490
|
+
},
|
|
1491
|
+
});
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
loaderResource.preload = (loaderConstructor, inputs, extensions) => {
|
|
1495
|
+
const params = getLoaderRequestParams(() => inputs, () => loaderConstructor, extensions);
|
|
1496
|
+
void Promise.all(getLoaderPromises(params));
|
|
1497
|
+
};
|
|
1498
|
+
loaderResource.destroy = () => {
|
|
1499
|
+
cached.clear();
|
|
1500
|
+
};
|
|
1501
|
+
loaderResource.clear = (urls) => {
|
|
1502
|
+
const urlToClear = Array.isArray(urls) ? urls : [urls];
|
|
1503
|
+
urlToClear.forEach((url) => {
|
|
1504
|
+
cached.delete(url);
|
|
1505
|
+
});
|
|
1506
|
+
};
|
|
1507
|
+
|
|
1508
|
+
const RGBA_REGEX = /rgba?\((\d+),\s*(\d+),\s*(\d+),?\s*(\d*\.?\d+)?\)/;
|
|
1509
|
+
const DEFAULT_COLOR = 0x000000;
|
|
1510
|
+
class NgtHexify {
|
|
1511
|
+
constructor() {
|
|
1512
|
+
this.document = inject(DOCUMENT, { optional: true });
|
|
1513
|
+
this.cache = {};
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* transforms a:
|
|
1517
|
+
* - hex string to a hex number
|
|
1518
|
+
* - rgb string to a hex number
|
|
1519
|
+
* - rgba string to a hex number
|
|
1520
|
+
* - css color string to a hex number
|
|
1521
|
+
*
|
|
1522
|
+
* always default to black if failed
|
|
1523
|
+
* @param value
|
|
1524
|
+
*/
|
|
1525
|
+
transform(value) {
|
|
1526
|
+
if (value == null)
|
|
1527
|
+
return DEFAULT_COLOR;
|
|
1528
|
+
if (value.startsWith('#')) {
|
|
1529
|
+
if (!this.cache[value]) {
|
|
1530
|
+
this.cache[value] = this.hexStringToNumber(value);
|
|
1625
1531
|
}
|
|
1532
|
+
return this.cache[value];
|
|
1626
1533
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
const instance = getInstanceState(eventObject);
|
|
1640
|
-
const handlers = instance?.handlers;
|
|
1641
|
-
internal.hovered.delete(makeId(hoveredObj));
|
|
1642
|
-
if (instance?.eventCount) {
|
|
1643
|
-
// Clear out intersects, they are outdated by now
|
|
1644
|
-
const data = { ...hoveredObj, intersections };
|
|
1645
|
-
handlers?.pointerout?.(data);
|
|
1646
|
-
handlers?.pointerleave?.(data);
|
|
1647
|
-
}
|
|
1534
|
+
if (!this.ctx) {
|
|
1535
|
+
this.ctx = this.document?.createElement('canvas').getContext('2d');
|
|
1536
|
+
}
|
|
1537
|
+
if (!this.ctx) {
|
|
1538
|
+
console.warn('[NGT] hexify: canvas context is not available');
|
|
1539
|
+
return DEFAULT_COLOR;
|
|
1540
|
+
}
|
|
1541
|
+
this.ctx.fillStyle = value;
|
|
1542
|
+
const computedValue = this.ctx.fillStyle;
|
|
1543
|
+
if (computedValue.startsWith('#')) {
|
|
1544
|
+
if (!this.cache[computedValue]) {
|
|
1545
|
+
this.cache[computedValue] = this.hexStringToNumber(computedValue);
|
|
1648
1546
|
}
|
|
1547
|
+
return this.cache[computedValue];
|
|
1548
|
+
}
|
|
1549
|
+
if (!computedValue.startsWith('rgba')) {
|
|
1550
|
+
console.warn(`[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${computedValue}`);
|
|
1551
|
+
return DEFAULT_COLOR;
|
|
1552
|
+
}
|
|
1553
|
+
const match = computedValue.match(RGBA_REGEX);
|
|
1554
|
+
if (!match) {
|
|
1555
|
+
console.warn(`[NGT] hexify: invalid color format. Expected rgba or hex, receive: ${computedValue}`);
|
|
1556
|
+
return DEFAULT_COLOR;
|
|
1557
|
+
}
|
|
1558
|
+
const r = parseInt(match[1], 10);
|
|
1559
|
+
const g = parseInt(match[2], 10);
|
|
1560
|
+
const b = parseInt(match[3], 10);
|
|
1561
|
+
const a = match[4] ? parseFloat(match[4]) : 1.0;
|
|
1562
|
+
const cacheKey = `${r}:${g}:${b}:${a}`;
|
|
1563
|
+
// check result from cache
|
|
1564
|
+
if (!this.cache[cacheKey]) {
|
|
1565
|
+
// Convert the components to hex strings
|
|
1566
|
+
const hexR = this.componentToHex(r);
|
|
1567
|
+
const hexG = this.componentToHex(g);
|
|
1568
|
+
const hexB = this.componentToHex(b);
|
|
1569
|
+
const hexA = this.componentToHex(Math.round(a * 255));
|
|
1570
|
+
// Combine the hex components into a single hex string
|
|
1571
|
+
const hex = `#${hexR}${hexG}${hexB}${hexA}`;
|
|
1572
|
+
this.cache[cacheKey] = this.hexStringToNumber(hex);
|
|
1649
1573
|
}
|
|
1574
|
+
return this.cache[cacheKey];
|
|
1650
1575
|
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1576
|
+
hexStringToNumber(hexString) {
|
|
1577
|
+
return parseInt(hexString.replace('#', ''), 16);
|
|
1578
|
+
}
|
|
1579
|
+
componentToHex(component) {
|
|
1580
|
+
const hex = component.toString(16);
|
|
1581
|
+
return hex.length === 1 ? '0' + hex : hex;
|
|
1582
|
+
}
|
|
1583
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtHexify, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
1584
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.1", ngImport: i0, type: NgtHexify, isStandalone: true, name: "hexify" }); }
|
|
1585
|
+
}
|
|
1586
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtHexify, decorators: [{
|
|
1587
|
+
type: Pipe,
|
|
1588
|
+
args: [{ name: 'hexify', pure: true }]
|
|
1589
|
+
}] });
|
|
1590
|
+
|
|
1591
|
+
const catalogue = {};
|
|
1592
|
+
function extend(objects) {
|
|
1593
|
+
Object.assign(catalogue, objects);
|
|
1594
|
+
}
|
|
1595
|
+
const NGT_CATALOGUE = new InjectionToken('NGT_CATALOGUE', { factory: () => catalogue });
|
|
1596
|
+
function injectCatalogue() {
|
|
1597
|
+
return inject(NGT_CATALOGUE);
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
function omit(objFn, keysToOmit, equal = (a, b) => is.equ(a, b, { objects: 'shallow', arrays: 'shallow' })) {
|
|
1601
|
+
return computed(() => {
|
|
1602
|
+
const obj = objFn();
|
|
1603
|
+
const result = {};
|
|
1604
|
+
for (const key of Object.keys(obj)) {
|
|
1605
|
+
if (keysToOmit.includes(key))
|
|
1606
|
+
continue;
|
|
1607
|
+
Object.assign(result, { [key]: obj[key] });
|
|
1608
|
+
}
|
|
1609
|
+
return result;
|
|
1610
|
+
}, { equal });
|
|
1611
|
+
}
|
|
1612
|
+
function pick(objFn, keyOrKeys, equal) {
|
|
1613
|
+
if (Array.isArray(keyOrKeys)) {
|
|
1614
|
+
if (!equal) {
|
|
1615
|
+
equal = (a, b) => is.equ(a, b, { objects: 'shallow', arrays: 'shallow' });
|
|
1655
1616
|
}
|
|
1617
|
+
return computed(() => {
|
|
1618
|
+
const obj = objFn();
|
|
1619
|
+
const result = {};
|
|
1620
|
+
for (const key of keyOrKeys) {
|
|
1621
|
+
if (!(key in obj))
|
|
1622
|
+
continue;
|
|
1623
|
+
Object.assign(result, { [key]: obj[key] });
|
|
1624
|
+
}
|
|
1625
|
+
return result;
|
|
1626
|
+
}, { equal });
|
|
1656
1627
|
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1628
|
+
return computed(() => objFn()[keyOrKeys], { equal });
|
|
1629
|
+
}
|
|
1630
|
+
function merge(objFn, toMerge, mode = 'override', equal = (a, b) => is.equ(a, b, { objects: 'shallow', arrays: 'shallow' })) {
|
|
1631
|
+
if (mode === 'override')
|
|
1632
|
+
return computed(() => ({ ...objFn(), ...toMerge }), { equal });
|
|
1633
|
+
return computed(() => ({ ...toMerge, ...objFn() }), { equal });
|
|
1634
|
+
}
|
|
1635
|
+
function createVectorComputed(vectorCtor) {
|
|
1636
|
+
return ((inputOrOptions, keyOrKeepUndefined, keepUndefined) => {
|
|
1637
|
+
if (typeof keyOrKeepUndefined === 'undefined' || typeof keyOrKeepUndefined === 'boolean') {
|
|
1638
|
+
keepUndefined = !!keyOrKeepUndefined;
|
|
1639
|
+
const input = inputOrOptions;
|
|
1640
|
+
return computed(() => {
|
|
1641
|
+
const value = input();
|
|
1642
|
+
if (keepUndefined && value == undefined)
|
|
1643
|
+
return undefined;
|
|
1644
|
+
if (typeof value === 'number')
|
|
1645
|
+
return new vectorCtor().setScalar(value);
|
|
1646
|
+
else if (Array.isArray(value))
|
|
1647
|
+
return new vectorCtor(...value);
|
|
1648
|
+
else if (value)
|
|
1649
|
+
return value;
|
|
1650
|
+
else
|
|
1651
|
+
return new vectorCtor();
|
|
1652
|
+
}, { equal: (a, b) => !!a && !!b && a.equals(b) });
|
|
1653
|
+
}
|
|
1654
|
+
const options = inputOrOptions;
|
|
1655
|
+
const key = keyOrKeepUndefined;
|
|
1656
|
+
return computed(() => {
|
|
1657
|
+
const value = options()[key];
|
|
1658
|
+
if (keepUndefined && value == undefined)
|
|
1659
|
+
return undefined;
|
|
1660
|
+
if (typeof value === 'number')
|
|
1661
|
+
return new vectorCtor().setScalar(value);
|
|
1662
|
+
else if (Array.isArray(value))
|
|
1663
|
+
return new vectorCtor(...value);
|
|
1664
|
+
else if (value)
|
|
1665
|
+
return value;
|
|
1666
|
+
else
|
|
1667
|
+
return new vectorCtor();
|
|
1668
|
+
}, { equal: (a, b) => !!a && !!b && a.equals(b) });
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
const vector2 = createVectorComputed(THREE.Vector2);
|
|
1672
|
+
const vector3 = createVectorComputed(THREE.Vector3);
|
|
1673
|
+
const vector4 = createVectorComputed(THREE.Vector4);
|
|
1674
|
+
|
|
1675
|
+
class NgtPortalAutoRender {
|
|
1676
|
+
constructor() {
|
|
1677
|
+
this.portalStore = injectStore({ host: true });
|
|
1678
|
+
this.parentStore = injectStore({ skipSelf: true });
|
|
1679
|
+
this.portal = inject(NgtPortalImpl, { host: true });
|
|
1680
|
+
this.renderPriority = input(1, { alias: 'autoRender', transform: (value) => numberAttribute(value, 1) });
|
|
1681
|
+
effect(() => {
|
|
1682
|
+
// this.portalStore.update((state) => ({ events: { ...state.events, priority: this.renderPriority() + 1 } }));
|
|
1683
|
+
});
|
|
1684
|
+
effect((onCleanup) => {
|
|
1685
|
+
const portalRendered = this.portal.portalRendered();
|
|
1686
|
+
if (!portalRendered)
|
|
1687
|
+
return;
|
|
1688
|
+
// track state
|
|
1689
|
+
const [renderPriority, { internal }] = [this.renderPriority(), this.portalStore()];
|
|
1690
|
+
let oldClean;
|
|
1691
|
+
const cleanup = internal.subscribe(({ gl, scene, camera }) => {
|
|
1692
|
+
const [parentScene, parentCamera] = [
|
|
1693
|
+
this.parentStore.snapshot.scene,
|
|
1694
|
+
this.parentStore.snapshot.camera,
|
|
1695
|
+
];
|
|
1696
|
+
oldClean = gl.autoClear;
|
|
1697
|
+
if (renderPriority === 1) {
|
|
1698
|
+
// clear scene and render with default
|
|
1699
|
+
gl.autoClear = true;
|
|
1700
|
+
gl.render(parentScene, parentCamera);
|
|
1701
|
+
}
|
|
1702
|
+
// disable cleaning
|
|
1703
|
+
gl.autoClear = false;
|
|
1704
|
+
gl.clearDepth();
|
|
1705
|
+
gl.render(scene, camera);
|
|
1706
|
+
// restore
|
|
1707
|
+
gl.autoClear = oldClean;
|
|
1708
|
+
}, renderPriority, this.portalStore);
|
|
1709
|
+
onCleanup(() => cleanup());
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtPortalAutoRender, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1713
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.2.1", type: NgtPortalAutoRender, isStandalone: true, selector: "ngt-portal[autoRender]", inputs: { renderPriority: { classPropertyName: "renderPriority", publicName: "autoRender", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|
|
1714
|
+
}
|
|
1715
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtPortalAutoRender, decorators: [{
|
|
1716
|
+
type: Directive,
|
|
1717
|
+
args: [{ selector: 'ngt-portal[autoRender]' }]
|
|
1718
|
+
}], ctorParameters: () => [] });
|
|
1719
|
+
class NgtPortalContent {
|
|
1720
|
+
static ngTemplateContextGuard(_, ctx) {
|
|
1721
|
+
return true;
|
|
1722
|
+
}
|
|
1723
|
+
constructor() {
|
|
1724
|
+
const host = inject(ElementRef, { skipSelf: true });
|
|
1725
|
+
const { element } = inject(ViewContainerRef);
|
|
1726
|
+
const injector = inject(Injector);
|
|
1727
|
+
const commentNode = element.nativeElement;
|
|
1728
|
+
const store = injectStore();
|
|
1729
|
+
commentNode.data = NGT_PORTAL_CONTENT_FLAG;
|
|
1730
|
+
commentNode[NGT_PORTAL_CONTENT_FLAG] = store;
|
|
1731
|
+
commentNode[NGT_DOM_PARENT_FLAG] = host.nativeElement;
|
|
1732
|
+
if (commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]) {
|
|
1733
|
+
commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG]('portal', injector);
|
|
1734
|
+
delete commentNode[NGT_INTERNAL_ADD_COMMENT_FLAG];
|
|
1680
1735
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1736
|
+
}
|
|
1737
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtPortalContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1738
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.1", type: NgtPortalContent, isStandalone: true, selector: "ng-template[portalContent]", ngImport: i0 }); }
|
|
1739
|
+
}
|
|
1740
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtPortalContent, decorators: [{
|
|
1741
|
+
type: Directive,
|
|
1742
|
+
args: [{ selector: 'ng-template[portalContent]' }]
|
|
1743
|
+
}], ctorParameters: () => [] });
|
|
1744
|
+
function mergeState(previousRoot, store, container, pointer, raycaster, events, size) {
|
|
1745
|
+
// we never want to spread the id
|
|
1746
|
+
const { id: _, ...previousState } = previousRoot.snapshot;
|
|
1747
|
+
const state = store.snapshot;
|
|
1748
|
+
let viewport = undefined;
|
|
1749
|
+
if (state.camera && size) {
|
|
1750
|
+
const camera = state.camera;
|
|
1751
|
+
// calculate the override viewport, if present
|
|
1752
|
+
viewport = previousState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
|
|
1753
|
+
// update the portal camera, if it differs from the previous layer
|
|
1754
|
+
if (camera !== previousState.camera)
|
|
1755
|
+
updateCamera(camera, size);
|
|
1756
|
+
}
|
|
1757
|
+
return {
|
|
1758
|
+
// the intersect consists of the previous root state
|
|
1759
|
+
...previousState,
|
|
1760
|
+
...state,
|
|
1761
|
+
// portals have their own scene, which forms the root, a raycaster and a pointer
|
|
1762
|
+
scene: container,
|
|
1763
|
+
pointer,
|
|
1764
|
+
raycaster,
|
|
1765
|
+
// their previous root is the layer before it
|
|
1766
|
+
previousRoot,
|
|
1767
|
+
events: { ...previousState.events, ...state.events, ...events },
|
|
1768
|
+
size: { ...previousState.size, ...size },
|
|
1769
|
+
viewport: { ...previousState.viewport, ...viewport },
|
|
1770
|
+
// layers are allowed to override events
|
|
1771
|
+
setEvents: (events) => store.update((state) => ({ ...state, events: { ...state.events, ...events } })),
|
|
1772
|
+
};
|
|
1773
|
+
}
|
|
1774
|
+
class NgtPortalImpl {
|
|
1775
|
+
constructor() {
|
|
1776
|
+
this.container = input.required();
|
|
1777
|
+
this.state = input({});
|
|
1778
|
+
this.contentRef = contentChild.required(NgtPortalContent, { read: TemplateRef });
|
|
1779
|
+
this.anchorRef = contentChild.required(NgtPortalContent, { read: ViewContainerRef });
|
|
1780
|
+
this.previousStore = injectStore({ skipSelf: true });
|
|
1781
|
+
this.portalStore = injectStore();
|
|
1782
|
+
this.injector = inject(Injector);
|
|
1783
|
+
this.size = pick(this.state, 'size');
|
|
1784
|
+
this.events = pick(this.state, 'events');
|
|
1785
|
+
this.restState = omit(this.state, ['size', 'events']);
|
|
1786
|
+
this.portalContentRendered = signal(false);
|
|
1787
|
+
this.portalRendered = this.portalContentRendered.asReadonly();
|
|
1788
|
+
extend({ Group });
|
|
1789
|
+
effect(() => {
|
|
1790
|
+
let [container, anchor, content] = [
|
|
1791
|
+
this.container(),
|
|
1792
|
+
this.anchorRef(),
|
|
1793
|
+
this.contentRef(),
|
|
1794
|
+
this.previousStore(),
|
|
1795
|
+
];
|
|
1796
|
+
const [size, events, restState] = [untracked(this.size), untracked(this.events), untracked(this.restState)];
|
|
1797
|
+
if (!is.instance(container)) {
|
|
1798
|
+
container = prepare(container, 'ngt-portal', { store: this.portalStore });
|
|
1698
1799
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
if (delta <= 2) {
|
|
1703
|
-
pointerMissed(event, internal.interaction);
|
|
1704
|
-
pointerMissed$.next(event);
|
|
1705
|
-
}
|
|
1800
|
+
const instanceState = getInstanceState(container);
|
|
1801
|
+
if (instanceState && instanceState.store !== this.portalStore) {
|
|
1802
|
+
instanceState.store = this.portalStore;
|
|
1706
1803
|
}
|
|
1707
|
-
|
|
1708
|
-
if (
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
const eventObject = data.eventObject;
|
|
1712
|
-
const instance = getInstanceState(eventObject);
|
|
1713
|
-
const handlers = instance?.handlers;
|
|
1714
|
-
// Check presence of handlers
|
|
1715
|
-
if (!instance?.eventCount)
|
|
1716
|
-
return;
|
|
1717
|
-
/*
|
|
1718
|
-
MAYBE TODO, DELETE IF NOT:
|
|
1719
|
-
Check if the object is captured, captured events should not have intersects running in parallel
|
|
1720
|
-
But wouldn't it be better to just replace capturedMap with a single entry?
|
|
1721
|
-
Also, are we OK with straight up making picking up multiple objects impossible?
|
|
1722
|
-
|
|
1723
|
-
const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
|
|
1724
|
-
if (pointerId !== undefined) {
|
|
1725
|
-
const capturedMeshSet = internal.capturedMap.get(pointerId)
|
|
1726
|
-
if (capturedMeshSet) {
|
|
1727
|
-
const captured = capturedMeshSet.get(eventObject)
|
|
1728
|
-
if (captured && captured.localState.stopped) return
|
|
1729
|
-
}
|
|
1730
|
-
}*/
|
|
1731
|
-
if (isPointerMove) {
|
|
1732
|
-
// Move event ...
|
|
1733
|
-
if (handlers?.pointerover ||
|
|
1734
|
-
handlers?.pointerenter ||
|
|
1735
|
-
handlers?.pointerout ||
|
|
1736
|
-
handlers?.pointerleave) {
|
|
1737
|
-
// When enter or out is present take care of hover-state
|
|
1738
|
-
const id = makeId(data);
|
|
1739
|
-
const hoveredItem = internal.hovered.get(id);
|
|
1740
|
-
if (!hoveredItem) {
|
|
1741
|
-
// If the object wasn't previously hovered, book it and call its handler
|
|
1742
|
-
internal.hovered.set(id, data);
|
|
1743
|
-
handlers.pointerover?.(data);
|
|
1744
|
-
handlers.pointerenter?.(data);
|
|
1745
|
-
}
|
|
1746
|
-
else if (hoveredItem.stopped) {
|
|
1747
|
-
// If the object was previously hovered and stopped, we shouldn't allow other items to proceed
|
|
1748
|
-
data.stopPropagation();
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
// Call mouse move
|
|
1752
|
-
handlers?.pointermove?.(data);
|
|
1753
|
-
}
|
|
1754
|
-
else {
|
|
1755
|
-
// All other events ...
|
|
1756
|
-
const handler = handlers?.[name];
|
|
1757
|
-
if (handler) {
|
|
1758
|
-
// Forward all events back to their respective handlers with the exception of click events,
|
|
1759
|
-
// which must use the initial target
|
|
1760
|
-
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
1761
|
-
// Missed events have to come first
|
|
1762
|
-
pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
|
|
1763
|
-
// Now call the handler
|
|
1764
|
-
handler(data);
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
else {
|
|
1768
|
-
// Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
|
|
1769
|
-
if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
1770
|
-
pointerMissed(event, internal.interaction.filter((object) => !internal.initialHits.includes(object)));
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1804
|
+
this.portalStore.update(restState, mergeState(this.previousStore, this.portalStore, container, this.portalStore.snapshot.pointer, this.portalStore.snapshot.raycaster, events, size));
|
|
1805
|
+
if (this.portalViewRef) {
|
|
1806
|
+
this.portalViewRef.detectChanges();
|
|
1807
|
+
return;
|
|
1774
1808
|
}
|
|
1775
|
-
|
|
1776
|
-
|
|
1809
|
+
this.portalViewRef = anchor.createEmbeddedView(content, { injector: this.injector }, { injector: this.injector });
|
|
1810
|
+
this.portalViewRef.detectChanges();
|
|
1811
|
+
this.portalContentRendered.set(true);
|
|
1812
|
+
});
|
|
1777
1813
|
}
|
|
1778
|
-
|
|
1814
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtPortalImpl, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1815
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.1", type: NgtPortalImpl, isStandalone: true, selector: "ngt-portal", inputs: { container: { classPropertyName: "container", publicName: "container", isSignal: true, isRequired: true, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
1816
|
+
{
|
|
1817
|
+
provide: NGT_STORE,
|
|
1818
|
+
useFactory: (previousStore) => {
|
|
1819
|
+
const pointer = new THREE.Vector2();
|
|
1820
|
+
const raycaster = new THREE.Raycaster();
|
|
1821
|
+
const { id: _skipId, ...previousState } = previousStore.snapshot;
|
|
1822
|
+
const store = signalState({
|
|
1823
|
+
id: makeId(),
|
|
1824
|
+
...previousState,
|
|
1825
|
+
scene: null,
|
|
1826
|
+
previousRoot: previousStore,
|
|
1827
|
+
pointer,
|
|
1828
|
+
raycaster,
|
|
1829
|
+
});
|
|
1830
|
+
store.update(mergeState(previousStore, store, null, pointer, raycaster));
|
|
1831
|
+
return store;
|
|
1832
|
+
},
|
|
1833
|
+
deps: [[new SkipSelf(), NGT_STORE]],
|
|
1834
|
+
},
|
|
1835
|
+
], queries: [{ propertyName: "contentRef", first: true, predicate: NgtPortalContent, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "anchorRef", first: true, predicate: NgtPortalContent, descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `
|
|
1836
|
+
@if (portalRendered()) {
|
|
1837
|
+
<!-- Without an element that receives pointer events state.pointer will always be 0/0 -->
|
|
1838
|
+
<ngt-group (pointerover)="(undefined)" attach="none" />
|
|
1839
|
+
}
|
|
1840
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1779
1841
|
}
|
|
1842
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: NgtPortalImpl, decorators: [{
|
|
1843
|
+
type: Component,
|
|
1844
|
+
args: [{
|
|
1845
|
+
selector: 'ngt-portal',
|
|
1846
|
+
template: `
|
|
1847
|
+
@if (portalRendered()) {
|
|
1848
|
+
<!-- Without an element that receives pointer events state.pointer will always be 0/0 -->
|
|
1849
|
+
<ngt-group (pointerover)="(undefined)" attach="none" />
|
|
1850
|
+
}
|
|
1851
|
+
`,
|
|
1852
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
1853
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
1854
|
+
providers: [
|
|
1855
|
+
{
|
|
1856
|
+
provide: NGT_STORE,
|
|
1857
|
+
useFactory: (previousStore) => {
|
|
1858
|
+
const pointer = new THREE.Vector2();
|
|
1859
|
+
const raycaster = new THREE.Raycaster();
|
|
1860
|
+
const { id: _skipId, ...previousState } = previousStore.snapshot;
|
|
1861
|
+
const store = signalState({
|
|
1862
|
+
id: makeId(),
|
|
1863
|
+
...previousState,
|
|
1864
|
+
scene: null,
|
|
1865
|
+
previousRoot: previousStore,
|
|
1866
|
+
pointer,
|
|
1867
|
+
raycaster,
|
|
1868
|
+
});
|
|
1869
|
+
store.update(mergeState(previousStore, store, null, pointer, raycaster));
|
|
1870
|
+
return store;
|
|
1871
|
+
},
|
|
1872
|
+
deps: [[new SkipSelf(), NGT_STORE]],
|
|
1873
|
+
},
|
|
1874
|
+
],
|
|
1875
|
+
}]
|
|
1876
|
+
}], ctorParameters: () => [] });
|
|
1877
|
+
const NgtPortal = [NgtPortalImpl, NgtPortalContent];
|
|
1780
1878
|
|
|
1781
1879
|
// This function prepares a set of changes to be applied to the instance
|
|
1782
1880
|
function diffProps(instance, props) {
|
|
@@ -2775,8 +2873,8 @@ class NgtRenderer2 {
|
|
|
2775
2873
|
}
|
|
2776
2874
|
|
|
2777
2875
|
const shallowLoose = { objects: 'shallow', strict: false };
|
|
2778
|
-
function
|
|
2779
|
-
return assertInjector(
|
|
2876
|
+
function canvasRootInitializer(injector) {
|
|
2877
|
+
return assertInjector(canvasRootInitializer, injector, () => {
|
|
2780
2878
|
const injectedStore = injectStore();
|
|
2781
2879
|
const loop = injectLoop();
|
|
2782
2880
|
return (canvas) => {
|
|
@@ -3080,9 +3178,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImpor
|
|
|
3080
3178
|
* )
|
|
3081
3179
|
* ```
|
|
3082
3180
|
*/
|
|
3083
|
-
function
|
|
3181
|
+
function beforeRender(cb, { priority = 0, injector } = {}) {
|
|
3084
3182
|
if (typeof priority === 'function') {
|
|
3085
|
-
const effectRef = assertInjector(
|
|
3183
|
+
const effectRef = assertInjector(beforeRender, injector, () => {
|
|
3086
3184
|
const store = injectStore();
|
|
3087
3185
|
const ref = effect((onCleanup) => {
|
|
3088
3186
|
const p = priority();
|
|
@@ -3094,13 +3192,18 @@ function injectBeforeRender(cb, { priority = 0, injector } = {}) {
|
|
|
3094
3192
|
});
|
|
3095
3193
|
return effectRef.destroy.bind(effectRef);
|
|
3096
3194
|
}
|
|
3097
|
-
return assertInjector(
|
|
3195
|
+
return assertInjector(beforeRender, injector, () => {
|
|
3098
3196
|
const store = injectStore();
|
|
3099
3197
|
const sub = store.snapshot.internal.subscribe(cb, priority, store);
|
|
3100
3198
|
inject(DestroyRef).onDestroy(() => void sub());
|
|
3101
3199
|
return sub;
|
|
3102
3200
|
});
|
|
3103
3201
|
}
|
|
3202
|
+
/**
|
|
3203
|
+
* @deprecated Use `beforeRender` instead. Will be removed in v5.0.0
|
|
3204
|
+
* @since v4.0.0
|
|
3205
|
+
*/
|
|
3206
|
+
const injectBeforeRender = beforeRender;
|
|
3104
3207
|
|
|
3105
3208
|
/**
|
|
3106
3209
|
* As host directive:
|
|
@@ -3146,7 +3249,7 @@ class NgtObjectEvents {
|
|
|
3146
3249
|
return ngtObject();
|
|
3147
3250
|
return ngtObject;
|
|
3148
3251
|
});
|
|
3149
|
-
|
|
3252
|
+
objectEvents(obj, {
|
|
3150
3253
|
click: this.emitEvent('click'),
|
|
3151
3254
|
dblclick: this.emitEvent('dblclick'),
|
|
3152
3255
|
contextmenu: this.emitEvent('contextmenu'),
|
|
@@ -3172,8 +3275,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImpor
|
|
|
3172
3275
|
type: Directive,
|
|
3173
3276
|
args: [{ selector: '[ngtObjectEvents]' }]
|
|
3174
3277
|
}], ctorParameters: () => [] });
|
|
3175
|
-
|
|
3176
|
-
|
|
3278
|
+
/**
|
|
3279
|
+
* @deprecated use objectEvents instead. Will be removed in v5.0.0
|
|
3280
|
+
* @since v4.0.0
|
|
3281
|
+
*/
|
|
3282
|
+
const injectObjectEvents = objectEvents;
|
|
3283
|
+
function objectEvents(target, events, { injector } = {}) {
|
|
3284
|
+
return assertInjector(objectEvents, injector, () => {
|
|
3177
3285
|
const renderer = inject(Renderer2);
|
|
3178
3286
|
const cleanUps = [];
|
|
3179
3287
|
effect((onCleanup) => {
|
|
@@ -3207,5 +3315,5 @@ function hasListener(...emitterRefs) {
|
|
|
3207
3315
|
* Generated bundle index. Do not edit.
|
|
3208
3316
|
*/
|
|
3209
3317
|
|
|
3210
|
-
export { NGT_APPLY_PROPS, NGT_ARGS_FLAG, NGT_CANVAS_CONTENT_FLAG, NGT_CATALOGUE, NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG, NGT_DOM_PARENT_FLAG, NGT_GET_NODE_ATTRIBUTE_FLAG, NGT_HTML_FLAG, NGT_INTERNAL_ADD_COMMENT_FLAG, NGT_INTERNAL_SET_PARENT_COMMENT_FLAG, NGT_LOOP, NGT_PARENT_FLAG, NGT_PORTAL_CONTENT_FLAG, NGT_RENDERER_NODE_FLAG, NGT_RENDERER_OPTIONS, NGT_STORE, NgtArgs, NgtHTML, NgtHexify, NgtObjectEvents, NgtParent, NgtPortal, NgtPortalAutoRender, NgtPortalContent, NgtPortalImpl, NgtRenderer2, NgtRendererFactory2, NgtRoutedScene, NgtSelect, NgtSelection, NgtSelectionApi, THREE_NATIVE_EVENTS, addAfterEffect, addEffect, addTail, applyProps, attach, checkNeedsUpdate, checkUpdate, createAttachFunction, createEvents, detach, dispose, extend, flushGlobalEffects, getEmitter, getInstanceState, getLocalState, hasListener, injectBeforeRender,
|
|
3318
|
+
export { NGT_APPLY_PROPS, NGT_ARGS_FLAG, NGT_CANVAS_CONTENT_FLAG, NGT_CATALOGUE, NGT_DELEGATE_RENDERER_DESTROY_NODE_PATCHED_FLAG, NGT_DOM_PARENT_FLAG, NGT_GET_NODE_ATTRIBUTE_FLAG, NGT_HTML_FLAG, NGT_INTERNAL_ADD_COMMENT_FLAG, NGT_INTERNAL_SET_PARENT_COMMENT_FLAG, NGT_LOOP, NGT_PARENT_FLAG, NGT_PORTAL_CONTENT_FLAG, NGT_RENDERER_NODE_FLAG, NGT_RENDERER_OPTIONS, NGT_STORE, NgtArgs, NgtHTML, NgtHexify, NgtObjectEvents, NgtParent, NgtPortal, NgtPortalAutoRender, NgtPortalContent, NgtPortalImpl, NgtRenderer2, NgtRendererFactory2, NgtRoutedScene, NgtSelect, NgtSelection, NgtSelectionApi, THREE_NATIVE_EVENTS, addAfterEffect, addEffect, addTail, applyProps, attach, beforeRender, canvasRootInitializer, checkNeedsUpdate, checkUpdate, createAttachFunction, createEvents, detach, dispose, extend, flushGlobalEffects, getEmitter, getInstanceState, getLocalState, hasListener, injectBeforeRender, injectCatalogue, injectLoader, injectLoop, injectObjectEvents, injectStore, invalidateInstance, is, loaderResource, makeCameraInstance, makeDpr, makeId, makeObjectGraph, makeRendererInstance, merge, objectEvents, omit, pick, prepare, provideHTMLDomElement, removeInteractivity, resolveInstanceKey, resolveRef, roots, signalState, storeFactory, updateCamera, vector2, vector3, vector4 };
|
|
3211
3319
|
//# sourceMappingURL=angular-three.mjs.map
|