@legendapp/list 0.5.5 → 0.5.6

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.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { ComponentProps, ReactNode, ForwardedRef, ReactElement } from 'react';
2
2
  import { ScrollView, StyleProp, ViewStyle, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
3
3
 
4
- type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset' | 'contentInset' | 'maintainVisibleContentPosition' | 'stickyHeaderIndices'> & {
5
- data: ArrayLike<any> & T[];
4
+ type LegendListProps<ItemT> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset' | 'contentInset' | 'maintainVisibleContentPosition' | 'stickyHeaderIndices'> & {
5
+ data: ArrayLike<any> & ItemT[];
6
6
  initialScrollOffset?: number;
7
7
  initialScrollIndex?: number;
8
8
  drawDistance?: number;
@@ -15,16 +15,16 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
15
15
  alignItemsAtEnd?: boolean;
16
16
  maintainVisibleContentPosition?: boolean;
17
17
  numColumns?: number;
18
- estimatedItemSize: number;
19
- getEstimatedItemSize?: (index: number, item: T) => number;
20
- onEndReached?: ((info: {
21
- distanceFromEnd: number;
22
- }) => void) | null | undefined;
18
+ estimatedItemSize?: number;
19
+ getEstimatedItemSize?: (index: number, item: ItemT) => number;
23
20
  onStartReached?: ((info: {
24
21
  distanceFromStart: number;
25
22
  }) => void) | null | undefined;
26
- keyExtractor?: (item: T, index: number) => string;
27
- renderItem?: (props: LegendListRenderItemProps<T>) => ReactNode;
23
+ onEndReached?: ((info: {
24
+ distanceFromEnd: number;
25
+ }) => void) | null | undefined;
26
+ keyExtractor?: (item: ItemT, index: number) => string;
27
+ renderItem?: (props: LegendListRenderItemProps<ItemT>) => ReactNode;
28
28
  ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
29
29
  ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
30
30
  ListFooterComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
@@ -40,6 +40,7 @@ interface InternalState {
40
40
  positions: Map<string, number>;
41
41
  columns: Map<string, number>;
42
42
  sizes: Map<string, number>;
43
+ sizesLaidOut: Map<string, number> | undefined;
43
44
  pendingAdjust: number;
44
45
  animFrameLayout: any;
45
46
  animFrameTotalSize: number | null;
@@ -63,6 +64,7 @@ interface InternalState {
63
64
  scrollAdjustPending: number;
64
65
  totalSize: number;
65
66
  timeouts: Set<number>;
67
+ timeoutSizeMessage: any;
66
68
  nativeMarginTop: number;
67
69
  indexByKey: Map<string, number>;
68
70
  contentSize: {
@@ -89,7 +91,7 @@ interface LegendListRenderItemProps<ItemT> {
89
91
  useViewability: (configId: string, callback: ViewabilityCallback) => void;
90
92
  useViewabilityAmount: (callback: ViewabilityAmountCallback) => void;
91
93
  useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
92
- useRecyclingState: <T>(updateState: (info: LegendListRecyclingState<ItemT>) => T) => [T, React.Dispatch<T>];
94
+ useRecyclingState: <T>(updateState: ((info: LegendListRecyclingState<ItemT>) => T) | T) => [T, React.Dispatch<T>];
93
95
  }
94
96
  type LegendListRef = {
95
97
  /**
package/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { ComponentProps, ReactNode, ForwardedRef, ReactElement } from 'react';
2
2
  import { ScrollView, StyleProp, ViewStyle, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
3
3
 
4
- type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset' | 'contentInset' | 'maintainVisibleContentPosition' | 'stickyHeaderIndices'> & {
5
- data: ArrayLike<any> & T[];
4
+ type LegendListProps<ItemT> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset' | 'contentInset' | 'maintainVisibleContentPosition' | 'stickyHeaderIndices'> & {
5
+ data: ArrayLike<any> & ItemT[];
6
6
  initialScrollOffset?: number;
7
7
  initialScrollIndex?: number;
8
8
  drawDistance?: number;
@@ -15,16 +15,16 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
15
15
  alignItemsAtEnd?: boolean;
16
16
  maintainVisibleContentPosition?: boolean;
17
17
  numColumns?: number;
18
- estimatedItemSize: number;
19
- getEstimatedItemSize?: (index: number, item: T) => number;
20
- onEndReached?: ((info: {
21
- distanceFromEnd: number;
22
- }) => void) | null | undefined;
18
+ estimatedItemSize?: number;
19
+ getEstimatedItemSize?: (index: number, item: ItemT) => number;
23
20
  onStartReached?: ((info: {
24
21
  distanceFromStart: number;
25
22
  }) => void) | null | undefined;
26
- keyExtractor?: (item: T, index: number) => string;
27
- renderItem?: (props: LegendListRenderItemProps<T>) => ReactNode;
23
+ onEndReached?: ((info: {
24
+ distanceFromEnd: number;
25
+ }) => void) | null | undefined;
26
+ keyExtractor?: (item: ItemT, index: number) => string;
27
+ renderItem?: (props: LegendListRenderItemProps<ItemT>) => ReactNode;
28
28
  ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
29
29
  ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
30
30
  ListFooterComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
@@ -40,6 +40,7 @@ interface InternalState {
40
40
  positions: Map<string, number>;
41
41
  columns: Map<string, number>;
42
42
  sizes: Map<string, number>;
43
+ sizesLaidOut: Map<string, number> | undefined;
43
44
  pendingAdjust: number;
44
45
  animFrameLayout: any;
45
46
  animFrameTotalSize: number | null;
@@ -63,6 +64,7 @@ interface InternalState {
63
64
  scrollAdjustPending: number;
64
65
  totalSize: number;
65
66
  timeouts: Set<number>;
67
+ timeoutSizeMessage: any;
66
68
  nativeMarginTop: number;
67
69
  indexByKey: Map<string, number>;
68
70
  contentSize: {
@@ -89,7 +91,7 @@ interface LegendListRenderItemProps<ItemT> {
89
91
  useViewability: (configId: string, callback: ViewabilityCallback) => void;
90
92
  useViewabilityAmount: (callback: ViewabilityAmountCallback) => void;
91
93
  useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
92
- useRecyclingState: <T>(updateState: (info: LegendListRecyclingState<ItemT>) => T) => [T, React.Dispatch<T>];
94
+ useRecyclingState: <T>(updateState: ((info: LegendListRecyclingState<ItemT>) => T) | T) => [T, React.Dispatch<T>];
93
95
  }
94
96
  type LegendListRef = {
95
97
  /**
package/index.js CHANGED
@@ -474,6 +474,7 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
474
474
  var DEFAULT_DRAW_DISTANCE = 250;
475
475
  var INITIAL_SCROLL_ADJUST = 1e4;
476
476
  var POSITION_OUT_OF_VIEW = -1e7;
477
+ var DEFAULT_ITEM_SIZE = 100;
477
478
  var LegendList = React7.forwardRef(function LegendList2(props, forwardedRef) {
478
479
  return /* @__PURE__ */ React7__namespace.createElement(StateProvider, null, /* @__PURE__ */ React7__namespace.createElement(LegendListInner, { ...props, ref: forwardedRef }));
479
480
  });
@@ -495,6 +496,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
495
496
  maintainVisibleContentPosition = false,
496
497
  onScroll: onScrollProp,
497
498
  numColumns: numColumnsProp = 1,
499
+ style: styleProp,
498
500
  keyExtractor,
499
501
  renderItem,
500
502
  estimatedItemSize,
@@ -504,7 +506,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
504
506
  ListEmptyComponent,
505
507
  ...rest
506
508
  } = props;
507
- const { style, contentContainerStyle } = rest;
509
+ const { contentContainerStyle } = props;
508
510
  const ctx = useStateContext();
509
511
  const internalRef = React7.useRef(null);
510
512
  const refScroller = internalRef;
@@ -520,11 +522,12 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
520
522
  return `${ret}`;
521
523
  };
522
524
  const getItemSize = (key, index, data2) => {
525
+ var _a2;
523
526
  const sizeKnown = refState.current.sizes.get(key);
524
527
  if (sizeKnown !== void 0) {
525
528
  return sizeKnown;
526
529
  }
527
- const size = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
530
+ const size = (_a2 = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize) != null ? _a2 : DEFAULT_ITEM_SIZE;
528
531
  refState.current.sizes.set(key, size);
529
532
  return size;
530
533
  };
@@ -576,7 +579,9 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
576
579
  indexByKey: /* @__PURE__ */ new Map(),
577
580
  scrollHistory: [],
578
581
  scrollVelocity: 0,
579
- contentSize: { width: 0, height: 0 }
582
+ contentSize: { width: 0, height: 0 },
583
+ sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
584
+ timeoutSizeMessage: 0
580
585
  };
581
586
  refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
582
587
  set$(ctx, "scrollAdjust", refState.current.scrollAdjustPending);
@@ -622,6 +627,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
622
627
  scroll: scrollState,
623
628
  startBuffered: startBufferedState,
624
629
  positions,
630
+ sizes,
625
631
  columns
626
632
  } = state;
627
633
  if (state.animFrameLayout) {
@@ -638,6 +644,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
638
644
  0,
639
645
  scrollState - topPad - (USE_CONTENT_INSET ? scrollAdjustPending : 0) + scrollExtra
640
646
  );
647
+ const scrollBottom = scroll + scrollLength;
641
648
  let startNoBuffer = null;
642
649
  let startBuffered = null;
643
650
  let endNoBuffer = null;
@@ -683,10 +690,10 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
683
690
  startBuffered = i;
684
691
  }
685
692
  if (startNoBuffer !== null) {
686
- if (top <= scroll + scrollLength) {
693
+ if (top <= scrollBottom) {
687
694
  endNoBuffer = i;
688
695
  }
689
- if (top <= scroll + scrollLength + scrollBuffer) {
696
+ if (top <= scrollBottom + scrollBuffer) {
690
697
  endBuffered = i;
691
698
  } else {
692
699
  break;
@@ -767,7 +774,14 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
767
774
  const item = data2[itemIndex];
768
775
  if (item) {
769
776
  const id = getId(itemIndex);
770
- if (!(itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered)) {
777
+ if (itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered) {
778
+ const prevPos = peek$(ctx, `containerPosition${i}`) - scrollAdjustPending;
779
+ const pos = positions.get(id) || 0;
780
+ const size = sizes.get(id) || 0;
781
+ if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
782
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
783
+ }
784
+ } else {
771
785
  const pos = (positions.get(id) || 0) + scrollAdjustPending;
772
786
  const column2 = columns.get(id) || 1;
773
787
  const prevPos = peek$(ctx, `containerPosition${i}`);
@@ -794,6 +808,18 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
794
808
  );
795
809
  }
796
810
  }, []);
811
+ const style = React7.useMemo(() => {
812
+ const extraStyle = {};
813
+ if (data.length > 0) {
814
+ const size = getItemSize(getId(0), 0, data[0]);
815
+ if (horizontal) {
816
+ extraStyle.minHeight = size;
817
+ } else {
818
+ extraStyle.minWidth = size;
819
+ }
820
+ }
821
+ return reactNative.StyleSheet.compose(styleProp, extraStyle);
822
+ }, []);
797
823
  const doUpdatePaddingTop = () => {
798
824
  if (alignItemsAtEnd) {
799
825
  const { scrollLength, totalSize } = refState.current;
@@ -859,6 +885,10 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
859
885
  };
860
886
  const isFirst = !refState.current.renderItem;
861
887
  if (isFirst || data !== refState.current.data || numColumnsProp !== peek$(ctx, "numColumns")) {
888
+ if (!keyExtractor && !isFirst && data !== refState.current.data) {
889
+ refState.current.sizes.clear();
890
+ refState.current.positions.clear();
891
+ }
862
892
  refState.current.data = data;
863
893
  let totalSize = 0;
864
894
  const indexByKey = /* @__PURE__ */ new Map();
@@ -900,6 +930,10 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
900
930
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
901
931
  set$(ctx, `containerColumn${i}`, -1);
902
932
  }
933
+ if (!keyExtractor) {
934
+ refState.current.sizes.clear();
935
+ refState.current.positions;
936
+ }
903
937
  calculateItemsInView();
904
938
  doMaintainScrollAtEnd(false);
905
939
  checkAtTop();
@@ -984,17 +1018,17 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
984
1018
  listen$(ctx, signal, run);
985
1019
  }, []);
986
1020
  };
987
- const useRecyclingState = (updateState) => {
1021
+ const useRecyclingState = (valueOrFun) => {
988
1022
  const stateInfo = React7.useState(
989
- () => updateState({
1023
+ () => typeof valueOrFun === "function" ? valueOrFun({
990
1024
  index,
991
1025
  item: refState.current.data[index],
992
1026
  prevIndex: void 0,
993
1027
  prevItem: void 0
994
- })
1028
+ }) : valueOrFun
995
1029
  );
996
1030
  useRecyclingEffect((state2) => {
997
- const newState = updateState(state2);
1031
+ const newState = typeof valueOrFun === "function" ? valueOrFun(state2) : valueOrFun;
998
1032
  stateInfo[1](newState);
999
1033
  });
1000
1034
  return stateInfo;
@@ -1010,9 +1044,10 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
1010
1044
  return renderedItem;
1011
1045
  }, []);
1012
1046
  useInit(() => {
1047
+ var _a2;
1013
1048
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
1014
1049
  const scrollLength = refState.current.scrollLength;
1015
- const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
1050
+ const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1016
1051
  const numContainers = (initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize)) * numColumnsProp;
1017
1052
  for (let i = 0; i < numContainers; i++) {
1018
1053
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -1028,7 +1063,8 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
1028
1063
  if (!data2) {
1029
1064
  return;
1030
1065
  }
1031
- const { sizes, indexByKey, idsInFirstRender, columns } = refState.current;
1066
+ const state = refState.current;
1067
+ const { sizes, indexByKey, idsInFirstRender, columns, sizesLaidOut } = state;
1032
1068
  const index = indexByKey.get(key);
1033
1069
  const wasInFirstRender = idsInFirstRender.has(key);
1034
1070
  const prevSize = sizes.get(key) || (wasInFirstRender ? getItemSize(key, index, data2[index]) : 0);
@@ -1056,9 +1092,27 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
1056
1092
  sizes.set(key, size);
1057
1093
  diff = size - prevSize;
1058
1094
  }
1095
+ if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
1096
+ sizesLaidOut.set(key, size);
1097
+ if (state.timeoutSizeMessage) {
1098
+ clearTimeout(state.timeoutSizeMessage);
1099
+ }
1100
+ state.timeoutSizeMessage = setTimeout(() => {
1101
+ state.timeoutSizeMessage = void 0;
1102
+ let total = 0;
1103
+ let num = 0;
1104
+ for (const [key2, size2] of sizesLaidOut) {
1105
+ num++;
1106
+ total += size2;
1107
+ }
1108
+ const avg = Math.round(total / num);
1109
+ console.warn(
1110
+ `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
1111
+ );
1112
+ }, 1e3);
1113
+ }
1059
1114
  addTotalSize(key, diff);
1060
1115
  doMaintainScrollAtEnd(true);
1061
- const state = refState.current;
1062
1116
  const scrollVelocity = state.scrollVelocity;
1063
1117
  if (!state.animFrameLayout && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1064
1118
  state.animFrameLayout = requestAnimationFrame(() => {
@@ -1176,7 +1230,8 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
1176
1230
  recycleItems,
1177
1231
  alignItemsAtEnd,
1178
1232
  addTotalSize,
1179
- ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0
1233
+ ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
1234
+ style
1180
1235
  }
1181
1236
  );
1182
1237
  });
package/index.mjs CHANGED
@@ -453,6 +453,7 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
453
453
  var DEFAULT_DRAW_DISTANCE = 250;
454
454
  var INITIAL_SCROLL_ADJUST = 1e4;
455
455
  var POSITION_OUT_OF_VIEW = -1e7;
456
+ var DEFAULT_ITEM_SIZE = 100;
456
457
  var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
457
458
  return /* @__PURE__ */ React7.createElement(StateProvider, null, /* @__PURE__ */ React7.createElement(LegendListInner, { ...props, ref: forwardedRef }));
458
459
  });
@@ -474,6 +475,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
474
475
  maintainVisibleContentPosition = false,
475
476
  onScroll: onScrollProp,
476
477
  numColumns: numColumnsProp = 1,
478
+ style: styleProp,
477
479
  keyExtractor,
478
480
  renderItem,
479
481
  estimatedItemSize,
@@ -483,7 +485,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
483
485
  ListEmptyComponent,
484
486
  ...rest
485
487
  } = props;
486
- const { style, contentContainerStyle } = rest;
488
+ const { contentContainerStyle } = props;
487
489
  const ctx = useStateContext();
488
490
  const internalRef = useRef(null);
489
491
  const refScroller = internalRef;
@@ -499,11 +501,12 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
499
501
  return `${ret}`;
500
502
  };
501
503
  const getItemSize = (key, index, data2) => {
504
+ var _a2;
502
505
  const sizeKnown = refState.current.sizes.get(key);
503
506
  if (sizeKnown !== void 0) {
504
507
  return sizeKnown;
505
508
  }
506
- const size = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
509
+ const size = (_a2 = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize) != null ? _a2 : DEFAULT_ITEM_SIZE;
507
510
  refState.current.sizes.set(key, size);
508
511
  return size;
509
512
  };
@@ -555,7 +558,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
555
558
  indexByKey: /* @__PURE__ */ new Map(),
556
559
  scrollHistory: [],
557
560
  scrollVelocity: 0,
558
- contentSize: { width: 0, height: 0 }
561
+ contentSize: { width: 0, height: 0 },
562
+ sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
563
+ timeoutSizeMessage: 0
559
564
  };
560
565
  refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
561
566
  set$(ctx, "scrollAdjust", refState.current.scrollAdjustPending);
@@ -601,6 +606,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
601
606
  scroll: scrollState,
602
607
  startBuffered: startBufferedState,
603
608
  positions,
609
+ sizes,
604
610
  columns
605
611
  } = state;
606
612
  if (state.animFrameLayout) {
@@ -617,6 +623,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
617
623
  0,
618
624
  scrollState - topPad - (USE_CONTENT_INSET ? scrollAdjustPending : 0) + scrollExtra
619
625
  );
626
+ const scrollBottom = scroll + scrollLength;
620
627
  let startNoBuffer = null;
621
628
  let startBuffered = null;
622
629
  let endNoBuffer = null;
@@ -662,10 +669,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
662
669
  startBuffered = i;
663
670
  }
664
671
  if (startNoBuffer !== null) {
665
- if (top <= scroll + scrollLength) {
672
+ if (top <= scrollBottom) {
666
673
  endNoBuffer = i;
667
674
  }
668
- if (top <= scroll + scrollLength + scrollBuffer) {
675
+ if (top <= scrollBottom + scrollBuffer) {
669
676
  endBuffered = i;
670
677
  } else {
671
678
  break;
@@ -746,7 +753,14 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
746
753
  const item = data2[itemIndex];
747
754
  if (item) {
748
755
  const id = getId(itemIndex);
749
- if (!(itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered)) {
756
+ if (itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered) {
757
+ const prevPos = peek$(ctx, `containerPosition${i}`) - scrollAdjustPending;
758
+ const pos = positions.get(id) || 0;
759
+ const size = sizes.get(id) || 0;
760
+ if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
761
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
762
+ }
763
+ } else {
750
764
  const pos = (positions.get(id) || 0) + scrollAdjustPending;
751
765
  const column2 = columns.get(id) || 1;
752
766
  const prevPos = peek$(ctx, `containerPosition${i}`);
@@ -773,6 +787,18 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
773
787
  );
774
788
  }
775
789
  }, []);
790
+ const style = useMemo(() => {
791
+ const extraStyle = {};
792
+ if (data.length > 0) {
793
+ const size = getItemSize(getId(0), 0, data[0]);
794
+ if (horizontal) {
795
+ extraStyle.minHeight = size;
796
+ } else {
797
+ extraStyle.minWidth = size;
798
+ }
799
+ }
800
+ return StyleSheet.compose(styleProp, extraStyle);
801
+ }, []);
776
802
  const doUpdatePaddingTop = () => {
777
803
  if (alignItemsAtEnd) {
778
804
  const { scrollLength, totalSize } = refState.current;
@@ -838,6 +864,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
838
864
  };
839
865
  const isFirst = !refState.current.renderItem;
840
866
  if (isFirst || data !== refState.current.data || numColumnsProp !== peek$(ctx, "numColumns")) {
867
+ if (!keyExtractor && !isFirst && data !== refState.current.data) {
868
+ refState.current.sizes.clear();
869
+ refState.current.positions.clear();
870
+ }
841
871
  refState.current.data = data;
842
872
  let totalSize = 0;
843
873
  const indexByKey = /* @__PURE__ */ new Map();
@@ -879,6 +909,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
879
909
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
880
910
  set$(ctx, `containerColumn${i}`, -1);
881
911
  }
912
+ if (!keyExtractor) {
913
+ refState.current.sizes.clear();
914
+ refState.current.positions;
915
+ }
882
916
  calculateItemsInView();
883
917
  doMaintainScrollAtEnd(false);
884
918
  checkAtTop();
@@ -963,17 +997,17 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
963
997
  listen$(ctx, signal, run);
964
998
  }, []);
965
999
  };
966
- const useRecyclingState = (updateState) => {
1000
+ const useRecyclingState = (valueOrFun) => {
967
1001
  const stateInfo = useState(
968
- () => updateState({
1002
+ () => typeof valueOrFun === "function" ? valueOrFun({
969
1003
  index,
970
1004
  item: refState.current.data[index],
971
1005
  prevIndex: void 0,
972
1006
  prevItem: void 0
973
- })
1007
+ }) : valueOrFun
974
1008
  );
975
1009
  useRecyclingEffect((state2) => {
976
- const newState = updateState(state2);
1010
+ const newState = typeof valueOrFun === "function" ? valueOrFun(state2) : valueOrFun;
977
1011
  stateInfo[1](newState);
978
1012
  });
979
1013
  return stateInfo;
@@ -989,9 +1023,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
989
1023
  return renderedItem;
990
1024
  }, []);
991
1025
  useInit(() => {
1026
+ var _a2;
992
1027
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
993
1028
  const scrollLength = refState.current.scrollLength;
994
- const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
1029
+ const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
995
1030
  const numContainers = (initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize)) * numColumnsProp;
996
1031
  for (let i = 0; i < numContainers; i++) {
997
1032
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -1007,7 +1042,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1007
1042
  if (!data2) {
1008
1043
  return;
1009
1044
  }
1010
- const { sizes, indexByKey, idsInFirstRender, columns } = refState.current;
1045
+ const state = refState.current;
1046
+ const { sizes, indexByKey, idsInFirstRender, columns, sizesLaidOut } = state;
1011
1047
  const index = indexByKey.get(key);
1012
1048
  const wasInFirstRender = idsInFirstRender.has(key);
1013
1049
  const prevSize = sizes.get(key) || (wasInFirstRender ? getItemSize(key, index, data2[index]) : 0);
@@ -1035,9 +1071,27 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1035
1071
  sizes.set(key, size);
1036
1072
  diff = size - prevSize;
1037
1073
  }
1074
+ if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
1075
+ sizesLaidOut.set(key, size);
1076
+ if (state.timeoutSizeMessage) {
1077
+ clearTimeout(state.timeoutSizeMessage);
1078
+ }
1079
+ state.timeoutSizeMessage = setTimeout(() => {
1080
+ state.timeoutSizeMessage = void 0;
1081
+ let total = 0;
1082
+ let num = 0;
1083
+ for (const [key2, size2] of sizesLaidOut) {
1084
+ num++;
1085
+ total += size2;
1086
+ }
1087
+ const avg = Math.round(total / num);
1088
+ console.warn(
1089
+ `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
1090
+ );
1091
+ }, 1e3);
1092
+ }
1038
1093
  addTotalSize(key, diff);
1039
1094
  doMaintainScrollAtEnd(true);
1040
- const state = refState.current;
1041
1095
  const scrollVelocity = state.scrollVelocity;
1042
1096
  if (!state.animFrameLayout && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1043
1097
  state.animFrameLayout = requestAnimationFrame(() => {
@@ -1155,7 +1209,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1155
1209
  recycleItems,
1156
1210
  alignItemsAtEnd,
1157
1211
  addTotalSize,
1158
- ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0
1212
+ ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
1213
+ style
1159
1214
  }
1160
1215
  );
1161
1216
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "legend-list",
5
5
  "sideEffects": false,
6
6
  "private": false,