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

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.includes(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.includes(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,61 +778,87 @@ 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) => {
624
- if (index) {
838
+ const calculateOffsetForIndex = (index = initialScrollIndex) => {
839
+ var _a2;
840
+ const data = dataProp;
841
+ if (index !== void 0) {
625
842
  let offset = 0;
626
- if (getEstimatedItemSize) {
843
+ const canGetSize = !!refState.current;
844
+ if (canGetSize || getEstimatedItemSize) {
845
+ const sizeFn = (index2) => {
846
+ if (canGetSize) {
847
+ return getItemSize(getId(index2), index2, data[index2]);
848
+ }
849
+ return getEstimatedItemSize(index2, data[index2]);
850
+ };
627
851
  for (let i = 0; i < index; i++) {
628
- offset += getEstimatedItemSize(i, data[i]);
852
+ offset += sizeFn(i);
629
853
  }
630
854
  } else if (estimatedItemSize) {
631
855
  offset = index * estimatedItemSize;
632
856
  }
633
- return offset / numColumnsProp;
857
+ return offset / numColumnsProp - (((_a2 = refState.current) == null ? void 0 : _a2.scrollAdjustHandler.getAppliedAdjust()) || 0);
634
858
  }
635
859
  return 0;
636
860
  };
637
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(calculateInitialOffset, []);
861
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(calculateOffsetForIndex, []);
638
862
  if (!refState.current) {
639
863
  const initialScrollLength = Dimensions.get("window")[horizontal ? "width" : "height"];
640
864
  refState.current = {
@@ -642,14 +866,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
642
866
  positions: /* @__PURE__ */ new Map(),
643
867
  columns: /* @__PURE__ */ new Map(),
644
868
  pendingAdjust: 0,
645
- waitingForMicrotask: false,
646
869
  isStartReached: initialContentOffset < initialScrollLength * onStartReachedThreshold,
647
870
  isEndReached: false,
648
871
  isAtBottom: false,
649
872
  isAtTop: false,
650
- data,
651
- idsInFirstRender: void 0,
652
- hasScrolled: false,
873
+ data: dataProp,
653
874
  scrollLength: initialScrollLength,
654
875
  startBuffered: 0,
655
876
  startNoBuffer: 0,
@@ -669,23 +890,28 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
669
890
  indexByKey: /* @__PURE__ */ new Map(),
670
891
  scrollHistory: [],
671
892
  scrollVelocity: 0,
672
- sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
893
+ sizesKnown: /* @__PURE__ */ new Map(),
673
894
  timeoutSizeMessage: 0,
674
895
  scrollTimer: void 0,
675
896
  belowAnchorElementPositions: void 0,
676
897
  rowHeights: /* @__PURE__ */ new Map(),
677
898
  startReachedBlockedByTimer: false,
899
+ endReachedBlockedByTimer: false,
678
900
  scrollForNextCalculateItemsInView: void 0,
679
- enableScrollForNextCalculateItemsInView: true
901
+ enableScrollForNextCalculateItemsInView: true,
902
+ minIndexSizeChanged: 0,
903
+ numPendingInitialLayout: 0,
904
+ queuedCalculateItemsInView: 0,
905
+ lastBatchingAction: Date.now(),
906
+ onScroll: onScrollProp
680
907
  };
681
- refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
682
908
  if (maintainVisibleContentPosition) {
683
909
  if (initialScrollIndex) {
684
910
  refState.current.anchorElement = {
685
911
  coordinate: initialContentOffset,
686
912
  id: getId(initialScrollIndex)
687
913
  };
688
- } else if (data.length) {
914
+ } else if (dataProp.length) {
689
915
  refState.current.anchorElement = {
690
916
  coordinate: initialContentOffset,
691
917
  id: getId(0)
@@ -698,6 +924,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
698
924
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
699
925
  set$(ctx, "extraData", extraData);
700
926
  }
927
+ const didDataChange = refState.current.data !== dataProp;
928
+ refState.current.data = dataProp;
929
+ refState.current.onScroll = onScrollProp;
701
930
  const getAnchorElementIndex = () => {
702
931
  const state = refState.current;
703
932
  if (state.anchorElement) {
@@ -706,16 +935,36 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
706
935
  }
707
936
  return void 0;
708
937
  };
938
+ const setDidLayout = () => {
939
+ var _a2;
940
+ refState.current.queuedInitialLayout = true;
941
+ if (initialScrollIndex) {
942
+ const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
943
+ (_a2 = refState.current) == null ? void 0 : _a2.scrollAdjustHandler.setDisableAdjust(true);
944
+ queueMicrotask(() => {
945
+ scrollTo(updatedOffset, false);
946
+ requestAnimationFrame(() => {
947
+ var _a3;
948
+ set$(ctx, "containersDidLayout", true);
949
+ (_a3 = refState.current) == null ? void 0 : _a3.scrollAdjustHandler.setDisableAdjust(false);
950
+ });
951
+ });
952
+ } else {
953
+ queueMicrotask(() => {
954
+ set$(ctx, "containersDidLayout", true);
955
+ });
956
+ }
957
+ };
709
958
  const addTotalSize = useCallback((key, add, totalSizeBelowAnchor) => {
710
959
  const state = refState.current;
711
- const index = key === null ? 0 : state.indexByKey.get(key);
960
+ const { indexByKey, anchorElement } = state;
961
+ const index = key === null ? 0 : indexByKey.get(key);
712
962
  let isAboveAnchor = false;
713
963
  if (maintainVisibleContentPosition) {
714
- if (state.anchorElement && index < getAnchorElementIndex()) {
964
+ if (anchorElement && index < getAnchorElementIndex()) {
715
965
  isAboveAnchor = true;
716
966
  }
717
967
  }
718
- state.totalSize;
719
968
  if (key === null) {
720
969
  state.totalSize = add;
721
970
  state.totalSizeBelowAnchor = totalSizeBelowAnchor;
@@ -725,29 +974,30 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
725
974
  state.totalSizeBelowAnchor += add;
726
975
  }
727
976
  }
728
- let applyAdjustValue = void 0;
729
- if (maintainVisibleContentPosition) {
730
- const newAdjust = state.anchorElement.coordinate - state.totalSizeBelowAnchor;
977
+ let applyAdjustValue = 0;
978
+ let resultSize = state.totalSize;
979
+ if (maintainVisibleContentPosition && anchorElement !== void 0) {
980
+ const newAdjust = anchorElement.coordinate - state.totalSizeBelowAnchor;
731
981
  applyAdjustValue = -newAdjust;
732
982
  state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
733
983
  state.rowHeights.clear();
984
+ if (applyAdjustValue !== void 0) {
985
+ resultSize -= applyAdjustValue;
986
+ state.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
987
+ state.scroll -= diff;
988
+ });
989
+ }
734
990
  }
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);
991
+ set$(ctx, "totalSize", state.totalSize);
992
+ set$(ctx, "totalSizeWithScrollAdjust", resultSize);
744
993
  if (alignItemsAtEnd) {
745
994
  doUpdatePaddingTop();
746
995
  }
747
996
  }, []);
748
997
  const getRowHeight = (n) => {
749
- const { rowHeights } = refState.current;
750
- if (numColumnsProp === 1) {
998
+ const { rowHeights, data } = refState.current;
999
+ const numColumns = peek$(ctx, "numColumns");
1000
+ if (numColumns === 1) {
751
1001
  const id = getId(n);
752
1002
  return getItemSize(id, n, data[n]);
753
1003
  }
@@ -755,8 +1005,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
755
1005
  return rowHeights.get(n) || 0;
756
1006
  }
757
1007
  let rowHeight = 0;
758
- const startEl = n * numColumnsProp;
759
- for (let i = startEl; i < startEl + numColumnsProp && i < data.length; i++) {
1008
+ const startEl = n * numColumns;
1009
+ for (let i = startEl; i < startEl + numColumns && i < data.length; i++) {
760
1010
  const id = getId(i);
761
1011
  const size = getItemSize(id, i, data[i]);
762
1012
  rowHeight = Math.max(rowHeight, size);
@@ -775,10 +1025,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
775
1025
  return /* @__PURE__ */ new Map();
776
1026
  }
777
1027
  const map = state.belowAnchorElementPositions || /* @__PURE__ */ new Map();
1028
+ const numColumns = peek$(ctx, "numColumns");
778
1029
  for (let i = anchorIndex - 1; i >= 0; i--) {
779
1030
  const id = getId(i);
780
- const rowNumber = Math.floor(i / numColumnsProp);
781
- if (i % numColumnsProp === 0) {
1031
+ const rowNumber = Math.floor(i / numColumns);
1032
+ if (i % numColumns === 0) {
782
1033
  top -= getRowHeight(rowNumber);
783
1034
  }
784
1035
  map.set(id, top);
@@ -786,37 +1037,61 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
786
1037
  return map;
787
1038
  };
788
1039
  const getElementPositionBelowAchor = (id) => {
1040
+ var _a2;
789
1041
  const state = refState.current;
790
1042
  if (!refState.current.belowAnchorElementPositions) {
791
1043
  state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
792
1044
  }
793
1045
  const res = state.belowAnchorElementPositions.get(id);
794
1046
  if (res === void 0) {
795
- throw new Error("Undefined position below achor");
1047
+ console.warn(`Undefined position below achor ${id} ${(_a2 = state.anchorElement) == null ? void 0 : _a2.id}`);
1048
+ return 0;
796
1049
  }
797
1050
  return res;
798
1051
  };
799
- const calculateItemsInView = useCallback((speed) => {
1052
+ const calculateItemsInView = useCallback(() => {
1053
+ var _a2;
800
1054
  const state = refState.current;
801
1055
  const {
802
- data: data2,
1056
+ data,
803
1057
  scrollLength,
804
- scroll: scrollState,
805
1058
  startBufferedId: startBufferedIdOrig,
806
1059
  positions,
807
1060
  columns,
808
- scrollAdjustHandler
1061
+ scrollAdjustHandler,
1062
+ scrollVelocity: speed
809
1063
  } = state;
810
- if (state.waitingForMicrotask) {
811
- state.waitingForMicrotask = false;
812
- }
813
- if (!data2) {
1064
+ if (!data || scrollLength === 0) {
814
1065
  return;
815
1066
  }
1067
+ const totalSize = peek$(ctx, "totalSizeWithScrollAdjust");
816
1068
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1069
+ const numColumns = peek$(ctx, "numColumns");
817
1070
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
818
1071
  const scrollExtra = Math.max(-16, Math.min(16, speed)) * 16;
819
- const scroll = scrollState - previousScrollAdjust - topPad - scrollExtra;
1072
+ let scrollState = state.scroll;
1073
+ if (!state.queuedInitialLayout && initialScrollIndex) {
1074
+ const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
1075
+ scrollState = updatedOffset;
1076
+ }
1077
+ let scroll = scrollState - previousScrollAdjust - topPad;
1078
+ if (scroll + scrollLength > totalSize) {
1079
+ scroll = totalSize - scrollLength;
1080
+ }
1081
+ if (ENABLE_DEBUG_VIEW) {
1082
+ set$(ctx, "debugRawScroll", scrollState);
1083
+ set$(ctx, "debugComputedScroll", scroll);
1084
+ }
1085
+ let scrollBufferTop = scrollBuffer;
1086
+ let scrollBufferBottom = scrollBuffer;
1087
+ if (scrollExtra > 8) {
1088
+ scrollBufferTop = 0;
1089
+ scrollBufferBottom = scrollBuffer + scrollExtra;
1090
+ }
1091
+ if (scrollExtra < -8) {
1092
+ scrollBufferTop = scrollBuffer - scrollExtra;
1093
+ scrollBufferBottom = 0;
1094
+ }
820
1095
  if (state.scrollForNextCalculateItemsInView) {
821
1096
  const { top: top2, bottom } = state.scrollForNextCalculateItemsInView;
822
1097
  if (scroll > top2 && scroll < bottom) {
@@ -829,8 +1104,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
829
1104
  let startBufferedId = null;
830
1105
  let endNoBuffer = null;
831
1106
  let endBuffered = null;
832
- const originalStartId = startBufferedIdOrig && state.indexByKey.get(startBufferedIdOrig);
833
- let loopStart = originalStartId || 0;
1107
+ let loopStart = startBufferedIdOrig ? state.indexByKey.get(startBufferedIdOrig) || 0 : 0;
1108
+ if (state.minIndexSizeChanged !== void 0) {
1109
+ loopStart = Math.min(state.minIndexSizeChanged, loopStart);
1110
+ state.minIndexSizeChanged = void 0;
1111
+ }
834
1112
  const anchorElementIndex = getAnchorElementIndex();
835
1113
  for (let i = loopStart; i >= 0; i--) {
836
1114
  const id = getId(i);
@@ -843,7 +1121,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
843
1121
  }
844
1122
  const top2 = newPosition || positions.get(id);
845
1123
  if (top2 !== void 0) {
846
- const size = getItemSize(id, i, data2[i]);
1124
+ const size = getItemSize(id, i, data[i]);
847
1125
  const bottom = top2 + size;
848
1126
  if (bottom > scroll - scrollBuffer) {
849
1127
  loopStart = i;
@@ -852,7 +1130,6 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
852
1130
  }
853
1131
  }
854
1132
  }
855
- const numColumns = peek$(ctx, "numColumns");
856
1133
  const loopStartMod = loopStart % numColumns;
857
1134
  if (loopStartMod > 0) {
858
1135
  loopStart -= loopStartMod;
@@ -861,22 +1138,22 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
861
1138
  let column = 1;
862
1139
  let maxSizeInRow = 0;
863
1140
  const getInitialTop = (i) => {
864
- var _a2;
1141
+ var _a3;
865
1142
  const id = getId(i);
866
1143
  let topOffset = 0;
867
1144
  if (positions.get(id)) {
868
1145
  topOffset = positions.get(id);
869
1146
  }
870
- if (id === ((_a2 = state.anchorElement) == null ? void 0 : _a2.id)) {
871
- topOffset = initialContentOffset || 0;
1147
+ if (id === ((_a3 = state.anchorElement) == null ? void 0 : _a3.id)) {
1148
+ topOffset = state.anchorElement.coordinate;
872
1149
  }
873
1150
  return topOffset;
874
1151
  };
875
- for (let i = loopStart; i < data2.length; i++) {
1152
+ for (let i = loopStart; i < data.length; i++) {
876
1153
  const id = getId(i);
877
- const size = getItemSize(id, i, data2[i]);
1154
+ const size = getItemSize(id, i, data[i]);
878
1155
  maxSizeInRow = Math.max(maxSizeInRow, size);
879
- if (top === void 0) {
1156
+ if (top === void 0 || id === ((_a2 = state.anchorElement) == null ? void 0 : _a2.id)) {
880
1157
  top = getInitialTop(i);
881
1158
  }
882
1159
  if (positions.get(id) !== top) {
@@ -888,7 +1165,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
888
1165
  if (startNoBuffer === null && top + size > scroll) {
889
1166
  startNoBuffer = i;
890
1167
  }
891
- if (startBuffered === null && top + size > scroll - scrollBuffer) {
1168
+ if (startBuffered === null && top + size > scroll - scrollBufferTop) {
892
1169
  startBuffered = i;
893
1170
  startBufferedId = id;
894
1171
  }
@@ -896,7 +1173,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
896
1173
  if (top <= scrollBottom) {
897
1174
  endNoBuffer = i;
898
1175
  }
899
- if (top <= scrollBottom + scrollBuffer) {
1176
+ if (top <= scrollBottom + scrollBufferBottom) {
900
1177
  endBuffered = i;
901
1178
  } else {
902
1179
  break;
@@ -929,6 +1206,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
929
1206
  if (startBuffered !== null && endBuffered !== null) {
930
1207
  const prevNumContainers = ctx.values.get("numContainers");
931
1208
  let numContainers = prevNumContainers;
1209
+ let didWarnMoreContainers = false;
932
1210
  for (let i = startBuffered; i <= endBuffered; i++) {
933
1211
  let isContained = false;
934
1212
  const id = getId(i);
@@ -962,18 +1240,19 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
962
1240
  if (furthestIndex >= 0) {
963
1241
  set$(ctx, `containerItemKey${furthestIndex}`, id);
964
1242
  const index = state.indexByKey.get(id);
965
- set$(ctx, `containerItemData${furthestIndex}`, data2[index]);
1243
+ set$(ctx, `containerItemData${furthestIndex}`, data[index]);
966
1244
  } else {
967
1245
  const containerId = numContainers;
968
1246
  numContainers++;
969
1247
  set$(ctx, `containerItemKey${containerId}`, id);
970
1248
  const index = state.indexByKey.get(id);
971
- set$(ctx, `containerItemData${containerId}`, data2[index]);
1249
+ set$(ctx, `containerItemData${containerId}`, data[index]);
972
1250
  set$(ctx, `containerPosition${containerId}`, ANCHORED_POSITION_OUT_OF_VIEW);
973
1251
  set$(ctx, `containerColumn${containerId}`, -1);
974
- if (__DEV__ && numContainers > peek$(ctx, "numContainersPooled")) {
1252
+ if (__DEV__ && !didWarnMoreContainers && numContainers > peek$(ctx, "numContainersPooled")) {
1253
+ didWarnMoreContainers = true;
975
1254
  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:",
1255
+ "[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
1256
  numContainers
978
1257
  );
979
1258
  }
@@ -983,19 +1262,19 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
983
1262
  if (numContainers !== prevNumContainers) {
984
1263
  set$(ctx, "numContainers", numContainers);
985
1264
  if (numContainers > peek$(ctx, "numContainersPooled")) {
986
- set$(ctx, "numContainersPooled", numContainers);
1265
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
987
1266
  }
988
1267
  }
989
1268
  for (let i = 0; i < numContainers; i++) {
990
1269
  const itemKey = peek$(ctx, `containerItemKey${i}`);
991
1270
  const itemIndex = state.indexByKey.get(itemKey);
992
- const item = data2[itemIndex];
993
- if (item) {
1271
+ const item = data[itemIndex];
1272
+ if (item !== void 0) {
994
1273
  const id = getId(itemIndex);
995
1274
  if (itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered) {
996
1275
  const prevPos = peek$(ctx, `containerPosition${i}`).top;
997
1276
  const pos = positions.get(id) || 0;
998
- const size = getItemSize(id, itemIndex, data2[i]);
1277
+ const size = getItemSize(id, itemIndex, data[i]);
999
1278
  if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
1000
1279
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1001
1280
  }
@@ -1007,9 +1286,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1007
1286
  };
1008
1287
  const column2 = columns.get(id) || 1;
1009
1288
  if (maintainVisibleContentPosition && itemIndex < anchorElementIndex) {
1010
- const currentRow = Math.floor(itemIndex / numColumnsProp);
1289
+ const currentRow = Math.floor(itemIndex / numColumns);
1011
1290
  const rowHeight = getRowHeight(currentRow);
1012
- const elementHeight = getItemSize(id, itemIndex, data2[i]);
1291
+ const elementHeight = getItemSize(id, itemIndex, data[i]);
1013
1292
  const diff = rowHeight - elementHeight;
1014
1293
  pos.relativeCoordinate = pos.top + getRowHeight(currentRow) - diff;
1015
1294
  pos.type = "bottom";
@@ -1024,13 +1303,25 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1024
1303
  set$(ctx, `containerColumn${i}`, column2);
1025
1304
  }
1026
1305
  if (prevData !== item) {
1027
- set$(ctx, `containerItemData${i}`, data2[itemIndex]);
1306
+ set$(ctx, `containerItemData${i}`, data[itemIndex]);
1028
1307
  }
1029
1308
  }
1030
1309
  }
1031
1310
  }
1032
1311
  }
1033
- set$(ctx, "containersDidLayout", true);
1312
+ if (state.numPendingInitialLayout === 0) {
1313
+ state.numPendingInitialLayout = state.endBuffered - state.startBuffered + 1;
1314
+ }
1315
+ if (!state.queuedInitialLayout && endBuffered !== null) {
1316
+ let areAllKnown = true;
1317
+ for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1318
+ const key = getId(i);
1319
+ areAllKnown && (areAllKnown = state.sizesKnown.has(key));
1320
+ }
1321
+ if (areAllKnown) {
1322
+ setDidLayout();
1323
+ }
1324
+ }
1034
1325
  if (state.viewabilityConfigCallbackPairs) {
1035
1326
  updateViewableItems(
1036
1327
  state,
@@ -1045,16 +1336,27 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1045
1336
  }, []);
1046
1337
  const doUpdatePaddingTop = () => {
1047
1338
  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));
1339
+ const { scrollLength } = refState.current;
1340
+ const contentSize = getContentSize(ctx);
1341
+ const paddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1051
1342
  set$(ctx, "paddingTop", paddingTop);
1052
1343
  }
1053
1344
  };
1345
+ const scrollTo = (offset, animated) => {
1346
+ var _a2;
1347
+ (_a2 = refScroller.current) == null ? void 0 : _a2.scrollTo({
1348
+ x: horizontal ? offset : 0,
1349
+ y: horizontal ? 0 : offset,
1350
+ animated: !!animated
1351
+ });
1352
+ };
1054
1353
  const doMaintainScrollAtEnd = (animated) => {
1055
1354
  const state = refState.current;
1056
- if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd) {
1057
- state.scroll = state.totalSize - state.scrollLength + peek$(ctx, "paddingTop");
1355
+ if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
1356
+ const paddingTop = peek$(ctx, "paddingTop") || 0;
1357
+ if (paddingTop > 0) {
1358
+ state.scroll = 0;
1359
+ }
1058
1360
  requestAnimationFrame(() => {
1059
1361
  var _a2;
1060
1362
  (_a2 = refScroller.current) == null ? void 0 : _a2.scrollToEnd({
@@ -1064,28 +1366,48 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1064
1366
  return true;
1065
1367
  }
1066
1368
  };
1369
+ const checkThreshold = (distance, threshold, isReached, isBlockedByTimer, onReached, blockTimer) => {
1370
+ const distanceAbs = Math.abs(distance);
1371
+ const isAtThreshold = distanceAbs < threshold;
1372
+ if (!isReached && !isBlockedByTimer) {
1373
+ if (isAtThreshold) {
1374
+ onReached == null ? void 0 : onReached(distance);
1375
+ blockTimer == null ? void 0 : blockTimer(true);
1376
+ setTimeout(() => {
1377
+ blockTimer == null ? void 0 : blockTimer(false);
1378
+ }, 700);
1379
+ return true;
1380
+ }
1381
+ } else {
1382
+ if (distance >= 1.3 * threshold) {
1383
+ return false;
1384
+ }
1385
+ }
1386
+ return isReached;
1387
+ };
1067
1388
  const checkAtBottom = () => {
1068
1389
  if (!refState.current) {
1069
1390
  return;
1070
1391
  }
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
- }
1392
+ const { scrollLength, scroll, hasScrolled } = refState.current;
1393
+ const contentSize = getContentSize(ctx);
1394
+ if (contentSize > 0 && hasScrolled) {
1395
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1396
+ const distanceFromEndAbs = Math.abs(distanceFromEnd);
1397
+ refState.current.isAtBottom = distanceFromEndAbs < scrollLength * maintainScrollAtEndThreshold;
1398
+ refState.current.isEndReached = checkThreshold(
1399
+ distanceFromEnd,
1400
+ onEndReachedThreshold * scrollLength,
1401
+ refState.current.isEndReached,
1402
+ refState.current.endReachedBlockedByTimer,
1403
+ (distance) => {
1404
+ var _a2, _b2;
1405
+ return (_b2 = (_a2 = callbacks.current).onEndReached) == null ? void 0 : _b2.call(_a2, { distanceFromEnd: distance });
1406
+ },
1407
+ (block) => {
1408
+ refState.current.endReachedBlockedByTimer = block;
1087
1409
  }
1088
- }
1410
+ );
1089
1411
  }
1090
1412
  };
1091
1413
  const checkAtTop = () => {
@@ -1094,112 +1416,118 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1094
1416
  }
1095
1417
  const { scrollLength, scroll } = refState.current;
1096
1418
  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
- }
1419
+ const distanceFromTopAbs = Math.abs(distanceFromTop);
1420
+ refState.current.isAtTop = distanceFromTopAbs < 0;
1421
+ refState.current.isStartReached = checkThreshold(
1422
+ distanceFromTop,
1423
+ onStartReachedThreshold * scrollLength,
1424
+ refState.current.isStartReached,
1425
+ refState.current.startReachedBlockedByTimer,
1426
+ (distance) => {
1427
+ var _a2, _b2;
1428
+ return (_b2 = (_a2 = callbacks.current).onStartReached) == null ? void 0 : _b2.call(_a2, { distanceFromStart: distance });
1429
+ },
1430
+ (block) => {
1431
+ refState.current.startReachedBlockedByTimer = block;
1112
1432
  }
1113
- }
1433
+ );
1114
1434
  };
1115
- const checkResetContainers = (reset) => {
1435
+ const checkResetContainers = (isFirst2) => {
1116
1436
  const state = refState.current;
1117
1437
  if (state) {
1118
- state.data = data;
1119
- if (reset) {
1438
+ state.data = dataProp;
1439
+ if (!isFirst2) {
1120
1440
  refState.current.scrollForNextCalculateItemsInView = void 0;
1121
1441
  const numContainers = peek$(ctx, "numContainers");
1122
1442
  for (let i = 0; i < numContainers; i++) {
1123
1443
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1124
1444
  if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
1125
1445
  set$(ctx, `containerItemKey${i}`, void 0);
1446
+ set$(ctx, `containerItemData${i}`, void 0);
1126
1447
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1127
1448
  set$(ctx, `containerColumn${i}`, -1);
1128
1449
  }
1129
1450
  }
1130
1451
  if (!keyExtractorProp) {
1131
- state.sizes.clear();
1132
1452
  state.positions.clear();
1133
1453
  }
1134
- calculateItemsInView(state.scrollVelocity);
1135
- }
1136
- const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1137
- if (!didMaintainScrollAtEnd && data.length > state.data.length) {
1138
- state.isEndReached = false;
1454
+ calculateItemsInView();
1455
+ const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1456
+ if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1457
+ state.isEndReached = false;
1458
+ }
1459
+ checkAtTop();
1460
+ checkAtBottom();
1139
1461
  }
1140
- checkAtTop();
1141
- checkAtBottom();
1142
1462
  }
1143
1463
  };
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;
1464
+ const calcTotalSizesAndPositions = ({ forgetPositions = false }) => {
1465
+ var _a2, _b2;
1151
1466
  let totalSize = 0;
1152
1467
  let totalSizeBelowIndex = 0;
1153
1468
  const indexByKey = /* @__PURE__ */ new Map();
1154
1469
  const newPositions = /* @__PURE__ */ new Map();
1155
1470
  let column = 1;
1156
1471
  let maxSizeInRow = 0;
1157
- for (let i = 0; i < data.length; i++) {
1472
+ const numColumns = (_a2 = peek$(ctx, "numColumns")) != null ? _a2 : numColumnsProp;
1473
+ if (!refState.current) {
1474
+ return;
1475
+ }
1476
+ for (let i = 0; i < dataProp.length; i++) {
1158
1477
  const key = getId(i);
1478
+ if (__DEV__) {
1479
+ if (indexByKey.has(key)) {
1480
+ console.error(
1481
+ `[legend-list] Error: Detected overlapping key (${key}) which causes missing items and gaps and other terrrible things. Check that keyExtractor returns unique values.`
1482
+ );
1483
+ }
1484
+ }
1159
1485
  indexByKey.set(key, i);
1160
- if (refState.current.positions.get(key) != null && refState.current.indexByKey.get(key) === i) {
1486
+ if (!forgetPositions && refState.current.positions.get(key) != null && refState.current.indexByKey.get(key) === i) {
1161
1487
  newPositions.set(key, refState.current.positions.get(key));
1162
1488
  }
1163
1489
  }
1164
1490
  refState.current.indexByKey = indexByKey;
1165
1491
  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 });
1492
+ if (!forgetPositions && !isFirst) {
1493
+ if (maintainVisibleContentPosition) {
1494
+ if (refState.current.anchorElement == null || indexByKey.get(refState.current.anchorElement.id) == null) {
1495
+ if (dataProp.length) {
1496
+ const newAnchorElement = {
1497
+ coordinate: 0,
1498
+ id: getId(0)
1499
+ };
1500
+ refState.current.anchorElement = newAnchorElement;
1501
+ (_b2 = refState.current.belowAnchorElementPositions) == null ? void 0 : _b2.clear();
1502
+ scrollTo(0, false);
1503
+ setTimeout(() => {
1504
+ calculateItemsInView();
1505
+ }, 0);
1506
+ } else {
1507
+ refState.current.startBufferedId = void 0;
1508
+ }
1509
+ }
1510
+ } else {
1511
+ if (refState.current.startBufferedId != null && newPositions.get(refState.current.startBufferedId) == null) {
1512
+ if (dataProp.length) {
1513
+ refState.current.startBufferedId = getId(0);
1514
+ } else {
1515
+ refState.current.startBufferedId = void 0;
1516
+ }
1517
+ scrollTo(0, false);
1176
1518
  setTimeout(() => {
1177
- calculateItemsInView(0);
1519
+ calculateItemsInView();
1178
1520
  }, 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
1521
  }
1190
- refScroller.current.scrollTo({ x: 0, y: 0, animated: false });
1191
- setTimeout(() => {
1192
- calculateItemsInView(0);
1193
- }, 0);
1194
1522
  }
1195
1523
  }
1196
1524
  const anchorElementIndex = getAnchorElementIndex();
1197
- for (let i = 0; i < data.length; i++) {
1525
+ for (let i = 0; i < dataProp.length; i++) {
1198
1526
  const key = getId(i);
1199
- const size = getItemSize(key, i, data[i]);
1527
+ const size = getItemSize(key, i, dataProp[i]);
1200
1528
  maxSizeInRow = Math.max(maxSizeInRow, size);
1201
1529
  column++;
1202
- if (column > numColumnsProp) {
1530
+ if (column > numColumns) {
1203
1531
  if (maintainVisibleContentPosition && anchorElementIndex !== void 0 && i < anchorElementIndex) {
1204
1532
  totalSizeBelowIndex += maxSizeInRow;
1205
1533
  }
@@ -1211,36 +1539,59 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1211
1539
  if (maxSizeInRow > 0) {
1212
1540
  totalSize += maxSizeInRow;
1213
1541
  }
1542
+ const state = refState.current;
1543
+ state.ignoreScrollFromCalcTotal = true;
1544
+ requestAnimationFrame(() => {
1545
+ state.ignoreScrollFromCalcTotal = false;
1546
+ });
1214
1547
  addTotalSize(null, totalSize, totalSizeBelowIndex);
1215
- }
1216
- useEffect(() => {
1217
- checkResetContainers(
1218
- /*reset*/
1219
- !isFirst
1548
+ };
1549
+ const isFirst = !refState.current.renderItem;
1550
+ const memoizedLastItemKeys = useMemo(() => {
1551
+ if (!dataProp.length) return [];
1552
+ return Array.from(
1553
+ { length: Math.min(numColumnsProp, dataProp.length) },
1554
+ (_, i) => getId(dataProp.length - 1 - i)
1220
1555
  );
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;
1556
+ }, [dataProp, numColumnsProp]);
1228
1557
  const initalizeStateVars = () => {
1229
- set$(ctx, "lastItemKey", lastItemKey);
1558
+ set$(ctx, "lastItemKeys", memoizedLastItemKeys);
1230
1559
  set$(ctx, "numColumns", numColumnsProp);
1231
1560
  set$(ctx, "stylePaddingTop", stylePaddingTop);
1232
1561
  };
1233
1562
  if (isFirst) {
1234
1563
  initalizeStateVars();
1235
1564
  }
1236
- useEffect(initalizeStateVars, [lastItemKey, numColumnsProp, stylePaddingTop]);
1565
+ if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
1566
+ refState.current.lastBatchingAction = Date.now();
1567
+ if (!keyExtractorProp && !isFirst && didDataChange) {
1568
+ refState.current.sizes.clear();
1569
+ refState.current.positions.clear();
1570
+ }
1571
+ calcTotalSizesAndPositions({ forgetPositions: false });
1572
+ }
1573
+ useEffect(() => {
1574
+ const didAllocateContainers = doInitialAllocateContainers();
1575
+ if (!didAllocateContainers) {
1576
+ checkResetContainers(
1577
+ /*isFirst*/
1578
+ isFirst
1579
+ );
1580
+ }
1581
+ }, [isFirst, dataProp, numColumnsProp]);
1582
+ useEffect(() => {
1583
+ set$(ctx, "extraData", extraData);
1584
+ }, [extraData]);
1585
+ refState.current.renderItem = renderItem;
1586
+ 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;
1587
+ useEffect(initalizeStateVars, [memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingTop]);
1237
1588
  const getRenderedItem = useCallback((key) => {
1238
1589
  var _a2, _b2;
1239
1590
  const state = refState.current;
1240
1591
  if (!state) {
1241
1592
  return null;
1242
1593
  }
1243
- const { data: data2, indexByKey } = state;
1594
+ const { data, indexByKey } = state;
1244
1595
  const index = indexByKey.get(key);
1245
1596
  if (index === void 0) {
1246
1597
  return null;
@@ -1258,64 +1609,86 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1258
1609
  return useRecyclingState(valueOrFun);
1259
1610
  };
1260
1611
  const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
1261
- item: data2[index],
1612
+ item: data[index],
1262
1613
  index,
1614
+ extraData: peek$(ctx, "extraData"),
1263
1615
  useViewability: useViewability2,
1264
1616
  useViewabilityAmount: useViewabilityAmount2,
1265
1617
  useRecyclingEffect: useRecyclingEffect2,
1266
1618
  useRecyclingState: useRecyclingState2
1267
1619
  });
1268
- return { index, renderedItem };
1620
+ return { index, item: data[index], renderedItem };
1269
1621
  }, []);
1270
- useInit(() => {
1622
+ const doInitialAllocateContainers = () => {
1271
1623
  var _a2;
1272
1624
  const state = refState.current;
1273
- const viewability = setupViewability(props);
1625
+ const { scrollLength, data } = state;
1626
+ if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
1627
+ const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1628
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1629
+ for (let i = 0; i < numContainers; i++) {
1630
+ set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1631
+ set$(ctx, `containerColumn${i}`, -1);
1632
+ }
1633
+ set$(ctx, "numContainers", numContainers);
1634
+ set$(ctx, "numContainersPooled", numContainers * initialContainerPoolRatio);
1635
+ if (initialScrollIndex) {
1636
+ requestAnimationFrame(() => {
1637
+ calculateItemsInView();
1638
+ });
1639
+ } else {
1640
+ calculateItemsInView();
1641
+ }
1642
+ return true;
1643
+ }
1644
+ };
1645
+ useEffect(() => {
1646
+ const state = refState.current;
1647
+ const viewability = setupViewability({
1648
+ viewabilityConfig,
1649
+ viewabilityConfigCallbackPairs,
1650
+ onViewableItemsChanged
1651
+ });
1274
1652
  state.viewabilityConfigCallbackPairs = viewability;
1275
1653
  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);
1654
+ }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
1655
+ useInit(() => {
1656
+ doInitialAllocateContainers();
1286
1657
  });
1287
- const updateItemSize = useCallback((containerId, itemKey, size) => {
1288
- var _a2;
1289
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
1290
- if (!data2) {
1658
+ const updateItemSize = useCallback((itemKey, size) => {
1659
+ const state = refState.current;
1660
+ const { sizes, indexByKey, sizesKnown, data, rowHeights } = state;
1661
+ if (!data) {
1291
1662
  return;
1292
1663
  }
1293
- const state = refState.current;
1294
- const { sizes, indexByKey, columns, sizesLaidOut } = state;
1295
1664
  const index = indexByKey.get(itemKey);
1296
1665
  const numColumns = peek$(ctx, "numColumns");
1297
- const row = Math.floor(index / numColumns);
1298
- const prevSize = getRowHeight(row);
1666
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1667
+ const prevSize = getItemSize(itemKey, index, data);
1668
+ let needsCalculate = false;
1669
+ if (state.numPendingInitialLayout > 0) {
1670
+ state.numPendingInitialLayout--;
1671
+ if (state.numPendingInitialLayout === 0) {
1672
+ needsCalculate = true;
1673
+ state.numPendingInitialLayout = -1;
1674
+ }
1675
+ }
1676
+ sizesKnown == null ? void 0 : sizesKnown.set(itemKey, size);
1299
1677
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
1300
1678
  let diff;
1679
+ needsCalculate = true;
1301
1680
  if (numColumns > 1) {
1302
- const prevMaxSizeInRow = getRowHeight(row);
1681
+ const rowNumber = Math.floor(index / numColumnsProp);
1682
+ const prevSizeInRow = getRowHeight(rowNumber);
1303
1683
  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;
1684
+ rowHeights.delete(rowNumber);
1685
+ const sizeInRow = getRowHeight(rowNumber);
1686
+ diff = sizeInRow - prevSizeInRow;
1313
1687
  } else {
1314
1688
  sizes.set(itemKey, size);
1315
1689
  diff = size - prevSize;
1316
1690
  }
1317
1691
  if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
1318
- sizesLaidOut.set(itemKey, size);
1319
1692
  if (state.timeoutSizeMessage) {
1320
1693
  clearTimeout(state.timeoutSizeMessage);
1321
1694
  }
@@ -1323,7 +1696,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1323
1696
  state.timeoutSizeMessage = void 0;
1324
1697
  let total = 0;
1325
1698
  let num = 0;
1326
- for (const [key, size2] of sizesLaidOut) {
1699
+ for (const [_, size2] of sizesKnown) {
1327
1700
  num++;
1328
1701
  total += size2;
1329
1702
  }
@@ -1335,38 +1708,48 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1335
1708
  }
1336
1709
  refState.current.scrollForNextCalculateItemsInView = void 0;
1337
1710
  addTotalSize(itemKey, diff, 0);
1338
- doMaintainScrollAtEnd(true);
1711
+ doMaintainScrollAtEnd(false);
1712
+ if (onItemSizeChanged) {
1713
+ onItemSizeChanged({
1714
+ size,
1715
+ previous: prevSize,
1716
+ index,
1717
+ itemKey,
1718
+ itemData: data[index]
1719
+ });
1720
+ }
1721
+ }
1722
+ if (needsCalculate) {
1339
1723
  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);
1347
- }
1348
- });
1724
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || state.numPendingInitialLayout < 0)) {
1725
+ if (Date.now() - state.lastBatchingAction < 500) {
1726
+ if (!state.queuedCalculateItemsInView) {
1727
+ state.queuedCalculateItemsInView = requestAnimationFrame(() => {
1728
+ state.queuedCalculateItemsInView = void 0;
1729
+ calculateItemsInView();
1730
+ });
1731
+ }
1349
1732
  } else {
1350
- calculateItemsInView(state.scrollVelocity);
1733
+ calculateItemsInView();
1351
1734
  }
1352
1735
  }
1353
- if (onItemSizeChanged) {
1354
- onItemSizeChanged({ size, previous: prevSize, index, itemKey, itemData: data2[index] });
1355
- }
1356
1736
  }
1357
1737
  }, []);
1358
- const handleScrollDebounced = useCallback((velocity) => {
1359
- calculateItemsInView(velocity);
1360
- checkAtBottom();
1361
- checkAtTop();
1362
- }, []);
1363
1738
  const onLayout = useCallback((event) => {
1739
+ const state = refState.current;
1364
1740
  const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
1365
- refState.current.scrollLength = scrollLength;
1741
+ const didChange = scrollLength !== state.scrollLength;
1742
+ state.scrollLength = scrollLength;
1743
+ state.lastBatchingAction = Date.now();
1744
+ state.scrollForNextCalculateItemsInView = void 0;
1745
+ doInitialAllocateContainers();
1366
1746
  doMaintainScrollAtEnd(false);
1367
1747
  doUpdatePaddingTop();
1368
1748
  checkAtBottom();
1369
1749
  checkAtTop();
1750
+ if (didChange) {
1751
+ calculateItemsInView();
1752
+ }
1370
1753
  if (__DEV__) {
1371
1754
  const isWidthZero = event.nativeEvent.layout.width === 0;
1372
1755
  const isHeightZero = event.nativeEvent.layout.height === 0;
@@ -1376,17 +1759,24 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1376
1759
  );
1377
1760
  }
1378
1761
  }
1762
+ if (onLayoutProp) {
1763
+ onLayoutProp(event);
1764
+ }
1379
1765
  }, []);
1380
1766
  const handleScroll = useCallback(
1381
1767
  (event, fromSelf) => {
1382
- var _a2, _b2, _c2;
1768
+ var _a2, _b2, _c2, _d2;
1383
1769
  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
1770
  return;
1385
1771
  }
1386
1772
  const state = refState.current;
1773
+ const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
1774
+ if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
1775
+ return;
1776
+ }
1387
1777
  state.hasScrolled = true;
1778
+ state.lastBatchingAction = Date.now();
1388
1779
  const currentTime = performance.now();
1389
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
1390
1780
  if (!(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
1391
1781
  state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1392
1782
  }
@@ -1412,9 +1802,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1412
1802
  state.scroll = newScroll;
1413
1803
  state.scrollTime = currentTime;
1414
1804
  state.scrollVelocity = velocity;
1415
- handleScrollDebounced(velocity);
1805
+ calculateItemsInView();
1806
+ checkAtBottom();
1807
+ checkAtTop();
1416
1808
  if (!fromSelf) {
1417
- onScrollProp == null ? void 0 : onScrollProp(event);
1809
+ (_d2 = state.onScroll) == null ? void 0 : _d2.call(state, event);
1418
1810
  }
1419
1811
  },
1420
1812
  []
@@ -1422,61 +1814,161 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1422
1814
  useImperativeHandle(
1423
1815
  forwardedRef,
1424
1816
  () => {
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 });
1817
+ const scrollToIndex = ({
1818
+ index,
1819
+ viewOffset = 0,
1820
+ animated = true,
1821
+ viewPosition = 0
1822
+ }) => {
1823
+ var _a2;
1824
+ const state = refState.current;
1825
+ const firstIndexOffset = calculateOffsetForIndex(index);
1826
+ let firstIndexScrollPostion = firstIndexOffset - viewOffset;
1827
+ const diff = Math.abs(state.scroll - firstIndexScrollPostion);
1828
+ const needsReanchoring = maintainVisibleContentPosition && diff > 100;
1829
+ state.scrollForNextCalculateItemsInView = void 0;
1830
+ if (needsReanchoring) {
1831
+ const id = getId(index);
1832
+ state.anchorElement = { id, coordinate: firstIndexOffset };
1833
+ (_a2 = state.belowAnchorElementPositions) == null ? void 0 : _a2.clear();
1834
+ state.positions.clear();
1835
+ calcTotalSizesAndPositions({ forgetPositions: true });
1836
+ state.startBufferedId = id;
1837
+ state.minIndexSizeChanged = index;
1838
+ firstIndexScrollPostion = firstIndexOffset - viewOffset + state.scrollAdjustHandler.getAppliedAdjust();
1839
+ }
1840
+ state.scrollAdjustHandler.setDisableAdjust(true);
1841
+ setTimeout(
1842
+ () => {
1843
+ state.scrollAdjustHandler.setDisableAdjust(false);
1844
+ calculateItemsInView();
1845
+ },
1846
+ animated ? 150 : 50
1847
+ );
1848
+ if (viewPosition) {
1849
+ firstIndexScrollPostion -= viewPosition * (state.scrollLength - getItemSize(getId(index), index, state.data[index]));
1850
+ }
1851
+ scrollTo(firstIndexScrollPostion, animated);
1852
+ const totalSizeWithScrollAdjust = peek$(ctx, "totalSizeWithScrollAdjust");
1853
+ if (maintainVisibleContentPosition && totalSizeWithScrollAdjust - firstIndexScrollPostion < state.scrollLength) {
1854
+ const doScrollTo = () => {
1855
+ scrollTo(firstIndexScrollPostion, animated);
1856
+ };
1857
+ setTimeout(doScrollTo, animated ? 150 : 50);
1858
+ if (animated) {
1859
+ setTimeout(doScrollTo, 350);
1860
+ }
1861
+ }
1862
+ };
1863
+ const scrollIndexIntoView = (options) => {
1864
+ if (refState.current) {
1865
+ const { index, ...rest2 } = options;
1866
+ const { startNoBuffer, endNoBuffer } = refState.current;
1867
+ if (index < startNoBuffer || index > endNoBuffer) {
1868
+ const viewPosition = index < startNoBuffer ? 0 : 1;
1869
+ scrollToIndex({
1870
+ ...rest2,
1871
+ viewPosition,
1872
+ index
1873
+ });
1874
+ }
1875
+ }
1429
1876
  };
1430
1877
  return {
1878
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
1431
1879
  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 });
1880
+ getScrollableNode: () => refScroller.current.getScrollableNode(),
1881
+ getScrollResponder: () => refScroller.current.getScrollResponder(),
1882
+ getState: () => {
1883
+ const state = refState.current;
1884
+ return state ? {
1885
+ contentLength: state.totalSize,
1886
+ end: state.endNoBuffer,
1887
+ endBuffered: state.endBuffered,
1888
+ isAtEnd: state.isAtBottom,
1889
+ isAtStart: state.isAtTop,
1890
+ scroll: state.scroll,
1891
+ scrollLength: state.scrollLength,
1892
+ start: state.startNoBuffer,
1893
+ startBuffered: state.startBuffered
1894
+ } : {};
1895
+ },
1896
+ scrollIndexIntoView,
1897
+ scrollItemIntoView: ({ item, ...props2 }) => {
1898
+ const { data } = refState.current;
1899
+ const index = data.indexOf(item);
1900
+ if (index !== -1) {
1901
+ scrollIndexIntoView({ index, ...props2 });
1902
+ }
1439
1903
  },
1440
- scrollToItem: ({ item, animated }) => {
1904
+ scrollToIndex,
1905
+ scrollToItem: ({ item, ...props2 }) => {
1906
+ const { data } = refState.current;
1441
1907
  const index = data.indexOf(item);
1442
1908
  if (index !== -1) {
1443
- scrollToIndex({ index, animated });
1909
+ scrollToIndex({ index, ...props2 });
1444
1910
  }
1445
1911
  },
1446
- scrollToEnd: refScroller.current.scrollToEnd
1912
+ scrollToOffset: ({ offset, animated }) => {
1913
+ scrollTo(offset, animated);
1914
+ },
1915
+ scrollToEnd: (options) => refScroller.current.scrollToEnd(options)
1447
1916
  };
1448
1917
  },
1449
1918
  []
1450
1919
  );
1451
- return /* @__PURE__ */ React5.createElement(
1920
+ if (Platform.OS === "web") {
1921
+ useEffect(() => {
1922
+ var _a2;
1923
+ if (initialContentOffset) {
1924
+ (_a2 = refState.current) == null ? void 0 : _a2.scrollAdjustHandler.setDisableAdjust(true);
1925
+ scrollTo(initialContentOffset, false);
1926
+ setTimeout(() => {
1927
+ var _a3;
1928
+ (_a3 = refState.current) == null ? void 0 : _a3.scrollAdjustHandler.setDisableAdjust(false);
1929
+ }, 0);
1930
+ }
1931
+ }, []);
1932
+ }
1933
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(
1452
1934
  ListComponent,
1453
1935
  {
1454
1936
  ...rest,
1455
1937
  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
- },
1938
+ refScrollView: combinedRef,
1466
1939
  initialContentOffset,
1467
1940
  getRenderedItem,
1468
1941
  updateItemSize,
1469
1942
  handleScroll,
1943
+ onMomentumScrollEnd: (event) => {
1944
+ const wasPaused = refState.current.scrollAdjustHandler.unPauseAdjust();
1945
+ if (wasPaused) {
1946
+ refState.current.scrollVelocity = 0;
1947
+ refState.current.scrollHistory = [];
1948
+ calculateItemsInView();
1949
+ }
1950
+ if (onMomentumScrollEnd) {
1951
+ onMomentumScrollEnd(event);
1952
+ }
1953
+ },
1470
1954
  onLayout,
1471
1955
  recycleItems,
1472
1956
  alignItemsAtEnd,
1473
- ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
1957
+ ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
1474
1958
  maintainVisibleContentPosition,
1475
1959
  scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : Platform.OS === "web" ? 16 : void 0,
1476
1960
  waitForInitialLayout,
1961
+ refreshControl: refreshControl != null ? refreshControl : onRefresh && /* @__PURE__ */ React6.createElement(
1962
+ RefreshControl,
1963
+ {
1964
+ refreshing: !!refreshing,
1965
+ onRefresh,
1966
+ progressViewOffset
1967
+ }
1968
+ ),
1477
1969
  style
1478
1970
  }
1479
- );
1971
+ ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React6.createElement(DebugView, { state: refState.current }));
1480
1972
  });
1481
1973
 
1482
1974
  export { LegendList, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };