@legendapp/list 1.0.9 → 1.0.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.
- package/CHANGELOG.md +48 -2
- package/index.js +44 -41
- package/index.mjs +44 -41
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
|
-
|
|
1
|
+
## 1.0.10
|
|
2
|
+
- Fix: Removed an optimization that only checked newly visible items, which could sometimes cause gaps in lists
|
|
3
|
+
- Fix: Scroll history resets properly during scroll operations, which was causing gaps after scroll
|
|
4
|
+
- Fix: Made scroll buffer calculations and scroll jump handling more reliable
|
|
2
5
|
|
|
6
|
+
## 1.0.9
|
|
7
|
+
- Fix: Use the `use-sync-external-store` shim to support older versions of react
|
|
8
|
+
- Fix: Lists sometimes leaving some gaps when reordering a list
|
|
9
|
+
- Fix: Sometimes precomputing next scroll position for calculation incorrectly
|
|
10
|
+
|
|
11
|
+
## 1.0.8
|
|
12
|
+
- Perf: The scroll buffering algorithm is smarter and adjusts based on scroll direction for better performance
|
|
13
|
+
- Perf: The container-finding logic keeps index order, reducing gaps in rendering
|
|
14
|
+
- Perf: Combine multiple hooks in Container to a single `useArray$` hook
|
|
15
|
+
|
|
16
|
+
## 1.0.7
|
|
17
|
+
- Fix: Containers that move out of view are handled better
|
|
18
|
+
|
|
19
|
+
## 1.0.6
|
|
20
|
+
- Fix: Average item size calculations are more accurate while scrolling
|
|
21
|
+
- Fix: Items in view are handled better when data changes
|
|
22
|
+
- Fix: Scroll position is maintained more accurately during updates
|
|
23
|
+
|
|
24
|
+
## 1.0.5
|
|
25
|
+
- Fix: Fast scrolling sometimes caused elements to disappear
|
|
26
|
+
- Fix: Out-of-range `scrollToIndex` calls are handled better
|
|
27
|
+
|
|
28
|
+
## 1.0.4
|
|
29
|
+
- Fix: Container allocation is more efficient
|
|
30
|
+
- Fix: Bidirectional infinite lists scroll better on the old architecture
|
|
31
|
+
- Fix: Item size updates are handled more reliably
|
|
32
|
+
- Fix: Container reuse logic is more accurate
|
|
33
|
+
- Fix: Zero-size layouts are handled better in the old architecture
|
|
34
|
+
|
|
35
|
+
## 1.0.3
|
|
36
|
+
- Fix: Items that are larger than the estimated size are handled correctly
|
|
37
|
+
|
|
38
|
+
## 1.0.2
|
|
39
|
+
- Fix: Initial layout works better in the old architecture
|
|
40
|
+
- Fix: Average size calculations are more accurate for bidirectional scrolling
|
|
41
|
+
- Fix: Initial scroll index behavior is more precise
|
|
42
|
+
- Fix: Item size calculations are more accurate overall
|
|
43
|
+
|
|
44
|
+
## 1.0.1
|
|
45
|
+
- Fix: Total size calculations are correct when using average sizes
|
|
46
|
+
- Fix: Keyboard avoiding behavior is improved for a smoother experience
|
|
47
|
+
|
|
48
|
+
## 1.0.0
|
|
3
49
|
Initial release! Major changes if you're coming from a beta version:
|
|
4
50
|
|
|
5
|
-
- Item hooks like `useRecyclingState` are no longer render props, but can be imported from `@legendapp/list
|
|
51
|
+
- Item hooks like `useRecyclingState` are no longer render props, but can be imported directly from `@legendapp/list`.
|
package/index.js
CHANGED
|
@@ -80,12 +80,6 @@ function createSelectorFunctionsArr(ctx, signalNames) {
|
|
|
80
80
|
}
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
-
function useArr$(signalNames) {
|
|
84
|
-
const ctx = React2__namespace.useContext(ContextState);
|
|
85
|
-
const { subscribe, get } = React2__namespace.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
|
|
86
|
-
const value = shim.useSyncExternalStore(subscribe, get);
|
|
87
|
-
return value;
|
|
88
|
-
}
|
|
89
83
|
function listen$(ctx, signalName, cb) {
|
|
90
84
|
const { listeners } = ctx;
|
|
91
85
|
let setListeners = listeners.get(signalName);
|
|
@@ -120,6 +114,12 @@ function getContentSize(ctx) {
|
|
|
120
114
|
const totalSize = values.get("totalSize") || 0;
|
|
121
115
|
return headerSize + footerSize + totalSize + stylePaddingTop;
|
|
122
116
|
}
|
|
117
|
+
function useArr$(signalNames) {
|
|
118
|
+
const ctx = React2__namespace.useContext(ContextState);
|
|
119
|
+
const { subscribe, get } = React2__namespace.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
|
|
120
|
+
const value = shim.useSyncExternalStore(subscribe, get);
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
123
|
|
|
124
124
|
// src/DebugView.tsx
|
|
125
125
|
var DebugRow = ({ children }) => {
|
|
@@ -1546,8 +1546,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1546
1546
|
maxSizeInRow = 0;
|
|
1547
1547
|
}
|
|
1548
1548
|
}
|
|
1549
|
-
const prevStartBuffered = state.startBuffered;
|
|
1550
|
-
const prevEndBuffered = state.endBuffered;
|
|
1551
1549
|
Object.assign(state, {
|
|
1552
1550
|
startBuffered,
|
|
1553
1551
|
startBufferedId,
|
|
@@ -1564,34 +1562,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1564
1562
|
if (startBuffered !== null && endBuffered !== null) {
|
|
1565
1563
|
let numContainers = prevNumContainers;
|
|
1566
1564
|
const needNewContainers = [];
|
|
1567
|
-
|
|
1568
|
-
const
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
return true;
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
};
|
|
1577
|
-
if (isReset) {
|
|
1578
|
-
for (let i = startBuffered; i <= endBuffered; i++) {
|
|
1579
|
-
if (!isContained(i)) {
|
|
1580
|
-
needNewContainers.push(i);
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
} else {
|
|
1584
|
-
for (let i = startBuffered; i < prevStartBuffered; i++) {
|
|
1585
|
-
if (!isContained(i)) {
|
|
1586
|
-
needNewContainers.push(i);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
for (let i = Math.max(prevEndBuffered + 1, startBuffered); i <= endBuffered; i++) {
|
|
1590
|
-
if (!isContained(i)) {
|
|
1591
|
-
needNewContainers.push(i);
|
|
1592
|
-
}
|
|
1565
|
+
const isContained = (i) => {
|
|
1566
|
+
const id = getId(i);
|
|
1567
|
+
for (let j = 0; j < numContainers; j++) {
|
|
1568
|
+
const key = peek$(ctx, `containerItemKey${j}`);
|
|
1569
|
+
if (key === id) {
|
|
1570
|
+
return true;
|
|
1593
1571
|
}
|
|
1594
1572
|
}
|
|
1573
|
+
};
|
|
1574
|
+
for (let i = startBuffered; i <= endBuffered; i++) {
|
|
1575
|
+
if (!isContained(i)) {
|
|
1576
|
+
needNewContainers.push(i);
|
|
1577
|
+
}
|
|
1595
1578
|
}
|
|
1596
1579
|
if (needNewContainers.length > 0) {
|
|
1597
1580
|
const availableContainers = findAvailableContainers(
|
|
@@ -1710,6 +1693,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1710
1693
|
if (state) {
|
|
1711
1694
|
state.scrollingToOffset = void 0;
|
|
1712
1695
|
state.scrollAdjustHandler.setDisableAdjust(false);
|
|
1696
|
+
state.scrollHistory.length = 0;
|
|
1713
1697
|
calculateItemsInView();
|
|
1714
1698
|
}
|
|
1715
1699
|
};
|
|
@@ -1717,6 +1701,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1717
1701
|
var _a;
|
|
1718
1702
|
const state = refState.current;
|
|
1719
1703
|
state.scrollAdjustHandler.setDisableAdjust(true);
|
|
1704
|
+
state.scrollHistory.length = 0;
|
|
1720
1705
|
state.scrollingToOffset = offset;
|
|
1721
1706
|
(_a = refScroller.current) == null ? void 0 : _a.scrollTo({
|
|
1722
1707
|
x: horizontal ? offset : 0,
|
|
@@ -1987,7 +1972,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1987
1972
|
}
|
|
1988
1973
|
if (__DEV__ && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
|
|
1989
1974
|
console.warn(
|
|
1990
|
-
"[legend-list] No container
|
|
1975
|
+
"[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize or increasing initialContainerPoolRatio.",
|
|
1976
|
+
{
|
|
1977
|
+
debugInfo: {
|
|
1978
|
+
numContainers,
|
|
1979
|
+
numNeeded,
|
|
1980
|
+
stillNeeded,
|
|
1981
|
+
numContainersPooled: peek$(ctx, "numContainersPooled")
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1991
1984
|
);
|
|
1992
1985
|
}
|
|
1993
1986
|
}
|
|
@@ -2240,11 +2233,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2240
2233
|
return;
|
|
2241
2234
|
}
|
|
2242
2235
|
const state = refState.current;
|
|
2236
|
+
const scrollingToOffset = state.scrollingToOffset;
|
|
2243
2237
|
const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
|
|
2244
2238
|
if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
|
|
2245
2239
|
return;
|
|
2246
2240
|
}
|
|
2247
|
-
if (
|
|
2241
|
+
if (scrollingToOffset !== void 0 && Math.abs(newScroll - scrollingToOffset) < 10) {
|
|
2248
2242
|
finishScrollTo();
|
|
2249
2243
|
}
|
|
2250
2244
|
if (state.disableScrollJumpsFrom !== void 0) {
|
|
@@ -2257,7 +2251,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2257
2251
|
state.hasScrolled = true;
|
|
2258
2252
|
state.lastBatchingAction = Date.now();
|
|
2259
2253
|
const currentTime = performance.now();
|
|
2260
|
-
if (!(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
|
|
2254
|
+
if (scrollingToOffset === void 0 && !(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
|
|
2261
2255
|
state.scrollHistory.push({ scroll: newScroll, time: currentTime });
|
|
2262
2256
|
}
|
|
2263
2257
|
if (state.scrollHistory.length > 5) {
|
|
@@ -2272,10 +2266,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2272
2266
|
let velocity = 0;
|
|
2273
2267
|
if (state.scrollHistory.length >= 2) {
|
|
2274
2268
|
const newest = state.scrollHistory[state.scrollHistory.length - 1];
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2269
|
+
let oldest;
|
|
2270
|
+
for (let i = 0; i < state.scrollHistory.length - 1; i++) {
|
|
2271
|
+
const entry = state.scrollHistory[i];
|
|
2272
|
+
if (newest.time - entry.time <= 100) {
|
|
2273
|
+
oldest = entry;
|
|
2274
|
+
break;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
if (oldest) {
|
|
2278
|
+
const scrollDiff = newest.scroll - oldest.scroll;
|
|
2279
|
+
const timeDiff = newest.time - oldest.time;
|
|
2280
|
+
velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
|
|
2281
|
+
}
|
|
2279
2282
|
}
|
|
2280
2283
|
state.scrollPrev = state.scroll;
|
|
2281
2284
|
state.scrollPrevTime = state.scrollTime;
|
package/index.mjs
CHANGED
|
@@ -59,12 +59,6 @@ function createSelectorFunctionsArr(ctx, signalNames) {
|
|
|
59
59
|
}
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
-
function useArr$(signalNames) {
|
|
63
|
-
const ctx = React2.useContext(ContextState);
|
|
64
|
-
const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
|
|
65
|
-
const value = useSyncExternalStore(subscribe, get);
|
|
66
|
-
return value;
|
|
67
|
-
}
|
|
68
62
|
function listen$(ctx, signalName, cb) {
|
|
69
63
|
const { listeners } = ctx;
|
|
70
64
|
let setListeners = listeners.get(signalName);
|
|
@@ -99,6 +93,12 @@ function getContentSize(ctx) {
|
|
|
99
93
|
const totalSize = values.get("totalSize") || 0;
|
|
100
94
|
return headerSize + footerSize + totalSize + stylePaddingTop;
|
|
101
95
|
}
|
|
96
|
+
function useArr$(signalNames) {
|
|
97
|
+
const ctx = React2.useContext(ContextState);
|
|
98
|
+
const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
|
|
99
|
+
const value = useSyncExternalStore(subscribe, get);
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
102
|
|
|
103
103
|
// src/DebugView.tsx
|
|
104
104
|
var DebugRow = ({ children }) => {
|
|
@@ -1525,8 +1525,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1525
1525
|
maxSizeInRow = 0;
|
|
1526
1526
|
}
|
|
1527
1527
|
}
|
|
1528
|
-
const prevStartBuffered = state.startBuffered;
|
|
1529
|
-
const prevEndBuffered = state.endBuffered;
|
|
1530
1528
|
Object.assign(state, {
|
|
1531
1529
|
startBuffered,
|
|
1532
1530
|
startBufferedId,
|
|
@@ -1543,34 +1541,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1543
1541
|
if (startBuffered !== null && endBuffered !== null) {
|
|
1544
1542
|
let numContainers = prevNumContainers;
|
|
1545
1543
|
const needNewContainers = [];
|
|
1546
|
-
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
return true;
|
|
1553
|
-
}
|
|
1554
|
-
}
|
|
1555
|
-
};
|
|
1556
|
-
if (isReset) {
|
|
1557
|
-
for (let i = startBuffered; i <= endBuffered; i++) {
|
|
1558
|
-
if (!isContained(i)) {
|
|
1559
|
-
needNewContainers.push(i);
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
} else {
|
|
1563
|
-
for (let i = startBuffered; i < prevStartBuffered; i++) {
|
|
1564
|
-
if (!isContained(i)) {
|
|
1565
|
-
needNewContainers.push(i);
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
for (let i = Math.max(prevEndBuffered + 1, startBuffered); i <= endBuffered; i++) {
|
|
1569
|
-
if (!isContained(i)) {
|
|
1570
|
-
needNewContainers.push(i);
|
|
1571
|
-
}
|
|
1544
|
+
const isContained = (i) => {
|
|
1545
|
+
const id = getId(i);
|
|
1546
|
+
for (let j = 0; j < numContainers; j++) {
|
|
1547
|
+
const key = peek$(ctx, `containerItemKey${j}`);
|
|
1548
|
+
if (key === id) {
|
|
1549
|
+
return true;
|
|
1572
1550
|
}
|
|
1573
1551
|
}
|
|
1552
|
+
};
|
|
1553
|
+
for (let i = startBuffered; i <= endBuffered; i++) {
|
|
1554
|
+
if (!isContained(i)) {
|
|
1555
|
+
needNewContainers.push(i);
|
|
1556
|
+
}
|
|
1574
1557
|
}
|
|
1575
1558
|
if (needNewContainers.length > 0) {
|
|
1576
1559
|
const availableContainers = findAvailableContainers(
|
|
@@ -1689,6 +1672,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1689
1672
|
if (state) {
|
|
1690
1673
|
state.scrollingToOffset = void 0;
|
|
1691
1674
|
state.scrollAdjustHandler.setDisableAdjust(false);
|
|
1675
|
+
state.scrollHistory.length = 0;
|
|
1692
1676
|
calculateItemsInView();
|
|
1693
1677
|
}
|
|
1694
1678
|
};
|
|
@@ -1696,6 +1680,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1696
1680
|
var _a;
|
|
1697
1681
|
const state = refState.current;
|
|
1698
1682
|
state.scrollAdjustHandler.setDisableAdjust(true);
|
|
1683
|
+
state.scrollHistory.length = 0;
|
|
1699
1684
|
state.scrollingToOffset = offset;
|
|
1700
1685
|
(_a = refScroller.current) == null ? void 0 : _a.scrollTo({
|
|
1701
1686
|
x: horizontal ? offset : 0,
|
|
@@ -1966,7 +1951,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
1966
1951
|
}
|
|
1967
1952
|
if (__DEV__ && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
|
|
1968
1953
|
console.warn(
|
|
1969
|
-
"[legend-list] No container
|
|
1954
|
+
"[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize or increasing initialContainerPoolRatio.",
|
|
1955
|
+
{
|
|
1956
|
+
debugInfo: {
|
|
1957
|
+
numContainers,
|
|
1958
|
+
numNeeded,
|
|
1959
|
+
stillNeeded,
|
|
1960
|
+
numContainersPooled: peek$(ctx, "numContainersPooled")
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1970
1963
|
);
|
|
1971
1964
|
}
|
|
1972
1965
|
}
|
|
@@ -2219,11 +2212,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2219
2212
|
return;
|
|
2220
2213
|
}
|
|
2221
2214
|
const state = refState.current;
|
|
2215
|
+
const scrollingToOffset = state.scrollingToOffset;
|
|
2222
2216
|
const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
|
|
2223
2217
|
if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
|
|
2224
2218
|
return;
|
|
2225
2219
|
}
|
|
2226
|
-
if (
|
|
2220
|
+
if (scrollingToOffset !== void 0 && Math.abs(newScroll - scrollingToOffset) < 10) {
|
|
2227
2221
|
finishScrollTo();
|
|
2228
2222
|
}
|
|
2229
2223
|
if (state.disableScrollJumpsFrom !== void 0) {
|
|
@@ -2236,7 +2230,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2236
2230
|
state.hasScrolled = true;
|
|
2237
2231
|
state.lastBatchingAction = Date.now();
|
|
2238
2232
|
const currentTime = performance.now();
|
|
2239
|
-
if (!(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
|
|
2233
|
+
if (scrollingToOffset === void 0 && !(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
|
|
2240
2234
|
state.scrollHistory.push({ scroll: newScroll, time: currentTime });
|
|
2241
2235
|
}
|
|
2242
2236
|
if (state.scrollHistory.length > 5) {
|
|
@@ -2251,10 +2245,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2251
2245
|
let velocity = 0;
|
|
2252
2246
|
if (state.scrollHistory.length >= 2) {
|
|
2253
2247
|
const newest = state.scrollHistory[state.scrollHistory.length - 1];
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2248
|
+
let oldest;
|
|
2249
|
+
for (let i = 0; i < state.scrollHistory.length - 1; i++) {
|
|
2250
|
+
const entry = state.scrollHistory[i];
|
|
2251
|
+
if (newest.time - entry.time <= 100) {
|
|
2252
|
+
oldest = entry;
|
|
2253
|
+
break;
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
if (oldest) {
|
|
2257
|
+
const scrollDiff = newest.scroll - oldest.scroll;
|
|
2258
|
+
const timeDiff = newest.time - oldest.time;
|
|
2259
|
+
velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
|
|
2260
|
+
}
|
|
2258
2261
|
}
|
|
2259
2262
|
state.scrollPrev = state.scroll;
|
|
2260
2263
|
state.scrollPrevTime = state.scrollTime;
|
package/package.json
CHANGED