@legendapp/list 1.0.13 → 1.0.15

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<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
6
+ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "maintainVisibleContentPosition" | "removeClippedSubviews" | "stickyHeaderIndices" | "contentInset" | "contentOffset"> & {
7
7
  alignItemsAtEnd?: boolean;
8
8
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
9
9
  data: readonly T[];
@@ -54,6 +54,9 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Om
54
54
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
55
55
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
56
56
  waitForInitialLayout?: boolean;
57
+ onLoad?: (info: {
58
+ elapsedTimeInMs: number;
59
+ }) => void;
57
60
  } & React$1.RefAttributes<_legendapp_list.LegendListRef>) => React.ReactNode)>;
58
61
 
59
62
  export { AnimatedLegendList };
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<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
6
+ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "maintainVisibleContentPosition" | "removeClippedSubviews" | "stickyHeaderIndices" | "contentInset" | "contentOffset"> & {
7
7
  alignItemsAtEnd?: boolean;
8
8
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
9
9
  data: readonly T[];
@@ -54,6 +54,9 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<Om
54
54
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
55
55
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
56
56
  waitForInitialLayout?: boolean;
57
+ onLoad?: (info: {
58
+ elapsedTimeInMs: number;
59
+ }) => void;
57
60
  } & React$1.RefAttributes<_legendapp_list.LegendListRef>) => React.ReactNode)>;
58
61
 
59
62
  export { AnimatedLegendList };
package/index.d.mts CHANGED
@@ -208,6 +208,9 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
208
208
  * @default false
209
209
  */
210
210
  waitForInitialLayout?: boolean;
211
+ onLoad?: (info: {
212
+ elapsedTimeInMs: number;
213
+ }) => void;
211
214
  };
212
215
  interface ColumnWrapperStyle {
213
216
  rowGap?: number;
@@ -280,6 +283,7 @@ interface InternalState {
280
283
  disableScrollJumpsFrom?: number;
281
284
  scrollingToOffset?: number | undefined;
282
285
  previousTotalSize?: number;
286
+ needsOtherAxisSize?: boolean;
283
287
  averageSizes: Record<string, {
284
288
  num: number;
285
289
  avg: number;
@@ -464,7 +468,7 @@ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqua
464
468
  };
465
469
  declare const typedMemo: TypedMemo;
466
470
 
467
- declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
471
+ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "maintainVisibleContentPosition" | "removeClippedSubviews" | "stickyHeaderIndices" | "contentInset" | "contentOffset"> & {
468
472
  alignItemsAtEnd?: boolean;
469
473
  columnWrapperStyle?: ColumnWrapperStyle;
470
474
  data: readonly T[];
@@ -515,11 +519,15 @@ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "sc
515
519
  viewabilityConfig?: ViewabilityConfig;
516
520
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
517
521
  waitForInitialLayout?: boolean;
522
+ onLoad?: (info: {
523
+ elapsedTimeInMs: number;
524
+ }) => void;
518
525
  } & React$1.RefAttributes<LegendListRef>) => React$1.ReactNode;
519
526
 
520
527
  declare function useViewability(callback: ViewabilityCallback, configId?: string): void;
521
528
  declare function useViewabilityAmount(callback: ViewabilityAmountCallback): void;
522
529
  declare function useRecyclingEffect(effect: (info: LegendListRecyclingState<unknown>) => void | (() => void)): void;
523
530
  declare function useRecyclingState<ItemT>(valueOrFun: ((info: LegendListRecyclingState<ItemT>) => ItemT) | ItemT): readonly [ItemT | null, Dispatch<SetStateAction<ItemT>>];
531
+ declare function useIsLastItem(): any;
524
532
 
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 };
533
+ 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, useIsLastItem, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
package/index.d.ts CHANGED
@@ -208,6 +208,9 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
208
208
  * @default false
209
209
  */
210
210
  waitForInitialLayout?: boolean;
211
+ onLoad?: (info: {
212
+ elapsedTimeInMs: number;
213
+ }) => void;
211
214
  };
212
215
  interface ColumnWrapperStyle {
213
216
  rowGap?: number;
@@ -280,6 +283,7 @@ interface InternalState {
280
283
  disableScrollJumpsFrom?: number;
281
284
  scrollingToOffset?: number | undefined;
282
285
  previousTotalSize?: number;
286
+ needsOtherAxisSize?: boolean;
283
287
  averageSizes: Record<string, {
284
288
  num: number;
285
289
  avg: number;
@@ -464,7 +468,7 @@ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqua
464
468
  };
465
469
  declare const typedMemo: TypedMemo;
466
470
 
467
- declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
471
+ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "maintainVisibleContentPosition" | "removeClippedSubviews" | "stickyHeaderIndices" | "contentInset" | "contentOffset"> & {
468
472
  alignItemsAtEnd?: boolean;
469
473
  columnWrapperStyle?: ColumnWrapperStyle;
470
474
  data: readonly T[];
@@ -515,11 +519,15 @@ declare const LegendList: <T>(props: Omit<Omit<react_native.ScrollViewProps, "sc
515
519
  viewabilityConfig?: ViewabilityConfig;
516
520
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
517
521
  waitForInitialLayout?: boolean;
522
+ onLoad?: (info: {
523
+ elapsedTimeInMs: number;
524
+ }) => void;
518
525
  } & React$1.RefAttributes<LegendListRef>) => React$1.ReactNode;
519
526
 
520
527
  declare function useViewability(callback: ViewabilityCallback, configId?: string): void;
521
528
  declare function useViewabilityAmount(callback: ViewabilityAmountCallback): void;
522
529
  declare function useRecyclingEffect(effect: (info: LegendListRecyclingState<unknown>) => void | (() => void)): void;
523
530
  declare function useRecyclingState<ItemT>(valueOrFun: ((info: LegendListRecyclingState<ItemT>) => ItemT) | ItemT): readonly [ItemT | null, Dispatch<SetStateAction<ItemT>>];
531
+ declare function useIsLastItem(): any;
524
532
 
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 };
533
+ 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, useIsLastItem, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
package/index.js CHANGED
@@ -120,6 +120,12 @@ function useArr$(signalNames) {
120
120
  const value = shim.useSyncExternalStore(subscribe, get);
121
121
  return value;
122
122
  }
123
+ function useSelector$(signalName, selector) {
124
+ const ctx = React2__namespace.useContext(ContextState);
125
+ const { subscribe, get } = React2__namespace.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
126
+ const value = shim.useSyncExternalStore(subscribe, () => selector(get()[0]));
127
+ return value;
128
+ }
123
129
 
124
130
  // src/DebugView.tsx
125
131
  var DebugRow = ({ children }) => {
@@ -308,6 +314,11 @@ function useRecyclingState(valueOrFun) {
308
314
  );
309
315
  return [refState.current.value, setState];
310
316
  }
317
+ function useIsLastItem() {
318
+ const { itemKey } = React2.useContext(ContextContainer);
319
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
320
+ return isLast;
321
+ }
311
322
  var LeanViewComponent = React2__namespace.forwardRef((props, ref) => {
312
323
  return React2__namespace.createElement("RCTView", { ...props, ref });
313
324
  });
@@ -366,12 +377,12 @@ var Container = ({
366
377
  if (horizontal) {
367
378
  paddingStyles = {
368
379
  paddingRight: !lastItemKeys.includes(itemKey) ? columnGap || gap || void 0 : void 0,
369
- paddingVertical: (rowGap || gap || 0) / 2
380
+ paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
370
381
  };
371
382
  } else {
372
383
  paddingStyles = {
373
384
  paddingBottom: !lastItemKeys.includes(itemKey) ? rowGap || gap || void 0 : void 0,
374
- paddingHorizontal: (columnGap || gap || 0) / 2
385
+ paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
375
386
  };
376
387
  }
377
388
  }
@@ -379,7 +390,6 @@ var Container = ({
379
390
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
380
391
  position: "absolute",
381
392
  top: otherAxisPos,
382
- bottom: numColumns > 1 ? null : 0,
383
393
  height: otherAxisSize,
384
394
  left: position.relativeCoordinate,
385
395
  ...paddingStyles || {}
@@ -402,17 +412,17 @@ var Container = ({
402
412
  const onLayout = (event) => {
403
413
  var _a, _b;
404
414
  if (!isNullOrUndefined(itemKey)) {
405
- const layout = event.nativeEvent.layout;
406
- let size = roundSize(layout[horizontal ? "width" : "height"]);
415
+ let layout = event.nativeEvent.layout;
416
+ const size = layout[horizontal ? "width" : "height"];
407
417
  const doUpdate = () => {
408
- refLastSize.current = size;
409
- updateItemSize(itemKey, size);
418
+ refLastSize.current = { width: layout.width, height: layout.height };
419
+ updateItemSize(itemKey, layout);
410
420
  };
411
421
  if (IsNewArchitecture || size > 0) {
412
422
  doUpdate();
413
423
  } else {
414
424
  (_b = (_a = ref.current) == null ? void 0 : _a.measure) == null ? void 0 : _b.call(_a, (x, y, width, height) => {
415
- size = roundSize(horizontal ? width : height);
425
+ layout = { width, height };
416
426
  doUpdate();
417
427
  });
418
428
  }
@@ -426,7 +436,7 @@ var Container = ({
426
436
  if (measured) {
427
437
  const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
428
438
  if (size) {
429
- updateItemSize(itemKey, size);
439
+ updateItemSize(itemKey, measured);
430
440
  }
431
441
  }
432
442
  }
@@ -451,7 +461,7 @@ var Container = ({
451
461
  }, [id, itemKey, index, data]);
452
462
  const contentFragment = /* @__PURE__ */ React2__namespace.default.createElement(React2__namespace.default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React2__namespace.default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && !lastItemKeys.includes(itemKey) && /* @__PURE__ */ React2__namespace.default.createElement(ItemSeparatorComponent, { leadingItem: renderedItemInfo.item })));
453
463
  if (maintainVisibleContentPosition) {
454
- const anchorStyle = position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
464
+ const anchorStyle = horizontal ? position.type === "top" ? { position: "absolute", left: 0, top: 0, bottom: 0, flexDirection: "row", alignItems: "stretch" } : { position: "absolute", right: 0, top: 0, bottom: 0, flexDirection: "row", alignItems: "stretch" } : position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
455
465
  if (__DEV__ && ENABLE_DEVMODE) {
456
466
  anchorStyle.borderColor = position.type === "top" ? "red" : "blue";
457
467
  anchorStyle.borderWidth = 1;
@@ -500,7 +510,7 @@ var Containers = typedMemo(function Containers2({
500
510
  }) {
501
511
  const ctx = useStateContext();
502
512
  const columnWrapperStyle = ctx.columnWrapperStyle;
503
- const [numContainers] = useArr$(["numContainersPooled"]);
513
+ const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
504
514
  const animSize = useValue$(
505
515
  "totalSizeWithScrollAdjust",
506
516
  void 0,
@@ -508,6 +518,12 @@ var Containers = typedMemo(function Containers2({
508
518
  true
509
519
  );
510
520
  const animOpacity = waitForInitialLayout ? useValue$("containersDidLayout", (value) => value ? 1 : 0) : void 0;
521
+ const otherAxisSize = useValue$(
522
+ "otherAxisSize",
523
+ void 0,
524
+ /*useMicrotask*/
525
+ true
526
+ );
511
527
  const containers = [];
512
528
  for (let i = 0; i < numContainers; i++) {
513
529
  containers.push(
@@ -525,8 +541,8 @@ var Containers = typedMemo(function Containers2({
525
541
  )
526
542
  );
527
543
  }
528
- const style = horizontal ? { width: animSize, opacity: animOpacity } : { height: animSize, opacity: animOpacity };
529
- if (columnWrapperStyle) {
544
+ const style = horizontal ? { width: animSize, opacity: animOpacity, minHeight: otherAxisSize } : { height: animSize, opacity: animOpacity, minWidth: otherAxisSize };
545
+ if (columnWrapperStyle && numColumns > 1) {
530
546
  const { columnGap, rowGap, gap } = columnWrapperStyle;
531
547
  if (horizontal) {
532
548
  const my = (rowGap || gap || 0) / 2;
@@ -1026,6 +1042,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1026
1042
  onViewableItemsChanged,
1027
1043
  ...rest
1028
1044
  } = props;
1045
+ const refLoadStartTime = React2.useRef(Date.now());
1029
1046
  const callbacks = React2.useRef({
1030
1047
  onStartReached: rest.onStartReached,
1031
1048
  onEndReached: rest.onEndReached
@@ -1230,6 +1247,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1230
1247
  const setDidLayout = () => {
1231
1248
  refState.current.queuedInitialLayout = true;
1232
1249
  checkAtBottom();
1250
+ const setIt = () => {
1251
+ set$(ctx, "containersDidLayout", true);
1252
+ if (props.onLoad) {
1253
+ props.onLoad({ elapsedTimeInMs: Date.now() - refLoadStartTime.current });
1254
+ }
1255
+ };
1233
1256
  if (initialScrollIndex) {
1234
1257
  queueMicrotask(() => {
1235
1258
  scrollToIndex({ index: initialScrollIndex, animated: false });
@@ -1237,13 +1260,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1237
1260
  if (!IsNewArchitecture) {
1238
1261
  scrollToIndex({ index: initialScrollIndex, animated: false });
1239
1262
  }
1240
- set$(ctx, "containersDidLayout", true);
1263
+ setIt();
1241
1264
  });
1242
1265
  });
1243
1266
  } else {
1244
- queueMicrotask(() => {
1245
- set$(ctx, "containersDidLayout", true);
1246
- });
1267
+ queueMicrotask(setIt);
1247
1268
  }
1248
1269
  };
1249
1270
  const addTotalSize = React2.useCallback((key, add, totalSizeBelowAnchor) => {
@@ -1375,10 +1396,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1375
1396
  });
1376
1397
  numMeasurements++;
1377
1398
  if (measured) {
1378
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
1379
1399
  updateItemSize(
1380
1400
  itemKey,
1381
- size,
1401
+ measured,
1382
1402
  /*fromFixGaps*/
1383
1403
  true
1384
1404
  );
@@ -1476,13 +1496,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1476
1496
  scrollBufferBottom = scrollBuffer * 0.1;
1477
1497
  }
1478
1498
  }
1499
+ const scrollTopBuffered = scroll - scrollBufferTop;
1500
+ const scrollBottom = scroll + scrollLength;
1501
+ const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
1479
1502
  if (state.scrollForNextCalculateItemsInView) {
1480
1503
  const { top: top2, bottom } = state.scrollForNextCalculateItemsInView;
1481
- if (scroll > top2 && scroll < bottom) {
1504
+ if (scrollTopBuffered > top2 && scrollBottomBuffered < bottom) {
1482
1505
  return;
1483
1506
  }
1484
1507
  }
1485
- const scrollBottom = scroll + scrollLength;
1486
1508
  let startNoBuffer = null;
1487
1509
  let startBuffered = null;
1488
1510
  let startBufferedId = null;
@@ -1562,7 +1584,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1562
1584
  if (startNoBuffer === null && top + size > scroll) {
1563
1585
  startNoBuffer = i;
1564
1586
  }
1565
- if (startBuffered === null && top + size > scroll - scrollBufferTop) {
1587
+ if (startBuffered === null && top + size > scrollTopBuffered) {
1566
1588
  startBuffered = i;
1567
1589
  startBufferedId = id;
1568
1590
  nextTop = top;
@@ -1571,7 +1593,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1571
1593
  if (top <= scrollBottom) {
1572
1594
  endNoBuffer = i;
1573
1595
  }
1574
- if (top <= scrollBottom + scrollBufferBottom) {
1596
+ if (top <= scrollBottomBuffered) {
1575
1597
  endBuffered = i;
1576
1598
  nextBottom = top + maxSizeInRow - scrollLength;
1577
1599
  } else {
@@ -1860,8 +1882,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1860
1882
  for (let i = 0; i < numContainers; i++) {
1861
1883
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1862
1884
  if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
1863
- set$(ctx, `containerItemKey${i}`, null);
1864
- set$(ctx, `containerItemData${i}`, null);
1885
+ set$(ctx, `containerItemKey${i}`, void 0);
1886
+ set$(ctx, `containerItemData${i}`, void 0);
1865
1887
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1866
1888
  set$(ctx, `containerColumn${i}`, -1);
1867
1889
  }
@@ -2142,111 +2164,130 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2142
2164
  useInit(() => {
2143
2165
  doInitialAllocateContainers();
2144
2166
  });
2145
- const updateItemSize = React2.useCallback((itemKey, size, fromFixGaps) => {
2146
- const state = refState.current;
2147
- const {
2148
- sizes,
2149
- indexByKey,
2150
- sizesKnown,
2151
- data,
2152
- rowHeights,
2153
- startBuffered,
2154
- endBuffered,
2155
- averageSizes,
2156
- queuedInitialLayout
2157
- } = state;
2158
- if (!data) {
2159
- return;
2160
- }
2161
- const index = indexByKey.get(itemKey);
2162
- const numColumns = peek$(ctx, "numColumns");
2163
- state.scrollForNextCalculateItemsInView = void 0;
2164
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
2165
- const prevSize = getItemSize(itemKey, index, data);
2166
- const prevSizeKnown = sizesKnown.get(itemKey);
2167
- let needsCalculate = false;
2168
- let needsUpdateContainersDidLayout = false;
2169
- sizesKnown.set(itemKey, size);
2170
- const itemType = "";
2171
- let averages = averageSizes[itemType];
2172
- if (!averages) {
2173
- averages = averageSizes[itemType] = {
2174
- num: 0,
2175
- avg: 0
2176
- };
2177
- }
2178
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2179
- averages.num++;
2180
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2181
- let diff;
2182
- needsCalculate = true;
2183
- if (numColumns > 1) {
2184
- const rowNumber = Math.floor(index / numColumnsProp);
2185
- const prevSizeInRow = getRowHeight(rowNumber);
2186
- sizes.set(itemKey, size);
2187
- rowHeights.delete(rowNumber);
2188
- const sizeInRow = getRowHeight(rowNumber);
2189
- diff = sizeInRow - prevSizeInRow;
2190
- } else {
2191
- sizes.set(itemKey, size);
2192
- diff = size - prevSize;
2167
+ const updateItemSize = React2.useCallback(
2168
+ (itemKey, sizeObj, fromFixGaps) => {
2169
+ const state = refState.current;
2170
+ const {
2171
+ sizes,
2172
+ indexByKey,
2173
+ sizesKnown,
2174
+ data,
2175
+ rowHeights,
2176
+ startBuffered,
2177
+ endBuffered,
2178
+ averageSizes,
2179
+ queuedInitialLayout
2180
+ } = state;
2181
+ if (!data) {
2182
+ return;
2183
+ }
2184
+ const index = indexByKey.get(itemKey);
2185
+ const numColumns = peek$(ctx, "numColumns");
2186
+ state.scrollForNextCalculateItemsInView = void 0;
2187
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
2188
+ const prevSize = getItemSize(itemKey, index, data);
2189
+ const prevSizeKnown = sizesKnown.get(itemKey);
2190
+ let needsCalculate = false;
2191
+ let needsUpdateContainersDidLayout = false;
2192
+ const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2193
+ sizesKnown.set(itemKey, size);
2194
+ const itemType = "";
2195
+ let averages = averageSizes[itemType];
2196
+ if (!averages) {
2197
+ averages = averageSizes[itemType] = {
2198
+ num: 0,
2199
+ avg: 0
2200
+ };
2193
2201
  }
2194
- if (__DEV__ && suggestEstimatedItemSize) {
2195
- if (state.timeoutSizeMessage) {
2196
- clearTimeout(state.timeoutSizeMessage);
2202
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2203
+ averages.num++;
2204
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2205
+ let diff;
2206
+ needsCalculate = true;
2207
+ if (numColumns > 1) {
2208
+ const rowNumber = Math.floor(index / numColumnsProp);
2209
+ const prevSizeInRow = getRowHeight(rowNumber);
2210
+ sizes.set(itemKey, size);
2211
+ rowHeights.delete(rowNumber);
2212
+ const sizeInRow = getRowHeight(rowNumber);
2213
+ diff = sizeInRow - prevSizeInRow;
2214
+ } else {
2215
+ sizes.set(itemKey, size);
2216
+ diff = size - prevSize;
2217
+ }
2218
+ if (__DEV__ && suggestEstimatedItemSize) {
2219
+ if (state.timeoutSizeMessage) {
2220
+ clearTimeout(state.timeoutSizeMessage);
2221
+ }
2222
+ state.timeoutSizeMessage = setTimeout(() => {
2223
+ state.timeoutSizeMessage = void 0;
2224
+ const num = sizesKnown.size;
2225
+ const avg = state.averageSizes[""].avg;
2226
+ console.warn(
2227
+ `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
2228
+ );
2229
+ }, 1e3);
2230
+ }
2231
+ state.scrollForNextCalculateItemsInView = void 0;
2232
+ addTotalSize(itemKey, diff, 0);
2233
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2234
+ doMaintainScrollAtEnd(false);
2235
+ }
2236
+ if (onItemSizeChanged) {
2237
+ onItemSizeChanged({
2238
+ size,
2239
+ previous: prevSize,
2240
+ index,
2241
+ itemKey,
2242
+ itemData: data[index]
2243
+ });
2197
2244
  }
2198
- state.timeoutSizeMessage = setTimeout(() => {
2199
- state.timeoutSizeMessage = void 0;
2200
- const num = sizesKnown.size;
2201
- const avg = state.averageSizes[""].avg;
2202
- console.warn(
2203
- `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
2204
- );
2205
- }, 1e3);
2206
2245
  }
2207
- state.scrollForNextCalculateItemsInView = void 0;
2208
- addTotalSize(itemKey, diff, 0);
2209
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2210
- doMaintainScrollAtEnd(false);
2211
- }
2212
- if (onItemSizeChanged) {
2213
- onItemSizeChanged({
2214
- size,
2215
- previous: prevSize,
2216
- index,
2217
- itemKey,
2218
- itemData: data[index]
2219
- });
2246
+ if (!queuedInitialLayout && checkAllSizesKnown()) {
2247
+ needsUpdateContainersDidLayout = true;
2220
2248
  }
2221
- }
2222
- if (!queuedInitialLayout && checkAllSizesKnown()) {
2223
- needsUpdateContainersDidLayout = true;
2224
- }
2225
- const isInView = index >= startBuffered && index <= endBuffered;
2226
- if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && (isInView || !queuedInitialLayout)) {
2227
- const scrollVelocity = state.scrollVelocity;
2228
- let didCalculate = false;
2229
- if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1 || state.scrollingToOffset !== void 0) && (!waitForInitialLayout || needsUpdateContainersDidLayout || queuedInitialLayout)) {
2230
- if (Date.now() - state.lastBatchingAction < 500) {
2231
- if (!state.queuedCalculateItemsInView) {
2232
- state.queuedCalculateItemsInView = requestAnimationFrame(() => {
2233
- state.queuedCalculateItemsInView = void 0;
2234
- calculateItemsInView();
2235
- });
2249
+ let isInView = index >= startBuffered && index <= endBuffered;
2250
+ if (!isInView) {
2251
+ const numContainers = ctx.values.get("numContainers");
2252
+ for (let i = 0; i < numContainers; i++) {
2253
+ if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2254
+ isInView = true;
2255
+ break;
2236
2256
  }
2237
- } else {
2238
- calculateItemsInView();
2239
- didCalculate = true;
2240
2257
  }
2241
2258
  }
2242
- if (!didCalculate && !needsUpdateContainersDidLayout && IsNewArchitecture) {
2243
- fixGaps();
2259
+ if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && (isInView || !queuedInitialLayout)) {
2260
+ const scrollVelocity = state.scrollVelocity;
2261
+ let didCalculate = false;
2262
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1 || state.scrollingToOffset !== void 0) && (!waitForInitialLayout || needsUpdateContainersDidLayout || queuedInitialLayout)) {
2263
+ if (Date.now() - state.lastBatchingAction < 500) {
2264
+ if (!state.queuedCalculateItemsInView) {
2265
+ state.queuedCalculateItemsInView = requestAnimationFrame(() => {
2266
+ state.queuedCalculateItemsInView = void 0;
2267
+ calculateItemsInView();
2268
+ });
2269
+ }
2270
+ } else {
2271
+ calculateItemsInView();
2272
+ didCalculate = true;
2273
+ }
2274
+ }
2275
+ if (!didCalculate && !needsUpdateContainersDidLayout && IsNewArchitecture) {
2276
+ fixGaps();
2277
+ }
2244
2278
  }
2245
- }
2246
- }, []);
2247
- const onLayout = React2.useCallback((event) => {
2279
+ if (state.needsOtherAxisSize) {
2280
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
2281
+ const cur = peek$(ctx, "otherAxisSize");
2282
+ if (!cur || otherAxisSize > cur) {
2283
+ set$(ctx, "otherAxisSize", otherAxisSize);
2284
+ }
2285
+ }
2286
+ },
2287
+ []
2288
+ );
2289
+ const handleLayout = React2.useCallback((scrollLength) => {
2248
2290
  const state = refState.current;
2249
- const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
2250
2291
  const didChange = scrollLength !== state.scrollLength;
2251
2292
  state.scrollLength = scrollLength;
2252
2293
  state.lastBatchingAction = Date.now();
@@ -2259,20 +2300,36 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2259
2300
  if (didChange) {
2260
2301
  calculateItemsInView();
2261
2302
  }
2262
- if (__DEV__) {
2263
- const isWidthZero = event.nativeEvent.layout.width === 0;
2264
- const isHeightZero = event.nativeEvent.layout.height === 0;
2265
- if (isWidthZero || isHeightZero) {
2266
- warnDevOnce(
2267
- "height0",
2268
- `[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.`
2269
- );
2270
- }
2303
+ }, []);
2304
+ const onLayout = React2.useCallback((event) => {
2305
+ const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
2306
+ handleLayout(scrollLength);
2307
+ const otherAxisSize = event.nativeEvent.layout[horizontal ? "height" : "width"];
2308
+ if (refState.current) {
2309
+ refState.current.needsOtherAxisSize = otherAxisSize - (stylePaddingTopState || 0) < 10;
2310
+ }
2311
+ if (__DEV__ && scrollLength === 0) {
2312
+ warnDevOnce(
2313
+ "height0",
2314
+ `List ${horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
2315
+ );
2271
2316
  }
2272
2317
  if (onLayoutProp) {
2273
2318
  onLayoutProp(event);
2274
2319
  }
2275
2320
  }, []);
2321
+ if (IsNewArchitecture) {
2322
+ React2.useLayoutEffect(() => {
2323
+ var _a, _b;
2324
+ const measured = (_b = (_a = refScroller.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
2325
+ if (measured) {
2326
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2327
+ if (size) {
2328
+ handleLayout(size);
2329
+ }
2330
+ }
2331
+ }, []);
2332
+ }
2276
2333
  const handleScroll = React2.useCallback(
2277
2334
  (event) => {
2278
2335
  var _a, _b, _c, _d;
@@ -2471,6 +2528,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2471
2528
  });
2472
2529
 
2473
2530
  exports.LegendList = LegendList;
2531
+ exports.useIsLastItem = useIsLastItem;
2474
2532
  exports.useRecyclingEffect = useRecyclingEffect;
2475
2533
  exports.useRecyclingState = useRecyclingState;
2476
2534
  exports.useViewability = useViewability;
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React2 from 'react';
2
- import React2__default, { useReducer, useEffect, createContext, useMemo, useRef, useCallback, useImperativeHandle, useContext, useState, forwardRef, memo, useLayoutEffect } from 'react';
2
+ import React2__default, { useReducer, useEffect, createContext, useMemo, useRef, useCallback, useLayoutEffect, useImperativeHandle, useContext, useState, forwardRef, memo } from 'react';
3
3
  import { View, Text, Platform, Animated, ScrollView, StyleSheet, Dimensions, RefreshControl } from 'react-native';
4
4
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
5
5
 
@@ -99,6 +99,12 @@ function useArr$(signalNames) {
99
99
  const value = useSyncExternalStore(subscribe, get);
100
100
  return value;
101
101
  }
102
+ function useSelector$(signalName, selector) {
103
+ const ctx = React2.useContext(ContextState);
104
+ const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
105
+ const value = useSyncExternalStore(subscribe, () => selector(get()[0]));
106
+ return value;
107
+ }
102
108
 
103
109
  // src/DebugView.tsx
104
110
  var DebugRow = ({ children }) => {
@@ -287,6 +293,11 @@ function useRecyclingState(valueOrFun) {
287
293
  );
288
294
  return [refState.current.value, setState];
289
295
  }
296
+ function useIsLastItem() {
297
+ const { itemKey } = useContext(ContextContainer);
298
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
299
+ return isLast;
300
+ }
290
301
  var LeanViewComponent = React2.forwardRef((props, ref) => {
291
302
  return React2.createElement("RCTView", { ...props, ref });
292
303
  });
@@ -345,12 +356,12 @@ var Container = ({
345
356
  if (horizontal) {
346
357
  paddingStyles = {
347
358
  paddingRight: !lastItemKeys.includes(itemKey) ? columnGap || gap || void 0 : void 0,
348
- paddingVertical: (rowGap || gap || 0) / 2
359
+ paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
349
360
  };
350
361
  } else {
351
362
  paddingStyles = {
352
363
  paddingBottom: !lastItemKeys.includes(itemKey) ? rowGap || gap || void 0 : void 0,
353
- paddingHorizontal: (columnGap || gap || 0) / 2
364
+ paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
354
365
  };
355
366
  }
356
367
  }
@@ -358,7 +369,6 @@ var Container = ({
358
369
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
359
370
  position: "absolute",
360
371
  top: otherAxisPos,
361
- bottom: numColumns > 1 ? null : 0,
362
372
  height: otherAxisSize,
363
373
  left: position.relativeCoordinate,
364
374
  ...paddingStyles || {}
@@ -381,17 +391,17 @@ var Container = ({
381
391
  const onLayout = (event) => {
382
392
  var _a, _b;
383
393
  if (!isNullOrUndefined(itemKey)) {
384
- const layout = event.nativeEvent.layout;
385
- let size = roundSize(layout[horizontal ? "width" : "height"]);
394
+ let layout = event.nativeEvent.layout;
395
+ const size = layout[horizontal ? "width" : "height"];
386
396
  const doUpdate = () => {
387
- refLastSize.current = size;
388
- updateItemSize(itemKey, size);
397
+ refLastSize.current = { width: layout.width, height: layout.height };
398
+ updateItemSize(itemKey, layout);
389
399
  };
390
400
  if (IsNewArchitecture || size > 0) {
391
401
  doUpdate();
392
402
  } else {
393
403
  (_b = (_a = ref.current) == null ? void 0 : _a.measure) == null ? void 0 : _b.call(_a, (x, y, width, height) => {
394
- size = roundSize(horizontal ? width : height);
404
+ layout = { width, height };
395
405
  doUpdate();
396
406
  });
397
407
  }
@@ -405,7 +415,7 @@ var Container = ({
405
415
  if (measured) {
406
416
  const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
407
417
  if (size) {
408
- updateItemSize(itemKey, size);
418
+ updateItemSize(itemKey, measured);
409
419
  }
410
420
  }
411
421
  }
@@ -430,7 +440,7 @@ var Container = ({
430
440
  }, [id, itemKey, index, data]);
431
441
  const contentFragment = /* @__PURE__ */ React2__default.createElement(React2__default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React2__default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && !lastItemKeys.includes(itemKey) && /* @__PURE__ */ React2__default.createElement(ItemSeparatorComponent, { leadingItem: renderedItemInfo.item })));
432
442
  if (maintainVisibleContentPosition) {
433
- const anchorStyle = position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
443
+ const anchorStyle = horizontal ? position.type === "top" ? { position: "absolute", left: 0, top: 0, bottom: 0, flexDirection: "row", alignItems: "stretch" } : { position: "absolute", right: 0, top: 0, bottom: 0, flexDirection: "row", alignItems: "stretch" } : position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
434
444
  if (__DEV__ && ENABLE_DEVMODE) {
435
445
  anchorStyle.borderColor = position.type === "top" ? "red" : "blue";
436
446
  anchorStyle.borderWidth = 1;
@@ -479,7 +489,7 @@ var Containers = typedMemo(function Containers2({
479
489
  }) {
480
490
  const ctx = useStateContext();
481
491
  const columnWrapperStyle = ctx.columnWrapperStyle;
482
- const [numContainers] = useArr$(["numContainersPooled"]);
492
+ const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
483
493
  const animSize = useValue$(
484
494
  "totalSizeWithScrollAdjust",
485
495
  void 0,
@@ -487,6 +497,12 @@ var Containers = typedMemo(function Containers2({
487
497
  true
488
498
  );
489
499
  const animOpacity = waitForInitialLayout ? useValue$("containersDidLayout", (value) => value ? 1 : 0) : void 0;
500
+ const otherAxisSize = useValue$(
501
+ "otherAxisSize",
502
+ void 0,
503
+ /*useMicrotask*/
504
+ true
505
+ );
490
506
  const containers = [];
491
507
  for (let i = 0; i < numContainers; i++) {
492
508
  containers.push(
@@ -504,8 +520,8 @@ var Containers = typedMemo(function Containers2({
504
520
  )
505
521
  );
506
522
  }
507
- const style = horizontal ? { width: animSize, opacity: animOpacity } : { height: animSize, opacity: animOpacity };
508
- if (columnWrapperStyle) {
523
+ const style = horizontal ? { width: animSize, opacity: animOpacity, minHeight: otherAxisSize } : { height: animSize, opacity: animOpacity, minWidth: otherAxisSize };
524
+ if (columnWrapperStyle && numColumns > 1) {
509
525
  const { columnGap, rowGap, gap } = columnWrapperStyle;
510
526
  if (horizontal) {
511
527
  const my = (rowGap || gap || 0) / 2;
@@ -1005,6 +1021,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1005
1021
  onViewableItemsChanged,
1006
1022
  ...rest
1007
1023
  } = props;
1024
+ const refLoadStartTime = useRef(Date.now());
1008
1025
  const callbacks = useRef({
1009
1026
  onStartReached: rest.onStartReached,
1010
1027
  onEndReached: rest.onEndReached
@@ -1209,6 +1226,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1209
1226
  const setDidLayout = () => {
1210
1227
  refState.current.queuedInitialLayout = true;
1211
1228
  checkAtBottom();
1229
+ const setIt = () => {
1230
+ set$(ctx, "containersDidLayout", true);
1231
+ if (props.onLoad) {
1232
+ props.onLoad({ elapsedTimeInMs: Date.now() - refLoadStartTime.current });
1233
+ }
1234
+ };
1212
1235
  if (initialScrollIndex) {
1213
1236
  queueMicrotask(() => {
1214
1237
  scrollToIndex({ index: initialScrollIndex, animated: false });
@@ -1216,13 +1239,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1216
1239
  if (!IsNewArchitecture) {
1217
1240
  scrollToIndex({ index: initialScrollIndex, animated: false });
1218
1241
  }
1219
- set$(ctx, "containersDidLayout", true);
1242
+ setIt();
1220
1243
  });
1221
1244
  });
1222
1245
  } else {
1223
- queueMicrotask(() => {
1224
- set$(ctx, "containersDidLayout", true);
1225
- });
1246
+ queueMicrotask(setIt);
1226
1247
  }
1227
1248
  };
1228
1249
  const addTotalSize = useCallback((key, add, totalSizeBelowAnchor) => {
@@ -1354,10 +1375,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1354
1375
  });
1355
1376
  numMeasurements++;
1356
1377
  if (measured) {
1357
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
1358
1378
  updateItemSize(
1359
1379
  itemKey,
1360
- size,
1380
+ measured,
1361
1381
  /*fromFixGaps*/
1362
1382
  true
1363
1383
  );
@@ -1455,13 +1475,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1455
1475
  scrollBufferBottom = scrollBuffer * 0.1;
1456
1476
  }
1457
1477
  }
1478
+ const scrollTopBuffered = scroll - scrollBufferTop;
1479
+ const scrollBottom = scroll + scrollLength;
1480
+ const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
1458
1481
  if (state.scrollForNextCalculateItemsInView) {
1459
1482
  const { top: top2, bottom } = state.scrollForNextCalculateItemsInView;
1460
- if (scroll > top2 && scroll < bottom) {
1483
+ if (scrollTopBuffered > top2 && scrollBottomBuffered < bottom) {
1461
1484
  return;
1462
1485
  }
1463
1486
  }
1464
- const scrollBottom = scroll + scrollLength;
1465
1487
  let startNoBuffer = null;
1466
1488
  let startBuffered = null;
1467
1489
  let startBufferedId = null;
@@ -1541,7 +1563,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1541
1563
  if (startNoBuffer === null && top + size > scroll) {
1542
1564
  startNoBuffer = i;
1543
1565
  }
1544
- if (startBuffered === null && top + size > scroll - scrollBufferTop) {
1566
+ if (startBuffered === null && top + size > scrollTopBuffered) {
1545
1567
  startBuffered = i;
1546
1568
  startBufferedId = id;
1547
1569
  nextTop = top;
@@ -1550,7 +1572,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1550
1572
  if (top <= scrollBottom) {
1551
1573
  endNoBuffer = i;
1552
1574
  }
1553
- if (top <= scrollBottom + scrollBufferBottom) {
1575
+ if (top <= scrollBottomBuffered) {
1554
1576
  endBuffered = i;
1555
1577
  nextBottom = top + maxSizeInRow - scrollLength;
1556
1578
  } else {
@@ -1839,8 +1861,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1839
1861
  for (let i = 0; i < numContainers; i++) {
1840
1862
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1841
1863
  if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
1842
- set$(ctx, `containerItemKey${i}`, null);
1843
- set$(ctx, `containerItemData${i}`, null);
1864
+ set$(ctx, `containerItemKey${i}`, void 0);
1865
+ set$(ctx, `containerItemData${i}`, void 0);
1844
1866
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1845
1867
  set$(ctx, `containerColumn${i}`, -1);
1846
1868
  }
@@ -2121,111 +2143,130 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2121
2143
  useInit(() => {
2122
2144
  doInitialAllocateContainers();
2123
2145
  });
2124
- const updateItemSize = useCallback((itemKey, size, fromFixGaps) => {
2125
- const state = refState.current;
2126
- const {
2127
- sizes,
2128
- indexByKey,
2129
- sizesKnown,
2130
- data,
2131
- rowHeights,
2132
- startBuffered,
2133
- endBuffered,
2134
- averageSizes,
2135
- queuedInitialLayout
2136
- } = state;
2137
- if (!data) {
2138
- return;
2139
- }
2140
- const index = indexByKey.get(itemKey);
2141
- const numColumns = peek$(ctx, "numColumns");
2142
- state.scrollForNextCalculateItemsInView = void 0;
2143
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
2144
- const prevSize = getItemSize(itemKey, index, data);
2145
- const prevSizeKnown = sizesKnown.get(itemKey);
2146
- let needsCalculate = false;
2147
- let needsUpdateContainersDidLayout = false;
2148
- sizesKnown.set(itemKey, size);
2149
- const itemType = "";
2150
- let averages = averageSizes[itemType];
2151
- if (!averages) {
2152
- averages = averageSizes[itemType] = {
2153
- num: 0,
2154
- avg: 0
2155
- };
2156
- }
2157
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2158
- averages.num++;
2159
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2160
- let diff;
2161
- needsCalculate = true;
2162
- if (numColumns > 1) {
2163
- const rowNumber = Math.floor(index / numColumnsProp);
2164
- const prevSizeInRow = getRowHeight(rowNumber);
2165
- sizes.set(itemKey, size);
2166
- rowHeights.delete(rowNumber);
2167
- const sizeInRow = getRowHeight(rowNumber);
2168
- diff = sizeInRow - prevSizeInRow;
2169
- } else {
2170
- sizes.set(itemKey, size);
2171
- diff = size - prevSize;
2146
+ const updateItemSize = useCallback(
2147
+ (itemKey, sizeObj, fromFixGaps) => {
2148
+ const state = refState.current;
2149
+ const {
2150
+ sizes,
2151
+ indexByKey,
2152
+ sizesKnown,
2153
+ data,
2154
+ rowHeights,
2155
+ startBuffered,
2156
+ endBuffered,
2157
+ averageSizes,
2158
+ queuedInitialLayout
2159
+ } = state;
2160
+ if (!data) {
2161
+ return;
2162
+ }
2163
+ const index = indexByKey.get(itemKey);
2164
+ const numColumns = peek$(ctx, "numColumns");
2165
+ state.scrollForNextCalculateItemsInView = void 0;
2166
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
2167
+ const prevSize = getItemSize(itemKey, index, data);
2168
+ const prevSizeKnown = sizesKnown.get(itemKey);
2169
+ let needsCalculate = false;
2170
+ let needsUpdateContainersDidLayout = false;
2171
+ const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2172
+ sizesKnown.set(itemKey, size);
2173
+ const itemType = "";
2174
+ let averages = averageSizes[itemType];
2175
+ if (!averages) {
2176
+ averages = averageSizes[itemType] = {
2177
+ num: 0,
2178
+ avg: 0
2179
+ };
2172
2180
  }
2173
- if (__DEV__ && suggestEstimatedItemSize) {
2174
- if (state.timeoutSizeMessage) {
2175
- clearTimeout(state.timeoutSizeMessage);
2181
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2182
+ averages.num++;
2183
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2184
+ let diff;
2185
+ needsCalculate = true;
2186
+ if (numColumns > 1) {
2187
+ const rowNumber = Math.floor(index / numColumnsProp);
2188
+ const prevSizeInRow = getRowHeight(rowNumber);
2189
+ sizes.set(itemKey, size);
2190
+ rowHeights.delete(rowNumber);
2191
+ const sizeInRow = getRowHeight(rowNumber);
2192
+ diff = sizeInRow - prevSizeInRow;
2193
+ } else {
2194
+ sizes.set(itemKey, size);
2195
+ diff = size - prevSize;
2196
+ }
2197
+ if (__DEV__ && suggestEstimatedItemSize) {
2198
+ if (state.timeoutSizeMessage) {
2199
+ clearTimeout(state.timeoutSizeMessage);
2200
+ }
2201
+ state.timeoutSizeMessage = setTimeout(() => {
2202
+ state.timeoutSizeMessage = void 0;
2203
+ const num = sizesKnown.size;
2204
+ const avg = state.averageSizes[""].avg;
2205
+ console.warn(
2206
+ `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
2207
+ );
2208
+ }, 1e3);
2209
+ }
2210
+ state.scrollForNextCalculateItemsInView = void 0;
2211
+ addTotalSize(itemKey, diff, 0);
2212
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2213
+ doMaintainScrollAtEnd(false);
2214
+ }
2215
+ if (onItemSizeChanged) {
2216
+ onItemSizeChanged({
2217
+ size,
2218
+ previous: prevSize,
2219
+ index,
2220
+ itemKey,
2221
+ itemData: data[index]
2222
+ });
2176
2223
  }
2177
- state.timeoutSizeMessage = setTimeout(() => {
2178
- state.timeoutSizeMessage = void 0;
2179
- const num = sizesKnown.size;
2180
- const avg = state.averageSizes[""].avg;
2181
- console.warn(
2182
- `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
2183
- );
2184
- }, 1e3);
2185
2224
  }
2186
- state.scrollForNextCalculateItemsInView = void 0;
2187
- addTotalSize(itemKey, diff, 0);
2188
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2189
- doMaintainScrollAtEnd(false);
2190
- }
2191
- if (onItemSizeChanged) {
2192
- onItemSizeChanged({
2193
- size,
2194
- previous: prevSize,
2195
- index,
2196
- itemKey,
2197
- itemData: data[index]
2198
- });
2225
+ if (!queuedInitialLayout && checkAllSizesKnown()) {
2226
+ needsUpdateContainersDidLayout = true;
2199
2227
  }
2200
- }
2201
- if (!queuedInitialLayout && checkAllSizesKnown()) {
2202
- needsUpdateContainersDidLayout = true;
2203
- }
2204
- const isInView = index >= startBuffered && index <= endBuffered;
2205
- if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && (isInView || !queuedInitialLayout)) {
2206
- const scrollVelocity = state.scrollVelocity;
2207
- let didCalculate = false;
2208
- if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1 || state.scrollingToOffset !== void 0) && (!waitForInitialLayout || needsUpdateContainersDidLayout || queuedInitialLayout)) {
2209
- if (Date.now() - state.lastBatchingAction < 500) {
2210
- if (!state.queuedCalculateItemsInView) {
2211
- state.queuedCalculateItemsInView = requestAnimationFrame(() => {
2212
- state.queuedCalculateItemsInView = void 0;
2213
- calculateItemsInView();
2214
- });
2228
+ let isInView = index >= startBuffered && index <= endBuffered;
2229
+ if (!isInView) {
2230
+ const numContainers = ctx.values.get("numContainers");
2231
+ for (let i = 0; i < numContainers; i++) {
2232
+ if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2233
+ isInView = true;
2234
+ break;
2215
2235
  }
2216
- } else {
2217
- calculateItemsInView();
2218
- didCalculate = true;
2219
2236
  }
2220
2237
  }
2221
- if (!didCalculate && !needsUpdateContainersDidLayout && IsNewArchitecture) {
2222
- fixGaps();
2238
+ if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && (isInView || !queuedInitialLayout)) {
2239
+ const scrollVelocity = state.scrollVelocity;
2240
+ let didCalculate = false;
2241
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1 || state.scrollingToOffset !== void 0) && (!waitForInitialLayout || needsUpdateContainersDidLayout || queuedInitialLayout)) {
2242
+ if (Date.now() - state.lastBatchingAction < 500) {
2243
+ if (!state.queuedCalculateItemsInView) {
2244
+ state.queuedCalculateItemsInView = requestAnimationFrame(() => {
2245
+ state.queuedCalculateItemsInView = void 0;
2246
+ calculateItemsInView();
2247
+ });
2248
+ }
2249
+ } else {
2250
+ calculateItemsInView();
2251
+ didCalculate = true;
2252
+ }
2253
+ }
2254
+ if (!didCalculate && !needsUpdateContainersDidLayout && IsNewArchitecture) {
2255
+ fixGaps();
2256
+ }
2223
2257
  }
2224
- }
2225
- }, []);
2226
- const onLayout = useCallback((event) => {
2258
+ if (state.needsOtherAxisSize) {
2259
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
2260
+ const cur = peek$(ctx, "otherAxisSize");
2261
+ if (!cur || otherAxisSize > cur) {
2262
+ set$(ctx, "otherAxisSize", otherAxisSize);
2263
+ }
2264
+ }
2265
+ },
2266
+ []
2267
+ );
2268
+ const handleLayout = useCallback((scrollLength) => {
2227
2269
  const state = refState.current;
2228
- const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
2229
2270
  const didChange = scrollLength !== state.scrollLength;
2230
2271
  state.scrollLength = scrollLength;
2231
2272
  state.lastBatchingAction = Date.now();
@@ -2238,20 +2279,36 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2238
2279
  if (didChange) {
2239
2280
  calculateItemsInView();
2240
2281
  }
2241
- if (__DEV__) {
2242
- const isWidthZero = event.nativeEvent.layout.width === 0;
2243
- const isHeightZero = event.nativeEvent.layout.height === 0;
2244
- if (isWidthZero || isHeightZero) {
2245
- warnDevOnce(
2246
- "height0",
2247
- `[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.`
2248
- );
2249
- }
2282
+ }, []);
2283
+ const onLayout = useCallback((event) => {
2284
+ const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
2285
+ handleLayout(scrollLength);
2286
+ const otherAxisSize = event.nativeEvent.layout[horizontal ? "height" : "width"];
2287
+ if (refState.current) {
2288
+ refState.current.needsOtherAxisSize = otherAxisSize - (stylePaddingTopState || 0) < 10;
2289
+ }
2290
+ if (__DEV__ && scrollLength === 0) {
2291
+ warnDevOnce(
2292
+ "height0",
2293
+ `List ${horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
2294
+ );
2250
2295
  }
2251
2296
  if (onLayoutProp) {
2252
2297
  onLayoutProp(event);
2253
2298
  }
2254
2299
  }, []);
2300
+ if (IsNewArchitecture) {
2301
+ useLayoutEffect(() => {
2302
+ var _a, _b;
2303
+ const measured = (_b = (_a = refScroller.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
2304
+ if (measured) {
2305
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2306
+ if (size) {
2307
+ handleLayout(size);
2308
+ }
2309
+ }
2310
+ }, []);
2311
+ }
2255
2312
  const handleScroll = useCallback(
2256
2313
  (event) => {
2257
2314
  var _a, _b, _c, _d;
@@ -2449,4 +2506,4 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2449
2506
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React2.createElement(DebugView, { state: refState.current }));
2450
2507
  });
2451
2508
 
2452
- export { LegendList, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
2509
+ export { LegendList, useIsLastItem, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
@@ -58,6 +58,9 @@ declare const LegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_nati
58
58
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
59
59
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
60
60
  waitForInitialLayout?: boolean;
61
+ onLoad?: (info: {
62
+ elapsedTimeInMs: number;
63
+ }) => void;
61
64
  } & React.RefAttributes<LegendListRef>) => React.ReactNode) | react_native.Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
62
65
  alignItemsAtEnd?: boolean;
63
66
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
@@ -109,6 +112,9 @@ declare const LegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_nati
109
112
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
110
113
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
111
114
  waitForInitialLayout?: boolean;
115
+ onLoad?: (info: {
116
+ elapsedTimeInMs: number;
117
+ }) => void;
112
118
  } & React.RefAttributes<LegendListRef>) => React.ReactNode)> | (<ItemT_1>(props: Omit<_legendapp_list_reanimated.AnimatedLegendListPropsBase<ItemT_1>, "refLegendList"> & {
113
119
  getEstimatedItemSize?: ((index: number, item: ItemT_1) => number) | undefined;
114
120
  ItemSeparatorComponent?: React.ComponentType<{
@@ -343,6 +349,9 @@ declare const LegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_nati
343
349
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
344
350
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
345
351
  waitForInitialLayout?: boolean;
352
+ onLoad?: (info: {
353
+ elapsedTimeInMs: number;
354
+ }) => void;
346
355
  } & React.RefAttributes<LegendListRef>) => React.ReactNode>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
347
356
  alignItemsAtEnd?: boolean;
348
357
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
@@ -394,6 +403,9 @@ declare const LegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_nati
394
403
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
395
404
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
396
405
  waitForInitialLayout?: boolean;
406
+ onLoad?: (info: {
407
+ elapsedTimeInMs: number;
408
+ }) => void;
397
409
  } & {
398
410
  LegendList?: ListT;
399
411
  } & React.RefAttributes<LegendListRef>) => React.ReactNode;
@@ -58,6 +58,9 @@ declare const LegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_nati
58
58
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
59
59
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
60
60
  waitForInitialLayout?: boolean;
61
+ onLoad?: (info: {
62
+ elapsedTimeInMs: number;
63
+ }) => void;
61
64
  } & React.RefAttributes<LegendListRef>) => React.ReactNode) | react_native.Animated.AnimatedComponent<(<T>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
62
65
  alignItemsAtEnd?: boolean;
63
66
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
@@ -109,6 +112,9 @@ declare const LegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_nati
109
112
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
110
113
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
111
114
  waitForInitialLayout?: boolean;
115
+ onLoad?: (info: {
116
+ elapsedTimeInMs: number;
117
+ }) => void;
112
118
  } & React.RefAttributes<LegendListRef>) => React.ReactNode)> | (<ItemT_1>(props: Omit<_legendapp_list_reanimated.AnimatedLegendListPropsBase<ItemT_1>, "refLegendList"> & {
113
119
  getEstimatedItemSize?: ((index: number, item: ItemT_1) => number) | undefined;
114
120
  ItemSeparatorComponent?: React.ComponentType<{
@@ -343,6 +349,9 @@ declare const LegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_nati
343
349
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
344
350
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
345
351
  waitForInitialLayout?: boolean;
352
+ onLoad?: (info: {
353
+ elapsedTimeInMs: number;
354
+ }) => void;
346
355
  } & React.RefAttributes<LegendListRef>) => React.ReactNode>(props: Omit<Omit<react_native.ScrollViewProps, "scrollEventThrottle">, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices" | "removeClippedSubviews"> & {
347
356
  alignItemsAtEnd?: boolean;
348
357
  columnWrapperStyle?: _legendapp_list.ColumnWrapperStyle;
@@ -394,6 +403,9 @@ declare const LegendList: <ItemT, ListT extends (<T>(props: Omit<Omit<react_nati
394
403
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
395
404
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
396
405
  waitForInitialLayout?: boolean;
406
+ onLoad?: (info: {
407
+ elapsedTimeInMs: number;
408
+ }) => void;
397
409
  } & {
398
410
  LegendList?: ListT;
399
411
  } & React.RefAttributes<LegendListRef>) => React.ReactNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Legend List is a drop-in replacement for FlatList with much better performance and supporting dynamically sized items.",
5
5
  "sideEffects": false,
6
6
  "private": false,