@legendapp/list 0.4.2 → 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
@@ -271,6 +271,7 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
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,25 +363,57 @@ 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;
@@ -509,9 +541,11 @@ var LegendListInner = React6.forwardRef(function LegendListInner2(props, forward
509
541
  if (!data2) {
510
542
  return null;
511
543
  }
512
- const itemKey = getId(index);
513
544
  const useViewability = (configId, callback) => {
514
- React6.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
551
  React6.useEffect(() => {
@@ -549,7 +583,6 @@ var LegendListInner = React6.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 = React6.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
  });
package/index.mjs CHANGED
@@ -250,6 +250,7 @@ var ListComponent = React6.memo(function ListComponent2({
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,25 +342,57 @@ 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;
@@ -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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "legend-list",
5
5
  "sideEffects": false,
6
6
  "private": false,