bunnyquery 1.8.15 → 1.8.17
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/bunnyquery.js +66 -139
- package/dist/engine.cjs +49 -114
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +42 -49
- package/dist/engine.d.ts +42 -49
- package/dist/engine.mjs +49 -114
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/scroll_anchor.ts +171 -318
package/dist/engine.d.mts
CHANGED
|
@@ -1815,32 +1815,54 @@ declare function shouldRescueInFlightMessage(m: ChatMessage, ctx: RescueDecision
|
|
|
1815
1815
|
* prepends, a poll resolves, an indexing row splices in or changes label, a link
|
|
1816
1816
|
* chip goes grey, an image preview finishes decoding, the "Fetching history..."
|
|
1817
1817
|
* bar appears and disappears. Every one of those changes the height of something
|
|
1818
|
-
* that may sit ABOVE the viewport, and the browser answers by keeping scrollTop
|
|
1819
|
-
* which slides the sentence the
|
|
1820
|
-
*
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
*
|
|
1824
|
-
*
|
|
1825
|
-
*
|
|
1818
|
+
* that may sit ABOVE the viewport, and the browser answers by keeping scrollTop,
|
|
1819
|
+
* which slides the sentence the reader was on out from under them.
|
|
1820
|
+
*
|
|
1821
|
+
* THE ONE RULE: nothing here stores a position to be applied later. Every method
|
|
1822
|
+
* acts at the moment it is called, against the box as it is at that moment, and
|
|
1823
|
+
* is finished when it returns.
|
|
1824
|
+
*
|
|
1825
|
+
* That rule is the whole design, and it was learned the hard way. An earlier
|
|
1826
|
+
* version of this module also had a "park the place they left, put them back when
|
|
1827
|
+
* they return" half — parked/parkedStuck/returning/returnBudget/sawFrozen, a
|
|
1828
|
+
* frozen mode, a retry budget. Every one of those was a stored instruction that
|
|
1829
|
+
* some later event would carry out, and the reader's experience of a stored
|
|
1830
|
+
* instruction is that the chat throws them somewhere for no reason they can see:
|
|
1831
|
+
* they came back from another app, tapped the composer, the on-screen keyboard
|
|
1832
|
+
* fired a resize, the resize triggered a fetch, the fetch settled, and the settle
|
|
1833
|
+
* dutifully executed an instruction recorded before they ever left. Compensating
|
|
1834
|
+
* at the moment of the change needs no such instruction, and a return then needs
|
|
1835
|
+
* no handling at all, because nothing moved the reader in the first place.
|
|
1836
|
+
*
|
|
1837
|
+
* Two shapes, both immediate:
|
|
1826
1838
|
*
|
|
1827
1839
|
* preserve(fn) / capture() + restore(a)
|
|
1828
1840
|
* A mutation you can bracket. Measures immediately before and immediately
|
|
1829
1841
|
* after, so it is exact even when the mutation tears the list down.
|
|
1830
1842
|
*
|
|
1831
1843
|
* remember() + hold()
|
|
1832
|
-
* A layout change you CANNOT bracket
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1838
|
-
*
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1844
|
+
* A layout change you CANNOT bracket: an image decoding, a font arriving, a
|
|
1845
|
+
* re-parse from a promise. remember() runs from the view's scroll handler,
|
|
1846
|
+
* so the anchor is always the reader's own last position.
|
|
1847
|
+
*
|
|
1848
|
+
* hold() is not an exception to the rule, and the staleness check is why. A height
|
|
1849
|
+
* change above the viewport does NOT change scrollTop; the browser preserves it,
|
|
1850
|
+
* which is precisely why the content appears to jump. So the remembered anchor is
|
|
1851
|
+
* valid exactly while `box.scrollTop` still equals the value it was captured at,
|
|
1852
|
+
* i.e. while nothing at all has moved the box. The instant that stops being true
|
|
1853
|
+
* it is re-measured, never replayed. hold() can therefore only ever undo a height
|
|
1854
|
+
* change that just happened, and can never carry out an old intention.
|
|
1855
|
+
*
|
|
1856
|
+
* Two more consequences worth stating, because both were once done the other way:
|
|
1857
|
+
*
|
|
1858
|
+
* NO FREEZE. Compensation runs whether or not the tab is visible. Layout and
|
|
1859
|
+
* getBoundingClientRect are live in a hidden tab; only painting and rAF stop.
|
|
1860
|
+
* Suspending compensation while hidden is what created the need to restore
|
|
1861
|
+
* something afterwards.
|
|
1862
|
+
*
|
|
1863
|
+
* A CLAMP IS ACCEPTED. If the content below the reader shrank, their line is
|
|
1864
|
+
* genuinely unreachable and the browser truncates the correction. Retrying that
|
|
1865
|
+
* later is how a correction turns into an ambush.
|
|
1844
1866
|
*
|
|
1845
1867
|
* DOM-free like the rest of the engine: the element shapes below are structural,
|
|
1846
1868
|
* so real DOM nodes satisfy them while this file imports nothing from lib.dom.
|
|
@@ -1914,21 +1936,6 @@ interface ScrollAnchorOptions {
|
|
|
1914
1936
|
* scrollToBottom* paths own the position, so every method here no-ops.
|
|
1915
1937
|
*/
|
|
1916
1938
|
isStuck: () => boolean;
|
|
1917
|
-
/**
|
|
1918
|
-
* The reader cannot see this box right now (the tab is hidden), so FREEZE:
|
|
1919
|
-
* remember where they were and refuse to move them.
|
|
1920
|
-
*
|
|
1921
|
-
* A hidden tab still runs everything that mutates the list — a resumed poll, a
|
|
1922
|
-
* head refresh and its deferred background batch, a settling request — and each
|
|
1923
|
-
* of those would otherwise write scrollTop against a layout nobody is looking at
|
|
1924
|
-
* and re-stamp the remembered position on the way through. The reader then comes
|
|
1925
|
-
* back to wherever the last of those writes happened to land, which is the
|
|
1926
|
-
* "somehow placed in the middle" they see, and only the NEXT correction puts
|
|
1927
|
-
* them right. So while frozen, reads still happen but nothing writes and nothing
|
|
1928
|
-
* re-stamps: the anchor holds the last position the reader actually had, and one
|
|
1929
|
-
* hold() on return puts them back on it.
|
|
1930
|
-
*/
|
|
1931
|
-
isFrozen?: () => boolean;
|
|
1932
1939
|
/**
|
|
1933
1940
|
* Fall back to the raw scrollTop when the anchored row cannot be found again.
|
|
1934
1941
|
*
|
|
@@ -1951,22 +1958,8 @@ interface ScrollAnchor {
|
|
|
1951
1958
|
remember: () => void;
|
|
1952
1959
|
/** Put the remembered place back, if it is still the reader's own. */
|
|
1953
1960
|
hold: () => void;
|
|
1954
|
-
/** The reader is going away: park the exact place they are leaving. */
|
|
1955
|
-
park: () => void;
|
|
1956
|
-
/**
|
|
1957
|
-
* They are back. Puts them on the parked place, and STAYS ARMED until it has
|
|
1958
|
-
* actually landed. Returns true when the host must pin to the bottom instead
|
|
1959
|
-
* (the reader left pinned), which only the host can do meaningfully.
|
|
1960
|
-
*/
|
|
1961
|
-
settleReturn: () => boolean;
|
|
1962
|
-
/** A return is armed: its position, not the host's, decides scrollTop. */
|
|
1963
|
-
isReturning: () => boolean;
|
|
1964
1961
|
/** Pin to the bottom, instantly, recording the write. The ONLY way to pin. */
|
|
1965
1962
|
pinBottom: () => void;
|
|
1966
|
-
/** The box is not being painted (hidden tab). Shared so hosts agree. */
|
|
1967
|
-
isFrozen: () => boolean;
|
|
1968
|
-
/** Deprecated alias of settleReturn, kept so a stale dist does not break. */
|
|
1969
|
-
thaw: () => void;
|
|
1970
1963
|
/** Absorb one element's own resize. See below. */
|
|
1971
1964
|
absorb: (el: AnchorGrowableEl | null | undefined) => void;
|
|
1972
1965
|
/** Drop the remembered place (chat switch, unmount). */
|
package/dist/engine.d.ts
CHANGED
|
@@ -1815,32 +1815,54 @@ declare function shouldRescueInFlightMessage(m: ChatMessage, ctx: RescueDecision
|
|
|
1815
1815
|
* prepends, a poll resolves, an indexing row splices in or changes label, a link
|
|
1816
1816
|
* chip goes grey, an image preview finishes decoding, the "Fetching history..."
|
|
1817
1817
|
* bar appears and disappears. Every one of those changes the height of something
|
|
1818
|
-
* that may sit ABOVE the viewport, and the browser answers by keeping scrollTop
|
|
1819
|
-
* which slides the sentence the
|
|
1820
|
-
*
|
|
1821
|
-
*
|
|
1822
|
-
*
|
|
1823
|
-
*
|
|
1824
|
-
*
|
|
1825
|
-
*
|
|
1818
|
+
* that may sit ABOVE the viewport, and the browser answers by keeping scrollTop,
|
|
1819
|
+
* which slides the sentence the reader was on out from under them.
|
|
1820
|
+
*
|
|
1821
|
+
* THE ONE RULE: nothing here stores a position to be applied later. Every method
|
|
1822
|
+
* acts at the moment it is called, against the box as it is at that moment, and
|
|
1823
|
+
* is finished when it returns.
|
|
1824
|
+
*
|
|
1825
|
+
* That rule is the whole design, and it was learned the hard way. An earlier
|
|
1826
|
+
* version of this module also had a "park the place they left, put them back when
|
|
1827
|
+
* they return" half — parked/parkedStuck/returning/returnBudget/sawFrozen, a
|
|
1828
|
+
* frozen mode, a retry budget. Every one of those was a stored instruction that
|
|
1829
|
+
* some later event would carry out, and the reader's experience of a stored
|
|
1830
|
+
* instruction is that the chat throws them somewhere for no reason they can see:
|
|
1831
|
+
* they came back from another app, tapped the composer, the on-screen keyboard
|
|
1832
|
+
* fired a resize, the resize triggered a fetch, the fetch settled, and the settle
|
|
1833
|
+
* dutifully executed an instruction recorded before they ever left. Compensating
|
|
1834
|
+
* at the moment of the change needs no such instruction, and a return then needs
|
|
1835
|
+
* no handling at all, because nothing moved the reader in the first place.
|
|
1836
|
+
*
|
|
1837
|
+
* Two shapes, both immediate:
|
|
1826
1838
|
*
|
|
1827
1839
|
* preserve(fn) / capture() + restore(a)
|
|
1828
1840
|
* A mutation you can bracket. Measures immediately before and immediately
|
|
1829
1841
|
* after, so it is exact even when the mutation tears the list down.
|
|
1830
1842
|
*
|
|
1831
1843
|
* remember() + hold()
|
|
1832
|
-
* A layout change you CANNOT bracket
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1837
|
-
*
|
|
1838
|
-
*
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1841
|
-
*
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1844
|
+
* A layout change you CANNOT bracket: an image decoding, a font arriving, a
|
|
1845
|
+
* re-parse from a promise. remember() runs from the view's scroll handler,
|
|
1846
|
+
* so the anchor is always the reader's own last position.
|
|
1847
|
+
*
|
|
1848
|
+
* hold() is not an exception to the rule, and the staleness check is why. A height
|
|
1849
|
+
* change above the viewport does NOT change scrollTop; the browser preserves it,
|
|
1850
|
+
* which is precisely why the content appears to jump. So the remembered anchor is
|
|
1851
|
+
* valid exactly while `box.scrollTop` still equals the value it was captured at,
|
|
1852
|
+
* i.e. while nothing at all has moved the box. The instant that stops being true
|
|
1853
|
+
* it is re-measured, never replayed. hold() can therefore only ever undo a height
|
|
1854
|
+
* change that just happened, and can never carry out an old intention.
|
|
1855
|
+
*
|
|
1856
|
+
* Two more consequences worth stating, because both were once done the other way:
|
|
1857
|
+
*
|
|
1858
|
+
* NO FREEZE. Compensation runs whether or not the tab is visible. Layout and
|
|
1859
|
+
* getBoundingClientRect are live in a hidden tab; only painting and rAF stop.
|
|
1860
|
+
* Suspending compensation while hidden is what created the need to restore
|
|
1861
|
+
* something afterwards.
|
|
1862
|
+
*
|
|
1863
|
+
* A CLAMP IS ACCEPTED. If the content below the reader shrank, their line is
|
|
1864
|
+
* genuinely unreachable and the browser truncates the correction. Retrying that
|
|
1865
|
+
* later is how a correction turns into an ambush.
|
|
1844
1866
|
*
|
|
1845
1867
|
* DOM-free like the rest of the engine: the element shapes below are structural,
|
|
1846
1868
|
* so real DOM nodes satisfy them while this file imports nothing from lib.dom.
|
|
@@ -1914,21 +1936,6 @@ interface ScrollAnchorOptions {
|
|
|
1914
1936
|
* scrollToBottom* paths own the position, so every method here no-ops.
|
|
1915
1937
|
*/
|
|
1916
1938
|
isStuck: () => boolean;
|
|
1917
|
-
/**
|
|
1918
|
-
* The reader cannot see this box right now (the tab is hidden), so FREEZE:
|
|
1919
|
-
* remember where they were and refuse to move them.
|
|
1920
|
-
*
|
|
1921
|
-
* A hidden tab still runs everything that mutates the list — a resumed poll, a
|
|
1922
|
-
* head refresh and its deferred background batch, a settling request — and each
|
|
1923
|
-
* of those would otherwise write scrollTop against a layout nobody is looking at
|
|
1924
|
-
* and re-stamp the remembered position on the way through. The reader then comes
|
|
1925
|
-
* back to wherever the last of those writes happened to land, which is the
|
|
1926
|
-
* "somehow placed in the middle" they see, and only the NEXT correction puts
|
|
1927
|
-
* them right. So while frozen, reads still happen but nothing writes and nothing
|
|
1928
|
-
* re-stamps: the anchor holds the last position the reader actually had, and one
|
|
1929
|
-
* hold() on return puts them back on it.
|
|
1930
|
-
*/
|
|
1931
|
-
isFrozen?: () => boolean;
|
|
1932
1939
|
/**
|
|
1933
1940
|
* Fall back to the raw scrollTop when the anchored row cannot be found again.
|
|
1934
1941
|
*
|
|
@@ -1951,22 +1958,8 @@ interface ScrollAnchor {
|
|
|
1951
1958
|
remember: () => void;
|
|
1952
1959
|
/** Put the remembered place back, if it is still the reader's own. */
|
|
1953
1960
|
hold: () => void;
|
|
1954
|
-
/** The reader is going away: park the exact place they are leaving. */
|
|
1955
|
-
park: () => void;
|
|
1956
|
-
/**
|
|
1957
|
-
* They are back. Puts them on the parked place, and STAYS ARMED until it has
|
|
1958
|
-
* actually landed. Returns true when the host must pin to the bottom instead
|
|
1959
|
-
* (the reader left pinned), which only the host can do meaningfully.
|
|
1960
|
-
*/
|
|
1961
|
-
settleReturn: () => boolean;
|
|
1962
|
-
/** A return is armed: its position, not the host's, decides scrollTop. */
|
|
1963
|
-
isReturning: () => boolean;
|
|
1964
1961
|
/** Pin to the bottom, instantly, recording the write. The ONLY way to pin. */
|
|
1965
1962
|
pinBottom: () => void;
|
|
1966
|
-
/** The box is not being painted (hidden tab). Shared so hosts agree. */
|
|
1967
|
-
isFrozen: () => boolean;
|
|
1968
|
-
/** Deprecated alias of settleReturn, kept so a stale dist does not break. */
|
|
1969
|
-
thaw: () => void;
|
|
1970
1963
|
/** Absorb one element's own resize. See below. */
|
|
1971
1964
|
absorb: (el: AnchorGrowableEl | null | undefined) => void;
|
|
1972
1965
|
/** Drop the remembered place (chat switch, unmount). */
|
package/dist/engine.mjs
CHANGED
|
@@ -2591,13 +2591,24 @@ function createScrollAnchor(options) {
|
|
|
2591
2591
|
var kids = box.children;
|
|
2592
2592
|
var fallback = null;
|
|
2593
2593
|
var fallbackAt = -1;
|
|
2594
|
+
var behind = [];
|
|
2594
2595
|
for (var i = 0; i < kids.length; i++) {
|
|
2595
2596
|
var el = kids[i];
|
|
2596
2597
|
if (!el || typeof el.getAttribute !== "function") continue;
|
|
2597
2598
|
var key = el.getAttribute(ROW_KEY_ATTR);
|
|
2598
2599
|
if (!key) continue;
|
|
2599
2600
|
var top = el.getBoundingClientRect().top - boxTop;
|
|
2600
|
-
if (top + el.offsetHeight <= 0)
|
|
2601
|
+
if (top + el.offsetHeight <= 0) {
|
|
2602
|
+
var bpos = el.getAttribute(ROW_POS_ATTR);
|
|
2603
|
+
behind.push({
|
|
2604
|
+
key,
|
|
2605
|
+
top,
|
|
2606
|
+
pos: bpos === null ? null : bpos || UNKNOWN_ROW_POS,
|
|
2607
|
+
el
|
|
2608
|
+
});
|
|
2609
|
+
if (behind.length > MAX_ALTS) behind.shift();
|
|
2610
|
+
continue;
|
|
2611
|
+
}
|
|
2601
2612
|
if (top >= box.clientHeight) break;
|
|
2602
2613
|
var rawPos = el.getAttribute(ROW_POS_ATTR);
|
|
2603
2614
|
var pos = rawPos === null ? null : rawPos || UNKNOWN_ROW_POS;
|
|
@@ -2610,7 +2621,7 @@ function createScrollAnchor(options) {
|
|
|
2610
2621
|
el
|
|
2611
2622
|
};
|
|
2612
2623
|
if (rawPos === null) {
|
|
2613
|
-
cand.alts = collectAlts(box, boxTop, i + 1);
|
|
2624
|
+
cand.alts = withBehind(collectAlts(box, boxTop, i + 1), behind);
|
|
2614
2625
|
return cand;
|
|
2615
2626
|
}
|
|
2616
2627
|
if (!fallback) {
|
|
@@ -2619,7 +2630,10 @@ function createScrollAnchor(options) {
|
|
|
2619
2630
|
}
|
|
2620
2631
|
}
|
|
2621
2632
|
if (fallback) {
|
|
2622
|
-
fallback.alts =
|
|
2633
|
+
fallback.alts = withBehind(
|
|
2634
|
+
collectAlts(box, boxTop, fallbackAt + 1, true) || collectAlts(box, boxTop, fallbackAt + 1),
|
|
2635
|
+
behind
|
|
2636
|
+
);
|
|
2623
2637
|
return fallback;
|
|
2624
2638
|
}
|
|
2625
2639
|
return {
|
|
@@ -2651,6 +2665,10 @@ function createScrollAnchor(options) {
|
|
|
2651
2665
|
}
|
|
2652
2666
|
return out.length ? out : void 0;
|
|
2653
2667
|
}
|
|
2668
|
+
function withBehind(forward, behind) {
|
|
2669
|
+
var out = (forward || []).concat(behind.slice().reverse());
|
|
2670
|
+
return out.length ? out : void 0;
|
|
2671
|
+
}
|
|
2654
2672
|
function findRow(box, anchor) {
|
|
2655
2673
|
var el = anchor.el;
|
|
2656
2674
|
if (el && el.parentNode === box) return el;
|
|
@@ -2663,20 +2681,7 @@ function createScrollAnchor(options) {
|
|
|
2663
2681
|
}
|
|
2664
2682
|
return null;
|
|
2665
2683
|
}
|
|
2666
|
-
|
|
2667
|
-
var parked = null;
|
|
2668
|
-
var parkedStuck = false;
|
|
2669
|
-
var returning = false;
|
|
2670
|
-
var wroteTop = -1;
|
|
2671
|
-
var restoreExact = true;
|
|
2672
|
-
var restorePinned = false;
|
|
2673
|
-
function frozen() {
|
|
2674
|
-
var f = !!options.isFrozen && options.isFrozen();
|
|
2675
|
-
if (f) sawFrozen = true;
|
|
2676
|
-
return f;
|
|
2677
|
-
}
|
|
2678
|
-
function restore(anchor, unbounded) {
|
|
2679
|
-
if (frozen()) return;
|
|
2684
|
+
function restore(anchor) {
|
|
2680
2685
|
var box = options.getBox();
|
|
2681
2686
|
if (!box || !anchor || options.isStuck()) return;
|
|
2682
2687
|
var el = findRow(box, anchor);
|
|
@@ -2688,32 +2693,36 @@ function createScrollAnchor(options) {
|
|
|
2688
2693
|
var boxTop = box.getBoundingClientRect().top;
|
|
2689
2694
|
var delta = el.getBoundingClientRect().top - boxTop - anchor.top;
|
|
2690
2695
|
var slack = Math.abs(box.scrollHeight - anchor.scrollHeight) + box.clientHeight;
|
|
2691
|
-
|
|
2692
|
-
|
|
2696
|
+
var moved = delta - (anchor.scrollTop - box.scrollTop);
|
|
2697
|
+
if (moved > slack || moved < -slack) {
|
|
2698
|
+
el = null;
|
|
2699
|
+
} else {
|
|
2700
|
+
if (delta >= 1 || delta <= -1) box.scrollTop += delta;
|
|
2701
|
+
held = {
|
|
2702
|
+
key: anchor.key,
|
|
2703
|
+
top: anchor.top,
|
|
2704
|
+
pos: anchor.pos,
|
|
2705
|
+
scrollTop: box.scrollTop,
|
|
2706
|
+
scrollHeight: box.scrollHeight,
|
|
2707
|
+
el,
|
|
2708
|
+
// Carried, not dropped: one successful restore used to disarm the
|
|
2709
|
+
// standbys for every later hold.
|
|
2710
|
+
alts: anchor.alts
|
|
2711
|
+
};
|
|
2693
2712
|
return;
|
|
2694
2713
|
}
|
|
2695
|
-
var want = box.scrollTop + delta;
|
|
2696
|
-
if (delta >= 1 || delta <= -1) box.scrollTop += delta;
|
|
2697
|
-
wroteTop = box.scrollTop;
|
|
2698
|
-
restorePinned = true;
|
|
2699
|
-
restoreExact = box.scrollTop >= want - 1 && box.scrollTop <= want + 1;
|
|
2700
|
-
held = {
|
|
2701
|
-
key: anchor.key,
|
|
2702
|
-
top: anchor.top,
|
|
2703
|
-
pos: anchor.pos,
|
|
2704
|
-
scrollTop: box.scrollTop,
|
|
2705
|
-
scrollHeight: box.scrollHeight,
|
|
2706
|
-
el,
|
|
2707
|
-
// Carried, not dropped: one successful restore used to disarm the
|
|
2708
|
-
// standbys for every later hold.
|
|
2709
|
-
alts: anchor.alts
|
|
2710
|
-
};
|
|
2711
|
-
return;
|
|
2712
2714
|
}
|
|
2713
2715
|
var alts = anchor.alts;
|
|
2714
2716
|
for (var ai = 0; alts && ai < alts.length; ai++) {
|
|
2715
2717
|
var alt = alts[ai];
|
|
2716
|
-
var ael = findRow(box, {
|
|
2718
|
+
var ael = findRow(box, {
|
|
2719
|
+
key: alt.key,
|
|
2720
|
+
top: alt.top,
|
|
2721
|
+
pos: alt.pos,
|
|
2722
|
+
scrollTop: anchor.scrollTop,
|
|
2723
|
+
scrollHeight: anchor.scrollHeight,
|
|
2724
|
+
el: alt.el
|
|
2725
|
+
});
|
|
2717
2726
|
if (!ael) continue;
|
|
2718
2727
|
if (alt.pos !== null && alt.pos !== UNKNOWN_ROW_POS) {
|
|
2719
2728
|
var altLive = ael.getAttribute(ROW_POS_ATTR) || UNKNOWN_ROW_POS;
|
|
@@ -2722,12 +2731,9 @@ function createScrollAnchor(options) {
|
|
|
2722
2731
|
var aboxTop = box.getBoundingClientRect().top;
|
|
2723
2732
|
var adelta = ael.getBoundingClientRect().top - aboxTop - alt.top;
|
|
2724
2733
|
var aslack = Math.abs(box.scrollHeight - anchor.scrollHeight) + box.clientHeight;
|
|
2725
|
-
|
|
2726
|
-
|
|
2734
|
+
var amoved = adelta - (anchor.scrollTop - box.scrollTop);
|
|
2735
|
+
if (amoved > aslack || amoved < -aslack) continue;
|
|
2727
2736
|
if (adelta >= 1 || adelta <= -1) box.scrollTop += adelta;
|
|
2728
|
-
wroteTop = box.scrollTop;
|
|
2729
|
-
restorePinned = true;
|
|
2730
|
-
restoreExact = box.scrollTop >= awant - 1 && box.scrollTop <= awant + 1;
|
|
2731
2737
|
held = {
|
|
2732
2738
|
key: alt.key,
|
|
2733
2739
|
top: alt.top,
|
|
@@ -2746,13 +2752,9 @@ function createScrollAnchor(options) {
|
|
|
2746
2752
|
var grew = box.scrollHeight - anchor.scrollHeight;
|
|
2747
2753
|
if (grew > 0) {
|
|
2748
2754
|
box.scrollTop = anchor.scrollTop + grew;
|
|
2749
|
-
wroteTop = box.scrollTop;
|
|
2750
2755
|
return;
|
|
2751
2756
|
}
|
|
2752
|
-
if (options.rawFallback)
|
|
2753
|
-
box.scrollTop = anchor.scrollTop;
|
|
2754
|
-
wroteTop = box.scrollTop;
|
|
2755
|
-
}
|
|
2757
|
+
if (options.rawFallback) box.scrollTop = anchor.scrollTop;
|
|
2756
2758
|
}
|
|
2757
2759
|
function preserve(mutate) {
|
|
2758
2760
|
var anchor = capture();
|
|
@@ -2761,21 +2763,9 @@ function createScrollAnchor(options) {
|
|
|
2761
2763
|
return result;
|
|
2762
2764
|
}
|
|
2763
2765
|
function remember() {
|
|
2764
|
-
var b0 = options.getBox();
|
|
2765
|
-
if (returning && b0 && b0.scrollTop !== wroteTop) {
|
|
2766
|
-
parked = null;
|
|
2767
|
-
parkedStuck = false;
|
|
2768
|
-
returning = false;
|
|
2769
|
-
}
|
|
2770
|
-
if (frozen()) return;
|
|
2771
2766
|
held = capture();
|
|
2772
2767
|
}
|
|
2773
2768
|
function hold() {
|
|
2774
|
-
if (frozen()) return;
|
|
2775
|
-
if (sawFrozen || returning) {
|
|
2776
|
-
settleReturn();
|
|
2777
|
-
return;
|
|
2778
|
-
}
|
|
2779
2769
|
var box = options.getBox();
|
|
2780
2770
|
if (!box || options.isStuck()) {
|
|
2781
2771
|
held = null;
|
|
@@ -2802,67 +2792,17 @@ function createScrollAnchor(options) {
|
|
|
2802
2792
|
if (prev === void 0) prev = 0;
|
|
2803
2793
|
var delta = h - prev;
|
|
2804
2794
|
if (delta === 0) return;
|
|
2805
|
-
if (frozen()) {
|
|
2806
|
-
foldFrozenGrowth(box, el, delta);
|
|
2807
|
-
return;
|
|
2808
|
-
}
|
|
2809
2795
|
if (el.getBoundingClientRect().top >= box.getBoundingClientRect().top) return;
|
|
2810
2796
|
box.scrollTop += delta;
|
|
2811
|
-
wroteTop = box.scrollTop;
|
|
2812
2797
|
if (held) held = capture();
|
|
2813
2798
|
}
|
|
2814
|
-
function park() {
|
|
2815
|
-
parked = capture();
|
|
2816
|
-
parkedStuck = !!options.isStuck();
|
|
2817
|
-
returning = true;
|
|
2818
|
-
}
|
|
2819
2799
|
function pinBottom() {
|
|
2820
2800
|
var box = options.getBox();
|
|
2821
2801
|
if (!box) return;
|
|
2822
2802
|
box.scrollTop = box.scrollHeight;
|
|
2823
|
-
wroteTop = box.scrollTop;
|
|
2824
|
-
}
|
|
2825
|
-
function settleReturn() {
|
|
2826
|
-
if (frozen()) return false;
|
|
2827
|
-
sawFrozen = false;
|
|
2828
|
-
if (parkedStuck) {
|
|
2829
|
-
pinBottom();
|
|
2830
|
-
return true;
|
|
2831
|
-
}
|
|
2832
|
-
var target = parked || held;
|
|
2833
|
-
restoreExact = true;
|
|
2834
|
-
restorePinned = false;
|
|
2835
|
-
restore(target, true);
|
|
2836
|
-
parked = !restorePinned || !restoreExact ? target : null;
|
|
2837
|
-
returning = !!parked;
|
|
2838
|
-
return false;
|
|
2839
|
-
}
|
|
2840
|
-
function isReturning() {
|
|
2841
|
-
return returning;
|
|
2842
|
-
}
|
|
2843
|
-
function thaw() {
|
|
2844
|
-
settleReturn();
|
|
2845
|
-
}
|
|
2846
|
-
function foldFrozenGrowth(box, el, delta) {
|
|
2847
|
-
var elTop = el.getBoundingClientRect().top;
|
|
2848
|
-
foldInto(box, held, elTop, delta);
|
|
2849
|
-
foldInto(box, parked, elTop, delta);
|
|
2850
|
-
}
|
|
2851
|
-
function foldInto(box, a, elTop, delta) {
|
|
2852
|
-
if (!a || a.top >= 0) return;
|
|
2853
|
-
var rowEl = findRow(box, a);
|
|
2854
|
-
if (!rowEl) return;
|
|
2855
|
-
var within = elTop - rowEl.getBoundingClientRect().top;
|
|
2856
|
-
if (within < 0 || within >= rowEl.offsetHeight) return;
|
|
2857
|
-
if (within >= -a.top) return;
|
|
2858
|
-
a.top -= delta;
|
|
2859
|
-
a.scrollHeight += delta;
|
|
2860
2803
|
}
|
|
2861
2804
|
function forget() {
|
|
2862
2805
|
held = null;
|
|
2863
|
-
parked = null;
|
|
2864
|
-
parkedStuck = false;
|
|
2865
|
-
returning = false;
|
|
2866
2806
|
}
|
|
2867
2807
|
return {
|
|
2868
2808
|
capture,
|
|
@@ -2870,12 +2810,7 @@ function createScrollAnchor(options) {
|
|
|
2870
2810
|
preserve,
|
|
2871
2811
|
remember,
|
|
2872
2812
|
hold,
|
|
2873
|
-
park,
|
|
2874
|
-
settleReturn,
|
|
2875
|
-
isReturning,
|
|
2876
2813
|
pinBottom,
|
|
2877
|
-
isFrozen: frozen,
|
|
2878
|
-
thaw,
|
|
2879
2814
|
absorb,
|
|
2880
2815
|
forget
|
|
2881
2816
|
};
|