@legendapp/list 1.0.0-beta.3 → 1.0.0-beta.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -1,22 +1,23 @@
1
- import * as React5 from 'react';
2
- import React5__default, { createContext, useMemo, forwardRef, useRef, useCallback, useEffect, useImperativeHandle, useSyncExternalStore, useContext, useState, useLayoutEffect } from 'react';
3
- import { Animated, ScrollView, View, Dimensions, StyleSheet, Platform, useAnimatedValue as useAnimatedValue$1 } from 'react-native';
1
+ import * as React6 from 'react';
2
+ import React6__default, { createContext, memo, useReducer, useEffect, useMemo, useRef, useCallback, useImperativeHandle, useSyncExternalStore, useContext, useState, forwardRef, useLayoutEffect } from 'react';
3
+ import { View, Text, Platform, Animated, ScrollView, Dimensions, StyleSheet, RefreshControl } from 'react-native';
4
4
 
5
5
  // src/LegendList.tsx
6
- var ContextState = React5.createContext(null);
6
+ var ContextState = React6.createContext(null);
7
7
  function StateProvider({ children }) {
8
- const [value] = React5.useState(() => ({
8
+ const [value] = React6.useState(() => ({
9
9
  listeners: /* @__PURE__ */ new Map(),
10
10
  values: /* @__PURE__ */ new Map(),
11
11
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
12
12
  mapViewabilityValues: /* @__PURE__ */ new Map(),
13
13
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
14
- mapViewabilityAmountValues: /* @__PURE__ */ new Map()
14
+ mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
15
+ columnWrapperStyle: void 0
15
16
  }));
16
- return /* @__PURE__ */ React5.createElement(ContextState.Provider, { value }, children);
17
+ return /* @__PURE__ */ React6.createElement(ContextState.Provider, { value }, children);
17
18
  }
18
19
  function useStateContext() {
19
- return React5.useContext(ContextState);
20
+ return React6.useContext(ContextState);
20
21
  }
21
22
  function createSelectorFunctions(ctx, signalName) {
22
23
  return {
@@ -25,8 +26,8 @@ function createSelectorFunctions(ctx, signalName) {
25
26
  };
26
27
  }
27
28
  function use$(signalName) {
28
- const ctx = React5.useContext(ContextState);
29
- const { subscribe, get } = React5.useMemo(() => createSelectorFunctions(ctx, signalName), []);
29
+ const ctx = React6.useContext(ContextState);
30
+ const { subscribe, get } = React6.useMemo(() => createSelectorFunctions(ctx, signalName), []);
30
31
  const value = useSyncExternalStore(subscribe, get);
31
32
  return value;
32
33
  }
@@ -56,6 +57,14 @@ function set$(ctx, signalName, value) {
56
57
  }
57
58
  }
58
59
  }
60
+ function getContentSize(ctx) {
61
+ const { values } = ctx;
62
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
63
+ const headerSize = values.get("headerSize") || 0;
64
+ const footerSize = values.get("footerSize") || 0;
65
+ const totalSize = values.get("totalSize") || 0;
66
+ return headerSize + footerSize + totalSize + stylePaddingTop;
67
+ }
59
68
  var symbolFirst = Symbol();
60
69
  function useInit(cb) {
61
70
  const refValue = useRef(symbolFirst);
@@ -141,10 +150,61 @@ function useRecyclingState(valueOrFun) {
141
150
  });
142
151
  return stateInfo;
143
152
  }
144
- var LeanView = React5.forwardRef((props, ref) => {
145
- return React5.createElement("RCTView", { ...props, ref });
153
+ var DebugRow = ({ children }) => {
154
+ return /* @__PURE__ */ React.createElement(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" } }, children);
155
+ };
156
+ var DebugView = memo(function DebugView2({ state }) {
157
+ const ctx = useStateContext();
158
+ const totalSize = use$("totalSize") || 0;
159
+ const totalSizeWithScrollAdjust = use$("totalSizeWithScrollAdjust") || 0;
160
+ const scrollAdjust = use$("scrollAdjust") || 0;
161
+ const rawScroll = use$("debugRawScroll") || 0;
162
+ const scroll = use$("debugComputedScroll") || 0;
163
+ const contentSize = getContentSize(ctx);
164
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
165
+ use$("numContainers");
166
+ use$("numContainersPooled");
167
+ useInterval(() => {
168
+ forceUpdate();
169
+ }, 100);
170
+ return /* @__PURE__ */ React.createElement(
171
+ View,
172
+ {
173
+ style: {
174
+ position: "absolute",
175
+ top: 0,
176
+ right: 0,
177
+ paddingLeft: 4,
178
+ paddingBottom: 4,
179
+ // height: 100,
180
+ backgroundColor: "#FFFFFFCC",
181
+ padding: 4,
182
+ borderRadius: 4
183
+ },
184
+ pointerEvents: "none"
185
+ },
186
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React.createElement(Text, null, totalSize.toFixed(2))),
187
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React.createElement(Text, null, contentSize.toFixed(2))),
188
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "At end:"), /* @__PURE__ */ React.createElement(Text, null, String(state.isAtBottom))),
189
+ /* @__PURE__ */ React.createElement(Text, null),
190
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React.createElement(Text, null, scrollAdjust.toFixed(2))),
191
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "TotalSizeReal: "), /* @__PURE__ */ React.createElement(Text, null, totalSizeWithScrollAdjust.toFixed(2))),
192
+ /* @__PURE__ */ React.createElement(Text, null),
193
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React.createElement(Text, null, rawScroll.toFixed(2))),
194
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React.createElement(Text, null, scroll.toFixed(2)))
195
+ );
146
196
  });
147
- LeanView.displayName = "RCTView";
197
+ function useInterval(callback, delay) {
198
+ useEffect(() => {
199
+ const interval = setInterval(callback, delay);
200
+ return () => clearInterval(interval);
201
+ }, [delay]);
202
+ }
203
+ var LeanViewComponent = React6.forwardRef((props, ref) => {
204
+ return React6.createElement("RCTView", { ...props, ref });
205
+ });
206
+ LeanViewComponent.displayName = "RCTView";
207
+ var LeanView = Platform.OS === "android" || Platform.OS === "ios" ? LeanViewComponent : View;
148
208
 
149
209
  // src/constants.ts
150
210
  var POSITION_OUT_OF_VIEW = -1e7;
@@ -153,8 +213,11 @@ var ANCHORED_POSITION_OUT_OF_VIEW = {
153
213
  relativeCoordinate: POSITION_OUT_OF_VIEW,
154
214
  top: POSITION_OUT_OF_VIEW
155
215
  };
216
+ var ENABLE_DEVMODE = __DEV__ && false;
217
+ var ENABLE_DEBUG_VIEW = __DEV__ && false;
156
218
 
157
219
  // src/Container.tsx
220
+ var isNewArchitecture = global.nativeFabricUIManager != null;
158
221
  var Container = ({
159
222
  id,
160
223
  recycleItems,
@@ -164,12 +227,26 @@ var Container = ({
164
227
  ItemSeparatorComponent
165
228
  }) => {
166
229
  const ctx = useStateContext();
230
+ const columnWrapperStyle = ctx.columnWrapperStyle;
167
231
  const maintainVisibleContentPosition = use$("maintainVisibleContentPosition");
168
232
  const position = use$(`containerPosition${id}`) || ANCHORED_POSITION_OUT_OF_VIEW;
169
233
  const column = use$(`containerColumn${id}`) || 0;
170
234
  const numColumns = use$("numColumns");
235
+ const lastItemKeys = use$("lastItemKeys");
236
+ const itemKey = use$(`containerItemKey${id}`);
237
+ const data = use$(`containerItemData${id}`);
238
+ const extraData = use$("extraData");
239
+ const refLastSize = useRef();
171
240
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
172
241
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
242
+ let verticalPaddingStyles;
243
+ if (columnWrapperStyle && !horizontal && numColumns > 1) {
244
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
245
+ verticalPaddingStyles = {
246
+ paddingBottom: !lastItemKeys.has(itemKey) ? rowGap || gap || void 0 : void 0,
247
+ paddingHorizontal: (columnGap || gap || 0) / 2
248
+ };
249
+ }
173
250
  const style = horizontal ? {
174
251
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
175
252
  position: "absolute",
@@ -182,76 +259,96 @@ var Container = ({
182
259
  left: otherAxisPos,
183
260
  right: numColumns > 1 ? null : 0,
184
261
  width: otherAxisSize,
185
- top: position.relativeCoordinate
262
+ top: position.relativeCoordinate,
263
+ ...verticalPaddingStyles || {}
186
264
  };
187
- const lastItemKey = use$("lastItemKey");
188
- const itemKey = use$(`containerItemKey${id}`);
189
- const data = use$(`containerItemData${id}`);
190
- const extraData = use$("extraData");
191
- const refLastSize = useRef();
192
265
  const renderedItemInfo = useMemo(
193
- () => itemKey !== void 0 && getRenderedItem(itemKey),
266
+ () => itemKey !== void 0 ? getRenderedItem(itemKey) : null,
194
267
  [itemKey, data, extraData]
195
268
  );
196
269
  const { index, renderedItem } = renderedItemInfo || {};
197
- useEffect(() => {
198
- if (itemKey) {
199
- const timeout = setTimeout(() => {
200
- if (refLastSize.current) {
201
- updateItemSize(id, itemKey, refLastSize.current);
202
- }
203
- }, 16);
204
- return () => {
205
- clearTimeout(timeout);
206
- };
207
- }
208
- }, [itemKey]);
209
270
  const onLayout = (event) => {
210
- const key = peek$(ctx, `containerItemKey${id}`);
211
- if (key !== void 0) {
212
- const size = Math.floor(event.nativeEvent.layout[horizontal ? "width" : "height"] * 8) / 8;
213
- updateItemSize(id, key, size);
271
+ if (itemKey !== void 0) {
272
+ const layout = event.nativeEvent.layout;
273
+ const size = Math.floor(layout[horizontal ? "width" : "height"] * 8) / 8;
274
+ refLastSize.current = size;
275
+ updateItemSize(itemKey, size);
214
276
  }
215
277
  };
216
278
  const ref = useRef(null);
217
- useLayoutEffect(() => {
218
- var _a, _b;
219
- if (itemKey) {
220
- const measured = (_b = (_a = ref.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
221
- if (measured) {
222
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
223
- if (size) {
224
- updateItemSize(id, itemKey, size);
279
+ if (isNewArchitecture) {
280
+ useLayoutEffect(() => {
281
+ var _a, _b;
282
+ if (itemKey !== void 0) {
283
+ const measured = (_b = (_a = ref.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
284
+ if (measured) {
285
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
286
+ if (size) {
287
+ updateItemSize(itemKey, size);
288
+ }
225
289
  }
226
290
  }
227
- }
228
- }, [itemKey]);
291
+ }, [itemKey]);
292
+ } else {
293
+ useEffect(() => {
294
+ if (itemKey) {
295
+ const timeout = setTimeout(() => {
296
+ if (refLastSize.current) {
297
+ updateItemSize(itemKey, refLastSize.current);
298
+ }
299
+ }, 16);
300
+ return () => {
301
+ clearTimeout(timeout);
302
+ };
303
+ }
304
+ }, [itemKey]);
305
+ }
229
306
  const contextValue = useMemo(
230
307
  () => ({ containerId: id, itemKey, index, value: data }),
231
308
  [id, itemKey, index, data]
232
309
  );
233
- const contentFragment = /* @__PURE__ */ React5__default.createElement(React5__default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React5__default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItem && ItemSeparatorComponent && itemKey !== lastItemKey && ItemSeparatorComponent));
310
+ const contentFragment = /* @__PURE__ */ React6__default.createElement(React6__default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React6__default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && !lastItemKeys.has(itemKey) && /* @__PURE__ */ React6__default.createElement(ItemSeparatorComponent, { leadingItem: renderedItemInfo.item })));
234
311
  if (maintainVisibleContentPosition) {
235
312
  const anchorStyle = position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
236
- return /* @__PURE__ */ React5__default.createElement(LeanView, { style, ref }, /* @__PURE__ */ React5__default.createElement(LeanView, { style: anchorStyle, onLayout }, contentFragment));
313
+ if (ENABLE_DEVMODE) {
314
+ anchorStyle.borderColor = position.type === "top" ? "red" : "blue";
315
+ anchorStyle.borderWidth = 1;
316
+ }
317
+ return /* @__PURE__ */ React6__default.createElement(LeanView, { style }, /* @__PURE__ */ React6__default.createElement(LeanView, { style: anchorStyle, onLayout, ref }, contentFragment, ENABLE_DEVMODE && /* @__PURE__ */ React6__default.createElement(Text, { style: { position: "absolute", top: 0, left: 0, zIndex: 1e3 } }, position.top)));
237
318
  }
238
- return /* @__PURE__ */ React5__default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
319
+ return /* @__PURE__ */ React6__default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
239
320
  };
240
- var useAnimatedValue = useAnimatedValue$1 || ((initialValue) => {
321
+ var typedForwardRef = forwardRef;
322
+ var typedMemo = memo;
323
+ var useAnimatedValue = (initialValue) => {
241
324
  return useRef(new Animated.Value(initialValue)).current;
242
- });
243
- function useValue$(key, getValue, key2) {
325
+ };
326
+
327
+ // src/useValue$.ts
328
+ function useValue$(key, getValue, useMicrotask) {
244
329
  var _a;
245
330
  const ctx = useStateContext();
246
331
  const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
247
332
  useMemo(() => {
248
- listen$(ctx, key, (v) => animValue.setValue(getValue ? getValue(v) : v));
333
+ let newValue = void 0;
334
+ listen$(ctx, key, (v) => {
335
+ if (useMicrotask && newValue === void 0) {
336
+ queueMicrotask(() => {
337
+ animValue.setValue(newValue);
338
+ newValue = void 0;
339
+ });
340
+ }
341
+ newValue = getValue ? getValue(v) : v;
342
+ if (!useMicrotask) {
343
+ animValue.setValue(newValue);
344
+ }
345
+ });
249
346
  }, []);
250
347
  return animValue;
251
348
  }
252
349
 
253
350
  // src/Containers.tsx
254
- var Containers = React5.memo(function Containers2({
351
+ var Containers = typedMemo(function Containers2({
255
352
  horizontal,
256
353
  recycleItems,
257
354
  ItemSeparatorComponent,
@@ -259,13 +356,21 @@ var Containers = React5.memo(function Containers2({
259
356
  updateItemSize,
260
357
  getRenderedItem
261
358
  }) {
359
+ const ctx = useStateContext();
360
+ const columnWrapperStyle = ctx.columnWrapperStyle;
361
+ const numColumns = use$("numColumns");
262
362
  const numContainers = use$("numContainersPooled");
263
- const animSize = useValue$("totalSize");
363
+ const animSize = useValue$(
364
+ "totalSizeWithScrollAdjust",
365
+ void 0,
366
+ /*useMicrotask*/
367
+ true
368
+ );
264
369
  const animOpacity = waitForInitialLayout ? useValue$("containersDidLayout", (value) => value ? 1 : 0) : void 0;
265
370
  const containers = [];
266
371
  for (let i = 0; i < numContainers; i++) {
267
372
  containers.push(
268
- /* @__PURE__ */ React5.createElement(
373
+ /* @__PURE__ */ React6.createElement(
269
374
  Container,
270
375
  {
271
376
  id: i,
@@ -280,20 +385,78 @@ var Containers = React5.memo(function Containers2({
280
385
  );
281
386
  }
282
387
  const style = horizontal ? { width: animSize, opacity: animOpacity } : { height: animSize, opacity: animOpacity };
283
- return /* @__PURE__ */ React5.createElement(Animated.View, { style }, containers);
388
+ if (columnWrapperStyle && !horizontal && numColumns > 1) {
389
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
390
+ const mx = (columnGap || gap || 0) / 2;
391
+ if (mx) {
392
+ style.marginHorizontal = -mx;
393
+ }
394
+ }
395
+ return /* @__PURE__ */ React6.createElement(Animated.View, { style }, containers);
284
396
  });
285
397
 
286
398
  // src/ListComponent.tsx
287
399
  var getComponent = (Component) => {
288
- if (React5.isValidElement(Component)) {
400
+ if (React6.isValidElement(Component)) {
289
401
  return Component;
290
402
  }
291
403
  if (Component) {
292
- return /* @__PURE__ */ React5.createElement(Component, null);
404
+ return /* @__PURE__ */ React6.createElement(Component, null);
293
405
  }
294
406
  return null;
295
407
  };
296
- var ListComponent = React5.memo(function ListComponent2({
408
+ var PaddingAndAdjust = () => {
409
+ const animPaddingTop = useValue$("paddingTop", (v) => v, true);
410
+ const animScrollAdjust = useValue$("scrollAdjust", (v) => v, true);
411
+ const additionalSize = { marginTop: animScrollAdjust, paddingTop: animPaddingTop };
412
+ return /* @__PURE__ */ React6.createElement(Animated.View, { style: additionalSize });
413
+ };
414
+ var PaddingAndAdjustDevMode = () => {
415
+ const animPaddingTop = useValue$("paddingTop", (v) => v, true);
416
+ const animScrollAdjust = useValue$("scrollAdjust", (v) => v, true);
417
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Animated.View, { style: { marginTop: animScrollAdjust } }), /* @__PURE__ */ React6.createElement(Animated.View, { style: { paddingTop: animPaddingTop } }), /* @__PURE__ */ React6.createElement(
418
+ Animated.View,
419
+ {
420
+ style: {
421
+ position: "absolute",
422
+ top: Animated.add(animScrollAdjust, Animated.multiply(animScrollAdjust, -1)),
423
+ height: animPaddingTop,
424
+ left: 0,
425
+ right: 0,
426
+ backgroundColor: "green"
427
+ }
428
+ }
429
+ ), /* @__PURE__ */ React6.createElement(
430
+ Animated.View,
431
+ {
432
+ style: {
433
+ position: "absolute",
434
+ top: animPaddingTop,
435
+ height: animScrollAdjust,
436
+ left: -16,
437
+ right: -16,
438
+ backgroundColor: "lightblue"
439
+ }
440
+ }
441
+ ), /* @__PURE__ */ React6.createElement(
442
+ Animated.View,
443
+ {
444
+ style: {
445
+ position: "absolute",
446
+ top: animPaddingTop,
447
+ height: Animated.multiply(animScrollAdjust, -1),
448
+ width: 8,
449
+ right: 4,
450
+ borderStyle: "dashed",
451
+ borderColor: "blue",
452
+ borderWidth: 1,
453
+ backgroundColor: "lightblue"
454
+ //backgroundColor: "blue",
455
+ }
456
+ }
457
+ ));
458
+ };
459
+ var ListComponent = typedMemo(function ListComponent2({
297
460
  style,
298
461
  contentContainerStyle,
299
462
  horizontal,
@@ -309,28 +472,27 @@ var ListComponent = React5.memo(function ListComponent2({
309
472
  ListFooterComponent,
310
473
  ListFooterComponentStyle,
311
474
  ListEmptyComponent,
312
- ListEmptyComponentStyle,
313
475
  getRenderedItem,
314
476
  updateItemSize,
315
477
  refScrollView,
316
478
  maintainVisibleContentPosition,
317
479
  renderScrollComponent,
480
+ onRefresh,
481
+ refreshing,
482
+ progressViewOffset,
318
483
  ...rest
319
484
  }) {
320
485
  const ctx = useStateContext();
321
- const animPaddingTop = useValue$("paddingTop");
322
- const animScrollAdjust = useValue$("scrollAdjust");
323
486
  const ScrollComponent = renderScrollComponent ? useMemo(
324
- () => React5.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
487
+ () => React6.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
325
488
  [renderScrollComponent]
326
489
  ) : ScrollView;
327
- const additionalSize = { marginTop: animScrollAdjust, paddingTop: animPaddingTop };
328
- return /* @__PURE__ */ React5.createElement(
490
+ return /* @__PURE__ */ React6.createElement(
329
491
  ScrollComponent,
330
492
  {
331
493
  ...rest,
332
494
  style,
333
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
495
+ maintainVisibleContentPosition: maintainVisibleContentPosition && !ListEmptyComponent ? { minIndexForVisible: 0 } : void 0,
334
496
  contentContainerStyle: [
335
497
  contentContainerStyle,
336
498
  horizontal ? {
@@ -343,34 +505,41 @@ var ListComponent = React5.memo(function ListComponent2({
343
505
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
344
506
  ref: refScrollView
345
507
  },
346
- /* @__PURE__ */ React5.createElement(Animated.View, { style: additionalSize }),
347
- ListHeaderComponent && /* @__PURE__ */ React5.createElement(
348
- Animated.View,
508
+ ENABLE_DEVMODE ? /* @__PURE__ */ React6.createElement(PaddingAndAdjustDevMode, null) : /* @__PURE__ */ React6.createElement(PaddingAndAdjust, null),
509
+ ListHeaderComponent && /* @__PURE__ */ React6.createElement(
510
+ View,
349
511
  {
350
512
  style: ListHeaderComponentStyle,
351
513
  onLayout: (event) => {
352
514
  const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
353
- const prevSize = peek$(ctx, "headerSize") || 0;
354
- if (size !== prevSize) {
355
- set$(ctx, "headerSize", size);
356
- }
515
+ set$(ctx, "headerSize", size);
357
516
  }
358
517
  },
359
518
  getComponent(ListHeaderComponent)
360
519
  ),
361
- ListEmptyComponent && /* @__PURE__ */ React5.createElement(Animated.View, { style: ListEmptyComponentStyle }, getComponent(ListEmptyComponent)),
362
- /* @__PURE__ */ React5.createElement(
520
+ ListEmptyComponent && getComponent(ListEmptyComponent),
521
+ /* @__PURE__ */ React6.createElement(
363
522
  Containers,
364
523
  {
365
524
  horizontal,
366
525
  recycleItems,
367
526
  waitForInitialLayout,
368
527
  getRenderedItem,
369
- ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
528
+ ItemSeparatorComponent,
370
529
  updateItemSize
371
530
  }
372
531
  ),
373
- ListFooterComponent && /* @__PURE__ */ React5.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
532
+ ListFooterComponent && /* @__PURE__ */ React6.createElement(
533
+ View,
534
+ {
535
+ style: ListFooterComponentStyle,
536
+ onLayout: (event) => {
537
+ const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
538
+ set$(ctx, "footerSize", size);
539
+ }
540
+ },
541
+ getComponent(ListFooterComponent)
542
+ )
374
543
  );
375
544
  });
376
545
 
@@ -379,36 +548,63 @@ var ScrollAdjustHandler = class {
379
548
  constructor(ctx) {
380
549
  this.ctx = ctx;
381
550
  this.appliedAdjust = 0;
382
- this.pendingAdjust = 0;
383
551
  this.busy = false;
384
- this.firstAdjust = true;
552
+ this.isPaused = false;
553
+ this.isDisabled = false;
385
554
  this.context = ctx;
386
555
  }
556
+ doAjdust() {
557
+ set$(this.context, "scrollAdjust", this.appliedAdjust);
558
+ this.busy = false;
559
+ }
387
560
  requestAdjust(adjust, onAdjusted) {
561
+ if (this.isDisabled) {
562
+ return;
563
+ }
388
564
  const oldAdjustTop = peek$(this.context, "scrollAdjust");
389
565
  if (oldAdjustTop === adjust) {
390
566
  return;
391
567
  }
392
568
  this.appliedAdjust = adjust;
393
- this.pendingAdjust = adjust;
394
- const doAjdust = () => {
395
- set$(this.context, "scrollAdjust", this.pendingAdjust);
396
- onAdjusted(oldAdjustTop - this.pendingAdjust);
397
- this.busy = false;
398
- };
399
- if (!this.busy) {
569
+ if (!this.busy && !this.isPaused) {
400
570
  this.busy = true;
401
- if (this.firstAdjust) {
402
- this.firstAdjust = false;
403
- setTimeout(doAjdust, 50);
404
- } else {
405
- doAjdust();
406
- }
571
+ this.doAjdust();
572
+ onAdjusted(oldAdjustTop - adjust);
407
573
  }
408
574
  }
409
575
  getAppliedAdjust() {
410
576
  return this.appliedAdjust;
411
577
  }
578
+ pauseAdjust() {
579
+ this.isPaused = true;
580
+ }
581
+ setDisableAdjust(disable) {
582
+ this.isDisabled = disable;
583
+ }
584
+ // return true if it was paused
585
+ unPauseAdjust() {
586
+ if (this.isPaused) {
587
+ this.isPaused = false;
588
+ this.doAjdust();
589
+ return true;
590
+ }
591
+ return false;
592
+ }
593
+ };
594
+ var useCombinedRef = (...refs) => {
595
+ const callback = useCallback((element) => {
596
+ for (const ref of refs) {
597
+ if (!ref) {
598
+ continue;
599
+ }
600
+ if (typeof ref === "function") {
601
+ ref(element);
602
+ } else {
603
+ ref.current = element;
604
+ }
605
+ }
606
+ }, refs);
607
+ return callback;
412
608
  };
413
609
 
414
610
  // src/viewability.ts
@@ -507,11 +703,13 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
507
703
  }
508
704
  }
509
705
  function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index) {
510
- const { sizes, positions, scroll } = state;
706
+ const { sizes, positions, scroll: scrollState, scrollAdjustHandler } = state;
511
707
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
512
708
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
513
709
  const viewAreaMode = viewAreaCoveragePercentThreshold != null;
514
710
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
711
+ const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
712
+ const scroll = scrollState - previousScrollAdjust - topPad;
515
713
  const top = positions.get(key) - scroll + topPad;
516
714
  const size = sizes.get(key) || 0;
517
715
  const bottom = top + size;
@@ -561,13 +759,13 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
561
759
  // src/LegendList.tsx
562
760
  var DEFAULT_DRAW_DISTANCE = 250;
563
761
  var DEFAULT_ITEM_SIZE = 100;
564
- var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
565
- return /* @__PURE__ */ React5.createElement(StateProvider, null, /* @__PURE__ */ React5.createElement(LegendListInner, { ...props, ref: forwardedRef }));
762
+ var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
763
+ return /* @__PURE__ */ React6.createElement(StateProvider, null, /* @__PURE__ */ React6.createElement(LegendListInner, { ...props, ref: forwardedRef }));
566
764
  });
567
- var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef) {
568
- var _a, _b, _c, _d, _e;
765
+ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
766
+ var _a, _b, _c, _d;
569
767
  const {
570
- data,
768
+ data: dataProp,
571
769
  initialScrollIndex,
572
770
  initialScrollOffset,
573
771
  horizontal,
@@ -580,52 +778,77 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
580
778
  alignItemsAtEnd = false,
581
779
  maintainVisibleContentPosition = false,
582
780
  onScroll: onScrollProp,
781
+ onMomentumScrollEnd,
583
782
  numColumns: numColumnsProp = 1,
783
+ columnWrapperStyle,
584
784
  keyExtractor: keyExtractorProp,
585
785
  renderItem,
586
786
  estimatedItemSize,
587
787
  getEstimatedItemSize,
588
- onEndReached,
589
- onStartReached,
590
788
  ListEmptyComponent,
591
789
  onItemSizeChanged,
592
790
  scrollEventThrottle,
593
791
  refScrollView,
594
792
  waitForInitialLayout = true,
595
793
  extraData,
794
+ onLayout: onLayoutProp,
795
+ onRefresh,
796
+ refreshing,
797
+ progressViewOffset,
798
+ refreshControl,
799
+ initialContainerPoolRatio = 2,
800
+ viewabilityConfig,
801
+ viewabilityConfigCallbackPairs,
802
+ onViewableItemsChanged,
596
803
  ...rest
597
804
  } = props;
598
805
  const { style, contentContainerStyle } = props;
806
+ const callbacks = useRef({
807
+ onStartReached: rest.onStartReached,
808
+ onEndReached: rest.onEndReached
809
+ });
810
+ callbacks.current.onStartReached = rest.onStartReached;
811
+ callbacks.current.onEndReached = rest.onEndReached;
599
812
  const ctx = useStateContext();
813
+ ctx.columnWrapperStyle = columnWrapperStyle;
600
814
  const refScroller = useRef(null);
601
- const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE;
815
+ const combinedRef = useCombinedRef(refScroller, refScrollView);
816
+ const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
602
817
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (item, index) => index.toString();
603
818
  const refState = useRef();
604
819
  const getId = (index) => {
605
820
  var _a2;
606
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
607
- if (!data2) {
821
+ const data = (_a2 = refState.current) == null ? void 0 : _a2.data;
822
+ if (!data) {
608
823
  return "";
609
824
  }
610
- const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
825
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
611
826
  return `${ret}`;
612
827
  };
613
- const getItemSize = (key, index, data2) => {
828
+ const getItemSize = (key, index, data) => {
614
829
  var _a2;
615
830
  const sizeKnown = refState.current.sizes.get(key);
616
831
  if (sizeKnown !== void 0) {
617
832
  return sizeKnown;
618
833
  }
619
- const size = (_a2 = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize) != null ? _a2 : DEFAULT_ITEM_SIZE;
834
+ const size = (_a2 = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize) != null ? _a2 : DEFAULT_ITEM_SIZE;
620
835
  refState.current.sizes.set(key, size);
621
836
  return size;
622
837
  };
623
- const calculateInitialOffset = (index = initialScrollIndex) => {
838
+ const calculateOffsetForIndex = (index = initialScrollIndex) => {
839
+ const data = dataProp;
624
840
  if (index) {
625
841
  let offset = 0;
626
- if (getEstimatedItemSize) {
842
+ const canGetSize = !!refState.current;
843
+ if (canGetSize || getEstimatedItemSize) {
844
+ const sizeFn = (index2) => {
845
+ if (canGetSize) {
846
+ return getItemSize(getId(index2), index2, data[index2]);
847
+ }
848
+ return getEstimatedItemSize(index2, data[index2]);
849
+ };
627
850
  for (let i = 0; i < index; i++) {
628
- offset += getEstimatedItemSize(i, data[i]);
851
+ offset += sizeFn(i);
629
852
  }
630
853
  } else if (estimatedItemSize) {
631
854
  offset = index * estimatedItemSize;
@@ -634,7 +857,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
634
857
  }
635
858
  return 0;
636
859
  };
637
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(calculateInitialOffset, []);
860
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(calculateOffsetForIndex, []);
638
861
  if (!refState.current) {
639
862
  const initialScrollLength = Dimensions.get("window")[horizontal ? "width" : "height"];
640
863
  refState.current = {
@@ -642,14 +865,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
642
865
  positions: /* @__PURE__ */ new Map(),
643
866
  columns: /* @__PURE__ */ new Map(),
644
867
  pendingAdjust: 0,
645
- waitingForMicrotask: false,
646
868
  isStartReached: initialContentOffset < initialScrollLength * onStartReachedThreshold,
647
869
  isEndReached: false,
648
870
  isAtBottom: false,
649
871
  isAtTop: false,
650
- data,
651
- idsInFirstRender: void 0,
652
- hasScrolled: false,
872
+ data: dataProp,
653
873
  scrollLength: initialScrollLength,
654
874
  startBuffered: 0,
655
875
  startNoBuffer: 0,
@@ -675,17 +895,22 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
675
895
  belowAnchorElementPositions: void 0,
676
896
  rowHeights: /* @__PURE__ */ new Map(),
677
897
  startReachedBlockedByTimer: false,
898
+ endReachedBlockedByTimer: false,
678
899
  scrollForNextCalculateItemsInView: void 0,
679
- enableScrollForNextCalculateItemsInView: true
900
+ enableScrollForNextCalculateItemsInView: true,
901
+ minIndexSizeChanged: 0,
902
+ numPendingInitialLayout: 0,
903
+ queuedCalculateItemsInView: 0,
904
+ lastBatchingAction: Date.now(),
905
+ onScroll: onScrollProp
680
906
  };
681
- refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
682
907
  if (maintainVisibleContentPosition) {
683
908
  if (initialScrollIndex) {
684
909
  refState.current.anchorElement = {
685
910
  coordinate: initialContentOffset,
686
911
  id: getId(initialScrollIndex)
687
912
  };
688
- } else if (data.length) {
913
+ } else if (dataProp.length) {
689
914
  refState.current.anchorElement = {
690
915
  coordinate: initialContentOffset,
691
916
  id: getId(0)
@@ -698,6 +923,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
698
923
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
699
924
  set$(ctx, "extraData", extraData);
700
925
  }
926
+ const didDataChange = refState.current.data !== dataProp;
927
+ refState.current.data = dataProp;
928
+ refState.current.onScroll = onScrollProp;
701
929
  const getAnchorElementIndex = () => {
702
930
  const state = refState.current;
703
931
  if (state.anchorElement) {
@@ -708,14 +936,14 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
708
936
  };
709
937
  const addTotalSize = useCallback((key, add, totalSizeBelowAnchor) => {
710
938
  const state = refState.current;
711
- const index = key === null ? 0 : state.indexByKey.get(key);
939
+ const { indexByKey, anchorElement } = state;
940
+ const index = key === null ? 0 : indexByKey.get(key);
712
941
  let isAboveAnchor = false;
713
942
  if (maintainVisibleContentPosition) {
714
- if (state.anchorElement && index < getAnchorElementIndex()) {
943
+ if (anchorElement && index < getAnchorElementIndex()) {
715
944
  isAboveAnchor = true;
716
945
  }
717
946
  }
718
- state.totalSize;
719
947
  if (key === null) {
720
948
  state.totalSize = add;
721
949
  state.totalSizeBelowAnchor = totalSizeBelowAnchor;
@@ -725,29 +953,30 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
725
953
  state.totalSizeBelowAnchor += add;
726
954
  }
727
955
  }
728
- let applyAdjustValue = void 0;
729
- if (maintainVisibleContentPosition) {
730
- const newAdjust = state.anchorElement.coordinate - state.totalSizeBelowAnchor;
956
+ let applyAdjustValue = 0;
957
+ let resultSize = state.totalSize;
958
+ if (maintainVisibleContentPosition && anchorElement !== void 0) {
959
+ const newAdjust = anchorElement.coordinate - state.totalSizeBelowAnchor;
731
960
  applyAdjustValue = -newAdjust;
732
961
  state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
733
962
  state.rowHeights.clear();
963
+ if (applyAdjustValue !== void 0) {
964
+ resultSize -= applyAdjustValue;
965
+ refState.current.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
966
+ state.scroll -= diff;
967
+ });
968
+ }
734
969
  }
735
- const totalSize = state.totalSize;
736
- let resultSize = totalSize;
737
- if (applyAdjustValue !== void 0) {
738
- resultSize -= applyAdjustValue;
739
- refState.current.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
740
- state.scroll -= diff;
741
- });
742
- }
743
- set$(ctx, "totalSize", resultSize);
970
+ set$(ctx, "totalSize", state.totalSize);
971
+ set$(ctx, "totalSizeWithScrollAdjust", resultSize);
744
972
  if (alignItemsAtEnd) {
745
973
  doUpdatePaddingTop();
746
974
  }
747
975
  }, []);
748
976
  const getRowHeight = (n) => {
749
- const { rowHeights } = refState.current;
750
- if (numColumnsProp === 1) {
977
+ const { rowHeights, data } = refState.current;
978
+ const numColumns = peek$(ctx, "numColumns");
979
+ if (numColumns === 1) {
751
980
  const id = getId(n);
752
981
  return getItemSize(id, n, data[n]);
753
982
  }
@@ -755,8 +984,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
755
984
  return rowHeights.get(n) || 0;
756
985
  }
757
986
  let rowHeight = 0;
758
- const startEl = n * numColumnsProp;
759
- for (let i = startEl; i < startEl + numColumnsProp && i < data.length; i++) {
987
+ const startEl = n * numColumns;
988
+ for (let i = startEl; i < startEl + numColumns && i < data.length; i++) {
760
989
  const id = getId(i);
761
990
  const size = getItemSize(id, i, data[i]);
762
991
  rowHeight = Math.max(rowHeight, size);
@@ -775,10 +1004,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
775
1004
  return /* @__PURE__ */ new Map();
776
1005
  }
777
1006
  const map = state.belowAnchorElementPositions || /* @__PURE__ */ new Map();
1007
+ const numColumns = peek$(ctx, "numColumns");
778
1008
  for (let i = anchorIndex - 1; i >= 0; i--) {
779
1009
  const id = getId(i);
780
- const rowNumber = Math.floor(i / numColumnsProp);
781
- if (i % numColumnsProp === 0) {
1010
+ const rowNumber = Math.floor(i / numColumns);
1011
+ if (i % numColumns === 0) {
782
1012
  top -= getRowHeight(rowNumber);
783
1013
  }
784
1014
  map.set(id, top);
@@ -786,37 +1016,53 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
786
1016
  return map;
787
1017
  };
788
1018
  const getElementPositionBelowAchor = (id) => {
1019
+ var _a2;
789
1020
  const state = refState.current;
790
1021
  if (!refState.current.belowAnchorElementPositions) {
791
1022
  state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
792
1023
  }
793
1024
  const res = state.belowAnchorElementPositions.get(id);
794
1025
  if (res === void 0) {
795
- throw new Error("Undefined position below achor");
1026
+ console.warn(`Undefined position below achor ${id} ${(_a2 = state.anchorElement) == null ? void 0 : _a2.id}`);
1027
+ return 0;
796
1028
  }
797
1029
  return res;
798
1030
  };
799
- const calculateItemsInView = useCallback((speed) => {
1031
+ const calculateItemsInView = useCallback(() => {
1032
+ var _a2;
800
1033
  const state = refState.current;
801
1034
  const {
802
- data: data2,
1035
+ data,
803
1036
  scrollLength,
804
1037
  scroll: scrollState,
805
1038
  startBufferedId: startBufferedIdOrig,
806
1039
  positions,
807
1040
  columns,
808
- scrollAdjustHandler
1041
+ scrollAdjustHandler,
1042
+ scrollVelocity: speed
809
1043
  } = state;
810
- if (state.waitingForMicrotask) {
811
- state.waitingForMicrotask = false;
812
- }
813
- if (!data2) {
1044
+ if (!data || scrollLength === 0) {
814
1045
  return;
815
1046
  }
816
1047
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1048
+ const numColumns = peek$(ctx, "numColumns");
817
1049
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
818
1050
  const scrollExtra = Math.max(-16, Math.min(16, speed)) * 16;
819
- const scroll = scrollState - previousScrollAdjust - topPad - scrollExtra;
1051
+ const scroll = scrollState - previousScrollAdjust - topPad;
1052
+ if (ENABLE_DEBUG_VIEW) {
1053
+ set$(ctx, "debugRawScroll", scrollState);
1054
+ set$(ctx, "debugComputedScroll", scroll);
1055
+ }
1056
+ let scrollBufferTop = scrollBuffer;
1057
+ let scrollBufferBottom = scrollBuffer;
1058
+ if (scrollExtra > 8) {
1059
+ scrollBufferTop = 0;
1060
+ scrollBufferBottom = scrollBuffer + scrollExtra;
1061
+ }
1062
+ if (scrollExtra < -8) {
1063
+ scrollBufferTop = scrollBuffer - scrollExtra;
1064
+ scrollBufferBottom = 0;
1065
+ }
820
1066
  if (state.scrollForNextCalculateItemsInView) {
821
1067
  const { top: top2, bottom } = state.scrollForNextCalculateItemsInView;
822
1068
  if (scroll > top2 && scroll < bottom) {
@@ -829,8 +1075,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
829
1075
  let startBufferedId = null;
830
1076
  let endNoBuffer = null;
831
1077
  let endBuffered = null;
832
- const originalStartId = startBufferedIdOrig && state.indexByKey.get(startBufferedIdOrig);
833
- let loopStart = originalStartId || 0;
1078
+ let loopStart = startBufferedIdOrig ? state.indexByKey.get(startBufferedIdOrig) || 0 : 0;
1079
+ if (state.minIndexSizeChanged !== void 0) {
1080
+ loopStart = Math.min(state.minIndexSizeChanged, loopStart);
1081
+ state.minIndexSizeChanged = void 0;
1082
+ }
834
1083
  const anchorElementIndex = getAnchorElementIndex();
835
1084
  for (let i = loopStart; i >= 0; i--) {
836
1085
  const id = getId(i);
@@ -843,7 +1092,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
843
1092
  }
844
1093
  const top2 = newPosition || positions.get(id);
845
1094
  if (top2 !== void 0) {
846
- const size = getItemSize(id, i, data2[i]);
1095
+ const size = getItemSize(id, i, data[i]);
847
1096
  const bottom = top2 + size;
848
1097
  if (bottom > scroll - scrollBuffer) {
849
1098
  loopStart = i;
@@ -852,7 +1101,6 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
852
1101
  }
853
1102
  }
854
1103
  }
855
- const numColumns = peek$(ctx, "numColumns");
856
1104
  const loopStartMod = loopStart % numColumns;
857
1105
  if (loopStartMod > 0) {
858
1106
  loopStart -= loopStartMod;
@@ -861,22 +1109,22 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
861
1109
  let column = 1;
862
1110
  let maxSizeInRow = 0;
863
1111
  const getInitialTop = (i) => {
864
- var _a2;
1112
+ var _a3;
865
1113
  const id = getId(i);
866
1114
  let topOffset = 0;
867
1115
  if (positions.get(id)) {
868
1116
  topOffset = positions.get(id);
869
1117
  }
870
- if (id === ((_a2 = state.anchorElement) == null ? void 0 : _a2.id)) {
871
- topOffset = initialContentOffset || 0;
1118
+ if (id === ((_a3 = state.anchorElement) == null ? void 0 : _a3.id)) {
1119
+ topOffset = state.anchorElement.coordinate;
872
1120
  }
873
1121
  return topOffset;
874
1122
  };
875
- for (let i = loopStart; i < data2.length; i++) {
1123
+ for (let i = loopStart; i < data.length; i++) {
876
1124
  const id = getId(i);
877
- const size = getItemSize(id, i, data2[i]);
1125
+ const size = getItemSize(id, i, data[i]);
878
1126
  maxSizeInRow = Math.max(maxSizeInRow, size);
879
- if (top === void 0) {
1127
+ if (top === void 0 || id === ((_a2 = state.anchorElement) == null ? void 0 : _a2.id)) {
880
1128
  top = getInitialTop(i);
881
1129
  }
882
1130
  if (positions.get(id) !== top) {
@@ -888,7 +1136,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
888
1136
  if (startNoBuffer === null && top + size > scroll) {
889
1137
  startNoBuffer = i;
890
1138
  }
891
- if (startBuffered === null && top + size > scroll - scrollBuffer) {
1139
+ if (startBuffered === null && top + size > scroll - scrollBufferTop) {
892
1140
  startBuffered = i;
893
1141
  startBufferedId = id;
894
1142
  }
@@ -896,7 +1144,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
896
1144
  if (top <= scrollBottom) {
897
1145
  endNoBuffer = i;
898
1146
  }
899
- if (top <= scrollBottom + scrollBuffer) {
1147
+ if (top <= scrollBottom + scrollBufferBottom) {
900
1148
  endBuffered = i;
901
1149
  } else {
902
1150
  break;
@@ -929,6 +1177,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
929
1177
  if (startBuffered !== null && endBuffered !== null) {
930
1178
  const prevNumContainers = ctx.values.get("numContainers");
931
1179
  let numContainers = prevNumContainers;
1180
+ let didWarnMoreContainers = false;
932
1181
  for (let i = startBuffered; i <= endBuffered; i++) {
933
1182
  let isContained = false;
934
1183
  const id = getId(i);
@@ -962,18 +1211,19 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
962
1211
  if (furthestIndex >= 0) {
963
1212
  set$(ctx, `containerItemKey${furthestIndex}`, id);
964
1213
  const index = state.indexByKey.get(id);
965
- set$(ctx, `containerItemData${furthestIndex}`, data2[index]);
1214
+ set$(ctx, `containerItemData${furthestIndex}`, data[index]);
966
1215
  } else {
967
1216
  const containerId = numContainers;
968
1217
  numContainers++;
969
1218
  set$(ctx, `containerItemKey${containerId}`, id);
970
1219
  const index = state.indexByKey.get(id);
971
- set$(ctx, `containerItemData${containerId}`, data2[index]);
1220
+ set$(ctx, `containerItemData${containerId}`, data[index]);
972
1221
  set$(ctx, `containerPosition${containerId}`, ANCHORED_POSITION_OUT_OF_VIEW);
973
1222
  set$(ctx, `containerColumn${containerId}`, -1);
974
- if (__DEV__ && numContainers > peek$(ctx, "numContainersPooled")) {
1223
+ if (__DEV__ && !didWarnMoreContainers && numContainers > peek$(ctx, "numContainersPooled")) {
1224
+ didWarnMoreContainers = true;
975
1225
  console.warn(
976
- "[legend-list] No container to recycle, so creating one on demand. This can be a performance issue and is likely caused by the estimatedItemSize being too small. Consider increasing estimatedItemSize. numContainers:",
1226
+ "[legend-list] No container to recycle, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize. numContainers:",
977
1227
  numContainers
978
1228
  );
979
1229
  }
@@ -983,19 +1233,19 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
983
1233
  if (numContainers !== prevNumContainers) {
984
1234
  set$(ctx, "numContainers", numContainers);
985
1235
  if (numContainers > peek$(ctx, "numContainersPooled")) {
986
- set$(ctx, "numContainersPooled", numContainers);
1236
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
987
1237
  }
988
1238
  }
989
1239
  for (let i = 0; i < numContainers; i++) {
990
1240
  const itemKey = peek$(ctx, `containerItemKey${i}`);
991
1241
  const itemIndex = state.indexByKey.get(itemKey);
992
- const item = data2[itemIndex];
993
- if (item) {
1242
+ const item = data[itemIndex];
1243
+ if (item !== void 0) {
994
1244
  const id = getId(itemIndex);
995
1245
  if (itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered) {
996
1246
  const prevPos = peek$(ctx, `containerPosition${i}`).top;
997
1247
  const pos = positions.get(id) || 0;
998
- const size = getItemSize(id, itemIndex, data2[i]);
1248
+ const size = getItemSize(id, itemIndex, data[i]);
999
1249
  if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
1000
1250
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1001
1251
  }
@@ -1007,9 +1257,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1007
1257
  };
1008
1258
  const column2 = columns.get(id) || 1;
1009
1259
  if (maintainVisibleContentPosition && itemIndex < anchorElementIndex) {
1010
- const currentRow = Math.floor(itemIndex / numColumnsProp);
1260
+ const currentRow = Math.floor(itemIndex / numColumns);
1011
1261
  const rowHeight = getRowHeight(currentRow);
1012
- const elementHeight = getItemSize(id, itemIndex, data2[i]);
1262
+ const elementHeight = getItemSize(id, itemIndex, data[i]);
1013
1263
  const diff = rowHeight - elementHeight;
1014
1264
  pos.relativeCoordinate = pos.top + getRowHeight(currentRow) - diff;
1015
1265
  pos.type = "bottom";
@@ -1024,13 +1274,15 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1024
1274
  set$(ctx, `containerColumn${i}`, column2);
1025
1275
  }
1026
1276
  if (prevData !== item) {
1027
- set$(ctx, `containerItemData${i}`, data2[itemIndex]);
1277
+ set$(ctx, `containerItemData${i}`, data[itemIndex]);
1028
1278
  }
1029
1279
  }
1030
1280
  }
1031
1281
  }
1032
1282
  }
1033
- set$(ctx, "containersDidLayout", true);
1283
+ if (state.numPendingInitialLayout === 0) {
1284
+ state.numPendingInitialLayout = state.endBuffered - state.startBuffered + 1;
1285
+ }
1034
1286
  if (state.viewabilityConfigCallbackPairs) {
1035
1287
  updateViewableItems(
1036
1288
  state,
@@ -1045,16 +1297,27 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1045
1297
  }, []);
1046
1298
  const doUpdatePaddingTop = () => {
1047
1299
  if (alignItemsAtEnd) {
1048
- const { scrollLength, totalSize } = refState.current;
1049
- const listPaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1050
- const paddingTop = Math.max(0, Math.floor(scrollLength - totalSize - listPaddingTop));
1300
+ const { scrollLength } = refState.current;
1301
+ const contentSize = getContentSize(ctx);
1302
+ const paddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1051
1303
  set$(ctx, "paddingTop", paddingTop);
1052
1304
  }
1053
1305
  };
1306
+ const scrollTo = (offset, animated) => {
1307
+ var _a2;
1308
+ (_a2 = refScroller.current) == null ? void 0 : _a2.scrollTo({
1309
+ x: horizontal ? offset : 0,
1310
+ y: horizontal ? 0 : offset,
1311
+ animated: !!animated
1312
+ });
1313
+ };
1054
1314
  const doMaintainScrollAtEnd = (animated) => {
1055
1315
  const state = refState.current;
1056
1316
  if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd) {
1057
- state.scroll = state.totalSize - state.scrollLength + peek$(ctx, "paddingTop");
1317
+ const paddingTop = peek$(ctx, "paddingTop") || 0;
1318
+ if (paddingTop > 0) {
1319
+ state.scroll = 0;
1320
+ }
1058
1321
  requestAnimationFrame(() => {
1059
1322
  var _a2;
1060
1323
  (_a2 = refScroller.current) == null ? void 0 : _a2.scrollToEnd({
@@ -1064,28 +1327,48 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1064
1327
  return true;
1065
1328
  }
1066
1329
  };
1330
+ const checkThreshold = (distance, threshold, isReached, isBlockedByTimer, onReached, blockTimer) => {
1331
+ const distanceAbs = Math.abs(distance);
1332
+ const isAtThreshold = distanceAbs < threshold;
1333
+ if (!isReached && !isBlockedByTimer) {
1334
+ if (isAtThreshold) {
1335
+ onReached == null ? void 0 : onReached(distance);
1336
+ blockTimer == null ? void 0 : blockTimer(true);
1337
+ setTimeout(() => {
1338
+ blockTimer == null ? void 0 : blockTimer(false);
1339
+ }, 700);
1340
+ return true;
1341
+ }
1342
+ } else {
1343
+ if (distance >= 1.3 * threshold) {
1344
+ return false;
1345
+ }
1346
+ }
1347
+ return isReached;
1348
+ };
1067
1349
  const checkAtBottom = () => {
1068
1350
  if (!refState.current) {
1069
1351
  return;
1070
1352
  }
1071
- const { scrollLength, scroll, totalSize } = refState.current;
1072
- if (totalSize > 0) {
1073
- const distanceFromEnd = totalSize - scroll - scrollLength;
1074
- if (refState.current) {
1075
- refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1076
- }
1077
- if (onEndReached) {
1078
- if (!refState.current.isEndReached) {
1079
- if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
1080
- refState.current.isEndReached = true;
1081
- onEndReached({ distanceFromEnd });
1082
- }
1083
- } else {
1084
- if (distanceFromEnd >= onEndReachedThreshold * scrollLength) {
1085
- refState.current.isEndReached = false;
1086
- }
1353
+ const { scrollLength, scroll, hasScrolled } = refState.current;
1354
+ const contentSize = getContentSize(ctx);
1355
+ if (contentSize > 0 && hasScrolled) {
1356
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1357
+ const distanceFromEndAbs = Math.abs(distanceFromEnd);
1358
+ refState.current.isAtBottom = distanceFromEndAbs < scrollLength * maintainScrollAtEndThreshold;
1359
+ refState.current.isEndReached = checkThreshold(
1360
+ distanceFromEnd,
1361
+ onEndReachedThreshold * scrollLength,
1362
+ refState.current.isEndReached,
1363
+ refState.current.endReachedBlockedByTimer,
1364
+ (distance) => {
1365
+ var _a2, _b2;
1366
+ return (_b2 = (_a2 = callbacks.current).onEndReached) == null ? void 0 : _b2.call(_a2, { distanceFromEnd: distance });
1367
+ },
1368
+ (block) => {
1369
+ refState.current.endReachedBlockedByTimer = block;
1087
1370
  }
1088
- }
1371
+ );
1089
1372
  }
1090
1373
  };
1091
1374
  const checkAtTop = () => {
@@ -1094,112 +1377,118 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1094
1377
  }
1095
1378
  const { scrollLength, scroll } = refState.current;
1096
1379
  const distanceFromTop = scroll;
1097
- refState.current.isAtTop = distanceFromTop < 0;
1098
- if (onStartReached) {
1099
- if (!refState.current.isStartReached && !refState.current.startReachedBlockedByTimer) {
1100
- if (distanceFromTop < onStartReachedThreshold * scrollLength) {
1101
- refState.current.isStartReached = true;
1102
- onStartReached({ distanceFromStart: scroll });
1103
- refState.current.startReachedBlockedByTimer = true;
1104
- setTimeout(() => {
1105
- refState.current.startReachedBlockedByTimer = false;
1106
- }, 700);
1107
- }
1108
- } else {
1109
- if (distanceFromTop >= 1.3 * onStartReachedThreshold * scrollLength) {
1110
- refState.current.isStartReached = false;
1111
- }
1380
+ const distanceFromTopAbs = Math.abs(distanceFromTop);
1381
+ refState.current.isAtTop = distanceFromTopAbs < 0;
1382
+ refState.current.isStartReached = checkThreshold(
1383
+ distanceFromTop,
1384
+ onStartReachedThreshold * scrollLength,
1385
+ refState.current.isStartReached,
1386
+ refState.current.startReachedBlockedByTimer,
1387
+ (distance) => {
1388
+ var _a2, _b2;
1389
+ return (_b2 = (_a2 = callbacks.current).onStartReached) == null ? void 0 : _b2.call(_a2, { distanceFromStart: distance });
1390
+ },
1391
+ (block) => {
1392
+ refState.current.startReachedBlockedByTimer = block;
1112
1393
  }
1113
- }
1394
+ );
1114
1395
  };
1115
- const checkResetContainers = (reset) => {
1396
+ const checkResetContainers = (isFirst2) => {
1116
1397
  const state = refState.current;
1117
1398
  if (state) {
1118
- state.data = data;
1119
- if (reset) {
1399
+ state.data = dataProp;
1400
+ if (!isFirst2) {
1120
1401
  refState.current.scrollForNextCalculateItemsInView = void 0;
1121
1402
  const numContainers = peek$(ctx, "numContainers");
1122
1403
  for (let i = 0; i < numContainers; i++) {
1123
1404
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1124
1405
  if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
1125
1406
  set$(ctx, `containerItemKey${i}`, void 0);
1407
+ set$(ctx, `containerItemData${i}`, void 0);
1126
1408
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1127
1409
  set$(ctx, `containerColumn${i}`, -1);
1128
1410
  }
1129
1411
  }
1130
1412
  if (!keyExtractorProp) {
1131
- state.sizes.clear();
1132
1413
  state.positions.clear();
1133
1414
  }
1134
- calculateItemsInView(state.scrollVelocity);
1135
- }
1136
- const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1137
- if (!didMaintainScrollAtEnd && data.length > state.data.length) {
1138
- state.isEndReached = false;
1415
+ calculateItemsInView();
1416
+ const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1417
+ if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1418
+ state.isEndReached = false;
1419
+ }
1420
+ checkAtTop();
1421
+ checkAtBottom();
1139
1422
  }
1140
- checkAtTop();
1141
- checkAtBottom();
1142
1423
  }
1143
1424
  };
1144
- const isFirst = !refState.current.renderItem;
1145
- if (isFirst || data !== refState.current.data || numColumnsProp !== peek$(ctx, "numColumns")) {
1146
- if (!keyExtractorProp && !isFirst && data !== refState.current.data) {
1147
- refState.current.sizes.clear();
1148
- refState.current.positions.clear();
1149
- }
1150
- refState.current.data = data;
1425
+ const calcTotalSizesAndPositions = ({ forgetPositions = false }) => {
1426
+ var _a2, _b2;
1151
1427
  let totalSize = 0;
1152
1428
  let totalSizeBelowIndex = 0;
1153
1429
  const indexByKey = /* @__PURE__ */ new Map();
1154
1430
  const newPositions = /* @__PURE__ */ new Map();
1155
1431
  let column = 1;
1156
1432
  let maxSizeInRow = 0;
1157
- for (let i = 0; i < data.length; i++) {
1433
+ const numColumns = (_a2 = peek$(ctx, "numColumns")) != null ? _a2 : numColumnsProp;
1434
+ if (!refState.current) {
1435
+ return;
1436
+ }
1437
+ for (let i = 0; i < dataProp.length; i++) {
1158
1438
  const key = getId(i);
1439
+ if (__DEV__) {
1440
+ if (indexByKey.has(key)) {
1441
+ console.error(
1442
+ `[legend-list] Error: Detected overlapping key (${key}) which causes missing items and gaps and other terrrible things. Check that keyExtractor returns unique values.`
1443
+ );
1444
+ }
1445
+ }
1159
1446
  indexByKey.set(key, i);
1160
- if (refState.current.positions.get(key) != null && refState.current.indexByKey.get(key) === i) {
1447
+ if (!forgetPositions && refState.current.positions.get(key) != null && refState.current.indexByKey.get(key) === i) {
1161
1448
  newPositions.set(key, refState.current.positions.get(key));
1162
1449
  }
1163
1450
  }
1164
1451
  refState.current.indexByKey = indexByKey;
1165
1452
  refState.current.positions = newPositions;
1166
- if (maintainVisibleContentPosition) {
1167
- if (refState.current.anchorElement == null || indexByKey.get(refState.current.anchorElement.id) == null) {
1168
- if (data.length) {
1169
- const newAnchorElement = {
1170
- coordinate: 0,
1171
- id: getId(0)
1172
- };
1173
- refState.current.anchorElement = newAnchorElement;
1174
- (_a = refState.current.belowAnchorElementPositions) == null ? void 0 : _a.clear();
1175
- refScroller.current.scrollTo({ x: 0, y: 0, animated: false });
1453
+ if (!forgetPositions && !isFirst) {
1454
+ if (maintainVisibleContentPosition) {
1455
+ if (refState.current.anchorElement == null || indexByKey.get(refState.current.anchorElement.id) == null) {
1456
+ if (dataProp.length) {
1457
+ const newAnchorElement = {
1458
+ coordinate: 0,
1459
+ id: getId(0)
1460
+ };
1461
+ refState.current.anchorElement = newAnchorElement;
1462
+ (_b2 = refState.current.belowAnchorElementPositions) == null ? void 0 : _b2.clear();
1463
+ scrollTo(0, false);
1464
+ setTimeout(() => {
1465
+ calculateItemsInView();
1466
+ }, 0);
1467
+ } else {
1468
+ refState.current.startBufferedId = void 0;
1469
+ }
1470
+ }
1471
+ } else {
1472
+ if (refState.current.startBufferedId != null && newPositions.get(refState.current.startBufferedId) == null) {
1473
+ if (dataProp.length) {
1474
+ refState.current.startBufferedId = getId(0);
1475
+ } else {
1476
+ refState.current.startBufferedId = void 0;
1477
+ }
1478
+ scrollTo(0, false);
1176
1479
  setTimeout(() => {
1177
- calculateItemsInView(0);
1480
+ calculateItemsInView();
1178
1481
  }, 0);
1179
- } else {
1180
- refState.current.startBufferedId = void 0;
1181
- }
1182
- }
1183
- } else {
1184
- if (refState.current.startBufferedId != null && newPositions.get(refState.current.startBufferedId) == null) {
1185
- if (data.length) {
1186
- refState.current.startBufferedId = getId(0);
1187
- } else {
1188
- refState.current.startBufferedId = void 0;
1189
1482
  }
1190
- refScroller.current.scrollTo({ x: 0, y: 0, animated: false });
1191
- setTimeout(() => {
1192
- calculateItemsInView(0);
1193
- }, 0);
1194
1483
  }
1195
1484
  }
1196
1485
  const anchorElementIndex = getAnchorElementIndex();
1197
- for (let i = 0; i < data.length; i++) {
1486
+ for (let i = 0; i < dataProp.length; i++) {
1198
1487
  const key = getId(i);
1199
- const size = getItemSize(key, i, data[i]);
1488
+ const size = getItemSize(key, i, dataProp[i]);
1200
1489
  maxSizeInRow = Math.max(maxSizeInRow, size);
1201
1490
  column++;
1202
- if (column > numColumnsProp) {
1491
+ if (column > numColumns) {
1203
1492
  if (maintainVisibleContentPosition && anchorElementIndex !== void 0 && i < anchorElementIndex) {
1204
1493
  totalSizeBelowIndex += maxSizeInRow;
1205
1494
  }
@@ -1211,36 +1500,58 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1211
1500
  if (maxSizeInRow > 0) {
1212
1501
  totalSize += maxSizeInRow;
1213
1502
  }
1503
+ const state = refState.current;
1504
+ state.ignoreScrollFromCalcTotal = true;
1505
+ requestAnimationFrame(() => {
1506
+ state.ignoreScrollFromCalcTotal = false;
1507
+ });
1214
1508
  addTotalSize(null, totalSize, totalSizeBelowIndex);
1215
- }
1216
- useEffect(() => {
1217
- checkResetContainers(
1218
- /*reset*/
1219
- !isFirst
1509
+ };
1510
+ const isFirst = !refState.current.renderItem;
1511
+ const memoizedLastItemKeys = useMemo(() => {
1512
+ if (!dataProp.length) return /* @__PURE__ */ new Set();
1513
+ return new Set(
1514
+ Array.from({ length: Math.min(numColumnsProp, dataProp.length) }, (_, i) => getId(dataProp.length - 1 - i))
1220
1515
  );
1221
- }, [isFirst, data, numColumnsProp]);
1222
- useEffect(() => {
1223
- set$(ctx, "extraData", extraData);
1224
- }, [extraData]);
1225
- refState.current.renderItem = renderItem;
1226
- const lastItemKey = getId(data[data.length - 1]);
1227
- const stylePaddingTop = (_e = (_d = (_b = StyleSheet.flatten(style)) == null ? void 0 : _b.paddingTop) != null ? _d : (_c = StyleSheet.flatten(contentContainerStyle)) == null ? void 0 : _c.paddingTop) != null ? _e : 0;
1516
+ }, [dataProp.length, numColumnsProp, dataProp.slice(-numColumnsProp).toString()]);
1228
1517
  const initalizeStateVars = () => {
1229
- set$(ctx, "lastItemKey", lastItemKey);
1518
+ set$(ctx, "lastItemKeys", memoizedLastItemKeys);
1230
1519
  set$(ctx, "numColumns", numColumnsProp);
1231
1520
  set$(ctx, "stylePaddingTop", stylePaddingTop);
1232
1521
  };
1233
1522
  if (isFirst) {
1234
1523
  initalizeStateVars();
1235
1524
  }
1236
- useEffect(initalizeStateVars, [lastItemKey, numColumnsProp, stylePaddingTop]);
1525
+ if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
1526
+ refState.current.lastBatchingAction = Date.now();
1527
+ if (!keyExtractorProp && !isFirst && didDataChange) {
1528
+ refState.current.sizes.clear();
1529
+ refState.current.positions.clear();
1530
+ }
1531
+ calcTotalSizesAndPositions({ forgetPositions: false });
1532
+ }
1533
+ useEffect(() => {
1534
+ const didAllocateContainers = doInitialAllocateContainers();
1535
+ if (!didAllocateContainers) {
1536
+ checkResetContainers(
1537
+ /*isFirst*/
1538
+ isFirst
1539
+ );
1540
+ }
1541
+ }, [isFirst, dataProp, numColumnsProp]);
1542
+ useEffect(() => {
1543
+ set$(ctx, "extraData", extraData);
1544
+ }, [extraData]);
1545
+ refState.current.renderItem = renderItem;
1546
+ const stylePaddingTop = (_d = (_c = (_a = StyleSheet.flatten(style)) == null ? void 0 : _a.paddingTop) != null ? _c : (_b = StyleSheet.flatten(contentContainerStyle)) == null ? void 0 : _b.paddingTop) != null ? _d : 0;
1547
+ useEffect(initalizeStateVars, [memoizedLastItemKeys, numColumnsProp, stylePaddingTop]);
1237
1548
  const getRenderedItem = useCallback((key) => {
1238
1549
  var _a2, _b2;
1239
1550
  const state = refState.current;
1240
1551
  if (!state) {
1241
1552
  return null;
1242
1553
  }
1243
- const { data: data2, indexByKey } = state;
1554
+ const { data, indexByKey } = state;
1244
1555
  const index = indexByKey.get(key);
1245
1556
  if (index === void 0) {
1246
1557
  return null;
@@ -1258,58 +1569,82 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1258
1569
  return useRecyclingState(valueOrFun);
1259
1570
  };
1260
1571
  const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
1261
- item: data2[index],
1572
+ item: data[index],
1262
1573
  index,
1574
+ extraData: peek$(ctx, "extraData"),
1263
1575
  useViewability: useViewability2,
1264
1576
  useViewabilityAmount: useViewabilityAmount2,
1265
1577
  useRecyclingEffect: useRecyclingEffect2,
1266
1578
  useRecyclingState: useRecyclingState2
1267
1579
  });
1268
- return { index, renderedItem };
1580
+ return { index, item: data[index], renderedItem };
1269
1581
  }, []);
1270
- useInit(() => {
1582
+ const doInitialAllocateContainers = () => {
1271
1583
  var _a2;
1272
1584
  const state = refState.current;
1273
- const viewability = setupViewability(props);
1585
+ const { scrollLength, data } = state;
1586
+ if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
1587
+ const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1588
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1589
+ for (let i = 0; i < numContainers; i++) {
1590
+ set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1591
+ set$(ctx, `containerColumn${i}`, -1);
1592
+ }
1593
+ set$(ctx, "numContainers", numContainers);
1594
+ set$(ctx, "numContainersPooled", numContainers * initialContainerPoolRatio);
1595
+ if (initialScrollIndex) {
1596
+ requestAnimationFrame(() => {
1597
+ calculateItemsInView();
1598
+ });
1599
+ } else {
1600
+ calculateItemsInView();
1601
+ }
1602
+ return true;
1603
+ }
1604
+ };
1605
+ useEffect(() => {
1606
+ const state = refState.current;
1607
+ const viewability = setupViewability({
1608
+ viewabilityConfig,
1609
+ viewabilityConfigCallbackPairs,
1610
+ onViewableItemsChanged
1611
+ });
1274
1612
  state.viewabilityConfigCallbackPairs = viewability;
1275
1613
  state.enableScrollForNextCalculateItemsInView = !viewability;
1276
- const scrollLength = state.scrollLength;
1277
- const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1278
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1279
- for (let i = 0; i < numContainers; i++) {
1280
- set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1281
- set$(ctx, `containerColumn${i}`, -1);
1282
- }
1283
- set$(ctx, "numContainers", numContainers);
1284
- set$(ctx, "numContainersPooled", numContainers * 2);
1285
- calculateItemsInView(state.scrollVelocity);
1614
+ }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
1615
+ useInit(() => {
1616
+ doInitialAllocateContainers();
1286
1617
  });
1287
- const updateItemSize = useCallback((containerId, itemKey, size) => {
1288
- var _a2;
1289
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
1290
- if (!data2) {
1618
+ const updateItemSize = useCallback((itemKey, size) => {
1619
+ const state = refState.current;
1620
+ const { sizes, indexByKey, sizesLaidOut, data, rowHeights } = state;
1621
+ if (!data) {
1291
1622
  return;
1292
1623
  }
1293
- const state = refState.current;
1294
- const { sizes, indexByKey, columns, sizesLaidOut } = state;
1295
1624
  const index = indexByKey.get(itemKey);
1296
1625
  const numColumns = peek$(ctx, "numColumns");
1297
- const row = Math.floor(index / numColumns);
1298
- const prevSize = getRowHeight(row);
1626
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1627
+ const prevSize = getItemSize(itemKey, index, data);
1628
+ let needsCalculate = false;
1629
+ let needsUpdateContainersDidLayout = false;
1630
+ if (state.numPendingInitialLayout > 0) {
1631
+ state.numPendingInitialLayout--;
1632
+ if (state.numPendingInitialLayout === 0) {
1633
+ needsCalculate = true;
1634
+ state.numPendingInitialLayout = -1;
1635
+ needsUpdateContainersDidLayout = true;
1636
+ }
1637
+ }
1299
1638
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
1300
1639
  let diff;
1640
+ needsCalculate = true;
1301
1641
  if (numColumns > 1) {
1302
- const prevMaxSizeInRow = getRowHeight(row);
1642
+ const rowNumber = Math.floor(index / numColumnsProp);
1643
+ const prevSizeInRow = getRowHeight(rowNumber);
1303
1644
  sizes.set(itemKey, size);
1304
- const column = columns.get(itemKey);
1305
- const loopStart = index - (column - 1);
1306
- let nextMaxSizeInRow = 0;
1307
- for (let i = loopStart; i < loopStart + numColumns && i < data2.length; i++) {
1308
- const id = getId(i);
1309
- const size2 = getItemSize(id, i, data2[i]);
1310
- nextMaxSizeInRow = Math.max(nextMaxSizeInRow, size2);
1311
- }
1312
- diff = nextMaxSizeInRow - prevMaxSizeInRow;
1645
+ rowHeights.delete(rowNumber);
1646
+ const sizeInRow = getRowHeight(rowNumber);
1647
+ diff = sizeInRow - prevSizeInRow;
1313
1648
  } else {
1314
1649
  sizes.set(itemKey, size);
1315
1650
  diff = size - prevSize;
@@ -1335,38 +1670,77 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1335
1670
  }
1336
1671
  refState.current.scrollForNextCalculateItemsInView = void 0;
1337
1672
  addTotalSize(itemKey, diff, 0);
1338
- doMaintainScrollAtEnd(true);
1673
+ doMaintainScrollAtEnd(false);
1674
+ if (onItemSizeChanged) {
1675
+ onItemSizeChanged({
1676
+ size,
1677
+ previous: prevSize,
1678
+ index,
1679
+ itemKey,
1680
+ itemData: data[index]
1681
+ });
1682
+ }
1683
+ }
1684
+ if (needsCalculate) {
1685
+ let fixingScroll = false;
1686
+ if (needsUpdateContainersDidLayout && initialScrollIndex && !state.didInitialScroll) {
1687
+ const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
1688
+ state.didInitialScroll = true;
1689
+ if (updatedOffset !== initialContentOffset) {
1690
+ fixingScroll = true;
1691
+ scrollTo(updatedOffset, false);
1692
+ requestAnimationFrame(() => {
1693
+ const updatedOffset2 = calculateOffsetForIndex(initialScrollIndex);
1694
+ scrollTo(updatedOffset2, false);
1695
+ });
1696
+ }
1697
+ }
1339
1698
  const scrollVelocity = state.scrollVelocity;
1340
- if (!state.waitingForMicrotask && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1341
- if (!peek$(ctx, "containersDidLayout")) {
1342
- state.waitingForMicrotask = true;
1343
- queueMicrotask(() => {
1344
- if (state.waitingForMicrotask) {
1345
- state.waitingForMicrotask = false;
1346
- calculateItemsInView(state.scrollVelocity);
1699
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || state.numPendingInitialLayout < 0)) {
1700
+ const setDidLayout = () => {
1701
+ set$(ctx, "containersDidLayout", true);
1702
+ if (Platform.OS === "web") {
1703
+ setTimeout(() => {
1704
+ state.scrollAdjustHandler.setDisableAdjust(false);
1705
+ }, 32);
1706
+ }
1707
+ };
1708
+ if (Date.now() - state.lastBatchingAction < 500) {
1709
+ state.queuedCalculateItemsInView = requestAnimationFrame(() => {
1710
+ state.queuedCalculateItemsInView = void 0;
1711
+ calculateItemsInView();
1712
+ if (needsUpdateContainersDidLayout) {
1713
+ setDidLayout();
1347
1714
  }
1348
1715
  });
1349
1716
  } else {
1350
- calculateItemsInView(state.scrollVelocity);
1717
+ calculateItemsInView();
1718
+ if (needsUpdateContainersDidLayout) {
1719
+ if (fixingScroll) {
1720
+ requestAnimationFrame(setDidLayout);
1721
+ } else {
1722
+ queueMicrotask(setDidLayout);
1723
+ }
1724
+ }
1351
1725
  }
1352
1726
  }
1353
- if (onItemSizeChanged) {
1354
- onItemSizeChanged({ size, previous: prevSize, index, itemKey, itemData: data2[index] });
1355
- }
1356
1727
  }
1357
1728
  }, []);
1358
- const handleScrollDebounced = useCallback((velocity) => {
1359
- calculateItemsInView(velocity);
1360
- checkAtBottom();
1361
- checkAtTop();
1362
- }, []);
1363
1729
  const onLayout = useCallback((event) => {
1730
+ const state = refState.current;
1364
1731
  const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
1365
- refState.current.scrollLength = scrollLength;
1732
+ const didChange = scrollLength !== state.scrollLength;
1733
+ state.scrollLength = scrollLength;
1734
+ state.lastBatchingAction = Date.now();
1735
+ state.scrollForNextCalculateItemsInView = void 0;
1736
+ doInitialAllocateContainers();
1366
1737
  doMaintainScrollAtEnd(false);
1367
1738
  doUpdatePaddingTop();
1368
1739
  checkAtBottom();
1369
1740
  checkAtTop();
1741
+ if (didChange) {
1742
+ calculateItemsInView();
1743
+ }
1370
1744
  if (__DEV__) {
1371
1745
  const isWidthZero = event.nativeEvent.layout.width === 0;
1372
1746
  const isHeightZero = event.nativeEvent.layout.height === 0;
@@ -1376,17 +1750,24 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1376
1750
  );
1377
1751
  }
1378
1752
  }
1753
+ if (onLayoutProp) {
1754
+ onLayoutProp(event);
1755
+ }
1379
1756
  }, []);
1380
1757
  const handleScroll = useCallback(
1381
1758
  (event, fromSelf) => {
1382
- var _a2, _b2, _c2;
1759
+ var _a2, _b2, _c2, _d2;
1383
1760
  if (((_b2 = (_a2 = event.nativeEvent) == null ? void 0 : _a2.contentSize) == null ? void 0 : _b2.height) === 0 && ((_c2 = event.nativeEvent.contentSize) == null ? void 0 : _c2.width) === 0) {
1384
1761
  return;
1385
1762
  }
1386
1763
  const state = refState.current;
1764
+ const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
1765
+ if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
1766
+ return;
1767
+ }
1387
1768
  state.hasScrolled = true;
1769
+ state.lastBatchingAction = Date.now();
1388
1770
  const currentTime = performance.now();
1389
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
1390
1771
  if (!(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
1391
1772
  state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1392
1773
  }
@@ -1412,9 +1793,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1412
1793
  state.scroll = newScroll;
1413
1794
  state.scrollTime = currentTime;
1414
1795
  state.scrollVelocity = velocity;
1415
- handleScrollDebounced(velocity);
1796
+ calculateItemsInView();
1797
+ checkAtBottom();
1798
+ checkAtTop();
1416
1799
  if (!fromSelf) {
1417
- onScrollProp == null ? void 0 : onScrollProp(event);
1800
+ (_d2 = state.onScroll) == null ? void 0 : _d2.call(state, event);
1418
1801
  }
1419
1802
  },
1420
1803
  []
@@ -1422,61 +1805,161 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1422
1805
  useImperativeHandle(
1423
1806
  forwardedRef,
1424
1807
  () => {
1425
- const scrollToIndex = ({ index, animated }) => {
1426
- const offsetObj = calculateInitialOffset(index);
1427
- const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
1428
- refScroller.current.scrollTo({ ...offset, animated });
1808
+ const scrollToIndex = ({
1809
+ index,
1810
+ viewOffset = 0,
1811
+ animated = true,
1812
+ viewPosition = 0
1813
+ }) => {
1814
+ var _a2;
1815
+ const state = refState.current;
1816
+ const firstIndexOffset = calculateOffsetForIndex(index);
1817
+ let firstIndexScrollPostion = firstIndexOffset - viewOffset;
1818
+ const diff = Math.abs(state.scroll - firstIndexScrollPostion);
1819
+ const needsReanchoring = maintainVisibleContentPosition && diff > 100;
1820
+ state.scrollForNextCalculateItemsInView = void 0;
1821
+ if (needsReanchoring) {
1822
+ const id = getId(index);
1823
+ state.anchorElement = { id, coordinate: firstIndexOffset };
1824
+ (_a2 = state.belowAnchorElementPositions) == null ? void 0 : _a2.clear();
1825
+ state.positions.clear();
1826
+ calcTotalSizesAndPositions({ forgetPositions: true });
1827
+ state.startBufferedId = id;
1828
+ state.minIndexSizeChanged = index;
1829
+ firstIndexScrollPostion = firstIndexOffset - viewOffset + state.scrollAdjustHandler.getAppliedAdjust();
1830
+ }
1831
+ state.scrollAdjustHandler.setDisableAdjust(true);
1832
+ setTimeout(
1833
+ () => {
1834
+ state.scrollAdjustHandler.setDisableAdjust(false);
1835
+ calculateItemsInView();
1836
+ },
1837
+ animated ? 150 : 50
1838
+ );
1839
+ if (viewPosition) {
1840
+ firstIndexScrollPostion -= viewPosition * (state.scrollLength - getItemSize(getId(index), index, state.data[index]));
1841
+ }
1842
+ scrollTo(firstIndexScrollPostion, animated);
1843
+ const totalSizeWithScrollAdjust = peek$(ctx, "totalSizeWithScrollAdjust");
1844
+ if (maintainVisibleContentPosition && totalSizeWithScrollAdjust - firstIndexScrollPostion < state.scrollLength) {
1845
+ const doScrollTo = () => {
1846
+ scrollTo(firstIndexScrollPostion, animated);
1847
+ };
1848
+ setTimeout(doScrollTo, animated ? 150 : 50);
1849
+ if (animated) {
1850
+ setTimeout(doScrollTo, 350);
1851
+ }
1852
+ }
1853
+ };
1854
+ const scrollIndexIntoView = (options) => {
1855
+ if (refState.current) {
1856
+ const { index, ...rest2 } = options;
1857
+ const { startNoBuffer, endNoBuffer } = refState.current;
1858
+ if (index < startNoBuffer || index > endNoBuffer) {
1859
+ const viewPosition = index < startNoBuffer ? 0 : 1;
1860
+ scrollToIndex({
1861
+ ...rest2,
1862
+ viewPosition,
1863
+ index
1864
+ });
1865
+ }
1866
+ }
1429
1867
  };
1430
1868
  return {
1869
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
1431
1870
  getNativeScrollRef: () => refScroller.current,
1432
- getScrollableNode: refScroller.current.getScrollableNode,
1433
- getScrollResponder: refScroller.current.getScrollResponder,
1434
- flashScrollIndicators: refScroller.current.flashScrollIndicators,
1435
- scrollToIndex,
1436
- scrollToOffset: ({ offset, animated }) => {
1437
- const offsetObj = horizontal ? { x: offset, y: 0 } : { x: 0, y: offset };
1438
- refScroller.current.scrollTo({ ...offsetObj, animated });
1871
+ getScrollableNode: () => refScroller.current.getScrollableNode(),
1872
+ getScrollResponder: () => refScroller.current.getScrollResponder(),
1873
+ getState: () => {
1874
+ const state = refState.current;
1875
+ return state ? {
1876
+ contentLength: state.totalSize,
1877
+ end: state.endNoBuffer,
1878
+ endBuffered: state.endBuffered,
1879
+ isAtEnd: state.isAtBottom,
1880
+ isAtStart: state.isAtTop,
1881
+ scroll: state.scroll,
1882
+ scrollLength: state.scrollLength,
1883
+ start: state.startNoBuffer,
1884
+ startBuffered: state.startBuffered
1885
+ } : {};
1886
+ },
1887
+ scrollIndexIntoView,
1888
+ scrollItemIntoView: ({ item, ...props2 }) => {
1889
+ const { data } = refState.current;
1890
+ const index = data.indexOf(item);
1891
+ if (index !== -1) {
1892
+ scrollIndexIntoView({ index, ...props2 });
1893
+ }
1439
1894
  },
1440
- scrollToItem: ({ item, animated }) => {
1895
+ scrollToIndex,
1896
+ scrollToItem: ({ item, ...props2 }) => {
1897
+ const { data } = refState.current;
1441
1898
  const index = data.indexOf(item);
1442
1899
  if (index !== -1) {
1443
- scrollToIndex({ index, animated });
1900
+ scrollToIndex({ index, ...props2 });
1444
1901
  }
1445
1902
  },
1446
- scrollToEnd: refScroller.current.scrollToEnd
1903
+ scrollToOffset: ({ offset, animated }) => {
1904
+ scrollTo(offset, animated);
1905
+ },
1906
+ scrollToEnd: (options) => refScroller.current.scrollToEnd(options)
1447
1907
  };
1448
1908
  },
1449
1909
  []
1450
1910
  );
1451
- return /* @__PURE__ */ React5.createElement(
1911
+ if (Platform.OS === "web") {
1912
+ useEffect(() => {
1913
+ var _a2;
1914
+ if (initialContentOffset) {
1915
+ (_a2 = refState.current) == null ? void 0 : _a2.scrollAdjustHandler.setDisableAdjust(true);
1916
+ scrollTo(initialContentOffset, false);
1917
+ setTimeout(() => {
1918
+ var _a3;
1919
+ (_a3 = refState.current) == null ? void 0 : _a3.scrollAdjustHandler.setDisableAdjust(false);
1920
+ }, 0);
1921
+ }
1922
+ }, []);
1923
+ }
1924
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(
1452
1925
  ListComponent,
1453
1926
  {
1454
1927
  ...rest,
1455
1928
  horizontal,
1456
- refScrollView: (r) => {
1457
- refScroller.current = r;
1458
- if (refScrollView) {
1459
- if (typeof refScrollView === "function") {
1460
- refScrollView(r);
1461
- } else {
1462
- refScrollView.current = r;
1463
- }
1464
- }
1465
- },
1929
+ refScrollView: combinedRef,
1466
1930
  initialContentOffset,
1467
1931
  getRenderedItem,
1468
1932
  updateItemSize,
1469
1933
  handleScroll,
1934
+ onMomentumScrollEnd: (event) => {
1935
+ const wasPaused = refState.current.scrollAdjustHandler.unPauseAdjust();
1936
+ if (wasPaused) {
1937
+ refState.current.scrollVelocity = 0;
1938
+ refState.current.scrollHistory = [];
1939
+ calculateItemsInView();
1940
+ }
1941
+ if (onMomentumScrollEnd) {
1942
+ onMomentumScrollEnd(event);
1943
+ }
1944
+ },
1470
1945
  onLayout,
1471
1946
  recycleItems,
1472
1947
  alignItemsAtEnd,
1473
- ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
1948
+ ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
1474
1949
  maintainVisibleContentPosition,
1475
1950
  scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : Platform.OS === "web" ? 16 : void 0,
1476
1951
  waitForInitialLayout,
1952
+ refreshControl: refreshControl != null ? refreshControl : onRefresh && /* @__PURE__ */ React6.createElement(
1953
+ RefreshControl,
1954
+ {
1955
+ refreshing: !!refreshing,
1956
+ onRefresh,
1957
+ progressViewOffset
1958
+ }
1959
+ ),
1477
1960
  style
1478
1961
  }
1479
- );
1962
+ ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React6.createElement(DebugView, { state: refState.current }));
1480
1963
  });
1481
1964
 
1482
1965
  export { LegendList, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };