@legendapp/list 2.0.9 → 2.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React2 from 'react';
2
- import React2__default, { useReducer, useEffect, createContext, useRef, useState, useMemo, useLayoutEffect, useCallback, useImperativeHandle, forwardRef, memo, useContext } from 'react';
2
+ import React2__default, { useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useImperativeHandle, forwardRef, memo, useContext } from 'react';
3
3
  import { View, Text, Platform, Animated, StyleSheet, Dimensions, RefreshControl, unstable_batchedUpdates } from 'react-native';
4
4
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
5
5
 
@@ -161,11 +161,6 @@ function useInterval(callback, delay) {
161
161
  return () => clearInterval(interval);
162
162
  }, [delay]);
163
163
  }
164
- var LeanViewComponent = React2.forwardRef((props, ref) => {
165
- return React2.createElement("RCTView", { ...props, ref });
166
- });
167
- LeanViewComponent.displayName = "RCTView";
168
- var LeanView = Platform.OS === "android" || Platform.OS === "ios" ? LeanViewComponent : View;
169
164
 
170
165
  // src/constants.ts
171
166
  var POSITION_OUT_OF_VIEW = -1e7;
@@ -227,7 +222,7 @@ var PositionViewState = typedMemo(function PositionView({
227
222
  }) {
228
223
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
229
224
  return /* @__PURE__ */ React2.createElement(
230
- LeanView,
225
+ View,
231
226
  {
232
227
  ref: refView,
233
228
  style: [
@@ -268,7 +263,7 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
268
263
  }) {
269
264
  const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
270
265
  const transform = React2.useMemo(() => {
271
- if (animatedScrollY && stickyOffset) {
266
+ if (animatedScrollY && stickyOffset !== void 0) {
272
267
  const stickyPosition = animatedScrollY.interpolate({
273
268
  extrapolate: "clamp",
274
269
  inputRange: [position + headerSize, position + 5e3 + headerSize],
@@ -433,6 +428,29 @@ function Separator({ ItemSeparatorComponent, leadingItem }) {
433
428
  const isLastItem = useIsLastItem();
434
429
  return isLastItem ? null : /* @__PURE__ */ React2.createElement(ItemSeparatorComponent, { leadingItem });
435
430
  }
431
+ function useOnLayoutSync({
432
+ ref,
433
+ onLayoutProp,
434
+ onLayoutChange
435
+ }, deps = []) {
436
+ const onLayout = useCallback(
437
+ (event) => {
438
+ onLayoutChange(event.nativeEvent.layout, false);
439
+ onLayoutProp == null ? void 0 : onLayoutProp(event);
440
+ },
441
+ [onLayoutChange]
442
+ );
443
+ if (IsNewArchitecture) {
444
+ useLayoutEffect(() => {
445
+ if (ref.current) {
446
+ ref.current.measure((x, y, width, height) => {
447
+ onLayoutChange({ height, width, x, y }, true);
448
+ });
449
+ }
450
+ }, deps);
451
+ }
452
+ return { onLayout };
453
+ }
436
454
 
437
455
  // src/components/Container.tsx
438
456
  var Container = typedMemo(function Container2({
@@ -459,7 +477,7 @@ var Container = typedMemo(function Container2({
459
477
  const [layoutRenderCount, forceLayoutRender] = useState(0);
460
478
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
461
479
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
462
- let didLayout = false;
480
+ const didLayoutRef = useRef(false);
463
481
  const style = useMemo(() => {
464
482
  let paddingStyles;
465
483
  if (columnWrapperStyle) {
@@ -509,15 +527,16 @@ var Container = typedMemo(function Container2({
509
527
  value: data
510
528
  };
511
529
  }, [id, itemKey, index, data]);
512
- const onLayout = (event) => {
530
+ const onLayoutChange = (rectangle) => {
513
531
  var _a, _b;
514
532
  if (!isNullOrUndefined(itemKey)) {
515
- didLayout = true;
516
- let layout = event.nativeEvent.layout;
517
- const size = layout[horizontal ? "width" : "height"];
533
+ didLayoutRef.current = true;
534
+ let layout = rectangle;
535
+ const size = Math.floor(rectangle[horizontal ? "width" : "height"] * 8) / 8;
518
536
  const doUpdate = () => {
519
537
  refLastSize.current = { height: layout.height, width: layout.width };
520
538
  updateItemSize2(itemKey, layout);
539
+ didLayoutRef.current = true;
521
540
  };
522
541
  if (IsNewArchitecture || size > 0) {
523
542
  doUpdate();
@@ -529,25 +548,20 @@ var Container = typedMemo(function Container2({
529
548
  }
530
549
  }
531
550
  };
532
- if (IsNewArchitecture) {
533
- useLayoutEffect(() => {
534
- var _a, _b;
535
- if (!isNullOrUndefined(itemKey)) {
536
- const measured = (_b = (_a = ref.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
537
- if (measured) {
538
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
539
- if (size) {
540
- updateItemSize2(itemKey, measured);
541
- }
542
- }
543
- }
544
- }, [itemKey, layoutRenderCount]);
545
- } else {
551
+ const { onLayout } = useOnLayoutSync(
552
+ {
553
+ onLayoutChange,
554
+ ref
555
+ },
556
+ [itemKey, layoutRenderCount]
557
+ );
558
+ if (!IsNewArchitecture) {
546
559
  useEffect(() => {
547
560
  if (!isNullOrUndefined(itemKey)) {
548
561
  const timeout = setTimeout(() => {
549
- if (!didLayout && refLastSize.current) {
562
+ if (!didLayoutRef.current && refLastSize.current) {
550
563
  updateItemSize2(itemKey, refLastSize.current);
564
+ didLayoutRef.current = true;
551
565
  }
552
566
  }, 16);
553
567
  return () => {
@@ -636,6 +650,11 @@ var Containers = typedMemo(function Containers2({
636
650
  }
637
651
  return /* @__PURE__ */ React2.createElement(Animated.View, { style }, containers);
638
652
  });
653
+ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
654
+ const ref = refView != null ? refView : useRef();
655
+ const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
656
+ return /* @__PURE__ */ React2.createElement(View, { ...rest, onLayout, ref });
657
+ };
639
658
  function ScrollAdjust() {
640
659
  const bias = 1e7;
641
660
  const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
@@ -657,68 +676,6 @@ function SnapWrapper({ ScrollComponent, ...props }) {
657
676
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
658
677
  return /* @__PURE__ */ React2__default.createElement(ScrollComponent, { ...props, snapToOffsets });
659
678
  }
660
- function useThrottleDebounce(mode) {
661
- const timeoutRef = useRef(null);
662
- const lastCallTimeRef = useRef(0);
663
- const lastArgsRef = useRef(null);
664
- const clearTimeoutRef = () => {
665
- if (timeoutRef.current) {
666
- clearTimeout(timeoutRef.current);
667
- timeoutRef.current = null;
668
- }
669
- };
670
- const execute = useCallback(
671
- (callback, delay, ...args) => {
672
- {
673
- const now = Date.now();
674
- lastArgsRef.current = args;
675
- if (now - lastCallTimeRef.current >= delay) {
676
- lastCallTimeRef.current = now;
677
- callback(...args);
678
- clearTimeoutRef();
679
- } else {
680
- clearTimeoutRef();
681
- timeoutRef.current = setTimeout(
682
- () => {
683
- if (lastArgsRef.current) {
684
- lastCallTimeRef.current = Date.now();
685
- callback(...lastArgsRef.current);
686
- timeoutRef.current = null;
687
- lastArgsRef.current = null;
688
- }
689
- },
690
- delay - (now - lastCallTimeRef.current)
691
- );
692
- }
693
- }
694
- },
695
- [mode]
696
- );
697
- return execute;
698
- }
699
-
700
- // src/hooks/useSyncLayout.tsx
701
- function useSyncLayout2({
702
- onChange
703
- }) {
704
- const ref = useRef(null);
705
- const onLayout = useCallback(
706
- (event) => {
707
- onChange(event.nativeEvent.layout, false);
708
- },
709
- [onChange]
710
- );
711
- if (IsNewArchitecture) {
712
- useLayoutEffect(() => {
713
- if (ref.current) {
714
- ref.current.measure((x, y, width, height) => {
715
- onChange({ height, width, x, y }, true);
716
- });
717
- }
718
- }, []);
719
- }
720
- return { onLayout, ref };
721
- }
722
679
 
723
680
  // src/components/ListComponent.tsx
724
681
  var getComponent = (Component) => {
@@ -779,9 +736,6 @@ var ListComponent = typedMemo(function ListComponent2({
779
736
  ...rest
780
737
  }) {
781
738
  const ctx = useStateContext();
782
- const { onLayout: onLayoutHeaderSync, ref: refHeader } = useSyncLayout2({
783
- onChange: onLayoutHeader
784
- });
785
739
  const ScrollComponent = renderScrollComponent ? useMemo(
786
740
  () => React2.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
787
741
  [renderScrollComponent]
@@ -815,7 +769,7 @@ var ListComponent = typedMemo(function ListComponent2({
815
769
  },
816
770
  maintainVisibleContentPosition && /* @__PURE__ */ React2.createElement(ScrollAdjust, null),
817
771
  ENABLE_DEVMODE ? /* @__PURE__ */ React2.createElement(PaddingDevMode, null) : /* @__PURE__ */ React2.createElement(Padding, null),
818
- ListHeaderComponent && /* @__PURE__ */ React2.createElement(View, { onLayout: onLayoutHeaderSync, ref: refHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
772
+ ListHeaderComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
819
773
  ListEmptyComponent && getComponent(ListEmptyComponent),
820
774
  canRender && !ListEmptyComponent && /* @__PURE__ */ React2.createElement(
821
775
  Containers,
@@ -829,10 +783,10 @@ var ListComponent = typedMemo(function ListComponent2({
829
783
  }
830
784
  ),
831
785
  ListFooterComponent && /* @__PURE__ */ React2.createElement(
832
- View,
786
+ LayoutView,
833
787
  {
834
- onLayout: (event) => {
835
- const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
788
+ onLayoutChange: (layout) => {
789
+ const size = layout[horizontal ? "width" : "height"];
836
790
  set$(ctx, "footerSize", size);
837
791
  },
838
792
  style: ListFooterComponentStyle
@@ -867,7 +821,7 @@ function getId(state, index) {
867
821
  }
868
822
  const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
869
823
  const id = ret;
870
- state.idCache.set(index, id);
824
+ state.idCache[index] = id;
871
825
  return id;
872
826
  }
873
827
 
@@ -1091,6 +1045,68 @@ function prepareMVCP(ctx, state, dataChanged) {
1091
1045
  };
1092
1046
  }
1093
1047
 
1048
+ // src/core/prepareColumnStartState.ts
1049
+ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1050
+ var _a;
1051
+ const numColumns = peek$(ctx, "numColumns");
1052
+ let rowStartIndex = startIndex;
1053
+ const columnAtStart = state.columns.get(state.idCache[startIndex]);
1054
+ if (columnAtStart !== 1) {
1055
+ rowStartIndex = findRowStartIndex(state, numColumns, startIndex);
1056
+ }
1057
+ let currentRowTop = 0;
1058
+ const curId = state.idCache[rowStartIndex];
1059
+ const column = state.columns.get(curId);
1060
+ if (rowStartIndex > 0) {
1061
+ const prevIndex = rowStartIndex - 1;
1062
+ const prevId = state.idCache[prevIndex];
1063
+ const prevPosition = (_a = state.positions.get(prevId)) != null ? _a : 0;
1064
+ const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1065
+ const prevRowHeight = calculateRowMaxSize(state, prevRowStart, prevIndex, useAverageSize);
1066
+ currentRowTop = prevPosition + prevRowHeight;
1067
+ }
1068
+ return {
1069
+ column,
1070
+ currentRowTop,
1071
+ startIndex: rowStartIndex
1072
+ };
1073
+ }
1074
+ function findRowStartIndex(state, numColumns, index) {
1075
+ if (numColumns <= 1) {
1076
+ return Math.max(0, index);
1077
+ }
1078
+ let rowStart = Math.max(0, index);
1079
+ while (rowStart > 0) {
1080
+ const columnForIndex = state.columns.get(state.idCache[rowStart]);
1081
+ if (columnForIndex === 1) {
1082
+ break;
1083
+ }
1084
+ rowStart--;
1085
+ }
1086
+ return rowStart;
1087
+ }
1088
+ function calculateRowMaxSize(state, startIndex, endIndex, useAverageSize) {
1089
+ if (endIndex < startIndex) {
1090
+ return 0;
1091
+ }
1092
+ const { data } = state.props;
1093
+ if (!data) {
1094
+ return 0;
1095
+ }
1096
+ let maxSize = 0;
1097
+ for (let i = startIndex; i <= endIndex; i++) {
1098
+ if (i < 0 || i >= data.length) {
1099
+ continue;
1100
+ }
1101
+ const id = state.idCache[i];
1102
+ const size = getItemSize(state, id, i, data[i], useAverageSize);
1103
+ if (size > maxSize) {
1104
+ maxSize = size;
1105
+ }
1106
+ }
1107
+ return maxSize;
1108
+ }
1109
+
1094
1110
  // src/utils/setPaddingTop.ts
1095
1111
  function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1096
1112
  if (stylePaddingTop !== void 0) {
@@ -1185,7 +1201,7 @@ function updateSnapToOffsets(ctx, state) {
1185
1201
 
1186
1202
  // src/core/updateItemPositions.ts
1187
1203
  function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered } = { scrollBottomBuffered: -1, startIndex: 0 }) {
1188
- var _a, _b, _c, _d, _e, _f;
1204
+ var _a, _b, _c, _d;
1189
1205
  const {
1190
1206
  columns,
1191
1207
  indexByKey,
@@ -1195,41 +1211,47 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1195
1211
  props: { getEstimatedItemSize, snapToIndices, enableAverages }
1196
1212
  } = state;
1197
1213
  const data = state.props.data;
1214
+ const dataLength = data.length;
1198
1215
  const numColumns = peek$(ctx, "numColumns");
1216
+ const hasColumns = numColumns > 1;
1199
1217
  const indexByKeyForChecking = __DEV__ ? /* @__PURE__ */ new Map() : void 0;
1200
1218
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1201
1219
  const useAverageSize = enableAverages && !getEstimatedItemSize;
1202
1220
  let currentRowTop = 0;
1203
1221
  let column = 1;
1204
1222
  let maxSizeInRow = 0;
1205
- const hasColumns = numColumns > 1;
1206
1223
  if (startIndex > 0) {
1207
- const prevIndex = startIndex - 1;
1208
- const prevId = (_a = idCache.get(prevIndex)) != null ? _a : getId(state, prevIndex);
1209
- const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1210
1224
  if (hasColumns) {
1211
- const prevColumn = (_c = columns.get(prevId)) != null ? _c : 1;
1212
- currentRowTop = prevPosition;
1213
- column = prevColumn % numColumns + 1;
1214
- } else {
1215
- const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1225
+ const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1226
+ ctx,
1227
+ state,
1228
+ startIndex,
1229
+ useAverageSize
1230
+ );
1231
+ startIndex = processedStartIndex;
1232
+ currentRowTop = initialRowTop;
1233
+ } else if (startIndex < dataLength) {
1234
+ const prevIndex = startIndex - 1;
1235
+ const prevId = getId(state, prevIndex);
1236
+ const prevPosition = (_a = positions.get(prevId)) != null ? _a : 0;
1237
+ const prevSize = (_b = sizesKnown.get(prevId)) != null ? _b : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1216
1238
  currentRowTop = prevPosition + prevSize;
1217
1239
  }
1218
1240
  }
1219
1241
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
1220
1242
  let didBreakEarly = false;
1221
1243
  let breakAt;
1222
- const dataLength = data.length;
1223
1244
  for (let i = startIndex; i < dataLength; i++) {
1224
1245
  if (breakAt && i > breakAt) {
1225
1246
  didBreakEarly = true;
1226
1247
  break;
1227
1248
  }
1228
- if (!dataChanged && currentRowTop > maxVisibleArea) {
1229
- breakAt = i + 10;
1249
+ if (breakAt === void 0 && !dataChanged && currentRowTop > maxVisibleArea) {
1250
+ const itemsPerRow = hasColumns ? numColumns : 1;
1251
+ breakAt = i + itemsPerRow + 10;
1230
1252
  }
1231
- const id = (_e = idCache.get(i)) != null ? _e : getId(state, i);
1232
- const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(state, id, i, data[i], useAverageSize);
1253
+ const id = (_c = idCache[i]) != null ? _c : getId(state, i);
1254
+ const size = (_d = sizesKnown.get(id)) != null ? _d : getItemSize(state, id, i, data[i], useAverageSize);
1233
1255
  if (__DEV__ && needsIndexByKey) {
1234
1256
  if (indexByKeyForChecking.has(id)) {
1235
1257
  console.error(
@@ -1466,9 +1488,12 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1466
1488
  var batchedUpdates = unstable_batchedUpdates || ((callback) => callback());
1467
1489
 
1468
1490
  // src/utils/checkAllSizesKnown.ts
1491
+ function isNullOrUndefined2(value) {
1492
+ return value === null || value === void 0;
1493
+ }
1469
1494
  function checkAllSizesKnown(state) {
1470
1495
  const { startBuffered, endBuffered, sizesKnown } = state;
1471
- if (endBuffered !== null) {
1496
+ if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
1472
1497
  let areAllKnown = true;
1473
1498
  for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1474
1499
  const key = getId(state, i);
@@ -1485,6 +1510,8 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1485
1510
  const { stickyContainerPool, containerItemTypes } = state;
1486
1511
  const result = [];
1487
1512
  const availableContainers = [];
1513
+ const pendingRemovalSet = new Set(pendingRemoval);
1514
+ let pendingRemovalChanged = false;
1488
1515
  const stickyIndicesSet = state.props.stickyIndicesSet;
1489
1516
  const stickyItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyIndicesSet.has(index))) || [];
1490
1517
  const canReuseContainer = (containerIndex, requiredType) => {
@@ -1500,12 +1527,11 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1500
1527
  let foundContainer = false;
1501
1528
  for (const containerIndex of stickyContainerPool) {
1502
1529
  const key = peek$(ctx, `containerItemKey${containerIndex}`);
1503
- const isPendingRemoval = pendingRemoval.includes(containerIndex);
1530
+ const isPendingRemoval = pendingRemovalSet.has(containerIndex);
1504
1531
  if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
1505
1532
  result.push(containerIndex);
1506
- if (isPendingRemoval) {
1507
- const index = pendingRemoval.indexOf(containerIndex);
1508
- pendingRemoval.splice(index, 1);
1533
+ if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
1534
+ pendingRemovalChanged = true;
1509
1535
  }
1510
1536
  foundContainer = true;
1511
1537
  if (requiredItemTypes) typeIndex++;
@@ -1525,13 +1551,11 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1525
1551
  }
1526
1552
  const key = peek$(ctx, `containerItemKey${u}`);
1527
1553
  let isOk = key === void 0;
1528
- if (!isOk) {
1529
- const index = pendingRemoval.indexOf(u);
1530
- if (index !== -1) {
1531
- pendingRemoval.splice(index, 1);
1532
- const requiredType = neededTypes[typeIndex];
1533
- isOk = canReuseContainer(u, requiredType);
1534
- }
1554
+ if (!isOk && pendingRemovalSet.has(u)) {
1555
+ pendingRemovalSet.delete(u);
1556
+ pendingRemovalChanged = true;
1557
+ const requiredType = neededTypes[typeIndex];
1558
+ isOk = canReuseContainer(u, requiredType);
1535
1559
  }
1536
1560
  if (isOk) {
1537
1561
  result.push(u);
@@ -1589,6 +1613,12 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1589
1613
  }
1590
1614
  }
1591
1615
  }
1616
+ if (pendingRemovalChanged) {
1617
+ pendingRemoval.length = 0;
1618
+ for (const value of pendingRemovalSet) {
1619
+ pendingRemoval.push(value);
1620
+ }
1621
+ }
1592
1622
  return result.sort(comparatorDefault);
1593
1623
  }
1594
1624
  function comparatorByDistance(a, b) {
@@ -1748,7 +1778,8 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
1748
1778
  const idCache = state.idCache;
1749
1779
  const positions = state.positions;
1750
1780
  for (let i = stickyArray.length - 1; i >= 0; i--) {
1751
- const stickyId = (_a = idCache.get(stickyArray[i])) != null ? _a : getId(state, stickyArray[i]);
1781
+ const stickyIndex = stickyArray[i];
1782
+ const stickyId = (_a = idCache[stickyIndex]) != null ? _a : getId(state, stickyIndex);
1752
1783
  const stickyPos = stickyId ? positions.get(stickyId) : void 0;
1753
1784
  if (stickyPos !== void 0 && scroll >= stickyPos) {
1754
1785
  return i;
@@ -1761,40 +1792,43 @@ function getActiveStickyIndices(ctx, state, stickyIndices) {
1761
1792
  Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyIndices.has(idx))
1762
1793
  );
1763
1794
  }
1764
- function handleStickyActivation(ctx, state, stickyIndices, stickyArray, scroll, needNewContainers, startBuffered, endBuffered) {
1795
+ function handleStickyActivation(ctx, state, stickyIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
1765
1796
  var _a;
1766
1797
  const activeIndices = getActiveStickyIndices(ctx, state, stickyIndices);
1767
- const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1768
1798
  state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
1769
1799
  for (let offset = 0; offset <= 1; offset++) {
1770
1800
  const idx = currentStickyIdx - offset;
1771
1801
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
1772
1802
  const stickyIndex = stickyArray[idx];
1773
- const stickyId = (_a = state.idCache.get(stickyIndex)) != null ? _a : getId(state, stickyIndex);
1803
+ const stickyId = (_a = state.idCache[stickyIndex]) != null ? _a : getId(state, stickyIndex);
1774
1804
  if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
1775
1805
  needNewContainers.push(stickyIndex);
1776
1806
  }
1777
1807
  }
1778
1808
  }
1779
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, pendingRemoval) {
1809
+ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
1780
1810
  var _a, _b, _c;
1781
- const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1782
1811
  for (const containerIndex of state.stickyContainerPool) {
1783
1812
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
1784
1813
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
1785
1814
  if (itemIndex === void 0) continue;
1786
1815
  const arrayIdx = stickyArray.indexOf(itemIndex);
1787
- if (arrayIdx === -1) continue;
1816
+ if (arrayIdx === -1) {
1817
+ state.stickyContainerPool.delete(containerIndex);
1818
+ set$(ctx, `containerSticky${containerIndex}`, false);
1819
+ set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
1820
+ continue;
1821
+ }
1788
1822
  const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
1789
1823
  if (isRecentSticky) continue;
1790
1824
  const nextIndex = stickyArray[arrayIdx + 1];
1791
1825
  let shouldRecycle = false;
1792
1826
  if (nextIndex) {
1793
- const nextId = (_a = state.idCache.get(nextIndex)) != null ? _a : getId(state, nextIndex);
1827
+ const nextId = (_a = state.idCache[nextIndex]) != null ? _a : getId(state, nextIndex);
1794
1828
  const nextPos = nextId ? state.positions.get(nextId) : void 0;
1795
1829
  shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
1796
1830
  } else {
1797
- const currentId = (_b = state.idCache.get(itemIndex)) != null ? _b : getId(state, itemIndex);
1831
+ const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
1798
1832
  if (currentId) {
1799
1833
  const currentPos = state.positions.get(currentId);
1800
1834
  const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
@@ -1822,7 +1856,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1822
1856
  sizes,
1823
1857
  startBufferedId: startBufferedIdOrig,
1824
1858
  viewabilityConfigCallbackPairs,
1825
- props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, scrollBuffer }
1859
+ props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer }
1826
1860
  } = state;
1827
1861
  const { data } = state.props;
1828
1862
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
@@ -1857,6 +1891,10 @@ function calculateItemsInView(ctx, state, params = {}) {
1857
1891
  set$(ctx, "debugRawScroll", scrollState);
1858
1892
  set$(ctx, "debugComputedScroll", scroll);
1859
1893
  }
1894
+ const previousStickyIndex = state.activeStickyIndex;
1895
+ const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
1896
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
1897
+ state.activeStickyIndex = nextActiveStickyIndex;
1860
1898
  let scrollBufferTop = scrollBuffer;
1861
1899
  let scrollBufferBottom = scrollBuffer;
1862
1900
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -1878,7 +1916,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1878
1916
  const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
1879
1917
  if (dataChanged) {
1880
1918
  indexByKey.clear();
1881
- idCache.clear();
1919
+ idCache.length = 0;
1882
1920
  positions.clear();
1883
1921
  }
1884
1922
  const startIndex = dataChanged ? 0 : (_a = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _a : 0;
@@ -1894,7 +1932,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1894
1932
  let endBuffered = null;
1895
1933
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
1896
1934
  for (let i = loopStart; i >= 0; i--) {
1897
- const id = (_b = idCache.get(i)) != null ? _b : getId(state, i);
1935
+ const id = (_b = idCache[i]) != null ? _b : getId(state, i);
1898
1936
  const top = positions.get(id);
1899
1937
  const size = (_c = sizes.get(id)) != null ? _c : getItemSize(state, id, i, data[i]);
1900
1938
  const bottom = top + size;
@@ -1922,7 +1960,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1922
1960
  let firstFullyOnScreenIndex;
1923
1961
  const dataLength = data.length;
1924
1962
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
1925
- const id = (_d = idCache.get(i)) != null ? _d : getId(state, i);
1963
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1926
1964
  const size = (_e = sizes.get(id)) != null ? _e : getItemSize(state, id, i, data[i]);
1927
1965
  const top = positions.get(id);
1928
1966
  if (!foundEnd) {
@@ -1952,7 +1990,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1952
1990
  }
1953
1991
  const idsInView = [];
1954
1992
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
1955
- const id = (_f = idCache.get(i)) != null ? _f : getId(state, i);
1993
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
1956
1994
  idsInView.push(id);
1957
1995
  }
1958
1996
  Object.assign(state, {
@@ -1984,7 +2022,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1984
2022
  let numContainers2 = prevNumContainers;
1985
2023
  const needNewContainers = [];
1986
2024
  for (let i = startBuffered; i <= endBuffered; i++) {
1987
- const id = (_g = idCache.get(i)) != null ? _g : getId(state, i);
2025
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
1988
2026
  if (!containerItemKeys.has(id)) {
1989
2027
  needNewContainers.push(i);
1990
2028
  }
@@ -1995,7 +2033,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1995
2033
  state,
1996
2034
  stickyIndicesSet,
1997
2035
  stickyIndicesArr,
1998
- scroll,
2036
+ currentStickyIdx,
1999
2037
  needNewContainers,
2000
2038
  startBuffered,
2001
2039
  endBuffered
@@ -2021,7 +2059,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2021
2059
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2022
2060
  const i = needNewContainers[idx];
2023
2061
  const containerIndex = availableContainers[idx];
2024
- const id = (_h = idCache.get(i)) != null ? _h : getId(state, i);
2062
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2025
2063
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2026
2064
  if (oldKey && oldKey !== id) {
2027
2065
  containerItemKeys.delete(oldKey);
@@ -2035,7 +2073,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2035
2073
  if (stickyIndicesSet.has(i)) {
2036
2074
  set$(ctx, `containerSticky${containerIndex}`, true);
2037
2075
  const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2038
- set$(ctx, `containerStickyOffset${containerIndex}`, new Animated.Value(topPadding));
2076
+ set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2039
2077
  state.stickyContainerPool.add(containerIndex);
2040
2078
  } else {
2041
2079
  set$(ctx, `containerSticky${containerIndex}`, false);
@@ -2054,7 +2092,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2054
2092
  }
2055
2093
  }
2056
2094
  if (stickyIndicesArr.length > 0) {
2057
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, pendingRemoval);
2095
+ handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2058
2096
  }
2059
2097
  for (let i = 0; i < numContainers; i++) {
2060
2098
  const itemKey = peek$(ctx, `containerItemKey${i}`);
@@ -2076,7 +2114,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2076
2114
  const itemIndex = indexByKey.get(itemKey);
2077
2115
  const item = data[itemIndex];
2078
2116
  if (item !== void 0) {
2079
- const id = (_i = idCache.get(itemIndex)) != null ? _i : getId(state, itemIndex);
2117
+ const id = (_i = idCache[itemIndex]) != null ? _i : getId(state, itemIndex);
2080
2118
  const position = positions.get(id);
2081
2119
  if (position === void 0) {
2082
2120
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -2106,6 +2144,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2106
2144
  if (viewabilityConfigCallbackPairs) {
2107
2145
  updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
2108
2146
  }
2147
+ if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
2148
+ const item = data[nextActiveStickyIndex];
2149
+ if (item !== void 0) {
2150
+ onStickyHeaderChange({ index: nextActiveStickyIndex, item });
2151
+ }
2152
+ }
2109
2153
  });
2110
2154
  }
2111
2155
 
@@ -2264,7 +2308,9 @@ function doInitialAllocateContainers(ctx, state) {
2264
2308
  let totalSize = 0;
2265
2309
  const num = Math.min(20, data.length);
2266
2310
  for (let i = 0; i < num; i++) {
2267
- totalSize += fn(0, data[0], getItemType ? (_a = getItemType(data[0], 0)) != null ? _a : "" : "");
2311
+ const item = data[i];
2312
+ const itemType = getItemType ? (_a = getItemType(item, i)) != null ? _a : "" : "";
2313
+ totalSize += fn(i, item, itemType);
2268
2314
  }
2269
2315
  averageItemSize = totalSize / num;
2270
2316
  } else {
@@ -2293,7 +2339,9 @@ function doInitialAllocateContainers(ctx, state) {
2293
2339
  // src/core/handleLayout.ts
2294
2340
  function handleLayout(ctx, state, layout, setCanRender) {
2295
2341
  const { maintainScrollAtEnd } = state.props;
2296
- const scrollLength = layout[state.props.horizontal ? "width" : "height"];
2342
+ const measuredLength = layout[state.props.horizontal ? "width" : "height"];
2343
+ const previousLength = state.scrollLength;
2344
+ const scrollLength = measuredLength > 0 ? measuredLength : previousLength;
2297
2345
  const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
2298
2346
  const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
2299
2347
  state.lastLayout = layout;
@@ -2304,7 +2352,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2304
2352
  state.otherAxisSize = otherAxisSize;
2305
2353
  state.lastBatchingAction = Date.now();
2306
2354
  state.scrollForNextCalculateItemsInView = void 0;
2307
- doInitialAllocateContainers(ctx, state);
2355
+ if (scrollLength > 0) {
2356
+ doInitialAllocateContainers(ctx, state);
2357
+ }
2308
2358
  if (needsCalculate) {
2309
2359
  calculateItemsInView(ctx, state, { doMVCP: true });
2310
2360
  }
@@ -2320,14 +2370,14 @@ function handleLayout(ctx, state, layout, setCanRender) {
2320
2370
  if (state) {
2321
2371
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2322
2372
  }
2323
- if (__DEV__ && scrollLength === 0) {
2373
+ if (__DEV__ && measuredLength === 0) {
2324
2374
  warnDevOnce(
2325
2375
  "height0",
2326
2376
  `List ${state.props.horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
2327
2377
  );
2328
2378
  }
2329
- setCanRender(true);
2330
2379
  }
2380
+ setCanRender(true);
2331
2381
  }
2332
2382
 
2333
2383
  // src/core/onScroll.ts
@@ -2577,18 +2627,59 @@ function getRenderedItem(ctx, state, key) {
2577
2627
  return null;
2578
2628
  }
2579
2629
  let renderedItem = null;
2580
- if (renderItem && data[index]) {
2630
+ const extraData = peek$(ctx, "extraData");
2631
+ const item = data[index];
2632
+ if (renderItem && !isNullOrUndefined(item)) {
2581
2633
  const itemProps = {
2582
2634
  data,
2583
- extraData: peek$(ctx, "extraData"),
2635
+ extraData,
2584
2636
  index,
2585
- item: data[index],
2586
- type: getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : ""
2637
+ item,
2638
+ type: getItemType ? (_a = getItemType(item, index)) != null ? _a : "" : ""
2587
2639
  };
2588
2640
  renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React2__default.createElement(renderItem, itemProps);
2589
2641
  }
2590
2642
  return { index, item: data[index], renderedItem };
2591
2643
  }
2644
+ function useThrottleDebounce(mode) {
2645
+ const timeoutRef = useRef(null);
2646
+ const lastCallTimeRef = useRef(0);
2647
+ const lastArgsRef = useRef(null);
2648
+ const clearTimeoutRef = () => {
2649
+ if (timeoutRef.current) {
2650
+ clearTimeout(timeoutRef.current);
2651
+ timeoutRef.current = null;
2652
+ }
2653
+ };
2654
+ const execute = useCallback(
2655
+ (callback, delay, ...args) => {
2656
+ {
2657
+ const now = Date.now();
2658
+ lastArgsRef.current = args;
2659
+ if (now - lastCallTimeRef.current >= delay) {
2660
+ lastCallTimeRef.current = now;
2661
+ callback(...args);
2662
+ clearTimeoutRef();
2663
+ } else {
2664
+ clearTimeoutRef();
2665
+ timeoutRef.current = setTimeout(
2666
+ () => {
2667
+ if (lastArgsRef.current) {
2668
+ lastCallTimeRef.current = Date.now();
2669
+ callback(...lastArgsRef.current);
2670
+ timeoutRef.current = null;
2671
+ lastArgsRef.current = null;
2672
+ }
2673
+ },
2674
+ delay - (now - lastCallTimeRef.current)
2675
+ );
2676
+ }
2677
+ }
2678
+ },
2679
+ [mode]
2680
+ );
2681
+ return execute;
2682
+ }
2592
2683
 
2593
2684
  // src/utils/throttledOnScroll.ts
2594
2685
  function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
@@ -2652,6 +2743,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2652
2743
  onScroll: onScrollProp,
2653
2744
  onStartReached,
2654
2745
  onStartReachedThreshold = 0.5,
2746
+ onStickyHeaderChange,
2655
2747
  onViewableItemsChanged,
2656
2748
  progressViewOffset,
2657
2749
  recycleItems = false,
@@ -2699,7 +2791,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2699
2791
  endNoBuffer: -1,
2700
2792
  endReachedBlockedByTimer: false,
2701
2793
  firstFullyOnScreenIndex: -1,
2702
- idCache: /* @__PURE__ */ new Map(),
2794
+ idCache: [],
2703
2795
  idsInView: [],
2704
2796
  indexByKey: /* @__PURE__ */ new Map(),
2705
2797
  initialScroll,
@@ -2774,6 +2866,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2774
2866
  onScroll: throttleScrollFn,
2775
2867
  onStartReached,
2776
2868
  onStartReachedThreshold,
2869
+ onStickyHeaderChange,
2777
2870
  recycleItems: !!recycleItems,
2778
2871
  renderItem,
2779
2872
  scrollBuffer,
@@ -2868,7 +2961,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2868
2961
  }
2869
2962
  }, [snapToIndices]);
2870
2963
  useLayoutEffect(() => {
2871
- const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainersCallback();
2964
+ const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainers(ctx, state);
2872
2965
  if (!didAllocateContainers) {
2873
2966
  checkResetContainers(
2874
2967
  ctx,
@@ -2882,30 +2975,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2882
2975
  useLayoutEffect(() => {
2883
2976
  set$(ctx, "extraData", extraData);
2884
2977
  }, [extraData]);
2885
- useLayoutEffect(() => {
2886
- var _a2;
2887
- if (IsNewArchitecture) {
2888
- let measured;
2889
- (_a2 = refScroller.current) == null ? void 0 : _a2.measure((x, y, width, height) => {
2890
- measured = { height, width, x, y };
2891
- });
2892
- if (measured) {
2893
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2894
- if (size) {
2895
- handleLayout(ctx, state, measured, setCanRender);
2896
- }
2897
- }
2898
- }
2899
- }, []);
2900
2978
  useLayoutEffect(initializeStateVars, [
2901
2979
  memoizedLastItemKeys.join(","),
2902
2980
  numColumnsProp,
2903
2981
  stylePaddingTopState,
2904
2982
  stylePaddingBottomState
2905
2983
  ]);
2906
- const doInitialAllocateContainersCallback = () => {
2907
- return doInitialAllocateContainers(ctx, state);
2908
- };
2909
2984
  useEffect(() => {
2910
2985
  const viewability = setupViewability({
2911
2986
  onViewableItemsChanged,
@@ -2917,16 +2992,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2917
2992
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
2918
2993
  if (!IsNewArchitecture) {
2919
2994
  useInit(() => {
2920
- doInitialAllocateContainersCallback();
2995
+ doInitialAllocateContainers(ctx, state);
2921
2996
  });
2922
2997
  }
2923
- const onLayout = useCallback((event) => {
2924
- const layout = event.nativeEvent.layout;
2998
+ const onLayoutChange = useCallback((layout) => {
2925
2999
  handleLayout(ctx, state, layout, setCanRender);
2926
- if (onLayoutProp) {
2927
- onLayoutProp(event);
2928
- }
2929
3000
  }, []);
3001
+ const { onLayout } = useOnLayoutSync({
3002
+ onLayoutChange,
3003
+ onLayoutProp,
3004
+ ref: refScroller
3005
+ // the type of ScrollView doesn't include measure?
3006
+ });
2930
3007
  useImperativeHandle(forwardedRef, () => {
2931
3008
  const scrollIndexIntoView = (options) => {
2932
3009
  const state2 = refState.current;