@legendapp/list 1.0.0-beta.33 → 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 {
@@ -474,9 +470,9 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
474
470
  keyExtractor?: ((item: T, index: number) => string) | undefined;
475
471
  ListEmptyComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
476
472
  ListFooterComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
477
- ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
473
+ ListFooterComponentStyle?: react_native.StyleProp<ViewStyle> | undefined;
478
474
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
479
- ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
475
+ ListHeaderComponentStyle?: react_native.StyleProp<ViewStyle> | undefined;
480
476
  maintainScrollAtEnd?: boolean;
481
477
  maintainScrollAtEndThreshold?: number;
482
478
  maintainVisibleContentPosition?: boolean;
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 {
@@ -474,9 +470,9 @@ declare const LegendList: <T>(props: Omit<react_native.ScrollViewProps, "content
474
470
  keyExtractor?: ((item: T, index: number) => string) | undefined;
475
471
  ListEmptyComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
476
472
  ListFooterComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
477
- ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
473
+ ListFooterComponentStyle?: react_native.StyleProp<ViewStyle> | undefined;
478
474
  ListHeaderComponent?: React$1.ComponentType<any> | React$1.ReactElement | null | undefined;
479
- ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
475
+ ListHeaderComponentStyle?: react_native.StyleProp<ViewStyle> | undefined;
480
476
  maintainScrollAtEnd?: boolean;
481
477
  maintainScrollAtEndThreshold?: number;
482
478
  maintainVisibleContentPosition?: boolean;
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
  });
@@ -261,7 +263,7 @@ var Container = ({
261
263
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
262
264
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
263
265
  let verticalPaddingStyles;
264
- if (columnWrapperStyle && !horizontal && numColumns > 1) {
266
+ if (columnWrapperStyle) {
265
267
  const { columnGap, rowGap, gap } = columnWrapperStyle;
266
268
  verticalPaddingStyles = {
267
269
  paddingBottom: !lastItemKeys.includes(itemKey) ? rowGap || gap || void 0 : void 0,
@@ -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);
@@ -781,8 +821,11 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
781
821
  var DEFAULT_DRAW_DISTANCE = 250;
782
822
  var DEFAULT_ITEM_SIZE = 100;
783
823
  function createColumnWrapperStyle(contentContainerStyle) {
784
- const { gap, columnGap, rowGap } = reactNative.StyleSheet.flatten(contentContainerStyle);
824
+ const { gap, columnGap, rowGap } = contentContainerStyle;
785
825
  if (gap || columnGap || rowGap) {
826
+ contentContainerStyle.gap = void 0;
827
+ contentContainerStyle.columnGap = void 0;
828
+ contentContainerStyle.rowGap = void 0;
786
829
  return {
787
830
  gap,
788
831
  columnGap,
@@ -796,7 +839,7 @@ var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
796
839
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
797
840
  var _a, _b, _c, _d;
798
841
  const {
799
- data: dataProp,
842
+ data: dataProp = [],
800
843
  initialScrollIndex,
801
844
  initialScrollOffset,
802
845
  horizontal,
@@ -822,6 +865,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
822
865
  refScrollView,
823
866
  waitForInitialLayout = true,
824
867
  extraData,
868
+ contentContainerStyle: contentContainerStyleProp,
825
869
  onLayout: onLayoutProp,
826
870
  onRefresh,
827
871
  refreshing,
@@ -833,13 +877,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
833
877
  onViewableItemsChanged,
834
878
  ...rest
835
879
  } = props;
836
- const { style, contentContainerStyle } = props;
880
+ const { style } = props;
837
881
  const callbacks = React6.useRef({
838
882
  onStartReached: rest.onStartReached,
839
883
  onEndReached: rest.onEndReached
840
884
  });
841
885
  callbacks.current.onStartReached = rest.onStartReached;
842
886
  callbacks.current.onEndReached = rest.onEndReached;
887
+ const contentContainerStyle = reactNative.StyleSheet.flatten(contentContainerStyleProp);
843
888
  const ctx = useStateContext();
844
889
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
845
890
  const refScroller = React6.useRef(null);
@@ -1628,22 +1673,31 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1628
1673
  if (index === void 0) {
1629
1674
  return null;
1630
1675
  }
1631
- const useViewability2 = (configId, callback) => {
1632
- useViewability(configId, callback);
1633
- };
1634
- const useViewabilityAmount2 = (callback) => {
1635
- useViewabilityAmount(callback);
1636
- };
1637
- const useRecyclingEffect2 = (effect) => {
1638
- useRecyclingEffect(effect);
1639
- };
1640
- const useRecyclingState2 = (valueOrFun) => {
1641
- return useRecyclingState(valueOrFun);
1642
- };
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;
1643
1696
  const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
1644
1697
  item: data[index],
1645
1698
  index,
1646
1699
  extraData: peek$(ctx, "extraData"),
1700
+ // @ts-expect-error TODO: Remove these before 1.0
1647
1701
  useViewability: useViewability2,
1648
1702
  useViewabilityAmount: useViewabilityAmount2,
1649
1703
  useRecyclingEffect: useRecyclingEffect2,
@@ -1993,7 +2047,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1993
2047
  progressViewOffset
1994
2048
  }
1995
2049
  ),
1996
- style
2050
+ style,
2051
+ contentContainerStyle
1997
2052
  }
1998
2053
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React6__namespace.createElement(DebugView, { state: refState.current }));
1999
2054
  });
package/index.mjs CHANGED
@@ -1,6 +1,6 @@
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';
3
- import { View, Text, Platform, Animated, ScrollView, Dimensions, StyleSheet, RefreshControl } from 'react-native';
2
+ import React6__default, { memo, useReducer, useEffect, createContext, useMemo, useRef, useCallback, useImperativeHandle, useSyncExternalStore, useContext, useState, forwardRef, useLayoutEffect } from 'react';
3
+ import { View, Text, Platform, Animated, ScrollView, StyleSheet, Dimensions, RefreshControl } from 'react-native';
4
4
 
5
5
  // src/LegendList.tsx
6
6
  var ContextState = React6.createContext(null);
@@ -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
  });
@@ -240,7 +242,7 @@ var Container = ({
240
242
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
241
243
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
242
244
  let verticalPaddingStyles;
243
- if (columnWrapperStyle && !horizontal && numColumns > 1) {
245
+ if (columnWrapperStyle) {
244
246
  const { columnGap, rowGap, gap } = columnWrapperStyle;
245
247
  verticalPaddingStyles = {
246
248
  paddingBottom: !lastItemKeys.includes(itemKey) ? rowGap || gap || void 0 : void 0,
@@ -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);
@@ -760,8 +800,11 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
760
800
  var DEFAULT_DRAW_DISTANCE = 250;
761
801
  var DEFAULT_ITEM_SIZE = 100;
762
802
  function createColumnWrapperStyle(contentContainerStyle) {
763
- const { gap, columnGap, rowGap } = StyleSheet.flatten(contentContainerStyle);
803
+ const { gap, columnGap, rowGap } = contentContainerStyle;
764
804
  if (gap || columnGap || rowGap) {
805
+ contentContainerStyle.gap = void 0;
806
+ contentContainerStyle.columnGap = void 0;
807
+ contentContainerStyle.rowGap = void 0;
765
808
  return {
766
809
  gap,
767
810
  columnGap,
@@ -775,7 +818,7 @@ var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
775
818
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
776
819
  var _a, _b, _c, _d;
777
820
  const {
778
- data: dataProp,
821
+ data: dataProp = [],
779
822
  initialScrollIndex,
780
823
  initialScrollOffset,
781
824
  horizontal,
@@ -801,6 +844,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
801
844
  refScrollView,
802
845
  waitForInitialLayout = true,
803
846
  extraData,
847
+ contentContainerStyle: contentContainerStyleProp,
804
848
  onLayout: onLayoutProp,
805
849
  onRefresh,
806
850
  refreshing,
@@ -812,13 +856,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
812
856
  onViewableItemsChanged,
813
857
  ...rest
814
858
  } = props;
815
- const { style, contentContainerStyle } = props;
859
+ const { style } = props;
816
860
  const callbacks = useRef({
817
861
  onStartReached: rest.onStartReached,
818
862
  onEndReached: rest.onEndReached
819
863
  });
820
864
  callbacks.current.onStartReached = rest.onStartReached;
821
865
  callbacks.current.onEndReached = rest.onEndReached;
866
+ const contentContainerStyle = StyleSheet.flatten(contentContainerStyleProp);
822
867
  const ctx = useStateContext();
823
868
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
824
869
  const refScroller = useRef(null);
@@ -1607,22 +1652,31 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1607
1652
  if (index === void 0) {
1608
1653
  return null;
1609
1654
  }
1610
- const useViewability2 = (configId, callback) => {
1611
- useViewability(configId, callback);
1612
- };
1613
- const useViewabilityAmount2 = (callback) => {
1614
- useViewabilityAmount(callback);
1615
- };
1616
- const useRecyclingEffect2 = (effect) => {
1617
- useRecyclingEffect(effect);
1618
- };
1619
- const useRecyclingState2 = (valueOrFun) => {
1620
- return useRecyclingState(valueOrFun);
1621
- };
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;
1622
1675
  const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
1623
1676
  item: data[index],
1624
1677
  index,
1625
1678
  extraData: peek$(ctx, "extraData"),
1679
+ // @ts-expect-error TODO: Remove these before 1.0
1626
1680
  useViewability: useViewability2,
1627
1681
  useViewabilityAmount: useViewabilityAmount2,
1628
1682
  useRecyclingEffect: useRecyclingEffect2,
@@ -1972,7 +2026,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1972
2026
  progressViewOffset
1973
2027
  }
1974
2028
  ),
1975
- style
2029
+ style,
2030
+ contentContainerStyle
1976
2031
  }
1977
2032
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React6.createElement(DebugView, { state: refState.current }));
1978
2033
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "1.0.0-beta.33",
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,