@legendapp/list 2.0.8 → 2.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 2.0.9
2
+ - Fix: Improve initialScrollIndex accuracy and reliability
3
+
1
4
  ## 2.0.8
2
5
  - Fix: Data changing sometimes left blank spaces because it was ignoring scroll
3
6
  - Fix: Toggling between empty and non-empty causing maintainVisibleContentPosition issues
package/index.d.mts CHANGED
@@ -5,6 +5,7 @@ import Animated$1 from 'react-native-reanimated';
5
5
 
6
6
  type ListenerType = "numContainers" | "numContainersPooled" | `containerItemKey${number}` | `containerItemData${number}` | `containerPosition${number}` | `containerColumn${number}` | `containerSticky${number}` | `containerStickyOffset${number}` | "containersDidLayout" | "extraData" | "numColumns" | "lastItemKeys" | "totalSize" | "alignItemsPaddingTop" | "stylePaddingTop" | "scrollAdjust" | "scrollAdjustUserOffset" | "headerSize" | "footerSize" | "maintainVisibleContentPosition" | "debugRawScroll" | "debugComputedScroll" | "otherAxisSize" | "snapToOffsets" | "scrollSize";
7
7
  interface StateContext {
8
+ internalState: InternalState | undefined;
8
9
  listeners: Map<ListenerType, Set<(value: any) => void>>;
9
10
  values: Map<ListenerType, any>;
10
11
  mapViewabilityCallbacks: Map<string, ViewabilityCallback>;
@@ -344,6 +345,7 @@ interface InternalState {
344
345
  viewOffset?: number;
345
346
  viewPosition?: number;
346
347
  animated?: boolean;
348
+ isInitialScroll?: boolean;
347
349
  } | undefined;
348
350
  needsOtherAxisSize?: boolean;
349
351
  averageSizes: Record<string, {
package/index.d.ts CHANGED
@@ -5,6 +5,7 @@ import Animated$1 from 'react-native-reanimated';
5
5
 
6
6
  type ListenerType = "numContainers" | "numContainersPooled" | `containerItemKey${number}` | `containerItemData${number}` | `containerPosition${number}` | `containerColumn${number}` | `containerSticky${number}` | `containerStickyOffset${number}` | "containersDidLayout" | "extraData" | "numColumns" | "lastItemKeys" | "totalSize" | "alignItemsPaddingTop" | "stylePaddingTop" | "scrollAdjust" | "scrollAdjustUserOffset" | "headerSize" | "footerSize" | "maintainVisibleContentPosition" | "debugRawScroll" | "debugComputedScroll" | "otherAxisSize" | "snapToOffsets" | "scrollSize";
7
7
  interface StateContext {
8
+ internalState: InternalState | undefined;
8
9
  listeners: Map<ListenerType, Set<(value: any) => void>>;
9
10
  values: Map<ListenerType, any>;
10
11
  mapViewabilityCallbacks: Map<string, ViewabilityCallback>;
@@ -344,6 +345,7 @@ interface InternalState {
344
345
  viewOffset?: number;
345
346
  viewPosition?: number;
346
347
  animated?: boolean;
348
+ isInitialScroll?: boolean;
347
349
  } | undefined;
348
350
  needsOtherAxisSize?: boolean;
349
351
  averageSizes: Record<string, {
package/index.js CHANGED
@@ -30,6 +30,7 @@ function StateProvider({ children }) {
30
30
  const [value] = React2__namespace.useState(() => ({
31
31
  animatedScrollY: new reactNative.Animated.Value(0),
32
32
  columnWrapperStyle: void 0,
33
+ internalState: void 0,
33
34
  listeners: /* @__PURE__ */ new Map(),
34
35
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
35
36
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
@@ -219,7 +220,9 @@ function useValue$(key, params) {
219
220
  prevValue = newValue;
220
221
  if (!didQueueTask) {
221
222
  didQueueTask = true;
222
- if (delayValue === 0) {
223
+ if (delayValue === void 0) {
224
+ fn();
225
+ } else if (delayValue === 0) {
223
226
  queueMicrotask(fn);
224
227
  } else {
225
228
  setTimeout(fn, delayValue);
@@ -606,7 +609,11 @@ var Containers = typedMemo(function Containers2({
606
609
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
607
610
  const animSize = useValue$("totalSize", {
608
611
  // Use a microtask if increasing the size significantly, otherwise use a timeout
609
- delay: (value, prevValue) => !prevValue || value - prevValue > 20 ? 0 : 200
612
+ // If this is the initial scroll, we don't want to delay because we want to update the size immediately
613
+ delay: (value, prevValue) => {
614
+ var _a;
615
+ return !((_a = ctx.internalState) == null ? void 0 : _a.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
616
+ }
610
617
  });
611
618
  const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("containersDidLayout", { getValue: (value) => value ? 1 : 0 }) : void 0;
612
619
  const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
@@ -967,7 +974,7 @@ var finishScrollTo = (state) => {
967
974
  // src/core/scrollTo.ts
968
975
  function scrollTo(state, params = {}) {
969
976
  var _a;
970
- const { animated, noScrollingTo } = params;
977
+ const { animated, noScrollingTo, isInitialScroll } = params;
971
978
  const {
972
979
  refScroller,
973
980
  props: { horizontal }
@@ -978,14 +985,21 @@ function scrollTo(state, params = {}) {
978
985
  state.scrollingTo = params;
979
986
  }
980
987
  state.scrollPending = offset;
981
- (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
982
- animated: !!animated,
983
- x: horizontal ? offset : 0,
984
- y: horizontal ? 0 : offset
985
- });
988
+ if (!params.isInitialScroll || reactNative.Platform.OS === "android") {
989
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
990
+ animated: !!animated,
991
+ x: horizontal ? offset : 0,
992
+ y: horizontal ? 0 : offset
993
+ });
994
+ }
986
995
  if (!animated) {
987
996
  state.scroll = offset;
988
997
  setTimeout(() => finishScrollTo(state), 100);
998
+ if (isInitialScroll) {
999
+ setTimeout(() => {
1000
+ state.initialScroll = void 0;
1001
+ }, 500);
1002
+ }
989
1003
  }
990
1004
  }
991
1005
 
@@ -1080,7 +1094,16 @@ function prepareMVCP(ctx, state, dataChanged) {
1080
1094
  if (targetId !== void 0 && prevPosition !== void 0) {
1081
1095
  const newPosition = positions.get(targetId);
1082
1096
  if (newPosition !== void 0) {
1083
- positionDiff = newPosition - prevPosition;
1097
+ const totalSize = peek$(ctx, "totalSize");
1098
+ let diff = newPosition - prevPosition;
1099
+ if (state.scroll + state.scrollLength > totalSize) {
1100
+ if (diff > 0) {
1101
+ diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1102
+ } else {
1103
+ diff = 0;
1104
+ }
1105
+ }
1106
+ positionDiff = diff;
1084
1107
  }
1085
1108
  }
1086
1109
  if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
@@ -1148,6 +1171,7 @@ function updateTotalSize(ctx, state) {
1148
1171
  }
1149
1172
  function addTotalSize(ctx, state, key, add) {
1150
1173
  const { alignItemsAtEnd } = state.props;
1174
+ const prevTotalSize = state.totalSize;
1151
1175
  if (key === null) {
1152
1176
  state.totalSize = add;
1153
1177
  if (state.timeoutSetPaddingTop) {
@@ -1157,9 +1181,11 @@ function addTotalSize(ctx, state, key, add) {
1157
1181
  } else {
1158
1182
  state.totalSize += add;
1159
1183
  }
1160
- set$(ctx, "totalSize", state.totalSize);
1161
- if (alignItemsAtEnd) {
1162
- updateAlignItemsPaddingTop(ctx, state);
1184
+ if (prevTotalSize !== state.totalSize) {
1185
+ set$(ctx, "totalSize", state.totalSize);
1186
+ if (alignItemsAtEnd) {
1187
+ updateAlignItemsPaddingTop(ctx, state);
1188
+ }
1163
1189
  }
1164
1190
  }
1165
1191
 
@@ -1721,17 +1747,16 @@ function setDidLayout(ctx, state) {
1721
1747
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1722
1748
  }
1723
1749
  };
1724
- if (reactNative.Platform.OS === "android" || !IsNewArchitecture) {
1725
- if (initialScroll) {
1726
- queueMicrotask(() => {
1750
+ if (reactNative.Platform.OS === "android" && initialScroll) {
1751
+ if (IsNewArchitecture) {
1752
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1753
+ requestAnimationFrame(() => {
1727
1754
  scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1728
- requestAnimationFrame(() => {
1729
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1730
- setIt();
1731
- });
1755
+ setIt();
1732
1756
  });
1733
1757
  } else {
1734
- queueMicrotask(setIt);
1758
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1759
+ setIt();
1735
1760
  }
1736
1761
  } else {
1737
1762
  setIt();
@@ -2383,7 +2408,7 @@ var ScrollAdjustHandler = class {
2383
2408
  this.context = ctx;
2384
2409
  }
2385
2410
  requestAdjust(add) {
2386
- const oldAdjustTop = peek$(this.context, "scrollAdjust") || 0;
2411
+ const oldAdjustTop = this.appliedAdjust;
2387
2412
  this.appliedAdjust = add + oldAdjustTop;
2388
2413
  const set = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2389
2414
  if (this.mounted) {
@@ -2402,7 +2427,7 @@ var ScrollAdjustHandler = class {
2402
2427
 
2403
2428
  // src/core/updateItemSize.ts
2404
2429
  function updateItemSize(ctx, state, itemKey, sizeObj) {
2405
- var _a, _b;
2430
+ var _a;
2406
2431
  const {
2407
2432
  sizesKnown,
2408
2433
  props: {
@@ -2417,17 +2442,17 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2417
2442
  }
2418
2443
  } = state;
2419
2444
  if (!data) return;
2445
+ const index = state.indexByKey.get(itemKey);
2420
2446
  if (getFixedItemSize) {
2421
- const index2 = state.indexByKey.get(itemKey);
2422
- if (index2 === void 0) {
2447
+ if (index === void 0) {
2423
2448
  return;
2424
2449
  }
2425
- const itemData = state.props.data[index2];
2450
+ const itemData = state.props.data[index];
2426
2451
  if (itemData === void 0) {
2427
2452
  return;
2428
2453
  }
2429
- const type = getItemType ? (_a = getItemType(itemData, index2)) != null ? _a : "" : "";
2430
- const size2 = getFixedItemSize(index2, itemData, type);
2454
+ const type = getItemType ? (_a = getItemType(itemData, index)) != null ? _a : "" : "";
2455
+ const size2 = getFixedItemSize(index, itemData, type);
2431
2456
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2432
2457
  return;
2433
2458
  }
@@ -2437,15 +2462,11 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2437
2462
  let shouldMaintainScrollAtEnd = false;
2438
2463
  let minIndexSizeChanged;
2439
2464
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2440
- const index = state.indexByKey.get(itemKey);
2441
2465
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2442
2466
  const diff = updateOneItemSize(state, itemKey, sizeObj);
2443
2467
  const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2444
2468
  if (diff !== 0) {
2445
2469
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2446
- if (((_b = state.scrollingTo) == null ? void 0 : _b.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
2447
- requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
2448
- }
2449
2470
  const { startBuffered, endBuffered } = state;
2450
2471
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2451
2472
  if (!needsRecalculate) {
@@ -2685,60 +2706,63 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2685
2706
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
2686
2707
  const refState = React2.useRef();
2687
2708
  if (!refState.current) {
2688
- const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : reactNative.Dimensions.get("window"))[horizontal ? "width" : "height"];
2689
- refState.current = {
2690
- activeStickyIndex: void 0,
2691
- averageSizes: {},
2692
- columns: /* @__PURE__ */ new Map(),
2693
- containerItemKeys: /* @__PURE__ */ new Set(),
2694
- containerItemTypes: /* @__PURE__ */ new Map(),
2695
- dataChangeNeedsScrollUpdate: false,
2696
- enableScrollForNextCalculateItemsInView: true,
2697
- endBuffered: -1,
2698
- endNoBuffer: -1,
2699
- endReachedBlockedByTimer: false,
2700
- firstFullyOnScreenIndex: -1,
2701
- idCache: /* @__PURE__ */ new Map(),
2702
- idsInView: [],
2703
- indexByKey: /* @__PURE__ */ new Map(),
2704
- initialScroll,
2705
- isAtEnd: false,
2706
- isAtStart: false,
2707
- isEndReached: false,
2708
- isStartReached: false,
2709
- lastBatchingAction: Date.now(),
2710
- lastLayout: void 0,
2711
- loadStartTime: Date.now(),
2712
- minIndexSizeChanged: 0,
2713
- nativeMarginTop: 0,
2714
- positions: /* @__PURE__ */ new Map(),
2715
- props: {},
2716
- queuedCalculateItemsInView: 0,
2717
- refScroller: void 0,
2718
- scroll: 0,
2719
- scrollAdjustHandler: new ScrollAdjustHandler(ctx),
2720
- scrollForNextCalculateItemsInView: void 0,
2721
- scrollHistory: [],
2722
- scrollLength: initialScrollLength,
2723
- scrollPending: 0,
2724
- scrollPrev: 0,
2725
- scrollPrevTime: 0,
2726
- scrollProcessingEnabled: true,
2727
- scrollTime: 0,
2728
- sizes: /* @__PURE__ */ new Map(),
2729
- sizesKnown: /* @__PURE__ */ new Map(),
2730
- startBuffered: -1,
2731
- startNoBuffer: -1,
2732
- startReachedBlockedByTimer: false,
2733
- stickyContainerPool: /* @__PURE__ */ new Set(),
2734
- stickyContainers: /* @__PURE__ */ new Map(),
2735
- timeoutSizeMessage: 0,
2736
- timeouts: /* @__PURE__ */ new Set(),
2737
- totalSize: 0,
2738
- viewabilityConfigCallbackPairs: void 0
2739
- };
2740
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
2741
- set$(ctx, "extraData", extraData);
2709
+ if (!ctx.internalState) {
2710
+ const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : reactNative.Dimensions.get("window"))[horizontal ? "width" : "height"];
2711
+ ctx.internalState = {
2712
+ activeStickyIndex: void 0,
2713
+ averageSizes: {},
2714
+ columns: /* @__PURE__ */ new Map(),
2715
+ containerItemKeys: /* @__PURE__ */ new Set(),
2716
+ containerItemTypes: /* @__PURE__ */ new Map(),
2717
+ dataChangeNeedsScrollUpdate: false,
2718
+ enableScrollForNextCalculateItemsInView: true,
2719
+ endBuffered: -1,
2720
+ endNoBuffer: -1,
2721
+ endReachedBlockedByTimer: false,
2722
+ firstFullyOnScreenIndex: -1,
2723
+ idCache: /* @__PURE__ */ new Map(),
2724
+ idsInView: [],
2725
+ indexByKey: /* @__PURE__ */ new Map(),
2726
+ initialScroll,
2727
+ isAtEnd: false,
2728
+ isAtStart: false,
2729
+ isEndReached: false,
2730
+ isStartReached: false,
2731
+ lastBatchingAction: Date.now(),
2732
+ lastLayout: void 0,
2733
+ loadStartTime: Date.now(),
2734
+ minIndexSizeChanged: 0,
2735
+ nativeMarginTop: 0,
2736
+ positions: /* @__PURE__ */ new Map(),
2737
+ props: {},
2738
+ queuedCalculateItemsInView: 0,
2739
+ refScroller: void 0,
2740
+ scroll: 0,
2741
+ scrollAdjustHandler: new ScrollAdjustHandler(ctx),
2742
+ scrollForNextCalculateItemsInView: void 0,
2743
+ scrollHistory: [],
2744
+ scrollLength: initialScrollLength,
2745
+ scrollPending: 0,
2746
+ scrollPrev: 0,
2747
+ scrollPrevTime: 0,
2748
+ scrollProcessingEnabled: true,
2749
+ scrollTime: 0,
2750
+ sizes: /* @__PURE__ */ new Map(),
2751
+ sizesKnown: /* @__PURE__ */ new Map(),
2752
+ startBuffered: -1,
2753
+ startNoBuffer: -1,
2754
+ startReachedBlockedByTimer: false,
2755
+ stickyContainerPool: /* @__PURE__ */ new Set(),
2756
+ stickyContainers: /* @__PURE__ */ new Map(),
2757
+ timeoutSizeMessage: 0,
2758
+ timeouts: /* @__PURE__ */ new Set(),
2759
+ totalSize: 0,
2760
+ viewabilityConfigCallbackPairs: void 0
2761
+ };
2762
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
2763
+ set$(ctx, "extraData", extraData);
2764
+ }
2765
+ refState.current = ctx.internalState;
2742
2766
  }
2743
2767
  const state = refState.current;
2744
2768
  const isFirst = !state.props.renderItem;
@@ -2821,7 +2845,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2821
2845
  }
2822
2846
  refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2823
2847
  if (initialContentOffset2 > 0) {
2824
- scrollTo(state, { animated: false, index, offset: initialContentOffset2 });
2848
+ scrollTo(state, {
2849
+ animated: false,
2850
+ index,
2851
+ isInitialScroll: true,
2852
+ offset: initialContentOffset2,
2853
+ viewPosition: index === dataProp.length - 1 ? 1 : 0
2854
+ });
2825
2855
  }
2826
2856
  return initialContentOffset2;
2827
2857
  }
package/index.mjs CHANGED
@@ -9,6 +9,7 @@ function StateProvider({ children }) {
9
9
  const [value] = React2.useState(() => ({
10
10
  animatedScrollY: new Animated.Value(0),
11
11
  columnWrapperStyle: void 0,
12
+ internalState: void 0,
12
13
  listeners: /* @__PURE__ */ new Map(),
13
14
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
14
15
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
@@ -198,7 +199,9 @@ function useValue$(key, params) {
198
199
  prevValue = newValue;
199
200
  if (!didQueueTask) {
200
201
  didQueueTask = true;
201
- if (delayValue === 0) {
202
+ if (delayValue === void 0) {
203
+ fn();
204
+ } else if (delayValue === 0) {
202
205
  queueMicrotask(fn);
203
206
  } else {
204
207
  setTimeout(fn, delayValue);
@@ -585,7 +588,11 @@ var Containers = typedMemo(function Containers2({
585
588
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
586
589
  const animSize = useValue$("totalSize", {
587
590
  // Use a microtask if increasing the size significantly, otherwise use a timeout
588
- delay: (value, prevValue) => !prevValue || value - prevValue > 20 ? 0 : 200
591
+ // If this is the initial scroll, we don't want to delay because we want to update the size immediately
592
+ delay: (value, prevValue) => {
593
+ var _a;
594
+ return !((_a = ctx.internalState) == null ? void 0 : _a.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
595
+ }
589
596
  });
590
597
  const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("containersDidLayout", { getValue: (value) => value ? 1 : 0 }) : void 0;
591
598
  const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
@@ -946,7 +953,7 @@ var finishScrollTo = (state) => {
946
953
  // src/core/scrollTo.ts
947
954
  function scrollTo(state, params = {}) {
948
955
  var _a;
949
- const { animated, noScrollingTo } = params;
956
+ const { animated, noScrollingTo, isInitialScroll } = params;
950
957
  const {
951
958
  refScroller,
952
959
  props: { horizontal }
@@ -957,14 +964,21 @@ function scrollTo(state, params = {}) {
957
964
  state.scrollingTo = params;
958
965
  }
959
966
  state.scrollPending = offset;
960
- (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
961
- animated: !!animated,
962
- x: horizontal ? offset : 0,
963
- y: horizontal ? 0 : offset
964
- });
967
+ if (!params.isInitialScroll || Platform.OS === "android") {
968
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
969
+ animated: !!animated,
970
+ x: horizontal ? offset : 0,
971
+ y: horizontal ? 0 : offset
972
+ });
973
+ }
965
974
  if (!animated) {
966
975
  state.scroll = offset;
967
976
  setTimeout(() => finishScrollTo(state), 100);
977
+ if (isInitialScroll) {
978
+ setTimeout(() => {
979
+ state.initialScroll = void 0;
980
+ }, 500);
981
+ }
968
982
  }
969
983
  }
970
984
 
@@ -1059,7 +1073,16 @@ function prepareMVCP(ctx, state, dataChanged) {
1059
1073
  if (targetId !== void 0 && prevPosition !== void 0) {
1060
1074
  const newPosition = positions.get(targetId);
1061
1075
  if (newPosition !== void 0) {
1062
- positionDiff = newPosition - prevPosition;
1076
+ const totalSize = peek$(ctx, "totalSize");
1077
+ let diff = newPosition - prevPosition;
1078
+ if (state.scroll + state.scrollLength > totalSize) {
1079
+ if (diff > 0) {
1080
+ diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1081
+ } else {
1082
+ diff = 0;
1083
+ }
1084
+ }
1085
+ positionDiff = diff;
1063
1086
  }
1064
1087
  }
1065
1088
  if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
@@ -1127,6 +1150,7 @@ function updateTotalSize(ctx, state) {
1127
1150
  }
1128
1151
  function addTotalSize(ctx, state, key, add) {
1129
1152
  const { alignItemsAtEnd } = state.props;
1153
+ const prevTotalSize = state.totalSize;
1130
1154
  if (key === null) {
1131
1155
  state.totalSize = add;
1132
1156
  if (state.timeoutSetPaddingTop) {
@@ -1136,9 +1160,11 @@ function addTotalSize(ctx, state, key, add) {
1136
1160
  } else {
1137
1161
  state.totalSize += add;
1138
1162
  }
1139
- set$(ctx, "totalSize", state.totalSize);
1140
- if (alignItemsAtEnd) {
1141
- updateAlignItemsPaddingTop(ctx, state);
1163
+ if (prevTotalSize !== state.totalSize) {
1164
+ set$(ctx, "totalSize", state.totalSize);
1165
+ if (alignItemsAtEnd) {
1166
+ updateAlignItemsPaddingTop(ctx, state);
1167
+ }
1142
1168
  }
1143
1169
  }
1144
1170
 
@@ -1700,17 +1726,16 @@ function setDidLayout(ctx, state) {
1700
1726
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1701
1727
  }
1702
1728
  };
1703
- if (Platform.OS === "android" || !IsNewArchitecture) {
1704
- if (initialScroll) {
1705
- queueMicrotask(() => {
1729
+ if (Platform.OS === "android" && initialScroll) {
1730
+ if (IsNewArchitecture) {
1731
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1732
+ requestAnimationFrame(() => {
1706
1733
  scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1707
- requestAnimationFrame(() => {
1708
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1709
- setIt();
1710
- });
1734
+ setIt();
1711
1735
  });
1712
1736
  } else {
1713
- queueMicrotask(setIt);
1737
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1738
+ setIt();
1714
1739
  }
1715
1740
  } else {
1716
1741
  setIt();
@@ -2362,7 +2387,7 @@ var ScrollAdjustHandler = class {
2362
2387
  this.context = ctx;
2363
2388
  }
2364
2389
  requestAdjust(add) {
2365
- const oldAdjustTop = peek$(this.context, "scrollAdjust") || 0;
2390
+ const oldAdjustTop = this.appliedAdjust;
2366
2391
  this.appliedAdjust = add + oldAdjustTop;
2367
2392
  const set = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2368
2393
  if (this.mounted) {
@@ -2381,7 +2406,7 @@ var ScrollAdjustHandler = class {
2381
2406
 
2382
2407
  // src/core/updateItemSize.ts
2383
2408
  function updateItemSize(ctx, state, itemKey, sizeObj) {
2384
- var _a, _b;
2409
+ var _a;
2385
2410
  const {
2386
2411
  sizesKnown,
2387
2412
  props: {
@@ -2396,17 +2421,17 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2396
2421
  }
2397
2422
  } = state;
2398
2423
  if (!data) return;
2424
+ const index = state.indexByKey.get(itemKey);
2399
2425
  if (getFixedItemSize) {
2400
- const index2 = state.indexByKey.get(itemKey);
2401
- if (index2 === void 0) {
2426
+ if (index === void 0) {
2402
2427
  return;
2403
2428
  }
2404
- const itemData = state.props.data[index2];
2429
+ const itemData = state.props.data[index];
2405
2430
  if (itemData === void 0) {
2406
2431
  return;
2407
2432
  }
2408
- const type = getItemType ? (_a = getItemType(itemData, index2)) != null ? _a : "" : "";
2409
- const size2 = getFixedItemSize(index2, itemData, type);
2433
+ const type = getItemType ? (_a = getItemType(itemData, index)) != null ? _a : "" : "";
2434
+ const size2 = getFixedItemSize(index, itemData, type);
2410
2435
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2411
2436
  return;
2412
2437
  }
@@ -2416,15 +2441,11 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2416
2441
  let shouldMaintainScrollAtEnd = false;
2417
2442
  let minIndexSizeChanged;
2418
2443
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2419
- const index = state.indexByKey.get(itemKey);
2420
2444
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2421
2445
  const diff = updateOneItemSize(state, itemKey, sizeObj);
2422
2446
  const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2423
2447
  if (diff !== 0) {
2424
2448
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2425
- if (((_b = state.scrollingTo) == null ? void 0 : _b.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
2426
- requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
2427
- }
2428
2449
  const { startBuffered, endBuffered } = state;
2429
2450
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2430
2451
  if (!needsRecalculate) {
@@ -2664,60 +2685,63 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2664
2685
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
2665
2686
  const refState = useRef();
2666
2687
  if (!refState.current) {
2667
- const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : Dimensions.get("window"))[horizontal ? "width" : "height"];
2668
- refState.current = {
2669
- activeStickyIndex: void 0,
2670
- averageSizes: {},
2671
- columns: /* @__PURE__ */ new Map(),
2672
- containerItemKeys: /* @__PURE__ */ new Set(),
2673
- containerItemTypes: /* @__PURE__ */ new Map(),
2674
- dataChangeNeedsScrollUpdate: false,
2675
- enableScrollForNextCalculateItemsInView: true,
2676
- endBuffered: -1,
2677
- endNoBuffer: -1,
2678
- endReachedBlockedByTimer: false,
2679
- firstFullyOnScreenIndex: -1,
2680
- idCache: /* @__PURE__ */ new Map(),
2681
- idsInView: [],
2682
- indexByKey: /* @__PURE__ */ new Map(),
2683
- initialScroll,
2684
- isAtEnd: false,
2685
- isAtStart: false,
2686
- isEndReached: false,
2687
- isStartReached: false,
2688
- lastBatchingAction: Date.now(),
2689
- lastLayout: void 0,
2690
- loadStartTime: Date.now(),
2691
- minIndexSizeChanged: 0,
2692
- nativeMarginTop: 0,
2693
- positions: /* @__PURE__ */ new Map(),
2694
- props: {},
2695
- queuedCalculateItemsInView: 0,
2696
- refScroller: void 0,
2697
- scroll: 0,
2698
- scrollAdjustHandler: new ScrollAdjustHandler(ctx),
2699
- scrollForNextCalculateItemsInView: void 0,
2700
- scrollHistory: [],
2701
- scrollLength: initialScrollLength,
2702
- scrollPending: 0,
2703
- scrollPrev: 0,
2704
- scrollPrevTime: 0,
2705
- scrollProcessingEnabled: true,
2706
- scrollTime: 0,
2707
- sizes: /* @__PURE__ */ new Map(),
2708
- sizesKnown: /* @__PURE__ */ new Map(),
2709
- startBuffered: -1,
2710
- startNoBuffer: -1,
2711
- startReachedBlockedByTimer: false,
2712
- stickyContainerPool: /* @__PURE__ */ new Set(),
2713
- stickyContainers: /* @__PURE__ */ new Map(),
2714
- timeoutSizeMessage: 0,
2715
- timeouts: /* @__PURE__ */ new Set(),
2716
- totalSize: 0,
2717
- viewabilityConfigCallbackPairs: void 0
2718
- };
2719
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
2720
- set$(ctx, "extraData", extraData);
2688
+ if (!ctx.internalState) {
2689
+ const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : Dimensions.get("window"))[horizontal ? "width" : "height"];
2690
+ ctx.internalState = {
2691
+ activeStickyIndex: void 0,
2692
+ averageSizes: {},
2693
+ columns: /* @__PURE__ */ new Map(),
2694
+ containerItemKeys: /* @__PURE__ */ new Set(),
2695
+ containerItemTypes: /* @__PURE__ */ new Map(),
2696
+ dataChangeNeedsScrollUpdate: false,
2697
+ enableScrollForNextCalculateItemsInView: true,
2698
+ endBuffered: -1,
2699
+ endNoBuffer: -1,
2700
+ endReachedBlockedByTimer: false,
2701
+ firstFullyOnScreenIndex: -1,
2702
+ idCache: /* @__PURE__ */ new Map(),
2703
+ idsInView: [],
2704
+ indexByKey: /* @__PURE__ */ new Map(),
2705
+ initialScroll,
2706
+ isAtEnd: false,
2707
+ isAtStart: false,
2708
+ isEndReached: false,
2709
+ isStartReached: false,
2710
+ lastBatchingAction: Date.now(),
2711
+ lastLayout: void 0,
2712
+ loadStartTime: Date.now(),
2713
+ minIndexSizeChanged: 0,
2714
+ nativeMarginTop: 0,
2715
+ positions: /* @__PURE__ */ new Map(),
2716
+ props: {},
2717
+ queuedCalculateItemsInView: 0,
2718
+ refScroller: void 0,
2719
+ scroll: 0,
2720
+ scrollAdjustHandler: new ScrollAdjustHandler(ctx),
2721
+ scrollForNextCalculateItemsInView: void 0,
2722
+ scrollHistory: [],
2723
+ scrollLength: initialScrollLength,
2724
+ scrollPending: 0,
2725
+ scrollPrev: 0,
2726
+ scrollPrevTime: 0,
2727
+ scrollProcessingEnabled: true,
2728
+ scrollTime: 0,
2729
+ sizes: /* @__PURE__ */ new Map(),
2730
+ sizesKnown: /* @__PURE__ */ new Map(),
2731
+ startBuffered: -1,
2732
+ startNoBuffer: -1,
2733
+ startReachedBlockedByTimer: false,
2734
+ stickyContainerPool: /* @__PURE__ */ new Set(),
2735
+ stickyContainers: /* @__PURE__ */ new Map(),
2736
+ timeoutSizeMessage: 0,
2737
+ timeouts: /* @__PURE__ */ new Set(),
2738
+ totalSize: 0,
2739
+ viewabilityConfigCallbackPairs: void 0
2740
+ };
2741
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
2742
+ set$(ctx, "extraData", extraData);
2743
+ }
2744
+ refState.current = ctx.internalState;
2721
2745
  }
2722
2746
  const state = refState.current;
2723
2747
  const isFirst = !state.props.renderItem;
@@ -2800,7 +2824,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2800
2824
  }
2801
2825
  refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2802
2826
  if (initialContentOffset2 > 0) {
2803
- scrollTo(state, { animated: false, index, offset: initialContentOffset2 });
2827
+ scrollTo(state, {
2828
+ animated: false,
2829
+ index,
2830
+ isInitialScroll: true,
2831
+ offset: initialContentOffset2,
2832
+ viewPosition: index === dataProp.length - 1 ? 1 : 0
2833
+ });
2804
2834
  }
2805
2835
  return initialContentOffset2;
2806
2836
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "2.0.8",
3
+ "version": "2.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,