@legendapp/list 1.0.0-beta.15 → 1.0.0-beta.17

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
@@ -34,7 +34,9 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<re
34
34
  ListFooterComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
35
35
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
36
36
  ListEmptyComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
37
- ItemSeparatorComponent?: React.ComponentType<any>;
37
+ ItemSeparatorComponent?: React$1.ComponentType<{
38
+ leadingItem: T;
39
+ }> | undefined;
38
40
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
39
41
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
40
42
  onViewableItemsChanged?: _legendapp_list.OnViewableItemsChanged | undefined;
@@ -47,6 +49,9 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<re
47
49
  }) => void) | undefined;
48
50
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
49
51
  extraData?: any;
52
+ refreshing?: boolean;
53
+ onRefresh?: () => void;
54
+ progressViewOffset?: number;
50
55
  } & React$1.RefAttributes<_legendapp_list.LegendListRef>) => React.ReactNode)>;
51
56
 
52
57
  export { AnimatedLegendList };
package/animated.d.ts CHANGED
@@ -34,7 +34,9 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<re
34
34
  ListFooterComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
35
35
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
36
36
  ListEmptyComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
37
- ItemSeparatorComponent?: React.ComponentType<any>;
37
+ ItemSeparatorComponent?: React$1.ComponentType<{
38
+ leadingItem: T;
39
+ }> | undefined;
38
40
  viewabilityConfigCallbackPairs?: _legendapp_list.ViewabilityConfigCallbackPairs | undefined;
39
41
  viewabilityConfig?: _legendapp_list.ViewabilityConfig;
40
42
  onViewableItemsChanged?: _legendapp_list.OnViewableItemsChanged | undefined;
@@ -47,6 +49,9 @@ declare const AnimatedLegendList: Animated.AnimatedComponent<(<T>(props: Omit<re
47
49
  }) => void) | undefined;
48
50
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React.ReactElement<react_native.ScrollViewProps>;
49
51
  extraData?: any;
52
+ refreshing?: boolean;
53
+ onRefresh?: () => void;
54
+ progressViewOffset?: number;
50
55
  } & React$1.RefAttributes<_legendapp_list.LegendListRef>) => React.ReactNode)>;
51
56
 
52
57
  export { AnimatedLegendList };
package/index.d.mts CHANGED
@@ -49,7 +49,9 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
49
49
  ListFooterComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
50
50
  ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
51
51
  ListEmptyComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
52
- ItemSeparatorComponent?: React.ComponentType<any>;
52
+ ItemSeparatorComponent?: React.ComponentType<{
53
+ leadingItem: ItemT;
54
+ }>;
53
55
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
54
56
  viewabilityConfig?: ViewabilityConfig;
55
57
  onViewableItemsChanged?: OnViewableItemsChanged | undefined;
@@ -66,6 +68,9 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
66
68
  */
67
69
  renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
68
70
  extraData?: any;
71
+ refreshing?: boolean;
72
+ onRefresh?: () => void;
73
+ progressViewOffset?: number;
69
74
  };
70
75
  interface ColumnWrapperStyle {
71
76
  rowGap?: number;
@@ -90,13 +95,11 @@ interface InternalState {
90
95
  sizes: Map<string, number>;
91
96
  sizesLaidOut: Map<string, number> | undefined;
92
97
  pendingAdjust: number;
93
- waitingForMicrotask: any;
94
98
  isStartReached: boolean;
95
99
  isEndReached: boolean;
96
100
  isAtBottom: boolean;
97
101
  isAtTop: boolean;
98
102
  data: readonly any[];
99
- idsInFirstRender: Set<string>;
100
103
  hasScrolled: boolean;
101
104
  scrollLength: number;
102
105
  startBuffered: number;
@@ -130,6 +133,7 @@ interface InternalState {
130
133
  } | undefined;
131
134
  enableScrollForNextCalculateItemsInView: boolean;
132
135
  minIndexSizeChanged: number | undefined;
136
+ numPendingInitialLayout: number;
133
137
  }
134
138
  interface ViewableRange<T> {
135
139
  startBuffered: number;
@@ -243,6 +247,10 @@ interface LegendListRecyclingState<T> {
243
247
  }
244
248
  type TypedForwardRef = <T, P = {}>(render: (props: P, ref: React.Ref<T>) => React.ReactNode) => (props: P & React.RefAttributes<T>) => React.ReactNode;
245
249
  declare const typedForwardRef: TypedForwardRef;
250
+ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqual?: (prevProps: Readonly<ComponentProps<T>>, nextProps: Readonly<ComponentProps<T>>) => boolean) => T & {
251
+ displayName?: string;
252
+ };
253
+ declare const typedMemo: TypedMemo;
246
254
 
247
255
  declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
248
256
  data: readonly T[];
@@ -275,7 +283,9 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
275
283
  ListFooterComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
276
284
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
277
285
  ListEmptyComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
278
- ItemSeparatorComponent?: React$1.ComponentType<any>;
286
+ ItemSeparatorComponent?: React$1.ComponentType<{
287
+ leadingItem: T;
288
+ }> | undefined;
279
289
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
280
290
  viewabilityConfig?: ViewabilityConfig;
281
291
  onViewableItemsChanged?: OnViewableItemsChanged | undefined;
@@ -288,6 +298,9 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
288
298
  }) => void) | undefined;
289
299
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React$1.ReactElement<react_native.ScrollViewProps>;
290
300
  extraData?: any;
301
+ refreshing?: boolean;
302
+ onRefresh?: () => void;
303
+ progressViewOffset?: number;
291
304
  } & React$1.RefAttributes<LegendListRef>) => React$1.ReactNode;
292
305
 
293
306
  declare function useViewability(configId: string, callback: ViewabilityCallback): void;
@@ -295,4 +308,4 @@ declare function useViewabilityAmount(callback: ViewabilityAmountCallback): void
295
308
  declare function useRecyclingEffect(effect: (info: LegendListRecyclingState<unknown>) => void | (() => void)): void;
296
309
  declare function useRecyclingState(valueOrFun: ((info: LegendListRecyclingState<unknown>) => any) | any): [any, React$1.Dispatch<any>];
297
310
 
298
- export { type AnchoredPosition, type ColumnWrapperStyle, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type TypedForwardRef, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
311
+ export { type AnchoredPosition, type ColumnWrapperStyle, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, 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
@@ -49,7 +49,9 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
49
49
  ListFooterComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
50
50
  ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
51
51
  ListEmptyComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
52
- ItemSeparatorComponent?: React.ComponentType<any>;
52
+ ItemSeparatorComponent?: React.ComponentType<{
53
+ leadingItem: ItemT;
54
+ }>;
53
55
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
54
56
  viewabilityConfig?: ViewabilityConfig;
55
57
  onViewableItemsChanged?: OnViewableItemsChanged | undefined;
@@ -66,6 +68,9 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
66
68
  */
67
69
  renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
68
70
  extraData?: any;
71
+ refreshing?: boolean;
72
+ onRefresh?: () => void;
73
+ progressViewOffset?: number;
69
74
  };
70
75
  interface ColumnWrapperStyle {
71
76
  rowGap?: number;
@@ -90,13 +95,11 @@ interface InternalState {
90
95
  sizes: Map<string, number>;
91
96
  sizesLaidOut: Map<string, number> | undefined;
92
97
  pendingAdjust: number;
93
- waitingForMicrotask: any;
94
98
  isStartReached: boolean;
95
99
  isEndReached: boolean;
96
100
  isAtBottom: boolean;
97
101
  isAtTop: boolean;
98
102
  data: readonly any[];
99
- idsInFirstRender: Set<string>;
100
103
  hasScrolled: boolean;
101
104
  scrollLength: number;
102
105
  startBuffered: number;
@@ -130,6 +133,7 @@ interface InternalState {
130
133
  } | undefined;
131
134
  enableScrollForNextCalculateItemsInView: boolean;
132
135
  minIndexSizeChanged: number | undefined;
136
+ numPendingInitialLayout: number;
133
137
  }
134
138
  interface ViewableRange<T> {
135
139
  startBuffered: number;
@@ -243,6 +247,10 @@ interface LegendListRecyclingState<T> {
243
247
  }
244
248
  type TypedForwardRef = <T, P = {}>(render: (props: P, ref: React.Ref<T>) => React.ReactNode) => (props: P & React.RefAttributes<T>) => React.ReactNode;
245
249
  declare const typedForwardRef: TypedForwardRef;
250
+ type TypedMemo = <T extends React.ComponentType<any>>(Component: T, propsAreEqual?: (prevProps: Readonly<ComponentProps<T>>, nextProps: Readonly<ComponentProps<T>>) => boolean) => T & {
251
+ displayName?: string;
252
+ };
253
+ declare const typedMemo: TypedMemo;
246
254
 
247
255
  declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "contentOffset" | "contentInset" | "maintainVisibleContentPosition" | "stickyHeaderIndices"> & {
248
256
  data: readonly T[];
@@ -275,7 +283,9 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
275
283
  ListFooterComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
276
284
  ListFooterComponentStyle?: react_native.StyleProp<react_native.ViewStyle> | undefined;
277
285
  ListEmptyComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
278
- ItemSeparatorComponent?: React$1.ComponentType<any>;
286
+ ItemSeparatorComponent?: React$1.ComponentType<{
287
+ leadingItem: T;
288
+ }> | undefined;
279
289
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
280
290
  viewabilityConfig?: ViewabilityConfig;
281
291
  onViewableItemsChanged?: OnViewableItemsChanged | undefined;
@@ -288,6 +298,9 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
288
298
  }) => void) | undefined;
289
299
  renderScrollComponent?: (props: react_native.ScrollViewProps) => React$1.ReactElement<react_native.ScrollViewProps>;
290
300
  extraData?: any;
301
+ refreshing?: boolean;
302
+ onRefresh?: () => void;
303
+ progressViewOffset?: number;
291
304
  } & React$1.RefAttributes<LegendListRef>) => React$1.ReactNode;
292
305
 
293
306
  declare function useViewability(configId: string, callback: ViewabilityCallback): void;
@@ -295,4 +308,4 @@ declare function useViewabilityAmount(callback: ViewabilityAmountCallback): void
295
308
  declare function useRecyclingEffect(effect: (info: LegendListRecyclingState<unknown>) => void | (() => void)): void;
296
309
  declare function useRecyclingState(valueOrFun: ((info: LegendListRecyclingState<unknown>) => any) | any): [any, React$1.Dispatch<any>];
297
310
 
298
- export { type AnchoredPosition, type ColumnWrapperStyle, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type TypedForwardRef, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange, typedForwardRef, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
311
+ export { type AnchoredPosition, type ColumnWrapperStyle, type InternalState, LegendList, type LegendListProps, type LegendListPropsBase, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, 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
@@ -169,7 +169,7 @@ var DebugView = React6.memo(function DebugView2({ state }) {
169
169
  useInterval(() => {
170
170
  forceUpdate();
171
171
  }, 100);
172
- return /* @__PURE__ */ React.createElement(
172
+ return /* @__PURE__ */ React6__namespace.createElement(
173
173
  reactNative.View,
174
174
  {
175
175
  style: {
@@ -182,8 +182,8 @@ var DebugView = React6.memo(function DebugView2({ state }) {
182
182
  backgroundColor: "#FFFFFFCC"
183
183
  }
184
184
  },
185
- /* @__PURE__ */ React.createElement(reactNative.Text, null, "PaddingTop: ", paddingTop),
186
- /* @__PURE__ */ React.createElement(reactNative.Text, null, "At end: ", String(state.isAtBottom))
185
+ /* @__PURE__ */ React6__namespace.createElement(reactNative.Text, null, "PaddingTop: ", paddingTop),
186
+ /* @__PURE__ */ React6__namespace.createElement(reactNative.Text, null, "At end: ", String(state.isAtBottom))
187
187
  );
188
188
  });
189
189
  function useInterval(callback, delay) {
@@ -224,13 +224,17 @@ var Container = ({
224
224
  const position = use$(`containerPosition${id}`) || ANCHORED_POSITION_OUT_OF_VIEW;
225
225
  const column = use$(`containerColumn${id}`) || 0;
226
226
  const numColumns = use$("numColumns");
227
+ const lastItemKeys = use$("lastItemKeys");
228
+ const itemKey = use$(`containerItemKey${id}`);
229
+ const data = use$(`containerItemData${id}`);
230
+ const extraData = use$("extraData");
227
231
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
228
232
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
229
233
  let verticalPaddingStyles;
230
234
  if (columnWrapperStyle && !horizontal && numColumns > 1) {
231
235
  const { columnGap, rowGap, gap } = columnWrapperStyle;
232
236
  verticalPaddingStyles = {
233
- paddingVertical: rowGap || gap || void 0,
237
+ paddingBottom: !lastItemKeys.has(itemKey) ? rowGap || gap || void 0 : void 0,
234
238
  // Apply horizontal padding based on column position (first, middle, or last)
235
239
  paddingLeft: column > 1 ? (columnGap || gap || 0) / 2 : void 0,
236
240
  paddingRight: column < numColumns ? (columnGap || gap || 0) / 2 : void 0
@@ -251,20 +255,24 @@ var Container = ({
251
255
  top: position.relativeCoordinate,
252
256
  ...verticalPaddingStyles || {}
253
257
  };
254
- const lastItemKey = use$("lastItemKey");
255
- const itemKey = use$(`containerItemKey${id}`);
256
- const data = use$(`containerItemData${id}`);
257
- const extraData = use$("extraData");
258
258
  const renderedItemInfo = React6.useMemo(
259
- () => itemKey !== void 0 && getRenderedItem(itemKey),
259
+ () => itemKey !== void 0 ? getRenderedItem(itemKey) : null,
260
260
  [itemKey, data, extraData]
261
261
  );
262
262
  const { index, renderedItem } = renderedItemInfo || {};
263
263
  const onLayout = (event) => {
264
264
  if (itemKey !== void 0) {
265
- const size = Math.floor(event.nativeEvent.layout[horizontal ? "width" : "height"] * 8) / 8;
265
+ const layout = event.nativeEvent.layout;
266
+ const size = Math.floor(layout[horizontal ? "width" : "height"] * 8) / 8;
266
267
  if (size === 0) {
267
- console.log("[WARN] Container 0 height reported, possible bug in LegendList", id, itemKey);
268
+ if (layout.x !== POSITION_OUT_OF_VIEW && layout.y !== POSITION_OUT_OF_VIEW) {
269
+ console.log(
270
+ "[WARN] Container 0 height reported, possible bug in LegendList",
271
+ id,
272
+ itemKey,
273
+ event.nativeEvent
274
+ );
275
+ }
268
276
  return;
269
277
  }
270
278
  updateItemSize(id, itemKey, size);
@@ -289,7 +297,7 @@ var Container = ({
289
297
  () => ({ containerId: id, itemKey, index, value: data }),
290
298
  [id, itemKey, index, data]
291
299
  );
292
- const contentFragment = /* @__PURE__ */ React6__namespace.default.createElement(React6__namespace.default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React6__namespace.default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItem && ItemSeparatorComponent && itemKey !== lastItemKey && ItemSeparatorComponent));
300
+ const contentFragment = /* @__PURE__ */ React6__namespace.default.createElement(React6__namespace.default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React6__namespace.default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && !lastItemKeys.has(itemKey) && /* @__PURE__ */ React6__namespace.default.createElement(ItemSeparatorComponent, { leadingItem: renderedItemInfo.item })));
293
301
  if (maintainVisibleContentPosition) {
294
302
  const anchorStyle = position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
295
303
  if (ENABLE_DEVMODE) {
@@ -300,6 +308,8 @@ var Container = ({
300
308
  }
301
309
  return /* @__PURE__ */ React6__namespace.default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
302
310
  };
311
+ var typedForwardRef = React6.forwardRef;
312
+ var typedMemo = React6.memo;
303
313
  var useAnimatedValue = (initialValue) => {
304
314
  return React6.useRef(new reactNative.Animated.Value(initialValue)).current;
305
315
  };
@@ -328,7 +338,7 @@ function useValue$(key, getValue, useMicrotask) {
328
338
  }
329
339
 
330
340
  // src/Containers.tsx
331
- var Containers = React6__namespace.memo(function Containers2({
341
+ var Containers = typedMemo(function Containers2({
332
342
  horizontal,
333
343
  recycleItems,
334
344
  ItemSeparatorComponent,
@@ -347,7 +357,7 @@ var Containers = React6__namespace.memo(function Containers2({
347
357
  const containers = [];
348
358
  for (let i = 0; i < numContainers; i++) {
349
359
  containers.push(
350
- /* @__PURE__ */ React6__namespace.createElement(
360
+ /* @__PURE__ */ React.createElement(
351
361
  Container,
352
362
  {
353
363
  id: i,
@@ -362,7 +372,7 @@ var Containers = React6__namespace.memo(function Containers2({
362
372
  );
363
373
  }
364
374
  const style = horizontal ? { width: animSize, opacity: animOpacity } : { height: animSize, opacity: animOpacity };
365
- return /* @__PURE__ */ React6__namespace.createElement(reactNative.Animated.View, { style }, containers);
375
+ return /* @__PURE__ */ React.createElement(reactNative.Animated.View, { style }, containers);
366
376
  });
367
377
 
368
378
  // src/ListComponent.tsx
@@ -426,7 +436,7 @@ var PaddingAndAdjustDevMode = () => {
426
436
  }
427
437
  ));
428
438
  };
429
- var ListComponent = React6__namespace.memo(function ListComponent2({
439
+ var ListComponent = typedMemo(function ListComponent2({
430
440
  style,
431
441
  contentContainerStyle,
432
442
  horizontal,
@@ -447,6 +457,9 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
447
457
  refScrollView,
448
458
  maintainVisibleContentPosition,
449
459
  renderScrollComponent,
460
+ onRefresh,
461
+ refreshing,
462
+ progressViewOffset,
450
463
  ...rest
451
464
  }) {
452
465
  const ctx = useStateContext();
@@ -495,7 +508,7 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
495
508
  recycleItems,
496
509
  waitForInitialLayout,
497
510
  getRenderedItem,
498
- ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
511
+ ItemSeparatorComponent,
499
512
  updateItemSize
500
513
  }
501
514
  ),
@@ -544,7 +557,6 @@ var ScrollAdjustHandler = class {
544
557
  return false;
545
558
  }
546
559
  };
547
- var typedForwardRef = React6.forwardRef;
548
560
  var useCombinedRef = (...refs) => {
549
561
  const callback = React6.useCallback((element) => {
550
562
  for (const ref of refs) {
@@ -744,6 +756,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
744
756
  waitForInitialLayout = true,
745
757
  extraData,
746
758
  onLayout: onLayoutProp,
759
+ onRefresh,
760
+ refreshing,
761
+ progressViewOffset,
747
762
  ...rest
748
763
  } = props;
749
764
  const { style, contentContainerStyle } = props;
@@ -809,13 +824,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
809
824
  positions: /* @__PURE__ */ new Map(),
810
825
  columns: /* @__PURE__ */ new Map(),
811
826
  pendingAdjust: 0,
812
- waitingForMicrotask: false,
813
827
  isStartReached: initialContentOffset < initialScrollLength * onStartReachedThreshold,
814
828
  isEndReached: false,
815
829
  isAtBottom: false,
816
830
  isAtTop: false,
817
831
  data: dataProp,
818
- idsInFirstRender: void 0,
819
832
  hasScrolled: false,
820
833
  scrollLength: initialScrollLength,
821
834
  startBuffered: 0,
@@ -844,9 +857,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
844
857
  startReachedBlockedByTimer: false,
845
858
  scrollForNextCalculateItemsInView: void 0,
846
859
  enableScrollForNextCalculateItemsInView: true,
847
- minIndexSizeChanged: 0
860
+ minIndexSizeChanged: 0,
861
+ numPendingInitialLayout: 0
848
862
  };
849
- refState.current.idsInFirstRender = new Set(dataProp.map((_, i) => getId(i)));
850
863
  if (maintainVisibleContentPosition) {
851
864
  if (initialScrollIndex) {
852
865
  refState.current.anchorElement = {
@@ -866,6 +879,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
866
879
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
867
880
  set$(ctx, "extraData", extraData);
868
881
  }
882
+ const didDataChange = refState.current.data !== dataProp;
883
+ refState.current.data = dataProp;
869
884
  const getAnchorElementIndex = () => {
870
885
  const state = refState.current;
871
886
  if (state.anchorElement) {
@@ -977,10 +992,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
977
992
  columns,
978
993
  scrollAdjustHandler
979
994
  } = state;
980
- if (state.waitingForMicrotask) {
981
- state.waitingForMicrotask = false;
982
- }
983
- if (!data) {
995
+ if (!data || scrollLength === 0) {
984
996
  return;
985
997
  }
986
998
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
@@ -1213,7 +1225,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1213
1225
  }
1214
1226
  }
1215
1227
  }
1216
- set$(ctx, "containersDidLayout", true);
1228
+ if (state.numPendingInitialLayout === 0) {
1229
+ state.numPendingInitialLayout = state.endBuffered - state.startBuffered + 1;
1230
+ }
1217
1231
  if (state.viewabilityConfigCallbackPairs) {
1218
1232
  updateViewableItems(
1219
1233
  state,
@@ -1254,9 +1268,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1254
1268
  if (!refState.current) {
1255
1269
  return;
1256
1270
  }
1257
- const { scrollLength, scroll, totalSize } = refState.current;
1258
- if (totalSize > 0) {
1259
- const distanceFromEnd = totalSize - scroll - scrollLength + (peek$(ctx, "paddingTop") || 0);
1271
+ const { scrollLength, scroll, totalSize, hasScrolled } = refState.current;
1272
+ if (totalSize > 0 && hasScrolled) {
1273
+ const distanceFromEnd = Math.abs(
1274
+ totalSize - scroll - scrollLength + (peek$(ctx, "paddingTop") || 0)
1275
+ );
1260
1276
  if (refState.current) {
1261
1277
  refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1262
1278
  }
@@ -1405,18 +1421,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1405
1421
  addTotalSize(null, totalSize, totalSizeBelowIndex);
1406
1422
  };
1407
1423
  const isFirst = !refState.current.renderItem;
1408
- if (isFirst || dataProp !== refState.current.data || numColumnsProp !== peek$(ctx, "numColumns")) {
1409
- if (!keyExtractorProp && !isFirst && dataProp !== refState.current.data) {
1424
+ if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
1425
+ if (!keyExtractorProp && !isFirst && didDataChange) {
1410
1426
  refState.current.sizes.clear();
1411
1427
  refState.current.positions.clear();
1412
1428
  }
1413
- refState.current.data = dataProp;
1414
- const indexByKey = /* @__PURE__ */ new Map();
1415
- for (let i = 0; i < dataProp.length; i++) {
1416
- const key = getId(i);
1417
- indexByKey.set(key, i);
1418
- }
1419
- refState.current.indexByKey = indexByKey;
1420
1429
  calcTotalSizesAndPositions({ forgetPositions: false });
1421
1430
  }
1422
1431
  React6.useEffect(() => {
@@ -1429,17 +1438,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1429
1438
  set$(ctx, "extraData", extraData);
1430
1439
  }, [extraData]);
1431
1440
  refState.current.renderItem = renderItem;
1432
- const lastItemKey = dataProp.length > 0 ? getId(dataProp.length - 1) : void 0;
1441
+ const memoizedLastItemKeys = React6.useMemo(() => {
1442
+ if (!dataProp.length) return [];
1443
+ return new Set(
1444
+ Array.from({ length: Math.min(numColumnsProp, dataProp.length) }, (_, i) => getId(dataProp.length - 1 - i))
1445
+ );
1446
+ }, [dataProp.length, numColumnsProp, dataProp.slice(-numColumnsProp).toString()]);
1433
1447
  const stylePaddingTop = (_d = (_c = (_a = reactNative.StyleSheet.flatten(style)) == null ? void 0 : _a.paddingTop) != null ? _c : (_b = reactNative.StyleSheet.flatten(contentContainerStyle)) == null ? void 0 : _b.paddingTop) != null ? _d : 0;
1434
1448
  const initalizeStateVars = () => {
1435
- set$(ctx, "lastItemKey", lastItemKey);
1449
+ set$(ctx, "lastItemKeys", memoizedLastItemKeys);
1436
1450
  set$(ctx, "numColumns", numColumnsProp);
1437
1451
  set$(ctx, "stylePaddingTop", stylePaddingTop);
1438
1452
  };
1439
1453
  if (isFirst) {
1440
1454
  initalizeStateVars();
1441
1455
  }
1442
- React6.useEffect(initalizeStateVars, [lastItemKey, numColumnsProp, stylePaddingTop]);
1456
+ React6.useEffect(initalizeStateVars, [memoizedLastItemKeys, numColumnsProp, stylePaddingTop]);
1443
1457
  const getRenderedItem = React6.useCallback((key) => {
1444
1458
  var _a2, _b2;
1445
1459
  const state = refState.current;
@@ -1471,34 +1485,40 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1471
1485
  useRecyclingEffect: useRecyclingEffect2,
1472
1486
  useRecyclingState: useRecyclingState2
1473
1487
  });
1474
- return { index, renderedItem };
1488
+ return { index, item: data[index], renderedItem };
1475
1489
  }, []);
1476
- useInit(() => {
1490
+ const doInitialAllocateContainers = () => {
1477
1491
  var _a2;
1478
1492
  const state = refState.current;
1479
- const viewability = setupViewability(props);
1480
- state.viewabilityConfigCallbackPairs = viewability;
1481
- state.enableScrollForNextCalculateItemsInView = !viewability;
1482
1493
  const scrollLength = state.scrollLength;
1483
- const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, dataProp[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1484
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1485
- for (let i = 0; i < numContainers; i++) {
1486
- set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1487
- set$(ctx, `containerColumn${i}`, -1);
1488
- }
1489
- set$(ctx, "numContainers", numContainers);
1490
- set$(ctx, "numContainersPooled", numContainers * 2);
1491
- if (initialScrollIndex) {
1492
- requestAnimationFrame(() => {
1494
+ if (scrollLength > 0 && !peek$(ctx, "numContainers")) {
1495
+ const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, dataProp[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1496
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1497
+ for (let i = 0; i < numContainers; i++) {
1498
+ set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1499
+ set$(ctx, `containerColumn${i}`, -1);
1500
+ }
1501
+ set$(ctx, "numContainers", numContainers);
1502
+ set$(ctx, "numContainersPooled", numContainers * 2);
1503
+ if (initialScrollIndex) {
1504
+ requestAnimationFrame(() => {
1505
+ calculateItemsInView(state.scrollVelocity);
1506
+ });
1507
+ } else {
1493
1508
  calculateItemsInView(state.scrollVelocity);
1494
- });
1495
- } else {
1496
- calculateItemsInView(state.scrollVelocity);
1509
+ }
1497
1510
  }
1511
+ };
1512
+ useInit(() => {
1513
+ const state = refState.current;
1514
+ const viewability = setupViewability(props);
1515
+ state.viewabilityConfigCallbackPairs = viewability;
1516
+ state.enableScrollForNextCalculateItemsInView = !viewability;
1517
+ doInitialAllocateContainers();
1498
1518
  });
1499
1519
  const updateItemSize = React6.useCallback((containerId, itemKey, size) => {
1500
1520
  const state = refState.current;
1501
- const { sizes, indexByKey, columns, sizesLaidOut, data, rowHeights } = state;
1521
+ const { sizes, indexByKey, sizesLaidOut, data, rowHeights } = state;
1502
1522
  if (!data) {
1503
1523
  return;
1504
1524
  }
@@ -1506,8 +1526,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1506
1526
  const numColumns = peek$(ctx, "numColumns");
1507
1527
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1508
1528
  const prevSize = getItemSize(itemKey, index, data);
1529
+ let needsCalculate = false;
1530
+ if (state.numPendingInitialLayout > 0) {
1531
+ state.numPendingInitialLayout--;
1532
+ if (state.numPendingInitialLayout === 0) {
1533
+ needsCalculate = true;
1534
+ state.numPendingInitialLayout = -1;
1535
+ queueMicrotask(() => {
1536
+ set$(ctx, "containersDidLayout", true);
1537
+ });
1538
+ }
1539
+ }
1509
1540
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
1510
1541
  let diff;
1542
+ needsCalculate = true;
1511
1543
  if (numColumns > 1) {
1512
1544
  const rowNumber = Math.floor(index / numColumnsProp);
1513
1545
  const prevSizeInRow = getRowHeight(rowNumber);
@@ -1541,22 +1573,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1541
1573
  refState.current.scrollForNextCalculateItemsInView = void 0;
1542
1574
  addTotalSize(itemKey, diff, 0);
1543
1575
  doMaintainScrollAtEnd(true);
1544
- const scrollVelocity = state.scrollVelocity;
1545
- if (!state.waitingForMicrotask && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1546
- if (!peek$(ctx, "containersDidLayout")) {
1547
- state.waitingForMicrotask = true;
1548
- queueMicrotask(() => {
1549
- if (state.waitingForMicrotask) {
1550
- state.waitingForMicrotask = false;
1551
- calculateItemsInView(state.scrollVelocity);
1552
- }
1553
- });
1554
- } else {
1555
- calculateItemsInView(state.scrollVelocity);
1556
- }
1557
- }
1558
1576
  if (onItemSizeChanged) {
1559
- onItemSizeChanged({ size, previous: prevSize, index, itemKey, itemData: data[index] });
1577
+ onItemSizeChanged({
1578
+ size,
1579
+ previous: prevSize,
1580
+ index,
1581
+ itemKey,
1582
+ itemData: data[index]
1583
+ });
1584
+ }
1585
+ }
1586
+ if (needsCalculate) {
1587
+ const scrollVelocity = state.scrollVelocity;
1588
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || state.numPendingInitialLayout < 0)) {
1589
+ calculateItemsInView(state.scrollVelocity);
1560
1590
  }
1561
1591
  }
1562
1592
  }, []);
@@ -1568,7 +1598,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1568
1598
  const onLayout = React6.useCallback((event) => {
1569
1599
  const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
1570
1600
  const didChange = scrollLength !== refState.current.scrollLength;
1601
+ refState.current.scrollLength;
1571
1602
  refState.current.scrollLength = scrollLength;
1603
+ doInitialAllocateContainers();
1572
1604
  doMaintainScrollAtEnd(false);
1573
1605
  doUpdatePaddingTop();
1574
1606
  checkAtBottom();
@@ -1688,7 +1720,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1688
1720
  scrollToIndex({ index, animated });
1689
1721
  }
1690
1722
  },
1691
- scrollToEnd: () => refScroller.current.scrollToEnd()
1723
+ scrollToEnd: (options) => refScroller.current.scrollToEnd(options)
1692
1724
  };
1693
1725
  },
1694
1726
  []
@@ -1721,6 +1753,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1721
1753
  maintainVisibleContentPosition,
1722
1754
  scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : reactNative.Platform.OS === "web" ? 16 : void 0,
1723
1755
  waitForInitialLayout,
1756
+ refreshControl: props.refreshControl == null ? /* @__PURE__ */ React6__namespace.createElement(
1757
+ reactNative.RefreshControl,
1758
+ {
1759
+ refreshing: !!refreshing,
1760
+ onRefresh,
1761
+ progressViewOffset
1762
+ }
1763
+ ) : props.refreshControl,
1724
1764
  style
1725
1765
  }
1726
1766
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React6__namespace.createElement(DebugView, { state: refState.current }));
package/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React6 from 'react';
2
2
  import React6__default, { createContext, memo, useReducer, useEffect, useMemo, useRef, useCallback, useImperativeHandle, useSyncExternalStore, useContext, useState, forwardRef, useLayoutEffect } from 'react';
3
- import { View, Text, Platform, Animated, ScrollView, Dimensions, StyleSheet } from 'react-native';
3
+ import { View, Text, Platform, Animated, ScrollView, Dimensions, StyleSheet, RefreshControl } from 'react-native';
4
4
 
5
5
  // src/LegendList.tsx
6
6
  var ContextState = React6.createContext(null);
@@ -148,7 +148,7 @@ var DebugView = memo(function DebugView2({ state }) {
148
148
  useInterval(() => {
149
149
  forceUpdate();
150
150
  }, 100);
151
- return /* @__PURE__ */ React.createElement(
151
+ return /* @__PURE__ */ React6.createElement(
152
152
  View,
153
153
  {
154
154
  style: {
@@ -161,8 +161,8 @@ var DebugView = memo(function DebugView2({ state }) {
161
161
  backgroundColor: "#FFFFFFCC"
162
162
  }
163
163
  },
164
- /* @__PURE__ */ React.createElement(Text, null, "PaddingTop: ", paddingTop),
165
- /* @__PURE__ */ React.createElement(Text, null, "At end: ", String(state.isAtBottom))
164
+ /* @__PURE__ */ React6.createElement(Text, null, "PaddingTop: ", paddingTop),
165
+ /* @__PURE__ */ React6.createElement(Text, null, "At end: ", String(state.isAtBottom))
166
166
  );
167
167
  });
168
168
  function useInterval(callback, delay) {
@@ -203,13 +203,17 @@ var Container = ({
203
203
  const position = use$(`containerPosition${id}`) || ANCHORED_POSITION_OUT_OF_VIEW;
204
204
  const column = use$(`containerColumn${id}`) || 0;
205
205
  const numColumns = use$("numColumns");
206
+ const lastItemKeys = use$("lastItemKeys");
207
+ const itemKey = use$(`containerItemKey${id}`);
208
+ const data = use$(`containerItemData${id}`);
209
+ const extraData = use$("extraData");
206
210
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
207
211
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
208
212
  let verticalPaddingStyles;
209
213
  if (columnWrapperStyle && !horizontal && numColumns > 1) {
210
214
  const { columnGap, rowGap, gap } = columnWrapperStyle;
211
215
  verticalPaddingStyles = {
212
- paddingVertical: rowGap || gap || void 0,
216
+ paddingBottom: !lastItemKeys.has(itemKey) ? rowGap || gap || void 0 : void 0,
213
217
  // Apply horizontal padding based on column position (first, middle, or last)
214
218
  paddingLeft: column > 1 ? (columnGap || gap || 0) / 2 : void 0,
215
219
  paddingRight: column < numColumns ? (columnGap || gap || 0) / 2 : void 0
@@ -230,20 +234,24 @@ var Container = ({
230
234
  top: position.relativeCoordinate,
231
235
  ...verticalPaddingStyles || {}
232
236
  };
233
- const lastItemKey = use$("lastItemKey");
234
- const itemKey = use$(`containerItemKey${id}`);
235
- const data = use$(`containerItemData${id}`);
236
- const extraData = use$("extraData");
237
237
  const renderedItemInfo = useMemo(
238
- () => itemKey !== void 0 && getRenderedItem(itemKey),
238
+ () => itemKey !== void 0 ? getRenderedItem(itemKey) : null,
239
239
  [itemKey, data, extraData]
240
240
  );
241
241
  const { index, renderedItem } = renderedItemInfo || {};
242
242
  const onLayout = (event) => {
243
243
  if (itemKey !== void 0) {
244
- const size = Math.floor(event.nativeEvent.layout[horizontal ? "width" : "height"] * 8) / 8;
244
+ const layout = event.nativeEvent.layout;
245
+ const size = Math.floor(layout[horizontal ? "width" : "height"] * 8) / 8;
245
246
  if (size === 0) {
246
- console.log("[WARN] Container 0 height reported, possible bug in LegendList", id, itemKey);
247
+ if (layout.x !== POSITION_OUT_OF_VIEW && layout.y !== POSITION_OUT_OF_VIEW) {
248
+ console.log(
249
+ "[WARN] Container 0 height reported, possible bug in LegendList",
250
+ id,
251
+ itemKey,
252
+ event.nativeEvent
253
+ );
254
+ }
247
255
  return;
248
256
  }
249
257
  updateItemSize(id, itemKey, size);
@@ -268,7 +276,7 @@ var Container = ({
268
276
  () => ({ containerId: id, itemKey, index, value: data }),
269
277
  [id, itemKey, index, data]
270
278
  );
271
- const contentFragment = /* @__PURE__ */ React6__default.createElement(React6__default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React6__default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItem && ItemSeparatorComponent && itemKey !== lastItemKey && ItemSeparatorComponent));
279
+ const contentFragment = /* @__PURE__ */ React6__default.createElement(React6__default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React6__default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && !lastItemKeys.has(itemKey) && /* @__PURE__ */ React6__default.createElement(ItemSeparatorComponent, { leadingItem: renderedItemInfo.item })));
272
280
  if (maintainVisibleContentPosition) {
273
281
  const anchorStyle = position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
274
282
  if (ENABLE_DEVMODE) {
@@ -279,6 +287,8 @@ var Container = ({
279
287
  }
280
288
  return /* @__PURE__ */ React6__default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
281
289
  };
290
+ var typedForwardRef = forwardRef;
291
+ var typedMemo = memo;
282
292
  var useAnimatedValue = (initialValue) => {
283
293
  return useRef(new Animated.Value(initialValue)).current;
284
294
  };
@@ -307,7 +317,7 @@ function useValue$(key, getValue, useMicrotask) {
307
317
  }
308
318
 
309
319
  // src/Containers.tsx
310
- var Containers = React6.memo(function Containers2({
320
+ var Containers = typedMemo(function Containers2({
311
321
  horizontal,
312
322
  recycleItems,
313
323
  ItemSeparatorComponent,
@@ -326,7 +336,7 @@ var Containers = React6.memo(function Containers2({
326
336
  const containers = [];
327
337
  for (let i = 0; i < numContainers; i++) {
328
338
  containers.push(
329
- /* @__PURE__ */ React6.createElement(
339
+ /* @__PURE__ */ React.createElement(
330
340
  Container,
331
341
  {
332
342
  id: i,
@@ -341,7 +351,7 @@ var Containers = React6.memo(function Containers2({
341
351
  );
342
352
  }
343
353
  const style = horizontal ? { width: animSize, opacity: animOpacity } : { height: animSize, opacity: animOpacity };
344
- return /* @__PURE__ */ React6.createElement(Animated.View, { style }, containers);
354
+ return /* @__PURE__ */ React.createElement(Animated.View, { style }, containers);
345
355
  });
346
356
 
347
357
  // src/ListComponent.tsx
@@ -405,7 +415,7 @@ var PaddingAndAdjustDevMode = () => {
405
415
  }
406
416
  ));
407
417
  };
408
- var ListComponent = React6.memo(function ListComponent2({
418
+ var ListComponent = typedMemo(function ListComponent2({
409
419
  style,
410
420
  contentContainerStyle,
411
421
  horizontal,
@@ -426,6 +436,9 @@ var ListComponent = React6.memo(function ListComponent2({
426
436
  refScrollView,
427
437
  maintainVisibleContentPosition,
428
438
  renderScrollComponent,
439
+ onRefresh,
440
+ refreshing,
441
+ progressViewOffset,
429
442
  ...rest
430
443
  }) {
431
444
  const ctx = useStateContext();
@@ -474,7 +487,7 @@ var ListComponent = React6.memo(function ListComponent2({
474
487
  recycleItems,
475
488
  waitForInitialLayout,
476
489
  getRenderedItem,
477
- ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
490
+ ItemSeparatorComponent,
478
491
  updateItemSize
479
492
  }
480
493
  ),
@@ -523,7 +536,6 @@ var ScrollAdjustHandler = class {
523
536
  return false;
524
537
  }
525
538
  };
526
- var typedForwardRef = forwardRef;
527
539
  var useCombinedRef = (...refs) => {
528
540
  const callback = useCallback((element) => {
529
541
  for (const ref of refs) {
@@ -723,6 +735,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
723
735
  waitForInitialLayout = true,
724
736
  extraData,
725
737
  onLayout: onLayoutProp,
738
+ onRefresh,
739
+ refreshing,
740
+ progressViewOffset,
726
741
  ...rest
727
742
  } = props;
728
743
  const { style, contentContainerStyle } = props;
@@ -788,13 +803,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
788
803
  positions: /* @__PURE__ */ new Map(),
789
804
  columns: /* @__PURE__ */ new Map(),
790
805
  pendingAdjust: 0,
791
- waitingForMicrotask: false,
792
806
  isStartReached: initialContentOffset < initialScrollLength * onStartReachedThreshold,
793
807
  isEndReached: false,
794
808
  isAtBottom: false,
795
809
  isAtTop: false,
796
810
  data: dataProp,
797
- idsInFirstRender: void 0,
798
811
  hasScrolled: false,
799
812
  scrollLength: initialScrollLength,
800
813
  startBuffered: 0,
@@ -823,9 +836,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
823
836
  startReachedBlockedByTimer: false,
824
837
  scrollForNextCalculateItemsInView: void 0,
825
838
  enableScrollForNextCalculateItemsInView: true,
826
- minIndexSizeChanged: 0
839
+ minIndexSizeChanged: 0,
840
+ numPendingInitialLayout: 0
827
841
  };
828
- refState.current.idsInFirstRender = new Set(dataProp.map((_, i) => getId(i)));
829
842
  if (maintainVisibleContentPosition) {
830
843
  if (initialScrollIndex) {
831
844
  refState.current.anchorElement = {
@@ -845,6 +858,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
845
858
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
846
859
  set$(ctx, "extraData", extraData);
847
860
  }
861
+ const didDataChange = refState.current.data !== dataProp;
862
+ refState.current.data = dataProp;
848
863
  const getAnchorElementIndex = () => {
849
864
  const state = refState.current;
850
865
  if (state.anchorElement) {
@@ -956,10 +971,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
956
971
  columns,
957
972
  scrollAdjustHandler
958
973
  } = state;
959
- if (state.waitingForMicrotask) {
960
- state.waitingForMicrotask = false;
961
- }
962
- if (!data) {
974
+ if (!data || scrollLength === 0) {
963
975
  return;
964
976
  }
965
977
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
@@ -1192,7 +1204,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1192
1204
  }
1193
1205
  }
1194
1206
  }
1195
- set$(ctx, "containersDidLayout", true);
1207
+ if (state.numPendingInitialLayout === 0) {
1208
+ state.numPendingInitialLayout = state.endBuffered - state.startBuffered + 1;
1209
+ }
1196
1210
  if (state.viewabilityConfigCallbackPairs) {
1197
1211
  updateViewableItems(
1198
1212
  state,
@@ -1233,9 +1247,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1233
1247
  if (!refState.current) {
1234
1248
  return;
1235
1249
  }
1236
- const { scrollLength, scroll, totalSize } = refState.current;
1237
- if (totalSize > 0) {
1238
- const distanceFromEnd = totalSize - scroll - scrollLength + (peek$(ctx, "paddingTop") || 0);
1250
+ const { scrollLength, scroll, totalSize, hasScrolled } = refState.current;
1251
+ if (totalSize > 0 && hasScrolled) {
1252
+ const distanceFromEnd = Math.abs(
1253
+ totalSize - scroll - scrollLength + (peek$(ctx, "paddingTop") || 0)
1254
+ );
1239
1255
  if (refState.current) {
1240
1256
  refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1241
1257
  }
@@ -1384,18 +1400,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1384
1400
  addTotalSize(null, totalSize, totalSizeBelowIndex);
1385
1401
  };
1386
1402
  const isFirst = !refState.current.renderItem;
1387
- if (isFirst || dataProp !== refState.current.data || numColumnsProp !== peek$(ctx, "numColumns")) {
1388
- if (!keyExtractorProp && !isFirst && dataProp !== refState.current.data) {
1403
+ if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
1404
+ if (!keyExtractorProp && !isFirst && didDataChange) {
1389
1405
  refState.current.sizes.clear();
1390
1406
  refState.current.positions.clear();
1391
1407
  }
1392
- refState.current.data = dataProp;
1393
- const indexByKey = /* @__PURE__ */ new Map();
1394
- for (let i = 0; i < dataProp.length; i++) {
1395
- const key = getId(i);
1396
- indexByKey.set(key, i);
1397
- }
1398
- refState.current.indexByKey = indexByKey;
1399
1408
  calcTotalSizesAndPositions({ forgetPositions: false });
1400
1409
  }
1401
1410
  useEffect(() => {
@@ -1408,17 +1417,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1408
1417
  set$(ctx, "extraData", extraData);
1409
1418
  }, [extraData]);
1410
1419
  refState.current.renderItem = renderItem;
1411
- const lastItemKey = dataProp.length > 0 ? getId(dataProp.length - 1) : void 0;
1420
+ const memoizedLastItemKeys = useMemo(() => {
1421
+ if (!dataProp.length) return [];
1422
+ return new Set(
1423
+ Array.from({ length: Math.min(numColumnsProp, dataProp.length) }, (_, i) => getId(dataProp.length - 1 - i))
1424
+ );
1425
+ }, [dataProp.length, numColumnsProp, dataProp.slice(-numColumnsProp).toString()]);
1412
1426
  const stylePaddingTop = (_d = (_c = (_a = StyleSheet.flatten(style)) == null ? void 0 : _a.paddingTop) != null ? _c : (_b = StyleSheet.flatten(contentContainerStyle)) == null ? void 0 : _b.paddingTop) != null ? _d : 0;
1413
1427
  const initalizeStateVars = () => {
1414
- set$(ctx, "lastItemKey", lastItemKey);
1428
+ set$(ctx, "lastItemKeys", memoizedLastItemKeys);
1415
1429
  set$(ctx, "numColumns", numColumnsProp);
1416
1430
  set$(ctx, "stylePaddingTop", stylePaddingTop);
1417
1431
  };
1418
1432
  if (isFirst) {
1419
1433
  initalizeStateVars();
1420
1434
  }
1421
- useEffect(initalizeStateVars, [lastItemKey, numColumnsProp, stylePaddingTop]);
1435
+ useEffect(initalizeStateVars, [memoizedLastItemKeys, numColumnsProp, stylePaddingTop]);
1422
1436
  const getRenderedItem = useCallback((key) => {
1423
1437
  var _a2, _b2;
1424
1438
  const state = refState.current;
@@ -1450,34 +1464,40 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1450
1464
  useRecyclingEffect: useRecyclingEffect2,
1451
1465
  useRecyclingState: useRecyclingState2
1452
1466
  });
1453
- return { index, renderedItem };
1467
+ return { index, item: data[index], renderedItem };
1454
1468
  }, []);
1455
- useInit(() => {
1469
+ const doInitialAllocateContainers = () => {
1456
1470
  var _a2;
1457
1471
  const state = refState.current;
1458
- const viewability = setupViewability(props);
1459
- state.viewabilityConfigCallbackPairs = viewability;
1460
- state.enableScrollForNextCalculateItemsInView = !viewability;
1461
1472
  const scrollLength = state.scrollLength;
1462
- const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, dataProp[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1463
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1464
- for (let i = 0; i < numContainers; i++) {
1465
- set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1466
- set$(ctx, `containerColumn${i}`, -1);
1467
- }
1468
- set$(ctx, "numContainers", numContainers);
1469
- set$(ctx, "numContainersPooled", numContainers * 2);
1470
- if (initialScrollIndex) {
1471
- requestAnimationFrame(() => {
1473
+ if (scrollLength > 0 && !peek$(ctx, "numContainers")) {
1474
+ const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, dataProp[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1475
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1476
+ for (let i = 0; i < numContainers; i++) {
1477
+ set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1478
+ set$(ctx, `containerColumn${i}`, -1);
1479
+ }
1480
+ set$(ctx, "numContainers", numContainers);
1481
+ set$(ctx, "numContainersPooled", numContainers * 2);
1482
+ if (initialScrollIndex) {
1483
+ requestAnimationFrame(() => {
1484
+ calculateItemsInView(state.scrollVelocity);
1485
+ });
1486
+ } else {
1472
1487
  calculateItemsInView(state.scrollVelocity);
1473
- });
1474
- } else {
1475
- calculateItemsInView(state.scrollVelocity);
1488
+ }
1476
1489
  }
1490
+ };
1491
+ useInit(() => {
1492
+ const state = refState.current;
1493
+ const viewability = setupViewability(props);
1494
+ state.viewabilityConfigCallbackPairs = viewability;
1495
+ state.enableScrollForNextCalculateItemsInView = !viewability;
1496
+ doInitialAllocateContainers();
1477
1497
  });
1478
1498
  const updateItemSize = useCallback((containerId, itemKey, size) => {
1479
1499
  const state = refState.current;
1480
- const { sizes, indexByKey, columns, sizesLaidOut, data, rowHeights } = state;
1500
+ const { sizes, indexByKey, sizesLaidOut, data, rowHeights } = state;
1481
1501
  if (!data) {
1482
1502
  return;
1483
1503
  }
@@ -1485,8 +1505,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1485
1505
  const numColumns = peek$(ctx, "numColumns");
1486
1506
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1487
1507
  const prevSize = getItemSize(itemKey, index, data);
1508
+ let needsCalculate = false;
1509
+ if (state.numPendingInitialLayout > 0) {
1510
+ state.numPendingInitialLayout--;
1511
+ if (state.numPendingInitialLayout === 0) {
1512
+ needsCalculate = true;
1513
+ state.numPendingInitialLayout = -1;
1514
+ queueMicrotask(() => {
1515
+ set$(ctx, "containersDidLayout", true);
1516
+ });
1517
+ }
1518
+ }
1488
1519
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
1489
1520
  let diff;
1521
+ needsCalculate = true;
1490
1522
  if (numColumns > 1) {
1491
1523
  const rowNumber = Math.floor(index / numColumnsProp);
1492
1524
  const prevSizeInRow = getRowHeight(rowNumber);
@@ -1520,22 +1552,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1520
1552
  refState.current.scrollForNextCalculateItemsInView = void 0;
1521
1553
  addTotalSize(itemKey, diff, 0);
1522
1554
  doMaintainScrollAtEnd(true);
1523
- const scrollVelocity = state.scrollVelocity;
1524
- if (!state.waitingForMicrotask && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1525
- if (!peek$(ctx, "containersDidLayout")) {
1526
- state.waitingForMicrotask = true;
1527
- queueMicrotask(() => {
1528
- if (state.waitingForMicrotask) {
1529
- state.waitingForMicrotask = false;
1530
- calculateItemsInView(state.scrollVelocity);
1531
- }
1532
- });
1533
- } else {
1534
- calculateItemsInView(state.scrollVelocity);
1535
- }
1536
- }
1537
1555
  if (onItemSizeChanged) {
1538
- onItemSizeChanged({ size, previous: prevSize, index, itemKey, itemData: data[index] });
1556
+ onItemSizeChanged({
1557
+ size,
1558
+ previous: prevSize,
1559
+ index,
1560
+ itemKey,
1561
+ itemData: data[index]
1562
+ });
1563
+ }
1564
+ }
1565
+ if (needsCalculate) {
1566
+ const scrollVelocity = state.scrollVelocity;
1567
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || state.numPendingInitialLayout < 0)) {
1568
+ calculateItemsInView(state.scrollVelocity);
1539
1569
  }
1540
1570
  }
1541
1571
  }, []);
@@ -1547,7 +1577,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1547
1577
  const onLayout = useCallback((event) => {
1548
1578
  const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
1549
1579
  const didChange = scrollLength !== refState.current.scrollLength;
1580
+ refState.current.scrollLength;
1550
1581
  refState.current.scrollLength = scrollLength;
1582
+ doInitialAllocateContainers();
1551
1583
  doMaintainScrollAtEnd(false);
1552
1584
  doUpdatePaddingTop();
1553
1585
  checkAtBottom();
@@ -1667,7 +1699,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1667
1699
  scrollToIndex({ index, animated });
1668
1700
  }
1669
1701
  },
1670
- scrollToEnd: () => refScroller.current.scrollToEnd()
1702
+ scrollToEnd: (options) => refScroller.current.scrollToEnd(options)
1671
1703
  };
1672
1704
  },
1673
1705
  []
@@ -1700,6 +1732,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1700
1732
  maintainVisibleContentPosition,
1701
1733
  scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : Platform.OS === "web" ? 16 : void 0,
1702
1734
  waitForInitialLayout,
1735
+ refreshControl: props.refreshControl == null ? /* @__PURE__ */ React6.createElement(
1736
+ RefreshControl,
1737
+ {
1738
+ refreshing: !!refreshing,
1739
+ onRefresh,
1740
+ progressViewOffset
1741
+ }
1742
+ ) : props.refreshControl,
1703
1743
  style
1704
1744
  }
1705
1745
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React6.createElement(DebugView, { state: refState.current }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "1.0.0-beta.15",
3
+ "version": "1.0.0-beta.17",
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,
package/reanimated.d.mts CHANGED
@@ -2,7 +2,7 @@ import { LegendListRef, LegendListPropsBase } from '@legendapp/list';
2
2
  import React__default, { ComponentProps } from 'react';
3
3
  import Animated from 'react-native-reanimated';
4
4
 
5
- type KeysToOmit = "getEstimatedItemSize" | "keyExtractor" | "animatedProps" | "renderItem" | "onItemSizeChanged";
5
+ type KeysToOmit = "getEstimatedItemSize" | "keyExtractor" | "animatedProps" | "renderItem" | "onItemSizeChanged" | "ItemSeparatorComponent";
6
6
  type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Animated.ScrollView>>;
7
7
  interface AnimatedLegendListProps<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
8
8
  refScrollView?: React__default.Ref<Animated.ScrollView>;
package/reanimated.d.ts CHANGED
@@ -2,7 +2,7 @@ import { LegendListRef, LegendListPropsBase } from '@legendapp/list';
2
2
  import React__default, { ComponentProps } from 'react';
3
3
  import Animated from 'react-native-reanimated';
4
4
 
5
- type KeysToOmit = "getEstimatedItemSize" | "keyExtractor" | "animatedProps" | "renderItem" | "onItemSizeChanged";
5
+ type KeysToOmit = "getEstimatedItemSize" | "keyExtractor" | "animatedProps" | "renderItem" | "onItemSizeChanged" | "ItemSeparatorComponent";
6
6
  type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Animated.ScrollView>>;
7
7
  interface AnimatedLegendListProps<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
8
8
  refScrollView?: React__default.Ref<Animated.ScrollView>;