@legendapp/list 1.0.0-beta.50 → 1.0.0-beta.51

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/animated.d.mts CHANGED
@@ -3,7 +3,7 @@ import * as _legendapp_list from '@legendapp/list';
3
3
  import * as react_native from 'react-native';
4
4
  import { Animated } from 'react-native';
5
5
 
6
- declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<react_native.ScrollViewProps, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
6
+ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
7
7
  alignItemsAtEnd?: boolean;
8
8
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
9
9
  data: readonly T[];
@@ -50,6 +50,7 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<re
50
50
  refreshing?: boolean;
51
51
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<T>) => React$1.ReactNode) | undefined;
52
52
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
53
+ suggestEstimatedItemSize?: boolean;
53
54
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
54
55
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
55
56
  waitForInitialLayout?: boolean;
package/animated.d.ts CHANGED
@@ -3,7 +3,7 @@ import * as _legendapp_list from '@legendapp/list';
3
3
  import * as react_native from 'react-native';
4
4
  import { Animated } from 'react-native';
5
5
 
6
- declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<react_native.ScrollViewProps, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
6
+ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
7
7
  alignItemsAtEnd?: boolean;
8
8
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
9
9
  data: readonly T[];
@@ -50,6 +50,7 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<re
50
50
  refreshing?: boolean;
51
51
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<T>) => React$1.ReactNode) | undefined;
52
52
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
53
+ suggestEstimatedItemSize?: boolean;
53
54
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
54
55
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
55
56
  waitForInitialLayout?: boolean;
package/index.d.mts CHANGED
@@ -191,6 +191,12 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
191
191
  * @default (props) => <ScrollView {...props} />
192
192
  */
193
193
  renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
194
+ /**
195
+ * This will log a suggested estimatedItemSize.
196
+ * @required
197
+ * @default false
198
+ */
199
+ suggestEstimatedItemSize?: boolean;
194
200
  /**
195
201
  * Configuration for determining item viewability.
196
202
  */
@@ -215,7 +221,7 @@ type AnchoredPosition = {
215
221
  relativeCoordinate: number;
216
222
  top: number;
217
223
  };
218
- type LegendListProps<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof ScrollView>>;
224
+ type LegendListProps<ItemT> = LegendListPropsBase<ItemT, Omit<ComponentProps<typeof ScrollView>, "scrollEventThrottle">>;
219
225
  interface InternalState {
220
226
  anchorElement?: {
221
227
  id: string;
@@ -226,7 +232,7 @@ interface InternalState {
226
232
  positions: Map<string, number>;
227
233
  columns: Map<string, number>;
228
234
  sizes: Map<string, number>;
229
- sizesKnown: Map<string, number> | undefined;
235
+ sizesKnown: Map<string, number>;
230
236
  pendingAdjust: number;
231
237
  isStartReached: boolean;
232
238
  isEndReached: boolean;
@@ -246,6 +252,7 @@ interface InternalState {
246
252
  scrollPrevTime: number;
247
253
  scrollVelocity: number;
248
254
  scrollAdjustHandler: ScrollAdjustHandler;
255
+ maintainingScrollAtEnd?: boolean;
249
256
  totalSize: number;
250
257
  totalSizeBelowAnchor: number;
251
258
  timeouts: Set<number>;
@@ -273,6 +280,10 @@ interface InternalState {
273
280
  lastBatchingAction: number;
274
281
  ignoreScrollFromCalcTotal?: boolean;
275
282
  scrollingToOffset?: number | undefined;
283
+ averageSizes: Record<string, {
284
+ num: number;
285
+ avg: number;
286
+ }>;
276
287
  onScroll: ((event: NativeSyntheticEvent<NativeScrollEvent>) => void) | undefined;
277
288
  }
278
289
  interface ViewableRange<T> {
@@ -453,7 +464,7 @@ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqua
453
464
  };
454
465
  declare const typedMemo: TypedMemo;
455
466
 
456
- declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
467
+ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
457
468
  alignItemsAtEnd?: boolean;
458
469
  columnWrapperStyle?: ColumnWrapperStyle;
459
470
  data: readonly T[];
@@ -500,6 +511,7 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
500
511
  refreshing?: boolean;
501
512
  renderItem?: ((props: LegendListRenderItemProps<T>) => React$1.ReactNode) | undefined;
502
513
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React$1.ReactElement<react_native.ScrollViewProps>;
514
+ suggestEstimatedItemSize?: boolean;
503
515
  viewabilityConfig?: ViewabilityConfig;
504
516
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
505
517
  waitForInitialLayout?: boolean;
@@ -508,6 +520,6 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
508
520
  declare function useViewability(callback: ViewabilityCallback, configId?: string): void;
509
521
  declare function useViewabilityAmount(callback: ViewabilityAmountCallback): void;
510
522
  declare function useRecyclingEffect(effect: (info: LegendListRecyclingState<unknown>) => void | (() => void)): void;
511
- declare function useRecyclingState<ItemT>(valueOrFun: ((info: LegendListRecyclingState<ItemT>) => ItemT) | ItemT): (ItemT | Dispatch<SetStateAction<ItemT>> | null)[];
523
+ declare function useRecyclingState<ItemT>(valueOrFun: ((info: LegendListRecyclingState<ItemT>) => ItemT) | ItemT): readonly [ItemT | null, Dispatch<SetStateAction<ItemT>>];
512
524
 
513
525
  export { type AnchoredPosition, type ColumnWrapperStyle, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ScrollState, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
package/index.d.ts CHANGED
@@ -191,6 +191,12 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
191
191
  * @default (props) => <ScrollView {...props} />
192
192
  */
193
193
  renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
194
+ /**
195
+ * This will log a suggested estimatedItemSize.
196
+ * @required
197
+ * @default false
198
+ */
199
+ suggestEstimatedItemSize?: boolean;
194
200
  /**
195
201
  * Configuration for determining item viewability.
196
202
  */
@@ -215,7 +221,7 @@ type AnchoredPosition = {
215
221
  relativeCoordinate: number;
216
222
  top: number;
217
223
  };
218
- type LegendListProps<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof ScrollView>>;
224
+ type LegendListProps<ItemT> = LegendListPropsBase<ItemT, Omit<ComponentProps<typeof ScrollView>, "scrollEventThrottle">>;
219
225
  interface InternalState {
220
226
  anchorElement?: {
221
227
  id: string;
@@ -226,7 +232,7 @@ interface InternalState {
226
232
  positions: Map<string, number>;
227
233
  columns: Map<string, number>;
228
234
  sizes: Map<string, number>;
229
- sizesKnown: Map<string, number> | undefined;
235
+ sizesKnown: Map<string, number>;
230
236
  pendingAdjust: number;
231
237
  isStartReached: boolean;
232
238
  isEndReached: boolean;
@@ -246,6 +252,7 @@ interface InternalState {
246
252
  scrollPrevTime: number;
247
253
  scrollVelocity: number;
248
254
  scrollAdjustHandler: ScrollAdjustHandler;
255
+ maintainingScrollAtEnd?: boolean;
249
256
  totalSize: number;
250
257
  totalSizeBelowAnchor: number;
251
258
  timeouts: Set<number>;
@@ -273,6 +280,10 @@ interface InternalState {
273
280
  lastBatchingAction: number;
274
281
  ignoreScrollFromCalcTotal?: boolean;
275
282
  scrollingToOffset?: number | undefined;
283
+ averageSizes: Record<string, {
284
+ num: number;
285
+ avg: number;
286
+ }>;
276
287
  onScroll: ((event: NativeSyntheticEvent<NativeScrollEvent>) => void) | undefined;
277
288
  }
278
289
  interface ViewableRange<T> {
@@ -453,7 +464,7 @@ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqua
453
464
  };
454
465
  declare const typedMemo: TypedMemo;
455
466
 
456
- declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
467
+ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
457
468
  alignItemsAtEnd?: boolean;
458
469
  columnWrapperStyle?: ColumnWrapperStyle;
459
470
  data: readonly T[];
@@ -500,6 +511,7 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
500
511
  refreshing?: boolean;
501
512
  renderItem?: ((props: LegendListRenderItemProps<T>) => React$1.ReactNode) | undefined;
502
513
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React$1.ReactElement<react_native.ScrollViewProps>;
514
+ suggestEstimatedItemSize?: boolean;
503
515
  viewabilityConfig?: ViewabilityConfig;
504
516
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
505
517
  waitForInitialLayout?: boolean;
@@ -508,6 +520,6 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
508
520
  declare function useViewability(callback: ViewabilityCallback, configId?: string): void;
509
521
  declare function useViewabilityAmount(callback: ViewabilityAmountCallback): void;
510
522
  declare function useRecyclingEffect(effect: (info: LegendListRecyclingState<unknown>) => void | (() => void)): void;
511
- declare function useRecyclingState<ItemT>(valueOrFun: ((info: LegendListRecyclingState<ItemT>) => ItemT) | ItemT): (ItemT | Dispatch<SetStateAction<ItemT>> | null)[];
523
+ declare function useRecyclingState<ItemT>(valueOrFun: ((info: LegendListRecyclingState<ItemT>) => ItemT) | ItemT): readonly [ItemT | null, Dispatch<SetStateAction<ItemT>>];
512
524
 
513
525
  export { type AnchoredPosition, type ColumnWrapperStyle, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ScrollState, type TypedForwardRef, type TypedMemo, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, typedMemo, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
package/index.js CHANGED
@@ -149,6 +149,16 @@ function useInterval(callback, delay) {
149
149
  function isFunction(obj) {
150
150
  return typeof obj === "function";
151
151
  }
152
+ var warned = /* @__PURE__ */ new Set();
153
+ function warnDevOnce(id, text) {
154
+ if (__DEV__ && !warned.has(id)) {
155
+ warned.add(id);
156
+ console.warn(`[legend-list] ${text}`);
157
+ }
158
+ }
159
+ function roundSize(size) {
160
+ return Math.floor(size * 8) / 8;
161
+ }
152
162
  var symbolFirst = Symbol();
153
163
  function useInit(cb) {
154
164
  const refValue = React6.useRef(symbolFirst);
@@ -327,7 +337,7 @@ var Container = ({
327
337
  const onLayout = (event) => {
328
338
  if (itemKey !== void 0) {
329
339
  const layout = event.nativeEvent.layout;
330
- const size = Math.floor(layout[horizontal ? "width" : "height"] * 8) / 8;
340
+ const size = roundSize(layout[horizontal ? "width" : "height"]);
331
341
  refLastSize.current = size;
332
342
  updateItemSize(itemKey, size);
333
343
  }
@@ -897,9 +907,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
897
907
  renderItem,
898
908
  estimatedItemSize,
899
909
  getEstimatedItemSize,
910
+ suggestEstimatedItemSize,
900
911
  ListEmptyComponent,
901
912
  onItemSizeChanged,
902
- scrollEventThrottle,
903
913
  refScrollView,
904
914
  waitForInitialLayout = true,
905
915
  extraData,
@@ -947,14 +957,28 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
947
957
  const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
948
958
  return `${ret}`;
949
959
  };
950
- const getItemSize = (key, index, data) => {
951
- var _a;
952
- const sizeKnown = refState.current.sizes.get(key);
960
+ const getItemSize = (key, index, data, useAverageSize = false) => {
961
+ const state = refState.current;
962
+ const sizeKnown = state.sizesKnown.get(key);
953
963
  if (sizeKnown !== void 0) {
954
964
  return sizeKnown;
955
965
  }
956
- const size = (_a = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize) != null ? _a : DEFAULT_ITEM_SIZE;
957
- refState.current.sizes.set(key, size);
966
+ let size;
967
+ if (size === void 0 && useAverageSize) {
968
+ const itemType = "";
969
+ const average = state.averageSizes[itemType];
970
+ if (average) {
971
+ size = roundSize(average.avg);
972
+ }
973
+ }
974
+ const sizePrevious = state.sizesKnown.get(key);
975
+ if (sizePrevious !== void 0) {
976
+ return sizePrevious;
977
+ }
978
+ if (size === void 0) {
979
+ size = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize != null ? estimatedItemSize : DEFAULT_ITEM_SIZE;
980
+ }
981
+ state.sizes.set(key, size);
958
982
  return size;
959
983
  };
960
984
  const calculateOffsetForIndex = (index = initialScrollIndex) => {
@@ -1025,6 +1049,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1025
1049
  numPendingInitialLayout: 0,
1026
1050
  queuedCalculateItemsInView: 0,
1027
1051
  lastBatchingAction: Date.now(),
1052
+ averageSizes: {},
1028
1053
  onScroll: onScrollProp
1029
1054
  };
1030
1055
  const dataLength = dataProp.length;
@@ -1040,7 +1065,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1040
1065
  id: getId(0)
1041
1066
  };
1042
1067
  } else {
1043
- console.warn("[legend-list] maintainVisibleContentPosition was not able to find an anchor element");
1068
+ __DEV__ && warnDevOnce(
1069
+ "maintainVisibleContentPosition",
1070
+ "[legend-list] maintainVisibleContentPosition was not able to find an anchor element"
1071
+ );
1044
1072
  }
1045
1073
  }
1046
1074
  set$(ctx, "scrollAdjust", 0);
@@ -1202,14 +1230,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1202
1230
  var _a;
1203
1231
  const state = refState.current;
1204
1232
  const { data, scrollLength, positions, startBuffered, endBuffered } = state;
1205
- if (!data || scrollLength === 0) {
1233
+ const numColumns = peek$(ctx, "numColumns");
1234
+ if (!data || scrollLength === 0 || numColumns > 1) {
1206
1235
  return;
1207
1236
  }
1208
1237
  const numContainers = ctx.values.get("numContainers");
1209
1238
  let numMeasurements = 0;
1210
1239
  for (let i = 0; i < numContainers; i++) {
1211
1240
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1212
- const isSizeKnown = refState.current.sizesKnown.get(itemKey);
1241
+ const isSizeKnown = state.sizesKnown.get(itemKey);
1213
1242
  if (itemKey && !isSizeKnown) {
1214
1243
  const containerRef = ctx.viewRefs.get(i);
1215
1244
  if (containerRef) {
@@ -1334,7 +1363,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1334
1363
  }
1335
1364
  const top2 = newPosition || positions.get(id);
1336
1365
  if (top2 !== void 0) {
1337
- const size = getItemSize(id, i, data[i]);
1366
+ const size = getItemSize(
1367
+ id,
1368
+ i,
1369
+ data[i],
1370
+ /*useAverageSize*/
1371
+ true
1372
+ );
1338
1373
  const bottom = top2 + size;
1339
1374
  if (bottom > scroll - scrollBuffer) {
1340
1375
  loopStart = i;
@@ -1364,7 +1399,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1364
1399
  };
1365
1400
  for (let i = Math.max(0, loopStart); i < data.length; i++) {
1366
1401
  const id = getId(i);
1367
- const size = getItemSize(id, i, data[i]);
1402
+ const size = getItemSize(
1403
+ id,
1404
+ i,
1405
+ data[i],
1406
+ /*useAverageSize*/
1407
+ true
1408
+ );
1368
1409
  maxSizeInRow = Math.max(maxSizeInRow, size);
1369
1410
  if (top === void 0 || id === ((_a = state.anchorElement) == null ? void 0 : _a.id)) {
1370
1411
  top = getInitialTop(i);
@@ -1588,9 +1629,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1588
1629
  }
1589
1630
  requestAnimationFrame(() => {
1590
1631
  var _a;
1632
+ state.maintainingScrollAtEnd = true;
1591
1633
  (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
1592
1634
  animated
1593
1635
  });
1636
+ setTimeout(
1637
+ () => {
1638
+ state.maintainingScrollAtEnd = false;
1639
+ },
1640
+ 0
1641
+ );
1594
1642
  });
1595
1643
  return true;
1596
1644
  }
@@ -1618,9 +1666,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1618
1666
  if (!refState.current) {
1619
1667
  return;
1620
1668
  }
1621
- const { queuedInitialLayout, scrollLength, scroll } = refState.current;
1669
+ const { queuedInitialLayout, scrollLength, scroll, maintainingScrollAtEnd } = refState.current;
1622
1670
  const contentSize = getContentSize(ctx);
1623
- if (contentSize > 0 && queuedInitialLayout) {
1671
+ if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1624
1672
  const distanceFromEnd = contentSize - scroll - scrollLength;
1625
1673
  const distanceFromEndAbs = Math.abs(distanceFromEnd);
1626
1674
  const isContentLess = contentSize < scrollLength;
@@ -1688,13 +1736,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1688
1736
  if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1689
1737
  state.isEndReached = false;
1690
1738
  }
1691
- checkAtTop();
1692
- checkAtBottom();
1739
+ if (!didMaintainScrollAtEnd) {
1740
+ checkAtTop();
1741
+ checkAtBottom();
1742
+ }
1693
1743
  }
1694
1744
  }
1695
1745
  };
1696
1746
  const calcTotalSizesAndPositions = ({ forgetPositions = false }) => {
1697
1747
  var _a, _b;
1748
+ const state = refState.current;
1698
1749
  let totalSize = 0;
1699
1750
  let totalSizeBelowIndex = 0;
1700
1751
  const indexByKey = /* @__PURE__ */ new Map();
@@ -1702,7 +1753,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1702
1753
  let column = 1;
1703
1754
  let maxSizeInRow = 0;
1704
1755
  const numColumns = (_a = peek$(ctx, "numColumns")) != null ? _a : numColumnsProp;
1705
- if (!refState.current) {
1756
+ if (!state) {
1706
1757
  return;
1707
1758
  }
1708
1759
  for (let i = 0; i < dataProp.length; i++) {
@@ -1715,36 +1766,36 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1715
1766
  }
1716
1767
  }
1717
1768
  indexByKey.set(key, i);
1718
- if (!forgetPositions && refState.current.positions.get(key) != null && refState.current.indexByKey.get(key) === i) {
1719
- newPositions.set(key, refState.current.positions.get(key));
1769
+ if (!forgetPositions && state.positions.get(key) != null && state.indexByKey.get(key) === i) {
1770
+ newPositions.set(key, state.positions.get(key));
1720
1771
  }
1721
1772
  }
1722
- refState.current.indexByKey = indexByKey;
1723
- refState.current.positions = newPositions;
1773
+ state.indexByKey = indexByKey;
1774
+ state.positions = newPositions;
1724
1775
  if (!forgetPositions && !isFirst) {
1725
1776
  if (maintainVisibleContentPosition) {
1726
- if (refState.current.anchorElement == null || indexByKey.get(refState.current.anchorElement.id) == null) {
1777
+ if (state.anchorElement == null || indexByKey.get(state.anchorElement.id) == null) {
1727
1778
  if (dataProp.length) {
1728
1779
  const newAnchorElement = {
1729
1780
  coordinate: 0,
1730
1781
  id: getId(0)
1731
1782
  };
1732
- refState.current.anchorElement = newAnchorElement;
1733
- (_b = refState.current.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1783
+ state.anchorElement = newAnchorElement;
1784
+ (_b = state.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1734
1785
  scrollTo(0, false);
1735
1786
  setTimeout(() => {
1736
1787
  calculateItemsInView();
1737
1788
  }, 0);
1738
1789
  } else {
1739
- refState.current.startBufferedId = void 0;
1790
+ state.startBufferedId = void 0;
1740
1791
  }
1741
1792
  }
1742
1793
  } else {
1743
- if (refState.current.startBufferedId != null && newPositions.get(refState.current.startBufferedId) == null) {
1794
+ if (state.startBufferedId != null && newPositions.get(state.startBufferedId) == null) {
1744
1795
  if (dataProp.length) {
1745
- refState.current.startBufferedId = getId(0);
1796
+ state.startBufferedId = getId(0);
1746
1797
  } else {
1747
- refState.current.startBufferedId = void 0;
1798
+ state.startBufferedId = void 0;
1748
1799
  }
1749
1800
  scrollTo(0, false);
1750
1801
  setTimeout(() => {
@@ -1771,7 +1822,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1771
1822
  if (maxSizeInRow > 0) {
1772
1823
  totalSize += maxSizeInRow;
1773
1824
  }
1774
- const state = refState.current;
1775
1825
  state.ignoreScrollFromCalcTotal = true;
1776
1826
  requestAnimationFrame(() => {
1777
1827
  state.ignoreScrollFromCalcTotal = false;
@@ -1804,6 +1854,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1804
1854
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
1805
1855
  refState.current.lastBatchingAction = Date.now();
1806
1856
  if (!keyExtractorProp && !isFirst && didDataChange) {
1857
+ __DEV__ && warnDevOnce(
1858
+ "keyExtractor",
1859
+ "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
1860
+ );
1807
1861
  refState.current.sizes.clear();
1808
1862
  refState.current.positions.clear();
1809
1863
  }
@@ -1834,24 +1888,28 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1834
1888
  if (index === void 0) {
1835
1889
  return null;
1836
1890
  }
1837
- const useViewability2 = __DEV__ ? (configId, callback) => {
1838
- console.warn(
1839
- `[legend-list] useViewability has been moved from a render prop to a regular import: import { useViewability } from "@legendapp/list";`
1891
+ const useViewability2 = __DEV__ ? () => {
1892
+ warnDevOnce(
1893
+ "useViewability",
1894
+ `useViewability has been moved from a render prop to a regular import: import { useViewability } from "@legendapp/list";`
1840
1895
  );
1841
1896
  } : void 0;
1842
- const useViewabilityAmount2 = __DEV__ ? (callback) => {
1843
- console.warn(
1844
- `[legend-list] useViewabilityAmount has been moved from a render prop to a regular import: import { useViewabilityAmount } from "@legendapp/list";`
1897
+ const useViewabilityAmount2 = __DEV__ ? () => {
1898
+ warnDevOnce(
1899
+ "useViewabilityAmount",
1900
+ `useViewabilityAmount has been moved from a render prop to a regular import: import { useViewabilityAmount } from "@legendapp/list";`
1845
1901
  );
1846
1902
  } : void 0;
1847
- const useRecyclingEffect2 = __DEV__ ? (effect) => {
1848
- console.warn(
1849
- `[legend-list] useRecyclingEffect has been moved from a render prop to a regular import: import { useRecyclingEffect } from "@legendapp/list";`
1903
+ const useRecyclingEffect2 = __DEV__ ? () => {
1904
+ warnDevOnce(
1905
+ "useRecyclingEffect",
1906
+ `useRecyclingEffect has been moved from a render prop to a regular import: import { useRecyclingEffect } from "@legendapp/list";`
1850
1907
  );
1851
1908
  } : void 0;
1852
- const useRecyclingState2 = __DEV__ ? (valueOrFun) => {
1853
- console.warn(
1854
- `[legend-list] useRecyclingState has been moved from a render prop to a regular import: import { useRecyclingState } from "@legendapp/list";`
1909
+ const useRecyclingState2 = __DEV__ ? () => {
1910
+ warnDevOnce(
1911
+ "useRecyclingState",
1912
+ `useRecyclingState has been moved from a render prop to a regular import: import { useRecyclingState } from "@legendapp/list";`
1855
1913
  );
1856
1914
  } : void 0;
1857
1915
  const renderedItem = (_b = (_a = refState.current).renderItem) == null ? void 0 : _b.call(_a, {
@@ -1904,7 +1962,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1904
1962
  });
1905
1963
  const updateItemSize = React6.useCallback((itemKey, size, fromFixGaps) => {
1906
1964
  const state = refState.current;
1907
- const { sizes, indexByKey, sizesKnown, data, rowHeights, startBuffered, endBuffered } = state;
1965
+ const { sizes, indexByKey, sizesKnown, data, rowHeights, startBuffered, endBuffered, averageSizes } = state;
1908
1966
  if (!data) {
1909
1967
  return;
1910
1968
  }
@@ -1922,7 +1980,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1922
1980
  needsUpdateContainersDidLayout = true;
1923
1981
  }
1924
1982
  }
1925
- sizesKnown == null ? void 0 : sizesKnown.set(itemKey, size);
1983
+ sizesKnown.set(itemKey, size);
1984
+ const itemType = "";
1985
+ let averages = averageSizes[itemType];
1986
+ if (!averages) {
1987
+ averages = averageSizes[itemType] = {
1988
+ num: 0,
1989
+ avg: 0
1990
+ };
1991
+ }
1992
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
1993
+ averages.num++;
1926
1994
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
1927
1995
  let diff;
1928
1996
  needsCalculate = true;
@@ -1937,19 +2005,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1937
2005
  sizes.set(itemKey, size);
1938
2006
  diff = size - prevSize;
1939
2007
  }
1940
- if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
2008
+ if (__DEV__ && suggestEstimatedItemSize) {
1941
2009
  if (state.timeoutSizeMessage) {
1942
2010
  clearTimeout(state.timeoutSizeMessage);
1943
2011
  }
1944
2012
  state.timeoutSizeMessage = setTimeout(() => {
1945
2013
  state.timeoutSizeMessage = void 0;
1946
- let total = 0;
1947
- let num = 0;
1948
- for (const [_, size2] of sizesKnown) {
1949
- num++;
1950
- total += size2;
1951
- }
1952
- const avg = Math.round(total / num);
2014
+ const num = sizesKnown.size;
2015
+ const avg = state.averageSizes[""].avg;
1953
2016
  console.warn(
1954
2017
  `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
1955
2018
  );
@@ -2009,7 +2072,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2009
2072
  const isWidthZero = event.nativeEvent.layout.width === 0;
2010
2073
  const isHeightZero = event.nativeEvent.layout.height === 0;
2011
2074
  if (isWidthZero || isHeightZero) {
2012
- console.warn(
2075
+ warnDevOnce(
2076
+ "height0",
2013
2077
  `[legend-list] List ${isWidthZero ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
2014
2078
  );
2015
2079
  }
@@ -2176,7 +2240,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2176
2240
  alignItemsAtEnd,
2177
2241
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
2178
2242
  maintainVisibleContentPosition,
2179
- scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : reactNative.Platform.OS === "web" ? 16 : void 0,
2243
+ scrollEventThrottle: reactNative.Platform.OS === "web" ? 16 : void 0,
2180
2244
  waitForInitialLayout,
2181
2245
  refreshControl: refreshControl != null ? refreshControl : onRefresh && /* @__PURE__ */ React6__namespace.createElement(
2182
2246
  reactNative.RefreshControl,
package/index.mjs CHANGED
@@ -128,6 +128,16 @@ function useInterval(callback, delay) {
128
128
  function isFunction(obj) {
129
129
  return typeof obj === "function";
130
130
  }
131
+ var warned = /* @__PURE__ */ new Set();
132
+ function warnDevOnce(id, text) {
133
+ if (__DEV__ && !warned.has(id)) {
134
+ warned.add(id);
135
+ console.warn(`[legend-list] ${text}`);
136
+ }
137
+ }
138
+ function roundSize(size) {
139
+ return Math.floor(size * 8) / 8;
140
+ }
131
141
  var symbolFirst = Symbol();
132
142
  function useInit(cb) {
133
143
  const refValue = useRef(symbolFirst);
@@ -306,7 +316,7 @@ var Container = ({
306
316
  const onLayout = (event) => {
307
317
  if (itemKey !== void 0) {
308
318
  const layout = event.nativeEvent.layout;
309
- const size = Math.floor(layout[horizontal ? "width" : "height"] * 8) / 8;
319
+ const size = roundSize(layout[horizontal ? "width" : "height"]);
310
320
  refLastSize.current = size;
311
321
  updateItemSize(itemKey, size);
312
322
  }
@@ -876,9 +886,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
876
886
  renderItem,
877
887
  estimatedItemSize,
878
888
  getEstimatedItemSize,
889
+ suggestEstimatedItemSize,
879
890
  ListEmptyComponent,
880
891
  onItemSizeChanged,
881
- scrollEventThrottle,
882
892
  refScrollView,
883
893
  waitForInitialLayout = true,
884
894
  extraData,
@@ -926,14 +936,28 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
926
936
  const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
927
937
  return `${ret}`;
928
938
  };
929
- const getItemSize = (key, index, data) => {
930
- var _a;
931
- const sizeKnown = refState.current.sizes.get(key);
939
+ const getItemSize = (key, index, data, useAverageSize = false) => {
940
+ const state = refState.current;
941
+ const sizeKnown = state.sizesKnown.get(key);
932
942
  if (sizeKnown !== void 0) {
933
943
  return sizeKnown;
934
944
  }
935
- const size = (_a = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize) != null ? _a : DEFAULT_ITEM_SIZE;
936
- refState.current.sizes.set(key, size);
945
+ let size;
946
+ if (size === void 0 && useAverageSize) {
947
+ const itemType = "";
948
+ const average = state.averageSizes[itemType];
949
+ if (average) {
950
+ size = roundSize(average.avg);
951
+ }
952
+ }
953
+ const sizePrevious = state.sizesKnown.get(key);
954
+ if (sizePrevious !== void 0) {
955
+ return sizePrevious;
956
+ }
957
+ if (size === void 0) {
958
+ size = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize != null ? estimatedItemSize : DEFAULT_ITEM_SIZE;
959
+ }
960
+ state.sizes.set(key, size);
937
961
  return size;
938
962
  };
939
963
  const calculateOffsetForIndex = (index = initialScrollIndex) => {
@@ -1004,6 +1028,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1004
1028
  numPendingInitialLayout: 0,
1005
1029
  queuedCalculateItemsInView: 0,
1006
1030
  lastBatchingAction: Date.now(),
1031
+ averageSizes: {},
1007
1032
  onScroll: onScrollProp
1008
1033
  };
1009
1034
  const dataLength = dataProp.length;
@@ -1019,7 +1044,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1019
1044
  id: getId(0)
1020
1045
  };
1021
1046
  } else {
1022
- console.warn("[legend-list] maintainVisibleContentPosition was not able to find an anchor element");
1047
+ __DEV__ && warnDevOnce(
1048
+ "maintainVisibleContentPosition",
1049
+ "[legend-list] maintainVisibleContentPosition was not able to find an anchor element"
1050
+ );
1023
1051
  }
1024
1052
  }
1025
1053
  set$(ctx, "scrollAdjust", 0);
@@ -1181,14 +1209,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1181
1209
  var _a;
1182
1210
  const state = refState.current;
1183
1211
  const { data, scrollLength, positions, startBuffered, endBuffered } = state;
1184
- if (!data || scrollLength === 0) {
1212
+ const numColumns = peek$(ctx, "numColumns");
1213
+ if (!data || scrollLength === 0 || numColumns > 1) {
1185
1214
  return;
1186
1215
  }
1187
1216
  const numContainers = ctx.values.get("numContainers");
1188
1217
  let numMeasurements = 0;
1189
1218
  for (let i = 0; i < numContainers; i++) {
1190
1219
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1191
- const isSizeKnown = refState.current.sizesKnown.get(itemKey);
1220
+ const isSizeKnown = state.sizesKnown.get(itemKey);
1192
1221
  if (itemKey && !isSizeKnown) {
1193
1222
  const containerRef = ctx.viewRefs.get(i);
1194
1223
  if (containerRef) {
@@ -1313,7 +1342,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1313
1342
  }
1314
1343
  const top2 = newPosition || positions.get(id);
1315
1344
  if (top2 !== void 0) {
1316
- const size = getItemSize(id, i, data[i]);
1345
+ const size = getItemSize(
1346
+ id,
1347
+ i,
1348
+ data[i],
1349
+ /*useAverageSize*/
1350
+ true
1351
+ );
1317
1352
  const bottom = top2 + size;
1318
1353
  if (bottom > scroll - scrollBuffer) {
1319
1354
  loopStart = i;
@@ -1343,7 +1378,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1343
1378
  };
1344
1379
  for (let i = Math.max(0, loopStart); i < data.length; i++) {
1345
1380
  const id = getId(i);
1346
- const size = getItemSize(id, i, data[i]);
1381
+ const size = getItemSize(
1382
+ id,
1383
+ i,
1384
+ data[i],
1385
+ /*useAverageSize*/
1386
+ true
1387
+ );
1347
1388
  maxSizeInRow = Math.max(maxSizeInRow, size);
1348
1389
  if (top === void 0 || id === ((_a = state.anchorElement) == null ? void 0 : _a.id)) {
1349
1390
  top = getInitialTop(i);
@@ -1567,9 +1608,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1567
1608
  }
1568
1609
  requestAnimationFrame(() => {
1569
1610
  var _a;
1611
+ state.maintainingScrollAtEnd = true;
1570
1612
  (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
1571
1613
  animated
1572
1614
  });
1615
+ setTimeout(
1616
+ () => {
1617
+ state.maintainingScrollAtEnd = false;
1618
+ },
1619
+ 0
1620
+ );
1573
1621
  });
1574
1622
  return true;
1575
1623
  }
@@ -1597,9 +1645,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1597
1645
  if (!refState.current) {
1598
1646
  return;
1599
1647
  }
1600
- const { queuedInitialLayout, scrollLength, scroll } = refState.current;
1648
+ const { queuedInitialLayout, scrollLength, scroll, maintainingScrollAtEnd } = refState.current;
1601
1649
  const contentSize = getContentSize(ctx);
1602
- if (contentSize > 0 && queuedInitialLayout) {
1650
+ if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1603
1651
  const distanceFromEnd = contentSize - scroll - scrollLength;
1604
1652
  const distanceFromEndAbs = Math.abs(distanceFromEnd);
1605
1653
  const isContentLess = contentSize < scrollLength;
@@ -1667,13 +1715,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1667
1715
  if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1668
1716
  state.isEndReached = false;
1669
1717
  }
1670
- checkAtTop();
1671
- checkAtBottom();
1718
+ if (!didMaintainScrollAtEnd) {
1719
+ checkAtTop();
1720
+ checkAtBottom();
1721
+ }
1672
1722
  }
1673
1723
  }
1674
1724
  };
1675
1725
  const calcTotalSizesAndPositions = ({ forgetPositions = false }) => {
1676
1726
  var _a, _b;
1727
+ const state = refState.current;
1677
1728
  let totalSize = 0;
1678
1729
  let totalSizeBelowIndex = 0;
1679
1730
  const indexByKey = /* @__PURE__ */ new Map();
@@ -1681,7 +1732,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1681
1732
  let column = 1;
1682
1733
  let maxSizeInRow = 0;
1683
1734
  const numColumns = (_a = peek$(ctx, "numColumns")) != null ? _a : numColumnsProp;
1684
- if (!refState.current) {
1735
+ if (!state) {
1685
1736
  return;
1686
1737
  }
1687
1738
  for (let i = 0; i < dataProp.length; i++) {
@@ -1694,36 +1745,36 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1694
1745
  }
1695
1746
  }
1696
1747
  indexByKey.set(key, i);
1697
- if (!forgetPositions && refState.current.positions.get(key) != null && refState.current.indexByKey.get(key) === i) {
1698
- newPositions.set(key, refState.current.positions.get(key));
1748
+ if (!forgetPositions && state.positions.get(key) != null && state.indexByKey.get(key) === i) {
1749
+ newPositions.set(key, state.positions.get(key));
1699
1750
  }
1700
1751
  }
1701
- refState.current.indexByKey = indexByKey;
1702
- refState.current.positions = newPositions;
1752
+ state.indexByKey = indexByKey;
1753
+ state.positions = newPositions;
1703
1754
  if (!forgetPositions && !isFirst) {
1704
1755
  if (maintainVisibleContentPosition) {
1705
- if (refState.current.anchorElement == null || indexByKey.get(refState.current.anchorElement.id) == null) {
1756
+ if (state.anchorElement == null || indexByKey.get(state.anchorElement.id) == null) {
1706
1757
  if (dataProp.length) {
1707
1758
  const newAnchorElement = {
1708
1759
  coordinate: 0,
1709
1760
  id: getId(0)
1710
1761
  };
1711
- refState.current.anchorElement = newAnchorElement;
1712
- (_b = refState.current.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1762
+ state.anchorElement = newAnchorElement;
1763
+ (_b = state.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1713
1764
  scrollTo(0, false);
1714
1765
  setTimeout(() => {
1715
1766
  calculateItemsInView();
1716
1767
  }, 0);
1717
1768
  } else {
1718
- refState.current.startBufferedId = void 0;
1769
+ state.startBufferedId = void 0;
1719
1770
  }
1720
1771
  }
1721
1772
  } else {
1722
- if (refState.current.startBufferedId != null && newPositions.get(refState.current.startBufferedId) == null) {
1773
+ if (state.startBufferedId != null && newPositions.get(state.startBufferedId) == null) {
1723
1774
  if (dataProp.length) {
1724
- refState.current.startBufferedId = getId(0);
1775
+ state.startBufferedId = getId(0);
1725
1776
  } else {
1726
- refState.current.startBufferedId = void 0;
1777
+ state.startBufferedId = void 0;
1727
1778
  }
1728
1779
  scrollTo(0, false);
1729
1780
  setTimeout(() => {
@@ -1750,7 +1801,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1750
1801
  if (maxSizeInRow > 0) {
1751
1802
  totalSize += maxSizeInRow;
1752
1803
  }
1753
- const state = refState.current;
1754
1804
  state.ignoreScrollFromCalcTotal = true;
1755
1805
  requestAnimationFrame(() => {
1756
1806
  state.ignoreScrollFromCalcTotal = false;
@@ -1783,6 +1833,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1783
1833
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
1784
1834
  refState.current.lastBatchingAction = Date.now();
1785
1835
  if (!keyExtractorProp && !isFirst && didDataChange) {
1836
+ __DEV__ && warnDevOnce(
1837
+ "keyExtractor",
1838
+ "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
1839
+ );
1786
1840
  refState.current.sizes.clear();
1787
1841
  refState.current.positions.clear();
1788
1842
  }
@@ -1813,24 +1867,28 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1813
1867
  if (index === void 0) {
1814
1868
  return null;
1815
1869
  }
1816
- const useViewability2 = __DEV__ ? (configId, callback) => {
1817
- console.warn(
1818
- `[legend-list] useViewability has been moved from a render prop to a regular import: import { useViewability } from "@legendapp/list";`
1870
+ const useViewability2 = __DEV__ ? () => {
1871
+ warnDevOnce(
1872
+ "useViewability",
1873
+ `useViewability has been moved from a render prop to a regular import: import { useViewability } from "@legendapp/list";`
1819
1874
  );
1820
1875
  } : void 0;
1821
- const useViewabilityAmount2 = __DEV__ ? (callback) => {
1822
- console.warn(
1823
- `[legend-list] useViewabilityAmount has been moved from a render prop to a regular import: import { useViewabilityAmount } from "@legendapp/list";`
1876
+ const useViewabilityAmount2 = __DEV__ ? () => {
1877
+ warnDevOnce(
1878
+ "useViewabilityAmount",
1879
+ `useViewabilityAmount has been moved from a render prop to a regular import: import { useViewabilityAmount } from "@legendapp/list";`
1824
1880
  );
1825
1881
  } : void 0;
1826
- const useRecyclingEffect2 = __DEV__ ? (effect) => {
1827
- console.warn(
1828
- `[legend-list] useRecyclingEffect has been moved from a render prop to a regular import: import { useRecyclingEffect } from "@legendapp/list";`
1882
+ const useRecyclingEffect2 = __DEV__ ? () => {
1883
+ warnDevOnce(
1884
+ "useRecyclingEffect",
1885
+ `useRecyclingEffect has been moved from a render prop to a regular import: import { useRecyclingEffect } from "@legendapp/list";`
1829
1886
  );
1830
1887
  } : void 0;
1831
- const useRecyclingState2 = __DEV__ ? (valueOrFun) => {
1832
- console.warn(
1833
- `[legend-list] useRecyclingState has been moved from a render prop to a regular import: import { useRecyclingState } from "@legendapp/list";`
1888
+ const useRecyclingState2 = __DEV__ ? () => {
1889
+ warnDevOnce(
1890
+ "useRecyclingState",
1891
+ `useRecyclingState has been moved from a render prop to a regular import: import { useRecyclingState } from "@legendapp/list";`
1834
1892
  );
1835
1893
  } : void 0;
1836
1894
  const renderedItem = (_b = (_a = refState.current).renderItem) == null ? void 0 : _b.call(_a, {
@@ -1883,7 +1941,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1883
1941
  });
1884
1942
  const updateItemSize = useCallback((itemKey, size, fromFixGaps) => {
1885
1943
  const state = refState.current;
1886
- const { sizes, indexByKey, sizesKnown, data, rowHeights, startBuffered, endBuffered } = state;
1944
+ const { sizes, indexByKey, sizesKnown, data, rowHeights, startBuffered, endBuffered, averageSizes } = state;
1887
1945
  if (!data) {
1888
1946
  return;
1889
1947
  }
@@ -1901,7 +1959,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1901
1959
  needsUpdateContainersDidLayout = true;
1902
1960
  }
1903
1961
  }
1904
- sizesKnown == null ? void 0 : sizesKnown.set(itemKey, size);
1962
+ sizesKnown.set(itemKey, size);
1963
+ const itemType = "";
1964
+ let averages = averageSizes[itemType];
1965
+ if (!averages) {
1966
+ averages = averageSizes[itemType] = {
1967
+ num: 0,
1968
+ avg: 0
1969
+ };
1970
+ }
1971
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
1972
+ averages.num++;
1905
1973
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
1906
1974
  let diff;
1907
1975
  needsCalculate = true;
@@ -1916,19 +1984,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1916
1984
  sizes.set(itemKey, size);
1917
1985
  diff = size - prevSize;
1918
1986
  }
1919
- if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
1987
+ if (__DEV__ && suggestEstimatedItemSize) {
1920
1988
  if (state.timeoutSizeMessage) {
1921
1989
  clearTimeout(state.timeoutSizeMessage);
1922
1990
  }
1923
1991
  state.timeoutSizeMessage = setTimeout(() => {
1924
1992
  state.timeoutSizeMessage = void 0;
1925
- let total = 0;
1926
- let num = 0;
1927
- for (const [_, size2] of sizesKnown) {
1928
- num++;
1929
- total += size2;
1930
- }
1931
- const avg = Math.round(total / num);
1993
+ const num = sizesKnown.size;
1994
+ const avg = state.averageSizes[""].avg;
1932
1995
  console.warn(
1933
1996
  `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
1934
1997
  );
@@ -1988,7 +2051,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1988
2051
  const isWidthZero = event.nativeEvent.layout.width === 0;
1989
2052
  const isHeightZero = event.nativeEvent.layout.height === 0;
1990
2053
  if (isWidthZero || isHeightZero) {
1991
- console.warn(
2054
+ warnDevOnce(
2055
+ "height0",
1992
2056
  `[legend-list] List ${isWidthZero ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
1993
2057
  );
1994
2058
  }
@@ -2155,7 +2219,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2155
2219
  alignItemsAtEnd,
2156
2220
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
2157
2221
  maintainVisibleContentPosition,
2158
- scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : Platform.OS === "web" ? 16 : void 0,
2222
+ scrollEventThrottle: Platform.OS === "web" ? 16 : void 0,
2159
2223
  waitForInitialLayout,
2160
2224
  refreshControl: refreshControl != null ? refreshControl : onRefresh && /* @__PURE__ */ React6.createElement(
2161
2225
  RefreshControl,
@@ -190,7 +190,7 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
190
190
  }> | undefined;
191
191
  } & {
192
192
  ref?: React.Ref<LegendListRef>;
193
- }) => React.ReactElement | null) | (<T>(props: Omit<react_native.ScrollViewProps, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
193
+ }) => React.ReactElement | null) | (<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
194
194
  alignItemsAtEnd?: boolean;
195
195
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
196
196
  data: readonly T[];
@@ -237,10 +237,11 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
237
237
  refreshing?: boolean;
238
238
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<T>) => React.ReactNode) | undefined;
239
239
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
240
+ suggestEstimatedItemSize?: boolean;
240
241
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
241
242
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
242
243
  waitForInitialLayout?: boolean;
243
- } & React.RefAttributes<LegendListRef>) => React.ReactNode) | react_native.Animated.AnimatedComponent<(<T>(props: Omit<react_native.ScrollViewProps, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
244
+ } & React.RefAttributes<LegendListRef>) => React.ReactNode) | react_native.Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
244
245
  alignItemsAtEnd?: boolean;
245
246
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
246
247
  data: readonly T[];
@@ -287,10 +288,11 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
287
288
  refreshing?: boolean;
288
289
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<T>) => React.ReactNode) | undefined;
289
290
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
291
+ suggestEstimatedItemSize?: boolean;
290
292
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
291
293
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
292
294
  waitForInitialLayout?: boolean;
293
- } & React.RefAttributes<LegendListRef>) => React.ReactNode)> = <T>(props: Omit<react_native.ScrollViewProps, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
295
+ } & React.RefAttributes<LegendListRef>) => React.ReactNode)> = <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
294
296
  alignItemsAtEnd?: boolean;
295
297
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
296
298
  data: readonly T[];
@@ -337,10 +339,11 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
337
339
  refreshing?: boolean;
338
340
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<T>) => React.ReactNode) | undefined;
339
341
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
342
+ suggestEstimatedItemSize?: boolean;
340
343
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
341
344
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
342
345
  waitForInitialLayout?: boolean;
343
- } & React.RefAttributes<LegendListRef>) => React.ReactNode>(props: Omit<react_native.ScrollViewProps, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
346
+ } & React.RefAttributes<LegendListRef>) => React.ReactNode>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
344
347
  alignItemsAtEnd?: boolean;
345
348
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
346
349
  data: readonly ItemT[];
@@ -387,6 +390,7 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
387
390
  refreshing?: boolean;
388
391
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<ItemT>) => React.ReactNode) | undefined;
389
392
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
393
+ suggestEstimatedItemSize?: boolean;
390
394
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
391
395
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
392
396
  waitForInitialLayout?: boolean;
@@ -190,7 +190,7 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
190
190
  }> | undefined;
191
191
  } & {
192
192
  ref?: React.Ref<LegendListRef>;
193
- }) => React.ReactElement | null) | (<T>(props: Omit<react_native.ScrollViewProps, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
193
+ }) => React.ReactElement | null) | (<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
194
194
  alignItemsAtEnd?: boolean;
195
195
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
196
196
  data: readonly T[];
@@ -237,10 +237,11 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
237
237
  refreshing?: boolean;
238
238
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<T>) => React.ReactNode) | undefined;
239
239
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
240
+ suggestEstimatedItemSize?: boolean;
240
241
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
241
242
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
242
243
  waitForInitialLayout?: boolean;
243
- } & React.RefAttributes<LegendListRef>) => React.ReactNode) | react_native.Animated.AnimatedComponent<(<T>(props: Omit<react_native.ScrollViewProps, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
244
+ } & React.RefAttributes<LegendListRef>) => React.ReactNode) | react_native.Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
244
245
  alignItemsAtEnd?: boolean;
245
246
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
246
247
  data: readonly T[];
@@ -287,10 +288,11 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
287
288
  refreshing?: boolean;
288
289
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<T>) => React.ReactNode) | undefined;
289
290
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
291
+ suggestEstimatedItemSize?: boolean;
290
292
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
291
293
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
292
294
  waitForInitialLayout?: boolean;
293
- } & React.RefAttributes<LegendListRef>) => React.ReactNode)> = <T>(props: Omit<react_native.ScrollViewProps, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
295
+ } & React.RefAttributes<LegendListRef>) => React.ReactNode)> = <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
294
296
  alignItemsAtEnd?: boolean;
295
297
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
296
298
  data: readonly T[];
@@ -337,10 +339,11 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
337
339
  refreshing?: boolean;
338
340
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<T>) => React.ReactNode) | undefined;
339
341
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
342
+ suggestEstimatedItemSize?: boolean;
340
343
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
341
344
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
342
345
  waitForInitialLayout?: boolean;
343
- } & React.RefAttributes<LegendListRef>) => React.ReactNode>(props: Omit<react_native.ScrollViewProps, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
346
+ } & React.RefAttributes<LegendListRef>) => React.ReactNode>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "stickyHeaderIndices" | "contentInset" | "contentOffset" | "maintainVisibleContentPosition"> & {
344
347
  alignItemsAtEnd?: boolean;
345
348
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
346
349
  data: readonly ItemT[];
@@ -387,6 +390,7 @@ declare const LegendList: <ItemT, ListT extends (<ItemT_1>(props: Omit<_legendap
387
390
  refreshing?: boolean;
388
391
  renderItem?: ((props: _legendapp_list.LegendListRenderItemProps<ItemT>) => React.ReactNode) | undefined;
389
392
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
393
+ suggestEstimatedItemSize?: boolean;
390
394
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
391
395
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
392
396
  waitForInitialLayout?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "1.0.0-beta.50",
3
+ "version": "1.0.0-beta.51",
4
4
  "description": "Legend List aims to be a drop-in replacement for FlatList with much better performance and supporting dynamically sized items.",
5
5
  "sideEffects": false,
6
6
  "private": false,