@legendapp/list 1.0.2 → 1.0.4

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/README.md CHANGED
@@ -92,6 +92,9 @@ const LegendListExample = () => {
92
92
  keyExtractor={(item) => item.id}
93
93
  recycleItems={true}
94
94
 
95
+ // Recommended if data can change
96
+ maintainVisibleContentPosition
97
+
95
98
  ref={listRef}
96
99
  />
97
100
  )
package/animated.d.mts CHANGED
@@ -3,7 +3,7 @@ import * as _legendapp_list from '@legendapp/list';
3
3
  import * as react_native from 'react-native';
4
4
  import { Animated } from 'react-native';
5
5
 
6
- declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "maintainVisibleContentPosition" | "removeClippedSubviews" | "stickyHeaderIndices" | "contentInset" | "contentOffset"> & {
6
+ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
7
7
  alignItemsAtEnd?: boolean;
8
8
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
9
9
  data: readonly T[];
package/animated.d.ts CHANGED
@@ -3,7 +3,7 @@ import * as _legendapp_list from '@legendapp/list';
3
3
  import * as react_native from 'react-native';
4
4
  import { Animated } from 'react-native';
5
5
 
6
- declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "maintainVisibleContentPosition" | "removeClippedSubviews" | "stickyHeaderIndices" | "contentInset" | "contentOffset"> & {
6
+ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
7
7
  alignItemsAtEnd?: boolean;
8
8
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
9
9
  data: readonly T[];
package/index.d.mts CHANGED
@@ -57,7 +57,7 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
57
57
  getEstimatedItemSize?: (index: number, item: ItemT) => number;
58
58
  /**
59
59
  * Ratio of initial container pool size to data length (e.g., 0.5 for half).
60
- * @default 1
60
+ * @default 2
61
61
  */
62
62
  initialContainerPoolRatio?: number | undefined;
63
63
  /**
@@ -276,7 +276,7 @@ interface InternalState {
276
276
  queuedCalculateItemsInView: number | undefined;
277
277
  lastBatchingAction: number;
278
278
  ignoreScrollFromCalcTotal?: boolean;
279
- disableAveragesForScrolls?: number;
279
+ disableScrollJumpsFrom?: number;
280
280
  scrollingToOffset?: number | undefined;
281
281
  averageSizes: Record<string, {
282
282
  num: number;
@@ -462,7 +462,7 @@ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqua
462
462
  };
463
463
  declare const typedMemo: TypedMemo;
464
464
 
465
- declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "maintainVisibleContentPosition" | "removeClippedSubviews" | "stickyHeaderIndices" | "contentInset" | "contentOffset"> & {
465
+ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
466
466
  alignItemsAtEnd?: boolean;
467
467
  columnWrapperStyle?: ColumnWrapperStyle;
468
468
  data: readonly T[];
package/index.d.ts CHANGED
@@ -57,7 +57,7 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
57
57
  getEstimatedItemSize?: (index: number, item: ItemT) => number;
58
58
  /**
59
59
  * Ratio of initial container pool size to data length (e.g., 0.5 for half).
60
- * @default 1
60
+ * @default 2
61
61
  */
62
62
  initialContainerPoolRatio?: number | undefined;
63
63
  /**
@@ -276,7 +276,7 @@ interface InternalState {
276
276
  queuedCalculateItemsInView: number | undefined;
277
277
  lastBatchingAction: number;
278
278
  ignoreScrollFromCalcTotal?: boolean;
279
- disableAveragesForScrolls?: number;
279
+ disableScrollJumpsFrom?: number;
280
280
  scrollingToOffset?: number | undefined;
281
281
  averageSizes: Record<string, {
282
282
  num: number;
@@ -462,7 +462,7 @@ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqua
462
462
  };
463
463
  declare const typedMemo: TypedMemo;
464
464
 
465
- declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "maintainVisibleContentPosition" | "removeClippedSubviews" | "stickyHeaderIndices" | "contentInset" | "contentOffset"> & {
465
+ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
466
466
  alignItemsAtEnd?: boolean;
467
467
  columnWrapperStyle?: ColumnWrapperStyle;
468
468
  data: readonly T[];
package/index.js CHANGED
@@ -159,6 +159,9 @@ function warnDevOnce(id, text) {
159
159
  function roundSize(size) {
160
160
  return Math.floor(size * 8) / 8;
161
161
  }
162
+ function isNullOrUndefined(value) {
163
+ return value === null || value === void 0;
164
+ }
162
165
  var symbolFirst = Symbol();
163
166
  function useInit(cb) {
164
167
  const refValue = React2.useRef(symbolFirst);
@@ -335,20 +338,28 @@ var Container = ({
335
338
  forceLayoutRender((v) => v + 1);
336
339
  }, []);
337
340
  const onLayout = (event) => {
338
- if (itemKey !== void 0) {
341
+ var _a, _b;
342
+ if (!isNullOrUndefined(itemKey)) {
339
343
  const layout = event.nativeEvent.layout;
340
- const size = roundSize(layout[horizontal ? "width" : "height"]);
341
- if (!IsNewArchitecture && size === 0 && layout.x === 0 && layout.y === 0) {
342
- return;
344
+ let size = roundSize(layout[horizontal ? "width" : "height"]);
345
+ const doUpdate = () => {
346
+ refLastSize.current = size;
347
+ updateItemSize(itemKey, size);
348
+ };
349
+ if (IsNewArchitecture || size > 0) {
350
+ doUpdate();
351
+ } else {
352
+ (_b = (_a = ref.current) == null ? void 0 : _a.measure) == null ? void 0 : _b.call(_a, (x, y, width, height) => {
353
+ size = roundSize(horizontal ? width : height);
354
+ doUpdate();
355
+ });
343
356
  }
344
- refLastSize.current = size;
345
- updateItemSize(itemKey, size);
346
357
  }
347
358
  };
348
359
  if (IsNewArchitecture) {
349
360
  React2.useLayoutEffect(() => {
350
361
  var _a, _b;
351
- if (itemKey !== void 0) {
362
+ if (!isNullOrUndefined(itemKey)) {
352
363
  const measured = (_b = (_a = ref.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
353
364
  if (measured) {
354
365
  const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
@@ -360,7 +371,7 @@ var Container = ({
360
371
  }, [itemKey, layoutRenderCount]);
361
372
  } else {
362
373
  React2.useEffect(() => {
363
- if (itemKey) {
374
+ if (!isNullOrUndefined(itemKey)) {
364
375
  const timeout = setTimeout(() => {
365
376
  if (refLastSize.current) {
366
377
  updateItemSize(itemKey, refLastSize.current);
@@ -1227,6 +1238,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1227
1238
  }
1228
1239
  return map;
1229
1240
  };
1241
+ const disableScrollJumps = (timeout) => {
1242
+ const state = refState.current;
1243
+ state.disableScrollJumpsFrom = state.scroll - state.scrollAdjustHandler.getAppliedAdjust();
1244
+ setTimeout(() => {
1245
+ state.disableScrollJumpsFrom = void 0;
1246
+ }, timeout);
1247
+ };
1230
1248
  const getElementPositionBelowAchor = (id) => {
1231
1249
  var _a;
1232
1250
  const state = refState.current;
@@ -1305,6 +1323,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1305
1323
  }
1306
1324
  }
1307
1325
  }, []);
1326
+ const checkAllSizesKnown = React2.useCallback(() => {
1327
+ const { startBuffered, endBuffered, sizesKnown } = refState.current;
1328
+ if (endBuffered !== null) {
1329
+ let areAllKnown = true;
1330
+ for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1331
+ const key = getId(i);
1332
+ areAllKnown && (areAllKnown = sizesKnown.has(key));
1333
+ }
1334
+ return areAllKnown;
1335
+ }
1336
+ return false;
1337
+ }, []);
1308
1338
  const calculateItemsInView = React2.useCallback(() => {
1309
1339
  var _a;
1310
1340
  const state = refState.current;
@@ -1315,8 +1345,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1315
1345
  positions,
1316
1346
  columns,
1317
1347
  scrollAdjustHandler,
1318
- scrollVelocity: speed,
1319
- disableAveragesForScrolls
1348
+ scrollVelocity: speed
1320
1349
  } = state;
1321
1350
  if (!data || scrollLength === 0) {
1322
1351
  return;
@@ -1327,7 +1356,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1327
1356
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
1328
1357
  const scrollExtra = Math.max(-16, Math.min(16, speed)) * 16;
1329
1358
  let scrollState = state.scroll;
1330
- const useAverageSize = !disableAveragesForScrolls;
1359
+ const useAverageSize = !state.disableScrollJumpsFrom;
1331
1360
  if (!state.queuedInitialLayout && initialScrollIndex) {
1332
1361
  const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
1333
1362
  scrollState = updatedOffset;
@@ -1357,6 +1386,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1357
1386
  }
1358
1387
  }
1359
1388
  const scrollBottom = scroll + scrollLength;
1389
+ const prevEndBuffered = state.endBuffered;
1360
1390
  let startNoBuffer = null;
1361
1391
  let startBuffered = null;
1362
1392
  let startBufferedId = null;
@@ -1465,6 +1495,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1465
1495
  const prevNumContainers = ctx.values.get("numContainers");
1466
1496
  let numContainers = prevNumContainers;
1467
1497
  let didWarnMoreContainers = false;
1498
+ const allocatedContainers = /* @__PURE__ */ new Set();
1468
1499
  for (let i = startBuffered; i <= endBuffered; i++) {
1469
1500
  let isContained = false;
1470
1501
  const id = getId(i);
@@ -1480,38 +1511,44 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1480
1511
  let furthestIndex = -1;
1481
1512
  let furthestDistance = 0;
1482
1513
  for (let u = 0; u < numContainers; u++) {
1514
+ if (allocatedContainers.has(u)) {
1515
+ continue;
1516
+ }
1483
1517
  const key = peek$(ctx, `containerItemKey${u}`);
1484
1518
  if (key === void 0) {
1485
1519
  furthestIndex = u;
1486
1520
  break;
1487
1521
  }
1488
- const index = state.indexByKey.get(key);
1522
+ const index2 = state.indexByKey.get(key);
1489
1523
  const pos = peek$(ctx, `containerPosition${u}`).top;
1490
- if (index < startBuffered || index > endBuffered) {
1524
+ if (isNullOrUndefined(index2) || pos === POSITION_OUT_OF_VIEW) {
1525
+ furthestIndex = u;
1526
+ break;
1527
+ }
1528
+ if (index2 < startBuffered || index2 > endBuffered) {
1491
1529
  const distance = Math.abs(pos - top2);
1492
- if (index < 0 || distance > furthestDistance) {
1530
+ if (index2 < 0 || pos === POSITION_OUT_OF_VIEW || distance > furthestDistance) {
1493
1531
  furthestDistance = distance;
1494
1532
  furthestIndex = u;
1495
1533
  }
1496
1534
  }
1497
1535
  }
1498
- if (furthestIndex >= 0) {
1499
- set$(ctx, `containerItemKey${furthestIndex}`, id);
1500
- const index = state.indexByKey.get(id);
1501
- set$(ctx, `containerItemData${furthestIndex}`, data[index]);
1502
- } else {
1503
- const containerId = numContainers;
1536
+ const containerId = furthestIndex >= 0 ? furthestIndex : numContainers;
1537
+ set$(ctx, `containerItemKey${containerId}`, id);
1538
+ const index = state.indexByKey.get(id);
1539
+ set$(ctx, `containerItemData${containerId}`, data[index]);
1540
+ allocatedContainers.add(containerId);
1541
+ if (furthestIndex === -1) {
1504
1542
  numContainers++;
1505
1543
  set$(ctx, `containerItemKey${containerId}`, id);
1506
- const index = state.indexByKey.get(id);
1507
- set$(ctx, `containerItemData${containerId}`, data[index]);
1544
+ const index2 = state.indexByKey.get(id);
1545
+ set$(ctx, `containerItemData${containerId}`, data[index2]);
1508
1546
  set$(ctx, `containerPosition${containerId}`, ANCHORED_POSITION_OUT_OF_VIEW);
1509
1547
  set$(ctx, `containerColumn${containerId}`, -1);
1510
1548
  if (__DEV__ && !didWarnMoreContainers && numContainers > peek$(ctx, "numContainersPooled")) {
1511
1549
  didWarnMoreContainers = true;
1512
1550
  console.warn(
1513
- "[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. numContainers:",
1514
- numContainers
1551
+ "[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."
1515
1552
  );
1516
1553
  }
1517
1554
  }
@@ -1533,7 +1570,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1533
1570
  const prevPos = peek$(ctx, `containerPosition${i}`);
1534
1571
  const pos = positions.get(id) || 0;
1535
1572
  const size = getItemSize(id, itemIndex, data[i]);
1536
- if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
1573
+ if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom || endBuffered < prevEndBuffered) {
1537
1574
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1538
1575
  }
1539
1576
  } else {
@@ -1568,12 +1605,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1568
1605
  }
1569
1606
  }
1570
1607
  if (!state.queuedInitialLayout && endBuffered !== null) {
1571
- let areAllKnown = true;
1572
- for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1573
- const key = getId(i);
1574
- areAllKnown && (areAllKnown = state.sizesKnown.has(key));
1575
- }
1576
- if (areAllKnown) {
1608
+ if (checkAllSizesKnown()) {
1577
1609
  setDidLayout();
1578
1610
  }
1579
1611
  }
@@ -1725,7 +1757,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1725
1757
  if (state) {
1726
1758
  state.data = dataProp;
1727
1759
  if (!isFirst2) {
1728
- state.disableAveragesForScrolls = 2;
1760
+ disableScrollJumps(2e3);
1729
1761
  refState.current.scrollForNextCalculateItemsInView = void 0;
1730
1762
  const numContainers = peek$(ctx, "numContainers");
1731
1763
  for (let i = 0; i < numContainers; i++) {
@@ -1942,7 +1974,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1942
1974
  });
1943
1975
  const updateItemSize = React2.useCallback((itemKey, size, fromFixGaps) => {
1944
1976
  const state = refState.current;
1945
- const { sizes, indexByKey, sizesKnown, data, rowHeights, startBuffered, endBuffered, averageSizes } = state;
1977
+ const {
1978
+ sizes,
1979
+ indexByKey,
1980
+ sizesKnown,
1981
+ data,
1982
+ rowHeights,
1983
+ startBuffered,
1984
+ endBuffered,
1985
+ averageSizes,
1986
+ queuedInitialLayout
1987
+ } = state;
1946
1988
  if (!data) {
1947
1989
  return;
1948
1990
  }
@@ -1951,7 +1993,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1951
1993
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1952
1994
  const prevSize = getItemSize(itemKey, index, data);
1953
1995
  let needsCalculate = false;
1954
- const needsUpdateContainersDidLayout = !peek$(ctx, "containersDidLayout");
1996
+ let needsUpdateContainersDidLayout = false;
1955
1997
  sizesKnown.set(itemKey, size);
1956
1998
  const itemType = "";
1957
1999
  let averages = averageSizes[itemType];
@@ -1990,7 +2032,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1990
2032
  );
1991
2033
  }, 1e3);
1992
2034
  }
1993
- refState.current.scrollForNextCalculateItemsInView = void 0;
2035
+ state.scrollForNextCalculateItemsInView = void 0;
1994
2036
  addTotalSize(itemKey, diff, 0);
1995
2037
  doMaintainScrollAtEnd(false);
1996
2038
  if (onItemSizeChanged) {
@@ -2003,11 +2045,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2003
2045
  });
2004
2046
  }
2005
2047
  }
2048
+ if (!queuedInitialLayout && checkAllSizesKnown()) {
2049
+ needsUpdateContainersDidLayout = true;
2050
+ }
2006
2051
  const isInView = index >= startBuffered && index <= endBuffered;
2007
- if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && isInView) {
2052
+ if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && (isInView || !queuedInitialLayout)) {
2008
2053
  const scrollVelocity = state.scrollVelocity;
2009
2054
  let didCalculate = false;
2010
- if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || needsUpdateContainersDidLayout)) {
2055
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || needsUpdateContainersDidLayout || queuedInitialLayout)) {
2011
2056
  if (Date.now() - state.lastBatchingAction < 500) {
2012
2057
  if (!state.queuedCalculateItemsInView) {
2013
2058
  state.queuedCalculateItemsInView = requestAnimationFrame(() => {
@@ -2065,6 +2110,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2065
2110
  if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
2066
2111
  return;
2067
2112
  }
2113
+ if (state.disableScrollJumpsFrom !== void 0) {
2114
+ const scrollMinusAdjust = newScroll - state.scrollAdjustHandler.getAppliedAdjust();
2115
+ if (Math.abs(scrollMinusAdjust - state.disableScrollJumpsFrom) > 200) {
2116
+ return;
2117
+ }
2118
+ state.disableScrollJumpsFrom = void 0;
2119
+ }
2068
2120
  state.hasScrolled = true;
2069
2121
  state.lastBatchingAction = Date.now();
2070
2122
  const currentTime = performance.now();
@@ -2099,9 +2151,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2099
2151
  if (!fromSelf) {
2100
2152
  (_d = state.onScroll) == null ? void 0 : _d.call(state, event);
2101
2153
  }
2102
- if (state.disableAveragesForScrolls) {
2103
- state.disableAveragesForScrolls--;
2104
- }
2105
2154
  },
2106
2155
  []
2107
2156
  );
package/index.mjs CHANGED
@@ -138,6 +138,9 @@ function warnDevOnce(id, text) {
138
138
  function roundSize(size) {
139
139
  return Math.floor(size * 8) / 8;
140
140
  }
141
+ function isNullOrUndefined(value) {
142
+ return value === null || value === void 0;
143
+ }
141
144
  var symbolFirst = Symbol();
142
145
  function useInit(cb) {
143
146
  const refValue = useRef(symbolFirst);
@@ -314,20 +317,28 @@ var Container = ({
314
317
  forceLayoutRender((v) => v + 1);
315
318
  }, []);
316
319
  const onLayout = (event) => {
317
- if (itemKey !== void 0) {
320
+ var _a, _b;
321
+ if (!isNullOrUndefined(itemKey)) {
318
322
  const layout = event.nativeEvent.layout;
319
- const size = roundSize(layout[horizontal ? "width" : "height"]);
320
- if (!IsNewArchitecture && size === 0 && layout.x === 0 && layout.y === 0) {
321
- return;
323
+ let size = roundSize(layout[horizontal ? "width" : "height"]);
324
+ const doUpdate = () => {
325
+ refLastSize.current = size;
326
+ updateItemSize(itemKey, size);
327
+ };
328
+ if (IsNewArchitecture || size > 0) {
329
+ doUpdate();
330
+ } else {
331
+ (_b = (_a = ref.current) == null ? void 0 : _a.measure) == null ? void 0 : _b.call(_a, (x, y, width, height) => {
332
+ size = roundSize(horizontal ? width : height);
333
+ doUpdate();
334
+ });
322
335
  }
323
- refLastSize.current = size;
324
- updateItemSize(itemKey, size);
325
336
  }
326
337
  };
327
338
  if (IsNewArchitecture) {
328
339
  useLayoutEffect(() => {
329
340
  var _a, _b;
330
- if (itemKey !== void 0) {
341
+ if (!isNullOrUndefined(itemKey)) {
331
342
  const measured = (_b = (_a = ref.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
332
343
  if (measured) {
333
344
  const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
@@ -339,7 +350,7 @@ var Container = ({
339
350
  }, [itemKey, layoutRenderCount]);
340
351
  } else {
341
352
  useEffect(() => {
342
- if (itemKey) {
353
+ if (!isNullOrUndefined(itemKey)) {
343
354
  const timeout = setTimeout(() => {
344
355
  if (refLastSize.current) {
345
356
  updateItemSize(itemKey, refLastSize.current);
@@ -1206,6 +1217,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1206
1217
  }
1207
1218
  return map;
1208
1219
  };
1220
+ const disableScrollJumps = (timeout) => {
1221
+ const state = refState.current;
1222
+ state.disableScrollJumpsFrom = state.scroll - state.scrollAdjustHandler.getAppliedAdjust();
1223
+ setTimeout(() => {
1224
+ state.disableScrollJumpsFrom = void 0;
1225
+ }, timeout);
1226
+ };
1209
1227
  const getElementPositionBelowAchor = (id) => {
1210
1228
  var _a;
1211
1229
  const state = refState.current;
@@ -1284,6 +1302,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1284
1302
  }
1285
1303
  }
1286
1304
  }, []);
1305
+ const checkAllSizesKnown = useCallback(() => {
1306
+ const { startBuffered, endBuffered, sizesKnown } = refState.current;
1307
+ if (endBuffered !== null) {
1308
+ let areAllKnown = true;
1309
+ for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1310
+ const key = getId(i);
1311
+ areAllKnown && (areAllKnown = sizesKnown.has(key));
1312
+ }
1313
+ return areAllKnown;
1314
+ }
1315
+ return false;
1316
+ }, []);
1287
1317
  const calculateItemsInView = useCallback(() => {
1288
1318
  var _a;
1289
1319
  const state = refState.current;
@@ -1294,8 +1324,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1294
1324
  positions,
1295
1325
  columns,
1296
1326
  scrollAdjustHandler,
1297
- scrollVelocity: speed,
1298
- disableAveragesForScrolls
1327
+ scrollVelocity: speed
1299
1328
  } = state;
1300
1329
  if (!data || scrollLength === 0) {
1301
1330
  return;
@@ -1306,7 +1335,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1306
1335
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
1307
1336
  const scrollExtra = Math.max(-16, Math.min(16, speed)) * 16;
1308
1337
  let scrollState = state.scroll;
1309
- const useAverageSize = !disableAveragesForScrolls;
1338
+ const useAverageSize = !state.disableScrollJumpsFrom;
1310
1339
  if (!state.queuedInitialLayout && initialScrollIndex) {
1311
1340
  const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
1312
1341
  scrollState = updatedOffset;
@@ -1336,6 +1365,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1336
1365
  }
1337
1366
  }
1338
1367
  const scrollBottom = scroll + scrollLength;
1368
+ const prevEndBuffered = state.endBuffered;
1339
1369
  let startNoBuffer = null;
1340
1370
  let startBuffered = null;
1341
1371
  let startBufferedId = null;
@@ -1444,6 +1474,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1444
1474
  const prevNumContainers = ctx.values.get("numContainers");
1445
1475
  let numContainers = prevNumContainers;
1446
1476
  let didWarnMoreContainers = false;
1477
+ const allocatedContainers = /* @__PURE__ */ new Set();
1447
1478
  for (let i = startBuffered; i <= endBuffered; i++) {
1448
1479
  let isContained = false;
1449
1480
  const id = getId(i);
@@ -1459,38 +1490,44 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1459
1490
  let furthestIndex = -1;
1460
1491
  let furthestDistance = 0;
1461
1492
  for (let u = 0; u < numContainers; u++) {
1493
+ if (allocatedContainers.has(u)) {
1494
+ continue;
1495
+ }
1462
1496
  const key = peek$(ctx, `containerItemKey${u}`);
1463
1497
  if (key === void 0) {
1464
1498
  furthestIndex = u;
1465
1499
  break;
1466
1500
  }
1467
- const index = state.indexByKey.get(key);
1501
+ const index2 = state.indexByKey.get(key);
1468
1502
  const pos = peek$(ctx, `containerPosition${u}`).top;
1469
- if (index < startBuffered || index > endBuffered) {
1503
+ if (isNullOrUndefined(index2) || pos === POSITION_OUT_OF_VIEW) {
1504
+ furthestIndex = u;
1505
+ break;
1506
+ }
1507
+ if (index2 < startBuffered || index2 > endBuffered) {
1470
1508
  const distance = Math.abs(pos - top2);
1471
- if (index < 0 || distance > furthestDistance) {
1509
+ if (index2 < 0 || pos === POSITION_OUT_OF_VIEW || distance > furthestDistance) {
1472
1510
  furthestDistance = distance;
1473
1511
  furthestIndex = u;
1474
1512
  }
1475
1513
  }
1476
1514
  }
1477
- if (furthestIndex >= 0) {
1478
- set$(ctx, `containerItemKey${furthestIndex}`, id);
1479
- const index = state.indexByKey.get(id);
1480
- set$(ctx, `containerItemData${furthestIndex}`, data[index]);
1481
- } else {
1482
- const containerId = numContainers;
1515
+ const containerId = furthestIndex >= 0 ? furthestIndex : numContainers;
1516
+ set$(ctx, `containerItemKey${containerId}`, id);
1517
+ const index = state.indexByKey.get(id);
1518
+ set$(ctx, `containerItemData${containerId}`, data[index]);
1519
+ allocatedContainers.add(containerId);
1520
+ if (furthestIndex === -1) {
1483
1521
  numContainers++;
1484
1522
  set$(ctx, `containerItemKey${containerId}`, id);
1485
- const index = state.indexByKey.get(id);
1486
- set$(ctx, `containerItemData${containerId}`, data[index]);
1523
+ const index2 = state.indexByKey.get(id);
1524
+ set$(ctx, `containerItemData${containerId}`, data[index2]);
1487
1525
  set$(ctx, `containerPosition${containerId}`, ANCHORED_POSITION_OUT_OF_VIEW);
1488
1526
  set$(ctx, `containerColumn${containerId}`, -1);
1489
1527
  if (__DEV__ && !didWarnMoreContainers && numContainers > peek$(ctx, "numContainersPooled")) {
1490
1528
  didWarnMoreContainers = true;
1491
1529
  console.warn(
1492
- "[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. numContainers:",
1493
- numContainers
1530
+ "[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."
1494
1531
  );
1495
1532
  }
1496
1533
  }
@@ -1512,7 +1549,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1512
1549
  const prevPos = peek$(ctx, `containerPosition${i}`);
1513
1550
  const pos = positions.get(id) || 0;
1514
1551
  const size = getItemSize(id, itemIndex, data[i]);
1515
- if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
1552
+ if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom || endBuffered < prevEndBuffered) {
1516
1553
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1517
1554
  }
1518
1555
  } else {
@@ -1547,12 +1584,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1547
1584
  }
1548
1585
  }
1549
1586
  if (!state.queuedInitialLayout && endBuffered !== null) {
1550
- let areAllKnown = true;
1551
- for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1552
- const key = getId(i);
1553
- areAllKnown && (areAllKnown = state.sizesKnown.has(key));
1554
- }
1555
- if (areAllKnown) {
1587
+ if (checkAllSizesKnown()) {
1556
1588
  setDidLayout();
1557
1589
  }
1558
1590
  }
@@ -1704,7 +1736,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1704
1736
  if (state) {
1705
1737
  state.data = dataProp;
1706
1738
  if (!isFirst2) {
1707
- state.disableAveragesForScrolls = 2;
1739
+ disableScrollJumps(2e3);
1708
1740
  refState.current.scrollForNextCalculateItemsInView = void 0;
1709
1741
  const numContainers = peek$(ctx, "numContainers");
1710
1742
  for (let i = 0; i < numContainers; i++) {
@@ -1921,7 +1953,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1921
1953
  });
1922
1954
  const updateItemSize = useCallback((itemKey, size, fromFixGaps) => {
1923
1955
  const state = refState.current;
1924
- const { sizes, indexByKey, sizesKnown, data, rowHeights, startBuffered, endBuffered, averageSizes } = state;
1956
+ const {
1957
+ sizes,
1958
+ indexByKey,
1959
+ sizesKnown,
1960
+ data,
1961
+ rowHeights,
1962
+ startBuffered,
1963
+ endBuffered,
1964
+ averageSizes,
1965
+ queuedInitialLayout
1966
+ } = state;
1925
1967
  if (!data) {
1926
1968
  return;
1927
1969
  }
@@ -1930,7 +1972,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1930
1972
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1931
1973
  const prevSize = getItemSize(itemKey, index, data);
1932
1974
  let needsCalculate = false;
1933
- const needsUpdateContainersDidLayout = !peek$(ctx, "containersDidLayout");
1975
+ let needsUpdateContainersDidLayout = false;
1934
1976
  sizesKnown.set(itemKey, size);
1935
1977
  const itemType = "";
1936
1978
  let averages = averageSizes[itemType];
@@ -1969,7 +2011,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1969
2011
  );
1970
2012
  }, 1e3);
1971
2013
  }
1972
- refState.current.scrollForNextCalculateItemsInView = void 0;
2014
+ state.scrollForNextCalculateItemsInView = void 0;
1973
2015
  addTotalSize(itemKey, diff, 0);
1974
2016
  doMaintainScrollAtEnd(false);
1975
2017
  if (onItemSizeChanged) {
@@ -1982,11 +2024,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1982
2024
  });
1983
2025
  }
1984
2026
  }
2027
+ if (!queuedInitialLayout && checkAllSizesKnown()) {
2028
+ needsUpdateContainersDidLayout = true;
2029
+ }
1985
2030
  const isInView = index >= startBuffered && index <= endBuffered;
1986
- if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && isInView) {
2031
+ if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && (isInView || !queuedInitialLayout)) {
1987
2032
  const scrollVelocity = state.scrollVelocity;
1988
2033
  let didCalculate = false;
1989
- if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || needsUpdateContainersDidLayout)) {
2034
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || needsUpdateContainersDidLayout || queuedInitialLayout)) {
1990
2035
  if (Date.now() - state.lastBatchingAction < 500) {
1991
2036
  if (!state.queuedCalculateItemsInView) {
1992
2037
  state.queuedCalculateItemsInView = requestAnimationFrame(() => {
@@ -2044,6 +2089,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2044
2089
  if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
2045
2090
  return;
2046
2091
  }
2092
+ if (state.disableScrollJumpsFrom !== void 0) {
2093
+ const scrollMinusAdjust = newScroll - state.scrollAdjustHandler.getAppliedAdjust();
2094
+ if (Math.abs(scrollMinusAdjust - state.disableScrollJumpsFrom) > 200) {
2095
+ return;
2096
+ }
2097
+ state.disableScrollJumpsFrom = void 0;
2098
+ }
2047
2099
  state.hasScrolled = true;
2048
2100
  state.lastBatchingAction = Date.now();
2049
2101
  const currentTime = performance.now();
@@ -2078,9 +2130,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2078
2130
  if (!fromSelf) {
2079
2131
  (_d = state.onScroll) == null ? void 0 : _d.call(state, event);
2080
2132
  }
2081
- if (state.disableAveragesForScrolls) {
2082
- state.disableAveragesForScrolls--;
2083
- }
2084
2133
  },
2085
2134
  []
2086
2135
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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,