@legendapp/list 1.0.7 → 1.0.9

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.
Files changed (3) hide show
  1. package/index.js +219 -106
  2. package/index.mjs +219 -106
  3. package/package.json +4 -1
package/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var React2 = require('react');
4
4
  var reactNative = require('react-native');
5
+ var shim = require('use-sync-external-store/shim');
5
6
 
6
7
  function _interopNamespace(e) {
7
8
  if (e && e.__esModule) return e;
@@ -46,16 +47,43 @@ function StateProvider({ children }) {
46
47
  function useStateContext() {
47
48
  return React2__namespace.useContext(ContextState);
48
49
  }
49
- function createSelectorFunctions(ctx, signalName) {
50
+ function createSelectorFunctionsArr(ctx, signalNames) {
51
+ let lastValues = [];
52
+ let lastSignalValues = [];
50
53
  return {
51
- subscribe: (cb) => listen$(ctx, signalName, cb),
52
- get: () => peek$(ctx, signalName)
54
+ subscribe: (cb) => {
55
+ const listeners = [];
56
+ for (const signalName of signalNames) {
57
+ listeners.push(listen$(ctx, signalName, cb));
58
+ }
59
+ return () => {
60
+ for (const listener of listeners) {
61
+ listener();
62
+ }
63
+ };
64
+ },
65
+ get: () => {
66
+ const currentValues = [];
67
+ let hasChanged = false;
68
+ for (let i = 0; i < signalNames.length; i++) {
69
+ const value = peek$(ctx, signalNames[i]);
70
+ currentValues.push(value);
71
+ if (value !== lastSignalValues[i]) {
72
+ hasChanged = true;
73
+ }
74
+ }
75
+ lastSignalValues = currentValues;
76
+ if (hasChanged) {
77
+ lastValues = currentValues;
78
+ }
79
+ return lastValues;
80
+ }
53
81
  };
54
82
  }
55
- function use$(signalName) {
83
+ function useArr$(signalNames) {
56
84
  const ctx = React2__namespace.useContext(ContextState);
57
- const { subscribe, get } = React2__namespace.useMemo(() => createSelectorFunctions(ctx, signalName), []);
58
- const value = React2.useSyncExternalStore(subscribe, get);
85
+ const { subscribe, get } = React2__namespace.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
86
+ const value = shim.useSyncExternalStore(subscribe, get);
59
87
  return value;
60
88
  }
61
89
  function listen$(ctx, signalName, cb) {
@@ -99,15 +127,25 @@ var DebugRow = ({ children }) => {
99
127
  };
100
128
  var DebugView = React2__namespace.memo(function DebugView2({ state }) {
101
129
  const ctx = useStateContext();
102
- const totalSize = use$("totalSize") || 0;
103
- const totalSizeWithScrollAdjust = use$("totalSizeWithScrollAdjust") || 0;
104
- const scrollAdjust = use$("scrollAdjust") || 0;
105
- const rawScroll = use$("debugRawScroll") || 0;
106
- const scroll = use$("debugComputedScroll") || 0;
130
+ const [
131
+ totalSize = 0,
132
+ totalSizeWithScrollAdjust = 0,
133
+ scrollAdjust = 0,
134
+ rawScroll = 0,
135
+ scroll = 0,
136
+ numContainers = 0,
137
+ numContainersPooled = 0
138
+ ] = useArr$([
139
+ "totalSize",
140
+ "totalSizeWithScrollAdjust",
141
+ "scrollAdjust",
142
+ "debugRawScroll",
143
+ "debugComputedScroll",
144
+ "numContainers",
145
+ "numContainersPooled"
146
+ ]);
107
147
  const contentSize = getContentSize(ctx);
108
148
  const [, forceUpdate] = React2.useReducer((x) => x + 1, 0);
109
- use$("numContainers");
110
- use$("numContainersPooled");
111
149
  useInterval(() => {
112
150
  forceUpdate();
113
151
  }, 100);
@@ -162,6 +200,12 @@ function roundSize(size) {
162
200
  function isNullOrUndefined(value) {
163
201
  return value === null || value === void 0;
164
202
  }
203
+ function comparatorByDistance(a, b) {
204
+ return b.distance - a.distance;
205
+ }
206
+ function comparatorDefault(a, b) {
207
+ return a - b;
208
+ }
165
209
  var symbolFirst = Symbol();
166
210
  function useInit(cb) {
167
211
  const refValue = React2.useRef(symbolFirst);
@@ -285,14 +329,25 @@ var Container = ({
285
329
  }) => {
286
330
  const ctx = useStateContext();
287
331
  const columnWrapperStyle = ctx.columnWrapperStyle;
288
- const maintainVisibleContentPosition = use$("maintainVisibleContentPosition");
289
- const position = use$(`containerPosition${id}`) || ANCHORED_POSITION_OUT_OF_VIEW;
290
- const column = use$(`containerColumn${id}`) || 0;
291
- const numColumns = use$("numColumns");
292
- const lastItemKeys = use$("lastItemKeys");
293
- const itemKey = use$(`containerItemKey${id}`);
294
- const data = use$(`containerItemData${id}`);
295
- const extraData = use$("extraData");
332
+ const [
333
+ maintainVisibleContentPosition,
334
+ position = ANCHORED_POSITION_OUT_OF_VIEW,
335
+ column = 0,
336
+ numColumns,
337
+ lastItemKeys,
338
+ itemKey,
339
+ data,
340
+ extraData
341
+ ] = useArr$([
342
+ "maintainVisibleContentPosition",
343
+ `containerPosition${id}`,
344
+ `containerColumn${id}`,
345
+ "numColumns",
346
+ "lastItemKeys",
347
+ `containerItemKey${id}`,
348
+ `containerItemData${id}`,
349
+ "extraData"
350
+ ]);
296
351
  const refLastSize = React2.useRef();
297
352
  const ref = React2.useRef(null);
298
353
  const [layoutRenderCount, forceLayoutRender] = React2.useState(0);
@@ -390,11 +445,11 @@ var Container = ({
390
445
  const contentFragment = /* @__PURE__ */ React2__namespace.default.createElement(React2__namespace.default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React2__namespace.default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && !lastItemKeys.includes(itemKey) && /* @__PURE__ */ React2__namespace.default.createElement(ItemSeparatorComponent, { leadingItem: renderedItemInfo.item })));
391
446
  if (maintainVisibleContentPosition) {
392
447
  const anchorStyle = position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
393
- if (ENABLE_DEVMODE) {
448
+ if (__DEV__ && ENABLE_DEVMODE) {
394
449
  anchorStyle.borderColor = position.type === "top" ? "red" : "blue";
395
450
  anchorStyle.borderWidth = 1;
396
451
  }
397
- return /* @__PURE__ */ React2__namespace.default.createElement(LeanView, { style }, /* @__PURE__ */ React2__namespace.default.createElement(LeanView, { style: anchorStyle, onLayout, ref }, contentFragment, ENABLE_DEVMODE && /* @__PURE__ */ React2__namespace.default.createElement(reactNative.Text, { style: { position: "absolute", top: 0, left: 0, zIndex: 1e3 } }, position.top)));
452
+ return /* @__PURE__ */ React2__namespace.default.createElement(LeanView, { style }, /* @__PURE__ */ React2__namespace.default.createElement(LeanView, { style: anchorStyle, onLayout, ref }, contentFragment, __DEV__ && ENABLE_DEVMODE && /* @__PURE__ */ React2__namespace.default.createElement(reactNative.Text, { style: { position: "absolute", top: 0, left: 0, zIndex: 1e3 } }, position.top)));
398
453
  }
399
454
  return /* @__PURE__ */ React2__namespace.default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
400
455
  };
@@ -438,7 +493,7 @@ var Containers = typedMemo(function Containers2({
438
493
  }) {
439
494
  const ctx = useStateContext();
440
495
  const columnWrapperStyle = ctx.columnWrapperStyle;
441
- const numContainers = use$("numContainersPooled");
496
+ const [numContainers] = useArr$(["numContainersPooled"]);
442
497
  const animSize = useValue$(
443
498
  "totalSizeWithScrollAdjust",
444
499
  void 0,
@@ -1035,10 +1090,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1035
1090
  isAtTop: false,
1036
1091
  data: dataProp,
1037
1092
  scrollLength: initialScrollLength,
1038
- startBuffered: 0,
1039
- startNoBuffer: 0,
1040
- endBuffered: 0,
1041
- endNoBuffer: 0,
1093
+ startBuffered: -1,
1094
+ startNoBuffer: -1,
1095
+ endBuffered: -1,
1096
+ endNoBuffer: -1,
1042
1097
  scroll: initialContentOffset || 0,
1043
1098
  totalSize: 0,
1044
1099
  totalSizeBelowAnchor: 0,
@@ -1239,6 +1294,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1239
1294
  const state = refState.current;
1240
1295
  if (state.scrollingToOffset === void 0) {
1241
1296
  state.disableScrollJumpsFrom = state.scroll - state.scrollAdjustHandler.getAppliedAdjust();
1297
+ state.scrollHistory.length = 0;
1242
1298
  setTimeout(() => {
1243
1299
  state.disableScrollJumpsFrom = void 0;
1244
1300
  }, timeout);
@@ -1334,7 +1390,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1334
1390
  }
1335
1391
  return false;
1336
1392
  }, []);
1337
- const calculateItemsInView = React2.useCallback(() => {
1393
+ const calculateItemsInView = React2.useCallback((isReset) => {
1338
1394
  var _a;
1339
1395
  const state = refState.current;
1340
1396
  const {
@@ -1353,14 +1409,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1353
1409
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
1354
1410
  const numColumns = peek$(ctx, "numColumns");
1355
1411
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
1356
- const scrollExtra = Math.max(-16, Math.min(16, speed)) * 16;
1357
1412
  let scrollState = state.scroll;
1413
+ const scrollExtra = Math.max(-16, Math.min(16, speed)) * 24;
1358
1414
  const useAverageSize = !state.disableScrollJumpsFrom && speed >= 0 && peek$(ctx, "containersDidLayout");
1359
1415
  if (!state.queuedInitialLayout && initialScrollIndex) {
1360
1416
  const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
1361
1417
  scrollState = updatedOffset;
1362
1418
  }
1363
- let scroll = scrollState - previousScrollAdjust - topPad;
1419
+ const scrollAdjustPad = -previousScrollAdjust - topPad;
1420
+ let scroll = scrollState + scrollExtra + scrollAdjustPad;
1364
1421
  if (scroll + scrollLength > totalSize) {
1365
1422
  scroll = totalSize - scrollLength;
1366
1423
  }
@@ -1370,13 +1427,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1370
1427
  }
1371
1428
  let scrollBufferTop = scrollBuffer;
1372
1429
  let scrollBufferBottom = scrollBuffer;
1373
- if (scrollExtra > 8) {
1374
- scrollBufferTop = 0;
1375
- scrollBufferBottom = scrollBuffer + scrollExtra;
1376
- }
1377
- if (scrollExtra < -8) {
1378
- scrollBufferTop = scrollBuffer - scrollExtra;
1379
- scrollBufferBottom = 0;
1430
+ if (Math.abs(speed) > 4) {
1431
+ if (speed > 0) {
1432
+ scrollBufferTop = scrollBuffer * 0.1;
1433
+ scrollBufferBottom = scrollBuffer * 1.9;
1434
+ } else {
1435
+ scrollBufferTop = scrollBuffer * 1.9;
1436
+ scrollBufferBottom = scrollBuffer * 0.1;
1437
+ }
1380
1438
  }
1381
1439
  if (state.scrollForNextCalculateItemsInView) {
1382
1440
  const { top: top2, bottom } = state.scrollForNextCalculateItemsInView;
@@ -1436,6 +1494,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1436
1494
  return topOffset;
1437
1495
  };
1438
1496
  let foundEnd = false;
1497
+ let nextTop;
1498
+ let nextBottom;
1439
1499
  const prevNumContainers = ctx.values.get("numContainers");
1440
1500
  let maxIndexRendered = 0;
1441
1501
  for (let i = 0; i < prevNumContainers; i++) {
@@ -1465,6 +1525,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1465
1525
  if (startBuffered === null && top + size > scroll - scrollBufferTop) {
1466
1526
  startBuffered = i;
1467
1527
  startBufferedId = id;
1528
+ nextTop = top;
1468
1529
  }
1469
1530
  if (startNoBuffer !== null) {
1470
1531
  if (top <= scrollBottom) {
@@ -1472,6 +1533,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1472
1533
  }
1473
1534
  if (top <= scrollBottom + scrollBufferBottom) {
1474
1535
  endBuffered = i;
1536
+ nextBottom = top + maxSizeInRow - scrollLength;
1475
1537
  } else {
1476
1538
  foundEnd = true;
1477
1539
  }
@@ -1484,6 +1546,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1484
1546
  maxSizeInRow = 0;
1485
1547
  }
1486
1548
  }
1549
+ const prevStartBuffered = state.startBuffered;
1550
+ const prevEndBuffered = state.endBuffered;
1487
1551
  Object.assign(state, {
1488
1552
  startBuffered,
1489
1553
  startBufferedId,
@@ -1491,82 +1555,65 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1491
1555
  endBuffered,
1492
1556
  endNoBuffer
1493
1557
  });
1494
- const nextTop = Math.ceil(startBuffered !== null ? positions.get(startBufferedId) + scrollBuffer : 0);
1495
- const nextBottom = Math.floor(
1496
- endBuffered !== null ? (positions.get(getId(endBuffered + 1)) || 0) - scrollLength - scrollBuffer : 0
1497
- );
1498
- if (state.enableScrollForNextCalculateItemsInView) {
1499
- state.scrollForNextCalculateItemsInView = nextTop >= 0 && nextBottom >= 0 ? {
1558
+ if (state.enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0 && state.disableScrollJumpsFrom === void 0) {
1559
+ state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
1500
1560
  top: nextTop,
1501
1561
  bottom: nextBottom
1502
1562
  } : void 0;
1503
1563
  }
1504
1564
  if (startBuffered !== null && endBuffered !== null) {
1505
1565
  let numContainers = prevNumContainers;
1506
- let didWarnMoreContainers = false;
1507
- const allocatedContainers = /* @__PURE__ */ new Set();
1508
- for (let i = startBuffered; i <= endBuffered; i++) {
1509
- let isContained = false;
1510
- const id = getId(i);
1511
- for (let j = 0; j < numContainers; j++) {
1512
- const key = peek$(ctx, `containerItemKey${j}`);
1513
- if (key === id) {
1514
- isContained = true;
1515
- break;
1516
- }
1517
- }
1518
- if (!isContained) {
1519
- const top2 = positions.get(id) || 0;
1520
- let furthestIndex = -1;
1521
- let furthestDistance = 0;
1522
- for (let u = 0; u < numContainers; u++) {
1523
- if (allocatedContainers.has(u)) {
1524
- continue;
1566
+ const needNewContainers = [];
1567
+ if (isReset || startBuffered < prevStartBuffered || endBuffered > prevEndBuffered) {
1568
+ const isContained = (i) => {
1569
+ const id = getId(i);
1570
+ for (let j = 0; j < numContainers; j++) {
1571
+ const key = peek$(ctx, `containerItemKey${j}`);
1572
+ if (key === id) {
1573
+ return true;
1525
1574
  }
1526
- const key = peek$(ctx, `containerItemKey${u}`);
1527
- if (key === void 0) {
1528
- furthestIndex = u;
1529
- break;
1530
- }
1531
- const index2 = state.indexByKey.get(key);
1532
- const pos = peek$(ctx, `containerPosition${u}`).top;
1533
- if (isNullOrUndefined(index2) || pos === POSITION_OUT_OF_VIEW) {
1534
- furthestIndex = u;
1535
- break;
1575
+ }
1576
+ };
1577
+ if (isReset) {
1578
+ for (let i = startBuffered; i <= endBuffered; i++) {
1579
+ if (!isContained(i)) {
1580
+ needNewContainers.push(i);
1536
1581
  }
1537
- if (index2 < startBuffered || index2 > endBuffered) {
1538
- const distance = Math.abs(pos - top2);
1539
- if (index2 < 0 || pos === POSITION_OUT_OF_VIEW || distance > furthestDistance) {
1540
- furthestDistance = distance;
1541
- furthestIndex = u;
1542
- }
1582
+ }
1583
+ } else {
1584
+ for (let i = startBuffered; i < prevStartBuffered; i++) {
1585
+ if (!isContained(i)) {
1586
+ needNewContainers.push(i);
1543
1587
  }
1544
1588
  }
1545
- const containerId = furthestIndex >= 0 ? furthestIndex : numContainers;
1546
- set$(ctx, `containerItemKey${containerId}`, id);
1547
- const index = state.indexByKey.get(id);
1548
- set$(ctx, `containerItemData${containerId}`, data[index]);
1549
- allocatedContainers.add(containerId);
1550
- if (furthestIndex === -1) {
1551
- numContainers++;
1552
- set$(ctx, `containerItemKey${containerId}`, id);
1553
- const index2 = state.indexByKey.get(id);
1554
- set$(ctx, `containerItemData${containerId}`, data[index2]);
1555
- set$(ctx, `containerPosition${containerId}`, ANCHORED_POSITION_OUT_OF_VIEW);
1556
- set$(ctx, `containerColumn${containerId}`, -1);
1557
- if (__DEV__ && !didWarnMoreContainers && numContainers > peek$(ctx, "numContainersPooled")) {
1558
- didWarnMoreContainers = true;
1559
- console.warn(
1560
- "[legend-list] No container to recycle, 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."
1561
- );
1589
+ for (let i = Math.max(prevEndBuffered + 1, startBuffered); i <= endBuffered; i++) {
1590
+ if (!isContained(i)) {
1591
+ needNewContainers.push(i);
1562
1592
  }
1563
1593
  }
1564
1594
  }
1565
1595
  }
1566
- if (numContainers !== prevNumContainers) {
1567
- set$(ctx, "numContainers", numContainers);
1568
- if (numContainers > peek$(ctx, "numContainersPooled")) {
1569
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
1596
+ if (needNewContainers.length > 0) {
1597
+ const availableContainers = findAvailableContainers(
1598
+ needNewContainers.length,
1599
+ startBuffered,
1600
+ endBuffered
1601
+ );
1602
+ for (let idx = 0; idx < needNewContainers.length; idx++) {
1603
+ const i = needNewContainers[idx];
1604
+ const containerIndex = availableContainers[idx];
1605
+ const id = getId(i);
1606
+ set$(ctx, `containerItemKey${containerIndex}`, id);
1607
+ set$(ctx, `containerItemData${containerIndex}`, data[i]);
1608
+ if (containerIndex >= numContainers) {
1609
+ numContainers = containerIndex + 1;
1610
+ }
1611
+ }
1612
+ if (numContainers !== prevNumContainers) {
1613
+ set$(ctx, "numContainers", numContainers);
1614
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
1615
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
1616
+ }
1570
1617
  }
1571
1618
  }
1572
1619
  for (let i = 0; i < numContainers; i++) {
@@ -1596,7 +1643,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1596
1643
  const prevPos = peek$(ctx, `containerPosition${i}`);
1597
1644
  const prevColumn = peek$(ctx, `containerColumn${i}`);
1598
1645
  const prevData = peek$(ctx, `containerItemData${i}`);
1599
- if (pos.relativeCoordinate > POSITION_OUT_OF_VIEW && pos.top !== prevPos.top) {
1646
+ if (!prevPos || pos.relativeCoordinate > POSITION_OUT_OF_VIEW && pos.top !== prevPos.top) {
1600
1647
  set$(ctx, `containerPosition${i}`, pos);
1601
1648
  }
1602
1649
  if (column2 >= 0 && column2 !== prevColumn) {
@@ -1791,7 +1838,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1791
1838
  if (!keyExtractorProp) {
1792
1839
  state.positions.clear();
1793
1840
  }
1794
- calculateItemsInView();
1841
+ calculateItemsInView(
1842
+ /*isReset*/
1843
+ true
1844
+ );
1795
1845
  const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1796
1846
  if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1797
1847
  state.isEndReached = false;
@@ -1844,7 +1894,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1844
1894
  (_b = state.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1845
1895
  scrollTo(0, false);
1846
1896
  setTimeout(() => {
1847
- calculateItemsInView();
1897
+ calculateItemsInView(
1898
+ /*reset*/
1899
+ true
1900
+ );
1848
1901
  }, 0);
1849
1902
  } else {
1850
1903
  state.startBufferedId = void 0;
@@ -1859,7 +1912,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1859
1912
  }
1860
1913
  scrollTo(0, false);
1861
1914
  setTimeout(() => {
1862
- calculateItemsInView();
1915
+ calculateItemsInView(
1916
+ /*reset*/
1917
+ true
1918
+ );
1863
1919
  }, 0);
1864
1920
  }
1865
1921
  }
@@ -1888,6 +1944,56 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1888
1944
  });
1889
1945
  addTotalSize(null, totalSize, totalSizeBelowIndex);
1890
1946
  };
1947
+ const findAvailableContainers = (numNeeded, startBuffered, endBuffered) => {
1948
+ const state = refState.current;
1949
+ const numContainers = peek$(ctx, "numContainers");
1950
+ if (numNeeded === 0) return [];
1951
+ const result = [];
1952
+ const availableContainers = [];
1953
+ for (let u = 0; u < numContainers; u++) {
1954
+ const key = peek$(ctx, `containerItemKey${u}`);
1955
+ if (key === void 0) {
1956
+ result.push(u);
1957
+ if (result.length >= numNeeded) {
1958
+ return result;
1959
+ }
1960
+ }
1961
+ }
1962
+ for (let u = 0; u < numContainers; u++) {
1963
+ const key = peek$(ctx, `containerItemKey${u}`);
1964
+ if (key === void 0) continue;
1965
+ const index = state.indexByKey.get(key);
1966
+ if (index < startBuffered) {
1967
+ availableContainers.push({ index: u, distance: startBuffered - index });
1968
+ } else if (index > endBuffered) {
1969
+ availableContainers.push({ index: u, distance: index - endBuffered });
1970
+ }
1971
+ }
1972
+ const remaining = numNeeded - result.length;
1973
+ if (remaining > 0) {
1974
+ if (availableContainers.length > 0) {
1975
+ if (availableContainers.length > remaining) {
1976
+ availableContainers.sort(comparatorByDistance);
1977
+ availableContainers.length = remaining;
1978
+ }
1979
+ for (const container of availableContainers) {
1980
+ result.push(container.index);
1981
+ }
1982
+ }
1983
+ const stillNeeded = numNeeded - result.length;
1984
+ if (stillNeeded > 0) {
1985
+ for (let i = 0; i < stillNeeded; i++) {
1986
+ result.push(numContainers + i);
1987
+ }
1988
+ if (__DEV__ && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
1989
+ console.warn(
1990
+ "[legend-list] No container to recycle, 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."
1991
+ );
1992
+ }
1993
+ }
1994
+ }
1995
+ return result.sort(comparatorDefault);
1996
+ };
1891
1997
  const isFirst = !refState.current.renderItem;
1892
1998
  const memoizedLastItemKeys = React2.useMemo(() => {
1893
1999
  if (!dataProp.length) return [];
@@ -1969,10 +2075,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1969
2075
  set$(ctx, "numContainersPooled", numContainers * initialContainerPoolRatio);
1970
2076
  if (initialScrollIndex) {
1971
2077
  requestAnimationFrame(() => {
1972
- calculateItemsInView();
2078
+ calculateItemsInView(
2079
+ /*isReset*/
2080
+ true
2081
+ );
1973
2082
  });
1974
2083
  } else {
1975
- calculateItemsInView();
2084
+ calculateItemsInView(
2085
+ /*isReset*/
2086
+ true
2087
+ );
1976
2088
  }
1977
2089
  return true;
1978
2090
  }
@@ -2008,6 +2120,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2008
2120
  }
2009
2121
  const index = indexByKey.get(itemKey);
2010
2122
  const numColumns = peek$(ctx, "numColumns");
2123
+ state.scrollForNextCalculateItemsInView = void 0;
2011
2124
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
2012
2125
  const prevSize = getItemSize(itemKey, index, data);
2013
2126
  const prevSizeKnown = sizesKnown.get(itemKey);
package/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as React2 from 'react';
2
- import React2__default, { useReducer, useEffect, createContext, useMemo, useRef, useCallback, useImperativeHandle, useSyncExternalStore, useContext, useState, forwardRef, memo, useLayoutEffect } from 'react';
2
+ import React2__default, { useReducer, useEffect, createContext, useMemo, useRef, useCallback, useImperativeHandle, useContext, useState, forwardRef, memo, useLayoutEffect } from 'react';
3
3
  import { View, Text, Platform, Animated, ScrollView, StyleSheet, Dimensions, RefreshControl } from 'react-native';
4
+ import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
5
 
5
6
  // src/LegendList.tsx
6
7
  var ContextState = React2.createContext(null);
@@ -25,15 +26,42 @@ function StateProvider({ children }) {
25
26
  function useStateContext() {
26
27
  return React2.useContext(ContextState);
27
28
  }
28
- function createSelectorFunctions(ctx, signalName) {
29
+ function createSelectorFunctionsArr(ctx, signalNames) {
30
+ let lastValues = [];
31
+ let lastSignalValues = [];
29
32
  return {
30
- subscribe: (cb) => listen$(ctx, signalName, cb),
31
- get: () => peek$(ctx, signalName)
33
+ subscribe: (cb) => {
34
+ const listeners = [];
35
+ for (const signalName of signalNames) {
36
+ listeners.push(listen$(ctx, signalName, cb));
37
+ }
38
+ return () => {
39
+ for (const listener of listeners) {
40
+ listener();
41
+ }
42
+ };
43
+ },
44
+ get: () => {
45
+ const currentValues = [];
46
+ let hasChanged = false;
47
+ for (let i = 0; i < signalNames.length; i++) {
48
+ const value = peek$(ctx, signalNames[i]);
49
+ currentValues.push(value);
50
+ if (value !== lastSignalValues[i]) {
51
+ hasChanged = true;
52
+ }
53
+ }
54
+ lastSignalValues = currentValues;
55
+ if (hasChanged) {
56
+ lastValues = currentValues;
57
+ }
58
+ return lastValues;
59
+ }
32
60
  };
33
61
  }
34
- function use$(signalName) {
62
+ function useArr$(signalNames) {
35
63
  const ctx = React2.useContext(ContextState);
36
- const { subscribe, get } = React2.useMemo(() => createSelectorFunctions(ctx, signalName), []);
64
+ const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
37
65
  const value = useSyncExternalStore(subscribe, get);
38
66
  return value;
39
67
  }
@@ -78,15 +106,25 @@ var DebugRow = ({ children }) => {
78
106
  };
79
107
  var DebugView = React2.memo(function DebugView2({ state }) {
80
108
  const ctx = useStateContext();
81
- const totalSize = use$("totalSize") || 0;
82
- const totalSizeWithScrollAdjust = use$("totalSizeWithScrollAdjust") || 0;
83
- const scrollAdjust = use$("scrollAdjust") || 0;
84
- const rawScroll = use$("debugRawScroll") || 0;
85
- const scroll = use$("debugComputedScroll") || 0;
109
+ const [
110
+ totalSize = 0,
111
+ totalSizeWithScrollAdjust = 0,
112
+ scrollAdjust = 0,
113
+ rawScroll = 0,
114
+ scroll = 0,
115
+ numContainers = 0,
116
+ numContainersPooled = 0
117
+ ] = useArr$([
118
+ "totalSize",
119
+ "totalSizeWithScrollAdjust",
120
+ "scrollAdjust",
121
+ "debugRawScroll",
122
+ "debugComputedScroll",
123
+ "numContainers",
124
+ "numContainersPooled"
125
+ ]);
86
126
  const contentSize = getContentSize(ctx);
87
127
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
88
- use$("numContainers");
89
- use$("numContainersPooled");
90
128
  useInterval(() => {
91
129
  forceUpdate();
92
130
  }, 100);
@@ -141,6 +179,12 @@ function roundSize(size) {
141
179
  function isNullOrUndefined(value) {
142
180
  return value === null || value === void 0;
143
181
  }
182
+ function comparatorByDistance(a, b) {
183
+ return b.distance - a.distance;
184
+ }
185
+ function comparatorDefault(a, b) {
186
+ return a - b;
187
+ }
144
188
  var symbolFirst = Symbol();
145
189
  function useInit(cb) {
146
190
  const refValue = useRef(symbolFirst);
@@ -264,14 +308,25 @@ var Container = ({
264
308
  }) => {
265
309
  const ctx = useStateContext();
266
310
  const columnWrapperStyle = ctx.columnWrapperStyle;
267
- const maintainVisibleContentPosition = use$("maintainVisibleContentPosition");
268
- const position = use$(`containerPosition${id}`) || ANCHORED_POSITION_OUT_OF_VIEW;
269
- const column = use$(`containerColumn${id}`) || 0;
270
- const numColumns = use$("numColumns");
271
- const lastItemKeys = use$("lastItemKeys");
272
- const itemKey = use$(`containerItemKey${id}`);
273
- const data = use$(`containerItemData${id}`);
274
- const extraData = use$("extraData");
311
+ const [
312
+ maintainVisibleContentPosition,
313
+ position = ANCHORED_POSITION_OUT_OF_VIEW,
314
+ column = 0,
315
+ numColumns,
316
+ lastItemKeys,
317
+ itemKey,
318
+ data,
319
+ extraData
320
+ ] = useArr$([
321
+ "maintainVisibleContentPosition",
322
+ `containerPosition${id}`,
323
+ `containerColumn${id}`,
324
+ "numColumns",
325
+ "lastItemKeys",
326
+ `containerItemKey${id}`,
327
+ `containerItemData${id}`,
328
+ "extraData"
329
+ ]);
275
330
  const refLastSize = useRef();
276
331
  const ref = useRef(null);
277
332
  const [layoutRenderCount, forceLayoutRender] = useState(0);
@@ -369,11 +424,11 @@ var Container = ({
369
424
  const contentFragment = /* @__PURE__ */ React2__default.createElement(React2__default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React2__default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && !lastItemKeys.includes(itemKey) && /* @__PURE__ */ React2__default.createElement(ItemSeparatorComponent, { leadingItem: renderedItemInfo.item })));
370
425
  if (maintainVisibleContentPosition) {
371
426
  const anchorStyle = position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
372
- if (ENABLE_DEVMODE) {
427
+ if (__DEV__ && ENABLE_DEVMODE) {
373
428
  anchorStyle.borderColor = position.type === "top" ? "red" : "blue";
374
429
  anchorStyle.borderWidth = 1;
375
430
  }
376
- return /* @__PURE__ */ React2__default.createElement(LeanView, { style }, /* @__PURE__ */ React2__default.createElement(LeanView, { style: anchorStyle, onLayout, ref }, contentFragment, ENABLE_DEVMODE && /* @__PURE__ */ React2__default.createElement(Text, { style: { position: "absolute", top: 0, left: 0, zIndex: 1e3 } }, position.top)));
431
+ return /* @__PURE__ */ React2__default.createElement(LeanView, { style }, /* @__PURE__ */ React2__default.createElement(LeanView, { style: anchorStyle, onLayout, ref }, contentFragment, __DEV__ && ENABLE_DEVMODE && /* @__PURE__ */ React2__default.createElement(Text, { style: { position: "absolute", top: 0, left: 0, zIndex: 1e3 } }, position.top)));
377
432
  }
378
433
  return /* @__PURE__ */ React2__default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
379
434
  };
@@ -417,7 +472,7 @@ var Containers = typedMemo(function Containers2({
417
472
  }) {
418
473
  const ctx = useStateContext();
419
474
  const columnWrapperStyle = ctx.columnWrapperStyle;
420
- const numContainers = use$("numContainersPooled");
475
+ const [numContainers] = useArr$(["numContainersPooled"]);
421
476
  const animSize = useValue$(
422
477
  "totalSizeWithScrollAdjust",
423
478
  void 0,
@@ -1014,10 +1069,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1014
1069
  isAtTop: false,
1015
1070
  data: dataProp,
1016
1071
  scrollLength: initialScrollLength,
1017
- startBuffered: 0,
1018
- startNoBuffer: 0,
1019
- endBuffered: 0,
1020
- endNoBuffer: 0,
1072
+ startBuffered: -1,
1073
+ startNoBuffer: -1,
1074
+ endBuffered: -1,
1075
+ endNoBuffer: -1,
1021
1076
  scroll: initialContentOffset || 0,
1022
1077
  totalSize: 0,
1023
1078
  totalSizeBelowAnchor: 0,
@@ -1218,6 +1273,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1218
1273
  const state = refState.current;
1219
1274
  if (state.scrollingToOffset === void 0) {
1220
1275
  state.disableScrollJumpsFrom = state.scroll - state.scrollAdjustHandler.getAppliedAdjust();
1276
+ state.scrollHistory.length = 0;
1221
1277
  setTimeout(() => {
1222
1278
  state.disableScrollJumpsFrom = void 0;
1223
1279
  }, timeout);
@@ -1313,7 +1369,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1313
1369
  }
1314
1370
  return false;
1315
1371
  }, []);
1316
- const calculateItemsInView = useCallback(() => {
1372
+ const calculateItemsInView = useCallback((isReset) => {
1317
1373
  var _a;
1318
1374
  const state = refState.current;
1319
1375
  const {
@@ -1332,14 +1388,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1332
1388
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
1333
1389
  const numColumns = peek$(ctx, "numColumns");
1334
1390
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
1335
- const scrollExtra = Math.max(-16, Math.min(16, speed)) * 16;
1336
1391
  let scrollState = state.scroll;
1392
+ const scrollExtra = Math.max(-16, Math.min(16, speed)) * 24;
1337
1393
  const useAverageSize = !state.disableScrollJumpsFrom && speed >= 0 && peek$(ctx, "containersDidLayout");
1338
1394
  if (!state.queuedInitialLayout && initialScrollIndex) {
1339
1395
  const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
1340
1396
  scrollState = updatedOffset;
1341
1397
  }
1342
- let scroll = scrollState - previousScrollAdjust - topPad;
1398
+ const scrollAdjustPad = -previousScrollAdjust - topPad;
1399
+ let scroll = scrollState + scrollExtra + scrollAdjustPad;
1343
1400
  if (scroll + scrollLength > totalSize) {
1344
1401
  scroll = totalSize - scrollLength;
1345
1402
  }
@@ -1349,13 +1406,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1349
1406
  }
1350
1407
  let scrollBufferTop = scrollBuffer;
1351
1408
  let scrollBufferBottom = scrollBuffer;
1352
- if (scrollExtra > 8) {
1353
- scrollBufferTop = 0;
1354
- scrollBufferBottom = scrollBuffer + scrollExtra;
1355
- }
1356
- if (scrollExtra < -8) {
1357
- scrollBufferTop = scrollBuffer - scrollExtra;
1358
- scrollBufferBottom = 0;
1409
+ if (Math.abs(speed) > 4) {
1410
+ if (speed > 0) {
1411
+ scrollBufferTop = scrollBuffer * 0.1;
1412
+ scrollBufferBottom = scrollBuffer * 1.9;
1413
+ } else {
1414
+ scrollBufferTop = scrollBuffer * 1.9;
1415
+ scrollBufferBottom = scrollBuffer * 0.1;
1416
+ }
1359
1417
  }
1360
1418
  if (state.scrollForNextCalculateItemsInView) {
1361
1419
  const { top: top2, bottom } = state.scrollForNextCalculateItemsInView;
@@ -1415,6 +1473,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1415
1473
  return topOffset;
1416
1474
  };
1417
1475
  let foundEnd = false;
1476
+ let nextTop;
1477
+ let nextBottom;
1418
1478
  const prevNumContainers = ctx.values.get("numContainers");
1419
1479
  let maxIndexRendered = 0;
1420
1480
  for (let i = 0; i < prevNumContainers; i++) {
@@ -1444,6 +1504,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1444
1504
  if (startBuffered === null && top + size > scroll - scrollBufferTop) {
1445
1505
  startBuffered = i;
1446
1506
  startBufferedId = id;
1507
+ nextTop = top;
1447
1508
  }
1448
1509
  if (startNoBuffer !== null) {
1449
1510
  if (top <= scrollBottom) {
@@ -1451,6 +1512,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1451
1512
  }
1452
1513
  if (top <= scrollBottom + scrollBufferBottom) {
1453
1514
  endBuffered = i;
1515
+ nextBottom = top + maxSizeInRow - scrollLength;
1454
1516
  } else {
1455
1517
  foundEnd = true;
1456
1518
  }
@@ -1463,6 +1525,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1463
1525
  maxSizeInRow = 0;
1464
1526
  }
1465
1527
  }
1528
+ const prevStartBuffered = state.startBuffered;
1529
+ const prevEndBuffered = state.endBuffered;
1466
1530
  Object.assign(state, {
1467
1531
  startBuffered,
1468
1532
  startBufferedId,
@@ -1470,82 +1534,65 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1470
1534
  endBuffered,
1471
1535
  endNoBuffer
1472
1536
  });
1473
- const nextTop = Math.ceil(startBuffered !== null ? positions.get(startBufferedId) + scrollBuffer : 0);
1474
- const nextBottom = Math.floor(
1475
- endBuffered !== null ? (positions.get(getId(endBuffered + 1)) || 0) - scrollLength - scrollBuffer : 0
1476
- );
1477
- if (state.enableScrollForNextCalculateItemsInView) {
1478
- state.scrollForNextCalculateItemsInView = nextTop >= 0 && nextBottom >= 0 ? {
1537
+ if (state.enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0 && state.disableScrollJumpsFrom === void 0) {
1538
+ state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
1479
1539
  top: nextTop,
1480
1540
  bottom: nextBottom
1481
1541
  } : void 0;
1482
1542
  }
1483
1543
  if (startBuffered !== null && endBuffered !== null) {
1484
1544
  let numContainers = prevNumContainers;
1485
- let didWarnMoreContainers = false;
1486
- const allocatedContainers = /* @__PURE__ */ new Set();
1487
- for (let i = startBuffered; i <= endBuffered; i++) {
1488
- let isContained = false;
1489
- const id = getId(i);
1490
- for (let j = 0; j < numContainers; j++) {
1491
- const key = peek$(ctx, `containerItemKey${j}`);
1492
- if (key === id) {
1493
- isContained = true;
1494
- break;
1495
- }
1496
- }
1497
- if (!isContained) {
1498
- const top2 = positions.get(id) || 0;
1499
- let furthestIndex = -1;
1500
- let furthestDistance = 0;
1501
- for (let u = 0; u < numContainers; u++) {
1502
- if (allocatedContainers.has(u)) {
1503
- continue;
1545
+ const needNewContainers = [];
1546
+ if (isReset || startBuffered < prevStartBuffered || endBuffered > prevEndBuffered) {
1547
+ const isContained = (i) => {
1548
+ const id = getId(i);
1549
+ for (let j = 0; j < numContainers; j++) {
1550
+ const key = peek$(ctx, `containerItemKey${j}`);
1551
+ if (key === id) {
1552
+ return true;
1504
1553
  }
1505
- const key = peek$(ctx, `containerItemKey${u}`);
1506
- if (key === void 0) {
1507
- furthestIndex = u;
1508
- break;
1509
- }
1510
- const index2 = state.indexByKey.get(key);
1511
- const pos = peek$(ctx, `containerPosition${u}`).top;
1512
- if (isNullOrUndefined(index2) || pos === POSITION_OUT_OF_VIEW) {
1513
- furthestIndex = u;
1514
- break;
1554
+ }
1555
+ };
1556
+ if (isReset) {
1557
+ for (let i = startBuffered; i <= endBuffered; i++) {
1558
+ if (!isContained(i)) {
1559
+ needNewContainers.push(i);
1515
1560
  }
1516
- if (index2 < startBuffered || index2 > endBuffered) {
1517
- const distance = Math.abs(pos - top2);
1518
- if (index2 < 0 || pos === POSITION_OUT_OF_VIEW || distance > furthestDistance) {
1519
- furthestDistance = distance;
1520
- furthestIndex = u;
1521
- }
1561
+ }
1562
+ } else {
1563
+ for (let i = startBuffered; i < prevStartBuffered; i++) {
1564
+ if (!isContained(i)) {
1565
+ needNewContainers.push(i);
1522
1566
  }
1523
1567
  }
1524
- const containerId = furthestIndex >= 0 ? furthestIndex : numContainers;
1525
- set$(ctx, `containerItemKey${containerId}`, id);
1526
- const index = state.indexByKey.get(id);
1527
- set$(ctx, `containerItemData${containerId}`, data[index]);
1528
- allocatedContainers.add(containerId);
1529
- if (furthestIndex === -1) {
1530
- numContainers++;
1531
- set$(ctx, `containerItemKey${containerId}`, id);
1532
- const index2 = state.indexByKey.get(id);
1533
- set$(ctx, `containerItemData${containerId}`, data[index2]);
1534
- set$(ctx, `containerPosition${containerId}`, ANCHORED_POSITION_OUT_OF_VIEW);
1535
- set$(ctx, `containerColumn${containerId}`, -1);
1536
- if (__DEV__ && !didWarnMoreContainers && numContainers > peek$(ctx, "numContainersPooled")) {
1537
- didWarnMoreContainers = true;
1538
- console.warn(
1539
- "[legend-list] No container to recycle, 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."
1540
- );
1568
+ for (let i = Math.max(prevEndBuffered + 1, startBuffered); i <= endBuffered; i++) {
1569
+ if (!isContained(i)) {
1570
+ needNewContainers.push(i);
1541
1571
  }
1542
1572
  }
1543
1573
  }
1544
1574
  }
1545
- if (numContainers !== prevNumContainers) {
1546
- set$(ctx, "numContainers", numContainers);
1547
- if (numContainers > peek$(ctx, "numContainersPooled")) {
1548
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
1575
+ if (needNewContainers.length > 0) {
1576
+ const availableContainers = findAvailableContainers(
1577
+ needNewContainers.length,
1578
+ startBuffered,
1579
+ endBuffered
1580
+ );
1581
+ for (let idx = 0; idx < needNewContainers.length; idx++) {
1582
+ const i = needNewContainers[idx];
1583
+ const containerIndex = availableContainers[idx];
1584
+ const id = getId(i);
1585
+ set$(ctx, `containerItemKey${containerIndex}`, id);
1586
+ set$(ctx, `containerItemData${containerIndex}`, data[i]);
1587
+ if (containerIndex >= numContainers) {
1588
+ numContainers = containerIndex + 1;
1589
+ }
1590
+ }
1591
+ if (numContainers !== prevNumContainers) {
1592
+ set$(ctx, "numContainers", numContainers);
1593
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
1594
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
1595
+ }
1549
1596
  }
1550
1597
  }
1551
1598
  for (let i = 0; i < numContainers; i++) {
@@ -1575,7 +1622,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1575
1622
  const prevPos = peek$(ctx, `containerPosition${i}`);
1576
1623
  const prevColumn = peek$(ctx, `containerColumn${i}`);
1577
1624
  const prevData = peek$(ctx, `containerItemData${i}`);
1578
- if (pos.relativeCoordinate > POSITION_OUT_OF_VIEW && pos.top !== prevPos.top) {
1625
+ if (!prevPos || pos.relativeCoordinate > POSITION_OUT_OF_VIEW && pos.top !== prevPos.top) {
1579
1626
  set$(ctx, `containerPosition${i}`, pos);
1580
1627
  }
1581
1628
  if (column2 >= 0 && column2 !== prevColumn) {
@@ -1770,7 +1817,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1770
1817
  if (!keyExtractorProp) {
1771
1818
  state.positions.clear();
1772
1819
  }
1773
- calculateItemsInView();
1820
+ calculateItemsInView(
1821
+ /*isReset*/
1822
+ true
1823
+ );
1774
1824
  const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1775
1825
  if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1776
1826
  state.isEndReached = false;
@@ -1823,7 +1873,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1823
1873
  (_b = state.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1824
1874
  scrollTo(0, false);
1825
1875
  setTimeout(() => {
1826
- calculateItemsInView();
1876
+ calculateItemsInView(
1877
+ /*reset*/
1878
+ true
1879
+ );
1827
1880
  }, 0);
1828
1881
  } else {
1829
1882
  state.startBufferedId = void 0;
@@ -1838,7 +1891,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1838
1891
  }
1839
1892
  scrollTo(0, false);
1840
1893
  setTimeout(() => {
1841
- calculateItemsInView();
1894
+ calculateItemsInView(
1895
+ /*reset*/
1896
+ true
1897
+ );
1842
1898
  }, 0);
1843
1899
  }
1844
1900
  }
@@ -1867,6 +1923,56 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1867
1923
  });
1868
1924
  addTotalSize(null, totalSize, totalSizeBelowIndex);
1869
1925
  };
1926
+ const findAvailableContainers = (numNeeded, startBuffered, endBuffered) => {
1927
+ const state = refState.current;
1928
+ const numContainers = peek$(ctx, "numContainers");
1929
+ if (numNeeded === 0) return [];
1930
+ const result = [];
1931
+ const availableContainers = [];
1932
+ for (let u = 0; u < numContainers; u++) {
1933
+ const key = peek$(ctx, `containerItemKey${u}`);
1934
+ if (key === void 0) {
1935
+ result.push(u);
1936
+ if (result.length >= numNeeded) {
1937
+ return result;
1938
+ }
1939
+ }
1940
+ }
1941
+ for (let u = 0; u < numContainers; u++) {
1942
+ const key = peek$(ctx, `containerItemKey${u}`);
1943
+ if (key === void 0) continue;
1944
+ const index = state.indexByKey.get(key);
1945
+ if (index < startBuffered) {
1946
+ availableContainers.push({ index: u, distance: startBuffered - index });
1947
+ } else if (index > endBuffered) {
1948
+ availableContainers.push({ index: u, distance: index - endBuffered });
1949
+ }
1950
+ }
1951
+ const remaining = numNeeded - result.length;
1952
+ if (remaining > 0) {
1953
+ if (availableContainers.length > 0) {
1954
+ if (availableContainers.length > remaining) {
1955
+ availableContainers.sort(comparatorByDistance);
1956
+ availableContainers.length = remaining;
1957
+ }
1958
+ for (const container of availableContainers) {
1959
+ result.push(container.index);
1960
+ }
1961
+ }
1962
+ const stillNeeded = numNeeded - result.length;
1963
+ if (stillNeeded > 0) {
1964
+ for (let i = 0; i < stillNeeded; i++) {
1965
+ result.push(numContainers + i);
1966
+ }
1967
+ if (__DEV__ && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
1968
+ console.warn(
1969
+ "[legend-list] No container to recycle, 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."
1970
+ );
1971
+ }
1972
+ }
1973
+ }
1974
+ return result.sort(comparatorDefault);
1975
+ };
1870
1976
  const isFirst = !refState.current.renderItem;
1871
1977
  const memoizedLastItemKeys = useMemo(() => {
1872
1978
  if (!dataProp.length) return [];
@@ -1948,10 +2054,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1948
2054
  set$(ctx, "numContainersPooled", numContainers * initialContainerPoolRatio);
1949
2055
  if (initialScrollIndex) {
1950
2056
  requestAnimationFrame(() => {
1951
- calculateItemsInView();
2057
+ calculateItemsInView(
2058
+ /*isReset*/
2059
+ true
2060
+ );
1952
2061
  });
1953
2062
  } else {
1954
- calculateItemsInView();
2063
+ calculateItemsInView(
2064
+ /*isReset*/
2065
+ true
2066
+ );
1955
2067
  }
1956
2068
  return true;
1957
2069
  }
@@ -1987,6 +2099,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1987
2099
  }
1988
2100
  const index = indexByKey.get(itemKey);
1989
2101
  const numColumns = peek$(ctx, "numColumns");
2102
+ state.scrollForNextCalculateItemsInView = void 0;
1990
2103
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1991
2104
  const prevSize = getItemSize(itemKey, index, data);
1992
2105
  const prevSizeKnown = sizesKnown.get(itemKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Legend List is a drop-in replacement for FlatList with much better performance and supporting dynamically sized items.",
5
5
  "sideEffects": false,
6
6
  "private": false,
@@ -28,5 +28,8 @@
28
28
  "homepage": "https://github.com/LegendApp/legend-list#readme",
29
29
  "publishConfig": {
30
30
  "registry": "https://registry.npmjs.org/"
31
+ },
32
+ "dependencies": {
33
+ "use-sync-external-store": "^1.5.0"
31
34
  }
32
35
  }