@legendapp/list 3.3.5 → 3.3.6
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/react-native.js +259 -147
- package/react-native.mjs +259 -147
- package/react-native.web.js +240 -129
- package/react-native.web.mjs +240 -129
- package/react.js +240 -129
- package/react.mjs +240 -129
- package/.DS_Store +0 -0
package/react-native.js
CHANGED
|
@@ -68,10 +68,6 @@ function getStylePaddingEnd(props) {
|
|
|
68
68
|
}
|
|
69
69
|
return (isHorizontalRTLProps(props) ? props.stylePaddingLeft : props.stylePaddingRight) || 0;
|
|
70
70
|
}
|
|
71
|
-
function getLogicalHorizontalMaxOffset(state, contentWidth) {
|
|
72
|
-
var _a3;
|
|
73
|
-
return (_a3 = getHorizontalMaxOffset(state, contentWidth)) != null ? _a3 : 0;
|
|
74
|
-
}
|
|
75
71
|
function getHorizontalInsetEnd(state, inset) {
|
|
76
72
|
if (!inset) {
|
|
77
73
|
return 0;
|
|
@@ -733,6 +729,12 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
|
|
|
733
729
|
} = state;
|
|
734
730
|
const contentSize = getContentSize(ctx);
|
|
735
731
|
resetSharedEdgeGateIfOutsideHysteresis(ctx);
|
|
732
|
+
if (state.props.data.length === 0 && scrollLength > 0 && contentSize <= scrollLength) {
|
|
733
|
+
set$(ctx, "isAtEnd", true);
|
|
734
|
+
set$(ctx, "isNearEnd", true);
|
|
735
|
+
set$(ctx, "isWithinMaintainScrollAtEndThreshold", true);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
736
738
|
if (contentSize > 0 && queuedInitialLayout) {
|
|
737
739
|
const insetEnd = getContentInsetEnd(ctx);
|
|
738
740
|
const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
|
|
@@ -742,7 +744,7 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
|
|
|
742
744
|
set$(
|
|
743
745
|
ctx,
|
|
744
746
|
"isWithinMaintainScrollAtEndThreshold",
|
|
745
|
-
isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
|
|
747
|
+
isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength || state.pendingMaintainScrollAtEnd || maintainingScrollAtEnd === "animated" || maintainingScrollAtEnd === "instant"
|
|
746
748
|
);
|
|
747
749
|
const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
|
|
748
750
|
if (!shouldSkipThresholdChecks) {
|
|
@@ -1093,10 +1095,22 @@ function getAlignItemsAtEndPadding(ctx) {
|
|
|
1093
1095
|
return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
|
|
1094
1096
|
}
|
|
1095
1097
|
function updateContentMetricsState(ctx) {
|
|
1098
|
+
var _a3;
|
|
1099
|
+
const { state } = ctx;
|
|
1096
1100
|
const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
|
|
1097
1101
|
const nextPadding = getAlignItemsAtEndPadding(ctx);
|
|
1098
1102
|
if (previousPadding !== nextPadding) {
|
|
1099
|
-
|
|
1103
|
+
const isAnimatedMaintainActive = state.maintainingScrollAtEnd === "pending-animated" || state.maintainingScrollAtEnd === "animated";
|
|
1104
|
+
const isMaintainExpected = state.didContainersLayout && ((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.animated) && (isAnimatedMaintainActive || previousPadding > nextPadding && peek$(ctx, "isWithinMaintainScrollAtEndThreshold"));
|
|
1105
|
+
if (isMaintainExpected && !isAnimatedMaintainActive && !state.pendingMaintainScrollAtEnd) {
|
|
1106
|
+
state.scheduledWork.microtask(() => {
|
|
1107
|
+
if (!state.maintainingScrollAtEnd && !state.pendingMaintainScrollAtEnd) {
|
|
1108
|
+
set$(ctx, "alignItemsAtEndPadding", getAlignItemsAtEndPadding(ctx));
|
|
1109
|
+
}
|
|
1110
|
+
}, "alignItemsAtEndPaddingFallback");
|
|
1111
|
+
} else if (!isMaintainExpected) {
|
|
1112
|
+
set$(ctx, "alignItemsAtEndPadding", nextPadding);
|
|
1113
|
+
}
|
|
1100
1114
|
}
|
|
1101
1115
|
}
|
|
1102
1116
|
|
|
@@ -1549,81 +1563,55 @@ function doScrollTo(ctx, params) {
|
|
|
1549
1563
|
}
|
|
1550
1564
|
}
|
|
1551
1565
|
|
|
1552
|
-
// src/core/
|
|
1553
|
-
function
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
if (!state.maintainingScrollAtEnd) {
|
|
1574
|
-
const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
|
|
1575
|
-
const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
|
|
1576
|
-
const scrollAtRequest = state.scroll;
|
|
1577
|
-
state.maintainingScrollAtEnd = pendingState;
|
|
1578
|
-
requestAnimationFrame(() => {
|
|
1579
|
-
const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
|
|
1580
|
-
const didScrollSinceRequest = state.scroll !== scrollAtRequest;
|
|
1581
|
-
if (isStillWithinThreshold || !didScrollSinceRequest) {
|
|
1582
|
-
state.maintainingScrollAtEnd = activeState;
|
|
1583
|
-
const scroller = refScroller.current;
|
|
1584
|
-
if (state.props.horizontal && isHorizontalRTL(state)) {
|
|
1585
|
-
const currentContentSize = getContentSize(ctx);
|
|
1586
|
-
const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
|
|
1587
|
-
const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
|
|
1588
|
-
scroller == null ? void 0 : scroller.scrollTo({
|
|
1589
|
-
animated: maintainScrollAtEnd.animated,
|
|
1590
|
-
x: nativeOffset,
|
|
1591
|
-
y: 0
|
|
1592
|
-
});
|
|
1593
|
-
} else {
|
|
1594
|
-
scroller == null ? void 0 : scroller.scrollToEnd({
|
|
1595
|
-
animated: maintainScrollAtEnd.animated
|
|
1596
|
-
});
|
|
1597
|
-
}
|
|
1598
|
-
setTimeout(
|
|
1599
|
-
() => {
|
|
1600
|
-
if (state.maintainingScrollAtEnd === activeState) {
|
|
1601
|
-
state.maintainingScrollAtEnd = void 0;
|
|
1602
|
-
if (state.pendingMaintainScrollAtEnd) {
|
|
1603
|
-
doMaintainScrollAtEnd(ctx);
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
},
|
|
1607
|
-
maintainScrollAtEnd.animated ? 500 : 0
|
|
1608
|
-
);
|
|
1609
|
-
} else if (state.maintainingScrollAtEnd === pendingState) {
|
|
1610
|
-
state.maintainingScrollAtEnd = void 0;
|
|
1611
|
-
state.pendingMaintainScrollAtEnd = false;
|
|
1566
|
+
// src/core/scrollRequestTracker.ts
|
|
1567
|
+
function getScrollRequestTracker(ctx) {
|
|
1568
|
+
if (!ctx.scrollRequestTracker) {
|
|
1569
|
+
let currentToken = 0;
|
|
1570
|
+
const start = (resolve) => {
|
|
1571
|
+
const state = ctx.state;
|
|
1572
|
+
state.scheduledWork.cancel("imperativeScrollReady");
|
|
1573
|
+
const token = ++currentToken;
|
|
1574
|
+
settlePendingImperativeScroll(state);
|
|
1575
|
+
state.pendingScrollResolve = resolve;
|
|
1576
|
+
return token;
|
|
1577
|
+
};
|
|
1578
|
+
const runNow = (token, resolve, run) => {
|
|
1579
|
+
const state = ctx.state;
|
|
1580
|
+
if (token !== currentToken) {
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
const didStartScroll = run();
|
|
1584
|
+
if (!didStartScroll || !state.scrollingTo) {
|
|
1585
|
+
if (state.pendingScrollResolve === resolve) {
|
|
1586
|
+
state.pendingScrollResolve = void 0;
|
|
1612
1587
|
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1588
|
+
resolve();
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1591
|
+
ctx.scrollRequestTracker = {
|
|
1592
|
+
isCurrent: (token) => token === currentToken,
|
|
1593
|
+
runNow,
|
|
1594
|
+
runNowIfIdle: (run) => {
|
|
1595
|
+
const state = ctx.state;
|
|
1596
|
+
if (state.pendingScrollResolve || state.scrollingTo) {
|
|
1597
|
+
return Promise.resolve();
|
|
1598
|
+
}
|
|
1599
|
+
return new Promise((resolve) => {
|
|
1600
|
+
const token = start(resolve);
|
|
1601
|
+
runNow(token, resolve, run);
|
|
1602
|
+
});
|
|
1603
|
+
},
|
|
1604
|
+
start
|
|
1605
|
+
};
|
|
1618
1606
|
}
|
|
1619
|
-
|
|
1620
|
-
return false;
|
|
1607
|
+
return ctx.scrollRequestTracker;
|
|
1621
1608
|
}
|
|
1622
1609
|
|
|
1623
1610
|
// src/utils/requestAdjust.ts
|
|
1624
|
-
function requestAdjust(ctx, positionDiff,
|
|
1611
|
+
function requestAdjust(ctx, positionDiff, source) {
|
|
1625
1612
|
const state = ctx.state;
|
|
1626
1613
|
if (Math.abs(positionDiff) > 0.1) {
|
|
1614
|
+
const dataChanged = source === "data";
|
|
1627
1615
|
const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
|
|
1628
1616
|
const doit = () => {
|
|
1629
1617
|
if (needsScrollWorkaround) {
|
|
@@ -1640,7 +1628,7 @@ function requestAdjust(ctx, positionDiff, dataChanged) {
|
|
|
1640
1628
|
const readyToRender = peek$(ctx, "readyToRender");
|
|
1641
1629
|
if (readyToRender) {
|
|
1642
1630
|
doit();
|
|
1643
|
-
if (Platform.OS !== "web") {
|
|
1631
|
+
if (Platform.OS !== "web" && source !== "item-size") {
|
|
1644
1632
|
const threshold = state.scroll - positionDiff / 2;
|
|
1645
1633
|
if (!state.ignoreScrollFromMVCP) {
|
|
1646
1634
|
state.ignoreScrollFromMVCP = {};
|
|
@@ -1673,6 +1661,87 @@ function requestAdjust(ctx, positionDiff, dataChanged) {
|
|
|
1673
1661
|
}
|
|
1674
1662
|
}
|
|
1675
1663
|
|
|
1664
|
+
// src/core/doMaintainScrollAtEnd.ts
|
|
1665
|
+
function finishMaintainScrollAtEnd(ctx) {
|
|
1666
|
+
var _a3;
|
|
1667
|
+
const { state } = ctx;
|
|
1668
|
+
const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
|
|
1669
|
+
const nextPadding = getAlignItemsAtEndPadding(ctx);
|
|
1670
|
+
state.maintainingScrollAtEnd = void 0;
|
|
1671
|
+
state.pendingMaintainScrollAtEnd = false;
|
|
1672
|
+
if (currentPadding !== nextPadding) {
|
|
1673
|
+
set$(ctx, "alignItemsAtEndPadding", nextPadding);
|
|
1674
|
+
state.scrollForNextCalculateItemsInView = void 0;
|
|
1675
|
+
(_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
|
|
1676
|
+
const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
|
|
1677
|
+
requestAdjust(ctx, nextScroll - state.scroll);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
function doMaintainScrollAtEnd(ctx) {
|
|
1681
|
+
const state = ctx.state;
|
|
1682
|
+
const {
|
|
1683
|
+
didContainersLayout,
|
|
1684
|
+
didFinishInitialScroll,
|
|
1685
|
+
pendingNativeMVCPAdjust,
|
|
1686
|
+
props: { maintainScrollAtEnd }
|
|
1687
|
+
} = state;
|
|
1688
|
+
const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
|
|
1689
|
+
const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
|
|
1690
|
+
const shouldMaintainScrollAtEnd = !!((isWithinMaintainScrollAtEndThreshold || isReplayingPendingMaintain) && maintainScrollAtEnd && didFinishInitialScroll);
|
|
1691
|
+
if (shouldMaintainScrollAtEnd && !didContainersLayout) {
|
|
1692
|
+
state.pendingMaintainScrollAtEnd = true;
|
|
1693
|
+
return false;
|
|
1694
|
+
}
|
|
1695
|
+
if (pendingNativeMVCPAdjust) {
|
|
1696
|
+
state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
|
|
1697
|
+
return false;
|
|
1698
|
+
}
|
|
1699
|
+
if (shouldMaintainScrollAtEnd && didContainersLayout) {
|
|
1700
|
+
state.pendingMaintainScrollAtEnd = false;
|
|
1701
|
+
const contentSize = getContentSize(ctx);
|
|
1702
|
+
if (contentSize < state.scrollLength) {
|
|
1703
|
+
state.scroll = 0;
|
|
1704
|
+
}
|
|
1705
|
+
if (!state.maintainingScrollAtEnd) {
|
|
1706
|
+
const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
|
|
1707
|
+
const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
|
|
1708
|
+
const scrollAtRequest = state.scroll;
|
|
1709
|
+
state.maintainingScrollAtEnd = pendingState;
|
|
1710
|
+
requestAnimationFrame(() => {
|
|
1711
|
+
if (state.maintainingScrollAtEnd !== pendingState) {
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1714
|
+
const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
|
|
1715
|
+
const didScrollSinceRequest = state.scroll !== scrollAtRequest;
|
|
1716
|
+
if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
|
|
1717
|
+
state.maintainingScrollAtEnd = activeState;
|
|
1718
|
+
const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
|
|
1719
|
+
() => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
|
|
1720
|
+
);
|
|
1721
|
+
void scrollPromise.then(() => {
|
|
1722
|
+
if (state.maintainingScrollAtEnd !== activeState) {
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
if (state.pendingMaintainScrollAtEnd) {
|
|
1726
|
+
state.maintainingScrollAtEnd = void 0;
|
|
1727
|
+
doMaintainScrollAtEnd(ctx);
|
|
1728
|
+
} else {
|
|
1729
|
+
finishMaintainScrollAtEnd(ctx);
|
|
1730
|
+
}
|
|
1731
|
+
});
|
|
1732
|
+
} else if (state.maintainingScrollAtEnd === pendingState) {
|
|
1733
|
+
finishMaintainScrollAtEnd(ctx);
|
|
1734
|
+
}
|
|
1735
|
+
});
|
|
1736
|
+
} else {
|
|
1737
|
+
state.pendingMaintainScrollAtEnd = true;
|
|
1738
|
+
}
|
|
1739
|
+
return true;
|
|
1740
|
+
}
|
|
1741
|
+
finishMaintainScrollAtEnd(ctx);
|
|
1742
|
+
return false;
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1676
1745
|
// src/core/mvcp.ts
|
|
1677
1746
|
var MVCP_POSITION_EPSILON = 0.1;
|
|
1678
1747
|
var MVCP_ANCHOR_LOCK_TTL_MS = 300;
|
|
@@ -1746,7 +1815,7 @@ function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
|
|
|
1746
1815
|
state.pendingNativeMVCPAdjust = void 0;
|
|
1747
1816
|
const remaining = remainingAfterManual - nativeDelta;
|
|
1748
1817
|
if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
|
|
1749
|
-
requestAdjust(ctx, remaining,
|
|
1818
|
+
requestAdjust(ctx, remaining, "data");
|
|
1750
1819
|
}
|
|
1751
1820
|
}
|
|
1752
1821
|
function maybeApplyPredictedNativeMVCPAdjust(ctx) {
|
|
@@ -1765,7 +1834,7 @@ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
|
|
|
1765
1834
|
return;
|
|
1766
1835
|
}
|
|
1767
1836
|
pending.manualApplied = manualDesired;
|
|
1768
|
-
requestAdjust(ctx, manualDesired,
|
|
1837
|
+
requestAdjust(ctx, manualDesired, "data");
|
|
1769
1838
|
pending.furthestProgressTowardAmount = 0;
|
|
1770
1839
|
}
|
|
1771
1840
|
function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
|
|
@@ -1811,7 +1880,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
|
|
|
1811
1880
|
}
|
|
1812
1881
|
return false;
|
|
1813
1882
|
}
|
|
1814
|
-
function prepareMVCP(ctx, dataChanged) {
|
|
1883
|
+
function prepareMVCP(ctx, dataChanged, adjustmentSource) {
|
|
1815
1884
|
const state = ctx.state;
|
|
1816
1885
|
const { idsInView, positions, props } = state;
|
|
1817
1886
|
const {
|
|
@@ -1965,7 +2034,7 @@ function prepareMVCP(ctx, dataChanged) {
|
|
|
1965
2034
|
if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
|
|
1966
2035
|
const shouldSkipAdjustForMaintainedEnd = (state.maintainingScrollAtEnd === "pending-animated" || state.maintainingScrollAtEnd === "animated") && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
|
|
1967
2036
|
if (!shouldSkipAdjustForMaintainedEnd) {
|
|
1968
|
-
requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
|
|
2037
|
+
requestAdjust(ctx, positionDiff, dataChanged && mvcpData ? "data" : adjustmentSource);
|
|
1969
2038
|
}
|
|
1970
2039
|
}
|
|
1971
2040
|
};
|
|
@@ -2063,6 +2132,10 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
|
|
|
2063
2132
|
state.scrollTime = currentTime;
|
|
2064
2133
|
const scrollDelta = Math.abs(newScroll - prevScroll);
|
|
2065
2134
|
const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
|
|
2135
|
+
const didCancelMaintainScrollAtEnd = isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
|
|
2136
|
+
if (didCancelMaintainScrollAtEnd) {
|
|
2137
|
+
finishMaintainScrollAtEnd(ctx);
|
|
2138
|
+
}
|
|
2066
2139
|
const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
|
|
2067
2140
|
const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
|
|
2068
2141
|
const scrollLength = state.scrollLength;
|
|
@@ -2071,7 +2144,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
|
|
|
2071
2144
|
updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
|
|
2072
2145
|
const lastCalculated = state.scrollLastCalculate;
|
|
2073
2146
|
const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
|
|
2074
|
-
const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
|
|
2147
|
+
const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || didCancelMaintainScrollAtEnd || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
|
|
2075
2148
|
if (shouldUpdate) {
|
|
2076
2149
|
state.scrollLastCalculate = state.scroll;
|
|
2077
2150
|
state.ignoreScrollFromMVCPIgnored = false;
|
|
@@ -2900,7 +2973,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
|
|
|
2900
2973
|
}
|
|
2901
2974
|
}
|
|
2902
2975
|
function handleBootstrapInitialScrollLayoutChange(ctx) {
|
|
2903
|
-
var _a3, _b
|
|
2976
|
+
var _a3, _b;
|
|
2904
2977
|
const state = ctx.state;
|
|
2905
2978
|
const initialScroll = state.initialScroll;
|
|
2906
2979
|
const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
|
|
@@ -2914,19 +2987,15 @@ function handleBootstrapInitialScrollLayoutChange(ctx) {
|
|
|
2914
2987
|
if (state.didFinishInitialScroll) {
|
|
2915
2988
|
schedulePreservedEndAnchorCorrection(ctx);
|
|
2916
2989
|
} else if (scrollingTo) {
|
|
2917
|
-
const existingWatchdog = initialScrollWatchdog.get(state);
|
|
2918
|
-
scrollingTo.offset = resolvedOffset;
|
|
2919
|
-
scrollingTo.targetOffset = resolvedOffset;
|
|
2920
2990
|
state.initialScroll = {
|
|
2921
2991
|
...initialScroll,
|
|
2922
2992
|
contentOffset: resolvedOffset
|
|
2923
2993
|
};
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2994
|
+
dispatchInitialScroll(ctx, {
|
|
2995
|
+
forceScroll: true,
|
|
2996
|
+
resolvedOffset,
|
|
2997
|
+
target: state.initialScroll
|
|
2928
2998
|
});
|
|
2929
|
-
requestAdjust(ctx, offsetDiff);
|
|
2930
2999
|
}
|
|
2931
3000
|
}
|
|
2932
3001
|
} else {
|
|
@@ -3968,6 +4037,9 @@ function setDidLayout(ctx) {
|
|
|
3968
4037
|
state.queuedInitialLayout = true;
|
|
3969
4038
|
checkAtBottom(ctx);
|
|
3970
4039
|
setInitialRenderState(ctx, { didLayout: true });
|
|
4040
|
+
if (state.pendingMaintainScrollAtEnd) {
|
|
4041
|
+
doMaintainScrollAtEnd(ctx);
|
|
4042
|
+
}
|
|
3971
4043
|
}
|
|
3972
4044
|
|
|
3973
4045
|
// src/core/calculateItemsInView.ts
|
|
@@ -4204,7 +4276,7 @@ function calculateItemsInView(ctx, params = {}) {
|
|
|
4204
4276
|
const stickyHeaderIndicesArr = state.props.stickyHeaderIndicesArr || [];
|
|
4205
4277
|
const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
|
|
4206
4278
|
const drawDistance = getEffectiveDrawDistance(ctx, params.drawDistanceMode);
|
|
4207
|
-
const { dataChanged, doMVCP, forceFullItemPositions } = params;
|
|
4279
|
+
const { dataChanged, doMVCP, forceFullItemPositions, mvcpAdjustmentSource } = params;
|
|
4208
4280
|
const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
|
|
4209
4281
|
const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
|
|
4210
4282
|
const prevNumContainers = peek$(ctx, "numContainers");
|
|
@@ -4305,7 +4377,7 @@ function calculateItemsInView(ctx, params = {}) {
|
|
|
4305
4377
|
}
|
|
4306
4378
|
}
|
|
4307
4379
|
}
|
|
4308
|
-
const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged) : void 0;
|
|
4380
|
+
const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged, mvcpAdjustmentSource) : void 0;
|
|
4309
4381
|
if (dataChanged) {
|
|
4310
4382
|
resetLayoutCachesForDataChange(state);
|
|
4311
4383
|
}
|
|
@@ -4719,12 +4791,15 @@ function runOrScheduleMVCPRecalculate(ctx) {
|
|
|
4719
4791
|
} else if (Platform.OS === "web") {
|
|
4720
4792
|
if (!state.mvcpAnchorLock) {
|
|
4721
4793
|
state.scheduledWork.cancel("mvcpRecalculate");
|
|
4722
|
-
calculateItemsInView(ctx, { doMVCP: true });
|
|
4794
|
+
calculateItemsInView(ctx, { doMVCP: true, mvcpAdjustmentSource: "item-size" });
|
|
4723
4795
|
} else if (!state.scheduledWork.has("mvcpRecalculate")) {
|
|
4724
|
-
state.scheduledWork.frame(
|
|
4796
|
+
state.scheduledWork.frame(
|
|
4797
|
+
() => calculateItemsInView(ctx, { doMVCP: true, mvcpAdjustmentSource: "item-size" }),
|
|
4798
|
+
"mvcpRecalculate"
|
|
4799
|
+
);
|
|
4725
4800
|
}
|
|
4726
4801
|
} else {
|
|
4727
|
-
calculateItemsInView(ctx, { doMVCP: true });
|
|
4802
|
+
calculateItemsInView(ctx, { doMVCP: true, mvcpAdjustmentSource: "item-size" });
|
|
4728
4803
|
}
|
|
4729
4804
|
}
|
|
4730
4805
|
function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
|
|
@@ -5213,6 +5288,30 @@ function useContainerItemSignals(containerContext) {
|
|
|
5213
5288
|
itemKey
|
|
5214
5289
|
};
|
|
5215
5290
|
}
|
|
5291
|
+
function scheduleViewabilityConfigWarning(ctx, hookName, configId) {
|
|
5292
|
+
let cancelled = false;
|
|
5293
|
+
Promise.resolve().then(() => {
|
|
5294
|
+
var _a3;
|
|
5295
|
+
const viewabilityConfigCallbackPairs = (_a3 = ctx.state) == null ? void 0 : _a3.viewabilityConfigCallbackPairs;
|
|
5296
|
+
if (!cancelled && ctx.state) {
|
|
5297
|
+
if (!(viewabilityConfigCallbackPairs == null ? void 0 : viewabilityConfigCallbackPairs.length)) {
|
|
5298
|
+
warnDevOnce(
|
|
5299
|
+
`${hookName}-missing-viewability-config`,
|
|
5300
|
+
`${hookName} requires viewability to be configured on the owning LegendList. Add viewabilityConfig with a visibility threshold.`
|
|
5301
|
+
);
|
|
5302
|
+
} else if (hookName === "useViewability" && !viewabilityConfigCallbackPairs.some((pair) => pair.viewabilityConfig.id === (configId != null ? configId : ""))) {
|
|
5303
|
+
const configDescription = configId ? ` for configId "${configId}"` : "";
|
|
5304
|
+
warnDevOnce(
|
|
5305
|
+
`${hookName}-missing-viewability-config-${configId != null ? configId : "default"}`,
|
|
5306
|
+
`${hookName}${configDescription} requires a matching viewabilityConfig.id on the owning LegendList. Add an id to viewabilityConfig and pass the same id to useViewability.`
|
|
5307
|
+
);
|
|
5308
|
+
}
|
|
5309
|
+
}
|
|
5310
|
+
});
|
|
5311
|
+
return () => {
|
|
5312
|
+
cancelled = true;
|
|
5313
|
+
};
|
|
5314
|
+
}
|
|
5216
5315
|
function useAdaptiveRender() {
|
|
5217
5316
|
const [mode] = useArr$(["adaptiveRender"]);
|
|
5218
5317
|
return mode;
|
|
@@ -5252,7 +5351,9 @@ function useViewability(callback, configId) {
|
|
|
5252
5351
|
const { containerId } = containerContext;
|
|
5253
5352
|
const key = containerId + (configId != null ? configId : "");
|
|
5254
5353
|
ctx.mapViewabilityCallbacks.set(key, callback);
|
|
5354
|
+
const cancelConfigWarning = IS_DEV ? scheduleViewabilityConfigWarning(ctx, "useViewability", configId) : void 0;
|
|
5255
5355
|
return () => {
|
|
5356
|
+
cancelConfigWarning == null ? void 0 : cancelConfigWarning();
|
|
5256
5357
|
ctx.mapViewabilityCallbacks.delete(key);
|
|
5257
5358
|
};
|
|
5258
5359
|
}, [ctx, callback, configId, containerContext]);
|
|
@@ -5276,7 +5377,9 @@ function useViewabilityAmount(callback) {
|
|
|
5276
5377
|
}
|
|
5277
5378
|
const { containerId } = containerContext;
|
|
5278
5379
|
ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
|
|
5380
|
+
const cancelConfigWarning = IS_DEV ? scheduleViewabilityConfigWarning(ctx, "useViewabilityAmount") : void 0;
|
|
5279
5381
|
return () => {
|
|
5382
|
+
cancelConfigWarning == null ? void 0 : cancelConfigWarning();
|
|
5280
5383
|
ctx.mapViewabilityAmountCallbacks.delete(containerId);
|
|
5281
5384
|
};
|
|
5282
5385
|
}, [ctx, callback, containerContext]);
|
|
@@ -6383,11 +6486,29 @@ var ScheduledWork = class {
|
|
|
6383
6486
|
constructor() {
|
|
6384
6487
|
this.work = /* @__PURE__ */ new Map();
|
|
6385
6488
|
}
|
|
6489
|
+
microtask(callback, key) {
|
|
6490
|
+
const pendingWork = this.work.get(key);
|
|
6491
|
+
if (pendingWork == null ? void 0 : pendingWork.callback) {
|
|
6492
|
+
pendingWork.callback = callback;
|
|
6493
|
+
} else {
|
|
6494
|
+
this.cancel(key);
|
|
6495
|
+
const work = { callback, cancel: () => {
|
|
6496
|
+
} };
|
|
6497
|
+
this.work.set(key, work);
|
|
6498
|
+
queueMicrotask(() => {
|
|
6499
|
+
var _a3;
|
|
6500
|
+
if (this.work.get(key) === work) {
|
|
6501
|
+
this.work.delete(key);
|
|
6502
|
+
(_a3 = work.callback) == null ? void 0 : _a3.call(work);
|
|
6503
|
+
}
|
|
6504
|
+
});
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6386
6507
|
timeout(callback, delay, key) {
|
|
6387
6508
|
if (key) {
|
|
6388
6509
|
this.cancel(key);
|
|
6389
6510
|
}
|
|
6390
|
-
const work =
|
|
6511
|
+
const work = { cancel: () => clearTimeout(handle) };
|
|
6391
6512
|
const handle = setTimeout(() => {
|
|
6392
6513
|
const workKey = key != null ? key : handle;
|
|
6393
6514
|
if (this.work.get(workKey) === work) {
|
|
@@ -6395,14 +6516,13 @@ var ScheduledWork = class {
|
|
|
6395
6516
|
callback();
|
|
6396
6517
|
}
|
|
6397
6518
|
}, delay);
|
|
6398
|
-
work[0] = handle;
|
|
6399
6519
|
this.work.set(key != null ? key : handle, work);
|
|
6400
6520
|
}
|
|
6401
6521
|
frame(callback, key) {
|
|
6402
6522
|
this.cancel(key);
|
|
6403
|
-
const work =
|
|
6523
|
+
const work = { cancel: () => cancelAnimationFrame(handle) };
|
|
6404
6524
|
this.work.set(key, work);
|
|
6405
|
-
|
|
6525
|
+
const handle = requestAnimationFrame(() => {
|
|
6406
6526
|
if (this.work.get(key) === work) {
|
|
6407
6527
|
this.work.delete(key);
|
|
6408
6528
|
callback();
|
|
@@ -6411,22 +6531,21 @@ var ScheduledWork = class {
|
|
|
6411
6531
|
}
|
|
6412
6532
|
register(key, cancel) {
|
|
6413
6533
|
this.cancel(key);
|
|
6414
|
-
this.work.set(key,
|
|
6534
|
+
this.work.set(key, { cancel });
|
|
6415
6535
|
}
|
|
6416
6536
|
cancel(key) {
|
|
6417
6537
|
const work = this.work.get(key);
|
|
6418
6538
|
if (work) {
|
|
6419
6539
|
this.work.delete(key);
|
|
6420
|
-
|
|
6421
|
-
cancel(handle);
|
|
6540
|
+
work.cancel();
|
|
6422
6541
|
}
|
|
6423
6542
|
}
|
|
6424
6543
|
has(key) {
|
|
6425
6544
|
return this.work.has(key);
|
|
6426
6545
|
}
|
|
6427
6546
|
dispose() {
|
|
6428
|
-
for (const
|
|
6429
|
-
cancel(
|
|
6547
|
+
for (const work of this.work.values()) {
|
|
6548
|
+
work.cancel();
|
|
6430
6549
|
}
|
|
6431
6550
|
this.work.clear();
|
|
6432
6551
|
}
|
|
@@ -6483,6 +6602,25 @@ var ScrollAdjustHandler = class {
|
|
|
6483
6602
|
}
|
|
6484
6603
|
};
|
|
6485
6604
|
|
|
6605
|
+
// src/core/scrollToEnd.ts
|
|
6606
|
+
function scrollToEnd(ctx, options) {
|
|
6607
|
+
const state = ctx.state;
|
|
6608
|
+
const data = state.props.data;
|
|
6609
|
+
const index = data.length - 1;
|
|
6610
|
+
if (index === -1) {
|
|
6611
|
+
return false;
|
|
6612
|
+
}
|
|
6613
|
+
const paddingBottom = state.props.stylePaddingBottom || 0;
|
|
6614
|
+
const footerSize = peek$(ctx, "footerSize") || 0;
|
|
6615
|
+
scrollToIndex(ctx, {
|
|
6616
|
+
...options,
|
|
6617
|
+
index,
|
|
6618
|
+
viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
|
|
6619
|
+
viewPosition: 1
|
|
6620
|
+
});
|
|
6621
|
+
return true;
|
|
6622
|
+
}
|
|
6623
|
+
|
|
6486
6624
|
// src/core/updateContentInsetEndAdjustment.ts
|
|
6487
6625
|
function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
|
|
6488
6626
|
const state = ctx.state;
|
|
@@ -6557,25 +6695,6 @@ function createColumnWrapperStyle(contentContainerStyle) {
|
|
|
6557
6695
|
}
|
|
6558
6696
|
}
|
|
6559
6697
|
|
|
6560
|
-
// src/core/scrollToEnd.ts
|
|
6561
|
-
function scrollToEnd(ctx, options) {
|
|
6562
|
-
const state = ctx.state;
|
|
6563
|
-
const data = state.props.data;
|
|
6564
|
-
const index = data.length - 1;
|
|
6565
|
-
if (index === -1) {
|
|
6566
|
-
return false;
|
|
6567
|
-
}
|
|
6568
|
-
const paddingBottom = state.props.stylePaddingBottom || 0;
|
|
6569
|
-
const footerSize = peek$(ctx, "footerSize") || 0;
|
|
6570
|
-
scrollToIndex(ctx, {
|
|
6571
|
-
...options,
|
|
6572
|
-
index,
|
|
6573
|
-
viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
|
|
6574
|
-
viewPosition: 1
|
|
6575
|
-
});
|
|
6576
|
-
return true;
|
|
6577
|
-
}
|
|
6578
|
-
|
|
6579
6698
|
// src/utils/createImperativeHandle.ts
|
|
6580
6699
|
var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
|
|
6581
6700
|
function getAverageItemSizes(state) {
|
|
@@ -6602,9 +6721,9 @@ function triggerMountedContainerLayouts(ctx) {
|
|
|
6602
6721
|
}
|
|
6603
6722
|
function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
|
|
6604
6723
|
const state = ctx.state;
|
|
6724
|
+
const scrollRequestTracker = getScrollRequestTracker(ctx);
|
|
6605
6725
|
const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
|
|
6606
6726
|
const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
|
|
6607
|
-
let imperativeScrollToken = 0;
|
|
6608
6727
|
const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.scheduledWork.has("mvcpRecalculate") || state.ignoreScrollFromMVCP !== void 0;
|
|
6609
6728
|
const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
|
|
6610
6729
|
var _a3;
|
|
@@ -6626,7 +6745,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
|
|
|
6626
6745
|
const startedAt = Date.now();
|
|
6627
6746
|
let stableFrames = 0;
|
|
6628
6747
|
const check = () => {
|
|
6629
|
-
if (token
|
|
6748
|
+
if (!scrollRequestTracker.isCurrent(token)) {
|
|
6630
6749
|
return;
|
|
6631
6750
|
}
|
|
6632
6751
|
if (isSettlingAfterDataChange() || !isReady()) {
|
|
@@ -6644,33 +6763,15 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
|
|
|
6644
6763
|
state.scheduledWork.frame(check, "imperativeScrollReady");
|
|
6645
6764
|
};
|
|
6646
6765
|
const runScrollRequest = (token, resolve, run, isReady = () => true) => {
|
|
6647
|
-
const runNow = () =>
|
|
6648
|
-
if (token !== imperativeScrollToken) {
|
|
6649
|
-
return;
|
|
6650
|
-
}
|
|
6651
|
-
const didStartScroll = run();
|
|
6652
|
-
if (!didStartScroll || !state.scrollingTo) {
|
|
6653
|
-
if (state.pendingScrollResolve === resolve) {
|
|
6654
|
-
state.pendingScrollResolve = void 0;
|
|
6655
|
-
}
|
|
6656
|
-
resolve();
|
|
6657
|
-
}
|
|
6658
|
-
};
|
|
6766
|
+
const runNow = () => scrollRequestTracker.runNow(token, resolve, run);
|
|
6659
6767
|
if (isSettlingAfterDataChange() || !isReady()) {
|
|
6660
6768
|
runWhenReady(token, runNow, isReady);
|
|
6661
6769
|
} else {
|
|
6662
6770
|
runNow();
|
|
6663
6771
|
}
|
|
6664
6772
|
};
|
|
6665
|
-
const startImperativeScroll = (resolve) => {
|
|
6666
|
-
state.scheduledWork.cancel("imperativeScrollReady");
|
|
6667
|
-
const token = ++imperativeScrollToken;
|
|
6668
|
-
settlePendingImperativeScroll(state);
|
|
6669
|
-
state.pendingScrollResolve = resolve;
|
|
6670
|
-
return token;
|
|
6671
|
-
};
|
|
6672
6773
|
const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
|
|
6673
|
-
const token =
|
|
6774
|
+
const token = scrollRequestTracker.start(resolve);
|
|
6674
6775
|
supersedeInitialScroll(ctx);
|
|
6675
6776
|
runScrollRequest(token, resolve, run, isReady);
|
|
6676
6777
|
});
|
|
@@ -6678,7 +6779,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
|
|
|
6678
6779
|
const pendingScroll = state.pendingScrollToEnd;
|
|
6679
6780
|
if (pendingScroll) {
|
|
6680
6781
|
state.pendingScrollToEnd = void 0;
|
|
6681
|
-
if (pendingScroll.token
|
|
6782
|
+
if (scrollRequestTracker.isCurrent(pendingScroll.token)) {
|
|
6682
6783
|
runScrollRequest(
|
|
6683
6784
|
pendingScroll.token,
|
|
6684
6785
|
pendingScroll.resolve,
|
|
@@ -6792,7 +6893,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
|
|
|
6792
6893
|
}),
|
|
6793
6894
|
scrollToEnd: (options) => new Promise((resolve) => {
|
|
6794
6895
|
var _a3;
|
|
6795
|
-
const token =
|
|
6896
|
+
const token = scrollRequestTracker.start(resolve);
|
|
6796
6897
|
state.pendingScrollToEnd = {
|
|
6797
6898
|
options,
|
|
6798
6899
|
resolve,
|
|
@@ -7172,6 +7273,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
7172
7273
|
const shouldInitializeHorizontalRTL = !initialScrollAtEnd && !hasInitialScrollIndex && !hasInitialScrollOffset && isHorizontalRTLProps({ horizontal, rtl });
|
|
7173
7274
|
const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && (hasInitialScrollOffset || shouldInitializeHorizontalRTL);
|
|
7174
7275
|
const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
|
|
7276
|
+
const shouldBottomAlignNumericInitialScrollIndex = typeof initialScrollIndexProp === "number" && !hasInitialScrollOffset && dataProp.length > 1 && initialScrollIndexProp >= dataProp.length - 1;
|
|
7175
7277
|
const initialScrollProp = initialScrollAtEnd ? {
|
|
7176
7278
|
index: Math.max(0, dataProp.length - 1),
|
|
7177
7279
|
preserveForBottomPadding: true,
|
|
@@ -7184,7 +7286,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
7184
7286
|
viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
|
|
7185
7287
|
} : {
|
|
7186
7288
|
index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
|
|
7187
|
-
|
|
7289
|
+
preserveForBottomPadding: shouldBottomAlignNumericInitialScrollIndex ? true : void 0,
|
|
7290
|
+
viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? -stylePaddingEndState : 0,
|
|
7291
|
+
viewPosition: shouldBottomAlignNumericInitialScrollIndex ? 1 : void 0
|
|
7188
7292
|
} : initialScrollUsesOffsetOnly ? {
|
|
7189
7293
|
contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
|
|
7190
7294
|
index: 0,
|
|
@@ -7290,6 +7394,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
7290
7394
|
viewabilityConfigCallbackPairs: void 0
|
|
7291
7395
|
};
|
|
7292
7396
|
const internalState = ctx.state;
|
|
7397
|
+
ctx.scrollToEnd = (options) => scrollToEnd(ctx, options);
|
|
7293
7398
|
internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
|
|
7294
7399
|
internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
|
|
7295
7400
|
set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
|
|
@@ -7651,6 +7756,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
7651
7756
|
onScroll: (event) => onScroll(ctx, event),
|
|
7652
7757
|
onScrollBeginDrag: (event) => {
|
|
7653
7758
|
var _a4, _b2;
|
|
7759
|
+
const maintainingScrollAtEnd = state.maintainingScrollAtEnd;
|
|
7760
|
+
if (maintainingScrollAtEnd === "animated" || maintainingScrollAtEnd === "instant") {
|
|
7761
|
+
cancelImperativeScroll(state);
|
|
7762
|
+
}
|
|
7763
|
+
if (maintainingScrollAtEnd) {
|
|
7764
|
+
finishMaintainScrollAtEnd(ctx);
|
|
7765
|
+
}
|
|
7654
7766
|
prepareReachedEdgeForNextUserScroll(ctx);
|
|
7655
7767
|
(_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
|
|
7656
7768
|
},
|