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