@legendapp/list 2.1.0-beta.7 → 2.1.0-beta.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 2.0.14
2
+ - Feat: Add dataVersion prop to trigger re-render when mutating the data array in place
3
+
1
4
  ## 2.0.13
2
5
  - Feat: Allow returning undefined in getFixedItemSize to fall back to estimated size
3
6
  - Fix: scrollToIndex viewOffset was being subtracted twice, causing incorrect scroll positioning
package/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React$1 from 'react';
2
- import { ComponentProps, ReactNode, Dispatch, SetStateAction } from 'react';
2
+ import { ComponentProps, Key, ReactNode, Dispatch, SetStateAction } from 'react';
3
3
  import { View, ScrollView, Animated, LayoutRectangle, ScrollViewComponent, ScrollResponderMixin, StyleProp, ViewStyle, NativeSyntheticEvent, NativeScrollEvent, ScrollViewProps } from 'react-native';
4
4
  import Animated$1 from 'react-native-reanimated';
5
5
 
@@ -102,6 +102,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
102
102
  * Extra data to trigger re-rendering when changed.
103
103
  */
104
104
  extraData?: any;
105
+ /**
106
+ * Version token that forces the list to treat data as updated even when the array reference is stable.
107
+ * Increment or change this when mutating the data array in place.
108
+ */
109
+ dataVersion?: Key;
105
110
  /**
106
111
  * In case you have distinct item sizes, you can provide a function to get the size of an item.
107
112
  * Use instead of FlatList's getItemLayout or FlashList overrideItemLayout if you want to have accurate initialScrollOffset, you should provide this function
@@ -250,6 +255,7 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
250
255
  refreshing?: boolean;
251
256
  /**
252
257
  * Render custom ScrollView component.
258
+ * Note: When using `stickyHeaderIndices`, you must provide an Animated ScrollView component.
253
259
  * @default (props) => <ScrollView {...props} />
254
260
  */
255
261
  renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
@@ -312,6 +318,7 @@ interface ScrollTarget {
312
318
  viewPosition?: number;
313
319
  animated?: boolean;
314
320
  isInitialScroll?: boolean;
321
+ precomputedWithViewOffset?: boolean;
315
322
  }
316
323
  interface InternalState {
317
324
  positions: Map<string, number>;
@@ -339,6 +346,11 @@ interface InternalState {
339
346
  scrollPrev: number;
340
347
  scrollPrevTime: number;
341
348
  scrollAdjustHandler: ScrollAdjustHandler;
349
+ triggerCalculateItemsInView?: (params?: {
350
+ doMVCP?: boolean;
351
+ dataChanged?: boolean;
352
+ forceFullItemPositions?: boolean;
353
+ }) => void;
342
354
  maintainingScrollAtEnd?: boolean;
343
355
  totalSize: number;
344
356
  otherAxisSize?: number;
@@ -382,17 +394,17 @@ interface InternalState {
382
394
  }>;
383
395
  refScroller: React.RefObject<ScrollView>;
384
396
  loadStartTime: number;
385
- initialScroll: ScrollIndexWithOffset | undefined;
397
+ initialScroll: ScrollIndexWithOffsetAndContentOffset | undefined;
386
398
  lastLayout: LayoutRectangle | undefined;
387
399
  timeoutSetPaddingTop?: any;
388
400
  activeStickyIndex: number | undefined;
389
401
  stickyContainers: Map<number, number>;
390
402
  stickyContainerPool: Set<number>;
391
403
  scrollProcessingEnabled: boolean;
392
- isOptimizingItemPositions: boolean;
393
404
  props: {
394
405
  alignItemsAtEnd: boolean;
395
406
  data: readonly any[];
407
+ dataVersion: Key | undefined;
396
408
  estimatedItemSize: number | undefined;
397
409
  getEstimatedItemSize: LegendListProps["getEstimatedItemSize"];
398
410
  getFixedItemSize: LegendListProps["getFixedItemSize"];
@@ -443,6 +455,7 @@ type ScrollState = {
443
455
  activeStickyIndex: number | undefined;
444
456
  contentLength: number;
445
457
  data: readonly any[];
458
+ elementAtIndex: (index: number) => View | null | undefined;
446
459
  end: number;
447
460
  endBuffered: number;
448
461
  isAtEnd: boolean;
@@ -630,6 +643,9 @@ interface ScrollIndexWithOffset {
630
643
  interface ScrollIndexWithOffsetPosition extends ScrollIndexWithOffset {
631
644
  viewPosition: number;
632
645
  }
646
+ interface ScrollIndexWithOffsetAndContentOffset extends ScrollIndexWithOffset {
647
+ contentOffset?: number;
648
+ }
633
649
  type GetRenderedItemResult<ItemT> = {
634
650
  index: number;
635
651
  item: ItemT;
@@ -652,4 +668,4 @@ declare function useListScrollSize(): {
652
668
  };
653
669
  declare function useSyncLayout(): () => void;
654
670
 
655
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type ScrollTarget, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
671
+ export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type ScrollTarget, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React$1 from 'react';
2
- import { ComponentProps, ReactNode, Dispatch, SetStateAction } from 'react';
2
+ import { ComponentProps, Key, ReactNode, Dispatch, SetStateAction } from 'react';
3
3
  import { View, ScrollView, Animated, LayoutRectangle, ScrollViewComponent, ScrollResponderMixin, StyleProp, ViewStyle, NativeSyntheticEvent, NativeScrollEvent, ScrollViewProps } from 'react-native';
4
4
  import Animated$1 from 'react-native-reanimated';
5
5
 
@@ -102,6 +102,11 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
102
102
  * Extra data to trigger re-rendering when changed.
103
103
  */
104
104
  extraData?: any;
105
+ /**
106
+ * Version token that forces the list to treat data as updated even when the array reference is stable.
107
+ * Increment or change this when mutating the data array in place.
108
+ */
109
+ dataVersion?: Key;
105
110
  /**
106
111
  * In case you have distinct item sizes, you can provide a function to get the size of an item.
107
112
  * Use instead of FlatList's getItemLayout or FlashList overrideItemLayout if you want to have accurate initialScrollOffset, you should provide this function
@@ -250,6 +255,7 @@ interface LegendListSpecificProps<ItemT, TItemType extends string | undefined> {
250
255
  refreshing?: boolean;
251
256
  /**
252
257
  * Render custom ScrollView component.
258
+ * Note: When using `stickyHeaderIndices`, you must provide an Animated ScrollView component.
253
259
  * @default (props) => <ScrollView {...props} />
254
260
  */
255
261
  renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
@@ -312,6 +318,7 @@ interface ScrollTarget {
312
318
  viewPosition?: number;
313
319
  animated?: boolean;
314
320
  isInitialScroll?: boolean;
321
+ precomputedWithViewOffset?: boolean;
315
322
  }
316
323
  interface InternalState {
317
324
  positions: Map<string, number>;
@@ -339,6 +346,11 @@ interface InternalState {
339
346
  scrollPrev: number;
340
347
  scrollPrevTime: number;
341
348
  scrollAdjustHandler: ScrollAdjustHandler;
349
+ triggerCalculateItemsInView?: (params?: {
350
+ doMVCP?: boolean;
351
+ dataChanged?: boolean;
352
+ forceFullItemPositions?: boolean;
353
+ }) => void;
342
354
  maintainingScrollAtEnd?: boolean;
343
355
  totalSize: number;
344
356
  otherAxisSize?: number;
@@ -382,17 +394,17 @@ interface InternalState {
382
394
  }>;
383
395
  refScroller: React.RefObject<ScrollView>;
384
396
  loadStartTime: number;
385
- initialScroll: ScrollIndexWithOffset | undefined;
397
+ initialScroll: ScrollIndexWithOffsetAndContentOffset | undefined;
386
398
  lastLayout: LayoutRectangle | undefined;
387
399
  timeoutSetPaddingTop?: any;
388
400
  activeStickyIndex: number | undefined;
389
401
  stickyContainers: Map<number, number>;
390
402
  stickyContainerPool: Set<number>;
391
403
  scrollProcessingEnabled: boolean;
392
- isOptimizingItemPositions: boolean;
393
404
  props: {
394
405
  alignItemsAtEnd: boolean;
395
406
  data: readonly any[];
407
+ dataVersion: Key | undefined;
396
408
  estimatedItemSize: number | undefined;
397
409
  getEstimatedItemSize: LegendListProps["getEstimatedItemSize"];
398
410
  getFixedItemSize: LegendListProps["getFixedItemSize"];
@@ -443,6 +455,7 @@ type ScrollState = {
443
455
  activeStickyIndex: number | undefined;
444
456
  contentLength: number;
445
457
  data: readonly any[];
458
+ elementAtIndex: (index: number) => View | null | undefined;
446
459
  end: number;
447
460
  endBuffered: number;
448
461
  isAtEnd: boolean;
@@ -630,6 +643,9 @@ interface ScrollIndexWithOffset {
630
643
  interface ScrollIndexWithOffsetPosition extends ScrollIndexWithOffset {
631
644
  viewPosition: number;
632
645
  }
646
+ interface ScrollIndexWithOffsetAndContentOffset extends ScrollIndexWithOffset {
647
+ contentOffset?: number;
648
+ }
633
649
  type GetRenderedItemResult<ItemT> = {
634
650
  index: number;
635
651
  item: ItemT;
@@ -652,4 +668,4 @@ declare function useListScrollSize(): {
652
668
  };
653
669
  declare function useSyncLayout(): () => void;
654
670
 
655
- export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type ScrollTarget, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
671
+ export { type ColumnWrapperStyle, type GetRenderedItem, type GetRenderedItemResult, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type MaintainScrollAtEndOptions, type OnViewableItemsChanged, type ScrollIndexWithOffset, type ScrollIndexWithOffsetAndContentOffset, type ScrollIndexWithOffsetPosition, type ScrollState, type ScrollTarget, type ThresholdSnapshot, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
package/index.js CHANGED
@@ -313,6 +313,16 @@ function getPadding(s, type) {
313
313
  function extractPadding(style, contentContainerStyle, type) {
314
314
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
315
315
  }
316
+ function findContainerId(ctx, key) {
317
+ const numContainers = peek$(ctx, "numContainers");
318
+ for (let i = 0; i < numContainers; i++) {
319
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
320
+ if (itemKey === key) {
321
+ return i;
322
+ }
323
+ }
324
+ return -1;
325
+ }
316
326
 
317
327
  // src/state/ContextContainer.ts
318
328
  var ContextContainer = React3.createContext(null);
@@ -1196,26 +1206,6 @@ function calculateOffsetForIndex(ctx, state, index) {
1196
1206
  return position;
1197
1207
  }
1198
1208
 
1199
- // src/core/checkActualChange.ts
1200
- function checkActualChange(state, dataProp, previousData) {
1201
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
1202
- return true;
1203
- }
1204
- const {
1205
- idCache,
1206
- props: { keyExtractor }
1207
- } = state;
1208
- for (let i = 0; i < dataProp.length; i++) {
1209
- if (dataProp[i] !== previousData[i]) {
1210
- return true;
1211
- }
1212
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
1213
- return true;
1214
- }
1215
- }
1216
- return false;
1217
- }
1218
-
1219
1209
  // src/utils/setPaddingTop.ts
1220
1210
  function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1221
1211
  if (stylePaddingTop !== void 0) {
@@ -1305,7 +1295,7 @@ function setSize(ctx, state, itemKey, size) {
1305
1295
  }
1306
1296
 
1307
1297
  // src/utils/getItemSize.ts
1308
- function getItemSize(ctx, state, key, index, data, useAverageSize) {
1298
+ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1309
1299
  var _a3, _b;
1310
1300
  const {
1311
1301
  sizesKnown,
@@ -1320,6 +1310,12 @@ function getItemSize(ctx, state, key, index, data, useAverageSize) {
1320
1310
  let size;
1321
1311
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1322
1312
  const scrollingTo = peek$(ctx, "scrollingTo");
1313
+ if (preferCachedSize) {
1314
+ const cachedSize = sizes.get(key);
1315
+ if (cachedSize !== void 0) {
1316
+ return cachedSize;
1317
+ }
1318
+ }
1323
1319
  if (getFixedItemSize) {
1324
1320
  size = getFixedItemSize(index, data, itemType);
1325
1321
  if (size !== void 0) {
@@ -1529,14 +1525,13 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1529
1525
 
1530
1526
  // src/core/finishScrollTo.ts
1531
1527
  function finishScrollTo(ctx, state) {
1532
- var _a3;
1528
+ var _a3, _b;
1533
1529
  if (state) {
1534
1530
  state.scrollHistory.length = 0;
1535
1531
  state.initialScroll = void 0;
1536
- state.isOptimizingItemPositions = false;
1537
1532
  set$(ctx, "scrollingTo", void 0);
1538
1533
  if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1539
- calculateItemsInView(ctx, state, { forceFullItemPositions: true });
1534
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1540
1535
  }
1541
1536
  }
1542
1537
  }
@@ -1545,14 +1540,14 @@ function finishScrollTo(ctx, state) {
1545
1540
  function scrollTo(ctx, state, params) {
1546
1541
  var _a3;
1547
1542
  const { noScrollingTo, ...scrollTarget } = params;
1548
- const { animated, isInitialScroll, offset: scrollTargetOffset } = scrollTarget;
1543
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1549
1544
  const {
1550
1545
  refScroller,
1551
1546
  props: { horizontal }
1552
1547
  } = state;
1553
- let offset = calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1548
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1554
1549
  if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1555
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
1550
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1556
1551
  offset = Math.min(offset, maxOffset);
1557
1552
  }
1558
1553
  state.scrollHistory.length = 0;
@@ -1784,14 +1779,14 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1784
1779
  scrollBottomBuffered: -1,
1785
1780
  startIndex: 0
1786
1781
  }) {
1787
- var _a3, _b, _c, _d;
1782
+ var _a3, _b, _c, _d, _e;
1788
1783
  const {
1789
1784
  columns,
1790
1785
  indexByKey,
1791
1786
  positions,
1792
1787
  idCache,
1793
1788
  sizesKnown,
1794
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
1789
+ props: { getEstimatedItemSize, snapToIndices, enableAverages, maintainVisibleContentPosition }
1795
1790
  } = state;
1796
1791
  const data = state.props.data;
1797
1792
  const dataLength = data.length;
@@ -1800,9 +1795,9 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1800
1795
  const hasColumns = numColumns > 1;
1801
1796
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1802
1797
  const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1803
- state.isOptimizingItemPositions = shouldOptimize;
1804
1798
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1805
1799
  const useAverageSize = enableAverages && !getEstimatedItemSize;
1800
+ const preferCachedSize = maintainVisibleContentPosition && (dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0);
1806
1801
  let currentRowTop = 0;
1807
1802
  let column = 1;
1808
1803
  let maxSizeInRow = 0;
@@ -1819,8 +1814,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1819
1814
  } else if (startIndex < dataLength) {
1820
1815
  const prevIndex = startIndex - 1;
1821
1816
  const prevId = getId(state, prevIndex);
1822
- const prevPosition = (_a3 = positions.get(prevId)) != null ? _a3 : 0;
1823
- const prevSize = (_b = sizesKnown.get(prevId)) != null ? _b : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize);
1817
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1818
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1824
1819
  currentRowTop = prevPosition + prevSize;
1825
1820
  }
1826
1821
  }
@@ -1836,8 +1831,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1836
1831
  const itemsPerRow = hasColumns ? numColumns : 1;
1837
1832
  breakAt = i + itemsPerRow + 10;
1838
1833
  }
1839
- const id = (_c = idCache[i]) != null ? _c : getId(state, i);
1840
- const size = (_d = sizesKnown.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i], useAverageSize);
1834
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1835
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
1841
1836
  if (IS_DEV && needsIndexByKey) {
1842
1837
  if (indexByKeyForChecking.has(id)) {
1843
1838
  console.error(
@@ -2055,16 +2050,6 @@ function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize,
2055
2050
  const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2056
2051
  return value.isViewable;
2057
2052
  }
2058
- function findContainerId(ctx, key) {
2059
- const numContainers = peek$(ctx, "numContainers");
2060
- for (let i = 0; i < numContainers; i++) {
2061
- const itemKey = peek$(ctx, `containerItemKey${i}`);
2062
- if (itemKey === key) {
2063
- return i;
2064
- }
2065
- }
2066
- return -1;
2067
- }
2068
2053
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
2069
2054
  const key = containerId + configId;
2070
2055
  ctx.mapViewabilityValues.set(key, viewToken);
@@ -2586,7 +2571,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2586
2571
  for (let i = 0; i < numContainers; i++) {
2587
2572
  const itemKey = peek$(ctx, `containerItemKey${i}`);
2588
2573
  if (pendingRemoval.includes(i)) {
2589
- if (itemKey) {
2574
+ if (itemKey !== void 0) {
2590
2575
  containerItemKeys.delete(itemKey);
2591
2576
  }
2592
2577
  state.containerItemTypes.delete(i);
@@ -2647,6 +2632,26 @@ function calculateItemsInView(ctx, state, params = {}) {
2647
2632
  });
2648
2633
  }
2649
2634
 
2635
+ // src/core/checkActualChange.ts
2636
+ function checkActualChange(state, dataProp, previousData) {
2637
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
2638
+ return true;
2639
+ }
2640
+ const {
2641
+ idCache,
2642
+ props: { keyExtractor }
2643
+ } = state;
2644
+ for (let i = 0; i < dataProp.length; i++) {
2645
+ if (dataProp[i] !== previousData[i]) {
2646
+ return true;
2647
+ }
2648
+ if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
2649
+ return true;
2650
+ }
2651
+ }
2652
+ return false;
2653
+ }
2654
+
2650
2655
  // src/core/doMaintainScrollAtEnd.ts
2651
2656
  function doMaintainScrollAtEnd(ctx, state, animated) {
2652
2657
  const {
@@ -3162,6 +3167,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3162
3167
  columnWrapperStyle,
3163
3168
  contentContainerStyle: contentContainerStyleProp,
3164
3169
  data: dataProp = [],
3170
+ dataVersion,
3165
3171
  drawDistance = 250,
3166
3172
  enableAverages = true,
3167
3173
  estimatedItemSize: estimatedItemSizeProp,
@@ -3211,13 +3217,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3211
3217
  waitForInitialLayout = true,
3212
3218
  ...rest
3213
3219
  } = props;
3214
- const [renderNum, setRenderNum] = React3.useState(0);
3215
- const initialScrollProp = initialScrollAtEnd ? { index: dataProp.length - 1, viewOffset: 0 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3216
- const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
3217
3220
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3218
3221
  const style = { ...StyleSheet.flatten(styleProp) };
3219
3222
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3220
3223
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3224
+ const [renderNum, setRenderNum] = React3.useState(0);
3225
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3226
+ const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
3221
3227
  const ctx = useStateContext();
3222
3228
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
3223
3229
  const refScroller = React3.useRef(null);
@@ -3251,7 +3257,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3251
3257
  isAtStart: false,
3252
3258
  isEndReached: false,
3253
3259
  isFirst: true,
3254
- isOptimizingItemPositions: false,
3255
3260
  isStartReached: false,
3256
3261
  lastBatchingAction: Date.now(),
3257
3262
  lastLayout: void 0,
@@ -3284,6 +3289,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3284
3289
  totalSize: 0,
3285
3290
  viewabilityConfigCallbackPairs: void 0
3286
3291
  };
3292
+ const internalState = ctx.internalState;
3293
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3287
3294
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3288
3295
  set$(ctx, "extraData", extraData);
3289
3296
  }
@@ -3292,7 +3299,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3292
3299
  const state = refState.current;
3293
3300
  const isFirstLocal = state.isFirst;
3294
3301
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3295
- const didDataChangeLocal = state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3302
+ const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3296
3303
  if (didDataChangeLocal) {
3297
3304
  state.dataChangeNeedsScrollUpdate = true;
3298
3305
  state.didDataChange = true;
@@ -3302,6 +3309,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3302
3309
  state.props = {
3303
3310
  alignItemsAtEnd,
3304
3311
  data: dataProp,
3312
+ dataVersion,
3305
3313
  enableAverages,
3306
3314
  estimatedItemSize,
3307
3315
  getEstimatedItemSize,
@@ -3340,7 +3348,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3340
3348
  { length: Math.min(numColumnsProp, dataProp.length) },
3341
3349
  (_, i) => getId(state, dataProp.length - 1 - i)
3342
3350
  );
3343
- }, [dataProp, numColumnsProp]);
3351
+ }, [dataProp, dataVersion, numColumnsProp]);
3344
3352
  const initializeStateVars = () => {
3345
3353
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3346
3354
  set$(ctx, "numColumns", numColumnsProp);
@@ -3366,17 +3374,26 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3366
3374
  }
3367
3375
  const initialContentOffset = React3.useMemo(() => {
3368
3376
  const { initialScroll } = refState.current;
3369
- if (initialScroll) {
3370
- const { index, viewOffset } = initialScroll;
3371
- let initialContentOffset2 = viewOffset || 0;
3372
- if (index !== void 0) {
3373
- initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
3374
- }
3375
- refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
3376
- return initialContentOffset2;
3377
- }
3378
- return 0;
3379
- }, [renderNum]);
3377
+ if (!initialScroll) {
3378
+ return 0;
3379
+ }
3380
+ if (initialScroll.contentOffset !== void 0) {
3381
+ return initialScroll.contentOffset;
3382
+ }
3383
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3384
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3385
+ let clampedOffset = resolvedOffset;
3386
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3387
+ const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3388
+ clampedOffset = Math.min(clampedOffset, maxOffset);
3389
+ }
3390
+ clampedOffset = Math.max(0, clampedOffset);
3391
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3392
+ refState.current.initialScroll = updatedInitialScroll;
3393
+ state.initialScroll = updatedInitialScroll;
3394
+ refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3395
+ return clampedOffset;
3396
+ }, [renderNum, state.initialScroll]);
3380
3397
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3381
3398
  refState.current.lastBatchingAction = Date.now();
3382
3399
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
@@ -3403,9 +3420,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3403
3420
  }
3404
3421
  }, []);
3405
3422
  const doInitialScroll = React3.useCallback(() => {
3423
+ var _a4;
3406
3424
  const initialScroll = state.initialScroll;
3407
3425
  if (initialScroll) {
3408
- scrollTo(ctx, state, { animated: false, offset: initialContentOffset, ...state.initialScroll || {} });
3426
+ scrollTo(ctx, state, {
3427
+ animated: false,
3428
+ index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3429
+ offset: initialContentOffset,
3430
+ precomputedWithViewOffset: true
3431
+ });
3409
3432
  }
3410
3433
  }, [initialContentOffset, state.initialScroll]);
3411
3434
  const onLayoutChange = React3.useCallback((layout) => {
@@ -3437,15 +3460,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3437
3460
  state.didColumnsChange = false;
3438
3461
  state.didDataChange = false;
3439
3462
  state.isFirst = false;
3440
- }, [dataProp, numColumnsProp]);
3463
+ }, [dataProp, dataVersion, numColumnsProp]);
3441
3464
  React3.useLayoutEffect(() => {
3442
3465
  set$(ctx, "extraData", extraData);
3443
3466
  }, [extraData]);
3444
3467
  React3.useLayoutEffect(initializeStateVars, [
3468
+ dataVersion,
3445
3469
  memoizedLastItemKeys.join(","),
3446
3470
  numColumnsProp,
3447
- stylePaddingTopState,
3448
- stylePaddingBottomState
3471
+ stylePaddingBottomState,
3472
+ stylePaddingTopState
3449
3473
  ]);
3450
3474
  React3.useEffect(() => {
3451
3475
  const viewability = setupViewability({
@@ -3483,6 +3507,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3483
3507
  activeStickyIndex: state2.activeStickyIndex,
3484
3508
  contentLength: state2.totalSize,
3485
3509
  data: state2.props.data,
3510
+ elementAtIndex: (index) => {
3511
+ var _a4;
3512
+ return (_a4 = ctx.viewRefs.get(findContainerId(ctx, getId(state2, index)))) == null ? void 0 : _a4.current;
3513
+ },
3486
3514
  end: state2.endNoBuffer,
3487
3515
  endBuffered: state2.endBuffered,
3488
3516
  isAtEnd: state2.isAtEnd,