@legendapp/list 0.4.1 → 0.4.3

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
@@ -63,6 +63,7 @@ interface LegendListRenderItemProps<ItemT> {
63
63
  item: ItemT;
64
64
  index: number;
65
65
  useViewability: (configId: string, callback: ViewabilityCallback) => void;
66
+ useViewabilityAmount: (callback: ViewabilityAmountCallback) => void;
66
67
  useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
67
68
  useRecyclingState: <T>(updateState: (info: LegendListRecyclingState<ItemT>) => T) => [T, React.Dispatch<T>];
68
69
  }
@@ -107,6 +108,14 @@ interface ViewToken<ItemT = any> {
107
108
  index: number;
108
109
  isViewable: boolean;
109
110
  }
111
+ interface ViewAmountToken<ItemT = any> extends ViewToken<ItemT> {
112
+ sizeVisible: number;
113
+ size: number;
114
+ percentVisible: number;
115
+ percentOfScroller: number;
116
+ position: number;
117
+ scrollSize: number;
118
+ }
110
119
  interface ViewabilityConfigCallbackPair {
111
120
  viewabilityConfig: ViewabilityConfig;
112
121
  onViewableItemsChanged?: OnViewableItemsChanged;
@@ -146,6 +155,7 @@ interface ViewabilityConfig {
146
155
  waitForInteraction?: boolean | undefined;
147
156
  }
148
157
  type ViewabilityCallback = (viewToken: ViewToken) => void;
158
+ type ViewabilityAmountCallback = (viewToken: ViewAmountToken) => void;
149
159
  interface LegendListRecyclingState<T> {
150
160
  item: T;
151
161
  prevItem: T | undefined;
@@ -157,4 +167,4 @@ declare const LegendList: <T>(props: LegendListProps<T> & {
157
167
  ref?: ForwardedRef<LegendListRef>;
158
168
  }) => ReactElement;
159
169
 
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 };
170
+ 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
@@ -63,6 +63,7 @@ interface LegendListRenderItemProps<ItemT> {
63
63
  item: ItemT;
64
64
  index: number;
65
65
  useViewability: (configId: string, callback: ViewabilityCallback) => void;
66
+ useViewabilityAmount: (callback: ViewabilityAmountCallback) => void;
66
67
  useRecyclingEffect: (effect: (info: LegendListRecyclingState<ItemT>) => void | (() => void)) => void;
67
68
  useRecyclingState: <T>(updateState: (info: LegendListRecyclingState<ItemT>) => T) => [T, React.Dispatch<T>];
68
69
  }
@@ -107,6 +108,14 @@ interface ViewToken<ItemT = any> {
107
108
  index: number;
108
109
  isViewable: boolean;
109
110
  }
111
+ interface ViewAmountToken<ItemT = any> extends ViewToken<ItemT> {
112
+ sizeVisible: number;
113
+ size: number;
114
+ percentVisible: number;
115
+ percentOfScroller: number;
116
+ position: number;
117
+ scrollSize: number;
118
+ }
110
119
  interface ViewabilityConfigCallbackPair {
111
120
  viewabilityConfig: ViewabilityConfig;
112
121
  onViewableItemsChanged?: OnViewableItemsChanged;
@@ -146,6 +155,7 @@ interface ViewabilityConfig {
146
155
  waitForInteraction?: boolean | undefined;
147
156
  }
148
157
  type ViewabilityCallback = (viewToken: ViewToken) => void;
158
+ type ViewabilityAmountCallback = (viewToken: ViewAmountToken) => void;
149
159
  interface LegendListRecyclingState<T> {
150
160
  item: T;
151
161
  prevItem: T | undefined;
@@ -157,4 +167,4 @@ declare const LegendList: <T>(props: LegendListProps<T> & {
157
167
  ref?: ForwardedRef<LegendListRef>;
158
168
  }) => ReactElement;
159
169
 
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 };
170
+ 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
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var React7 = require('react');
3
+ var React6 = require('react');
4
4
  var reactNative = require('react-native');
5
5
 
6
6
  function _interopNamespace(e) {
@@ -21,28 +21,28 @@ function _interopNamespace(e) {
21
21
  return Object.freeze(n);
22
22
  }
23
23
 
24
- var React7__namespace = /*#__PURE__*/_interopNamespace(React7);
24
+ var React6__namespace = /*#__PURE__*/_interopNamespace(React6);
25
25
 
26
26
  // src/LegendList.tsx
27
- var LeanView = React7__namespace.forwardRef((props, ref) => {
28
- return React7__namespace.createElement("RCTView", { ...props, ref });
27
+ var LeanView = React6__namespace.forwardRef((props, ref) => {
28
+ return React6__namespace.createElement("RCTView", { ...props, ref });
29
29
  });
30
30
  LeanView.displayName = "RCTView";
31
- var ContextState = React7__namespace.createContext(null);
31
+ var ContextState = React6__namespace.createContext(null);
32
32
  function StateProvider({ children }) {
33
- const [value] = React7__namespace.useState(() => ({
33
+ const [value] = React6__namespace.useState(() => ({
34
34
  hooks: /* @__PURE__ */ new Map(),
35
35
  listeners: /* @__PURE__ */ new Map(),
36
36
  values: /* @__PURE__ */ new Map()
37
37
  }));
38
- return /* @__PURE__ */ React7__namespace.createElement(ContextState.Provider, { value }, children);
38
+ return /* @__PURE__ */ React6__namespace.createElement(ContextState.Provider, { value }, children);
39
39
  }
40
40
  function useStateContext() {
41
- return React7__namespace.useContext(ContextState);
41
+ return React6__namespace.useContext(ContextState);
42
42
  }
43
43
  function use$(signalName) {
44
- const { hooks, values } = React7__namespace.useContext(ContextState);
45
- const [, forceUpdate] = React7__namespace.useReducer((x) => x + 1, 0);
44
+ const { hooks, values } = React6__namespace.useContext(ContextState);
45
+ const [, forceUpdate] = React6__namespace.useReducer((x) => x + 1, 0);
46
46
  hooks.set(signalName, forceUpdate);
47
47
  return values.get(signalName);
48
48
  }
@@ -79,7 +79,7 @@ function set$(ctx, signalName, value) {
79
79
  function $View({ $key, $style, ...rest }) {
80
80
  use$($key);
81
81
  const style = $style();
82
- return /* @__PURE__ */ React7__namespace.createElement(LeanView, { style, ...rest });
82
+ return /* @__PURE__ */ React6__namespace.createElement(LeanView, { style, ...rest });
83
83
  }
84
84
  function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorComponent }) {
85
85
  const itemIndex = use$(`containerIndex${id}`);
@@ -87,7 +87,7 @@ function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorCompon
87
87
  if (itemIndex < 0) {
88
88
  return null;
89
89
  }
90
- return /* @__PURE__ */ React7__namespace.createElement(React7__namespace.Fragment, { key: recycleItems ? void 0 : itemIndex }, /* @__PURE__ */ React7__namespace.createElement(RenderedItem, { itemIndex, id, getRenderedItem }), ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
90
+ return /* @__PURE__ */ React6__namespace.createElement(React6__namespace.Fragment, { key: recycleItems ? void 0 : itemIndex }, /* @__PURE__ */ React6__namespace.createElement(RenderedItem, { itemIndex, id, getRenderedItem }), ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
91
91
  }
92
92
  function RenderedItem({
93
93
  itemIndex,
@@ -123,7 +123,7 @@ var Container = ({
123
123
  opacity: position < 0 ? 0 : 1
124
124
  };
125
125
  };
126
- return /* @__PURE__ */ React7__namespace.createElement(
126
+ return /* @__PURE__ */ React6__namespace.createElement(
127
127
  $View,
128
128
  {
129
129
  $key: `containerPosition${id}`,
@@ -136,7 +136,7 @@ var Container = ({
136
136
  }
137
137
  }
138
138
  },
139
- /* @__PURE__ */ React7__namespace.createElement(
139
+ /* @__PURE__ */ React6__namespace.createElement(
140
140
  InnerContainer,
141
141
  {
142
142
  id,
@@ -149,7 +149,7 @@ var Container = ({
149
149
  };
150
150
 
151
151
  // src/Containers.tsx
152
- var Containers = React7__namespace.memo(function Containers2({
152
+ var Containers = React6__namespace.memo(function Containers2({
153
153
  horizontal,
154
154
  recycleItems,
155
155
  ItemSeparatorComponent,
@@ -161,7 +161,7 @@ var Containers = React7__namespace.memo(function Containers2({
161
161
  const containers = [];
162
162
  for (let i = 0; i < numContainers; i++) {
163
163
  containers.push(
164
- /* @__PURE__ */ React7__namespace.createElement(
164
+ /* @__PURE__ */ React6__namespace.createElement(
165
165
  Container,
166
166
  {
167
167
  id: i,
@@ -175,7 +175,7 @@ var Containers = React7__namespace.memo(function Containers2({
175
175
  )
176
176
  );
177
177
  }
178
- return /* @__PURE__ */ React7__namespace.createElement(
178
+ return /* @__PURE__ */ React6__namespace.createElement(
179
179
  $View,
180
180
  {
181
181
  $key: "totalSize",
@@ -191,15 +191,15 @@ var Containers = React7__namespace.memo(function Containers2({
191
191
 
192
192
  // src/ListComponent.tsx
193
193
  var getComponent = (Component) => {
194
- if (React7__namespace.isValidElement(Component)) {
194
+ if (React6__namespace.isValidElement(Component)) {
195
195
  return Component;
196
196
  }
197
197
  if (Component) {
198
- return /* @__PURE__ */ React7__namespace.createElement(Component, null);
198
+ return /* @__PURE__ */ React6__namespace.createElement(Component, null);
199
199
  }
200
200
  return null;
201
201
  };
202
- var ListComponent = React7__namespace.memo(function ListComponent2({
202
+ var ListComponent = React6__namespace.memo(function ListComponent2({
203
203
  style,
204
204
  contentContainerStyle,
205
205
  horizontal,
@@ -220,7 +220,7 @@ var ListComponent = React7__namespace.memo(function ListComponent2({
220
220
  ...rest
221
221
  }) {
222
222
  const ctx = useStateContext();
223
- return /* @__PURE__ */ React7__namespace.createElement(
223
+ return /* @__PURE__ */ React6__namespace.createElement(
224
224
  reactNative.ScrollView,
225
225
  {
226
226
  ...rest,
@@ -238,8 +238,8 @@ var ListComponent = React7__namespace.memo(function ListComponent2({
238
238
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
239
239
  ref: refScroller
240
240
  },
241
- alignItemsAtEnd && /* @__PURE__ */ React7__namespace.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
242
- ListHeaderComponent && /* @__PURE__ */ React7__namespace.createElement(
241
+ alignItemsAtEnd && /* @__PURE__ */ React6__namespace.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
242
+ ListHeaderComponent && /* @__PURE__ */ React6__namespace.createElement(
243
243
  reactNative.View,
244
244
  {
245
245
  style: ListHeaderComponentStyle,
@@ -254,7 +254,7 @@ var ListComponent = React7__namespace.memo(function ListComponent2({
254
254
  },
255
255
  getComponent(ListHeaderComponent)
256
256
  ),
257
- /* @__PURE__ */ React7__namespace.createElement(
257
+ /* @__PURE__ */ React6__namespace.createElement(
258
258
  Containers,
259
259
  {
260
260
  horizontal,
@@ -264,13 +264,14 @@ var ListComponent = React7__namespace.memo(function ListComponent2({
264
264
  updateItemSize
265
265
  }
266
266
  ),
267
- ListFooterComponent && /* @__PURE__ */ React7__namespace.createElement(reactNative.View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
267
+ ListFooterComponent && /* @__PURE__ */ React6__namespace.createElement(reactNative.View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
268
268
  );
269
269
  });
270
270
 
271
271
  // src/viewability.ts
272
272
  var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
273
273
  var mapViewabilityCallbacks = /* @__PURE__ */ new Map();
274
+ var mapViewabilityAmountCallbacks = /* @__PURE__ */ new Map();
274
275
  function setupViewability(props) {
275
276
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
276
277
  viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
@@ -315,7 +316,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
315
316
  const changed = [];
316
317
  if (previousViewableItems) {
317
318
  for (const viewToken of previousViewableItems) {
318
- if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize)) {
319
+ if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize, viewToken.item, viewToken.index)) {
319
320
  viewToken.isViewable = false;
320
321
  changed.push(viewToken);
321
322
  }
@@ -326,7 +327,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
326
327
  const item = data[i];
327
328
  if (item) {
328
329
  const key = getId(i);
329
- if (isViewable(state, ctx, viewabilityConfig, key, scrollSize)) {
330
+ if (isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, i)) {
330
331
  const viewToken = {
331
332
  item,
332
333
  key,
@@ -342,7 +343,6 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
342
343
  }
343
344
  Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
344
345
  if (changed.length > 0) {
345
- console.log("changed", changed);
346
346
  viewabilityState.viewableItems = viewableItems;
347
347
  for (let i = 0; i < changed.length; i++) {
348
348
  const change = changed[i];
@@ -353,7 +353,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
353
353
  }
354
354
  }
355
355
  }
356
- function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
356
+ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index) {
357
357
  const { sizes, positions, scroll } = state;
358
358
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
359
359
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
@@ -363,33 +363,65 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
363
363
  const size = sizes.get(key) || 0;
364
364
  const bottom = top + size;
365
365
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
366
- if (isEntirelyVisible) {
367
- return true;
366
+ const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
367
+ const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
368
+ const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
369
+ const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
370
+ const isViewable2 = percent >= viewablePercentThreshold;
371
+ const containerId = findContainerId(state, ctx, index);
372
+ const cb = mapViewabilityAmountCallbacks.get(containerId);
373
+ if (cb) {
374
+ cb({
375
+ index,
376
+ isViewable: isViewable2,
377
+ item,
378
+ key,
379
+ percentVisible,
380
+ percentOfScroller,
381
+ sizeVisible,
382
+ size,
383
+ position: top,
384
+ scrollSize
385
+ });
386
+ }
387
+ return isViewable2;
388
+ }
389
+ function findContainerId(state, ctx, index) {
390
+ const numContainers = peek$(ctx, "numContainers");
391
+ for (let i = 0; i < numContainers; i++) {
392
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
393
+ if (itemIndex === index) {
394
+ return i;
395
+ }
368
396
  }
369
- const visibleHeight = Math.min(bottom, scrollSize) - Math.max(top, 0);
370
- const percent = 100 * (visibleHeight / (viewAreaMode ? scrollSize : size));
371
- return percent >= viewablePercentThreshold;
397
+ return -1;
372
398
  }
373
399
  function maybeUpdateViewabilityCallback(configId, viewToken) {
374
400
  const key = viewToken.key + configId;
375
401
  const cb = mapViewabilityCallbacks.get(key);
376
402
  cb == null ? void 0 : cb(viewToken);
377
403
  }
378
- function registerViewabilityCallback(itemKey, configId, callback) {
379
- const key = itemKey + configId;
404
+ function registerViewabilityCallback(containerId, configId, callback) {
405
+ const key = containerId + configId;
380
406
  mapViewabilityCallbacks.set(key, callback);
381
407
  return () => {
382
408
  mapViewabilityCallbacks.delete(key);
383
409
  };
384
410
  }
411
+ function registerViewabilityAmountCallback(containerId, callback) {
412
+ mapViewabilityAmountCallbacks.set(containerId, callback);
413
+ return () => {
414
+ mapViewabilityAmountCallbacks.delete(containerId);
415
+ };
416
+ }
385
417
 
386
418
  // src/LegendList.tsx
387
419
  var DEFAULT_SCROLL_BUFFER = 0;
388
420
  var POSITION_OUT_OF_VIEW = -1e4;
389
- var LegendList = React7.forwardRef(function LegendList2(props, forwardedRef) {
390
- return /* @__PURE__ */ React.createElement(StateProvider, null, /* @__PURE__ */ React.createElement(LegendListInner, { ...props, ref: forwardedRef }));
421
+ var LegendList = React6.forwardRef(function LegendList2(props, forwardedRef) {
422
+ return /* @__PURE__ */ React6__namespace.createElement(StateProvider, null, /* @__PURE__ */ React6__namespace.createElement(LegendListInner, { ...props, ref: forwardedRef }));
391
423
  });
392
- var LegendListInner = React7.forwardRef(function LegendListInner2(props, forwardedRef) {
424
+ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forwardedRef) {
393
425
  var _a, _b;
394
426
  const {
395
427
  data,
@@ -415,17 +447,17 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
415
447
  ...rest
416
448
  } = props;
417
449
  const ctx = useStateContext();
418
- const internalRef = React7.useRef(null);
450
+ const internalRef = React6.useRef(null);
419
451
  const refScroller = internalRef;
420
452
  const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_SCROLL_BUFFER;
421
453
  const styleFlattened = reactNative.StyleSheet.flatten(styleProp);
422
- const style = React7.useMemo(() => styleFlattened, [JSON.stringify(styleFlattened)]);
454
+ const style = React6.useMemo(() => styleFlattened, [JSON.stringify(styleFlattened)]);
423
455
  const contentContainerStyleFlattened = reactNative.StyleSheet.flatten(contentContainerStyleProp);
424
- const contentContainerStyle = React7.useMemo(
456
+ const contentContainerStyle = React6.useMemo(
425
457
  () => contentContainerStyleFlattened,
426
458
  [JSON.stringify(contentContainerStyleProp)]
427
459
  );
428
- const refState = React7.useRef();
460
+ const refState = React6.useRef();
429
461
  const getId = (index) => {
430
462
  var _a2;
431
463
  const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
@@ -453,7 +485,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
453
485
  }
454
486
  return void 0;
455
487
  };
456
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : React7.useMemo(calculateInitialOffset, []);
488
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : React6.useMemo(calculateInitialOffset, []);
457
489
  if (!refState.current) {
458
490
  refState.current = {
459
491
  sizes: /* @__PURE__ */ new Map(),
@@ -483,7 +515,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
483
515
  refState.current.data = data;
484
516
  set$(ctx, "numItems", data.length);
485
517
  set$(ctx, "stylePaddingTop", (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
486
- const addTotalSize = React7.useCallback((add) => {
518
+ const addTotalSize = React6.useCallback((add) => {
487
519
  const prev = refState.current.totalSize;
488
520
  refState.current.totalSize += add;
489
521
  const totalSize = refState.current.totalSize;
@@ -502,19 +534,21 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
502
534
  refState.current.animFrameTotalSize = requestAnimationFrame(doAdd);
503
535
  }
504
536
  }, []);
505
- const getRenderedItem = React7.useCallback(
537
+ const getRenderedItem = React6.useCallback(
506
538
  (index, containerIndex) => {
507
539
  var _a2;
508
540
  const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
509
541
  if (!data2) {
510
542
  return null;
511
543
  }
512
- const itemKey = getId(index);
513
544
  const useViewability = (configId, callback) => {
514
- React7.useEffect(() => registerViewabilityCallback(itemKey, configId, callback), []);
545
+ React6.useEffect(() => registerViewabilityCallback(containerIndex, configId, callback), []);
546
+ };
547
+ const useViewabilityAmount = (callback) => {
548
+ React6.useEffect(() => registerViewabilityAmountCallback(containerIndex, callback), []);
515
549
  };
516
550
  const useRecyclingEffect = (effect) => {
517
- React7.useEffect(() => {
551
+ React6.useEffect(() => {
518
552
  let prevIndex = index;
519
553
  let prevItem = data2[index];
520
554
  const signal = `containerIndex${containerIndex}`;
@@ -539,7 +573,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
539
573
  }, []);
540
574
  };
541
575
  const useRecyclingState = (updateState) => {
542
- const stateInfo = React7.useState(
576
+ const stateInfo = React6.useState(
543
577
  () => updateState({
544
578
  index,
545
579
  item: data2[index],
@@ -549,7 +583,6 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
549
583
  );
550
584
  useRecyclingEffect((state) => {
551
585
  const newState = updateState(state);
552
- console.log("setting state", newState);
553
586
  stateInfo[1](newState);
554
587
  });
555
588
  return stateInfo;
@@ -558,6 +591,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
558
591
  item: data2[index],
559
592
  index,
560
593
  useViewability,
594
+ useViewabilityAmount,
561
595
  useRecyclingEffect,
562
596
  useRecyclingState
563
597
  });
@@ -565,7 +599,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
565
599
  },
566
600
  [renderItem]
567
601
  );
568
- const calculateItemsInView = React7.useCallback(() => {
602
+ const calculateItemsInView = React6.useCallback(() => {
569
603
  reactNative.unstable_batchedUpdates(() => {
570
604
  var _a2, _b2, _c;
571
605
  const {
@@ -726,7 +760,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
726
760
  }
727
761
  });
728
762
  }, [data]);
729
- React7.useMemo(() => {
763
+ React6.useMemo(() => {
730
764
  var _a2, _b2;
731
765
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
732
766
  const scrollLength = refState.current.scrollLength;
@@ -763,14 +797,14 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
763
797
  }
764
798
  }
765
799
  };
766
- React7.useEffect(() => {
800
+ React6.useEffect(() => {
767
801
  if (refState.current) {
768
802
  refState.current.isEndReached = false;
769
803
  }
770
804
  calculateItemsInView();
771
805
  checkAtBottom();
772
806
  }, [data]);
773
- const updateItemSize = React7.useCallback((index, size) => {
807
+ const updateItemSize = React6.useCallback((index, size) => {
774
808
  var _a2, _b2, _c, _d;
775
809
  const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
776
810
  if (!data2) {
@@ -800,18 +834,18 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
800
834
  }
801
835
  }
802
836
  }, []);
803
- const handleScrollDebounced = React7.useCallback(() => {
837
+ const handleScrollDebounced = React6.useCallback(() => {
804
838
  calculateItemsInView();
805
839
  checkAtBottom();
806
840
  if (refState.current) {
807
841
  refState.current.animFrameScroll = null;
808
842
  }
809
843
  }, []);
810
- const onLayout = React7.useCallback((event) => {
844
+ const onLayout = React6.useCallback((event) => {
811
845
  const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
812
846
  refState.current.scrollLength = scrollLength;
813
847
  }, []);
814
- const handleScroll = React7.useCallback(
848
+ const handleScroll = React6.useCallback(
815
849
  (event, fromSelf) => {
816
850
  var _a2, _b2, _c;
817
851
  if (((_b2 = (_a2 = event.nativeEvent) == null ? void 0 : _a2.contentSize) == null ? void 0 : _b2.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
@@ -829,7 +863,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
829
863
  },
830
864
  []
831
865
  );
832
- React7.useImperativeHandle(forwardedRef, () => {
866
+ React6.useImperativeHandle(forwardedRef, () => {
833
867
  const scrollToIndex = ({ index, animated }) => {
834
868
  const offsetObj = calculateInitialOffset(index);
835
869
  const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
@@ -854,7 +888,7 @@ var LegendListInner = React7.forwardRef(function LegendListInner2(props, forward
854
888
  scrollToEnd: refScroller.current.scrollToEnd
855
889
  };
856
890
  }, []);
857
- return /* @__PURE__ */ React.createElement(
891
+ return /* @__PURE__ */ React6__namespace.createElement(
858
892
  ListComponent,
859
893
  {
860
894
  ...rest,
package/index.mjs CHANGED
@@ -1,27 +1,27 @@
1
- import * as React7 from 'react';
1
+ import * as React6 from 'react';
2
2
  import { forwardRef, useRef, useMemo, useCallback, useEffect, useImperativeHandle, useState } from 'react';
3
3
  import { ScrollView, View, StyleSheet, Dimensions, unstable_batchedUpdates } from 'react-native';
4
4
 
5
5
  // src/LegendList.tsx
6
- var LeanView = React7.forwardRef((props, ref) => {
7
- return React7.createElement("RCTView", { ...props, ref });
6
+ var LeanView = React6.forwardRef((props, ref) => {
7
+ return React6.createElement("RCTView", { ...props, ref });
8
8
  });
9
9
  LeanView.displayName = "RCTView";
10
- var ContextState = React7.createContext(null);
10
+ var ContextState = React6.createContext(null);
11
11
  function StateProvider({ children }) {
12
- const [value] = React7.useState(() => ({
12
+ const [value] = React6.useState(() => ({
13
13
  hooks: /* @__PURE__ */ new Map(),
14
14
  listeners: /* @__PURE__ */ new Map(),
15
15
  values: /* @__PURE__ */ new Map()
16
16
  }));
17
- return /* @__PURE__ */ React7.createElement(ContextState.Provider, { value }, children);
17
+ return /* @__PURE__ */ React6.createElement(ContextState.Provider, { value }, children);
18
18
  }
19
19
  function useStateContext() {
20
- return React7.useContext(ContextState);
20
+ return React6.useContext(ContextState);
21
21
  }
22
22
  function use$(signalName) {
23
- const { hooks, values } = React7.useContext(ContextState);
24
- const [, forceUpdate] = React7.useReducer((x) => x + 1, 0);
23
+ const { hooks, values } = React6.useContext(ContextState);
24
+ const [, forceUpdate] = React6.useReducer((x) => x + 1, 0);
25
25
  hooks.set(signalName, forceUpdate);
26
26
  return values.get(signalName);
27
27
  }
@@ -58,7 +58,7 @@ function set$(ctx, signalName, value) {
58
58
  function $View({ $key, $style, ...rest }) {
59
59
  use$($key);
60
60
  const style = $style();
61
- return /* @__PURE__ */ React7.createElement(LeanView, { style, ...rest });
61
+ return /* @__PURE__ */ React6.createElement(LeanView, { style, ...rest });
62
62
  }
63
63
  function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorComponent }) {
64
64
  const itemIndex = use$(`containerIndex${id}`);
@@ -66,7 +66,7 @@ function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorCompon
66
66
  if (itemIndex < 0) {
67
67
  return null;
68
68
  }
69
- return /* @__PURE__ */ React7.createElement(React7.Fragment, { key: recycleItems ? void 0 : itemIndex }, /* @__PURE__ */ React7.createElement(RenderedItem, { itemIndex, id, getRenderedItem }), ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
69
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, { key: recycleItems ? void 0 : itemIndex }, /* @__PURE__ */ React6.createElement(RenderedItem, { itemIndex, id, getRenderedItem }), ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
70
70
  }
71
71
  function RenderedItem({
72
72
  itemIndex,
@@ -102,7 +102,7 @@ var Container = ({
102
102
  opacity: position < 0 ? 0 : 1
103
103
  };
104
104
  };
105
- return /* @__PURE__ */ React7.createElement(
105
+ return /* @__PURE__ */ React6.createElement(
106
106
  $View,
107
107
  {
108
108
  $key: `containerPosition${id}`,
@@ -115,7 +115,7 @@ var Container = ({
115
115
  }
116
116
  }
117
117
  },
118
- /* @__PURE__ */ React7.createElement(
118
+ /* @__PURE__ */ React6.createElement(
119
119
  InnerContainer,
120
120
  {
121
121
  id,
@@ -128,7 +128,7 @@ var Container = ({
128
128
  };
129
129
 
130
130
  // src/Containers.tsx
131
- var Containers = React7.memo(function Containers2({
131
+ var Containers = React6.memo(function Containers2({
132
132
  horizontal,
133
133
  recycleItems,
134
134
  ItemSeparatorComponent,
@@ -140,7 +140,7 @@ var Containers = React7.memo(function Containers2({
140
140
  const containers = [];
141
141
  for (let i = 0; i < numContainers; i++) {
142
142
  containers.push(
143
- /* @__PURE__ */ React7.createElement(
143
+ /* @__PURE__ */ React6.createElement(
144
144
  Container,
145
145
  {
146
146
  id: i,
@@ -154,7 +154,7 @@ var Containers = React7.memo(function Containers2({
154
154
  )
155
155
  );
156
156
  }
157
- return /* @__PURE__ */ React7.createElement(
157
+ return /* @__PURE__ */ React6.createElement(
158
158
  $View,
159
159
  {
160
160
  $key: "totalSize",
@@ -170,15 +170,15 @@ var Containers = React7.memo(function Containers2({
170
170
 
171
171
  // src/ListComponent.tsx
172
172
  var getComponent = (Component) => {
173
- if (React7.isValidElement(Component)) {
173
+ if (React6.isValidElement(Component)) {
174
174
  return Component;
175
175
  }
176
176
  if (Component) {
177
- return /* @__PURE__ */ React7.createElement(Component, null);
177
+ return /* @__PURE__ */ React6.createElement(Component, null);
178
178
  }
179
179
  return null;
180
180
  };
181
- var ListComponent = React7.memo(function ListComponent2({
181
+ var ListComponent = React6.memo(function ListComponent2({
182
182
  style,
183
183
  contentContainerStyle,
184
184
  horizontal,
@@ -199,7 +199,7 @@ var ListComponent = React7.memo(function ListComponent2({
199
199
  ...rest
200
200
  }) {
201
201
  const ctx = useStateContext();
202
- return /* @__PURE__ */ React7.createElement(
202
+ return /* @__PURE__ */ React6.createElement(
203
203
  ScrollView,
204
204
  {
205
205
  ...rest,
@@ -217,8 +217,8 @@ var ListComponent = React7.memo(function ListComponent2({
217
217
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
218
218
  ref: refScroller
219
219
  },
220
- alignItemsAtEnd && /* @__PURE__ */ React7.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
221
- ListHeaderComponent && /* @__PURE__ */ React7.createElement(
220
+ alignItemsAtEnd && /* @__PURE__ */ React6.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
221
+ ListHeaderComponent && /* @__PURE__ */ React6.createElement(
222
222
  View,
223
223
  {
224
224
  style: ListHeaderComponentStyle,
@@ -233,7 +233,7 @@ var ListComponent = React7.memo(function ListComponent2({
233
233
  },
234
234
  getComponent(ListHeaderComponent)
235
235
  ),
236
- /* @__PURE__ */ React7.createElement(
236
+ /* @__PURE__ */ React6.createElement(
237
237
  Containers,
238
238
  {
239
239
  horizontal,
@@ -243,13 +243,14 @@ var ListComponent = React7.memo(function ListComponent2({
243
243
  updateItemSize
244
244
  }
245
245
  ),
246
- ListFooterComponent && /* @__PURE__ */ React7.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
246
+ ListFooterComponent && /* @__PURE__ */ React6.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
247
247
  );
248
248
  });
249
249
 
250
250
  // src/viewability.ts
251
251
  var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
252
252
  var mapViewabilityCallbacks = /* @__PURE__ */ new Map();
253
+ var mapViewabilityAmountCallbacks = /* @__PURE__ */ new Map();
253
254
  function setupViewability(props) {
254
255
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
255
256
  viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
@@ -294,7 +295,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
294
295
  const changed = [];
295
296
  if (previousViewableItems) {
296
297
  for (const viewToken of previousViewableItems) {
297
- if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize)) {
298
+ if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize, viewToken.item, viewToken.index)) {
298
299
  viewToken.isViewable = false;
299
300
  changed.push(viewToken);
300
301
  }
@@ -305,7 +306,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
305
306
  const item = data[i];
306
307
  if (item) {
307
308
  const key = getId(i);
308
- if (isViewable(state, ctx, viewabilityConfig, key, scrollSize)) {
309
+ if (isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, i)) {
309
310
  const viewToken = {
310
311
  item,
311
312
  key,
@@ -321,7 +322,6 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
321
322
  }
322
323
  Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
323
324
  if (changed.length > 0) {
324
- console.log("changed", changed);
325
325
  viewabilityState.viewableItems = viewableItems;
326
326
  for (let i = 0; i < changed.length; i++) {
327
327
  const change = changed[i];
@@ -332,7 +332,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
332
332
  }
333
333
  }
334
334
  }
335
- function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
335
+ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index) {
336
336
  const { sizes, positions, scroll } = state;
337
337
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
338
338
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
@@ -342,31 +342,63 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
342
342
  const size = sizes.get(key) || 0;
343
343
  const bottom = top + size;
344
344
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
345
- if (isEntirelyVisible) {
346
- return true;
345
+ const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
346
+ const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
347
+ const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
348
+ const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
349
+ const isViewable2 = percent >= viewablePercentThreshold;
350
+ const containerId = findContainerId(state, ctx, index);
351
+ const cb = mapViewabilityAmountCallbacks.get(containerId);
352
+ if (cb) {
353
+ cb({
354
+ index,
355
+ isViewable: isViewable2,
356
+ item,
357
+ key,
358
+ percentVisible,
359
+ percentOfScroller,
360
+ sizeVisible,
361
+ size,
362
+ position: top,
363
+ scrollSize
364
+ });
365
+ }
366
+ return isViewable2;
367
+ }
368
+ function findContainerId(state, ctx, index) {
369
+ const numContainers = peek$(ctx, "numContainers");
370
+ for (let i = 0; i < numContainers; i++) {
371
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
372
+ if (itemIndex === index) {
373
+ return i;
374
+ }
347
375
  }
348
- const visibleHeight = Math.min(bottom, scrollSize) - Math.max(top, 0);
349
- const percent = 100 * (visibleHeight / (viewAreaMode ? scrollSize : size));
350
- return percent >= viewablePercentThreshold;
376
+ return -1;
351
377
  }
352
378
  function maybeUpdateViewabilityCallback(configId, viewToken) {
353
379
  const key = viewToken.key + configId;
354
380
  const cb = mapViewabilityCallbacks.get(key);
355
381
  cb == null ? void 0 : cb(viewToken);
356
382
  }
357
- function registerViewabilityCallback(itemKey, configId, callback) {
358
- const key = itemKey + configId;
383
+ function registerViewabilityCallback(containerId, configId, callback) {
384
+ const key = containerId + configId;
359
385
  mapViewabilityCallbacks.set(key, callback);
360
386
  return () => {
361
387
  mapViewabilityCallbacks.delete(key);
362
388
  };
363
389
  }
390
+ function registerViewabilityAmountCallback(containerId, callback) {
391
+ mapViewabilityAmountCallbacks.set(containerId, callback);
392
+ return () => {
393
+ mapViewabilityAmountCallbacks.delete(containerId);
394
+ };
395
+ }
364
396
 
365
397
  // src/LegendList.tsx
366
398
  var DEFAULT_SCROLL_BUFFER = 0;
367
399
  var POSITION_OUT_OF_VIEW = -1e4;
368
400
  var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
369
- return /* @__PURE__ */ React.createElement(StateProvider, null, /* @__PURE__ */ React.createElement(LegendListInner, { ...props, ref: forwardedRef }));
401
+ return /* @__PURE__ */ React6.createElement(StateProvider, null, /* @__PURE__ */ React6.createElement(LegendListInner, { ...props, ref: forwardedRef }));
370
402
  });
371
403
  var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef) {
372
404
  var _a, _b;
@@ -488,9 +520,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
488
520
  if (!data2) {
489
521
  return null;
490
522
  }
491
- const itemKey = getId(index);
492
523
  const useViewability = (configId, callback) => {
493
- useEffect(() => registerViewabilityCallback(itemKey, configId, callback), []);
524
+ useEffect(() => registerViewabilityCallback(containerIndex, configId, callback), []);
525
+ };
526
+ const useViewabilityAmount = (callback) => {
527
+ useEffect(() => registerViewabilityAmountCallback(containerIndex, callback), []);
494
528
  };
495
529
  const useRecyclingEffect = (effect) => {
496
530
  useEffect(() => {
@@ -528,7 +562,6 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
528
562
  );
529
563
  useRecyclingEffect((state) => {
530
564
  const newState = updateState(state);
531
- console.log("setting state", newState);
532
565
  stateInfo[1](newState);
533
566
  });
534
567
  return stateInfo;
@@ -537,6 +570,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
537
570
  item: data2[index],
538
571
  index,
539
572
  useViewability,
573
+ useViewabilityAmount,
540
574
  useRecyclingEffect,
541
575
  useRecyclingState
542
576
  });
@@ -833,7 +867,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
833
867
  scrollToEnd: refScroller.current.scrollToEnd
834
868
  };
835
869
  }, []);
836
- return /* @__PURE__ */ React.createElement(
870
+ return /* @__PURE__ */ React6.createElement(
837
871
  ListComponent,
838
872
  {
839
873
  ...rest,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "legend-list",
5
5
  "sideEffects": false,
6
6
  "private": false,