@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.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 = Math.round(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,391 +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 {
221
- 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) => {
256
- var _a2;
257
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
258
- if (!data2) {
259
- return "";
260
- }
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;
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]);
277
363
  }
364
+ return offset;
365
+ }
366
+ if (estimatedItemSize) {
367
+ return index * estimatedItemSize;
278
368
  }
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
- isStartReached: false,
290
- isEndReached: false,
291
- isAtBottom: false,
292
- data,
293
- idsInFirstRender: void 0,
294
- hasScrolled: false,
295
- scrollLength: Dimensions.get("window")[horizontal ? "width" : "height"],
296
- startBuffered: 0,
297
- startNoBuffer: 0,
298
- endBuffered: 0,
299
- endNoBuffer: 0,
300
- scroll: initialContentOffset || 0
301
- };
302
- refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
303
369
  }
304
- refState.current.data = data;
305
- set$(ctx, `numItems`, data.length);
306
- set$(ctx, `stylePaddingTop`, (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
307
- const addTotalSize = (add) => {
308
- const length = (peek$(ctx, `totalLength`) || 0) + add;
309
- set$(ctx, `totalLength`, length);
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,
384
+ data,
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);
310
409
  const screenLength = refState.current.scrollLength;
311
410
  if (alignItemsAtEnd) {
312
- const listPaddingTop = peek$(ctx, `stylePaddingTop`);
313
- set$(ctx, `paddingTop`, Math.max(0, screenLength - length - listPaddingTop));
411
+ const listPaddingTop = peek$(ctx, "stylePaddingTop");
412
+ set$(ctx, "paddingTop", Math.max(0, screenLength - totalSize - listPaddingTop));
314
413
  }
315
414
  };
316
- const allocateContainers = useCallback(() => {
317
- const scrollLength = refState.current.scrollLength;
318
- const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
319
- const numContainers = initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) + 4;
320
- for (let i = 0; i < numContainers; i++) {
321
- set$(ctx, `containerIndex${i}`, -1);
322
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
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) => {
423
+ var _a2;
424
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
425
+ if (!data2) {
426
+ return null;
323
427
  }
324
- set$(ctx, `numContainers`, numContainers);
325
- }, []);
326
- const getRenderedItem = useCallback(
327
- (index) => {
328
- var _a2;
329
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
330
- if (!data2) {
331
- return null;
332
- }
333
- const renderedItem = renderItem == null ? void 0 : renderItem({
334
- item: data2[index],
335
- index
336
- });
337
- return renderedItem;
338
- },
339
- [renderItem]
340
- );
341
- const calculateItemsInView = useCallback(() => {
342
- unstable_batchedUpdates(() => {
343
- var _a2, _b2, _c;
344
- const {
345
- data: data2,
346
- scrollLength,
347
- scroll: scrollState,
348
- startNoBuffer: startNoBufferState,
349
- startBuffered: startBufferedState,
350
- endNoBuffer: endNoBufferState,
351
- endBuffered: endBufferedState
352
- } = refState.current;
353
- if (!data2) {
354
- return;
355
- }
356
- const topPad = (peek$(ctx, `stylePaddingTop`) || 0) + (peek$(ctx, `headerSize`) || 0);
357
- const scroll = scrollState - topPad;
358
- const { lengths, positions } = refState.current;
359
- let startNoBuffer = null;
360
- let startBuffered = null;
361
- let endNoBuffer = null;
362
- let endBuffered = null;
363
- let loopStart = startBufferedState || 0;
364
- if (startBufferedState) {
365
- for (let i = startBufferedState; i >= 0; i--) {
366
- const id = getId(i);
367
- const top2 = positions.get(id);
368
- if (top2 !== void 0) {
369
- const length = (_a2 = lengths.get(id)) != null ? _a2 : getItemLength(i, data2[i]);
370
- const bottom = top2 + length;
371
- if (bottom > scroll - scrollBuffer) {
372
- loopStart = i;
373
- } else {
374
- break;
375
- }
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;
450
+ }
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;
376
470
  }
377
471
  }
378
472
  }
379
- let top = loopStart > 0 ? positions.get(getId(loopStart)) : 0;
380
- for (let i = loopStart; i < data2.length; i++) {
381
- const id = getId(i);
382
- const length = (_b2 = lengths.get(id)) != null ? _b2 : getItemLength(i, data2[i]);
383
- if (positions.get(id) !== top) {
384
- positions.set(id, top);
385
- }
386
- if (startNoBuffer === null && top + length > scroll) {
387
- startNoBuffer = i;
473
+ }
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);
480
+ }
481
+ if (startNoBuffer === null && top + size > scroll) {
482
+ startNoBuffer = i;
483
+ }
484
+ if (startBuffered === null && top + size > scroll - scrollBuffer) {
485
+ startBuffered = i;
486
+ }
487
+ if (startNoBuffer !== null) {
488
+ if (top <= scroll + scrollLength) {
489
+ endNoBuffer = i;
388
490
  }
389
- if (startBuffered === null && top + length > scroll - scrollBuffer) {
390
- startBuffered = i;
491
+ if (top <= scroll + scrollLength + scrollBuffer) {
492
+ endBuffered = i;
493
+ } else {
494
+ break;
391
495
  }
392
- if (startNoBuffer !== null) {
393
- if (top <= scroll + scrollLength) {
394
- endNoBuffer = i;
395
- }
396
- if (top <= scroll + scrollLength + scrollBuffer) {
397
- endBuffered = i;
398
- } 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;
399
514
  break;
400
515
  }
401
516
  }
402
- top += length;
403
- }
404
- Object.assign(refState.current, {
405
- startBuffered,
406
- startNoBuffer,
407
- endBuffered,
408
- endNoBuffer
409
- });
410
- if (startBuffered !== null && endBuffered !== null) {
411
- const prevNumContainers = ctx.values.get("numContainers");
412
- let numContainers = prevNumContainers;
413
- for (let i = startBuffered; i <= endBuffered; i++) {
414
- let isContained = false;
415
- for (let j = 0; j < numContainers; j++) {
416
- const index = peek$(ctx, `containerIndex${j}`);
417
- if (index === i) {
418
- 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;
419
526
  break;
420
527
  }
421
- }
422
- if (!isContained) {
423
- let didRecycle = false;
424
- for (let u = 0; u < numContainers; u++) {
425
- const index = peek$(ctx, `containerIndex${u}`);
426
- if (index < startBuffered || index > endBuffered) {
427
- set$(ctx, `containerIndex${u}`, i);
428
- didRecycle = true;
429
- 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;
430
534
  }
431
535
  }
432
- if (!didRecycle) {
433
- if (__DEV__) {
434
- console.warn(
435
- "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemSize",
436
- i
437
- );
438
- }
439
- const id = numContainers;
440
- numContainers++;
441
- set$(ctx, `containerIndex${id}`, i);
442
- 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
+ );
443
545
  }
546
+ const containerId = numContainers;
547
+ numContainers++;
548
+ set$(ctx, `containerIndex${containerId}`, i);
549
+ set$(ctx, `containerPosition${containerId}`, POSITION_OUT_OF_VIEW);
444
550
  }
445
551
  }
446
- if (numContainers !== prevNumContainers) {
447
- set$(ctx, `numContainers`, numContainers);
448
- }
449
- for (let i = 0; i < numContainers; i++) {
450
- const itemIndex = peek$(ctx, `containerIndex${i}`);
451
- const item = data2[itemIndex];
452
- if (item) {
453
- const id = getId(itemIndex);
454
- if (itemIndex < startBuffered || itemIndex > endBuffered) {
455
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
456
- } else {
457
- const pos = (_c = positions.get(id)) != null ? _c : -1;
458
- const prevPos = peek$(ctx, `containerPosition${i}`);
459
- if (pos >= 0 && pos !== prevPos) {
460
- set$(ctx, `containerPosition${i}`, pos);
461
- }
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);
462
568
  }
463
569
  }
464
570
  }
465
- if (onViewableRangeChanged) {
466
- if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
467
- onViewableRangeChanged({
468
- start: startNoBuffer,
469
- startBuffered,
470
- end: endNoBuffer,
471
- endBuffered,
472
- items: data2.slice(startNoBuffer, endNoBuffer + 1)
473
- });
474
- }
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
+ });
475
581
  }
476
582
  }
477
- });
478
- }, [data]);
479
- useMemo(() => {
480
- var _a2, _b2;
481
- allocateContainers();
482
- calculateItemsInView();
483
- const lengths = (_a2 = refState.current) == null ? void 0 : _a2.lengths;
484
- let totalLength = 0;
485
- for (let i = 0; i < data.length; i++) {
486
- const id = getId(i);
487
- totalLength += (_b2 = lengths.get(id)) != null ? _b2 : getItemLength(i, data[i]);
488
- }
489
- addTotalSize(totalLength);
490
- }, []);
491
- const checkAtBottom = () => {
492
- var _a2;
493
- const { scrollLength, scroll } = refState.current;
494
- const totalLength = peek$(ctx, "totalLength");
495
- const distanceFromEnd = totalLength - scroll - scrollLength;
496
- if (refState.current) {
497
- 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
+ );
498
592
  }
499
- if (onEndReached && !((_a2 = refState.current) == null ? void 0 : _a2.isEndReached)) {
500
- if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
501
- if (refState.current) {
502
- refState.current.isEndReached = true;
503
- }
504
- 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;
505
627
  }
628
+ onEndReached({ distanceFromEnd });
506
629
  }
507
- };
508
- useMemo(() => {
509
- if (refState.current) {
510
- 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
+ });
511
659
  }
512
- calculateItemsInView();
513
- checkAtBottom();
514
- }, [data]);
515
- const updateItemSize = useCallback((index, length) => {
516
- var _a2, _b2, _c, _d;
517
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
518
- 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) {
519
684
  return;
520
685
  }
521
- const lengths = (_b2 = refState.current) == null ? void 0 : _b2.lengths;
522
- const id = getId(index);
523
- const wasInFirstRender = (_c = refState.current) == null ? void 0 : _c.idsInFirstRender.has(id);
524
- const prevLength = lengths.get(id) || (wasInFirstRender ? getItemLength(index, data2[index]) : 0);
525
- if (!prevLength || prevLength !== length) {
526
- lengths.set(id, length);
527
- addTotalSize(length - prevLength);
528
- if (((_d = refState.current) == null ? void 0 : _d.isAtBottom) && maintainScrollAtEnd) {
529
- requestAnimationFrame(() => {
530
- var _a3;
531
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
532
- animated: true
533
- });
534
- });
535
- }
536
- const state = refState.current;
537
- if (!state.animFrameScroll && !state.animFrameLayout) {
538
- state.animFrameLayout = requestAnimationFrame(() => {
539
- state.animFrameLayout = null;
540
- calculateItemsInView();
541
- });
542
- }
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);
543
691
  }
544
- }, []);
545
- const handleScrollDebounced = useCallback(() => {
546
- calculateItemsInView();
547
- checkAtBottom();
548
- if (refState.current) {
549
- refState.current.animFrameScroll = null;
692
+ if (!fromSelf) {
693
+ onScrollProp == null ? void 0 : onScrollProp(event);
550
694
  }
551
- }, []);
552
- const onLayout = useCallback((event) => {
553
- const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
554
- refState.current.scrollLength = scrollLength;
555
- }, []);
556
- const handleScroll = useCallback(
557
- (event, fromSelf) => {
558
- var _a2, _b2, _c;
559
- 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) {
560
- return;
561
- }
562
- refState.current.hasScrolled = true;
563
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
564
- refState.current.scroll = newScroll;
565
- if (refState.current && !refState.current.animFrameScroll) {
566
- refState.current.animFrameScroll = requestAnimationFrame(handleScrollDebounced);
567
- }
568
- if (!fromSelf) {
569
- 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 });
570
718
  }
571
719
  },
572
- []
573
- );
574
- return /* @__PURE__ */ React6.createElement(
575
- ListComponent,
576
- {
577
- ...rest,
578
- contentContainerStyle,
579
- style,
580
- horizontal,
581
- refScroller,
582
- initialContentOffset,
583
- getRenderedItem,
584
- updateItemSize,
585
- handleScroll,
586
- onLayout,
587
- recycleItems,
588
- alignItemsAtEnd
589
- }
590
- );
591
- }
592
- );
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
+ });
593
741
 
594
742
  export { LegendList };