@legendapp/list 0.4.3 → 0.4.5

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/README.md CHANGED
@@ -48,7 +48,6 @@ interface PropsOptional {
48
48
  maintainScrollAtEndThreshold?: number;
49
49
  onEndReached?: ((info: { distanceFromEnd: number }) => void) | null | undefined;
50
50
  keyExtractor?: (item: T, index: number) => string;
51
- onViewableRangeChanged?: (range: ViewableRange<T>) => void;
52
51
  }
53
52
  ```
54
53
 
package/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ComponentProps, ReactNode, ForwardedRef, ReactElement } from 'react';
2
2
  import { ScrollView, StyleProp, ViewStyle, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
3
3
 
4
- type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset'> & {
4
+ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, "contentOffset"> & {
5
5
  data: ArrayLike<any> & T[];
6
6
  initialScrollOffset?: number;
7
7
  initialScrollIndex?: number;
@@ -19,11 +19,12 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
19
19
  }) => void) | null | undefined;
20
20
  keyExtractor?: (item: T, index: number) => string;
21
21
  renderItem?: (props: LegendListRenderItemProps<T>) => ReactNode;
22
- onViewableRangeChanged?: (range: ViewableRange<T>) => void;
23
22
  ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
24
23
  ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
25
24
  ListFooterComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
26
25
  ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
26
+ ListEmptyComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
27
+ ListEmptyComponentStyle?: StyleProp<ViewStyle> | undefined;
27
28
  ItemSeparatorComponent?: React.ComponentType<any>;
28
29
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
29
30
  viewabilityConfig?: ViewabilityConfig;
@@ -51,6 +52,7 @@ interface InternalState {
51
52
  totalSize: number;
52
53
  timeouts: Set<number>;
53
54
  viewabilityConfigCallbackPairs: ViewabilityConfigCallbackPairs;
55
+ renderItem: (props: LegendListRenderItemProps<any>) => ReactNode;
54
56
  }
55
57
  interface ViewableRange<T> {
56
58
  startBuffered: number;
package/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ComponentProps, ReactNode, ForwardedRef, ReactElement } from 'react';
2
2
  import { ScrollView, StyleProp, ViewStyle, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
3
3
 
4
- type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset'> & {
4
+ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, "contentOffset"> & {
5
5
  data: ArrayLike<any> & T[];
6
6
  initialScrollOffset?: number;
7
7
  initialScrollIndex?: number;
@@ -19,11 +19,12 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
19
19
  }) => void) | null | undefined;
20
20
  keyExtractor?: (item: T, index: number) => string;
21
21
  renderItem?: (props: LegendListRenderItemProps<T>) => ReactNode;
22
- onViewableRangeChanged?: (range: ViewableRange<T>) => void;
23
22
  ListHeaderComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
24
23
  ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
25
24
  ListFooterComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
26
25
  ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
26
+ ListEmptyComponent?: React.ComponentType<any> | React.ReactElement | null | undefined;
27
+ ListEmptyComponentStyle?: StyleProp<ViewStyle> | undefined;
27
28
  ItemSeparatorComponent?: React.ComponentType<any>;
28
29
  viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
29
30
  viewabilityConfig?: ViewabilityConfig;
@@ -51,6 +52,7 @@ interface InternalState {
51
52
  totalSize: number;
52
53
  timeouts: Set<number>;
53
54
  viewabilityConfigCallbackPairs: ViewabilityConfigCallbackPairs;
55
+ renderItem: (props: LegendListRenderItemProps<any>) => ReactNode;
54
56
  }
55
57
  interface ViewableRange<T> {
56
58
  startBuffered: number;
package/index.js CHANGED
@@ -33,7 +33,11 @@ function StateProvider({ children }) {
33
33
  const [value] = React6__namespace.useState(() => ({
34
34
  hooks: /* @__PURE__ */ new Map(),
35
35
  listeners: /* @__PURE__ */ new Map(),
36
- values: /* @__PURE__ */ new Map()
36
+ values: /* @__PURE__ */ new Map(),
37
+ mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
38
+ mapViewabilityValues: /* @__PURE__ */ new Map(),
39
+ mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
40
+ mapViewabilityAmountValues: /* @__PURE__ */ new Map()
37
41
  }));
38
42
  return /* @__PURE__ */ React6__namespace.createElement(ContextState.Provider, { value }, children);
39
43
  }
@@ -213,6 +217,8 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
213
217
  ListHeaderComponentStyle,
214
218
  ListFooterComponent,
215
219
  ListFooterComponentStyle,
220
+ ListEmptyComponent,
221
+ ListEmptyComponentStyle,
216
222
  getRenderedItem,
217
223
  updateItemSize,
218
224
  addTotalSize,
@@ -254,6 +260,7 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
254
260
  },
255
261
  getComponent(ListHeaderComponent)
256
262
  ),
263
+ ListEmptyComponent && /* @__PURE__ */ React6__namespace.createElement(reactNative.View, { style: ListEmptyComponentStyle }, getComponent(ListEmptyComponent)),
257
264
  /* @__PURE__ */ React6__namespace.createElement(
258
265
  Containers,
259
266
  {
@@ -267,15 +274,26 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
267
274
  ListFooterComponent && /* @__PURE__ */ React6__namespace.createElement(reactNative.View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
268
275
  );
269
276
  });
277
+ var symbolFirst = Symbol();
278
+ function useInit(cb) {
279
+ const refValue = React6.useRef(symbolFirst);
280
+ if (refValue.current === symbolFirst) {
281
+ refValue.current = cb();
282
+ }
283
+ return refValue.current;
284
+ }
270
285
 
271
286
  // src/viewability.ts
272
287
  var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
273
- var mapViewabilityCallbacks = /* @__PURE__ */ new Map();
274
- var mapViewabilityAmountCallbacks = /* @__PURE__ */ new Map();
275
288
  function setupViewability(props) {
276
289
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
277
290
  viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
278
- { viewabilityConfig: viewabilityConfig || { viewAreaCoveragePercentThreshold: 0 }, onViewableItemsChanged }
291
+ {
292
+ viewabilityConfig: viewabilityConfig || {
293
+ viewAreaCoveragePercentThreshold: 0
294
+ },
295
+ onViewableItemsChanged
296
+ }
279
297
  ];
280
298
  if (viewabilityConfigCallbackPairs) {
281
299
  for (const pair of viewabilityConfigCallbackPairs) {
@@ -341,12 +359,16 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
341
359
  }
342
360
  }
343
361
  }
344
- Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
362
+ Object.assign(viewabilityState, {
363
+ viewableItems,
364
+ previousStart: start,
365
+ previousEnd: end
366
+ });
345
367
  if (changed.length > 0) {
346
368
  viewabilityState.viewableItems = viewableItems;
347
369
  for (let i = 0; i < changed.length; i++) {
348
370
  const change = changed[i];
349
- maybeUpdateViewabilityCallback(configId, change);
371
+ maybeUpdateViewabilityCallback(ctx, configId, change);
350
372
  }
351
373
  if (onViewableItemsChanged) {
352
374
  onViewableItemsChanged({ viewableItems, changed });
@@ -369,20 +391,22 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
369
391
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
370
392
  const isViewable2 = percent >= viewablePercentThreshold;
371
393
  const containerId = findContainerId(state, ctx, index);
372
- const cb = mapViewabilityAmountCallbacks.get(containerId);
394
+ const value = {
395
+ index,
396
+ isViewable: isViewable2,
397
+ item,
398
+ key,
399
+ percentVisible,
400
+ percentOfScroller,
401
+ sizeVisible,
402
+ size,
403
+ position: top,
404
+ scrollSize
405
+ };
406
+ ctx.mapViewabilityAmountValues.set(containerId, value);
407
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
373
408
  if (cb) {
374
- cb({
375
- index,
376
- isViewable: isViewable2,
377
- item,
378
- key,
379
- percentVisible,
380
- percentOfScroller,
381
- sizeVisible,
382
- size,
383
- position: top,
384
- scrollSize
385
- });
409
+ cb(value);
386
410
  }
387
411
  return isViewable2;
388
412
  }
@@ -396,24 +420,12 @@ function findContainerId(state, ctx, index) {
396
420
  }
397
421
  return -1;
398
422
  }
399
- function maybeUpdateViewabilityCallback(configId, viewToken) {
423
+ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
400
424
  const key = viewToken.key + configId;
401
- const cb = mapViewabilityCallbacks.get(key);
425
+ ctx.mapViewabilityValues.set(key, viewToken);
426
+ const cb = ctx.mapViewabilityCallbacks.get(key);
402
427
  cb == null ? void 0 : cb(viewToken);
403
428
  }
404
- function registerViewabilityCallback(containerId, configId, callback) {
405
- const key = containerId + configId;
406
- mapViewabilityCallbacks.set(key, callback);
407
- return () => {
408
- mapViewabilityCallbacks.delete(key);
409
- };
410
- }
411
- function registerViewabilityAmountCallback(containerId, callback) {
412
- mapViewabilityAmountCallbacks.set(containerId, callback);
413
- return () => {
414
- mapViewabilityAmountCallbacks.delete(containerId);
415
- };
416
- }
417
429
 
418
430
  // src/LegendList.tsx
419
431
  var DEFAULT_SCROLL_BUFFER = 0;
@@ -443,7 +455,7 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
443
455
  estimatedItemSize,
444
456
  getEstimatedItemSize,
445
457
  onEndReached,
446
- onViewableRangeChanged,
458
+ ListEmptyComponent,
447
459
  ...rest
448
460
  } = props;
449
461
  const ctx = useStateContext();
@@ -508,11 +520,13 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
508
520
  scroll: initialContentOffset || 0,
509
521
  totalSize: 0,
510
522
  timeouts: /* @__PURE__ */ new Set(),
511
- viewabilityConfigCallbackPairs: void 0
523
+ viewabilityConfigCallbackPairs: void 0,
524
+ renderItem: void 0
512
525
  };
513
526
  refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
514
527
  }
515
528
  refState.current.data = data;
529
+ refState.current.renderItem = renderItem;
516
530
  set$(ctx, "numItems", data.length);
517
531
  set$(ctx, "stylePaddingTop", (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
518
532
  const addTotalSize = React6.useCallback((add) => {
@@ -534,71 +548,93 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
534
548
  refState.current.animFrameTotalSize = requestAnimationFrame(doAdd);
535
549
  }
536
550
  }, []);
537
- const getRenderedItem = React6.useCallback(
538
- (index, containerIndex) => {
539
- var _a2;
540
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
541
- if (!data2) {
542
- return null;
543
- }
544
- const useViewability = (configId, callback) => {
545
- React6.useEffect(() => registerViewabilityCallback(containerIndex, configId, callback), []);
546
- };
547
- const useViewabilityAmount = (callback) => {
548
- React6.useEffect(() => registerViewabilityAmountCallback(containerIndex, callback), []);
549
- };
550
- const useRecyclingEffect = (effect) => {
551
- React6.useEffect(() => {
552
- let prevIndex = index;
553
- let prevItem = data2[index];
554
- const signal = `containerIndex${containerIndex}`;
555
- listen$(ctx, signal, () => {
556
- var _a3;
557
- const data3 = (_a3 = refState.current) == null ? void 0 : _a3.data;
558
- if (data3) {
559
- const newIndex = peek$(ctx, signal);
560
- const newItem = data3[newIndex];
561
- if (newItem) {
562
- effect({
563
- index: newIndex,
564
- item: newItem,
565
- prevIndex,
566
- prevItem
567
- });
568
- }
569
- prevIndex = newIndex;
570
- prevItem = newItem;
551
+ const getRenderedItem = React6.useCallback((index, containerIndex) => {
552
+ var _a2, _b2, _c;
553
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
554
+ if (!data2) {
555
+ return null;
556
+ }
557
+ const useViewability = (configId, callback) => {
558
+ const key = containerIndex + configId;
559
+ useInit(() => {
560
+ const value = ctx.mapViewabilityValues.get(key);
561
+ if (value) {
562
+ callback(value);
563
+ }
564
+ });
565
+ ctx.mapViewabilityCallbacks.set(key, callback);
566
+ React6.useEffect(
567
+ () => () => {
568
+ ctx.mapViewabilityCallbacks.delete(key);
569
+ },
570
+ []
571
+ );
572
+ };
573
+ const useViewabilityAmount = (callback) => {
574
+ useInit(() => {
575
+ const value = ctx.mapViewabilityAmountValues.get(containerIndex);
576
+ if (value) {
577
+ callback(value);
578
+ }
579
+ });
580
+ ctx.mapViewabilityAmountCallbacks.set(containerIndex, callback);
581
+ React6.useEffect(
582
+ () => () => {
583
+ ctx.mapViewabilityAmountCallbacks.delete(containerIndex);
584
+ },
585
+ []
586
+ );
587
+ };
588
+ const useRecyclingEffect = (effect) => {
589
+ React6.useEffect(() => {
590
+ const state = refState.current;
591
+ let prevIndex = index;
592
+ let prevItem = state.data[index];
593
+ const signal = `containerIndex${containerIndex}`;
594
+ listen$(ctx, signal, () => {
595
+ const data3 = state.data;
596
+ if (data3) {
597
+ const newIndex = peek$(ctx, signal);
598
+ const newItem = data3[newIndex];
599
+ if (newItem) {
600
+ effect({
601
+ index: newIndex,
602
+ item: newItem,
603
+ prevIndex,
604
+ prevItem
605
+ });
571
606
  }
572
- });
573
- }, []);
574
- };
575
- const useRecyclingState = (updateState) => {
576
- const stateInfo = React6.useState(
577
- () => updateState({
578
- index,
579
- item: data2[index],
580
- prevIndex: void 0,
581
- prevItem: void 0
582
- })
583
- );
584
- useRecyclingEffect((state) => {
585
- const newState = updateState(state);
586
- stateInfo[1](newState);
607
+ prevIndex = newIndex;
608
+ prevItem = newItem;
609
+ }
587
610
  });
588
- return stateInfo;
589
- };
590
- const renderedItem = renderItem == null ? void 0 : renderItem({
591
- item: data2[index],
592
- index,
593
- useViewability,
594
- useViewabilityAmount,
595
- useRecyclingEffect,
596
- useRecyclingState
611
+ }, []);
612
+ };
613
+ const useRecyclingState = (updateState) => {
614
+ const stateInfo = React6.useState(
615
+ () => updateState({
616
+ index,
617
+ item: refState.current.data[index],
618
+ prevIndex: void 0,
619
+ prevItem: void 0
620
+ })
621
+ );
622
+ useRecyclingEffect((state) => {
623
+ const newState = updateState(state);
624
+ stateInfo[1](newState);
597
625
  });
598
- return renderedItem;
599
- },
600
- [renderItem]
601
- );
626
+ return stateInfo;
627
+ };
628
+ const renderedItem = (_c = (_b2 = refState.current).renderItem) == null ? void 0 : _c.call(_b2, {
629
+ item: data2[index],
630
+ index,
631
+ useViewability,
632
+ useViewabilityAmount,
633
+ useRecyclingEffect,
634
+ useRecyclingState
635
+ });
636
+ return renderedItem;
637
+ }, []);
602
638
  const calculateItemsInView = React6.useCallback(() => {
603
639
  reactNative.unstable_batchedUpdates(() => {
604
640
  var _a2, _b2, _c;
@@ -735,32 +771,21 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
735
771
  }
736
772
  }
737
773
  }
738
- if (onViewableRangeChanged) {
739
- if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
740
- onViewableRangeChanged({
741
- start: startNoBuffer,
742
- startBuffered,
743
- end: endNoBuffer,
744
- endBuffered,
745
- items: data2.slice(startNoBuffer, endNoBuffer + 1)
746
- });
747
- }
748
- }
749
- if (refState.current.viewabilityConfigCallbackPairs) {
750
- updateViewableItems(
751
- refState.current,
752
- ctx,
753
- refState.current.viewabilityConfigCallbackPairs,
754
- getId,
755
- scrollLength,
756
- startNoBuffer,
757
- endNoBuffer
758
- );
759
- }
774
+ }
775
+ if (refState.current.viewabilityConfigCallbackPairs) {
776
+ updateViewableItems(
777
+ refState.current,
778
+ ctx,
779
+ refState.current.viewabilityConfigCallbackPairs,
780
+ getId,
781
+ scrollLength,
782
+ startNoBuffer,
783
+ endNoBuffer
784
+ );
760
785
  }
761
786
  });
762
- }, [data]);
763
- React6.useMemo(() => {
787
+ }, []);
788
+ useInit(() => {
764
789
  var _a2, _b2;
765
790
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
766
791
  const scrollLength = refState.current.scrollLength;
@@ -779,7 +804,7 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
779
804
  totalSize += (_b2 = sizes.get(id)) != null ? _b2 : getItemSize(i, data[i]);
780
805
  }
781
806
  addTotalSize(totalSize);
782
- }, []);
807
+ });
783
808
  const checkAtBottom = () => {
784
809
  var _a2;
785
810
  const { scrollLength, scroll } = refState.current;
@@ -801,6 +826,15 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
801
826
  if (refState.current) {
802
827
  refState.current.isEndReached = false;
803
828
  }
829
+ const numContainers = peek$(ctx, "numContainers");
830
+ if (data.length < numContainers) {
831
+ for (let i = 0; i < numContainers; i++) {
832
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
833
+ if (itemIndex >= data.length) {
834
+ set$(ctx, `containerIndex${i}`, -1);
835
+ }
836
+ }
837
+ }
804
838
  calculateItemsInView();
805
839
  checkAtBottom();
806
840
  }, [data]);
@@ -863,31 +897,35 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
863
897
  },
864
898
  []
865
899
  );
866
- React6.useImperativeHandle(forwardedRef, () => {
867
- const scrollToIndex = ({ index, animated }) => {
868
- const offsetObj = calculateInitialOffset(index);
869
- const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
870
- refScroller.current.scrollTo({ ...offset, animated });
871
- };
872
- return {
873
- getNativeScrollRef: () => refScroller.current,
874
- getScrollableNode: refScroller.current.getScrollableNode,
875
- getScrollResponder: refScroller.current.getScrollResponder,
876
- flashScrollIndicators: refScroller.current.flashScrollIndicators,
877
- scrollToIndex,
878
- scrollToOffset: ({ offset, animated }) => {
879
- const offsetObj = horizontal ? { x: offset, y: 0 } : { x: 0, y: offset };
880
- refScroller.current.scrollTo({ ...offsetObj, animated });
881
- },
882
- scrollToItem: ({ item, animated }) => {
883
- const index = data.indexOf(item);
884
- if (index !== -1) {
885
- scrollToIndex({ index, animated });
886
- }
887
- },
888
- scrollToEnd: refScroller.current.scrollToEnd
889
- };
890
- }, []);
900
+ React6.useImperativeHandle(
901
+ forwardedRef,
902
+ () => {
903
+ const scrollToIndex = ({ index, animated }) => {
904
+ const offsetObj = calculateInitialOffset(index);
905
+ const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
906
+ refScroller.current.scrollTo({ ...offset, animated });
907
+ };
908
+ return {
909
+ getNativeScrollRef: () => refScroller.current,
910
+ getScrollableNode: refScroller.current.getScrollableNode,
911
+ getScrollResponder: refScroller.current.getScrollResponder,
912
+ flashScrollIndicators: refScroller.current.flashScrollIndicators,
913
+ scrollToIndex,
914
+ scrollToOffset: ({ offset, animated }) => {
915
+ const offsetObj = horizontal ? { x: offset, y: 0 } : { x: 0, y: offset };
916
+ refScroller.current.scrollTo({ ...offsetObj, animated });
917
+ },
918
+ scrollToItem: ({ item, animated }) => {
919
+ const index = data.indexOf(item);
920
+ if (index !== -1) {
921
+ scrollToIndex({ index, animated });
922
+ }
923
+ },
924
+ scrollToEnd: refScroller.current.scrollToEnd
925
+ };
926
+ },
927
+ []
928
+ );
891
929
  return /* @__PURE__ */ React6__namespace.createElement(
892
930
  ListComponent,
893
931
  {
@@ -903,7 +941,8 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
903
941
  onLayout,
904
942
  recycleItems,
905
943
  alignItemsAtEnd,
906
- addTotalSize
944
+ addTotalSize,
945
+ ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0
907
946
  }
908
947
  );
909
948
  });
package/index.mjs CHANGED
@@ -12,7 +12,11 @@ function StateProvider({ children }) {
12
12
  const [value] = React6.useState(() => ({
13
13
  hooks: /* @__PURE__ */ new Map(),
14
14
  listeners: /* @__PURE__ */ new Map(),
15
- values: /* @__PURE__ */ new Map()
15
+ values: /* @__PURE__ */ new Map(),
16
+ mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
17
+ mapViewabilityValues: /* @__PURE__ */ new Map(),
18
+ mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
19
+ mapViewabilityAmountValues: /* @__PURE__ */ new Map()
16
20
  }));
17
21
  return /* @__PURE__ */ React6.createElement(ContextState.Provider, { value }, children);
18
22
  }
@@ -192,6 +196,8 @@ var ListComponent = React6.memo(function ListComponent2({
192
196
  ListHeaderComponentStyle,
193
197
  ListFooterComponent,
194
198
  ListFooterComponentStyle,
199
+ ListEmptyComponent,
200
+ ListEmptyComponentStyle,
195
201
  getRenderedItem,
196
202
  updateItemSize,
197
203
  addTotalSize,
@@ -233,6 +239,7 @@ var ListComponent = React6.memo(function ListComponent2({
233
239
  },
234
240
  getComponent(ListHeaderComponent)
235
241
  ),
242
+ ListEmptyComponent && /* @__PURE__ */ React6.createElement(View, { style: ListEmptyComponentStyle }, getComponent(ListEmptyComponent)),
236
243
  /* @__PURE__ */ React6.createElement(
237
244
  Containers,
238
245
  {
@@ -246,15 +253,26 @@ var ListComponent = React6.memo(function ListComponent2({
246
253
  ListFooterComponent && /* @__PURE__ */ React6.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
247
254
  );
248
255
  });
256
+ var symbolFirst = Symbol();
257
+ function useInit(cb) {
258
+ const refValue = useRef(symbolFirst);
259
+ if (refValue.current === symbolFirst) {
260
+ refValue.current = cb();
261
+ }
262
+ return refValue.current;
263
+ }
249
264
 
250
265
  // src/viewability.ts
251
266
  var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
252
- var mapViewabilityCallbacks = /* @__PURE__ */ new Map();
253
- var mapViewabilityAmountCallbacks = /* @__PURE__ */ new Map();
254
267
  function setupViewability(props) {
255
268
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
256
269
  viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
257
- { viewabilityConfig: viewabilityConfig || { viewAreaCoveragePercentThreshold: 0 }, onViewableItemsChanged }
270
+ {
271
+ viewabilityConfig: viewabilityConfig || {
272
+ viewAreaCoveragePercentThreshold: 0
273
+ },
274
+ onViewableItemsChanged
275
+ }
258
276
  ];
259
277
  if (viewabilityConfigCallbackPairs) {
260
278
  for (const pair of viewabilityConfigCallbackPairs) {
@@ -320,12 +338,16 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
320
338
  }
321
339
  }
322
340
  }
323
- Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
341
+ Object.assign(viewabilityState, {
342
+ viewableItems,
343
+ previousStart: start,
344
+ previousEnd: end
345
+ });
324
346
  if (changed.length > 0) {
325
347
  viewabilityState.viewableItems = viewableItems;
326
348
  for (let i = 0; i < changed.length; i++) {
327
349
  const change = changed[i];
328
- maybeUpdateViewabilityCallback(configId, change);
350
+ maybeUpdateViewabilityCallback(ctx, configId, change);
329
351
  }
330
352
  if (onViewableItemsChanged) {
331
353
  onViewableItemsChanged({ viewableItems, changed });
@@ -348,20 +370,22 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
348
370
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
349
371
  const isViewable2 = percent >= viewablePercentThreshold;
350
372
  const containerId = findContainerId(state, ctx, index);
351
- const cb = mapViewabilityAmountCallbacks.get(containerId);
373
+ const value = {
374
+ index,
375
+ isViewable: isViewable2,
376
+ item,
377
+ key,
378
+ percentVisible,
379
+ percentOfScroller,
380
+ sizeVisible,
381
+ size,
382
+ position: top,
383
+ scrollSize
384
+ };
385
+ ctx.mapViewabilityAmountValues.set(containerId, value);
386
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
352
387
  if (cb) {
353
- cb({
354
- index,
355
- isViewable: isViewable2,
356
- item,
357
- key,
358
- percentVisible,
359
- percentOfScroller,
360
- sizeVisible,
361
- size,
362
- position: top,
363
- scrollSize
364
- });
388
+ cb(value);
365
389
  }
366
390
  return isViewable2;
367
391
  }
@@ -375,24 +399,12 @@ function findContainerId(state, ctx, index) {
375
399
  }
376
400
  return -1;
377
401
  }
378
- function maybeUpdateViewabilityCallback(configId, viewToken) {
402
+ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
379
403
  const key = viewToken.key + configId;
380
- const cb = mapViewabilityCallbacks.get(key);
404
+ ctx.mapViewabilityValues.set(key, viewToken);
405
+ const cb = ctx.mapViewabilityCallbacks.get(key);
381
406
  cb == null ? void 0 : cb(viewToken);
382
407
  }
383
- function registerViewabilityCallback(containerId, configId, callback) {
384
- const key = containerId + configId;
385
- mapViewabilityCallbacks.set(key, callback);
386
- return () => {
387
- mapViewabilityCallbacks.delete(key);
388
- };
389
- }
390
- function registerViewabilityAmountCallback(containerId, callback) {
391
- mapViewabilityAmountCallbacks.set(containerId, callback);
392
- return () => {
393
- mapViewabilityAmountCallbacks.delete(containerId);
394
- };
395
- }
396
408
 
397
409
  // src/LegendList.tsx
398
410
  var DEFAULT_SCROLL_BUFFER = 0;
@@ -422,7 +434,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
422
434
  estimatedItemSize,
423
435
  getEstimatedItemSize,
424
436
  onEndReached,
425
- onViewableRangeChanged,
437
+ ListEmptyComponent,
426
438
  ...rest
427
439
  } = props;
428
440
  const ctx = useStateContext();
@@ -487,11 +499,13 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
487
499
  scroll: initialContentOffset || 0,
488
500
  totalSize: 0,
489
501
  timeouts: /* @__PURE__ */ new Set(),
490
- viewabilityConfigCallbackPairs: void 0
502
+ viewabilityConfigCallbackPairs: void 0,
503
+ renderItem: void 0
491
504
  };
492
505
  refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
493
506
  }
494
507
  refState.current.data = data;
508
+ refState.current.renderItem = renderItem;
495
509
  set$(ctx, "numItems", data.length);
496
510
  set$(ctx, "stylePaddingTop", (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
497
511
  const addTotalSize = useCallback((add) => {
@@ -513,71 +527,93 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
513
527
  refState.current.animFrameTotalSize = requestAnimationFrame(doAdd);
514
528
  }
515
529
  }, []);
516
- const getRenderedItem = useCallback(
517
- (index, containerIndex) => {
518
- var _a2;
519
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
520
- if (!data2) {
521
- return null;
522
- }
523
- const useViewability = (configId, callback) => {
524
- useEffect(() => registerViewabilityCallback(containerIndex, configId, callback), []);
525
- };
526
- const useViewabilityAmount = (callback) => {
527
- useEffect(() => registerViewabilityAmountCallback(containerIndex, callback), []);
528
- };
529
- const useRecyclingEffect = (effect) => {
530
- useEffect(() => {
531
- let prevIndex = index;
532
- let prevItem = data2[index];
533
- const signal = `containerIndex${containerIndex}`;
534
- listen$(ctx, signal, () => {
535
- var _a3;
536
- const data3 = (_a3 = refState.current) == null ? void 0 : _a3.data;
537
- if (data3) {
538
- const newIndex = peek$(ctx, signal);
539
- const newItem = data3[newIndex];
540
- if (newItem) {
541
- effect({
542
- index: newIndex,
543
- item: newItem,
544
- prevIndex,
545
- prevItem
546
- });
547
- }
548
- prevIndex = newIndex;
549
- prevItem = newItem;
530
+ const getRenderedItem = useCallback((index, containerIndex) => {
531
+ var _a2, _b2, _c;
532
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
533
+ if (!data2) {
534
+ return null;
535
+ }
536
+ const useViewability = (configId, callback) => {
537
+ const key = containerIndex + configId;
538
+ useInit(() => {
539
+ const value = ctx.mapViewabilityValues.get(key);
540
+ if (value) {
541
+ callback(value);
542
+ }
543
+ });
544
+ ctx.mapViewabilityCallbacks.set(key, callback);
545
+ useEffect(
546
+ () => () => {
547
+ ctx.mapViewabilityCallbacks.delete(key);
548
+ },
549
+ []
550
+ );
551
+ };
552
+ const useViewabilityAmount = (callback) => {
553
+ useInit(() => {
554
+ const value = ctx.mapViewabilityAmountValues.get(containerIndex);
555
+ if (value) {
556
+ callback(value);
557
+ }
558
+ });
559
+ ctx.mapViewabilityAmountCallbacks.set(containerIndex, callback);
560
+ useEffect(
561
+ () => () => {
562
+ ctx.mapViewabilityAmountCallbacks.delete(containerIndex);
563
+ },
564
+ []
565
+ );
566
+ };
567
+ const useRecyclingEffect = (effect) => {
568
+ useEffect(() => {
569
+ const state = refState.current;
570
+ let prevIndex = index;
571
+ let prevItem = state.data[index];
572
+ const signal = `containerIndex${containerIndex}`;
573
+ listen$(ctx, signal, () => {
574
+ const data3 = state.data;
575
+ if (data3) {
576
+ const newIndex = peek$(ctx, signal);
577
+ const newItem = data3[newIndex];
578
+ if (newItem) {
579
+ effect({
580
+ index: newIndex,
581
+ item: newItem,
582
+ prevIndex,
583
+ prevItem
584
+ });
550
585
  }
551
- });
552
- }, []);
553
- };
554
- const useRecyclingState = (updateState) => {
555
- const stateInfo = useState(
556
- () => updateState({
557
- index,
558
- item: data2[index],
559
- prevIndex: void 0,
560
- prevItem: void 0
561
- })
562
- );
563
- useRecyclingEffect((state) => {
564
- const newState = updateState(state);
565
- stateInfo[1](newState);
586
+ prevIndex = newIndex;
587
+ prevItem = newItem;
588
+ }
566
589
  });
567
- return stateInfo;
568
- };
569
- const renderedItem = renderItem == null ? void 0 : renderItem({
570
- item: data2[index],
571
- index,
572
- useViewability,
573
- useViewabilityAmount,
574
- useRecyclingEffect,
575
- useRecyclingState
590
+ }, []);
591
+ };
592
+ const useRecyclingState = (updateState) => {
593
+ const stateInfo = useState(
594
+ () => updateState({
595
+ index,
596
+ item: refState.current.data[index],
597
+ prevIndex: void 0,
598
+ prevItem: void 0
599
+ })
600
+ );
601
+ useRecyclingEffect((state) => {
602
+ const newState = updateState(state);
603
+ stateInfo[1](newState);
576
604
  });
577
- return renderedItem;
578
- },
579
- [renderItem]
580
- );
605
+ return stateInfo;
606
+ };
607
+ const renderedItem = (_c = (_b2 = refState.current).renderItem) == null ? void 0 : _c.call(_b2, {
608
+ item: data2[index],
609
+ index,
610
+ useViewability,
611
+ useViewabilityAmount,
612
+ useRecyclingEffect,
613
+ useRecyclingState
614
+ });
615
+ return renderedItem;
616
+ }, []);
581
617
  const calculateItemsInView = useCallback(() => {
582
618
  unstable_batchedUpdates(() => {
583
619
  var _a2, _b2, _c;
@@ -714,32 +750,21 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
714
750
  }
715
751
  }
716
752
  }
717
- if (onViewableRangeChanged) {
718
- if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
719
- onViewableRangeChanged({
720
- start: startNoBuffer,
721
- startBuffered,
722
- end: endNoBuffer,
723
- endBuffered,
724
- items: data2.slice(startNoBuffer, endNoBuffer + 1)
725
- });
726
- }
727
- }
728
- if (refState.current.viewabilityConfigCallbackPairs) {
729
- updateViewableItems(
730
- refState.current,
731
- ctx,
732
- refState.current.viewabilityConfigCallbackPairs,
733
- getId,
734
- scrollLength,
735
- startNoBuffer,
736
- endNoBuffer
737
- );
738
- }
753
+ }
754
+ if (refState.current.viewabilityConfigCallbackPairs) {
755
+ updateViewableItems(
756
+ refState.current,
757
+ ctx,
758
+ refState.current.viewabilityConfigCallbackPairs,
759
+ getId,
760
+ scrollLength,
761
+ startNoBuffer,
762
+ endNoBuffer
763
+ );
739
764
  }
740
765
  });
741
- }, [data]);
742
- useMemo(() => {
766
+ }, []);
767
+ useInit(() => {
743
768
  var _a2, _b2;
744
769
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
745
770
  const scrollLength = refState.current.scrollLength;
@@ -758,7 +783,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
758
783
  totalSize += (_b2 = sizes.get(id)) != null ? _b2 : getItemSize(i, data[i]);
759
784
  }
760
785
  addTotalSize(totalSize);
761
- }, []);
786
+ });
762
787
  const checkAtBottom = () => {
763
788
  var _a2;
764
789
  const { scrollLength, scroll } = refState.current;
@@ -780,6 +805,15 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
780
805
  if (refState.current) {
781
806
  refState.current.isEndReached = false;
782
807
  }
808
+ const numContainers = peek$(ctx, "numContainers");
809
+ if (data.length < numContainers) {
810
+ for (let i = 0; i < numContainers; i++) {
811
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
812
+ if (itemIndex >= data.length) {
813
+ set$(ctx, `containerIndex${i}`, -1);
814
+ }
815
+ }
816
+ }
783
817
  calculateItemsInView();
784
818
  checkAtBottom();
785
819
  }, [data]);
@@ -842,31 +876,35 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
842
876
  },
843
877
  []
844
878
  );
845
- useImperativeHandle(forwardedRef, () => {
846
- const scrollToIndex = ({ index, animated }) => {
847
- const offsetObj = calculateInitialOffset(index);
848
- const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
849
- refScroller.current.scrollTo({ ...offset, animated });
850
- };
851
- return {
852
- getNativeScrollRef: () => refScroller.current,
853
- getScrollableNode: refScroller.current.getScrollableNode,
854
- getScrollResponder: refScroller.current.getScrollResponder,
855
- flashScrollIndicators: refScroller.current.flashScrollIndicators,
856
- scrollToIndex,
857
- scrollToOffset: ({ offset, animated }) => {
858
- const offsetObj = horizontal ? { x: offset, y: 0 } : { x: 0, y: offset };
859
- refScroller.current.scrollTo({ ...offsetObj, animated });
860
- },
861
- scrollToItem: ({ item, animated }) => {
862
- const index = data.indexOf(item);
863
- if (index !== -1) {
864
- scrollToIndex({ index, animated });
865
- }
866
- },
867
- scrollToEnd: refScroller.current.scrollToEnd
868
- };
869
- }, []);
879
+ useImperativeHandle(
880
+ forwardedRef,
881
+ () => {
882
+ const scrollToIndex = ({ index, animated }) => {
883
+ const offsetObj = calculateInitialOffset(index);
884
+ const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
885
+ refScroller.current.scrollTo({ ...offset, animated });
886
+ };
887
+ return {
888
+ getNativeScrollRef: () => refScroller.current,
889
+ getScrollableNode: refScroller.current.getScrollableNode,
890
+ getScrollResponder: refScroller.current.getScrollResponder,
891
+ flashScrollIndicators: refScroller.current.flashScrollIndicators,
892
+ scrollToIndex,
893
+ scrollToOffset: ({ offset, animated }) => {
894
+ const offsetObj = horizontal ? { x: offset, y: 0 } : { x: 0, y: offset };
895
+ refScroller.current.scrollTo({ ...offsetObj, animated });
896
+ },
897
+ scrollToItem: ({ item, animated }) => {
898
+ const index = data.indexOf(item);
899
+ if (index !== -1) {
900
+ scrollToIndex({ index, animated });
901
+ }
902
+ },
903
+ scrollToEnd: refScroller.current.scrollToEnd
904
+ };
905
+ },
906
+ []
907
+ );
870
908
  return /* @__PURE__ */ React6.createElement(
871
909
  ListComponent,
872
910
  {
@@ -882,7 +920,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
882
920
  onLayout,
883
921
  recycleItems,
884
922
  alignItemsAtEnd,
885
- addTotalSize
923
+ addTotalSize,
924
+ ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0
886
925
  }
887
926
  );
888
927
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "legend-list",
5
5
  "sideEffects": false,
6
6
  "private": false,