@legendapp/list 0.3.5 → 0.3.6

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.
Files changed (5) hide show
  1. package/index.d.mts +104 -3
  2. package/index.d.ts +104 -3
  3. package/index.js +518 -381
  4. package/index.mjs +517 -380
  5. package/package.json +1 -1
package/index.mjs CHANGED
@@ -1,26 +1,26 @@
1
- import * as React6 from 'react';
2
- import { forwardRef, useRef, useMemo, useCallback } from 'react';
1
+ import * as React7 from 'react';
2
+ import { forwardRef, useRef, useMemo, useCallback, useEffect, useImperativeHandle } from 'react';
3
3
  import { ScrollView, View, StyleSheet, Dimensions, unstable_batchedUpdates } from 'react-native';
4
4
 
5
5
  // src/LegendList.tsx
6
- var LeanView = React6.forwardRef((props, ref) => {
7
- return React6.createElement("RCTView", { ...props, ref });
6
+ var LeanView = React7.forwardRef((props, ref) => {
7
+ return React7.createElement("RCTView", { ...props, ref });
8
8
  });
9
9
  LeanView.displayName = "RCTView";
10
- var ContextListener = React6.createContext(null);
10
+ var ContextState = React7.createContext(null);
11
11
  function StateProvider({ children }) {
12
- const [value] = React6.useState(() => ({
12
+ const [value] = React7.useState(() => ({
13
13
  listeners: /* @__PURE__ */ new Map(),
14
14
  values: /* @__PURE__ */ new Map()
15
15
  }));
16
- return /* @__PURE__ */ React6.createElement(ContextListener.Provider, { value }, children);
16
+ return /* @__PURE__ */ React7.createElement(ContextState.Provider, { value }, children);
17
17
  }
18
18
  function useStateContext() {
19
- return React6.useContext(ContextListener);
19
+ return React7.useContext(ContextState);
20
20
  }
21
21
  function use$(signalName) {
22
- const { listeners, values } = React6.useContext(ContextListener);
23
- const [, forceUpdate] = React6.useReducer((x) => x + 1, 0);
22
+ const { listeners, values } = React7.useContext(ContextState);
23
+ const [, forceUpdate] = React7.useReducer((x) => x + 1, 0);
24
24
  listeners.set(signalName, forceUpdate);
25
25
  return values.get(signalName);
26
26
  }
@@ -41,7 +41,7 @@ function set$(ctx, signalName, value) {
41
41
  function $View({ $key, $style, ...rest }) {
42
42
  use$($key);
43
43
  const style = $style();
44
- return /* @__PURE__ */ React6.createElement(LeanView, { style, ...rest });
44
+ return /* @__PURE__ */ React7.createElement(LeanView, { style, ...rest });
45
45
  }
46
46
  function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorComponent }) {
47
47
  const itemIndex = use$(`containerIndex${id}`);
@@ -50,7 +50,7 @@ function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorCompon
50
50
  return null;
51
51
  }
52
52
  const renderedItem = getRenderedItem(itemIndex);
53
- return /* @__PURE__ */ React6.createElement(React6.Fragment, { key: recycleItems ? void 0 : itemIndex }, renderedItem, ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
53
+ return /* @__PURE__ */ React7.createElement(React7.Fragment, { key: recycleItems ? void 0 : itemIndex }, renderedItem, ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
54
54
  }
55
55
  var Container = ({
56
56
  id,
@@ -78,7 +78,7 @@ var Container = ({
78
78
  opacity: position < 0 ? 0 : 1
79
79
  };
80
80
  };
81
- return /* @__PURE__ */ React6.createElement(
81
+ return /* @__PURE__ */ React7.createElement(
82
82
  $View,
83
83
  {
84
84
  $key: `containerPosition${id}`,
@@ -86,12 +86,12 @@ var Container = ({
86
86
  onLayout: (event) => {
87
87
  const index = peek$(ctx, `containerIndex${id}`);
88
88
  if (index >= 0) {
89
- const length = event.nativeEvent.layout[horizontal ? "width" : "height"];
90
- onLayout(index, length);
89
+ const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
90
+ onLayout(index, size);
91
91
  }
92
92
  }
93
93
  },
94
- /* @__PURE__ */ React6.createElement(
94
+ /* @__PURE__ */ React7.createElement(
95
95
  InnerContainer,
96
96
  {
97
97
  id,
@@ -104,7 +104,7 @@ var Container = ({
104
104
  };
105
105
 
106
106
  // src/Containers.tsx
107
- var Containers = React6.memo(function Containers2({
107
+ var Containers = React7.memo(function Containers2({
108
108
  horizontal,
109
109
  recycleItems,
110
110
  ItemSeparatorComponent,
@@ -116,7 +116,7 @@ var Containers = React6.memo(function Containers2({
116
116
  const containers = [];
117
117
  for (let i = 0; i < numContainers; i++) {
118
118
  containers.push(
119
- /* @__PURE__ */ React6.createElement(
119
+ /* @__PURE__ */ React7.createElement(
120
120
  Container,
121
121
  {
122
122
  id: i,
@@ -130,14 +130,14 @@ var Containers = React6.memo(function Containers2({
130
130
  )
131
131
  );
132
132
  }
133
- return /* @__PURE__ */ React6.createElement(
133
+ return /* @__PURE__ */ React7.createElement(
134
134
  $View,
135
135
  {
136
- $key: "totalLength",
136
+ $key: "totalSize",
137
137
  $style: () => horizontal ? {
138
- width: peek$(ctx, "totalLength")
138
+ width: peek$(ctx, "totalSize")
139
139
  } : {
140
- height: peek$(ctx, "totalLength")
140
+ height: peek$(ctx, "totalSize")
141
141
  }
142
142
  },
143
143
  containers
@@ -146,15 +146,15 @@ var Containers = React6.memo(function Containers2({
146
146
 
147
147
  // src/ListComponent.tsx
148
148
  var getComponent = (Component) => {
149
- if (React6.isValidElement(Component)) {
149
+ if (React7.isValidElement(Component)) {
150
150
  return Component;
151
151
  }
152
152
  if (Component) {
153
- return /* @__PURE__ */ React6.createElement(Component, null);
153
+ return /* @__PURE__ */ React7.createElement(Component, null);
154
154
  }
155
155
  return null;
156
156
  };
157
- var ListComponent = React6.memo(function ListComponent2({
157
+ var ListComponent = React7.memo(function ListComponent2({
158
158
  style,
159
159
  contentContainerStyle,
160
160
  horizontal,
@@ -174,7 +174,7 @@ var ListComponent = React6.memo(function ListComponent2({
174
174
  ...rest
175
175
  }) {
176
176
  const ctx = useStateContext();
177
- return /* @__PURE__ */ React6.createElement(
177
+ return /* @__PURE__ */ React7.createElement(
178
178
  ScrollView,
179
179
  {
180
180
  ...rest,
@@ -192,9 +192,9 @@ var ListComponent = React6.memo(function ListComponent2({
192
192
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
193
193
  ref: refScroller
194
194
  },
195
- alignItemsAtEnd && /* @__PURE__ */ React6.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
196
- ListHeaderComponent && /* @__PURE__ */ React6.createElement(View, { style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
197
- /* @__PURE__ */ React6.createElement(
195
+ alignItemsAtEnd && /* @__PURE__ */ React7.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
196
+ ListHeaderComponent && /* @__PURE__ */ React7.createElement(View, { style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
197
+ /* @__PURE__ */ React7.createElement(
198
198
  Containers,
199
199
  {
200
200
  horizontal,
@@ -204,402 +204,539 @@ var ListComponent = React6.memo(function ListComponent2({
204
204
  updateItemSize
205
205
  }
206
206
  ),
207
- ListFooterComponent && /* @__PURE__ */ React6.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
207
+ ListFooterComponent && /* @__PURE__ */ React7.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
208
208
  );
209
209
  });
210
210
 
211
+ // src/viewability.ts
212
+ var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new WeakMap();
213
+ function setupViewability(props) {
214
+ let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
215
+ viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || onViewableItemsChanged && [
216
+ { viewabilityConfig: viewabilityConfig || { viewAreaCoveragePercentThreshold: 0 }, onViewableItemsChanged }
217
+ ];
218
+ if (viewabilityConfigCallbackPairs) {
219
+ for (const pair of viewabilityConfigCallbackPairs) {
220
+ mapViewabilityConfigCallbackPairs.set(pair, {
221
+ viewableItems: [],
222
+ start: -1,
223
+ end: -1,
224
+ previousStart: -1,
225
+ previousEnd: -1
226
+ });
227
+ }
228
+ }
229
+ return viewabilityConfigCallbackPairs;
230
+ }
231
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, getId, scrollSize, start, end) {
232
+ for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
233
+ const viewabilityState = mapViewabilityConfigCallbackPairs.get(viewabilityConfigCallbackPair);
234
+ viewabilityState.start = start;
235
+ viewabilityState.end = end;
236
+ if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
237
+ const timer = setTimeout(() => {
238
+ state.timeouts.delete(timer);
239
+ updateViewableItemsWithConfig(state.data, viewabilityConfigCallbackPair, getId, state, ctx, scrollSize);
240
+ }, viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime);
241
+ state.timeouts.add(timer);
242
+ } else {
243
+ updateViewableItemsWithConfig(state.data, viewabilityConfigCallbackPair, getId, state, ctx, scrollSize);
244
+ }
245
+ }
246
+ }
247
+ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getId, state, ctx, scrollSize) {
248
+ var _a;
249
+ const viewabilityState = mapViewabilityConfigCallbackPairs.get(viewabilityConfigCallbackPair);
250
+ const { viewableItems: previousViewableItems, start, previousStart, end, previousEnd } = viewabilityState;
251
+ const changed = [];
252
+ if (previousViewableItems) {
253
+ for (const viewToken of previousViewableItems) {
254
+ if (viewToken.index < start || viewToken.index > end) {
255
+ viewToken.isViewable = false;
256
+ changed.push(viewToken);
257
+ }
258
+ }
259
+ }
260
+ const viewableItems = [];
261
+ for (let i = start; i <= end; i++) {
262
+ const item = data[i];
263
+ if (item) {
264
+ const key = getId(i);
265
+ if (isViewable(state, ctx, viewabilityConfigCallbackPair.viewabilityConfig, key, scrollSize)) {
266
+ const viewToken = {
267
+ item,
268
+ key,
269
+ index: i,
270
+ isViewable: true
271
+ };
272
+ viewableItems.push(viewToken);
273
+ if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
274
+ changed.push(viewToken);
275
+ }
276
+ }
277
+ }
278
+ }
279
+ Object.assign(viewabilityState, { viewableItems, previousStart: start, previousEnd: end });
280
+ if (changed.length > 0) {
281
+ (_a = viewabilityConfigCallbackPair.onViewableItemsChanged) == null ? void 0 : _a.call(viewabilityConfigCallbackPair, { viewableItems, changed });
282
+ }
283
+ }
284
+ function isViewable(state, ctx, viewabilityConfig, key, scrollSize) {
285
+ const { sizes, positions, scroll } = state;
286
+ const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
287
+ const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
288
+ const viewAreaMode = viewAreaCoveragePercentThreshold != null;
289
+ const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
290
+ const top = positions.get(key) - scroll + topPad;
291
+ const size = sizes.get(key) || 0;
292
+ const bottom = top + size;
293
+ const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
294
+ if (isEntirelyVisible) {
295
+ return true;
296
+ }
297
+ const visibleHeight = Math.min(bottom, scrollSize) - Math.max(top, 0);
298
+ const percent = 100 * (visibleHeight / (viewAreaMode ? scrollSize : size));
299
+ return percent >= viewablePercentThreshold;
300
+ }
301
+
211
302
  // src/LegendList.tsx
212
303
  var DEFAULT_SCROLL_BUFFER = 0;
213
304
  var POSITION_OUT_OF_VIEW = -1e4;
214
305
  var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
215
- return /* @__PURE__ */ React6.createElement(StateProvider, null, /* @__PURE__ */ React6.createElement(LegendListInner, { ...props, ref: forwardedRef }));
306
+ return /* @__PURE__ */ React.createElement(StateProvider, null, /* @__PURE__ */ React.createElement(LegendListInner, { ...props, ref: forwardedRef }));
216
307
  });
217
- var LegendListInner = forwardRef(
218
- function LegendListInner2(props, forwardedRef) {
219
- var _a, _b;
220
- const {
308
+ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef) {
309
+ var _a, _b;
310
+ const {
311
+ data,
312
+ initialScrollIndex,
313
+ initialScrollOffset,
314
+ horizontal,
315
+ style: styleProp,
316
+ contentContainerStyle: contentContainerStyleProp,
317
+ initialNumContainers,
318
+ drawDistance,
319
+ recycleItems = true,
320
+ onEndReachedThreshold = 0.5,
321
+ maintainScrollAtEnd = false,
322
+ maintainScrollAtEndThreshold = 0.1,
323
+ alignItemsAtEnd = false,
324
+ onScroll: onScrollProp,
325
+ keyExtractor,
326
+ renderItem,
327
+ estimatedItemSize,
328
+ getEstimatedItemSize,
329
+ onEndReached,
330
+ onViewableRangeChanged,
331
+ ...rest
332
+ } = props;
333
+ const ctx = useStateContext();
334
+ const internalRef = useRef(null);
335
+ const refScroller = internalRef;
336
+ const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_SCROLL_BUFFER;
337
+ const styleFlattened = StyleSheet.flatten(styleProp);
338
+ const style = useMemo(() => styleFlattened, [JSON.stringify(styleFlattened)]);
339
+ const contentContainerStyleFlattened = StyleSheet.flatten(contentContainerStyleProp);
340
+ const contentContainerStyle = useMemo(
341
+ () => contentContainerStyleFlattened,
342
+ [JSON.stringify(contentContainerStyleProp)]
343
+ );
344
+ const refState = useRef();
345
+ const getId = (index) => {
346
+ var _a2;
347
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
348
+ if (!data2) {
349
+ return "";
350
+ }
351
+ const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
352
+ return `${ret}`;
353
+ };
354
+ const getItemSize = (index, data2) => {
355
+ return getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
356
+ };
357
+ const calculateInitialOffset = (index = initialScrollIndex) => {
358
+ if (index) {
359
+ if (getEstimatedItemSize) {
360
+ let offset = 0;
361
+ for (let i = 0; i < index; i++) {
362
+ offset += getEstimatedItemSize(i, data[i]);
363
+ }
364
+ return offset;
365
+ }
366
+ if (estimatedItemSize) {
367
+ return index * estimatedItemSize;
368
+ }
369
+ }
370
+ return void 0;
371
+ };
372
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(calculateInitialOffset, []);
373
+ if (!refState.current) {
374
+ refState.current = {
375
+ sizes: /* @__PURE__ */ new Map(),
376
+ positions: /* @__PURE__ */ new Map(),
377
+ pendingAdjust: 0,
378
+ animFrameScroll: null,
379
+ animFrameLayout: null,
380
+ animFrameTotalSize: null,
381
+ isStartReached: false,
382
+ isEndReached: false,
383
+ isAtBottom: false,
221
384
  data,
222
- initialScrollIndex,
223
- initialScrollOffset,
224
- horizontal,
225
- style: styleProp,
226
- contentContainerStyle: contentContainerStyleProp,
227
- initialNumContainers,
228
- drawDistance,
229
- recycleItems = true,
230
- onEndReachedThreshold = 0.5,
231
- maintainScrollAtEnd = false,
232
- maintainScrollAtEndThreshold = 0.1,
233
- alignItemsAtEnd = false,
234
- onScroll: onScrollProp,
235
- keyExtractor,
236
- renderItem,
237
- estimatedItemSize,
238
- getEstimatedItemSize,
239
- onEndReached,
240
- onViewableRangeChanged,
241
- ...rest
242
- } = props;
243
- const ctx = useStateContext();
244
- const internalRef = useRef(null);
245
- const refScroller = forwardedRef || internalRef;
246
- const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_SCROLL_BUFFER;
247
- const styleFlattened = StyleSheet.flatten(styleProp);
248
- const style = useMemo(() => styleFlattened, [JSON.stringify(styleProp)]);
249
- const contentContainerStyleFlattened = StyleSheet.flatten(contentContainerStyleProp);
250
- const contentContainerStyle = useMemo(
251
- () => contentContainerStyleFlattened,
252
- [JSON.stringify(contentContainerStyleProp)]
253
- );
254
- const refState = useRef();
255
- const getId = (index) => {
385
+ idsInFirstRender: void 0,
386
+ hasScrolled: false,
387
+ scrollLength: Dimensions.get("window")[horizontal ? "width" : "height"],
388
+ startBuffered: 0,
389
+ startNoBuffer: 0,
390
+ endBuffered: 0,
391
+ endNoBuffer: 0,
392
+ scroll: initialContentOffset || 0,
393
+ totalSize: 0,
394
+ timeouts: /* @__PURE__ */ new Set(),
395
+ viewabilityConfigCallbackPairs: void 0
396
+ };
397
+ refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
398
+ }
399
+ refState.current.data = data;
400
+ set$(ctx, "numItems", data.length);
401
+ set$(ctx, "stylePaddingTop", (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
402
+ const addTotalSize = (add) => {
403
+ const prev = refState.current.totalSize;
404
+ refState.current.totalSize += add;
405
+ const totalSize = refState.current.totalSize;
406
+ const doAdd = () => {
407
+ refState.current.animFrameTotalSize = null;
408
+ set$(ctx, "totalSize", totalSize);
409
+ const screenLength = refState.current.scrollLength;
410
+ if (alignItemsAtEnd) {
411
+ const listPaddingTop = peek$(ctx, "stylePaddingTop");
412
+ set$(ctx, "paddingTop", Math.max(0, screenLength - totalSize - listPaddingTop));
413
+ }
414
+ };
415
+ if (!prev) {
416
+ doAdd();
417
+ } else if (!refState.current.animFrameTotalSize) {
418
+ refState.current.animFrameTotalSize = requestAnimationFrame(doAdd);
419
+ }
420
+ };
421
+ const getRenderedItem = useCallback(
422
+ (index) => {
256
423
  var _a2;
257
424
  const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
258
425
  if (!data2) {
259
- return "";
426
+ return null;
260
427
  }
261
- const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
262
- return ret + "";
263
- };
264
- const getItemLength = (index, data2) => {
265
- return getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
266
- };
267
- const calculateInitialOffset = () => {
268
- if (initialScrollIndex) {
269
- if (getEstimatedItemSize) {
270
- let offset = 0;
271
- for (let i = 0; i < initialScrollIndex; i++) {
272
- offset += getEstimatedItemSize(i, data[i]);
273
- }
274
- return offset;
275
- } else if (estimatedItemSize) {
276
- return initialScrollIndex * estimatedItemSize;
277
- }
428
+ const renderedItem = renderItem == null ? void 0 : renderItem({
429
+ item: data2[index],
430
+ index
431
+ });
432
+ return renderedItem;
433
+ },
434
+ [renderItem]
435
+ );
436
+ const calculateItemsInView = useCallback(() => {
437
+ unstable_batchedUpdates(() => {
438
+ var _a2, _b2, _c;
439
+ const {
440
+ data: data2,
441
+ scrollLength,
442
+ scroll: scrollState,
443
+ startNoBuffer: startNoBufferState,
444
+ startBuffered: startBufferedState,
445
+ endNoBuffer: endNoBufferState,
446
+ endBuffered: endBufferedState
447
+ } = refState.current;
448
+ if (!data2) {
449
+ return;
278
450
  }
279
- return void 0;
280
- };
281
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(calculateInitialOffset, [initialScrollIndex, estimatedItemSize]);
282
- if (!refState.current) {
283
- refState.current = {
284
- lengths: /* @__PURE__ */ new Map(),
285
- positions: /* @__PURE__ */ new Map(),
286
- pendingAdjust: 0,
287
- animFrameScroll: null,
288
- animFrameLayout: null,
289
- animFrameTotalSize: null,
290
- isStartReached: false,
291
- isEndReached: false,
292
- isAtBottom: false,
293
- data,
294
- idsInFirstRender: void 0,
295
- hasScrolled: false,
296
- scrollLength: Dimensions.get("window")[horizontal ? "width" : "height"],
297
- startBuffered: 0,
298
- startNoBuffer: 0,
299
- endBuffered: 0,
300
- endNoBuffer: 0,
301
- scroll: initialContentOffset || 0,
302
- totalSize: 0
303
- };
304
- refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
305
- }
306
- refState.current.data = data;
307
- set$(ctx, `numItems`, data.length);
308
- set$(ctx, `stylePaddingTop`, (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
309
- const addTotalSize = (add) => {
310
- const prev = refState.current.totalSize;
311
- const length = refState.current.totalSize += add;
312
- const doAdd = () => {
313
- refState.current.animFrameTotalSize = null;
314
- set$(ctx, `totalLength`, length);
315
- const screenLength = refState.current.scrollLength;
316
- if (alignItemsAtEnd) {
317
- const listPaddingTop = peek$(ctx, `stylePaddingTop`);
318
- set$(ctx, `paddingTop`, Math.max(0, screenLength - length - listPaddingTop));
451
+ const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
452
+ const scroll = scrollState - topPad;
453
+ const { sizes, positions } = refState.current;
454
+ let startNoBuffer = null;
455
+ let startBuffered = null;
456
+ let endNoBuffer = null;
457
+ let endBuffered = null;
458
+ let loopStart = startBufferedState || 0;
459
+ if (startBufferedState) {
460
+ for (let i = startBufferedState; i >= 0; i--) {
461
+ const id = getId(i);
462
+ const top2 = positions.get(id);
463
+ if (top2 !== void 0) {
464
+ const size = (_a2 = sizes.get(id)) != null ? _a2 : getItemSize(i, data2[i]);
465
+ const bottom = top2 + size;
466
+ if (bottom > scroll - scrollBuffer) {
467
+ loopStart = i;
468
+ } else {
469
+ break;
470
+ }
471
+ }
319
472
  }
320
- };
321
- if (!prev) {
322
- doAdd();
323
- } else if (!refState.current.animFrameTotalSize) {
324
- refState.current.animFrameTotalSize = requestAnimationFrame(doAdd);
325
473
  }
326
- };
327
- const allocateContainers = useCallback(() => {
328
- const scrollLength = refState.current.scrollLength;
329
- const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
330
- const numContainers = initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) + 4;
331
- for (let i = 0; i < numContainers; i++) {
332
- set$(ctx, `containerIndex${i}`, -1);
333
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
334
- }
335
- set$(ctx, `numContainers`, numContainers);
336
- }, []);
337
- const getRenderedItem = useCallback(
338
- (index) => {
339
- var _a2;
340
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
341
- if (!data2) {
342
- return null;
474
+ let top = loopStart > 0 ? positions.get(getId(loopStart)) : 0;
475
+ for (let i = loopStart; i < data2.length; i++) {
476
+ const id = getId(i);
477
+ const size = (_b2 = sizes.get(id)) != null ? _b2 : getItemSize(i, data2[i]);
478
+ if (positions.get(id) !== top) {
479
+ positions.set(id, top);
343
480
  }
344
- const renderedItem = renderItem == null ? void 0 : renderItem({
345
- item: data2[index],
346
- index
347
- });
348
- return renderedItem;
349
- },
350
- [renderItem]
351
- );
352
- const calculateItemsInView = useCallback(() => {
353
- unstable_batchedUpdates(() => {
354
- var _a2, _b2, _c;
355
- const {
356
- data: data2,
357
- scrollLength,
358
- scroll: scrollState,
359
- startNoBuffer: startNoBufferState,
360
- startBuffered: startBufferedState,
361
- endNoBuffer: endNoBufferState,
362
- endBuffered: endBufferedState
363
- } = refState.current;
364
- if (!data2) {
365
- return;
481
+ if (startNoBuffer === null && top + size > scroll) {
482
+ startNoBuffer = i;
366
483
  }
367
- const topPad = (peek$(ctx, `stylePaddingTop`) || 0) + (peek$(ctx, `headerSize`) || 0);
368
- const scroll = scrollState - topPad;
369
- const { lengths, positions } = refState.current;
370
- let startNoBuffer = null;
371
- let startBuffered = null;
372
- let endNoBuffer = null;
373
- let endBuffered = null;
374
- let loopStart = startBufferedState || 0;
375
- if (startBufferedState) {
376
- for (let i = startBufferedState; i >= 0; i--) {
377
- const id = getId(i);
378
- const top2 = positions.get(id);
379
- if (top2 !== void 0) {
380
- const length = (_a2 = lengths.get(id)) != null ? _a2 : getItemLength(i, data2[i]);
381
- const bottom = top2 + length;
382
- if (bottom > scroll - scrollBuffer) {
383
- loopStart = i;
384
- } else {
385
- break;
386
- }
387
- }
388
- }
484
+ if (startBuffered === null && top + size > scroll - scrollBuffer) {
485
+ startBuffered = i;
389
486
  }
390
- let top = loopStart > 0 ? positions.get(getId(loopStart)) : 0;
391
- for (let i = loopStart; i < data2.length; i++) {
392
- const id = getId(i);
393
- const length = (_b2 = lengths.get(id)) != null ? _b2 : getItemLength(i, data2[i]);
394
- if (positions.get(id) !== top) {
395
- positions.set(id, top);
396
- }
397
- if (startNoBuffer === null && top + length > scroll) {
398
- startNoBuffer = i;
487
+ if (startNoBuffer !== null) {
488
+ if (top <= scroll + scrollLength) {
489
+ endNoBuffer = i;
399
490
  }
400
- if (startBuffered === null && top + length > scroll - scrollBuffer) {
401
- startBuffered = i;
491
+ if (top <= scroll + scrollLength + scrollBuffer) {
492
+ endBuffered = i;
493
+ } else {
494
+ break;
402
495
  }
403
- if (startNoBuffer !== null) {
404
- if (top <= scroll + scrollLength) {
405
- endNoBuffer = i;
406
- }
407
- if (top <= scroll + scrollLength + scrollBuffer) {
408
- endBuffered = i;
409
- } else {
496
+ }
497
+ top += size;
498
+ }
499
+ Object.assign(refState.current, {
500
+ startBuffered,
501
+ startNoBuffer,
502
+ endBuffered,
503
+ endNoBuffer
504
+ });
505
+ if (startBuffered !== null && endBuffered !== null) {
506
+ const prevNumContainers = ctx.values.get("numContainers");
507
+ let numContainers = prevNumContainers;
508
+ for (let i = startBuffered; i <= endBuffered; i++) {
509
+ let isContained = false;
510
+ for (let j = 0; j < numContainers; j++) {
511
+ const index = peek$(ctx, `containerIndex${j}`);
512
+ if (index === i) {
513
+ isContained = true;
410
514
  break;
411
515
  }
412
516
  }
413
- top += length;
414
- }
415
- Object.assign(refState.current, {
416
- startBuffered,
417
- startNoBuffer,
418
- endBuffered,
419
- endNoBuffer
420
- });
421
- if (startBuffered !== null && endBuffered !== null) {
422
- const prevNumContainers = ctx.values.get("numContainers");
423
- let numContainers = prevNumContainers;
424
- for (let i = startBuffered; i <= endBuffered; i++) {
425
- let isContained = false;
426
- for (let j = 0; j < numContainers; j++) {
427
- const index = peek$(ctx, `containerIndex${j}`);
428
- if (index === i) {
429
- isContained = true;
517
+ if (!isContained) {
518
+ const id = getId(i);
519
+ const top2 = positions.get(id) || 0;
520
+ let furthestIndex = -1;
521
+ let furthestDistance = 0;
522
+ for (let u = 0; u < numContainers; u++) {
523
+ const index = peek$(ctx, `containerIndex${u}`);
524
+ if (index < 0) {
525
+ furthestIndex = u;
430
526
  break;
431
527
  }
432
- }
433
- if (!isContained) {
434
- let didRecycle = false;
435
- for (let u = 0; u < numContainers; u++) {
436
- const index = peek$(ctx, `containerIndex${u}`);
437
- if (index < startBuffered || index > endBuffered) {
438
- set$(ctx, `containerIndex${u}`, i);
439
- didRecycle = true;
440
- break;
528
+ const pos = peek$(ctx, `containerPosition${u}`);
529
+ if (index < startBuffered || index > endBuffered) {
530
+ const distance = Math.abs(pos - top2);
531
+ if (index < 0 || distance > furthestDistance) {
532
+ furthestDistance = distance;
533
+ furthestIndex = u;
441
534
  }
442
535
  }
443
- if (!didRecycle) {
444
- if (__DEV__) {
445
- console.warn(
446
- "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemSize",
447
- i
448
- );
449
- }
450
- const id = numContainers;
451
- numContainers++;
452
- set$(ctx, `containerIndex${id}`, i);
453
- set$(ctx, `containerPosition${id}`, POSITION_OUT_OF_VIEW);
536
+ }
537
+ if (furthestIndex >= 0) {
538
+ set$(ctx, `containerIndex${furthestIndex}`, i);
539
+ } else {
540
+ if (__DEV__) {
541
+ console.warn(
542
+ "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemSize",
543
+ i
544
+ );
454
545
  }
546
+ const containerId = numContainers;
547
+ numContainers++;
548
+ set$(ctx, `containerIndex${containerId}`, i);
549
+ set$(ctx, `containerPosition${containerId}`, POSITION_OUT_OF_VIEW);
455
550
  }
456
551
  }
457
- if (numContainers !== prevNumContainers) {
458
- set$(ctx, `numContainers`, numContainers);
459
- }
460
- for (let i = 0; i < numContainers; i++) {
461
- const itemIndex = peek$(ctx, `containerIndex${i}`);
462
- const item = data2[itemIndex];
463
- if (item) {
464
- const id = getId(itemIndex);
465
- if (itemIndex < startBuffered || itemIndex > endBuffered) {
466
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
467
- } else {
468
- const pos = (_c = positions.get(id)) != null ? _c : -1;
469
- const prevPos = peek$(ctx, `containerPosition${i}`);
470
- if (pos >= 0 && pos !== prevPos) {
471
- set$(ctx, `containerPosition${i}`, pos);
472
- }
552
+ }
553
+ if (numContainers !== prevNumContainers) {
554
+ set$(ctx, "numContainers", numContainers);
555
+ }
556
+ for (let i = 0; i < numContainers; i++) {
557
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
558
+ const item = data2[itemIndex];
559
+ if (item) {
560
+ const id = getId(itemIndex);
561
+ if (itemIndex < startBuffered || itemIndex > endBuffered) {
562
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
563
+ } else {
564
+ const pos = (_c = positions.get(id)) != null ? _c : -1;
565
+ const prevPos = peek$(ctx, `containerPosition${i}`);
566
+ if (pos >= 0 && pos !== prevPos) {
567
+ set$(ctx, `containerPosition${i}`, pos);
473
568
  }
474
569
  }
475
570
  }
476
- if (onViewableRangeChanged) {
477
- if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
478
- onViewableRangeChanged({
479
- start: startNoBuffer,
480
- startBuffered,
481
- end: endNoBuffer,
482
- endBuffered,
483
- items: data2.slice(startNoBuffer, endNoBuffer + 1)
484
- });
485
- }
571
+ }
572
+ if (onViewableRangeChanged) {
573
+ if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
574
+ onViewableRangeChanged({
575
+ start: startNoBuffer,
576
+ startBuffered,
577
+ end: endNoBuffer,
578
+ endBuffered,
579
+ items: data2.slice(startNoBuffer, endNoBuffer + 1)
580
+ });
486
581
  }
487
582
  }
488
- });
489
- }, [data]);
490
- useMemo(() => {
491
- var _a2, _b2;
492
- allocateContainers();
493
- calculateItemsInView();
494
- const lengths = (_a2 = refState.current) == null ? void 0 : _a2.lengths;
495
- let totalLength = 0;
496
- for (let i = 0; i < data.length; i++) {
497
- const id = getId(i);
498
- totalLength += (_b2 = lengths.get(id)) != null ? _b2 : getItemLength(i, data[i]);
499
- }
500
- addTotalSize(totalLength);
501
- }, []);
502
- const checkAtBottom = () => {
503
- var _a2;
504
- const { scrollLength, scroll } = refState.current;
505
- const totalLength = peek$(ctx, "totalLength");
506
- const distanceFromEnd = totalLength - scroll - scrollLength;
507
- if (refState.current) {
508
- refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
583
+ updateViewableItems(
584
+ refState.current,
585
+ ctx,
586
+ refState.current.viewabilityConfigCallbackPairs,
587
+ getId,
588
+ scrollLength,
589
+ startNoBuffer,
590
+ endNoBuffer
591
+ );
509
592
  }
510
- if (onEndReached && !((_a2 = refState.current) == null ? void 0 : _a2.isEndReached)) {
511
- if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
512
- if (refState.current) {
513
- refState.current.isEndReached = true;
514
- }
515
- onEndReached({ distanceFromEnd });
593
+ });
594
+ }, [data]);
595
+ useMemo(() => {
596
+ var _a2, _b2;
597
+ refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
598
+ const scrollLength = refState.current.scrollLength;
599
+ const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
600
+ const numContainers = initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) + 4;
601
+ for (let i = 0; i < numContainers; i++) {
602
+ set$(ctx, `containerIndex${i}`, -1);
603
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
604
+ }
605
+ set$(ctx, "numContainers", numContainers);
606
+ calculateItemsInView();
607
+ const sizes = (_a2 = refState.current) == null ? void 0 : _a2.sizes;
608
+ let totalSize = 0;
609
+ for (let i = 0; i < data.length; i++) {
610
+ const id = getId(i);
611
+ totalSize += (_b2 = sizes.get(id)) != null ? _b2 : getItemSize(i, data[i]);
612
+ }
613
+ addTotalSize(totalSize);
614
+ }, []);
615
+ const checkAtBottom = () => {
616
+ var _a2;
617
+ const { scrollLength, scroll } = refState.current;
618
+ const totalSize = peek$(ctx, "totalSize");
619
+ const distanceFromEnd = totalSize - scroll - scrollLength;
620
+ if (refState.current) {
621
+ refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
622
+ }
623
+ if (onEndReached && !((_a2 = refState.current) == null ? void 0 : _a2.isEndReached)) {
624
+ if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
625
+ if (refState.current) {
626
+ refState.current.isEndReached = true;
516
627
  }
628
+ onEndReached({ distanceFromEnd });
517
629
  }
518
- };
519
- useMemo(() => {
520
- if (refState.current) {
521
- refState.current.isEndReached = false;
630
+ }
631
+ };
632
+ useEffect(() => {
633
+ if (refState.current) {
634
+ refState.current.isEndReached = false;
635
+ }
636
+ calculateItemsInView();
637
+ checkAtBottom();
638
+ }, [data]);
639
+ const updateItemSize = useCallback((index, size) => {
640
+ var _a2, _b2, _c, _d;
641
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
642
+ if (!data2) {
643
+ return;
644
+ }
645
+ const sizes = (_b2 = refState.current) == null ? void 0 : _b2.sizes;
646
+ const id = getId(index);
647
+ const wasInFirstRender = (_c = refState.current) == null ? void 0 : _c.idsInFirstRender.has(id);
648
+ const prevSize = sizes.get(id) || (wasInFirstRender ? getItemSize(index, data2[index]) : 0);
649
+ if (!prevSize || Math.abs(prevSize - size) > 0.5) {
650
+ sizes.set(id, size);
651
+ addTotalSize(size - prevSize);
652
+ if (((_d = refState.current) == null ? void 0 : _d.isAtBottom) && maintainScrollAtEnd) {
653
+ requestAnimationFrame(() => {
654
+ var _a3;
655
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
656
+ animated: true
657
+ });
658
+ });
522
659
  }
523
- calculateItemsInView();
524
- checkAtBottom();
525
- }, [data]);
526
- const updateItemSize = useCallback((index, length) => {
527
- var _a2, _b2, _c, _d;
528
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
529
- if (!data2) {
660
+ const state = refState.current;
661
+ if (!state.animFrameScroll && !state.animFrameLayout) {
662
+ state.animFrameLayout = requestAnimationFrame(() => {
663
+ state.animFrameLayout = null;
664
+ calculateItemsInView();
665
+ });
666
+ }
667
+ }
668
+ }, []);
669
+ const handleScrollDebounced = useCallback(() => {
670
+ calculateItemsInView();
671
+ checkAtBottom();
672
+ if (refState.current) {
673
+ refState.current.animFrameScroll = null;
674
+ }
675
+ }, []);
676
+ const onLayout = useCallback((event) => {
677
+ const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
678
+ refState.current.scrollLength = scrollLength;
679
+ }, []);
680
+ const handleScroll = useCallback(
681
+ (event, fromSelf) => {
682
+ var _a2, _b2, _c;
683
+ 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) {
530
684
  return;
531
685
  }
532
- const lengths = (_b2 = refState.current) == null ? void 0 : _b2.lengths;
533
- const id = getId(index);
534
- const wasInFirstRender = (_c = refState.current) == null ? void 0 : _c.idsInFirstRender.has(id);
535
- const prevLength = lengths.get(id) || (wasInFirstRender ? getItemLength(index, data2[index]) : 0);
536
- if (!prevLength || Math.abs(prevLength - length) > 0.5) {
537
- lengths.set(id, length);
538
- addTotalSize(length - prevLength);
539
- if (((_d = refState.current) == null ? void 0 : _d.isAtBottom) && maintainScrollAtEnd) {
540
- requestAnimationFrame(() => {
541
- var _a3;
542
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
543
- animated: true
544
- });
545
- });
546
- }
547
- const state = refState.current;
548
- if (!state.animFrameScroll && !state.animFrameLayout) {
549
- state.animFrameLayout = requestAnimationFrame(() => {
550
- state.animFrameLayout = null;
551
- calculateItemsInView();
552
- });
553
- }
686
+ refState.current.hasScrolled = true;
687
+ const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
688
+ refState.current.scroll = newScroll;
689
+ if (refState.current && !refState.current.animFrameScroll) {
690
+ refState.current.animFrameScroll = requestAnimationFrame(handleScrollDebounced);
554
691
  }
555
- }, []);
556
- const handleScrollDebounced = useCallback(() => {
557
- calculateItemsInView();
558
- checkAtBottom();
559
- if (refState.current) {
560
- refState.current.animFrameScroll = null;
692
+ if (!fromSelf) {
693
+ onScrollProp == null ? void 0 : onScrollProp(event);
561
694
  }
562
- }, []);
563
- const onLayout = useCallback((event) => {
564
- const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
565
- refState.current.scrollLength = scrollLength;
566
- }, []);
567
- const handleScroll = useCallback(
568
- (event, fromSelf) => {
569
- var _a2, _b2, _c;
570
- 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) {
571
- return;
572
- }
573
- refState.current.hasScrolled = true;
574
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
575
- refState.current.scroll = newScroll;
576
- if (refState.current && !refState.current.animFrameScroll) {
577
- refState.current.animFrameScroll = requestAnimationFrame(handleScrollDebounced);
578
- }
579
- if (!fromSelf) {
580
- onScrollProp == null ? void 0 : onScrollProp(event);
695
+ },
696
+ []
697
+ );
698
+ useImperativeHandle(forwardedRef, () => {
699
+ const scrollToIndex = ({ index, animated }) => {
700
+ const offsetObj = calculateInitialOffset(index);
701
+ const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
702
+ refScroller.current.scrollTo({ ...offset, animated });
703
+ };
704
+ return {
705
+ getNativeScrollRef: () => refScroller.current,
706
+ getScrollableNode: refScroller.current.getScrollableNode,
707
+ getScrollResponder: refScroller.current.getScrollResponder,
708
+ flashScrollIndicators: refScroller.current.flashScrollIndicators,
709
+ scrollToIndex,
710
+ scrollToOffset: ({ offset, animated }) => {
711
+ const offsetObj = horizontal ? { x: offset, y: 0 } : { x: 0, y: offset };
712
+ refScroller.current.scrollTo({ ...offsetObj, animated });
713
+ },
714
+ scrollToItem: ({ item, animated }) => {
715
+ const index = data.indexOf(item);
716
+ if (index !== -1) {
717
+ scrollToIndex({ index, animated });
581
718
  }
582
719
  },
583
- []
584
- );
585
- return /* @__PURE__ */ React6.createElement(
586
- ListComponent,
587
- {
588
- ...rest,
589
- contentContainerStyle,
590
- style,
591
- horizontal,
592
- refScroller,
593
- initialContentOffset,
594
- getRenderedItem,
595
- updateItemSize,
596
- handleScroll,
597
- onLayout,
598
- recycleItems,
599
- alignItemsAtEnd
600
- }
601
- );
602
- }
603
- );
720
+ scrollToEnd: refScroller.current.scrollToEnd
721
+ };
722
+ }, []);
723
+ return /* @__PURE__ */ React.createElement(
724
+ ListComponent,
725
+ {
726
+ ...rest,
727
+ contentContainerStyle,
728
+ style,
729
+ horizontal,
730
+ refScroller,
731
+ initialContentOffset,
732
+ getRenderedItem,
733
+ updateItemSize,
734
+ handleScroll,
735
+ onLayout,
736
+ recycleItems,
737
+ alignItemsAtEnd
738
+ }
739
+ );
740
+ });
604
741
 
605
742
  export { LegendList };