@legendapp/list 0.4.2 → 0.4.4

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
@@ -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;
@@ -63,6 +64,7 @@ interface LegendListRenderItemProps<ItemT> {
63
64
  item: ItemT;
64
65
  index: number;
65
66
  useViewability: (configId: string, callback: ViewabilityCallback) => void;
67
+ useViewabilityAmount: (callback: ViewabilityAmountCallback) => void;
66
68
  useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
67
69
  useRecyclingState: <T>(updateState: (info: LegendListRecyclingState<ItemT>) => T) => [T, React.Dispatch<T>];
68
70
  }
@@ -107,6 +109,14 @@ interface ViewToken<ItemT = any> {
107
109
  index: number;
108
110
  isViewable: boolean;
109
111
  }
112
+ interface ViewAmountToken<ItemT = any> extends ViewToken<ItemT> {
113
+ sizeVisible: number;
114
+ size: number;
115
+ percentVisible: number;
116
+ percentOfScroller: number;
117
+ position: number;
118
+ scrollSize: number;
119
+ }
110
120
  interface ViewabilityConfigCallbackPair {
111
121
  viewabilityConfig: ViewabilityConfig;
112
122
  onViewableItemsChanged?: OnViewableItemsChanged;
@@ -146,6 +156,7 @@ interface ViewabilityConfig {
146
156
  waitForInteraction?: boolean | undefined;
147
157
  }
148
158
  type ViewabilityCallback = (viewToken: ViewToken) => void;
159
+ type ViewabilityAmountCallback = (viewToken: ViewAmountToken) => void;
149
160
  interface LegendListRecyclingState<T> {
150
161
  item: T;
151
162
  prevItem: T | undefined;
@@ -157,4 +168,4 @@ declare const LegendList: <T>(props: LegendListProps<T> & {
157
168
  ref?: ForwardedRef<LegendListRef>;
158
169
  }) => ReactElement;
159
170
 
160
- export { type InternalState, LegendList, type LegendListProps, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ViewToken, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange };
171
+ export { type InternalState, LegendList, type LegendListProps, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange };
package/index.d.ts CHANGED
@@ -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;
@@ -63,6 +64,7 @@ interface LegendListRenderItemProps<ItemT> {
63
64
  item: ItemT;
64
65
  index: number;
65
66
  useViewability: (configId: string, callback: ViewabilityCallback) => void;
67
+ useViewabilityAmount: (callback: ViewabilityAmountCallback) => void;
66
68
  useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
67
69
  useRecyclingState: <T>(updateState: (info: LegendListRecyclingState<ItemT>) => T) => [T, React.Dispatch<T>];
68
70
  }
@@ -107,6 +109,14 @@ interface ViewToken<ItemT = any> {
107
109
  index: number;
108
110
  isViewable: boolean;
109
111
  }
112
+ interface ViewAmountToken<ItemT = any> extends ViewToken<ItemT> {
113
+ sizeVisible: number;
114
+ size: number;
115
+ percentVisible: number;
116
+ percentOfScroller: number;
117
+ position: number;
118
+ scrollSize: number;
119
+ }
110
120
  interface ViewabilityConfigCallbackPair {
111
121
  viewabilityConfig: ViewabilityConfig;
112
122
  onViewableItemsChanged?: OnViewableItemsChanged;
@@ -146,6 +156,7 @@ interface ViewabilityConfig {
146
156
  waitForInteraction?: boolean | undefined;
147
157
  }
148
158
  type ViewabilityCallback = (viewToken: ViewToken) => void;
159
+ type ViewabilityAmountCallback = (viewToken: ViewAmountToken) => void;
149
160
  interface LegendListRecyclingState<T> {
150
161
  item: T;
151
162
  prevItem: T | undefined;
@@ -157,4 +168,4 @@ declare const LegendList: <T>(props: LegendListProps<T> & {
157
168
  ref?: ForwardedRef<LegendListRef>;
158
169
  }) => ReactElement;
159
170
 
160
- export { type InternalState, LegendList, type LegendListProps, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ViewToken, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange };
171
+ export { type InternalState, LegendList, type LegendListProps, type LegendListRecyclingState, type LegendListRef, type LegendListRenderItemProps, type OnViewableItemsChanged, type ViewAmountToken, type ViewToken, type ViewabilityAmountCallback, type ViewabilityCallback, type ViewabilityConfig, type ViewabilityConfigCallbackPair, type ViewabilityConfigCallbackPairs, type ViewableRange };
package/index.js CHANGED
@@ -213,6 +213,8 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
213
213
  ListHeaderComponentStyle,
214
214
  ListFooterComponent,
215
215
  ListFooterComponentStyle,
216
+ ListEmptyComponent,
217
+ ListEmptyComponentStyle,
216
218
  getRenderedItem,
217
219
  updateItemSize,
218
220
  addTotalSize,
@@ -254,6 +256,13 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
254
256
  },
255
257
  getComponent(ListHeaderComponent)
256
258
  ),
259
+ ListEmptyComponent && /* @__PURE__ */ React6__namespace.createElement(
260
+ reactNative.View,
261
+ {
262
+ style: ListEmptyComponentStyle
263
+ },
264
+ getComponent(ListEmptyComponent)
265
+ ),
257
266
  /* @__PURE__ */ React6__namespace.createElement(
258
267
  Containers,
259
268
  {
@@ -271,6 +280,9 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
271
280
  // src/viewability.ts
272
281
  var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
273
282
  var mapViewabilityCallbacks = /* @__PURE__ */ new Map();
283
+ var mapViewabilityValues = /* @__PURE__ */ new Map();
284
+ var mapViewabilityAmountCallbacks = /* @__PURE__ */ new Map();
285
+ var mapViewabilityAmountValues = /* @__PURE__ */ new Map();
274
286
  function setupViewability(props) {
275
287
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
276
288
  viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
@@ -315,7 +327,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
315
327
  const changed = [];
316
328
  if (previousViewableItems) {
317
329
  for (const viewToken of previousViewableItems) {
318
- if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize)) {
330
+ if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize, viewToken.item, viewToken.index)) {
319
331
  viewToken.isViewable = false;
320
332
  changed.push(viewToken);
321
333
  }
@@ -326,7 +338,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
326
338
  const item = data[i];
327
339
  if (item) {
328
340
  const key = getId(i);
329
- if (isViewable(state, ctx, viewabilityConfig, key, scrollSize)) {
341
+ if (isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, i)) {
330
342
  const viewToken = {
331
343
  item,
332
344
  key,
@@ -342,7 +354,6 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
342
354
  }
343
355
  Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
344
356
  if (changed.length > 0) {
345
- console.log("changed", changed);
346
357
  viewabilityState.viewableItems = viewableItems;
347
358
  for (let i = 0; i < changed.length; i++) {
348
359
  const change = changed[i];
@@ -353,7 +364,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
353
364
  }
354
365
  }
355
366
  }
356
- function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
367
+ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index) {
357
368
  const { sizes, positions, scroll } = state;
358
369
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
359
370
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
@@ -363,25 +374,60 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
363
374
  const size = sizes.get(key) || 0;
364
375
  const bottom = top + size;
365
376
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
366
- if (isEntirelyVisible) {
367
- return true;
377
+ const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
378
+ const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
379
+ const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
380
+ const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
381
+ const isViewable2 = percent >= viewablePercentThreshold;
382
+ const containerId = findContainerId(state, ctx, index);
383
+ const value = {
384
+ index,
385
+ isViewable: isViewable2,
386
+ item,
387
+ key,
388
+ percentVisible,
389
+ percentOfScroller,
390
+ sizeVisible,
391
+ size,
392
+ position: top,
393
+ scrollSize
394
+ };
395
+ mapViewabilityAmountValues.set(containerId, value);
396
+ const cb = mapViewabilityAmountCallbacks.get(containerId);
397
+ if (cb) {
398
+ cb(value);
399
+ }
400
+ return isViewable2;
401
+ }
402
+ function findContainerId(state, ctx, index) {
403
+ const numContainers = peek$(ctx, "numContainers");
404
+ for (let i = 0; i < numContainers; i++) {
405
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
406
+ if (itemIndex === index) {
407
+ return i;
408
+ }
368
409
  }
369
- const visibleHeight = Math.min(bottom, scrollSize) - Math.max(top, 0);
370
- const percent = 100 * (visibleHeight / (viewAreaMode ? scrollSize : size));
371
- return percent >= viewablePercentThreshold;
410
+ return -1;
372
411
  }
373
412
  function maybeUpdateViewabilityCallback(configId, viewToken) {
374
413
  const key = viewToken.key + configId;
414
+ mapViewabilityValues.set(key, viewToken);
375
415
  const cb = mapViewabilityCallbacks.get(key);
376
416
  cb == null ? void 0 : cb(viewToken);
377
417
  }
378
- function registerViewabilityCallback(itemKey, configId, callback) {
379
- const key = itemKey + configId;
418
+ function registerViewabilityCallback(containerId, configId, callback) {
419
+ const key = containerId + configId;
380
420
  mapViewabilityCallbacks.set(key, callback);
381
421
  return () => {
382
422
  mapViewabilityCallbacks.delete(key);
383
423
  };
384
424
  }
425
+ function registerViewabilityAmountCallback(containerId, callback) {
426
+ mapViewabilityAmountCallbacks.set(containerId, callback);
427
+ return () => {
428
+ mapViewabilityAmountCallbacks.delete(containerId);
429
+ };
430
+ }
385
431
 
386
432
  // src/LegendList.tsx
387
433
  var DEFAULT_SCROLL_BUFFER = 0;
@@ -411,7 +457,7 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
411
457
  estimatedItemSize,
412
458
  getEstimatedItemSize,
413
459
  onEndReached,
414
- onViewableRangeChanged,
460
+ ListEmptyComponent,
415
461
  ...rest
416
462
  } = props;
417
463
  const ctx = useStateContext();
@@ -509,9 +555,23 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
509
555
  if (!data2) {
510
556
  return null;
511
557
  }
512
- const itemKey = getId(index);
513
558
  const useViewability = (configId, callback) => {
514
- React6.useEffect(() => registerViewabilityCallback(itemKey, configId, callback), []);
559
+ React6.useMemo(() => {
560
+ const value = mapViewabilityValues.get(containerIndex + configId);
561
+ if (value) {
562
+ callback(value);
563
+ }
564
+ }, []);
565
+ React6.useEffect(() => registerViewabilityCallback(containerIndex, configId, callback), []);
566
+ };
567
+ const useViewabilityAmount = (callback) => {
568
+ React6.useMemo(() => {
569
+ const value = mapViewabilityAmountValues.get(containerIndex);
570
+ if (value) {
571
+ callback(value);
572
+ }
573
+ }, []);
574
+ React6.useEffect(() => registerViewabilityAmountCallback(containerIndex, callback), []);
515
575
  };
516
576
  const useRecyclingEffect = (effect) => {
517
577
  React6.useEffect(() => {
@@ -549,7 +609,6 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
549
609
  );
550
610
  useRecyclingEffect((state) => {
551
611
  const newState = updateState(state);
552
- console.log("setting state", newState);
553
612
  stateInfo[1](newState);
554
613
  });
555
614
  return stateInfo;
@@ -558,6 +617,7 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
558
617
  item: data2[index],
559
618
  index,
560
619
  useViewability,
620
+ useViewabilityAmount,
561
621
  useRecyclingEffect,
562
622
  useRecyclingState
563
623
  });
@@ -701,31 +761,20 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
701
761
  }
702
762
  }
703
763
  }
704
- if (onViewableRangeChanged) {
705
- if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
706
- onViewableRangeChanged({
707
- start: startNoBuffer,
708
- startBuffered,
709
- end: endNoBuffer,
710
- endBuffered,
711
- items: data2.slice(startNoBuffer, endNoBuffer + 1)
712
- });
713
- }
714
- }
715
- if (refState.current.viewabilityConfigCallbackPairs) {
716
- updateViewableItems(
717
- refState.current,
718
- ctx,
719
- refState.current.viewabilityConfigCallbackPairs,
720
- getId,
721
- scrollLength,
722
- startNoBuffer,
723
- endNoBuffer
724
- );
725
- }
764
+ }
765
+ if (refState.current.viewabilityConfigCallbackPairs) {
766
+ updateViewableItems(
767
+ refState.current,
768
+ ctx,
769
+ refState.current.viewabilityConfigCallbackPairs,
770
+ getId,
771
+ scrollLength,
772
+ startNoBuffer,
773
+ endNoBuffer
774
+ );
726
775
  }
727
776
  });
728
- }, [data]);
777
+ }, []);
729
778
  React6.useMemo(() => {
730
779
  var _a2, _b2;
731
780
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
@@ -767,6 +816,15 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
767
816
  if (refState.current) {
768
817
  refState.current.isEndReached = false;
769
818
  }
819
+ const numContainers = peek$(ctx, "numContainers");
820
+ if (data.length < numContainers) {
821
+ for (let i = 0; i < numContainers; i++) {
822
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
823
+ if (itemIndex >= data.length) {
824
+ set$(ctx, `containerIndex${i}`, -1);
825
+ }
826
+ }
827
+ }
770
828
  calculateItemsInView();
771
829
  checkAtBottom();
772
830
  }, [data]);
@@ -869,7 +927,8 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
869
927
  onLayout,
870
928
  recycleItems,
871
929
  alignItemsAtEnd,
872
- addTotalSize
930
+ addTotalSize,
931
+ ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0
873
932
  }
874
933
  );
875
934
  });
package/index.mjs CHANGED
@@ -192,6 +192,8 @@ var ListComponent = React6.memo(function ListComponent2({
192
192
  ListHeaderComponentStyle,
193
193
  ListFooterComponent,
194
194
  ListFooterComponentStyle,
195
+ ListEmptyComponent,
196
+ ListEmptyComponentStyle,
195
197
  getRenderedItem,
196
198
  updateItemSize,
197
199
  addTotalSize,
@@ -233,6 +235,13 @@ var ListComponent = React6.memo(function ListComponent2({
233
235
  },
234
236
  getComponent(ListHeaderComponent)
235
237
  ),
238
+ ListEmptyComponent && /* @__PURE__ */ React6.createElement(
239
+ View,
240
+ {
241
+ style: ListEmptyComponentStyle
242
+ },
243
+ getComponent(ListEmptyComponent)
244
+ ),
236
245
  /* @__PURE__ */ React6.createElement(
237
246
  Containers,
238
247
  {
@@ -250,6 +259,9 @@ var ListComponent = React6.memo(function ListComponent2({
250
259
  // src/viewability.ts
251
260
  var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
252
261
  var mapViewabilityCallbacks = /* @__PURE__ */ new Map();
262
+ var mapViewabilityValues = /* @__PURE__ */ new Map();
263
+ var mapViewabilityAmountCallbacks = /* @__PURE__ */ new Map();
264
+ var mapViewabilityAmountValues = /* @__PURE__ */ new Map();
253
265
  function setupViewability(props) {
254
266
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
255
267
  viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
@@ -294,7 +306,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
294
306
  const changed = [];
295
307
  if (previousViewableItems) {
296
308
  for (const viewToken of previousViewableItems) {
297
- if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize)) {
309
+ if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize, viewToken.item, viewToken.index)) {
298
310
  viewToken.isViewable = false;
299
311
  changed.push(viewToken);
300
312
  }
@@ -305,7 +317,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
305
317
  const item = data[i];
306
318
  if (item) {
307
319
  const key = getId(i);
308
- if (isViewable(state, ctx, viewabilityConfig, key, scrollSize)) {
320
+ if (isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, i)) {
309
321
  const viewToken = {
310
322
  item,
311
323
  key,
@@ -321,7 +333,6 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
321
333
  }
322
334
  Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
323
335
  if (changed.length > 0) {
324
- console.log("changed", changed);
325
336
  viewabilityState.viewableItems = viewableItems;
326
337
  for (let i = 0; i < changed.length; i++) {
327
338
  const change = changed[i];
@@ -332,7 +343,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
332
343
  }
333
344
  }
334
345
  }
335
- function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
346
+ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index) {
336
347
  const { sizes, positions, scroll } = state;
337
348
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
338
349
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
@@ -342,25 +353,60 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
342
353
  const size = sizes.get(key) || 0;
343
354
  const bottom = top + size;
344
355
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
345
- if (isEntirelyVisible) {
346
- return true;
356
+ const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
357
+ const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
358
+ const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
359
+ const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
360
+ const isViewable2 = percent >= viewablePercentThreshold;
361
+ const containerId = findContainerId(state, ctx, index);
362
+ const value = {
363
+ index,
364
+ isViewable: isViewable2,
365
+ item,
366
+ key,
367
+ percentVisible,
368
+ percentOfScroller,
369
+ sizeVisible,
370
+ size,
371
+ position: top,
372
+ scrollSize
373
+ };
374
+ mapViewabilityAmountValues.set(containerId, value);
375
+ const cb = mapViewabilityAmountCallbacks.get(containerId);
376
+ if (cb) {
377
+ cb(value);
378
+ }
379
+ return isViewable2;
380
+ }
381
+ function findContainerId(state, ctx, index) {
382
+ const numContainers = peek$(ctx, "numContainers");
383
+ for (let i = 0; i < numContainers; i++) {
384
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
385
+ if (itemIndex === index) {
386
+ return i;
387
+ }
347
388
  }
348
- const visibleHeight = Math.min(bottom, scrollSize) - Math.max(top, 0);
349
- const percent = 100 * (visibleHeight / (viewAreaMode ? scrollSize : size));
350
- return percent >= viewablePercentThreshold;
389
+ return -1;
351
390
  }
352
391
  function maybeUpdateViewabilityCallback(configId, viewToken) {
353
392
  const key = viewToken.key + configId;
393
+ mapViewabilityValues.set(key, viewToken);
354
394
  const cb = mapViewabilityCallbacks.get(key);
355
395
  cb == null ? void 0 : cb(viewToken);
356
396
  }
357
- function registerViewabilityCallback(itemKey, configId, callback) {
358
- const key = itemKey + configId;
397
+ function registerViewabilityCallback(containerId, configId, callback) {
398
+ const key = containerId + configId;
359
399
  mapViewabilityCallbacks.set(key, callback);
360
400
  return () => {
361
401
  mapViewabilityCallbacks.delete(key);
362
402
  };
363
403
  }
404
+ function registerViewabilityAmountCallback(containerId, callback) {
405
+ mapViewabilityAmountCallbacks.set(containerId, callback);
406
+ return () => {
407
+ mapViewabilityAmountCallbacks.delete(containerId);
408
+ };
409
+ }
364
410
 
365
411
  // src/LegendList.tsx
366
412
  var DEFAULT_SCROLL_BUFFER = 0;
@@ -390,7 +436,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
390
436
  estimatedItemSize,
391
437
  getEstimatedItemSize,
392
438
  onEndReached,
393
- onViewableRangeChanged,
439
+ ListEmptyComponent,
394
440
  ...rest
395
441
  } = props;
396
442
  const ctx = useStateContext();
@@ -488,9 +534,23 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
488
534
  if (!data2) {
489
535
  return null;
490
536
  }
491
- const itemKey = getId(index);
492
537
  const useViewability = (configId, callback) => {
493
- useEffect(() => registerViewabilityCallback(itemKey, configId, callback), []);
538
+ useMemo(() => {
539
+ const value = mapViewabilityValues.get(containerIndex + configId);
540
+ if (value) {
541
+ callback(value);
542
+ }
543
+ }, []);
544
+ useEffect(() => registerViewabilityCallback(containerIndex, configId, callback), []);
545
+ };
546
+ const useViewabilityAmount = (callback) => {
547
+ useMemo(() => {
548
+ const value = mapViewabilityAmountValues.get(containerIndex);
549
+ if (value) {
550
+ callback(value);
551
+ }
552
+ }, []);
553
+ useEffect(() => registerViewabilityAmountCallback(containerIndex, callback), []);
494
554
  };
495
555
  const useRecyclingEffect = (effect) => {
496
556
  useEffect(() => {
@@ -528,7 +588,6 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
528
588
  );
529
589
  useRecyclingEffect((state) => {
530
590
  const newState = updateState(state);
531
- console.log("setting state", newState);
532
591
  stateInfo[1](newState);
533
592
  });
534
593
  return stateInfo;
@@ -537,6 +596,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
537
596
  item: data2[index],
538
597
  index,
539
598
  useViewability,
599
+ useViewabilityAmount,
540
600
  useRecyclingEffect,
541
601
  useRecyclingState
542
602
  });
@@ -680,31 +740,20 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
680
740
  }
681
741
  }
682
742
  }
683
- if (onViewableRangeChanged) {
684
- if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
685
- onViewableRangeChanged({
686
- start: startNoBuffer,
687
- startBuffered,
688
- end: endNoBuffer,
689
- endBuffered,
690
- items: data2.slice(startNoBuffer, endNoBuffer + 1)
691
- });
692
- }
693
- }
694
- if (refState.current.viewabilityConfigCallbackPairs) {
695
- updateViewableItems(
696
- refState.current,
697
- ctx,
698
- refState.current.viewabilityConfigCallbackPairs,
699
- getId,
700
- scrollLength,
701
- startNoBuffer,
702
- endNoBuffer
703
- );
704
- }
743
+ }
744
+ if (refState.current.viewabilityConfigCallbackPairs) {
745
+ updateViewableItems(
746
+ refState.current,
747
+ ctx,
748
+ refState.current.viewabilityConfigCallbackPairs,
749
+ getId,
750
+ scrollLength,
751
+ startNoBuffer,
752
+ endNoBuffer
753
+ );
705
754
  }
706
755
  });
707
- }, [data]);
756
+ }, []);
708
757
  useMemo(() => {
709
758
  var _a2, _b2;
710
759
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
@@ -746,6 +795,15 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
746
795
  if (refState.current) {
747
796
  refState.current.isEndReached = false;
748
797
  }
798
+ const numContainers = peek$(ctx, "numContainers");
799
+ if (data.length < numContainers) {
800
+ for (let i = 0; i < numContainers; i++) {
801
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
802
+ if (itemIndex >= data.length) {
803
+ set$(ctx, `containerIndex${i}`, -1);
804
+ }
805
+ }
806
+ }
749
807
  calculateItemsInView();
750
808
  checkAtBottom();
751
809
  }, [data]);
@@ -848,7 +906,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
848
906
  onLayout,
849
907
  recycleItems,
850
908
  alignItemsAtEnd,
851
- addTotalSize
909
+ addTotalSize,
910
+ ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0
852
911
  }
853
912
  );
854
913
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "legend-list",
5
5
  "sideEffects": false,
6
6
  "private": false,