@legendapp/list 1.0.0-beta.34 → 1.0.0-beta.35

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/index.d.mts CHANGED
@@ -286,10 +286,6 @@ interface LegendListRenderItemProps<ItemT> {
286
286
  item: ItemT;
287
287
  index: number;
288
288
  extraData: any;
289
- useViewability: (configId: string, callback: ViewabilityCallback) => void;
290
- useViewabilityAmount: (callback: ViewabilityAmountCallback) => void;
291
- useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
292
- useRecyclingState: <T>(updateState: ((info: LegendListRecyclingState<ItemT>) => T) | T) => [T, React.Dispatch<T>];
293
289
  }
294
290
  type ScrollState = {
295
291
  contentLength: number;
@@ -395,13 +391,13 @@ interface ViewToken<ItemT = any> {
395
391
  key: string;
396
392
  index: number;
397
393
  isViewable: boolean;
394
+ containerId: number;
398
395
  }
399
396
  interface ViewAmountToken<ItemT = any> extends ViewToken<ItemT> {
400
397
  sizeVisible: number;
401
398
  size: number;
402
399
  percentVisible: number;
403
400
  percentOfScroller: number;
404
- position: number;
405
401
  scrollSize: number;
406
402
  }
407
403
  interface ViewabilityConfigCallbackPair {
package/index.d.ts CHANGED
@@ -286,10 +286,6 @@ interface LegendListRenderItemProps<ItemT> {
286
286
  item: ItemT;
287
287
  index: number;
288
288
  extraData: any;
289
- useViewability: (configId: string, callback: ViewabilityCallback) => void;
290
- useViewabilityAmount: (callback: ViewabilityAmountCallback) => void;
291
- useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
292
- useRecyclingState: <T>(updateState: ((info: LegendListRecyclingState<ItemT>) => T) | T) => [T, React.Dispatch<T>];
293
289
  }
294
290
  type ScrollState = {
295
291
  contentLength: number;
@@ -395,13 +391,13 @@ interface ViewToken<ItemT = any> {
395
391
  key: string;
396
392
  index: number;
397
393
  isViewable: boolean;
394
+ containerId: number;
398
395
  }
399
396
  interface ViewAmountToken<ItemT = any> extends ViewToken<ItemT> {
400
397
  sizeVisible: number;
401
398
  size: number;
402
399
  percentVisible: number;
403
400
  percentOfScroller: number;
404
- position: number;
405
401
  scrollSize: number;
406
402
  }
407
403
  interface ViewabilityConfigCallbackPair {
package/index.js CHANGED
@@ -86,6 +86,58 @@ function getContentSize(ctx) {
86
86
  const totalSize = values.get("totalSize") || 0;
87
87
  return headerSize + footerSize + totalSize + stylePaddingTop;
88
88
  }
89
+
90
+ // src/DebugView.tsx
91
+ var DebugRow = ({ children }) => {
92
+ return /* @__PURE__ */ React.createElement(reactNative.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" } }, children);
93
+ };
94
+ var DebugView = React6.memo(function DebugView2({ state }) {
95
+ const ctx = useStateContext();
96
+ const totalSize = use$("totalSize") || 0;
97
+ const totalSizeWithScrollAdjust = use$("totalSizeWithScrollAdjust") || 0;
98
+ const scrollAdjust = use$("scrollAdjust") || 0;
99
+ const rawScroll = use$("debugRawScroll") || 0;
100
+ const scroll = use$("debugComputedScroll") || 0;
101
+ const contentSize = getContentSize(ctx);
102
+ const [, forceUpdate] = React6.useReducer((x) => x + 1, 0);
103
+ use$("numContainers");
104
+ use$("numContainersPooled");
105
+ useInterval(() => {
106
+ forceUpdate();
107
+ }, 100);
108
+ return /* @__PURE__ */ React.createElement(
109
+ reactNative.View,
110
+ {
111
+ style: {
112
+ position: "absolute",
113
+ top: 0,
114
+ right: 0,
115
+ paddingLeft: 4,
116
+ paddingBottom: 4,
117
+ // height: 100,
118
+ backgroundColor: "#FFFFFFCC",
119
+ padding: 4,
120
+ borderRadius: 4
121
+ },
122
+ pointerEvents: "none"
123
+ },
124
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "TotalSize:"), /* @__PURE__ */ React.createElement(reactNative.Text, null, totalSize.toFixed(2))),
125
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "ContentSize:"), /* @__PURE__ */ React.createElement(reactNative.Text, null, contentSize.toFixed(2))),
126
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "At end:"), /* @__PURE__ */ React.createElement(reactNative.Text, null, String(state.isAtBottom))),
127
+ /* @__PURE__ */ React.createElement(reactNative.Text, null),
128
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "ScrollAdjust:"), /* @__PURE__ */ React.createElement(reactNative.Text, null, scrollAdjust.toFixed(2))),
129
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "TotalSizeReal: "), /* @__PURE__ */ React.createElement(reactNative.Text, null, totalSizeWithScrollAdjust.toFixed(2))),
130
+ /* @__PURE__ */ React.createElement(reactNative.Text, null),
131
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "RawScroll: "), /* @__PURE__ */ React.createElement(reactNative.Text, null, rawScroll.toFixed(2))),
132
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "ComputedScroll: "), /* @__PURE__ */ React.createElement(reactNative.Text, null, scroll.toFixed(2)))
133
+ );
134
+ });
135
+ function useInterval(callback, delay) {
136
+ React6.useEffect(() => {
137
+ const interval = setInterval(callback, delay);
138
+ return () => clearInterval(interval);
139
+ }, [delay]);
140
+ }
89
141
  var symbolFirst = Symbol();
90
142
  function useInit(cb) {
91
143
  const refValue = React6.useRef(symbolFirst);
@@ -171,56 +223,6 @@ function useRecyclingState(valueOrFun) {
171
223
  });
172
224
  return stateInfo;
173
225
  }
174
- var DebugRow = ({ children }) => {
175
- return /* @__PURE__ */ React.createElement(reactNative.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" } }, children);
176
- };
177
- var DebugView = React6.memo(function DebugView2({ state }) {
178
- const ctx = useStateContext();
179
- const totalSize = use$("totalSize") || 0;
180
- const totalSizeWithScrollAdjust = use$("totalSizeWithScrollAdjust") || 0;
181
- const scrollAdjust = use$("scrollAdjust") || 0;
182
- const rawScroll = use$("debugRawScroll") || 0;
183
- const scroll = use$("debugComputedScroll") || 0;
184
- const contentSize = getContentSize(ctx);
185
- const [, forceUpdate] = React6.useReducer((x) => x + 1, 0);
186
- use$("numContainers");
187
- use$("numContainersPooled");
188
- useInterval(() => {
189
- forceUpdate();
190
- }, 100);
191
- return /* @__PURE__ */ React.createElement(
192
- reactNative.View,
193
- {
194
- style: {
195
- position: "absolute",
196
- top: 0,
197
- right: 0,
198
- paddingLeft: 4,
199
- paddingBottom: 4,
200
- // height: 100,
201
- backgroundColor: "#FFFFFFCC",
202
- padding: 4,
203
- borderRadius: 4
204
- },
205
- pointerEvents: "none"
206
- },
207
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "TotalSize:"), /* @__PURE__ */ React.createElement(reactNative.Text, null, totalSize.toFixed(2))),
208
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "ContentSize:"), /* @__PURE__ */ React.createElement(reactNative.Text, null, contentSize.toFixed(2))),
209
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "At end:"), /* @__PURE__ */ React.createElement(reactNative.Text, null, String(state.isAtBottom))),
210
- /* @__PURE__ */ React.createElement(reactNative.Text, null),
211
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "ScrollAdjust:"), /* @__PURE__ */ React.createElement(reactNative.Text, null, scrollAdjust.toFixed(2))),
212
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "TotalSizeReal: "), /* @__PURE__ */ React.createElement(reactNative.Text, null, totalSizeWithScrollAdjust.toFixed(2))),
213
- /* @__PURE__ */ React.createElement(reactNative.Text, null),
214
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "RawScroll: "), /* @__PURE__ */ React.createElement(reactNative.Text, null, rawScroll.toFixed(2))),
215
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(reactNative.Text, null, "ComputedScroll: "), /* @__PURE__ */ React.createElement(reactNative.Text, null, scroll.toFixed(2)))
216
- );
217
- });
218
- function useInterval(callback, delay) {
219
- React6.useEffect(() => {
220
- const interval = setInterval(callback, delay);
221
- return () => clearInterval(interval);
222
- }, [delay]);
223
- }
224
226
  var LeanViewComponent = React6__namespace.forwardRef((props, ref) => {
225
227
  return React6__namespace.createElement("RCTView", { ...props, ref });
226
228
  });
@@ -678,11 +680,37 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
678
680
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
679
681
  const configId = viewabilityConfig.id;
680
682
  const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
681
- const { viewableItems: previousViewableItems, start, previousStart, end, previousEnd } = viewabilityState;
683
+ const { viewableItems: previousViewableItems, start, end } = viewabilityState;
684
+ const viewabilityTokens = /* @__PURE__ */ new Map();
685
+ for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
686
+ viewabilityTokens.set(
687
+ containerId,
688
+ computeViewability(
689
+ state,
690
+ ctx,
691
+ viewabilityConfig,
692
+ containerId,
693
+ value.key,
694
+ scrollSize,
695
+ value.item,
696
+ value.index
697
+ )
698
+ );
699
+ }
682
700
  const changed = [];
683
701
  if (previousViewableItems) {
684
702
  for (const viewToken of previousViewableItems) {
685
- if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize, viewToken.item, viewToken.index)) {
703
+ const containerId = findContainerId(ctx, viewToken.key);
704
+ if (!isViewable(
705
+ state,
706
+ ctx,
707
+ viewabilityConfig,
708
+ containerId,
709
+ viewToken.key,
710
+ scrollSize,
711
+ viewToken.item,
712
+ viewToken.index
713
+ )) {
686
714
  viewToken.isViewable = false;
687
715
  changed.push(viewToken);
688
716
  }
@@ -693,12 +721,14 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
693
721
  const item = data[i];
694
722
  if (item) {
695
723
  const key = getId(i);
696
- if (isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, i)) {
724
+ const containerId = findContainerId(ctx, key);
725
+ if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
697
726
  const viewToken = {
698
727
  item,
699
728
  key,
700
729
  index: i,
701
- isViewable: true
730
+ isViewable: true,
731
+ containerId
702
732
  };
703
733
  viewableItems.push(viewToken);
704
734
  if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
@@ -716,14 +746,19 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
716
746
  viewabilityState.viewableItems = viewableItems;
717
747
  for (let i = 0; i < changed.length; i++) {
718
748
  const change = changed[i];
719
- maybeUpdateViewabilityCallback(ctx, configId, change);
749
+ maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
720
750
  }
721
751
  if (onViewableItemsChanged) {
722
752
  onViewableItemsChanged({ viewableItems, changed });
723
753
  }
724
754
  }
755
+ for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
756
+ if (value.sizeVisible < 0) {
757
+ ctx.mapViewabilityAmountValues.delete(containerId);
758
+ }
759
+ }
725
760
  }
726
- function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index) {
761
+ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
727
762
  const { sizes, positions, scroll: scrollState, scrollAdjustHandler } = state;
728
763
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
729
764
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
@@ -731,7 +766,7 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
731
766
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
732
767
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
733
768
  const scroll = scrollState - previousScrollAdjust - topPad;
734
- const top = positions.get(key) - scroll + topPad;
769
+ const top = positions.get(key) - scroll;
735
770
  const size = sizes.get(key) || 0;
736
771
  const bottom = top + size;
737
772
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
@@ -740,7 +775,6 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
740
775
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
741
776
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
742
777
  const isViewable2 = percent >= viewablePercentThreshold;
743
- const containerId = findContainerId(ctx, key);
744
778
  const value = {
745
779
  index,
746
780
  isViewable: isViewable2,
@@ -750,15 +784,21 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
750
784
  percentOfScroller,
751
785
  sizeVisible,
752
786
  size,
753
- position: top,
754
- scrollSize
787
+ scrollSize,
788
+ containerId
755
789
  };
756
- ctx.mapViewabilityAmountValues.set(containerId, value);
757
- const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
758
- if (cb) {
759
- cb(value);
790
+ if (JSON.stringify(value) !== JSON.stringify(ctx.mapViewabilityAmountValues.get(containerId))) {
791
+ ctx.mapViewabilityAmountValues.set(containerId, value);
792
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
793
+ if (cb) {
794
+ cb(value);
795
+ }
760
796
  }
761
- return isViewable2;
797
+ return value;
798
+ }
799
+ function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
800
+ const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
801
+ return value.isViewable;
762
802
  }
763
803
  function findContainerId(ctx, key) {
764
804
  const numContainers = peek$(ctx, "numContainers");
@@ -770,8 +810,8 @@ function findContainerId(ctx, key) {
770
810
  }
771
811
  return -1;
772
812
  }
773
- function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
774
- const key = viewToken.key + configId;
813
+ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
814
+ const key = containerId + configId;
775
815
  ctx.mapViewabilityValues.set(key, viewToken);
776
816
  const cb = ctx.mapViewabilityCallbacks.get(key);
777
817
  cb == null ? void 0 : cb(viewToken);
@@ -799,7 +839,7 @@ var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
799
839
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
800
840
  var _a, _b, _c, _d;
801
841
  const {
802
- data: dataProp,
842
+ data: dataProp = [],
803
843
  initialScrollIndex,
804
844
  initialScrollOffset,
805
845
  horizontal,
@@ -1633,22 +1673,31 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1633
1673
  if (index === void 0) {
1634
1674
  return null;
1635
1675
  }
1636
- const useViewability2 = (configId, callback) => {
1637
- useViewability(configId, callback);
1638
- };
1639
- const useViewabilityAmount2 = (callback) => {
1640
- useViewabilityAmount(callback);
1641
- };
1642
- const useRecyclingEffect2 = (effect) => {
1643
- useRecyclingEffect(effect);
1644
- };
1645
- const useRecyclingState2 = (valueOrFun) => {
1646
- return useRecyclingState(valueOrFun);
1647
- };
1676
+ const useViewability2 = __DEV__ ? (configId, callback) => {
1677
+ console.warn(
1678
+ `[legend-list] useViewability has been moved from a render prop to a regular import: import { useViewability } from "@legendapp/list";`
1679
+ );
1680
+ } : void 0;
1681
+ const useViewabilityAmount2 = __DEV__ ? (callback) => {
1682
+ console.warn(
1683
+ `[legend-list] useViewabilityAmount has been moved from a render prop to a regular import: import { useViewabilityAmount } from "@legendapp/list";`
1684
+ );
1685
+ } : void 0;
1686
+ const useRecyclingEffect2 = __DEV__ ? (effect) => {
1687
+ console.warn(
1688
+ `[legend-list] useRecyclingEffect has been moved from a render prop to a regular import: import { useRecyclingEffect } from "@legendapp/list";`
1689
+ );
1690
+ } : void 0;
1691
+ const useRecyclingState2 = __DEV__ ? (valueOrFun) => {
1692
+ console.warn(
1693
+ `[legend-list] useRecyclingState has been moved from a render prop to a regular import: import { useRecyclingState } from "@legendapp/list";`
1694
+ );
1695
+ } : void 0;
1648
1696
  const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
1649
1697
  item: data[index],
1650
1698
  index,
1651
1699
  extraData: peek$(ctx, "extraData"),
1700
+ // @ts-expect-error TODO: Remove these before 1.0
1652
1701
  useViewability: useViewability2,
1653
1702
  useViewabilityAmount: useViewabilityAmount2,
1654
1703
  useRecyclingEffect: useRecyclingEffect2,
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React6 from 'react';
2
- import React6__default, { createContext, memo, useReducer, useEffect, useMemo, useRef, useCallback, useImperativeHandle, useSyncExternalStore, useContext, useState, forwardRef, useLayoutEffect } from 'react';
2
+ import React6__default, { memo, useReducer, useEffect, createContext, useMemo, useRef, useCallback, useImperativeHandle, useSyncExternalStore, useContext, useState, forwardRef, useLayoutEffect } from 'react';
3
3
  import { View, Text, Platform, Animated, ScrollView, StyleSheet, Dimensions, RefreshControl } from 'react-native';
4
4
 
5
5
  // src/LegendList.tsx
@@ -65,6 +65,58 @@ function getContentSize(ctx) {
65
65
  const totalSize = values.get("totalSize") || 0;
66
66
  return headerSize + footerSize + totalSize + stylePaddingTop;
67
67
  }
68
+
69
+ // src/DebugView.tsx
70
+ var DebugRow = ({ children }) => {
71
+ return /* @__PURE__ */ React.createElement(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" } }, children);
72
+ };
73
+ var DebugView = memo(function DebugView2({ state }) {
74
+ const ctx = useStateContext();
75
+ const totalSize = use$("totalSize") || 0;
76
+ const totalSizeWithScrollAdjust = use$("totalSizeWithScrollAdjust") || 0;
77
+ const scrollAdjust = use$("scrollAdjust") || 0;
78
+ const rawScroll = use$("debugRawScroll") || 0;
79
+ const scroll = use$("debugComputedScroll") || 0;
80
+ const contentSize = getContentSize(ctx);
81
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
82
+ use$("numContainers");
83
+ use$("numContainersPooled");
84
+ useInterval(() => {
85
+ forceUpdate();
86
+ }, 100);
87
+ return /* @__PURE__ */ React.createElement(
88
+ View,
89
+ {
90
+ style: {
91
+ position: "absolute",
92
+ top: 0,
93
+ right: 0,
94
+ paddingLeft: 4,
95
+ paddingBottom: 4,
96
+ // height: 100,
97
+ backgroundColor: "#FFFFFFCC",
98
+ padding: 4,
99
+ borderRadius: 4
100
+ },
101
+ pointerEvents: "none"
102
+ },
103
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React.createElement(Text, null, totalSize.toFixed(2))),
104
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React.createElement(Text, null, contentSize.toFixed(2))),
105
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "At end:"), /* @__PURE__ */ React.createElement(Text, null, String(state.isAtBottom))),
106
+ /* @__PURE__ */ React.createElement(Text, null),
107
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React.createElement(Text, null, scrollAdjust.toFixed(2))),
108
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "TotalSizeReal: "), /* @__PURE__ */ React.createElement(Text, null, totalSizeWithScrollAdjust.toFixed(2))),
109
+ /* @__PURE__ */ React.createElement(Text, null),
110
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React.createElement(Text, null, rawScroll.toFixed(2))),
111
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React.createElement(Text, null, scroll.toFixed(2)))
112
+ );
113
+ });
114
+ function useInterval(callback, delay) {
115
+ useEffect(() => {
116
+ const interval = setInterval(callback, delay);
117
+ return () => clearInterval(interval);
118
+ }, [delay]);
119
+ }
68
120
  var symbolFirst = Symbol();
69
121
  function useInit(cb) {
70
122
  const refValue = useRef(symbolFirst);
@@ -150,56 +202,6 @@ function useRecyclingState(valueOrFun) {
150
202
  });
151
203
  return stateInfo;
152
204
  }
153
- var DebugRow = ({ children }) => {
154
- return /* @__PURE__ */ React.createElement(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" } }, children);
155
- };
156
- var DebugView = memo(function DebugView2({ state }) {
157
- const ctx = useStateContext();
158
- const totalSize = use$("totalSize") || 0;
159
- const totalSizeWithScrollAdjust = use$("totalSizeWithScrollAdjust") || 0;
160
- const scrollAdjust = use$("scrollAdjust") || 0;
161
- const rawScroll = use$("debugRawScroll") || 0;
162
- const scroll = use$("debugComputedScroll") || 0;
163
- const contentSize = getContentSize(ctx);
164
- const [, forceUpdate] = useReducer((x) => x + 1, 0);
165
- use$("numContainers");
166
- use$("numContainersPooled");
167
- useInterval(() => {
168
- forceUpdate();
169
- }, 100);
170
- return /* @__PURE__ */ React.createElement(
171
- View,
172
- {
173
- style: {
174
- position: "absolute",
175
- top: 0,
176
- right: 0,
177
- paddingLeft: 4,
178
- paddingBottom: 4,
179
- // height: 100,
180
- backgroundColor: "#FFFFFFCC",
181
- padding: 4,
182
- borderRadius: 4
183
- },
184
- pointerEvents: "none"
185
- },
186
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React.createElement(Text, null, totalSize.toFixed(2))),
187
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React.createElement(Text, null, contentSize.toFixed(2))),
188
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "At end:"), /* @__PURE__ */ React.createElement(Text, null, String(state.isAtBottom))),
189
- /* @__PURE__ */ React.createElement(Text, null),
190
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React.createElement(Text, null, scrollAdjust.toFixed(2))),
191
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "TotalSizeReal: "), /* @__PURE__ */ React.createElement(Text, null, totalSizeWithScrollAdjust.toFixed(2))),
192
- /* @__PURE__ */ React.createElement(Text, null),
193
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React.createElement(Text, null, rawScroll.toFixed(2))),
194
- /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React.createElement(Text, null, scroll.toFixed(2)))
195
- );
196
- });
197
- function useInterval(callback, delay) {
198
- useEffect(() => {
199
- const interval = setInterval(callback, delay);
200
- return () => clearInterval(interval);
201
- }, [delay]);
202
- }
203
205
  var LeanViewComponent = React6.forwardRef((props, ref) => {
204
206
  return React6.createElement("RCTView", { ...props, ref });
205
207
  });
@@ -657,11 +659,37 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
657
659
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
658
660
  const configId = viewabilityConfig.id;
659
661
  const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
660
- const { viewableItems: previousViewableItems, start, previousStart, end, previousEnd } = viewabilityState;
662
+ const { viewableItems: previousViewableItems, start, end } = viewabilityState;
663
+ const viewabilityTokens = /* @__PURE__ */ new Map();
664
+ for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
665
+ viewabilityTokens.set(
666
+ containerId,
667
+ computeViewability(
668
+ state,
669
+ ctx,
670
+ viewabilityConfig,
671
+ containerId,
672
+ value.key,
673
+ scrollSize,
674
+ value.item,
675
+ value.index
676
+ )
677
+ );
678
+ }
661
679
  const changed = [];
662
680
  if (previousViewableItems) {
663
681
  for (const viewToken of previousViewableItems) {
664
- if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize, viewToken.item, viewToken.index)) {
682
+ const containerId = findContainerId(ctx, viewToken.key);
683
+ if (!isViewable(
684
+ state,
685
+ ctx,
686
+ viewabilityConfig,
687
+ containerId,
688
+ viewToken.key,
689
+ scrollSize,
690
+ viewToken.item,
691
+ viewToken.index
692
+ )) {
665
693
  viewToken.isViewable = false;
666
694
  changed.push(viewToken);
667
695
  }
@@ -672,12 +700,14 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
672
700
  const item = data[i];
673
701
  if (item) {
674
702
  const key = getId(i);
675
- if (isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, i)) {
703
+ const containerId = findContainerId(ctx, key);
704
+ if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
676
705
  const viewToken = {
677
706
  item,
678
707
  key,
679
708
  index: i,
680
- isViewable: true
709
+ isViewable: true,
710
+ containerId
681
711
  };
682
712
  viewableItems.push(viewToken);
683
713
  if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
@@ -695,14 +725,19 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
695
725
  viewabilityState.viewableItems = viewableItems;
696
726
  for (let i = 0; i < changed.length; i++) {
697
727
  const change = changed[i];
698
- maybeUpdateViewabilityCallback(ctx, configId, change);
728
+ maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
699
729
  }
700
730
  if (onViewableItemsChanged) {
701
731
  onViewableItemsChanged({ viewableItems, changed });
702
732
  }
703
733
  }
734
+ for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
735
+ if (value.sizeVisible < 0) {
736
+ ctx.mapViewabilityAmountValues.delete(containerId);
737
+ }
738
+ }
704
739
  }
705
- function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index) {
740
+ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
706
741
  const { sizes, positions, scroll: scrollState, scrollAdjustHandler } = state;
707
742
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
708
743
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
@@ -710,7 +745,7 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
710
745
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
711
746
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
712
747
  const scroll = scrollState - previousScrollAdjust - topPad;
713
- const top = positions.get(key) - scroll + topPad;
748
+ const top = positions.get(key) - scroll;
714
749
  const size = sizes.get(key) || 0;
715
750
  const bottom = top + size;
716
751
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
@@ -719,7 +754,6 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
719
754
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
720
755
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
721
756
  const isViewable2 = percent >= viewablePercentThreshold;
722
- const containerId = findContainerId(ctx, key);
723
757
  const value = {
724
758
  index,
725
759
  isViewable: isViewable2,
@@ -729,15 +763,21 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
729
763
  percentOfScroller,
730
764
  sizeVisible,
731
765
  size,
732
- position: top,
733
- scrollSize
766
+ scrollSize,
767
+ containerId
734
768
  };
735
- ctx.mapViewabilityAmountValues.set(containerId, value);
736
- const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
737
- if (cb) {
738
- cb(value);
769
+ if (JSON.stringify(value) !== JSON.stringify(ctx.mapViewabilityAmountValues.get(containerId))) {
770
+ ctx.mapViewabilityAmountValues.set(containerId, value);
771
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
772
+ if (cb) {
773
+ cb(value);
774
+ }
739
775
  }
740
- return isViewable2;
776
+ return value;
777
+ }
778
+ function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
779
+ const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
780
+ return value.isViewable;
741
781
  }
742
782
  function findContainerId(ctx, key) {
743
783
  const numContainers = peek$(ctx, "numContainers");
@@ -749,8 +789,8 @@ function findContainerId(ctx, key) {
749
789
  }
750
790
  return -1;
751
791
  }
752
- function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
753
- const key = viewToken.key + configId;
792
+ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
793
+ const key = containerId + configId;
754
794
  ctx.mapViewabilityValues.set(key, viewToken);
755
795
  const cb = ctx.mapViewabilityCallbacks.get(key);
756
796
  cb == null ? void 0 : cb(viewToken);
@@ -778,7 +818,7 @@ var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
778
818
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
779
819
  var _a, _b, _c, _d;
780
820
  const {
781
- data: dataProp,
821
+ data: dataProp = [],
782
822
  initialScrollIndex,
783
823
  initialScrollOffset,
784
824
  horizontal,
@@ -1612,22 +1652,31 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1612
1652
  if (index === void 0) {
1613
1653
  return null;
1614
1654
  }
1615
- const useViewability2 = (configId, callback) => {
1616
- useViewability(configId, callback);
1617
- };
1618
- const useViewabilityAmount2 = (callback) => {
1619
- useViewabilityAmount(callback);
1620
- };
1621
- const useRecyclingEffect2 = (effect) => {
1622
- useRecyclingEffect(effect);
1623
- };
1624
- const useRecyclingState2 = (valueOrFun) => {
1625
- return useRecyclingState(valueOrFun);
1626
- };
1655
+ const useViewability2 = __DEV__ ? (configId, callback) => {
1656
+ console.warn(
1657
+ `[legend-list] useViewability has been moved from a render prop to a regular import: import { useViewability } from "@legendapp/list";`
1658
+ );
1659
+ } : void 0;
1660
+ const useViewabilityAmount2 = __DEV__ ? (callback) => {
1661
+ console.warn(
1662
+ `[legend-list] useViewabilityAmount has been moved from a render prop to a regular import: import { useViewabilityAmount } from "@legendapp/list";`
1663
+ );
1664
+ } : void 0;
1665
+ const useRecyclingEffect2 = __DEV__ ? (effect) => {
1666
+ console.warn(
1667
+ `[legend-list] useRecyclingEffect has been moved from a render prop to a regular import: import { useRecyclingEffect } from "@legendapp/list";`
1668
+ );
1669
+ } : void 0;
1670
+ const useRecyclingState2 = __DEV__ ? (valueOrFun) => {
1671
+ console.warn(
1672
+ `[legend-list] useRecyclingState has been moved from a render prop to a regular import: import { useRecyclingState } from "@legendapp/list";`
1673
+ );
1674
+ } : void 0;
1627
1675
  const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
1628
1676
  item: data[index],
1629
1677
  index,
1630
1678
  extraData: peek$(ctx, "extraData"),
1679
+ // @ts-expect-error TODO: Remove these before 1.0
1631
1680
  useViewability: useViewability2,
1632
1681
  useViewabilityAmount: useViewabilityAmount2,
1633
1682
  useRecyclingEffect: useRecyclingEffect2,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "1.0.0-beta.34",
3
+ "version": "1.0.0-beta.35",
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,