@legendapp/list 0.5.5 → 0.5.7

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
  /**
@@ -153,7 +155,7 @@ interface ViewabilityConfig {
153
155
  /**
154
156
  * A unique ID to identify this viewability config
155
157
  */
156
- id: string;
158
+ id?: string;
157
159
  /**
158
160
  * Minimum amount of time (in milliseconds) that an item must be physically viewable before the
159
161
  * viewability callback will be fired. A high number means that scrolling through content without
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
  /**
@@ -153,7 +155,7 @@ interface ViewabilityConfig {
153
155
  /**
154
156
  * A unique ID to identify this viewability config
155
157
  */
156
- id: string;
158
+ id?: string;
157
159
  /**
158
160
  * Minimum amount of time (in milliseconds) that an item must be physically viewable before the
159
161
  * viewability callback will be fired. A high number means that scrolling through content without
package/index.js CHANGED
@@ -161,6 +161,8 @@ var Container = ({
161
161
  if (key !== void 0) {
162
162
  const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
163
163
  onLayout(key, size);
164
+ const otherAxisSize = horizontal ? event.nativeEvent.layout.width : event.nativeEvent.layout.height;
165
+ set$(ctx, "otherAxisSize", Math.max(otherAxisSize, peek$(ctx, "otherAxisSize") || 0));
164
166
  const measured = peek$(ctx, `containerDidLayout${id}`);
165
167
  if (!measured) {
166
168
  requestAnimationFrame(() => {
@@ -260,6 +262,7 @@ var ListComponent = React7__namespace.memo(function ListComponent2({
260
262
  ...rest
261
263
  }) {
262
264
  const ctx = useStateContext();
265
+ use$("otherAxisSize") || 0;
263
266
  return /* @__PURE__ */ React7__namespace.createElement(
264
267
  $ScrollView,
265
268
  {
@@ -474,6 +477,7 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
474
477
  var DEFAULT_DRAW_DISTANCE = 250;
475
478
  var INITIAL_SCROLL_ADJUST = 1e4;
476
479
  var POSITION_OUT_OF_VIEW = -1e7;
480
+ var DEFAULT_ITEM_SIZE = 100;
477
481
  var LegendList = React7.forwardRef(function LegendList2(props, forwardedRef) {
478
482
  return /* @__PURE__ */ React7__namespace.createElement(StateProvider, null, /* @__PURE__ */ React7__namespace.createElement(LegendListInner, { ...props, ref: forwardedRef }));
479
483
  });
@@ -504,7 +508,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
504
508
  ListEmptyComponent,
505
509
  ...rest
506
510
  } = props;
507
- const { style, contentContainerStyle } = rest;
511
+ const { style, contentContainerStyle } = props;
508
512
  const ctx = useStateContext();
509
513
  const internalRef = React7.useRef(null);
510
514
  const refScroller = internalRef;
@@ -520,11 +524,12 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
520
524
  return `${ret}`;
521
525
  };
522
526
  const getItemSize = (key, index, data2) => {
527
+ var _a2;
523
528
  const sizeKnown = refState.current.sizes.get(key);
524
529
  if (sizeKnown !== void 0) {
525
530
  return sizeKnown;
526
531
  }
527
- const size = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
532
+ const size = (_a2 = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize) != null ? _a2 : DEFAULT_ITEM_SIZE;
528
533
  refState.current.sizes.set(key, size);
529
534
  return size;
530
535
  };
@@ -576,7 +581,9 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
576
581
  indexByKey: /* @__PURE__ */ new Map(),
577
582
  scrollHistory: [],
578
583
  scrollVelocity: 0,
579
- contentSize: { width: 0, height: 0 }
584
+ contentSize: { width: 0, height: 0 },
585
+ sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
586
+ timeoutSizeMessage: 0
580
587
  };
581
588
  refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
582
589
  set$(ctx, "scrollAdjust", refState.current.scrollAdjustPending);
@@ -622,6 +629,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
622
629
  scroll: scrollState,
623
630
  startBuffered: startBufferedState,
624
631
  positions,
632
+ sizes,
625
633
  columns
626
634
  } = state;
627
635
  if (state.animFrameLayout) {
@@ -638,6 +646,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
638
646
  0,
639
647
  scrollState - topPad - (USE_CONTENT_INSET ? scrollAdjustPending : 0) + scrollExtra
640
648
  );
649
+ const scrollBottom = scroll + scrollLength;
641
650
  let startNoBuffer = null;
642
651
  let startBuffered = null;
643
652
  let endNoBuffer = null;
@@ -683,10 +692,10 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
683
692
  startBuffered = i;
684
693
  }
685
694
  if (startNoBuffer !== null) {
686
- if (top <= scroll + scrollLength) {
695
+ if (top <= scrollBottom) {
687
696
  endNoBuffer = i;
688
697
  }
689
- if (top <= scroll + scrollLength + scrollBuffer) {
698
+ if (top <= scrollBottom + scrollBuffer) {
690
699
  endBuffered = i;
691
700
  } else {
692
701
  break;
@@ -767,7 +776,14 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
767
776
  const item = data2[itemIndex];
768
777
  if (item) {
769
778
  const id = getId(itemIndex);
770
- if (!(itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered)) {
779
+ if (itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered) {
780
+ const prevPos = peek$(ctx, `containerPosition${i}`) - scrollAdjustPending;
781
+ const pos = positions.get(id) || 0;
782
+ const size = sizes.get(id) || 0;
783
+ if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
784
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
785
+ }
786
+ } else {
771
787
  const pos = (positions.get(id) || 0) + scrollAdjustPending;
772
788
  const column2 = columns.get(id) || 1;
773
789
  const prevPos = peek$(ctx, `containerPosition${i}`);
@@ -859,6 +875,10 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
859
875
  };
860
876
  const isFirst = !refState.current.renderItem;
861
877
  if (isFirst || data !== refState.current.data || numColumnsProp !== peek$(ctx, "numColumns")) {
878
+ if (!keyExtractor && !isFirst && data !== refState.current.data) {
879
+ refState.current.sizes.clear();
880
+ refState.current.positions.clear();
881
+ }
862
882
  refState.current.data = data;
863
883
  let totalSize = 0;
864
884
  const indexByKey = /* @__PURE__ */ new Map();
@@ -900,6 +920,10 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
900
920
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
901
921
  set$(ctx, `containerColumn${i}`, -1);
902
922
  }
923
+ if (!keyExtractor) {
924
+ refState.current.sizes.clear();
925
+ refState.current.positions;
926
+ }
903
927
  calculateItemsInView();
904
928
  doMaintainScrollAtEnd(false);
905
929
  checkAtTop();
@@ -984,17 +1008,17 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
984
1008
  listen$(ctx, signal, run);
985
1009
  }, []);
986
1010
  };
987
- const useRecyclingState = (updateState) => {
1011
+ const useRecyclingState = (valueOrFun) => {
988
1012
  const stateInfo = React7.useState(
989
- () => updateState({
1013
+ () => typeof valueOrFun === "function" ? valueOrFun({
990
1014
  index,
991
1015
  item: refState.current.data[index],
992
1016
  prevIndex: void 0,
993
1017
  prevItem: void 0
994
- })
1018
+ }) : valueOrFun
995
1019
  );
996
1020
  useRecyclingEffect((state2) => {
997
- const newState = updateState(state2);
1021
+ const newState = typeof valueOrFun === "function" ? valueOrFun(state2) : valueOrFun;
998
1022
  stateInfo[1](newState);
999
1023
  });
1000
1024
  return stateInfo;
@@ -1010,9 +1034,10 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
1010
1034
  return renderedItem;
1011
1035
  }, []);
1012
1036
  useInit(() => {
1037
+ var _a2;
1013
1038
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
1014
1039
  const scrollLength = refState.current.scrollLength;
1015
- const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
1040
+ const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1016
1041
  const numContainers = (initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize)) * numColumnsProp;
1017
1042
  for (let i = 0; i < numContainers; i++) {
1018
1043
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -1028,7 +1053,8 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
1028
1053
  if (!data2) {
1029
1054
  return;
1030
1055
  }
1031
- const { sizes, indexByKey, idsInFirstRender, columns } = refState.current;
1056
+ const state = refState.current;
1057
+ const { sizes, indexByKey, idsInFirstRender, columns, sizesLaidOut } = state;
1032
1058
  const index = indexByKey.get(key);
1033
1059
  const wasInFirstRender = idsInFirstRender.has(key);
1034
1060
  const prevSize = sizes.get(key) || (wasInFirstRender ? getItemSize(key, index, data2[index]) : 0);
@@ -1056,9 +1082,27 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
1056
1082
  sizes.set(key, size);
1057
1083
  diff = size - prevSize;
1058
1084
  }
1085
+ if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
1086
+ sizesLaidOut.set(key, size);
1087
+ if (state.timeoutSizeMessage) {
1088
+ clearTimeout(state.timeoutSizeMessage);
1089
+ }
1090
+ state.timeoutSizeMessage = setTimeout(() => {
1091
+ state.timeoutSizeMessage = void 0;
1092
+ let total = 0;
1093
+ let num = 0;
1094
+ for (const [key2, size2] of sizesLaidOut) {
1095
+ num++;
1096
+ total += size2;
1097
+ }
1098
+ const avg = Math.round(total / num);
1099
+ console.warn(
1100
+ `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
1101
+ );
1102
+ }, 1e3);
1103
+ }
1059
1104
  addTotalSize(key, diff);
1060
1105
  doMaintainScrollAtEnd(true);
1061
- const state = refState.current;
1062
1106
  const scrollVelocity = state.scrollVelocity;
1063
1107
  if (!state.animFrameLayout && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1064
1108
  state.animFrameLayout = requestAnimationFrame(() => {
@@ -1176,7 +1220,8 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
1176
1220
  recycleItems,
1177
1221
  alignItemsAtEnd,
1178
1222
  addTotalSize,
1179
- ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0
1223
+ ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
1224
+ style
1180
1225
  }
1181
1226
  );
1182
1227
  });
package/index.mjs CHANGED
@@ -140,6 +140,8 @@ var Container = ({
140
140
  if (key !== void 0) {
141
141
  const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
142
142
  onLayout(key, size);
143
+ const otherAxisSize = horizontal ? event.nativeEvent.layout.width : event.nativeEvent.layout.height;
144
+ set$(ctx, "otherAxisSize", Math.max(otherAxisSize, peek$(ctx, "otherAxisSize") || 0));
143
145
  const measured = peek$(ctx, `containerDidLayout${id}`);
144
146
  if (!measured) {
145
147
  requestAnimationFrame(() => {
@@ -239,6 +241,7 @@ var ListComponent = React7.memo(function ListComponent2({
239
241
  ...rest
240
242
  }) {
241
243
  const ctx = useStateContext();
244
+ use$("otherAxisSize") || 0;
242
245
  return /* @__PURE__ */ React7.createElement(
243
246
  $ScrollView,
244
247
  {
@@ -453,6 +456,7 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
453
456
  var DEFAULT_DRAW_DISTANCE = 250;
454
457
  var INITIAL_SCROLL_ADJUST = 1e4;
455
458
  var POSITION_OUT_OF_VIEW = -1e7;
459
+ var DEFAULT_ITEM_SIZE = 100;
456
460
  var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
457
461
  return /* @__PURE__ */ React7.createElement(StateProvider, null, /* @__PURE__ */ React7.createElement(LegendListInner, { ...props, ref: forwardedRef }));
458
462
  });
@@ -483,7 +487,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
483
487
  ListEmptyComponent,
484
488
  ...rest
485
489
  } = props;
486
- const { style, contentContainerStyle } = rest;
490
+ const { style, contentContainerStyle } = props;
487
491
  const ctx = useStateContext();
488
492
  const internalRef = useRef(null);
489
493
  const refScroller = internalRef;
@@ -499,11 +503,12 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
499
503
  return `${ret}`;
500
504
  };
501
505
  const getItemSize = (key, index, data2) => {
506
+ var _a2;
502
507
  const sizeKnown = refState.current.sizes.get(key);
503
508
  if (sizeKnown !== void 0) {
504
509
  return sizeKnown;
505
510
  }
506
- const size = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
511
+ const size = (_a2 = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize) != null ? _a2 : DEFAULT_ITEM_SIZE;
507
512
  refState.current.sizes.set(key, size);
508
513
  return size;
509
514
  };
@@ -555,7 +560,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
555
560
  indexByKey: /* @__PURE__ */ new Map(),
556
561
  scrollHistory: [],
557
562
  scrollVelocity: 0,
558
- contentSize: { width: 0, height: 0 }
563
+ contentSize: { width: 0, height: 0 },
564
+ sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
565
+ timeoutSizeMessage: 0
559
566
  };
560
567
  refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
561
568
  set$(ctx, "scrollAdjust", refState.current.scrollAdjustPending);
@@ -601,6 +608,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
601
608
  scroll: scrollState,
602
609
  startBuffered: startBufferedState,
603
610
  positions,
611
+ sizes,
604
612
  columns
605
613
  } = state;
606
614
  if (state.animFrameLayout) {
@@ -617,6 +625,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
617
625
  0,
618
626
  scrollState - topPad - (USE_CONTENT_INSET ? scrollAdjustPending : 0) + scrollExtra
619
627
  );
628
+ const scrollBottom = scroll + scrollLength;
620
629
  let startNoBuffer = null;
621
630
  let startBuffered = null;
622
631
  let endNoBuffer = null;
@@ -662,10 +671,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
662
671
  startBuffered = i;
663
672
  }
664
673
  if (startNoBuffer !== null) {
665
- if (top <= scroll + scrollLength) {
674
+ if (top <= scrollBottom) {
666
675
  endNoBuffer = i;
667
676
  }
668
- if (top <= scroll + scrollLength + scrollBuffer) {
677
+ if (top <= scrollBottom + scrollBuffer) {
669
678
  endBuffered = i;
670
679
  } else {
671
680
  break;
@@ -746,7 +755,14 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
746
755
  const item = data2[itemIndex];
747
756
  if (item) {
748
757
  const id = getId(itemIndex);
749
- if (!(itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered)) {
758
+ if (itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered) {
759
+ const prevPos = peek$(ctx, `containerPosition${i}`) - scrollAdjustPending;
760
+ const pos = positions.get(id) || 0;
761
+ const size = sizes.get(id) || 0;
762
+ if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
763
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
764
+ }
765
+ } else {
750
766
  const pos = (positions.get(id) || 0) + scrollAdjustPending;
751
767
  const column2 = columns.get(id) || 1;
752
768
  const prevPos = peek$(ctx, `containerPosition${i}`);
@@ -838,6 +854,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
838
854
  };
839
855
  const isFirst = !refState.current.renderItem;
840
856
  if (isFirst || data !== refState.current.data || numColumnsProp !== peek$(ctx, "numColumns")) {
857
+ if (!keyExtractor && !isFirst && data !== refState.current.data) {
858
+ refState.current.sizes.clear();
859
+ refState.current.positions.clear();
860
+ }
841
861
  refState.current.data = data;
842
862
  let totalSize = 0;
843
863
  const indexByKey = /* @__PURE__ */ new Map();
@@ -879,6 +899,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
879
899
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
880
900
  set$(ctx, `containerColumn${i}`, -1);
881
901
  }
902
+ if (!keyExtractor) {
903
+ refState.current.sizes.clear();
904
+ refState.current.positions;
905
+ }
882
906
  calculateItemsInView();
883
907
  doMaintainScrollAtEnd(false);
884
908
  checkAtTop();
@@ -963,17 +987,17 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
963
987
  listen$(ctx, signal, run);
964
988
  }, []);
965
989
  };
966
- const useRecyclingState = (updateState) => {
990
+ const useRecyclingState = (valueOrFun) => {
967
991
  const stateInfo = useState(
968
- () => updateState({
992
+ () => typeof valueOrFun === "function" ? valueOrFun({
969
993
  index,
970
994
  item: refState.current.data[index],
971
995
  prevIndex: void 0,
972
996
  prevItem: void 0
973
- })
997
+ }) : valueOrFun
974
998
  );
975
999
  useRecyclingEffect((state2) => {
976
- const newState = updateState(state2);
1000
+ const newState = typeof valueOrFun === "function" ? valueOrFun(state2) : valueOrFun;
977
1001
  stateInfo[1](newState);
978
1002
  });
979
1003
  return stateInfo;
@@ -989,9 +1013,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
989
1013
  return renderedItem;
990
1014
  }, []);
991
1015
  useInit(() => {
1016
+ var _a2;
992
1017
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
993
1018
  const scrollLength = refState.current.scrollLength;
994
- const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
1019
+ const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
995
1020
  const numContainers = (initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize)) * numColumnsProp;
996
1021
  for (let i = 0; i < numContainers; i++) {
997
1022
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -1007,7 +1032,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1007
1032
  if (!data2) {
1008
1033
  return;
1009
1034
  }
1010
- const { sizes, indexByKey, idsInFirstRender, columns } = refState.current;
1035
+ const state = refState.current;
1036
+ const { sizes, indexByKey, idsInFirstRender, columns, sizesLaidOut } = state;
1011
1037
  const index = indexByKey.get(key);
1012
1038
  const wasInFirstRender = idsInFirstRender.has(key);
1013
1039
  const prevSize = sizes.get(key) || (wasInFirstRender ? getItemSize(key, index, data2[index]) : 0);
@@ -1035,9 +1061,27 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1035
1061
  sizes.set(key, size);
1036
1062
  diff = size - prevSize;
1037
1063
  }
1064
+ if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
1065
+ sizesLaidOut.set(key, size);
1066
+ if (state.timeoutSizeMessage) {
1067
+ clearTimeout(state.timeoutSizeMessage);
1068
+ }
1069
+ state.timeoutSizeMessage = setTimeout(() => {
1070
+ state.timeoutSizeMessage = void 0;
1071
+ let total = 0;
1072
+ let num = 0;
1073
+ for (const [key2, size2] of sizesLaidOut) {
1074
+ num++;
1075
+ total += size2;
1076
+ }
1077
+ const avg = Math.round(total / num);
1078
+ console.warn(
1079
+ `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
1080
+ );
1081
+ }, 1e3);
1082
+ }
1038
1083
  addTotalSize(key, diff);
1039
1084
  doMaintainScrollAtEnd(true);
1040
- const state = refState.current;
1041
1085
  const scrollVelocity = state.scrollVelocity;
1042
1086
  if (!state.animFrameLayout && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1043
1087
  state.animFrameLayout = requestAnimationFrame(() => {
@@ -1155,7 +1199,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1155
1199
  recycleItems,
1156
1200
  alignItemsAtEnd,
1157
1201
  addTotalSize,
1158
- ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0
1202
+ ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
1203
+ style
1159
1204
  }
1160
1205
  );
1161
1206
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "legend-list",
5
5
  "sideEffects": false,
6
6
  "private": false,