@blotch/angular-lynx 22.2.10 → 22.2.11
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.
|
@@ -2590,6 +2590,11 @@ const flushesBefore = (first, second) => {
|
|
|
2590
2590
|
const isIOS = () => {
|
|
2591
2591
|
return ((typeof lynx !== "undefined" ? lynx.SystemInfo : void 0) ?? (typeof SystemInfo !== "undefined" ? SystemInfo : void 0))?.platform === "iOS";
|
|
2592
2592
|
};
|
|
2593
|
+
const listenForScrollsFromTheStart = (element) => {
|
|
2594
|
+
if (__WEB__ || !isIOS()) return;
|
|
2595
|
+
element.addEventListener("bindscroll", ignoreScroll);
|
|
2596
|
+
};
|
|
2597
|
+
const ignoreScroll = () => {};
|
|
2593
2598
|
const insertHidesBlurView = (parent, child) => !__WEB__ && child.tagName === "blur-view" && parent.tagName !== "blur-view" && isIOS();
|
|
2594
2599
|
const isTextish = (el) => el.tagName === "text" || el.tagName === "raw-text";
|
|
2595
2600
|
var LynxElement = class {
|
|
@@ -2615,6 +2620,7 @@ var LynxElement = class {
|
|
|
2615
2620
|
#ownTraits = null;
|
|
2616
2621
|
#addedTraits = [];
|
|
2617
2622
|
#listeners = [];
|
|
2623
|
+
#heldEvents = /* @__PURE__ */ new Map();
|
|
2618
2624
|
#webLongPress = null;
|
|
2619
2625
|
#pointerEvents = null;
|
|
2620
2626
|
#webPointer = null;
|
|
@@ -2630,6 +2636,8 @@ var LynxElement = class {
|
|
|
2630
2636
|
static #pendingAttributeRemovals = /* @__PURE__ */ new Set();
|
|
2631
2637
|
#removedAttributes = /* @__PURE__ */ new Set();
|
|
2632
2638
|
static #byNativeId = /* @__PURE__ */ new Map();
|
|
2639
|
+
static #pendingReleases = /* @__PURE__ */ new Set();
|
|
2640
|
+
#destroyed = false;
|
|
2633
2641
|
static #canonicalFor(ref) {
|
|
2634
2642
|
return _a.#byNativeId.get(__GetElementUniqueID(ref)) ?? new _a(ref);
|
|
2635
2643
|
}
|
|
@@ -2644,6 +2652,15 @@ var LynxElement = class {
|
|
|
2644
2652
|
const id = __GetElementUniqueID(this.#nativeRef);
|
|
2645
2653
|
if (_a.#byNativeId.get(id) === this) _a.#byNativeId.delete(id);
|
|
2646
2654
|
}
|
|
2655
|
+
markDestroyed() {
|
|
2656
|
+
if (this.isRootPageElement) return;
|
|
2657
|
+
this.#destroyed = true;
|
|
2658
|
+
_a.#pendingReleases.add(this);
|
|
2659
|
+
scheduleSettleFlush();
|
|
2660
|
+
}
|
|
2661
|
+
virtualChildren() {
|
|
2662
|
+
return [];
|
|
2663
|
+
}
|
|
2647
2664
|
get ownerDocument() {
|
|
2648
2665
|
return globalEvents().document;
|
|
2649
2666
|
}
|
|
@@ -2762,6 +2779,7 @@ var LynxElement = class {
|
|
|
2762
2779
|
insertBefore(newChild, refChild) {
|
|
2763
2780
|
if (newChild.isRootPageElement) return;
|
|
2764
2781
|
newChild.#cancelPendingRemoval();
|
|
2782
|
+
newChild.#keepIfDestroyed();
|
|
2765
2783
|
newChild.#removeIfMoveWouldBeLost(this);
|
|
2766
2784
|
newChild.#recreateIfDead();
|
|
2767
2785
|
if (refChild == null) this.appendChild(newChild);
|
|
@@ -2783,6 +2801,7 @@ var LynxElement = class {
|
|
|
2783
2801
|
appendChild(newChild) {
|
|
2784
2802
|
if (newChild.isRootPageElement) return;
|
|
2785
2803
|
newChild.#cancelPendingRemoval();
|
|
2804
|
+
newChild.#keepIfDestroyed();
|
|
2786
2805
|
newChild.#removeIfMoveWouldBeLost(this);
|
|
2787
2806
|
newChild.#recreateIfDead();
|
|
2788
2807
|
if (wouldFormCycle(this.element, newChild.element)) return;
|
|
@@ -2841,10 +2860,18 @@ var LynxElement = class {
|
|
|
2841
2860
|
}
|
|
2842
2861
|
}
|
|
2843
2862
|
static commitPendingRemovals() {
|
|
2844
|
-
if (_a.#pendingRemovals.size
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2863
|
+
if (_a.#pendingRemovals.size > 0) {
|
|
2864
|
+
const items = [..._a.#pendingRemovals];
|
|
2865
|
+
_a.#pendingRemovals.clear();
|
|
2866
|
+
for (const el of items) el.#doRemove();
|
|
2867
|
+
}
|
|
2868
|
+
_a.#commitPendingReleases();
|
|
2869
|
+
}
|
|
2870
|
+
static #commitPendingReleases() {
|
|
2871
|
+
if (_a.#pendingReleases.size === 0) return;
|
|
2872
|
+
const elements = [..._a.#pendingReleases];
|
|
2873
|
+
_a.#pendingReleases.clear();
|
|
2874
|
+
for (const el of elements) if (el.#destroyed && !el.#jsParent && !el._virtualParent) el.#release();
|
|
2848
2875
|
}
|
|
2849
2876
|
static #enqueueTextNormalization(el) {
|
|
2850
2877
|
_a.#pendingTextNormalization.add(el);
|
|
@@ -2896,6 +2923,11 @@ var LynxElement = class {
|
|
|
2896
2923
|
#cancelPendingRemoval() {
|
|
2897
2924
|
_a.#pendingRemovals.delete(this);
|
|
2898
2925
|
}
|
|
2926
|
+
#keepIfDestroyed() {
|
|
2927
|
+
if (!this.#destroyed) return;
|
|
2928
|
+
this.#destroyed = false;
|
|
2929
|
+
_a.#pendingReleases.delete(this);
|
|
2930
|
+
}
|
|
2899
2931
|
#removeIfMoveWouldBeLost(newParent) {
|
|
2900
2932
|
if (__WEB__ || this.#paintingDead || this._virtualParent) return;
|
|
2901
2933
|
const oldParent = __GetParent(this.element);
|
|
@@ -2903,7 +2935,7 @@ var LynxElement = class {
|
|
|
2903
2935
|
if (flushesBefore(newParent.element, oldParent)) this.#doRemove();
|
|
2904
2936
|
}
|
|
2905
2937
|
#doRemove() {
|
|
2906
|
-
const parent = this.
|
|
2938
|
+
const parent = __GetParent(this.element);
|
|
2907
2939
|
if (!parent) return;
|
|
2908
2940
|
for (const anim of this.#activeAnimations) anim.cancel();
|
|
2909
2941
|
if (this.#activeAnimations.length > 0) {
|
|
@@ -2911,7 +2943,7 @@ var LynxElement = class {
|
|
|
2911
2943
|
__AddInlineStyle(this.element, "transform", null);
|
|
2912
2944
|
this.#activeAnimations.length = 0;
|
|
2913
2945
|
}
|
|
2914
|
-
__RemoveElement(parent
|
|
2946
|
+
__RemoveElement(parent, this.element);
|
|
2915
2947
|
const jsParent = this.#jsParent;
|
|
2916
2948
|
if (jsParent) {
|
|
2917
2949
|
const i = jsParent.#children.indexOf(this);
|
|
@@ -2921,6 +2953,15 @@ var LynxElement = class {
|
|
|
2921
2953
|
}
|
|
2922
2954
|
this.#markPaintingDead();
|
|
2923
2955
|
if (jsParent && isTextish(this)) _a.#enqueueTextNormalization(jsParent);
|
|
2956
|
+
if (this.#destroyed) this.#release();
|
|
2957
|
+
}
|
|
2958
|
+
#release() {
|
|
2959
|
+
this.#destroyed = false;
|
|
2960
|
+
this.#paintingDead = true;
|
|
2961
|
+
this.deregisterNative();
|
|
2962
|
+
for (const { eventType, eventName } of this.#nativeHandlers.values()) __AddEvent(this.element, eventType, eventName, void 0);
|
|
2963
|
+
for (const child of this.#children) child.#release();
|
|
2964
|
+
for (const child of this.virtualChildren()) child.#release();
|
|
2924
2965
|
}
|
|
2925
2966
|
#markPaintingDead() {
|
|
2926
2967
|
this.#paintingDead = true;
|
|
@@ -3132,11 +3173,13 @@ var LynxElement = class {
|
|
|
3132
3173
|
}
|
|
3133
3174
|
#syncNativeHandler(entry) {
|
|
3134
3175
|
const key = nativeEventKey(entry.eventType, entry.eventName);
|
|
3176
|
+
if (this.#heldEvents.has(key)) return;
|
|
3135
3177
|
const listeners = this.#listenersLike(entry);
|
|
3136
3178
|
const current = this.#nativeHandlers.get(key);
|
|
3137
3179
|
const eventType = listeners.length ? combinedEventType(listeners) : null;
|
|
3138
3180
|
if (current?.eventType === eventType) return;
|
|
3139
3181
|
scheduleSettleFlush();
|
|
3182
|
+
this.#sendHandlersWithNextFlush();
|
|
3140
3183
|
if (current) {
|
|
3141
3184
|
__AddEvent(this.element, current.eventType, entry.eventName, void 0);
|
|
3142
3185
|
this.#nativeHandlers.delete(key);
|
|
@@ -3153,6 +3196,25 @@ var LynxElement = class {
|
|
|
3153
3196
|
value: handler.dispatch
|
|
3154
3197
|
});
|
|
3155
3198
|
}
|
|
3199
|
+
holdNativeEvent(eventName) {
|
|
3200
|
+
this.#heldEvents.set(eventName, (this.#heldEvents.get(eventName) ?? 0) + 1);
|
|
3201
|
+
this.#nativeHandlers.delete(eventName);
|
|
3202
|
+
}
|
|
3203
|
+
releaseNativeEvent(eventName) {
|
|
3204
|
+
const held = this.#heldEvents.get(eventName);
|
|
3205
|
+
if (held === void 0) return;
|
|
3206
|
+
if (held > 1) {
|
|
3207
|
+
this.#heldEvents.set(eventName, held - 1);
|
|
3208
|
+
return;
|
|
3209
|
+
}
|
|
3210
|
+
this.#heldEvents.delete(eventName);
|
|
3211
|
+
const entry = this.#listeners.find((listener) => listener.eventName === eventName && listener.eventType !== "global-bindEvent");
|
|
3212
|
+
if (entry) this.#syncNativeHandler(entry);
|
|
3213
|
+
}
|
|
3214
|
+
#sendHandlersWithNextFlush() {
|
|
3215
|
+
if (__WEB__) return;
|
|
3216
|
+
__SetDataset(this.element, { ...this.#dataset });
|
|
3217
|
+
}
|
|
3156
3218
|
#dispatch(entry, event) {
|
|
3157
3219
|
if (event && typeof event === "object") {
|
|
3158
3220
|
if (typeof event.stopPropagation !== "function") event.stopPropagation = () => {};
|
|
@@ -3269,6 +3331,11 @@ var LynxListElement = class extends LynxElement {
|
|
|
3269
3331
|
}
|
|
3270
3332
|
return children;
|
|
3271
3333
|
}
|
|
3334
|
+
virtualChildren() {
|
|
3335
|
+
const children = [];
|
|
3336
|
+
for (let child = this.#firstVirtualChild; child; child = child._virtualNext) children.push(child);
|
|
3337
|
+
return children;
|
|
3338
|
+
}
|
|
3272
3339
|
getCommittedUIChildren() {
|
|
3273
3340
|
return this.#committedUIChildren;
|
|
3274
3341
|
}
|
|
@@ -5937,6 +6004,9 @@ const noHandler = () => ({
|
|
|
5937
6004
|
});
|
|
5938
6005
|
var LynxMainThreadEvent = class LynxMainThreadEvent {
|
|
5939
6006
|
#el = inject(ElementRef).nativeElement;
|
|
6007
|
+
get #host() {
|
|
6008
|
+
return this.#el;
|
|
6009
|
+
}
|
|
5940
6010
|
#registrations = /* @__PURE__ */ new Map();
|
|
5941
6011
|
mainThreadBindtap;
|
|
5942
6012
|
mainThreadCatchtap;
|
|
@@ -5981,9 +6051,13 @@ var LynxMainThreadEvent = class LynxMainThreadEvent {
|
|
|
5981
6051
|
__workletMap[workletId] = handle;
|
|
5982
6052
|
}
|
|
5983
6053
|
if (workletId === void 0) {
|
|
5984
|
-
if (this.#registrations.delete(inputName))
|
|
6054
|
+
if (this.#registrations.delete(inputName)) {
|
|
6055
|
+
__AddEvent(elementRef, eventType, eventName, noHandler());
|
|
6056
|
+
this.#host.releaseNativeEvent?.(eventName);
|
|
6057
|
+
}
|
|
5985
6058
|
continue;
|
|
5986
6059
|
}
|
|
6060
|
+
if (!this.#registrations.has(inputName)) this.#host.holdNativeEvent?.(eventName);
|
|
5987
6061
|
__AddEvent(elementRef, eventType, eventName, {
|
|
5988
6062
|
type: "worklet",
|
|
5989
6063
|
value: {
|
|
@@ -5999,6 +6073,7 @@ var LynxMainThreadEvent = class LynxMainThreadEvent {
|
|
|
5999
6073
|
if (!elementRef) return;
|
|
6000
6074
|
for (const [inputName, { eventType, eventName }] of this.#registrations) {
|
|
6001
6075
|
__AddEvent(elementRef, eventType, eventName, noHandler());
|
|
6076
|
+
this.#host.releaseNativeEvent?.(eventName);
|
|
6002
6077
|
this.#release(inputName);
|
|
6003
6078
|
}
|
|
6004
6079
|
this.#registrations.clear();
|
|
@@ -9593,6 +9668,7 @@ const CONTEXT_EVENTS = /* @__PURE__ */ new Set([
|
|
|
9593
9668
|
"webglcontextrestored",
|
|
9594
9669
|
"webglcontextcreationerror"
|
|
9595
9670
|
]);
|
|
9671
|
+
const contextFinalizer = typeof FinalizationRegistry === "function" ? new FinalizationRegistry((free) => free()) : null;
|
|
9596
9672
|
const kebab = (name) => name.startsWith("--") ? name : name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
9597
9673
|
const createStyle = (element) => {
|
|
9598
9674
|
return new Proxy({}, {
|
|
@@ -9702,6 +9778,7 @@ var LynxCanvasElement = class extends LynxElement {
|
|
|
9702
9778
|
return null;
|
|
9703
9779
|
}
|
|
9704
9780
|
this.#context = new LynxWebGL2RenderingContext(bridge, contextId, this, resolved);
|
|
9781
|
+
contextFinalizer?.register(this, () => bridge.destroy(contextId));
|
|
9705
9782
|
return this.#context;
|
|
9706
9783
|
}
|
|
9707
9784
|
addEventListener(name, cb) {
|
|
@@ -9840,7 +9917,11 @@ var LynxDocument = class {
|
|
|
9840
9917
|
/* v8 ignore next */
|
|
9841
9918
|
if (__PROFILE__) devStats.elementCreated++;
|
|
9842
9919
|
scheduleSettleFlush();
|
|
9843
|
-
if (tag === "list")
|
|
9920
|
+
if (tag === "list") {
|
|
9921
|
+
const list = createListElement(this.#pageId);
|
|
9922
|
+
listenForScrollsFromTheStart(list);
|
|
9923
|
+
return list;
|
|
9924
|
+
}
|
|
9844
9925
|
if (tag === "page") {
|
|
9845
9926
|
const result = createPageElement(this.page, this.#pageElementRequested);
|
|
9846
9927
|
this.#pageElementRequested = result.pageElementRequested;
|
|
@@ -9851,6 +9932,7 @@ var LynxDocument = class {
|
|
|
9851
9932
|
el.tagName = tag;
|
|
9852
9933
|
if (tag === "raw-text") el.setInitialText(value ?? "");
|
|
9853
9934
|
if (tag === "blur-view") el.updateBlurMaterial();
|
|
9935
|
+
if (tag === "scroll-view") listenForScrollsFromTheStart(el);
|
|
9854
9936
|
return el;
|
|
9855
9937
|
}
|
|
9856
9938
|
createText(value) {
|
|
@@ -9994,7 +10076,7 @@ var LynxRenderer = class {
|
|
|
9994
10076
|
return this.#document.createText(collapseWhitespace(value));
|
|
9995
10077
|
}
|
|
9996
10078
|
destroyNode = (node) => {
|
|
9997
|
-
node.
|
|
10079
|
+
node.markDestroyed?.();
|
|
9998
10080
|
node.releaseContext?.();
|
|
9999
10081
|
};
|
|
10000
10082
|
appendChild(parent, newChild) {
|
|
@@ -11407,7 +11489,7 @@ const sizeOf = (width, height) => [{
|
|
|
11407
11489
|
inlineSize: width,
|
|
11408
11490
|
blockSize: height
|
|
11409
11491
|
}];
|
|
11410
|
-
const entryFor
|
|
11492
|
+
const entryFor = (target, rect) => {
|
|
11411
11493
|
const ratio = pixelRatio();
|
|
11412
11494
|
return {
|
|
11413
11495
|
target,
|
|
@@ -11436,7 +11518,7 @@ var LynxResizeObserver = class {
|
|
|
11436
11518
|
const notify = (rect) => {
|
|
11437
11519
|
if (last && last.width === rect.width && last.height === rect.height) return;
|
|
11438
11520
|
last = rect;
|
|
11439
|
-
this.#callback([entryFor
|
|
11521
|
+
this.#callback([entryFor(target, rect)], this);
|
|
11440
11522
|
};
|
|
11441
11523
|
this.#observed.set(target, target.observeLayout(notify));
|
|
11442
11524
|
const rect = target.layoutRect;
|
|
@@ -11491,40 +11573,64 @@ const wrapResizeObserver = (NativeResizeObserver) => {
|
|
|
11491
11573
|
};
|
|
11492
11574
|
};
|
|
11493
11575
|
const isLynxElement = (value) => typeof value === "object" && value !== null && "element" in value && typeof value.addEventListener === "function" && typeof value.observeLayout === "function" && typeof value.parentNode === "function";
|
|
11494
|
-
const
|
|
11495
|
-
"scroll-view",
|
|
11496
|
-
"list",
|
|
11497
|
-
"viewpager",
|
|
11498
|
-
"scroll-coordinator"
|
|
11576
|
+
const SCROLL_EVENTS = /* @__PURE__ */ new Map([
|
|
11577
|
+
["scroll-view", "bindscroll"],
|
|
11578
|
+
["list", "bindscroll"],
|
|
11579
|
+
["viewpager", "bindoffsetchange"],
|
|
11580
|
+
["scroll-coordinator", "bindoffset"]
|
|
11499
11581
|
]);
|
|
11500
|
-
const
|
|
11501
|
-
|
|
11582
|
+
const measurements = /* @__PURE__ */ new Map();
|
|
11583
|
+
let sampleMeasurements = null;
|
|
11584
|
+
const measure = (element) => {
|
|
11585
|
+
const native = element.element;
|
|
11586
|
+
const shared = sampleMeasurements?.get(native) ?? measurements.get(native);
|
|
11587
|
+
if (shared) return shared;
|
|
11588
|
+
let answered = false;
|
|
11589
|
+
const rect = new Promise((resolve) => measureLayout(native, (box) => {
|
|
11590
|
+
answered = true;
|
|
11591
|
+
measurements.delete(native);
|
|
11592
|
+
resolve(box);
|
|
11593
|
+
}));
|
|
11594
|
+
sampleMeasurements?.set(native, rect);
|
|
11595
|
+
if (!answered) measurements.set(native, rect);
|
|
11596
|
+
return rect;
|
|
11597
|
+
};
|
|
11598
|
+
const measureGeometry = async (target, root) => {
|
|
11502
11599
|
const scrollContainers = [];
|
|
11503
11600
|
let top = target;
|
|
11601
|
+
let inRoot = false;
|
|
11504
11602
|
while (!top.isRootPageElement) {
|
|
11505
11603
|
const parent = top.parentNode();
|
|
11506
11604
|
if (!parent) break;
|
|
11507
|
-
if (
|
|
11605
|
+
if (parent === root) {
|
|
11606
|
+
inRoot = true;
|
|
11607
|
+
break;
|
|
11608
|
+
}
|
|
11609
|
+
if (SCROLL_EVENTS.has(parent.tagName)) scrollContainers.push(parent);
|
|
11508
11610
|
top = parent;
|
|
11509
11611
|
}
|
|
11510
|
-
|
|
11511
|
-
const
|
|
11612
|
+
if (!root) inRoot = top.isRootPageElement === true;
|
|
11613
|
+
const rootElement = root ?? (inRoot ? top : null);
|
|
11614
|
+
const clips = inRoot ? scrollContainers : [];
|
|
11615
|
+
const [box, rootBox, ...scrollBoxes] = await Promise.all([
|
|
11512
11616
|
measure(target),
|
|
11513
|
-
|
|
11514
|
-
...
|
|
11617
|
+
rootElement ? measure(rootElement) : null,
|
|
11618
|
+
...clips.map(measure)
|
|
11515
11619
|
]);
|
|
11516
11620
|
return {
|
|
11517
11621
|
box: box ?? null,
|
|
11518
|
-
root:
|
|
11519
|
-
|
|
11622
|
+
root: rootBox ?? null,
|
|
11623
|
+
inRoot,
|
|
11624
|
+
scrollContainers: scrollBoxes,
|
|
11625
|
+
scrolledBy: root && inRoot && SCROLL_EVENTS.has(root.tagName) ? [...clips, root] : clips
|
|
11520
11626
|
};
|
|
11521
11627
|
};
|
|
11522
|
-
const intersect = (a, b) => {
|
|
11628
|
+
const intersect = (a, b, touching) => {
|
|
11523
11629
|
const left = Math.max(a.left, b.left);
|
|
11524
11630
|
const top = Math.max(a.top, b.top);
|
|
11525
11631
|
const right = Math.min(a.left + a.width, b.left + b.width);
|
|
11526
11632
|
const bottom = Math.min(a.top + a.height, b.top + b.height);
|
|
11527
|
-
return right > left && bottom > top ? {
|
|
11633
|
+
return (touching ? right >= left && bottom >= top : right > left && bottom > top) ? {
|
|
11528
11634
|
left,
|
|
11529
11635
|
top,
|
|
11530
11636
|
width: right - left,
|
|
@@ -11533,11 +11639,11 @@ const intersect = (a, b) => {
|
|
|
11533
11639
|
};
|
|
11534
11640
|
const visiblePart = ({ box, root, scrollContainers }) => {
|
|
11535
11641
|
let visible = box;
|
|
11536
|
-
for (const clip of [root, ...scrollContainers]) visible = visible && clip ? intersect(visible, clip) : null;
|
|
11642
|
+
for (const clip of [root, ...scrollContainers]) visible = visible && clip ? intersect(visible, clip, false) : null;
|
|
11537
11643
|
return visible;
|
|
11538
11644
|
};
|
|
11539
11645
|
const area = (rect) => rect.width * rect.height;
|
|
11540
|
-
const
|
|
11646
|
+
const exposureEntryFor = (target, geometry, exposed) => {
|
|
11541
11647
|
const visible = visiblePart(geometry);
|
|
11542
11648
|
const isIntersecting = exposed ?? visible !== null;
|
|
11543
11649
|
const { box } = geometry;
|
|
@@ -11553,6 +11659,51 @@ const entryFor = (target, geometry, exposed) => {
|
|
|
11553
11659
|
isIntersecting
|
|
11554
11660
|
};
|
|
11555
11661
|
};
|
|
11662
|
+
const grow = (rect, margin) => {
|
|
11663
|
+
const [top, right, bottom, left] = margin.map((side, index) => side.percent ? side.value * (index % 2 ? rect.width : rect.height) / 100 : side.value);
|
|
11664
|
+
return {
|
|
11665
|
+
left: rect.left - left,
|
|
11666
|
+
top: rect.top - top,
|
|
11667
|
+
width: Math.max(rect.width + left + right, 0),
|
|
11668
|
+
height: Math.max(rect.height + top + bottom, 0)
|
|
11669
|
+
};
|
|
11670
|
+
};
|
|
11671
|
+
const thresholdIndexOf = (ratio, thresholds) => {
|
|
11672
|
+
let index = 0;
|
|
11673
|
+
while (index < thresholds.length && thresholds[index] <= ratio + 1e-6) index++;
|
|
11674
|
+
return index;
|
|
11675
|
+
};
|
|
11676
|
+
const measuredEntryFor = (target, geometry, margin, thresholds, exposed) => {
|
|
11677
|
+
const { box } = geometry;
|
|
11678
|
+
const rootBounds = geometry.root && grow(geometry.root, margin);
|
|
11679
|
+
let visible = null;
|
|
11680
|
+
let overlaps = false;
|
|
11681
|
+
let ratio = 0;
|
|
11682
|
+
if (!box) {
|
|
11683
|
+
overlaps = exposed === true;
|
|
11684
|
+
ratio = overlaps ? 1 : 0;
|
|
11685
|
+
} else if (geometry.inRoot && rootBounds) {
|
|
11686
|
+
visible = box;
|
|
11687
|
+
for (const clip of [...geometry.scrollContainers, rootBounds]) visible = visible && clip ? intersect(visible, clip, true) : null;
|
|
11688
|
+
if (visible) {
|
|
11689
|
+
overlaps = true;
|
|
11690
|
+
ratio = area(box) === 0 ? 1 : Math.min(area(visible) / area(box), 1);
|
|
11691
|
+
}
|
|
11692
|
+
}
|
|
11693
|
+
const index = overlaps ? thresholdIndexOf(ratio, thresholds) : 0;
|
|
11694
|
+
return {
|
|
11695
|
+
index,
|
|
11696
|
+
entry: {
|
|
11697
|
+
target,
|
|
11698
|
+
time: performance.now(),
|
|
11699
|
+
rootBounds: rootBounds ? toDOMRect(rootBounds) : null,
|
|
11700
|
+
boundingClientRect: toDOMRect(box),
|
|
11701
|
+
intersectionRect: toDOMRect(visible),
|
|
11702
|
+
intersectionRatio: ratio,
|
|
11703
|
+
isIntersecting: index > 0
|
|
11704
|
+
}
|
|
11705
|
+
};
|
|
11706
|
+
};
|
|
11556
11707
|
const thresholdsOf = (threshold) => {
|
|
11557
11708
|
const given = threshold === void 0 ? [] : Array.isArray(threshold) ? threshold.map(Number) : [Number(threshold)];
|
|
11558
11709
|
for (const value of given) if (!(value >= 0 && value <= 1)) throw new RangeError("Failed to construct 'IntersectionObserver': Threshold values must be numbers between 0 and 1");
|
|
@@ -11561,26 +11712,53 @@ const thresholdsOf = (threshold) => {
|
|
|
11561
11712
|
const rootMarginOf = (margin) => {
|
|
11562
11713
|
const tokens = String(margin ?? "").trim().split(/\s+/).filter(Boolean);
|
|
11563
11714
|
if (tokens.length > 4) throw invalidRootMargin();
|
|
11564
|
-
const [top =
|
|
11715
|
+
const [top = NO_MARGIN, right = top, bottom = top, left = right] = tokens.map((token) => {
|
|
11565
11716
|
const match = /^(-?(?:\d+\.?\d*|\.\d+))(px|%)?$/.exec(token);
|
|
11566
11717
|
if (!match || !match[2] && Number(match[1]) !== 0) throw invalidRootMargin();
|
|
11567
|
-
return match[2] ?
|
|
11718
|
+
return match[2] ? {
|
|
11719
|
+
text: token,
|
|
11720
|
+
value: Number(match[1]),
|
|
11721
|
+
percent: match[2] === "%"
|
|
11722
|
+
} : NO_MARGIN;
|
|
11568
11723
|
});
|
|
11569
|
-
return
|
|
11724
|
+
return [
|
|
11725
|
+
top,
|
|
11726
|
+
right,
|
|
11727
|
+
bottom,
|
|
11728
|
+
left
|
|
11729
|
+
];
|
|
11730
|
+
};
|
|
11731
|
+
const NO_MARGIN = {
|
|
11732
|
+
text: "0px",
|
|
11733
|
+
value: 0,
|
|
11734
|
+
percent: false
|
|
11570
11735
|
};
|
|
11571
11736
|
const invalidRootMargin = () => /* @__PURE__ */ new SyntaxError("Failed to construct 'IntersectionObserver': rootMargin must be specified in pixels or percent.");
|
|
11572
|
-
const
|
|
11573
|
-
|
|
11574
|
-
const
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11737
|
+
const SAMPLE_INTERVAL = 50;
|
|
11738
|
+
const SAMPLES_AFTER_SCROLL = 6;
|
|
11739
|
+
const scrolling = /* @__PURE__ */ new Map();
|
|
11740
|
+
let samplingTimer = null;
|
|
11741
|
+
const sampleWhileScrolling = (sample) => {
|
|
11742
|
+
scrolling.set(sample, SAMPLES_AFTER_SCROLL);
|
|
11743
|
+
if (samplingTimer === null) samplingTimer = setTimeout(sampleScrolling, SAMPLE_INTERVAL);
|
|
11744
|
+
};
|
|
11745
|
+
const sampleScrolling = () => {
|
|
11746
|
+
samplingTimer = null;
|
|
11747
|
+
sampleMeasurements = /* @__PURE__ */ new Map();
|
|
11748
|
+
for (const [sample, left] of Array.from(scrolling)) {
|
|
11749
|
+
if (left > 1) scrolling.set(sample, left - 1);
|
|
11750
|
+
else scrolling.delete(sample);
|
|
11751
|
+
sample();
|
|
11752
|
+
}
|
|
11753
|
+
sampleMeasurements = null;
|
|
11754
|
+
if (scrolling.size && samplingTimer === null) samplingTimer = setTimeout(sampleScrolling, SAMPLE_INTERVAL);
|
|
11755
|
+
};
|
|
11756
|
+
const stopSampling = (sample) => {
|
|
11757
|
+
scrolling.delete(sample);
|
|
11758
|
+
if (!scrolling.size && samplingTimer !== null) {
|
|
11759
|
+
clearTimeout(samplingTimer);
|
|
11760
|
+
samplingTimer = null;
|
|
11761
|
+
}
|
|
11584
11762
|
};
|
|
11585
11763
|
var LynxIntersectionObserver = class {
|
|
11586
11764
|
static forLynxElements = true;
|
|
@@ -11588,6 +11766,9 @@ var LynxIntersectionObserver = class {
|
|
|
11588
11766
|
rootMargin;
|
|
11589
11767
|
thresholds;
|
|
11590
11768
|
#callback;
|
|
11769
|
+
#root;
|
|
11770
|
+
#margin;
|
|
11771
|
+
#measures;
|
|
11591
11772
|
#observed = /* @__PURE__ */ new Map();
|
|
11592
11773
|
#records = [];
|
|
11593
11774
|
#delivering = false;
|
|
@@ -11595,39 +11776,56 @@ var LynxIntersectionObserver = class {
|
|
|
11595
11776
|
if (typeof callback !== "function") throw new TypeError("Failed to construct 'IntersectionObserver': parameter 1 is not of type 'Function'.");
|
|
11596
11777
|
this.#callback = callback;
|
|
11597
11778
|
const root = options.root;
|
|
11598
|
-
|
|
11599
|
-
this
|
|
11779
|
+
if (root === void 0 || root === null || root === globalThis.document) this.#root = null;
|
|
11780
|
+
else if (isLynxElement(root)) this.#root = root;
|
|
11781
|
+
else throw new TypeError("Failed to construct 'IntersectionObserver': Failed to read the 'root' property from 'IntersectionObserverInit': The provided value is not of type '(Document or Element)'.");
|
|
11782
|
+
this.root = this.#root;
|
|
11783
|
+
this.#margin = rootMarginOf(options.rootMargin);
|
|
11784
|
+
this.rootMargin = this.#margin.map((side) => side.text).join(" ");
|
|
11600
11785
|
this.thresholds = thresholdsOf(options.threshold);
|
|
11601
|
-
|
|
11786
|
+
this.#measures = this.#root !== null || this.#margin.some((side) => side.value !== 0) || this.thresholds.length !== 1 || this.thresholds[0] !== 0;
|
|
11602
11787
|
}
|
|
11603
11788
|
observe(target) {
|
|
11604
11789
|
if (!isLynxElement(target)) throw new TypeError("Failed to execute 'observe' on 'IntersectionObserver': parameter 1 is not of type 'Element'.");
|
|
11605
11790
|
if (this.#observed.has(target)) return;
|
|
11606
11791
|
const stopAppear = target.addEventListener("binduiappear", () => {
|
|
11607
|
-
this.#
|
|
11792
|
+
this.#exposure(target, observation, true);
|
|
11608
11793
|
});
|
|
11609
11794
|
const stopDisappear = target.addEventListener("binduidisappear", () => {
|
|
11610
|
-
this.#
|
|
11795
|
+
this.#exposure(target, observation, false);
|
|
11611
11796
|
});
|
|
11612
11797
|
let laidOut = false;
|
|
11613
|
-
const
|
|
11798
|
+
const layout = () => {
|
|
11799
|
+
if (this.#measures) {
|
|
11800
|
+
observation.sample();
|
|
11801
|
+
return;
|
|
11802
|
+
}
|
|
11614
11803
|
if (laidOut) return;
|
|
11615
11804
|
laidOut = true;
|
|
11616
11805
|
stopLayout();
|
|
11617
11806
|
this.#report(target, observation, null);
|
|
11618
11807
|
};
|
|
11619
|
-
const stopLayout = target.observeLayout(
|
|
11808
|
+
const stopLayout = target.observeLayout(layout);
|
|
11620
11809
|
const observation = {
|
|
11621
11810
|
stop: () => {
|
|
11622
11811
|
stopAppear();
|
|
11623
11812
|
stopDisappear();
|
|
11624
11813
|
stopLayout();
|
|
11814
|
+
for (const stopScroll of observation.scrollListeners.values()) stopScroll();
|
|
11815
|
+
observation.scrollListeners.clear();
|
|
11816
|
+
stopSampling(observation.sample);
|
|
11625
11817
|
},
|
|
11626
|
-
|
|
11627
|
-
|
|
11818
|
+
index: null,
|
|
11819
|
+
exposed: null,
|
|
11820
|
+
events: 0,
|
|
11821
|
+
measuring: false,
|
|
11822
|
+
again: false,
|
|
11823
|
+
measured: false,
|
|
11824
|
+
scrollListeners: /* @__PURE__ */ new Map(),
|
|
11825
|
+
sample: () => this.#sample(target, observation)
|
|
11628
11826
|
};
|
|
11629
11827
|
this.#observed.set(target, observation);
|
|
11630
|
-
if (target.layoutRect) queueMicrotask(
|
|
11828
|
+
if (target.layoutRect) queueMicrotask(layout);
|
|
11631
11829
|
}
|
|
11632
11830
|
unobserve(target) {
|
|
11633
11831
|
const observation = this.#observed.get(target);
|
|
@@ -11644,14 +11842,61 @@ var LynxIntersectionObserver = class {
|
|
|
11644
11842
|
takeRecords() {
|
|
11645
11843
|
return this.#records.splice(0);
|
|
11646
11844
|
}
|
|
11845
|
+
#exposure(target, observation, exposed) {
|
|
11846
|
+
observation.exposed = exposed;
|
|
11847
|
+
if (this.#measures) observation.sample();
|
|
11848
|
+
else this.#report(target, observation, exposed);
|
|
11849
|
+
}
|
|
11647
11850
|
async #report(target, observation, exposed) {
|
|
11648
11851
|
const event = exposed === null ? 0 : ++observation.events;
|
|
11649
|
-
const geometry = await measureGeometry(target);
|
|
11852
|
+
const geometry = await measureGeometry(target, null);
|
|
11650
11853
|
if (this.#observed.get(target) !== observation) return;
|
|
11651
11854
|
if (observation.events !== event) return;
|
|
11652
|
-
const entry =
|
|
11653
|
-
|
|
11654
|
-
observation.
|
|
11855
|
+
const entry = exposureEntryFor(target, geometry, exposed);
|
|
11856
|
+
const index = entry.isIntersecting ? 1 : 0;
|
|
11857
|
+
if (index === observation.index) return;
|
|
11858
|
+
observation.index = index;
|
|
11859
|
+
this.#queue(entry);
|
|
11860
|
+
}
|
|
11861
|
+
#sample(target, observation) {
|
|
11862
|
+
if (this.#observed.get(target) !== observation) return;
|
|
11863
|
+
if (observation.measuring) {
|
|
11864
|
+
observation.again = true;
|
|
11865
|
+
return;
|
|
11866
|
+
}
|
|
11867
|
+
observation.measuring = true;
|
|
11868
|
+
measureGeometry(target, this.#root).then((geometry) => {
|
|
11869
|
+
observation.measuring = false;
|
|
11870
|
+
if (this.#observed.get(target) !== observation) return;
|
|
11871
|
+
this.#listenForScrolls(observation, geometry.scrolledBy);
|
|
11872
|
+
if (geometry.box) observation.measured = true;
|
|
11873
|
+
const { entry, index } = measuredEntryFor(target, geometry, this.#margin, this.thresholds, observation.measured ? null : observation.exposed);
|
|
11874
|
+
if (index !== observation.index) {
|
|
11875
|
+
observation.index = index;
|
|
11876
|
+
this.#queue(entry);
|
|
11877
|
+
}
|
|
11878
|
+
if (observation.again) {
|
|
11879
|
+
observation.again = false;
|
|
11880
|
+
observation.sample();
|
|
11881
|
+
}
|
|
11882
|
+
});
|
|
11883
|
+
}
|
|
11884
|
+
#listenForScrolls(observation, scrolledBy) {
|
|
11885
|
+
const current = new Set(scrolledBy);
|
|
11886
|
+
for (const [scroller, stopScroll] of observation.scrollListeners) {
|
|
11887
|
+
if (current.has(scroller)) continue;
|
|
11888
|
+
stopScroll();
|
|
11889
|
+
observation.scrollListeners.delete(scroller);
|
|
11890
|
+
}
|
|
11891
|
+
for (const scroller of scrolledBy) {
|
|
11892
|
+
if (observation.scrollListeners.has(scroller)) continue;
|
|
11893
|
+
const event = SCROLL_EVENTS.get(scroller.tagName);
|
|
11894
|
+
observation.scrollListeners.set(scroller, scroller.addEventListener(event, () => {
|
|
11895
|
+
sampleWhileScrolling(observation.sample);
|
|
11896
|
+
}));
|
|
11897
|
+
}
|
|
11898
|
+
}
|
|
11899
|
+
#queue(entry) {
|
|
11655
11900
|
this.#records.push(entry);
|
|
11656
11901
|
this.#deliverSoon();
|
|
11657
11902
|
}
|