@legendapp/list 1.0.0-beta.5 → 1.0.0-beta.50

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,29 @@
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, { memo, useReducer, useEffect, createContext, useMemo, useRef, useCallback, useImperativeHandle, useSyncExternalStore, useContext, useState, forwardRef, useLayoutEffect } from 'react';
3
+ import { View, Text, Platform, Animated, ScrollView, StyleSheet, Dimensions, 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
- values: /* @__PURE__ */ new Map(),
10
+ values: /* @__PURE__ */ new Map([
11
+ ["paddingTop", 0],
12
+ ["alignItemsPaddingTop", 0],
13
+ ["stylePaddingTop", 0],
14
+ ["headerSize", 0]
15
+ ]),
11
16
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
12
17
  mapViewabilityValues: /* @__PURE__ */ new Map(),
13
18
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
14
- mapViewabilityAmountValues: /* @__PURE__ */ new Map()
19
+ mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
20
+ columnWrapperStyle: void 0,
21
+ viewRefs: /* @__PURE__ */ new Map()
15
22
  }));
16
- return /* @__PURE__ */ React5.createElement(ContextState.Provider, { value }, children);
23
+ return /* @__PURE__ */ React6.createElement(ContextState.Provider, { value }, children);
17
24
  }
18
25
  function useStateContext() {
19
- return React5.useContext(ContextState);
26
+ return React6.useContext(ContextState);
20
27
  }
21
28
  function createSelectorFunctions(ctx, signalName) {
22
29
  return {
@@ -25,8 +32,8 @@ function createSelectorFunctions(ctx, signalName) {
25
32
  };
26
33
  }
27
34
  function use$(signalName) {
28
- const ctx = React5.useContext(ContextState);
29
- const { subscribe, get } = React5.useMemo(() => createSelectorFunctions(ctx, signalName), []);
35
+ const ctx = React6.useContext(ContextState);
36
+ const { subscribe, get } = React6.useMemo(() => createSelectorFunctions(ctx, signalName), []);
30
37
  const value = useSyncExternalStore(subscribe, get);
31
38
  return value;
32
39
  }
@@ -56,6 +63,71 @@ function set$(ctx, signalName, value) {
56
63
  }
57
64
  }
58
65
  }
66
+ function getContentSize(ctx) {
67
+ const { values } = ctx;
68
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
69
+ const headerSize = values.get("headerSize") || 0;
70
+ const footerSize = values.get("footerSize") || 0;
71
+ const totalSize = values.get("totalSize") || 0;
72
+ return headerSize + footerSize + totalSize + stylePaddingTop;
73
+ }
74
+
75
+ // src/DebugView.tsx
76
+ var DebugRow = ({ children }) => {
77
+ return /* @__PURE__ */ React.createElement(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" } }, children);
78
+ };
79
+ var DebugView = memo(function DebugView2({ state }) {
80
+ const ctx = useStateContext();
81
+ const totalSize = use$("totalSize") || 0;
82
+ const totalSizeWithScrollAdjust = use$("totalSizeWithScrollAdjust") || 0;
83
+ const scrollAdjust = use$("scrollAdjust") || 0;
84
+ const rawScroll = use$("debugRawScroll") || 0;
85
+ const scroll = use$("debugComputedScroll") || 0;
86
+ const contentSize = getContentSize(ctx);
87
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
88
+ use$("numContainers");
89
+ use$("numContainersPooled");
90
+ useInterval(() => {
91
+ forceUpdate();
92
+ }, 100);
93
+ return /* @__PURE__ */ React.createElement(
94
+ View,
95
+ {
96
+ style: {
97
+ position: "absolute",
98
+ top: 0,
99
+ right: 0,
100
+ paddingLeft: 4,
101
+ paddingBottom: 4,
102
+ // height: 100,
103
+ backgroundColor: "#FFFFFFCC",
104
+ padding: 4,
105
+ borderRadius: 4
106
+ },
107
+ pointerEvents: "none"
108
+ },
109
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React.createElement(Text, null, totalSize.toFixed(2))),
110
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React.createElement(Text, null, contentSize.toFixed(2))),
111
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "At end:"), /* @__PURE__ */ React.createElement(Text, null, String(state.isAtBottom))),
112
+ /* @__PURE__ */ React.createElement(Text, null),
113
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React.createElement(Text, null, scrollAdjust.toFixed(2))),
114
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "TotalSizeReal: "), /* @__PURE__ */ React.createElement(Text, null, totalSizeWithScrollAdjust.toFixed(2))),
115
+ /* @__PURE__ */ React.createElement(Text, null),
116
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React.createElement(Text, null, rawScroll.toFixed(2))),
117
+ /* @__PURE__ */ React.createElement(DebugRow, null, /* @__PURE__ */ React.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React.createElement(Text, null, scroll.toFixed(2)))
118
+ );
119
+ });
120
+ function useInterval(callback, delay) {
121
+ useEffect(() => {
122
+ const interval = setInterval(callback, delay);
123
+ return () => clearInterval(interval);
124
+ }, [delay]);
125
+ }
126
+
127
+ // src/helpers.ts
128
+ function isFunction(obj) {
129
+ return typeof obj === "function";
130
+ }
59
131
  var symbolFirst = Symbol();
60
132
  function useInit(cb) {
61
133
  const refValue = useRef(symbolFirst);
@@ -67,10 +139,10 @@ function useInit(cb) {
67
139
 
68
140
  // src/ContextContainer.ts
69
141
  var ContextContainer = createContext(null);
70
- function useViewability(configId, callback) {
142
+ function useViewability(callback, configId) {
71
143
  const ctx = useStateContext();
72
144
  const { containerId } = useContext(ContextContainer);
73
- const key = containerId + configId;
145
+ const key = containerId + (configId != null ? configId : "");
74
146
  useInit(() => {
75
147
  const value = ctx.mapViewabilityValues.get(key);
76
148
  if (value) {
@@ -126,25 +198,36 @@ function useRecyclingEffect(effect) {
126
198
  }, [index, value]);
127
199
  }
128
200
  function useRecyclingState(valueOrFun) {
129
- const { index, value } = useContext(ContextContainer);
130
- const stateInfo = useState(
131
- () => typeof valueOrFun === "function" ? valueOrFun({
201
+ const { index, value, itemKey, triggerLayout } = useContext(ContextContainer);
202
+ const refState = useRef({
203
+ itemKey: null,
204
+ value: null
205
+ });
206
+ const [_, setRenderNum] = useState(0);
207
+ if (refState.current.itemKey !== itemKey) {
208
+ refState.current.itemKey = itemKey;
209
+ refState.current.value = isFunction(valueOrFun) ? valueOrFun({
132
210
  index,
133
211
  item: value,
134
212
  prevIndex: void 0,
135
213
  prevItem: void 0
136
- }) : valueOrFun
214
+ }) : valueOrFun;
215
+ }
216
+ const setState = useCallback(
217
+ (newState) => {
218
+ refState.current.value = isFunction(newState) ? newState(refState.current.value) : newState;
219
+ setRenderNum((v) => v + 1);
220
+ triggerLayout();
221
+ },
222
+ [triggerLayout]
137
223
  );
138
- useRecyclingEffect((state) => {
139
- const newState = typeof valueOrFun === "function" ? valueOrFun(state) : valueOrFun;
140
- stateInfo[1](newState);
141
- });
142
- return stateInfo;
224
+ return [refState.current.value, setState];
143
225
  }
144
- var LeanView = React5.forwardRef((props, ref) => {
145
- return React5.createElement("RCTView", { ...props, ref });
226
+ var LeanViewComponent = React6.forwardRef((props, ref) => {
227
+ return React6.createElement("RCTView", { ...props, ref });
146
228
  });
147
- LeanView.displayName = "RCTView";
229
+ LeanViewComponent.displayName = "RCTView";
230
+ var LeanView = Platform.OS === "android" || Platform.OS === "ios" ? LeanViewComponent : View;
148
231
 
149
232
  // src/constants.ts
150
233
  var POSITION_OUT_OF_VIEW = -1e7;
@@ -153,9 +236,11 @@ var ANCHORED_POSITION_OUT_OF_VIEW = {
153
236
  relativeCoordinate: POSITION_OUT_OF_VIEW,
154
237
  top: POSITION_OUT_OF_VIEW
155
238
  };
239
+ var ENABLE_DEVMODE = __DEV__ && false;
240
+ var ENABLE_DEBUG_VIEW = __DEV__ && false;
241
+ var IsNewArchitecture = global.nativeFabricUIManager != null;
156
242
 
157
243
  // src/Container.tsx
158
- var isNewArchitecture = global.nativeFabricUIManager != null;
159
244
  var Container = ({
160
245
  id,
161
246
  recycleItems,
@@ -164,87 +249,141 @@ var Container = ({
164
249
  updateItemSize,
165
250
  ItemSeparatorComponent
166
251
  }) => {
167
- useStateContext();
252
+ const ctx = useStateContext();
253
+ const columnWrapperStyle = ctx.columnWrapperStyle;
168
254
  const maintainVisibleContentPosition = use$("maintainVisibleContentPosition");
169
255
  const position = use$(`containerPosition${id}`) || ANCHORED_POSITION_OUT_OF_VIEW;
170
256
  const column = use$(`containerColumn${id}`) || 0;
171
257
  const numColumns = use$("numColumns");
258
+ const lastItemKeys = use$("lastItemKeys");
259
+ const itemKey = use$(`containerItemKey${id}`);
260
+ const data = use$(`containerItemData${id}`);
261
+ const extraData = use$("extraData");
262
+ const refLastSize = useRef();
263
+ const ref = useRef(null);
264
+ const [layoutRenderCount, forceLayoutRender] = useState(0);
172
265
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
173
266
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
267
+ let paddingStyles;
268
+ if (columnWrapperStyle) {
269
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
270
+ if (horizontal) {
271
+ paddingStyles = {
272
+ paddingRight: !lastItemKeys.includes(itemKey) ? columnGap || gap || void 0 : void 0,
273
+ paddingVertical: (rowGap || gap || 0) / 2
274
+ };
275
+ } else {
276
+ paddingStyles = {
277
+ paddingBottom: !lastItemKeys.includes(itemKey) ? rowGap || gap || void 0 : void 0,
278
+ paddingHorizontal: (columnGap || gap || 0) / 2
279
+ };
280
+ }
281
+ }
174
282
  const style = horizontal ? {
175
283
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
176
284
  position: "absolute",
177
285
  top: otherAxisPos,
178
286
  bottom: numColumns > 1 ? null : 0,
179
287
  height: otherAxisSize,
180
- left: position.relativeCoordinate
288
+ left: position.relativeCoordinate,
289
+ ...paddingStyles || {}
181
290
  } : {
182
291
  position: "absolute",
183
292
  left: otherAxisPos,
184
293
  right: numColumns > 1 ? null : 0,
185
294
  width: otherAxisSize,
186
- top: position.relativeCoordinate
295
+ top: position.relativeCoordinate,
296
+ ...paddingStyles || {}
187
297
  };
188
- const lastItemKey = use$("lastItemKey");
189
- const itemKey = use$(`containerItemKey${id}`);
190
- const data = use$(`containerItemData${id}`);
191
- const extraData = use$("extraData");
192
298
  const renderedItemInfo = useMemo(
193
- () => itemKey !== void 0 && getRenderedItem(itemKey),
299
+ () => itemKey !== void 0 ? getRenderedItem(itemKey) : null,
194
300
  [itemKey, data, extraData]
195
301
  );
196
302
  const { index, renderedItem } = renderedItemInfo || {};
303
+ const triggerLayout = useCallback(() => {
304
+ forceLayoutRender((v) => v + 1);
305
+ }, []);
197
306
  const onLayout = (event) => {
198
307
  if (itemKey !== void 0) {
199
- const size = Math.floor(event.nativeEvent.layout[horizontal ? "width" : "height"] * 8) / 8;
200
- if (size === 0) {
201
- console.log("[WARN] Container 0 height reported, possible bug in LegendList", id, itemKey);
202
- return;
203
- }
204
- updateItemSize(id, itemKey, size);
308
+ const layout = event.nativeEvent.layout;
309
+ const size = Math.floor(layout[horizontal ? "width" : "height"] * 8) / 8;
310
+ refLastSize.current = size;
311
+ updateItemSize(itemKey, size);
205
312
  }
206
313
  };
207
- const ref = useRef(null);
208
- if (isNewArchitecture) {
314
+ if (IsNewArchitecture) {
209
315
  useLayoutEffect(() => {
210
316
  var _a, _b;
211
- if (itemKey) {
317
+ if (itemKey !== void 0) {
212
318
  const measured = (_b = (_a = ref.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
213
319
  if (measured) {
214
320
  const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
215
321
  if (size) {
216
- updateItemSize(id, itemKey, size);
322
+ updateItemSize(itemKey, size);
217
323
  }
218
324
  }
219
325
  }
326
+ }, [itemKey, layoutRenderCount]);
327
+ } else {
328
+ useEffect(() => {
329
+ if (itemKey) {
330
+ const timeout = setTimeout(() => {
331
+ if (refLastSize.current) {
332
+ updateItemSize(itemKey, refLastSize.current);
333
+ }
334
+ }, 16);
335
+ return () => {
336
+ clearTimeout(timeout);
337
+ };
338
+ }
220
339
  }, [itemKey]);
221
340
  }
222
- const contextValue = useMemo(
223
- () => ({ containerId: id, itemKey, index, value: data }),
224
- [id, itemKey, index, data]
225
- );
226
- 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));
341
+ const contextValue = useMemo(() => {
342
+ ctx.viewRefs.set(id, ref);
343
+ return { containerId: id, itemKey, index, value: data, triggerLayout };
344
+ }, [id, itemKey, index, data]);
345
+ 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 })));
227
346
  if (maintainVisibleContentPosition) {
228
347
  const anchorStyle = position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
229
- return /* @__PURE__ */ React5__default.createElement(LeanView, { style }, /* @__PURE__ */ React5__default.createElement(LeanView, { style: anchorStyle, onLayout, ref }, contentFragment));
348
+ if (ENABLE_DEVMODE) {
349
+ anchorStyle.borderColor = position.type === "top" ? "red" : "blue";
350
+ anchorStyle.borderWidth = 1;
351
+ }
352
+ 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)));
230
353
  }
231
- return /* @__PURE__ */ React5__default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
354
+ return /* @__PURE__ */ React6__default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
232
355
  };
233
- var useAnimatedValue = useAnimatedValue$1 || ((initialValue) => {
356
+ var typedForwardRef = forwardRef;
357
+ var typedMemo = memo;
358
+ var useAnimatedValue = (initialValue) => {
234
359
  return useRef(new Animated.Value(initialValue)).current;
235
- });
236
- function useValue$(key, getValue, key2) {
360
+ };
361
+
362
+ // src/useValue$.ts
363
+ function useValue$(key, getValue, useMicrotask) {
237
364
  var _a;
238
365
  const ctx = useStateContext();
239
366
  const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
240
367
  useMemo(() => {
241
- listen$(ctx, key, (v) => animValue.setValue(getValue ? getValue(v) : v));
368
+ let newValue = void 0;
369
+ listen$(ctx, key, (v) => {
370
+ if (useMicrotask && newValue === void 0) {
371
+ queueMicrotask(() => {
372
+ animValue.setValue(newValue);
373
+ newValue = void 0;
374
+ });
375
+ }
376
+ newValue = getValue ? getValue(v) : v;
377
+ if (!useMicrotask) {
378
+ animValue.setValue(newValue);
379
+ }
380
+ });
242
381
  }, []);
243
382
  return animValue;
244
383
  }
245
384
 
246
385
  // src/Containers.tsx
247
- var Containers = React5.memo(function Containers2({
386
+ var Containers = typedMemo(function Containers2({
248
387
  horizontal,
249
388
  recycleItems,
250
389
  ItemSeparatorComponent,
@@ -252,13 +391,20 @@ var Containers = React5.memo(function Containers2({
252
391
  updateItemSize,
253
392
  getRenderedItem
254
393
  }) {
394
+ const ctx = useStateContext();
395
+ const columnWrapperStyle = ctx.columnWrapperStyle;
255
396
  const numContainers = use$("numContainersPooled");
256
- const animSize = useValue$("totalSize");
397
+ const animSize = useValue$(
398
+ "totalSizeWithScrollAdjust",
399
+ void 0,
400
+ /*useMicrotask*/
401
+ true
402
+ );
257
403
  const animOpacity = waitForInitialLayout ? useValue$("containersDidLayout", (value) => value ? 1 : 0) : void 0;
258
404
  const containers = [];
259
405
  for (let i = 0; i < numContainers; i++) {
260
406
  containers.push(
261
- /* @__PURE__ */ React5.createElement(
407
+ /* @__PURE__ */ React6.createElement(
262
408
  Container,
263
409
  {
264
410
  id: i,
@@ -273,20 +419,85 @@ var Containers = React5.memo(function Containers2({
273
419
  );
274
420
  }
275
421
  const style = horizontal ? { width: animSize, opacity: animOpacity } : { height: animSize, opacity: animOpacity };
276
- return /* @__PURE__ */ React5.createElement(Animated.View, { style }, containers);
422
+ if (columnWrapperStyle) {
423
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
424
+ if (horizontal) {
425
+ const my = (rowGap || gap || 0) / 2;
426
+ if (my) {
427
+ style.marginVertical = -my;
428
+ }
429
+ } else {
430
+ const mx = (columnGap || gap || 0) / 2;
431
+ if (mx) {
432
+ style.marginHorizontal = -mx;
433
+ }
434
+ }
435
+ }
436
+ return /* @__PURE__ */ React6.createElement(Animated.View, { style }, containers);
277
437
  });
278
438
 
279
439
  // src/ListComponent.tsx
280
440
  var getComponent = (Component) => {
281
- if (React5.isValidElement(Component)) {
441
+ if (React6.isValidElement(Component)) {
282
442
  return Component;
283
443
  }
284
444
  if (Component) {
285
- return /* @__PURE__ */ React5.createElement(Component, null);
445
+ return /* @__PURE__ */ React6.createElement(Component, null);
286
446
  }
287
447
  return null;
288
448
  };
289
- var ListComponent = React5.memo(function ListComponent2({
449
+ var PaddingAndAdjust = () => {
450
+ const animPaddingTop = useValue$("paddingTop", (v) => v, true);
451
+ const animScrollAdjust = useValue$("scrollAdjust", (v) => v, true);
452
+ const additionalSize = { marginTop: animScrollAdjust, paddingTop: animPaddingTop };
453
+ return /* @__PURE__ */ React6.createElement(Animated.View, { style: additionalSize });
454
+ };
455
+ var PaddingAndAdjustDevMode = () => {
456
+ const animPaddingTop = useValue$("paddingTop", (v) => v, true);
457
+ const animScrollAdjust = useValue$("scrollAdjust", (v) => v, true);
458
+ 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(
459
+ Animated.View,
460
+ {
461
+ style: {
462
+ position: "absolute",
463
+ top: Animated.add(animScrollAdjust, Animated.multiply(animScrollAdjust, -1)),
464
+ height: animPaddingTop,
465
+ left: 0,
466
+ right: 0,
467
+ backgroundColor: "green"
468
+ }
469
+ }
470
+ ), /* @__PURE__ */ React6.createElement(
471
+ Animated.View,
472
+ {
473
+ style: {
474
+ position: "absolute",
475
+ top: animPaddingTop,
476
+ height: animScrollAdjust,
477
+ left: -16,
478
+ right: -16,
479
+ backgroundColor: "lightblue"
480
+ }
481
+ }
482
+ ), /* @__PURE__ */ React6.createElement(
483
+ Animated.View,
484
+ {
485
+ style: {
486
+ position: "absolute",
487
+ top: animPaddingTop,
488
+ height: Animated.multiply(animScrollAdjust, -1),
489
+ width: 8,
490
+ right: 4,
491
+ borderStyle: "dashed",
492
+ borderColor: "blue",
493
+ borderWidth: 1,
494
+ backgroundColor: "lightblue"
495
+ //backgroundColor: "blue",
496
+ }
497
+ }
498
+ ));
499
+ };
500
+ var ListComponent = typedMemo(function ListComponent2({
290
501
  style,
291
502
  contentContainerStyle,
292
503
  horizontal,
@@ -307,22 +518,22 @@ var ListComponent = React5.memo(function ListComponent2({
307
518
  refScrollView,
308
519
  maintainVisibleContentPosition,
309
520
  renderScrollComponent,
521
+ onRefresh,
522
+ refreshing,
523
+ progressViewOffset,
310
524
  ...rest
311
525
  }) {
312
526
  const ctx = useStateContext();
313
- const animPaddingTop = useValue$("paddingTop");
314
- const animScrollAdjust = useValue$("scrollAdjust");
315
527
  const ScrollComponent = renderScrollComponent ? useMemo(
316
- () => React5.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
528
+ () => React6.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
317
529
  [renderScrollComponent]
318
530
  ) : ScrollView;
319
- const additionalSize = { marginTop: animScrollAdjust, paddingTop: animPaddingTop };
320
- return /* @__PURE__ */ React5.createElement(
531
+ return /* @__PURE__ */ React6.createElement(
321
532
  ScrollComponent,
322
533
  {
323
534
  ...rest,
324
535
  style,
325
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
536
+ maintainVisibleContentPosition: maintainVisibleContentPosition && !ListEmptyComponent ? { minIndexForVisible: 0 } : void 0,
326
537
  contentContainerStyle: [
327
538
  contentContainerStyle,
328
539
  horizontal ? {
@@ -335,34 +546,41 @@ var ListComponent = React5.memo(function ListComponent2({
335
546
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
336
547
  ref: refScrollView
337
548
  },
338
- /* @__PURE__ */ React5.createElement(Animated.View, { style: additionalSize }),
339
- ListHeaderComponent && /* @__PURE__ */ React5.createElement(
340
- Animated.View,
549
+ !ListEmptyComponent && (ENABLE_DEVMODE ? /* @__PURE__ */ React6.createElement(PaddingAndAdjustDevMode, null) : /* @__PURE__ */ React6.createElement(PaddingAndAdjust, null)),
550
+ ListHeaderComponent && /* @__PURE__ */ React6.createElement(
551
+ View,
341
552
  {
342
553
  style: ListHeaderComponentStyle,
343
554
  onLayout: (event) => {
344
555
  const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
345
- const prevSize = peek$(ctx, "headerSize") || 0;
346
- if (size !== prevSize) {
347
- set$(ctx, "headerSize", size);
348
- }
556
+ set$(ctx, "headerSize", size);
349
557
  }
350
558
  },
351
559
  getComponent(ListHeaderComponent)
352
560
  ),
353
561
  ListEmptyComponent && getComponent(ListEmptyComponent),
354
- /* @__PURE__ */ React5.createElement(
562
+ /* @__PURE__ */ React6.createElement(
355
563
  Containers,
356
564
  {
357
565
  horizontal,
358
566
  recycleItems,
359
567
  waitForInitialLayout,
360
568
  getRenderedItem,
361
- ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
569
+ ItemSeparatorComponent,
362
570
  updateItemSize
363
571
  }
364
572
  ),
365
- ListFooterComponent && /* @__PURE__ */ React5.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
573
+ ListFooterComponent && /* @__PURE__ */ React6.createElement(
574
+ View,
575
+ {
576
+ style: ListFooterComponentStyle,
577
+ onLayout: (event) => {
578
+ const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
579
+ set$(ctx, "footerSize", size);
580
+ }
581
+ },
582
+ getComponent(ListFooterComponent)
583
+ )
366
584
  );
367
585
  });
368
586
 
@@ -371,36 +589,63 @@ var ScrollAdjustHandler = class {
371
589
  constructor(ctx) {
372
590
  this.ctx = ctx;
373
591
  this.appliedAdjust = 0;
374
- this.pendingAdjust = 0;
375
592
  this.busy = false;
376
- this.firstAdjust = true;
593
+ this.isPaused = false;
594
+ this.isDisabled = false;
377
595
  this.context = ctx;
378
596
  }
597
+ doAjdust() {
598
+ set$(this.context, "scrollAdjust", this.appliedAdjust);
599
+ this.busy = false;
600
+ }
379
601
  requestAdjust(adjust, onAdjusted) {
602
+ if (this.isDisabled) {
603
+ return;
604
+ }
380
605
  const oldAdjustTop = peek$(this.context, "scrollAdjust");
381
606
  if (oldAdjustTop === adjust) {
382
607
  return;
383
608
  }
384
609
  this.appliedAdjust = adjust;
385
- this.pendingAdjust = adjust;
386
- const doAjdust = () => {
387
- set$(this.context, "scrollAdjust", this.pendingAdjust);
388
- onAdjusted(oldAdjustTop - this.pendingAdjust);
389
- this.busy = false;
390
- };
391
- if (!this.busy) {
610
+ if (!this.busy && !this.isPaused) {
392
611
  this.busy = true;
393
- if (this.firstAdjust) {
394
- this.firstAdjust = false;
395
- setTimeout(doAjdust, 50);
396
- } else {
397
- doAjdust();
398
- }
612
+ this.doAjdust();
613
+ onAdjusted(oldAdjustTop - adjust);
399
614
  }
400
615
  }
401
616
  getAppliedAdjust() {
402
617
  return this.appliedAdjust;
403
618
  }
619
+ pauseAdjust() {
620
+ this.isPaused = true;
621
+ }
622
+ setDisableAdjust(disable) {
623
+ this.isDisabled = disable;
624
+ }
625
+ // return true if it was paused
626
+ unPauseAdjust() {
627
+ if (this.isPaused) {
628
+ this.isPaused = false;
629
+ this.doAjdust();
630
+ return true;
631
+ }
632
+ return false;
633
+ }
634
+ };
635
+ var useCombinedRef = (...refs) => {
636
+ const callback = useCallback((element) => {
637
+ for (const ref of refs) {
638
+ if (!ref) {
639
+ continue;
640
+ }
641
+ if (isFunction(ref)) {
642
+ ref(element);
643
+ } else {
644
+ ref.current = element;
645
+ }
646
+ }
647
+ }, refs);
648
+ return callback;
404
649
  };
405
650
 
406
651
  // src/viewability.ts
@@ -453,11 +698,37 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
453
698
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
454
699
  const configId = viewabilityConfig.id;
455
700
  const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
456
- const { viewableItems: previousViewableItems, start, previousStart, end, previousEnd } = viewabilityState;
701
+ const { viewableItems: previousViewableItems, start, end } = viewabilityState;
702
+ const viewabilityTokens = /* @__PURE__ */ new Map();
703
+ for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
704
+ viewabilityTokens.set(
705
+ containerId,
706
+ computeViewability(
707
+ state,
708
+ ctx,
709
+ viewabilityConfig,
710
+ containerId,
711
+ value.key,
712
+ scrollSize,
713
+ value.item,
714
+ value.index
715
+ )
716
+ );
717
+ }
457
718
  const changed = [];
458
719
  if (previousViewableItems) {
459
720
  for (const viewToken of previousViewableItems) {
460
- if (!isViewable(state, ctx, viewabilityConfig, viewToken.key, scrollSize, viewToken.item, viewToken.index)) {
721
+ const containerId = findContainerId(ctx, viewToken.key);
722
+ if (!isViewable(
723
+ state,
724
+ ctx,
725
+ viewabilityConfig,
726
+ containerId,
727
+ viewToken.key,
728
+ scrollSize,
729
+ viewToken.item,
730
+ viewToken.index
731
+ )) {
461
732
  viewToken.isViewable = false;
462
733
  changed.push(viewToken);
463
734
  }
@@ -468,12 +739,14 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
468
739
  const item = data[i];
469
740
  if (item) {
470
741
  const key = getId(i);
471
- if (isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, i)) {
742
+ const containerId = findContainerId(ctx, key);
743
+ if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
472
744
  const viewToken = {
473
745
  item,
474
746
  key,
475
747
  index: i,
476
- isViewable: true
748
+ isViewable: true,
749
+ containerId
477
750
  };
478
751
  viewableItems.push(viewToken);
479
752
  if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
@@ -491,20 +764,27 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
491
764
  viewabilityState.viewableItems = viewableItems;
492
765
  for (let i = 0; i < changed.length; i++) {
493
766
  const change = changed[i];
494
- maybeUpdateViewabilityCallback(ctx, configId, change);
767
+ maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
495
768
  }
496
769
  if (onViewableItemsChanged) {
497
770
  onViewableItemsChanged({ viewableItems, changed });
498
771
  }
499
772
  }
773
+ for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
774
+ if (value.sizeVisible < 0) {
775
+ ctx.mapViewabilityAmountValues.delete(containerId);
776
+ }
777
+ }
500
778
  }
501
- function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index) {
502
- const { sizes, positions, scroll } = state;
779
+ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
780
+ const { sizes, positions, scroll: scrollState, scrollAdjustHandler } = state;
503
781
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
504
782
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
505
783
  const viewAreaMode = viewAreaCoveragePercentThreshold != null;
506
784
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
507
- const top = positions.get(key) - scroll + topPad;
785
+ const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
786
+ const scroll = scrollState - previousScrollAdjust - topPad;
787
+ const top = positions.get(key) - scroll;
508
788
  const size = sizes.get(key) || 0;
509
789
  const bottom = top + size;
510
790
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
@@ -513,7 +793,6 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
513
793
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
514
794
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
515
795
  const isViewable2 = percent >= viewablePercentThreshold;
516
- const containerId = findContainerId(ctx, key);
517
796
  const value = {
518
797
  index,
519
798
  isViewable: isViewable2,
@@ -523,15 +802,21 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
523
802
  percentOfScroller,
524
803
  sizeVisible,
525
804
  size,
526
- position: top,
527
- scrollSize
805
+ scrollSize,
806
+ containerId
528
807
  };
529
- ctx.mapViewabilityAmountValues.set(containerId, value);
530
- const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
531
- if (cb) {
532
- cb(value);
808
+ if (JSON.stringify(value) !== JSON.stringify(ctx.mapViewabilityAmountValues.get(containerId))) {
809
+ ctx.mapViewabilityAmountValues.set(containerId, value);
810
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
811
+ if (cb) {
812
+ cb(value);
813
+ }
533
814
  }
534
- return isViewable2;
815
+ return value;
816
+ }
817
+ function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
818
+ const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
819
+ return value.isViewable;
535
820
  }
536
821
  function findContainerId(ctx, key) {
537
822
  const numContainers = peek$(ctx, "numContainers");
@@ -543,8 +828,8 @@ function findContainerId(ctx, key) {
543
828
  }
544
829
  return -1;
545
830
  }
546
- function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
547
- const key = viewToken.key + configId;
831
+ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
832
+ const key = containerId + configId;
548
833
  ctx.mapViewabilityValues.set(key, viewToken);
549
834
  const cb = ctx.mapViewabilityCallbacks.get(key);
550
835
  cb == null ? void 0 : cb(viewToken);
@@ -553,13 +838,25 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
553
838
  // src/LegendList.tsx
554
839
  var DEFAULT_DRAW_DISTANCE = 250;
555
840
  var DEFAULT_ITEM_SIZE = 100;
556
- var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
557
- return /* @__PURE__ */ React5.createElement(StateProvider, null, /* @__PURE__ */ React5.createElement(LegendListInner, { ...props, ref: forwardedRef }));
841
+ function createColumnWrapperStyle(contentContainerStyle) {
842
+ const { gap, columnGap, rowGap } = contentContainerStyle;
843
+ if (gap || columnGap || rowGap) {
844
+ contentContainerStyle.gap = void 0;
845
+ contentContainerStyle.columnGap = void 0;
846
+ contentContainerStyle.rowGap = void 0;
847
+ return {
848
+ gap,
849
+ columnGap,
850
+ rowGap
851
+ };
852
+ }
853
+ }
854
+ var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
855
+ return /* @__PURE__ */ React6.createElement(StateProvider, null, /* @__PURE__ */ React6.createElement(LegendListInner, { ...props, ref: forwardedRef }));
558
856
  });
559
- var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef) {
560
- var _a, _b, _c, _d, _e;
857
+ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
561
858
  const {
562
- data,
859
+ data: dataProp = [],
563
860
  initialScrollIndex,
564
861
  initialScrollOffset,
565
862
  horizontal,
@@ -572,7 +869,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
572
869
  alignItemsAtEnd = false,
573
870
  maintainVisibleContentPosition = false,
574
871
  onScroll: onScrollProp,
872
+ onMomentumScrollEnd,
575
873
  numColumns: numColumnsProp = 1,
874
+ columnWrapperStyle,
576
875
  keyExtractor: keyExtractorProp,
577
876
  renderItem,
578
877
  estimatedItemSize,
@@ -583,55 +882,84 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
583
882
  refScrollView,
584
883
  waitForInitialLayout = true,
585
884
  extraData,
885
+ contentContainerStyle: contentContainerStyleProp,
886
+ style: styleProp,
586
887
  onLayout: onLayoutProp,
888
+ onRefresh,
889
+ refreshing,
890
+ progressViewOffset,
891
+ refreshControl,
892
+ initialContainerPoolRatio = 2,
893
+ viewabilityConfig,
894
+ viewabilityConfigCallbackPairs,
895
+ onViewableItemsChanged,
587
896
  ...rest
588
897
  } = props;
589
- const { style, contentContainerStyle } = props;
590
898
  const callbacks = useRef({
591
899
  onStartReached: rest.onStartReached,
592
900
  onEndReached: rest.onEndReached
593
901
  });
594
902
  callbacks.current.onStartReached = rest.onStartReached;
595
903
  callbacks.current.onEndReached = rest.onEndReached;
904
+ const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
905
+ const style = { ...StyleSheet.flatten(styleProp) };
906
+ const stylePaddingTopState = ((style == null ? void 0 : style.paddingTop) || 0) + ((contentContainerStyle == null ? void 0 : contentContainerStyle.paddingTop) || 0);
907
+ if (style == null ? void 0 : style.paddingTop) {
908
+ style.paddingTop = void 0;
909
+ }
910
+ if (contentContainerStyle == null ? void 0 : contentContainerStyle.paddingTop) {
911
+ contentContainerStyle.paddingTop = void 0;
912
+ }
596
913
  const ctx = useStateContext();
914
+ ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
597
915
  const refScroller = useRef(null);
598
- const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE;
916
+ const combinedRef = useCombinedRef(refScroller, refScrollView);
917
+ const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
599
918
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (item, index) => index.toString();
600
919
  const refState = useRef();
601
920
  const getId = (index) => {
602
- var _a2;
603
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
604
- if (!data2) {
921
+ var _a;
922
+ const data = (_a = refState.current) == null ? void 0 : _a.data;
923
+ if (!data) {
605
924
  return "";
606
925
  }
607
- const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
926
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
608
927
  return `${ret}`;
609
928
  };
610
- const getItemSize = (key, index, data2) => {
611
- var _a2;
929
+ const getItemSize = (key, index, data) => {
930
+ var _a;
612
931
  const sizeKnown = refState.current.sizes.get(key);
613
932
  if (sizeKnown !== void 0) {
614
933
  return sizeKnown;
615
934
  }
616
- const size = (_a2 = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize) != null ? _a2 : DEFAULT_ITEM_SIZE;
935
+ const size = (_a = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize) != null ? _a : DEFAULT_ITEM_SIZE;
617
936
  refState.current.sizes.set(key, size);
618
937
  return size;
619
938
  };
620
- const calculateInitialOffset = (index = initialScrollIndex) => {
621
- if (index) {
939
+ const calculateOffsetForIndex = (index = initialScrollIndex) => {
940
+ var _a;
941
+ const data = dataProp;
942
+ if (index !== void 0) {
622
943
  let offset = 0;
623
- if (getEstimatedItemSize) {
944
+ const canGetSize = !!refState.current;
945
+ if (canGetSize || getEstimatedItemSize) {
946
+ const sizeFn = (index2) => {
947
+ if (canGetSize) {
948
+ return getItemSize(getId(index2), index2, data[index2]);
949
+ }
950
+ return getEstimatedItemSize(index2, data[index2]);
951
+ };
624
952
  for (let i = 0; i < index; i++) {
625
- offset += getEstimatedItemSize(i, data[i]);
953
+ offset += sizeFn(i);
626
954
  }
627
955
  } else if (estimatedItemSize) {
628
956
  offset = index * estimatedItemSize;
629
957
  }
630
- return offset / numColumnsProp;
958
+ return offset / numColumnsProp - (((_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler.getAppliedAdjust()) || 0);
631
959
  }
632
960
  return 0;
633
961
  };
634
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(calculateInitialOffset, []);
962
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(calculateOffsetForIndex, []);
635
963
  if (!refState.current) {
636
964
  const initialScrollLength = Dimensions.get("window")[horizontal ? "width" : "height"];
637
965
  refState.current = {
@@ -639,14 +967,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
639
967
  positions: /* @__PURE__ */ new Map(),
640
968
  columns: /* @__PURE__ */ new Map(),
641
969
  pendingAdjust: 0,
642
- waitingForMicrotask: false,
643
970
  isStartReached: initialContentOffset < initialScrollLength * onStartReachedThreshold,
644
971
  isEndReached: false,
645
972
  isAtBottom: false,
646
973
  isAtTop: false,
647
- data,
648
- idsInFirstRender: void 0,
649
- hasScrolled: false,
974
+ data: dataProp,
650
975
  scrollLength: initialScrollLength,
651
976
  startBuffered: 0,
652
977
  startNoBuffer: 0,
@@ -666,23 +991,29 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
666
991
  indexByKey: /* @__PURE__ */ new Map(),
667
992
  scrollHistory: [],
668
993
  scrollVelocity: 0,
669
- sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
994
+ sizesKnown: /* @__PURE__ */ new Map(),
670
995
  timeoutSizeMessage: 0,
671
996
  scrollTimer: void 0,
672
997
  belowAnchorElementPositions: void 0,
673
998
  rowHeights: /* @__PURE__ */ new Map(),
674
999
  startReachedBlockedByTimer: false,
1000
+ endReachedBlockedByTimer: false,
675
1001
  scrollForNextCalculateItemsInView: void 0,
676
- enableScrollForNextCalculateItemsInView: true
1002
+ enableScrollForNextCalculateItemsInView: true,
1003
+ minIndexSizeChanged: 0,
1004
+ numPendingInitialLayout: 0,
1005
+ queuedCalculateItemsInView: 0,
1006
+ lastBatchingAction: Date.now(),
1007
+ onScroll: onScrollProp
677
1008
  };
678
- refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
679
- if (maintainVisibleContentPosition) {
680
- if (initialScrollIndex) {
1009
+ const dataLength = dataProp.length;
1010
+ if (maintainVisibleContentPosition && dataLength > 0) {
1011
+ if (initialScrollIndex && initialScrollIndex < dataLength) {
681
1012
  refState.current.anchorElement = {
682
1013
  coordinate: initialContentOffset,
683
1014
  id: getId(initialScrollIndex)
684
1015
  };
685
- } else if (data.length) {
1016
+ } else if (dataLength > 0) {
686
1017
  refState.current.anchorElement = {
687
1018
  coordinate: initialContentOffset,
688
1019
  id: getId(0)
@@ -695,6 +1026,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
695
1026
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
696
1027
  set$(ctx, "extraData", extraData);
697
1028
  }
1029
+ const didDataChange = refState.current.data !== dataProp;
1030
+ refState.current.data = dataProp;
1031
+ refState.current.onScroll = onScrollProp;
698
1032
  const getAnchorElementIndex = () => {
699
1033
  const state = refState.current;
700
1034
  if (state.anchorElement) {
@@ -703,12 +1037,59 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
703
1037
  }
704
1038
  return void 0;
705
1039
  };
1040
+ const scrollToIndex = ({
1041
+ index,
1042
+ viewOffset = 0,
1043
+ animated = true,
1044
+ viewPosition = 0
1045
+ }) => {
1046
+ var _a;
1047
+ const state = refState.current;
1048
+ const firstIndexOffset = calculateOffsetForIndex(index);
1049
+ let firstIndexScrollPostion = firstIndexOffset - viewOffset;
1050
+ const diff = Math.abs(state.scroll - firstIndexScrollPostion);
1051
+ const needsReanchoring = maintainVisibleContentPosition && diff > 100;
1052
+ state.scrollForNextCalculateItemsInView = void 0;
1053
+ if (needsReanchoring) {
1054
+ const id = getId(index);
1055
+ state.anchorElement = { id, coordinate: firstIndexOffset };
1056
+ (_a = state.belowAnchorElementPositions) == null ? void 0 : _a.clear();
1057
+ state.positions.clear();
1058
+ calcTotalSizesAndPositions({ forgetPositions: true });
1059
+ state.startBufferedId = id;
1060
+ state.minIndexSizeChanged = index;
1061
+ firstIndexScrollPostion = firstIndexOffset - viewOffset + state.scrollAdjustHandler.getAppliedAdjust();
1062
+ }
1063
+ if (viewPosition) {
1064
+ firstIndexScrollPostion -= viewPosition * (state.scrollLength - getItemSize(getId(index), index, state.data[index]));
1065
+ }
1066
+ state.scrollAdjustHandler.setDisableAdjust(true);
1067
+ state.scrollingToOffset = firstIndexScrollPostion;
1068
+ scrollTo(firstIndexScrollPostion, animated);
1069
+ };
1070
+ const setDidLayout = () => {
1071
+ refState.current.queuedInitialLayout = true;
1072
+ checkAtBottom();
1073
+ if (initialScrollIndex) {
1074
+ queueMicrotask(() => {
1075
+ scrollToIndex({ index: initialScrollIndex, animated: false });
1076
+ requestAnimationFrame(() => {
1077
+ set$(ctx, "containersDidLayout", true);
1078
+ });
1079
+ });
1080
+ } else {
1081
+ queueMicrotask(() => {
1082
+ set$(ctx, "containersDidLayout", true);
1083
+ });
1084
+ }
1085
+ };
706
1086
  const addTotalSize = useCallback((key, add, totalSizeBelowAnchor) => {
707
1087
  const state = refState.current;
708
- const index = key === null ? 0 : state.indexByKey.get(key);
1088
+ const { indexByKey, anchorElement } = state;
1089
+ const index = key === null ? 0 : indexByKey.get(key);
709
1090
  let isAboveAnchor = false;
710
1091
  if (maintainVisibleContentPosition) {
711
- if (state.anchorElement && index < getAnchorElementIndex()) {
1092
+ if (anchorElement && index < getAnchorElementIndex()) {
712
1093
  isAboveAnchor = true;
713
1094
  }
714
1095
  }
@@ -721,29 +1102,30 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
721
1102
  state.totalSizeBelowAnchor += add;
722
1103
  }
723
1104
  }
724
- let applyAdjustValue = void 0;
725
- if (maintainVisibleContentPosition) {
726
- const newAdjust = state.anchorElement.coordinate - state.totalSizeBelowAnchor;
1105
+ let applyAdjustValue = 0;
1106
+ let resultSize = state.totalSize;
1107
+ if (maintainVisibleContentPosition && anchorElement !== void 0) {
1108
+ const newAdjust = anchorElement.coordinate - state.totalSizeBelowAnchor;
727
1109
  applyAdjustValue = -newAdjust;
728
1110
  state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
729
1111
  state.rowHeights.clear();
1112
+ if (applyAdjustValue !== void 0) {
1113
+ resultSize -= applyAdjustValue;
1114
+ state.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
1115
+ state.scroll -= diff;
1116
+ });
1117
+ }
730
1118
  }
731
- const totalSize = state.totalSize;
732
- let resultSize = totalSize;
733
- if (applyAdjustValue !== void 0) {
734
- resultSize -= applyAdjustValue;
735
- refState.current.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
736
- state.scroll -= diff;
737
- });
738
- }
739
- set$(ctx, "totalSize", resultSize);
1119
+ set$(ctx, "totalSize", state.totalSize);
1120
+ set$(ctx, "totalSizeWithScrollAdjust", resultSize);
740
1121
  if (alignItemsAtEnd) {
741
- doUpdatePaddingTop();
1122
+ updateAlignItemsPaddingTop();
742
1123
  }
743
1124
  }, []);
744
1125
  const getRowHeight = (n) => {
745
- const { rowHeights } = refState.current;
746
- if (numColumnsProp === 1) {
1126
+ const { rowHeights, data } = refState.current;
1127
+ const numColumns = peek$(ctx, "numColumns");
1128
+ if (numColumns === 1) {
747
1129
  const id = getId(n);
748
1130
  return getItemSize(id, n, data[n]);
749
1131
  }
@@ -751,8 +1133,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
751
1133
  return rowHeights.get(n) || 0;
752
1134
  }
753
1135
  let rowHeight = 0;
754
- const startEl = n * numColumnsProp;
755
- for (let i = startEl; i < startEl + numColumnsProp && i < data.length; i++) {
1136
+ const startEl = n * numColumns;
1137
+ for (let i = startEl; i < startEl + numColumns && i < data.length; i++) {
756
1138
  const id = getId(i);
757
1139
  const size = getItemSize(id, i, data[i]);
758
1140
  rowHeight = Math.max(rowHeight, size);
@@ -765,16 +1147,17 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
765
1147
  if (!state.anchorElement) {
766
1148
  return /* @__PURE__ */ new Map();
767
1149
  }
768
- let top = state.anchorElement.coordinate;
769
1150
  const anchorIndex = state.indexByKey.get(state.anchorElement.id);
770
1151
  if (anchorIndex === 0) {
771
1152
  return /* @__PURE__ */ new Map();
772
1153
  }
773
1154
  const map = state.belowAnchorElementPositions || /* @__PURE__ */ new Map();
1155
+ const numColumns = peek$(ctx, "numColumns");
1156
+ let top = state.anchorElement.coordinate;
774
1157
  for (let i = anchorIndex - 1; i >= 0; i--) {
775
1158
  const id = getId(i);
776
- const rowNumber = Math.floor(i / numColumnsProp);
777
- if (i % numColumnsProp === 0) {
1159
+ const rowNumber = Math.floor(i / numColumns);
1160
+ if (i % numColumns === 0) {
778
1161
  top -= getRowHeight(rowNumber);
779
1162
  }
780
1163
  map.set(id, top);
@@ -782,37 +1165,115 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
782
1165
  return map;
783
1166
  };
784
1167
  const getElementPositionBelowAchor = (id) => {
1168
+ var _a;
785
1169
  const state = refState.current;
786
1170
  if (!refState.current.belowAnchorElementPositions) {
787
1171
  state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
788
1172
  }
789
1173
  const res = state.belowAnchorElementPositions.get(id);
790
1174
  if (res === void 0) {
791
- throw new Error("Undefined position below achor");
1175
+ console.warn(`Undefined position below anchor ${id} ${(_a = state.anchorElement) == null ? void 0 : _a.id}`);
1176
+ return 0;
792
1177
  }
793
1178
  return res;
794
1179
  };
795
- const calculateItemsInView = useCallback((speed) => {
1180
+ const fixGaps = useCallback(() => {
1181
+ var _a;
1182
+ const state = refState.current;
1183
+ const { data, scrollLength, positions, startBuffered, endBuffered } = state;
1184
+ if (!data || scrollLength === 0) {
1185
+ return;
1186
+ }
1187
+ const numContainers = ctx.values.get("numContainers");
1188
+ let numMeasurements = 0;
1189
+ for (let i = 0; i < numContainers; i++) {
1190
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1191
+ const isSizeKnown = refState.current.sizesKnown.get(itemKey);
1192
+ if (itemKey && !isSizeKnown) {
1193
+ const containerRef = ctx.viewRefs.get(i);
1194
+ if (containerRef) {
1195
+ let measured;
1196
+ (_a = containerRef.current) == null ? void 0 : _a.measure((x, y, width, height) => {
1197
+ measured = { width, height };
1198
+ });
1199
+ numMeasurements++;
1200
+ if (measured) {
1201
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
1202
+ updateItemSize(
1203
+ itemKey,
1204
+ size,
1205
+ /*fromFixGaps*/
1206
+ true
1207
+ );
1208
+ }
1209
+ }
1210
+ }
1211
+ }
1212
+ if (numMeasurements > 0) {
1213
+ let top;
1214
+ const diffs = /* @__PURE__ */ new Map();
1215
+ for (let i = startBuffered; i <= endBuffered; i++) {
1216
+ const id = getId(i);
1217
+ if (top === void 0) {
1218
+ top = positions.get(id);
1219
+ }
1220
+ if (positions.get(id) !== top) {
1221
+ diffs.set(id, top - positions.get(id));
1222
+ positions.set(id, top);
1223
+ }
1224
+ const size = getItemSize(id, i, data[i]);
1225
+ const bottom = top + size;
1226
+ top = bottom;
1227
+ }
1228
+ for (let i = 0; i < numContainers; i++) {
1229
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1230
+ const diff = diffs.get(itemKey);
1231
+ if (diff) {
1232
+ const prevPos = peek$(ctx, `containerPosition${i}`);
1233
+ const newPos = prevPos.top + diff;
1234
+ if (prevPos.top !== newPos) {
1235
+ const pos = { ...prevPos };
1236
+ pos.relativeCoordinate += diff;
1237
+ pos.top += diff;
1238
+ set$(ctx, `containerPosition${i}`, pos);
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+ }, []);
1244
+ const calculateItemsInView = useCallback(() => {
1245
+ var _a;
796
1246
  const state = refState.current;
797
1247
  const {
798
- data: data2,
1248
+ data,
799
1249
  scrollLength,
800
- scroll: scrollState,
801
1250
  startBufferedId: startBufferedIdOrig,
802
1251
  positions,
803
1252
  columns,
804
- scrollAdjustHandler
1253
+ scrollAdjustHandler,
1254
+ scrollVelocity: speed
805
1255
  } = state;
806
- if (state.waitingForMicrotask) {
807
- state.waitingForMicrotask = false;
808
- }
809
- if (!data2) {
1256
+ if (!data || scrollLength === 0) {
810
1257
  return;
811
1258
  }
812
- const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1259
+ const totalSize = peek$(ctx, "totalSizeWithScrollAdjust");
1260
+ const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
1261
+ const numColumns = peek$(ctx, "numColumns");
813
1262
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
814
1263
  const scrollExtra = Math.max(-16, Math.min(16, speed)) * 16;
815
- const scroll = scrollState - previousScrollAdjust - topPad;
1264
+ let scrollState = state.scroll;
1265
+ if (!state.queuedInitialLayout && initialScrollIndex) {
1266
+ const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
1267
+ scrollState = updatedOffset;
1268
+ }
1269
+ let scroll = scrollState - previousScrollAdjust - topPad;
1270
+ if (scroll + scrollLength > totalSize) {
1271
+ scroll = totalSize - scrollLength;
1272
+ }
1273
+ if (ENABLE_DEBUG_VIEW) {
1274
+ set$(ctx, "debugRawScroll", scrollState);
1275
+ set$(ctx, "debugComputedScroll", scroll);
1276
+ }
816
1277
  let scrollBufferTop = scrollBuffer;
817
1278
  let scrollBufferBottom = scrollBuffer;
818
1279
  if (scrollExtra > 8) {
@@ -835,8 +1296,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
835
1296
  let startBufferedId = null;
836
1297
  let endNoBuffer = null;
837
1298
  let endBuffered = null;
838
- const originalStartId = startBufferedIdOrig && state.indexByKey.get(startBufferedIdOrig);
839
- let loopStart = originalStartId || 0;
1299
+ let loopStart = startBufferedIdOrig ? state.indexByKey.get(startBufferedIdOrig) || 0 : 0;
1300
+ if (state.minIndexSizeChanged !== void 0) {
1301
+ loopStart = Math.min(state.minIndexSizeChanged, loopStart);
1302
+ state.minIndexSizeChanged = void 0;
1303
+ }
840
1304
  const anchorElementIndex = getAnchorElementIndex();
841
1305
  for (let i = loopStart; i >= 0; i--) {
842
1306
  const id = getId(i);
@@ -849,7 +1313,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
849
1313
  }
850
1314
  const top2 = newPosition || positions.get(id);
851
1315
  if (top2 !== void 0) {
852
- const size = getItemSize(id, i, data2[i]);
1316
+ const size = getItemSize(id, i, data[i]);
853
1317
  const bottom = top2 + size;
854
1318
  if (bottom > scroll - scrollBuffer) {
855
1319
  loopStart = i;
@@ -858,7 +1322,6 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
858
1322
  }
859
1323
  }
860
1324
  }
861
- const numColumns = peek$(ctx, "numColumns");
862
1325
  const loopStartMod = loopStart % numColumns;
863
1326
  if (loopStartMod > 0) {
864
1327
  loopStart -= loopStartMod;
@@ -874,15 +1337,15 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
874
1337
  topOffset = positions.get(id);
875
1338
  }
876
1339
  if (id === ((_a2 = state.anchorElement) == null ? void 0 : _a2.id)) {
877
- topOffset = initialContentOffset || 0;
1340
+ topOffset = state.anchorElement.coordinate;
878
1341
  }
879
1342
  return topOffset;
880
1343
  };
881
- for (let i = loopStart; i < data2.length; i++) {
1344
+ for (let i = Math.max(0, loopStart); i < data.length; i++) {
882
1345
  const id = getId(i);
883
- const size = getItemSize(id, i, data2[i]);
1346
+ const size = getItemSize(id, i, data[i]);
884
1347
  maxSizeInRow = Math.max(maxSizeInRow, size);
885
- if (top === void 0) {
1348
+ if (top === void 0 || id === ((_a = state.anchorElement) == null ? void 0 : _a.id)) {
886
1349
  top = getInitialTop(i);
887
1350
  }
888
1351
  if (positions.get(id) !== top) {
@@ -935,6 +1398,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
935
1398
  if (startBuffered !== null && endBuffered !== null) {
936
1399
  const prevNumContainers = ctx.values.get("numContainers");
937
1400
  let numContainers = prevNumContainers;
1401
+ let didWarnMoreContainers = false;
938
1402
  for (let i = startBuffered; i <= endBuffered; i++) {
939
1403
  let isContained = false;
940
1404
  const id = getId(i);
@@ -968,18 +1432,19 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
968
1432
  if (furthestIndex >= 0) {
969
1433
  set$(ctx, `containerItemKey${furthestIndex}`, id);
970
1434
  const index = state.indexByKey.get(id);
971
- set$(ctx, `containerItemData${furthestIndex}`, data2[index]);
1435
+ set$(ctx, `containerItemData${furthestIndex}`, data[index]);
972
1436
  } else {
973
1437
  const containerId = numContainers;
974
1438
  numContainers++;
975
1439
  set$(ctx, `containerItemKey${containerId}`, id);
976
1440
  const index = state.indexByKey.get(id);
977
- set$(ctx, `containerItemData${containerId}`, data2[index]);
1441
+ set$(ctx, `containerItemData${containerId}`, data[index]);
978
1442
  set$(ctx, `containerPosition${containerId}`, ANCHORED_POSITION_OUT_OF_VIEW);
979
1443
  set$(ctx, `containerColumn${containerId}`, -1);
980
- if (__DEV__ && numContainers > peek$(ctx, "numContainersPooled")) {
1444
+ if (__DEV__ && !didWarnMoreContainers && numContainers > peek$(ctx, "numContainersPooled")) {
1445
+ didWarnMoreContainers = true;
981
1446
  console.warn(
982
- "[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:",
1447
+ "[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:",
983
1448
  numContainers
984
1449
  );
985
1450
  }
@@ -989,19 +1454,19 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
989
1454
  if (numContainers !== prevNumContainers) {
990
1455
  set$(ctx, "numContainers", numContainers);
991
1456
  if (numContainers > peek$(ctx, "numContainersPooled")) {
992
- set$(ctx, "numContainersPooled", numContainers);
1457
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
993
1458
  }
994
1459
  }
995
1460
  for (let i = 0; i < numContainers; i++) {
996
1461
  const itemKey = peek$(ctx, `containerItemKey${i}`);
997
1462
  const itemIndex = state.indexByKey.get(itemKey);
998
- const item = data2[itemIndex];
999
- if (item) {
1463
+ const item = data[itemIndex];
1464
+ if (item !== void 0) {
1000
1465
  const id = getId(itemIndex);
1001
1466
  if (itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered) {
1002
1467
  const prevPos = peek$(ctx, `containerPosition${i}`).top;
1003
1468
  const pos = positions.get(id) || 0;
1004
- const size = getItemSize(id, itemIndex, data2[i]);
1469
+ const size = getItemSize(id, itemIndex, data[i]);
1005
1470
  if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
1006
1471
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1007
1472
  }
@@ -1013,9 +1478,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1013
1478
  };
1014
1479
  const column2 = columns.get(id) || 1;
1015
1480
  if (maintainVisibleContentPosition && itemIndex < anchorElementIndex) {
1016
- const currentRow = Math.floor(itemIndex / numColumnsProp);
1481
+ const currentRow = Math.floor(itemIndex / numColumns);
1017
1482
  const rowHeight = getRowHeight(currentRow);
1018
- const elementHeight = getItemSize(id, itemIndex, data2[i]);
1483
+ const elementHeight = getItemSize(id, itemIndex, data[i]);
1019
1484
  const diff = rowHeight - elementHeight;
1020
1485
  pos.relativeCoordinate = pos.top + getRowHeight(currentRow) - diff;
1021
1486
  pos.type = "bottom";
@@ -1030,13 +1495,25 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1030
1495
  set$(ctx, `containerColumn${i}`, column2);
1031
1496
  }
1032
1497
  if (prevData !== item) {
1033
- set$(ctx, `containerItemData${i}`, data2[itemIndex]);
1498
+ set$(ctx, `containerItemData${i}`, data[itemIndex]);
1034
1499
  }
1035
1500
  }
1036
1501
  }
1037
1502
  }
1038
1503
  }
1039
- set$(ctx, "containersDidLayout", true);
1504
+ if (state.numPendingInitialLayout === 0) {
1505
+ state.numPendingInitialLayout = state.endBuffered - state.startBuffered + 1;
1506
+ }
1507
+ if (!state.queuedInitialLayout && endBuffered !== null) {
1508
+ let areAllKnown = true;
1509
+ for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1510
+ const key = getId(i);
1511
+ areAllKnown && (areAllKnown = state.sizesKnown.has(key));
1512
+ }
1513
+ if (areAllKnown) {
1514
+ setDidLayout();
1515
+ }
1516
+ }
1040
1517
  if (state.viewabilityConfigCallbackPairs) {
1041
1518
  updateViewableItems(
1042
1519
  state,
@@ -1049,50 +1526,98 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1049
1526
  );
1050
1527
  }
1051
1528
  }, []);
1052
- const doUpdatePaddingTop = () => {
1529
+ const setPaddingTop = ({
1530
+ stylePaddingTop,
1531
+ alignItemsPaddingTop
1532
+ }) => {
1533
+ if (stylePaddingTop !== void 0) {
1534
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1535
+ }
1536
+ if (alignItemsPaddingTop !== void 0) {
1537
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1538
+ }
1539
+ set$(
1540
+ ctx,
1541
+ "paddingTop",
1542
+ (stylePaddingTop != null ? stylePaddingTop : peek$(ctx, "stylePaddingTop")) + (alignItemsPaddingTop != null ? alignItemsPaddingTop : peek$(ctx, "alignItemsPaddingTop"))
1543
+ );
1544
+ };
1545
+ const updateAlignItemsPaddingTop = () => {
1053
1546
  if (alignItemsAtEnd) {
1054
- const { scrollLength, totalSize } = refState.current;
1055
- const listPaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1056
- const paddingTop = Math.max(0, Math.floor(scrollLength - totalSize - listPaddingTop));
1057
- set$(ctx, "paddingTop", paddingTop);
1547
+ const { scrollLength } = refState.current;
1548
+ const contentSize = getContentSize(ctx);
1549
+ const paddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1550
+ setPaddingTop({ alignItemsPaddingTop: paddingTop });
1058
1551
  }
1059
1552
  };
1553
+ const scrollTo = (offset, animated) => {
1554
+ var _a;
1555
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1556
+ x: horizontal ? offset : 0,
1557
+ y: horizontal ? 0 : offset,
1558
+ animated: !!animated
1559
+ });
1560
+ };
1060
1561
  const doMaintainScrollAtEnd = (animated) => {
1061
1562
  const state = refState.current;
1062
- if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd) {
1063
- state.scroll = state.totalSize - state.scrollLength + peek$(ctx, "paddingTop");
1563
+ if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
1564
+ const paddingTop = peek$(ctx, "alignItemsPaddingTop");
1565
+ if (paddingTop > 0) {
1566
+ state.scroll = 0;
1567
+ }
1064
1568
  requestAnimationFrame(() => {
1065
- var _a2;
1066
- (_a2 = refScroller.current) == null ? void 0 : _a2.scrollToEnd({
1569
+ var _a;
1570
+ (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
1067
1571
  animated
1068
1572
  });
1069
1573
  });
1070
1574
  return true;
1071
1575
  }
1072
1576
  };
1577
+ const checkThreshold = (distance, atThreshold, threshold, isReached, isBlockedByTimer, onReached, blockTimer) => {
1578
+ const distanceAbs = Math.abs(distance);
1579
+ const isAtThreshold = atThreshold || distanceAbs < threshold;
1580
+ if (!isReached && !isBlockedByTimer) {
1581
+ if (isAtThreshold) {
1582
+ onReached == null ? void 0 : onReached(distance);
1583
+ blockTimer == null ? void 0 : blockTimer(true);
1584
+ setTimeout(() => {
1585
+ blockTimer == null ? void 0 : blockTimer(false);
1586
+ }, 700);
1587
+ return true;
1588
+ }
1589
+ } else {
1590
+ if (distance >= 1.3 * threshold) {
1591
+ return false;
1592
+ }
1593
+ }
1594
+ return isReached;
1595
+ };
1073
1596
  const checkAtBottom = () => {
1074
1597
  if (!refState.current) {
1075
1598
  return;
1076
1599
  }
1077
- const { scrollLength, scroll, totalSize } = refState.current;
1078
- if (totalSize > 0) {
1079
- const distanceFromEnd = totalSize - scroll - scrollLength;
1080
- if (refState.current) {
1081
- refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1082
- }
1083
- const { onEndReached } = callbacks.current;
1084
- if (onEndReached) {
1085
- if (!refState.current.isEndReached) {
1086
- if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
1087
- refState.current.isEndReached = true;
1088
- onEndReached({ distanceFromEnd });
1089
- }
1090
- } else {
1091
- if (distanceFromEnd >= onEndReachedThreshold * scrollLength) {
1092
- refState.current.isEndReached = false;
1093
- }
1600
+ const { queuedInitialLayout, scrollLength, scroll } = refState.current;
1601
+ const contentSize = getContentSize(ctx);
1602
+ if (contentSize > 0 && queuedInitialLayout) {
1603
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1604
+ const distanceFromEndAbs = Math.abs(distanceFromEnd);
1605
+ const isContentLess = contentSize < scrollLength;
1606
+ refState.current.isAtBottom = isContentLess || distanceFromEndAbs < scrollLength * maintainScrollAtEndThreshold;
1607
+ refState.current.isEndReached = checkThreshold(
1608
+ distanceFromEnd,
1609
+ isContentLess,
1610
+ onEndReachedThreshold * scrollLength,
1611
+ refState.current.isEndReached,
1612
+ refState.current.endReachedBlockedByTimer,
1613
+ (distance) => {
1614
+ var _a, _b;
1615
+ return (_b = (_a = callbacks.current).onEndReached) == null ? void 0 : _b.call(_a, { distanceFromEnd: distance });
1616
+ },
1617
+ (block) => {
1618
+ refState.current.endReachedBlockedByTimer = block;
1094
1619
  }
1095
- }
1620
+ );
1096
1621
  }
1097
1622
  };
1098
1623
  const checkAtTop = () => {
@@ -1101,113 +1626,119 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1101
1626
  }
1102
1627
  const { scrollLength, scroll } = refState.current;
1103
1628
  const distanceFromTop = scroll;
1104
- refState.current.isAtTop = distanceFromTop < 0;
1105
- const { onStartReached } = callbacks.current;
1106
- if (onStartReached) {
1107
- if (!refState.current.isStartReached && !refState.current.startReachedBlockedByTimer) {
1108
- if (distanceFromTop < onStartReachedThreshold * scrollLength) {
1109
- refState.current.isStartReached = true;
1110
- onStartReached({ distanceFromStart: scroll });
1111
- refState.current.startReachedBlockedByTimer = true;
1112
- setTimeout(() => {
1113
- refState.current.startReachedBlockedByTimer = false;
1114
- }, 700);
1115
- }
1116
- } else {
1117
- if (distanceFromTop >= 1.3 * onStartReachedThreshold * scrollLength) {
1118
- refState.current.isStartReached = false;
1119
- }
1629
+ const distanceFromTopAbs = Math.abs(distanceFromTop);
1630
+ refState.current.isAtTop = distanceFromTopAbs < 0;
1631
+ refState.current.isStartReached = checkThreshold(
1632
+ distanceFromTop,
1633
+ false,
1634
+ onStartReachedThreshold * scrollLength,
1635
+ refState.current.isStartReached,
1636
+ refState.current.startReachedBlockedByTimer,
1637
+ (distance) => {
1638
+ var _a, _b;
1639
+ return (_b = (_a = callbacks.current).onStartReached) == null ? void 0 : _b.call(_a, { distanceFromStart: distance });
1640
+ },
1641
+ (block) => {
1642
+ refState.current.startReachedBlockedByTimer = block;
1120
1643
  }
1121
- }
1644
+ );
1122
1645
  };
1123
- const checkResetContainers = (reset) => {
1646
+ const checkResetContainers = (isFirst2) => {
1124
1647
  const state = refState.current;
1125
1648
  if (state) {
1126
- state.data = data;
1127
- if (reset) {
1649
+ state.data = dataProp;
1650
+ if (!isFirst2) {
1128
1651
  refState.current.scrollForNextCalculateItemsInView = void 0;
1129
1652
  const numContainers = peek$(ctx, "numContainers");
1130
1653
  for (let i = 0; i < numContainers; i++) {
1131
1654
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1132
1655
  if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
1133
1656
  set$(ctx, `containerItemKey${i}`, void 0);
1657
+ set$(ctx, `containerItemData${i}`, void 0);
1134
1658
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1135
1659
  set$(ctx, `containerColumn${i}`, -1);
1136
1660
  }
1137
1661
  }
1138
1662
  if (!keyExtractorProp) {
1139
- state.sizes.clear();
1140
1663
  state.positions.clear();
1141
1664
  }
1142
- calculateItemsInView(state.scrollVelocity);
1143
- }
1144
- const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1145
- if (!didMaintainScrollAtEnd && data.length > state.data.length) {
1146
- state.isEndReached = false;
1665
+ calculateItemsInView();
1666
+ const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1667
+ if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1668
+ state.isEndReached = false;
1669
+ }
1670
+ checkAtTop();
1671
+ checkAtBottom();
1147
1672
  }
1148
- checkAtTop();
1149
- checkAtBottom();
1150
1673
  }
1151
1674
  };
1152
- const isFirst = !refState.current.renderItem;
1153
- if (isFirst || data !== refState.current.data || numColumnsProp !== peek$(ctx, "numColumns")) {
1154
- if (!keyExtractorProp && !isFirst && data !== refState.current.data) {
1155
- refState.current.sizes.clear();
1156
- refState.current.positions.clear();
1157
- }
1158
- refState.current.data = data;
1675
+ const calcTotalSizesAndPositions = ({ forgetPositions = false }) => {
1676
+ var _a, _b;
1159
1677
  let totalSize = 0;
1160
1678
  let totalSizeBelowIndex = 0;
1161
1679
  const indexByKey = /* @__PURE__ */ new Map();
1162
1680
  const newPositions = /* @__PURE__ */ new Map();
1163
1681
  let column = 1;
1164
1682
  let maxSizeInRow = 0;
1165
- for (let i = 0; i < data.length; i++) {
1683
+ const numColumns = (_a = peek$(ctx, "numColumns")) != null ? _a : numColumnsProp;
1684
+ if (!refState.current) {
1685
+ return;
1686
+ }
1687
+ for (let i = 0; i < dataProp.length; i++) {
1166
1688
  const key = getId(i);
1689
+ if (__DEV__) {
1690
+ if (indexByKey.has(key)) {
1691
+ console.error(
1692
+ `[legend-list] Error: Detected overlapping key (${key}) which causes missing items and gaps and other terrrible things. Check that keyExtractor returns unique values.`
1693
+ );
1694
+ }
1695
+ }
1167
1696
  indexByKey.set(key, i);
1168
- if (refState.current.positions.get(key) != null && refState.current.indexByKey.get(key) === i) {
1697
+ if (!forgetPositions && refState.current.positions.get(key) != null && refState.current.indexByKey.get(key) === i) {
1169
1698
  newPositions.set(key, refState.current.positions.get(key));
1170
1699
  }
1171
1700
  }
1172
1701
  refState.current.indexByKey = indexByKey;
1173
1702
  refState.current.positions = newPositions;
1174
- if (maintainVisibleContentPosition) {
1175
- if (refState.current.anchorElement == null || indexByKey.get(refState.current.anchorElement.id) == null) {
1176
- if (data.length) {
1177
- const newAnchorElement = {
1178
- coordinate: 0,
1179
- id: getId(0)
1180
- };
1181
- refState.current.anchorElement = newAnchorElement;
1182
- (_a = refState.current.belowAnchorElementPositions) == null ? void 0 : _a.clear();
1183
- refScroller.current.scrollTo({ x: 0, y: 0, animated: false });
1703
+ if (!forgetPositions && !isFirst) {
1704
+ if (maintainVisibleContentPosition) {
1705
+ if (refState.current.anchorElement == null || indexByKey.get(refState.current.anchorElement.id) == null) {
1706
+ if (dataProp.length) {
1707
+ const newAnchorElement = {
1708
+ coordinate: 0,
1709
+ id: getId(0)
1710
+ };
1711
+ refState.current.anchorElement = newAnchorElement;
1712
+ (_b = refState.current.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1713
+ scrollTo(0, false);
1714
+ setTimeout(() => {
1715
+ calculateItemsInView();
1716
+ }, 0);
1717
+ } else {
1718
+ refState.current.startBufferedId = void 0;
1719
+ }
1720
+ }
1721
+ } else {
1722
+ if (refState.current.startBufferedId != null && newPositions.get(refState.current.startBufferedId) == null) {
1723
+ if (dataProp.length) {
1724
+ refState.current.startBufferedId = getId(0);
1725
+ } else {
1726
+ refState.current.startBufferedId = void 0;
1727
+ }
1728
+ scrollTo(0, false);
1184
1729
  setTimeout(() => {
1185
- calculateItemsInView(0);
1730
+ calculateItemsInView();
1186
1731
  }, 0);
1187
- } else {
1188
- refState.current.startBufferedId = void 0;
1189
- }
1190
- }
1191
- } else {
1192
- if (refState.current.startBufferedId != null && newPositions.get(refState.current.startBufferedId) == null) {
1193
- if (data.length) {
1194
- refState.current.startBufferedId = getId(0);
1195
- } else {
1196
- refState.current.startBufferedId = void 0;
1197
1732
  }
1198
- refScroller.current.scrollTo({ x: 0, y: 0, animated: false });
1199
- setTimeout(() => {
1200
- calculateItemsInView(0);
1201
- }, 0);
1202
1733
  }
1203
1734
  }
1204
1735
  const anchorElementIndex = getAnchorElementIndex();
1205
- for (let i = 0; i < data.length; i++) {
1736
+ for (let i = 0; i < dataProp.length; i++) {
1206
1737
  const key = getId(i);
1207
- const size = getItemSize(key, i, data[i]);
1738
+ const size = getItemSize(key, i, dataProp[i]);
1208
1739
  maxSizeInRow = Math.max(maxSizeInRow, size);
1209
1740
  column++;
1210
- if (column > numColumnsProp) {
1741
+ if (column > numColumns) {
1211
1742
  if (maintainVisibleContentPosition && anchorElementIndex !== void 0 && i < anchorElementIndex) {
1212
1743
  totalSizeBelowIndex += maxSizeInRow;
1213
1744
  }
@@ -1219,111 +1750,173 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1219
1750
  if (maxSizeInRow > 0) {
1220
1751
  totalSize += maxSizeInRow;
1221
1752
  }
1753
+ const state = refState.current;
1754
+ state.ignoreScrollFromCalcTotal = true;
1755
+ requestAnimationFrame(() => {
1756
+ state.ignoreScrollFromCalcTotal = false;
1757
+ });
1222
1758
  addTotalSize(null, totalSize, totalSizeBelowIndex);
1223
- }
1224
- useEffect(() => {
1225
- checkResetContainers(
1226
- /*reset*/
1227
- !isFirst
1759
+ };
1760
+ const isFirst = !refState.current.renderItem;
1761
+ const memoizedLastItemKeys = useMemo(() => {
1762
+ if (!dataProp.length) return [];
1763
+ return Array.from(
1764
+ { length: Math.min(numColumnsProp, dataProp.length) },
1765
+ (_, i) => getId(dataProp.length - 1 - i)
1228
1766
  );
1229
- }, [isFirst, data, numColumnsProp]);
1230
- useEffect(() => {
1231
- set$(ctx, "extraData", extraData);
1232
- }, [extraData]);
1233
- refState.current.renderItem = renderItem;
1234
- const lastItemKey = data.length > 0 ? getId(data.length - 1) : void 0;
1235
- 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;
1767
+ }, [dataProp, numColumnsProp]);
1236
1768
  const initalizeStateVars = () => {
1237
- set$(ctx, "lastItemKey", lastItemKey);
1769
+ set$(ctx, "lastItemKeys", memoizedLastItemKeys);
1238
1770
  set$(ctx, "numColumns", numColumnsProp);
1239
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1771
+ const prevPaddingTop = peek$(ctx, "stylePaddingTop");
1772
+ setPaddingTop({ stylePaddingTop: stylePaddingTopState });
1773
+ const paddingDiff = stylePaddingTopState - prevPaddingTop;
1774
+ if (paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
1775
+ queueMicrotask(() => {
1776
+ scrollTo(refState.current.scroll + paddingDiff, false);
1777
+ });
1778
+ }
1240
1779
  };
1241
1780
  if (isFirst) {
1242
1781
  initalizeStateVars();
1243
1782
  }
1244
- useEffect(initalizeStateVars, [lastItemKey, numColumnsProp, stylePaddingTop]);
1783
+ if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
1784
+ refState.current.lastBatchingAction = Date.now();
1785
+ if (!keyExtractorProp && !isFirst && didDataChange) {
1786
+ refState.current.sizes.clear();
1787
+ refState.current.positions.clear();
1788
+ }
1789
+ calcTotalSizesAndPositions({ forgetPositions: false });
1790
+ }
1791
+ useEffect(() => {
1792
+ const didAllocateContainers = doInitialAllocateContainers();
1793
+ if (!didAllocateContainers) {
1794
+ checkResetContainers(
1795
+ /*isFirst*/
1796
+ isFirst
1797
+ );
1798
+ }
1799
+ }, [isFirst, dataProp, numColumnsProp]);
1800
+ useEffect(() => {
1801
+ set$(ctx, "extraData", extraData);
1802
+ }, [extraData]);
1803
+ refState.current.renderItem = renderItem;
1804
+ useEffect(initalizeStateVars, [memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingTopState]);
1245
1805
  const getRenderedItem = useCallback((key) => {
1246
- var _a2, _b2;
1806
+ var _a, _b;
1247
1807
  const state = refState.current;
1248
1808
  if (!state) {
1249
1809
  return null;
1250
1810
  }
1251
- const { data: data2, indexByKey } = state;
1811
+ const { data, indexByKey } = state;
1252
1812
  const index = indexByKey.get(key);
1253
1813
  if (index === void 0) {
1254
1814
  return null;
1255
1815
  }
1256
- const useViewability2 = (configId, callback) => {
1257
- useViewability(configId, callback);
1258
- };
1259
- const useViewabilityAmount2 = (callback) => {
1260
- useViewabilityAmount(callback);
1261
- };
1262
- const useRecyclingEffect2 = (effect) => {
1263
- useRecyclingEffect(effect);
1264
- };
1265
- const useRecyclingState2 = (valueOrFun) => {
1266
- return useRecyclingState(valueOrFun);
1267
- };
1268
- const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
1269
- item: data2[index],
1816
+ const useViewability2 = __DEV__ ? (configId, callback) => {
1817
+ console.warn(
1818
+ `[legend-list] useViewability has been moved from a render prop to a regular import: import { useViewability } from "@legendapp/list";`
1819
+ );
1820
+ } : void 0;
1821
+ const useViewabilityAmount2 = __DEV__ ? (callback) => {
1822
+ console.warn(
1823
+ `[legend-list] useViewabilityAmount has been moved from a render prop to a regular import: import { useViewabilityAmount } from "@legendapp/list";`
1824
+ );
1825
+ } : void 0;
1826
+ const useRecyclingEffect2 = __DEV__ ? (effect) => {
1827
+ console.warn(
1828
+ `[legend-list] useRecyclingEffect has been moved from a render prop to a regular import: import { useRecyclingEffect } from "@legendapp/list";`
1829
+ );
1830
+ } : void 0;
1831
+ const useRecyclingState2 = __DEV__ ? (valueOrFun) => {
1832
+ console.warn(
1833
+ `[legend-list] useRecyclingState has been moved from a render prop to a regular import: import { useRecyclingState } from "@legendapp/list";`
1834
+ );
1835
+ } : void 0;
1836
+ const renderedItem = (_b = (_a = refState.current).renderItem) == null ? void 0 : _b.call(_a, {
1837
+ item: data[index],
1270
1838
  index,
1839
+ extraData: peek$(ctx, "extraData"),
1840
+ // @ts-expect-error TODO: Remove these before 1.0
1271
1841
  useViewability: useViewability2,
1272
1842
  useViewabilityAmount: useViewabilityAmount2,
1273
1843
  useRecyclingEffect: useRecyclingEffect2,
1274
1844
  useRecyclingState: useRecyclingState2
1275
1845
  });
1276
- return { index, renderedItem };
1846
+ return { index, item: data[index], renderedItem };
1277
1847
  }, []);
1278
- useInit(() => {
1279
- var _a2;
1848
+ const doInitialAllocateContainers = () => {
1849
+ var _a;
1280
1850
  const state = refState.current;
1281
- const viewability = setupViewability(props);
1851
+ const { scrollLength, data } = state;
1852
+ if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
1853
+ const averageItemSize = (_a = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a : DEFAULT_ITEM_SIZE;
1854
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1855
+ for (let i = 0; i < numContainers; i++) {
1856
+ set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1857
+ set$(ctx, `containerColumn${i}`, -1);
1858
+ }
1859
+ set$(ctx, "numContainers", numContainers);
1860
+ set$(ctx, "numContainersPooled", numContainers * initialContainerPoolRatio);
1861
+ if (initialScrollIndex) {
1862
+ requestAnimationFrame(() => {
1863
+ calculateItemsInView();
1864
+ });
1865
+ } else {
1866
+ calculateItemsInView();
1867
+ }
1868
+ return true;
1869
+ }
1870
+ };
1871
+ useEffect(() => {
1872
+ const state = refState.current;
1873
+ const viewability = setupViewability({
1874
+ viewabilityConfig,
1875
+ viewabilityConfigCallbackPairs,
1876
+ onViewableItemsChanged
1877
+ });
1282
1878
  state.viewabilityConfigCallbackPairs = viewability;
1283
1879
  state.enableScrollForNextCalculateItemsInView = !viewability;
1284
- const scrollLength = state.scrollLength;
1285
- const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1286
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1287
- for (let i = 0; i < numContainers; i++) {
1288
- set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1289
- set$(ctx, `containerColumn${i}`, -1);
1290
- }
1291
- set$(ctx, "numContainers", numContainers);
1292
- set$(ctx, "numContainersPooled", numContainers * 2);
1293
- calculateItemsInView(state.scrollVelocity);
1880
+ }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
1881
+ useInit(() => {
1882
+ doInitialAllocateContainers();
1294
1883
  });
1295
- const updateItemSize = useCallback((containerId, itemKey, size) => {
1296
- var _a2;
1297
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
1298
- if (!data2) {
1884
+ const updateItemSize = useCallback((itemKey, size, fromFixGaps) => {
1885
+ const state = refState.current;
1886
+ const { sizes, indexByKey, sizesKnown, data, rowHeights, startBuffered, endBuffered } = state;
1887
+ if (!data) {
1299
1888
  return;
1300
1889
  }
1301
- const state = refState.current;
1302
- const { sizes, indexByKey, columns, sizesLaidOut } = state;
1303
1890
  const index = indexByKey.get(itemKey);
1304
1891
  const numColumns = peek$(ctx, "numColumns");
1305
- const row = Math.floor(index / numColumns);
1306
- const prevSize = getRowHeight(row);
1892
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1893
+ const prevSize = getItemSize(itemKey, index, data);
1894
+ let needsCalculate = false;
1895
+ let needsUpdateContainersDidLayout = false;
1896
+ if (state.numPendingInitialLayout > 0) {
1897
+ state.numPendingInitialLayout--;
1898
+ if (state.numPendingInitialLayout === 0) {
1899
+ needsCalculate = true;
1900
+ state.numPendingInitialLayout = -1;
1901
+ needsUpdateContainersDidLayout = true;
1902
+ }
1903
+ }
1904
+ sizesKnown == null ? void 0 : sizesKnown.set(itemKey, size);
1307
1905
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
1308
1906
  let diff;
1907
+ needsCalculate = true;
1309
1908
  if (numColumns > 1) {
1310
- const prevMaxSizeInRow = getRowHeight(row);
1909
+ const rowNumber = Math.floor(index / numColumnsProp);
1910
+ const prevSizeInRow = getRowHeight(rowNumber);
1311
1911
  sizes.set(itemKey, size);
1312
- const column = columns.get(itemKey);
1313
- const loopStart = index - (column - 1);
1314
- let nextMaxSizeInRow = 0;
1315
- for (let i = loopStart; i < loopStart + numColumns && i < data2.length; i++) {
1316
- const id = getId(i);
1317
- const size2 = getItemSize(id, i, data2[i]);
1318
- nextMaxSizeInRow = Math.max(nextMaxSizeInRow, size2);
1319
- }
1320
- diff = nextMaxSizeInRow - prevMaxSizeInRow;
1912
+ rowHeights.delete(rowNumber);
1913
+ const sizeInRow = getRowHeight(rowNumber);
1914
+ diff = sizeInRow - prevSizeInRow;
1321
1915
  } else {
1322
1916
  sizes.set(itemKey, size);
1323
1917
  diff = size - prevSize;
1324
1918
  }
1325
1919
  if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
1326
- sizesLaidOut.set(itemKey, size);
1327
1920
  if (state.timeoutSizeMessage) {
1328
1921
  clearTimeout(state.timeoutSizeMessage);
1329
1922
  }
@@ -1331,7 +1924,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1331
1924
  state.timeoutSizeMessage = void 0;
1332
1925
  let total = 0;
1333
1926
  let num = 0;
1334
- for (const [key, size2] of sizesLaidOut) {
1927
+ for (const [_, size2] of sizesKnown) {
1335
1928
  num++;
1336
1929
  total += size2;
1337
1930
  }
@@ -1343,38 +1936,54 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1343
1936
  }
1344
1937
  refState.current.scrollForNextCalculateItemsInView = void 0;
1345
1938
  addTotalSize(itemKey, diff, 0);
1346
- doMaintainScrollAtEnd(true);
1939
+ doMaintainScrollAtEnd(false);
1940
+ if (onItemSizeChanged) {
1941
+ onItemSizeChanged({
1942
+ size,
1943
+ previous: prevSize,
1944
+ index,
1945
+ itemKey,
1946
+ itemData: data[index]
1947
+ });
1948
+ }
1949
+ }
1950
+ const isInView = index >= startBuffered && index <= endBuffered;
1951
+ if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && isInView) {
1347
1952
  const scrollVelocity = state.scrollVelocity;
1348
- if (!state.waitingForMicrotask && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1349
- if (!peek$(ctx, "containersDidLayout")) {
1350
- state.waitingForMicrotask = true;
1351
- queueMicrotask(() => {
1352
- if (state.waitingForMicrotask) {
1353
- state.waitingForMicrotask = false;
1354
- calculateItemsInView(state.scrollVelocity);
1355
- }
1356
- });
1953
+ let didCalculate = false;
1954
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || state.numPendingInitialLayout < 0)) {
1955
+ if (Date.now() - state.lastBatchingAction < 500) {
1956
+ if (!state.queuedCalculateItemsInView) {
1957
+ state.queuedCalculateItemsInView = requestAnimationFrame(() => {
1958
+ state.queuedCalculateItemsInView = void 0;
1959
+ calculateItemsInView();
1960
+ });
1961
+ }
1357
1962
  } else {
1358
- calculateItemsInView(state.scrollVelocity);
1963
+ calculateItemsInView();
1964
+ didCalculate = true;
1359
1965
  }
1360
1966
  }
1361
- if (onItemSizeChanged) {
1362
- onItemSizeChanged({ size, previous: prevSize, index, itemKey, itemData: data2[index] });
1967
+ if (!didCalculate && IsNewArchitecture) {
1968
+ fixGaps();
1363
1969
  }
1364
1970
  }
1365
1971
  }, []);
1366
- const handleScrollDebounced = useCallback((velocity) => {
1367
- calculateItemsInView(velocity);
1368
- checkAtBottom();
1369
- checkAtTop();
1370
- }, []);
1371
1972
  const onLayout = useCallback((event) => {
1973
+ const state = refState.current;
1372
1974
  const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
1373
- refState.current.scrollLength = scrollLength;
1975
+ const didChange = scrollLength !== state.scrollLength;
1976
+ state.scrollLength = scrollLength;
1977
+ state.lastBatchingAction = Date.now();
1978
+ state.scrollForNextCalculateItemsInView = void 0;
1979
+ doInitialAllocateContainers();
1374
1980
  doMaintainScrollAtEnd(false);
1375
- doUpdatePaddingTop();
1981
+ updateAlignItemsPaddingTop();
1376
1982
  checkAtBottom();
1377
1983
  checkAtTop();
1984
+ if (didChange) {
1985
+ calculateItemsInView();
1986
+ }
1378
1987
  if (__DEV__) {
1379
1988
  const isWidthZero = event.nativeEvent.layout.width === 0;
1380
1989
  const isHeightZero = event.nativeEvent.layout.height === 0;
@@ -1390,14 +1999,18 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1390
1999
  }, []);
1391
2000
  const handleScroll = useCallback(
1392
2001
  (event, fromSelf) => {
1393
- var _a2, _b2, _c2;
1394
- 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) {
2002
+ var _a, _b, _c, _d;
2003
+ if (((_b = (_a = event.nativeEvent) == null ? void 0 : _a.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
1395
2004
  return;
1396
2005
  }
1397
2006
  const state = refState.current;
2007
+ const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
2008
+ if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
2009
+ return;
2010
+ }
1398
2011
  state.hasScrolled = true;
2012
+ state.lastBatchingAction = Date.now();
1399
2013
  const currentTime = performance.now();
1400
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
1401
2014
  if (!(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
1402
2015
  state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1403
2016
  }
@@ -1423,9 +2036,11 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1423
2036
  state.scroll = newScroll;
1424
2037
  state.scrollTime = currentTime;
1425
2038
  state.scrollVelocity = velocity;
1426
- handleScrollDebounced(velocity);
2039
+ calculateItemsInView();
2040
+ checkAtBottom();
2041
+ checkAtTop();
1427
2042
  if (!fromSelf) {
1428
- onScrollProp == null ? void 0 : onScrollProp(event);
2043
+ (_d = state.onScroll) == null ? void 0 : _d.call(state, event);
1429
2044
  }
1430
2045
  },
1431
2046
  []
@@ -1433,61 +2048,127 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
1433
2048
  useImperativeHandle(
1434
2049
  forwardedRef,
1435
2050
  () => {
1436
- const scrollToIndex = ({ index, animated }) => {
1437
- const offsetObj = calculateInitialOffset(index);
1438
- const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
1439
- refScroller.current.scrollTo({ ...offset, animated });
2051
+ const scrollIndexIntoView = (options) => {
2052
+ if (refState.current) {
2053
+ const { index, ...rest2 } = options;
2054
+ const { startNoBuffer, endNoBuffer } = refState.current;
2055
+ if (index < startNoBuffer || index > endNoBuffer) {
2056
+ const viewPosition = index < startNoBuffer ? 0 : 1;
2057
+ scrollToIndex({
2058
+ ...rest2,
2059
+ viewPosition,
2060
+ index
2061
+ });
2062
+ }
2063
+ }
1440
2064
  };
1441
2065
  return {
2066
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
1442
2067
  getNativeScrollRef: () => refScroller.current,
1443
2068
  getScrollableNode: () => refScroller.current.getScrollableNode(),
1444
2069
  getScrollResponder: () => refScroller.current.getScrollResponder(),
1445
- flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
1446
- scrollToIndex,
1447
- scrollToOffset: ({ offset, animated }) => {
1448
- const offsetObj = horizontal ? { x: offset, y: 0 } : { x: 0, y: offset };
1449
- refScroller.current.scrollTo({ ...offsetObj, animated });
2070
+ getState: () => {
2071
+ const state = refState.current;
2072
+ return state ? {
2073
+ contentLength: state.totalSize,
2074
+ end: state.endNoBuffer,
2075
+ endBuffered: state.endBuffered,
2076
+ isAtEnd: state.isAtBottom,
2077
+ isAtStart: state.isAtTop,
2078
+ scroll: state.scroll,
2079
+ scrollLength: state.scrollLength,
2080
+ start: state.startNoBuffer,
2081
+ startBuffered: state.startBuffered
2082
+ } : {};
1450
2083
  },
1451
- scrollToItem: ({ item, animated }) => {
2084
+ scrollIndexIntoView,
2085
+ scrollItemIntoView: ({ item, ...props2 }) => {
2086
+ const { data } = refState.current;
1452
2087
  const index = data.indexOf(item);
1453
2088
  if (index !== -1) {
1454
- scrollToIndex({ index, animated });
2089
+ scrollIndexIntoView({ index, ...props2 });
1455
2090
  }
1456
2091
  },
1457
- scrollToEnd: () => refScroller.current.scrollToEnd()
2092
+ scrollToIndex,
2093
+ scrollToItem: ({ item, ...props2 }) => {
2094
+ const { data } = refState.current;
2095
+ const index = data.indexOf(item);
2096
+ if (index !== -1) {
2097
+ scrollToIndex({ index, ...props2 });
2098
+ }
2099
+ },
2100
+ scrollToOffset: ({ offset, animated }) => {
2101
+ scrollTo(offset, animated);
2102
+ },
2103
+ scrollToEnd: (options) => refScroller.current.scrollToEnd(options)
1458
2104
  };
1459
2105
  },
1460
2106
  []
1461
2107
  );
1462
- return /* @__PURE__ */ React5.createElement(
2108
+ if (Platform.OS === "web") {
2109
+ useEffect(() => {
2110
+ var _a;
2111
+ if (initialContentOffset) {
2112
+ (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler.setDisableAdjust(true);
2113
+ scrollTo(initialContentOffset, false);
2114
+ setTimeout(() => {
2115
+ var _a2;
2116
+ (_a2 = refState.current) == null ? void 0 : _a2.scrollAdjustHandler.setDisableAdjust(false);
2117
+ }, 0);
2118
+ }
2119
+ }, []);
2120
+ }
2121
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(
1463
2122
  ListComponent,
1464
2123
  {
1465
2124
  ...rest,
1466
2125
  horizontal,
1467
- refScrollView: (r) => {
1468
- refScroller.current = r;
1469
- if (refScrollView) {
1470
- if (typeof refScrollView === "function") {
1471
- refScrollView(r);
1472
- } else {
1473
- refScrollView.current = r;
1474
- }
1475
- }
1476
- },
2126
+ refScrollView: combinedRef,
1477
2127
  initialContentOffset,
1478
2128
  getRenderedItem,
1479
2129
  updateItemSize,
1480
2130
  handleScroll,
2131
+ onMomentumScrollEnd: (event) => {
2132
+ var _a;
2133
+ const scrollingToOffset = (_a = refState.current) == null ? void 0 : _a.scrollingToOffset;
2134
+ if (scrollingToOffset !== void 0) {
2135
+ requestAnimationFrame(() => {
2136
+ scrollTo(scrollingToOffset, false);
2137
+ refState.current.scrollingToOffset = void 0;
2138
+ requestAnimationFrame(() => {
2139
+ refState.current.scrollAdjustHandler.setDisableAdjust(false);
2140
+ });
2141
+ });
2142
+ }
2143
+ const wasPaused = refState.current.scrollAdjustHandler.unPauseAdjust();
2144
+ if (wasPaused) {
2145
+ refState.current.scrollVelocity = 0;
2146
+ refState.current.scrollHistory = [];
2147
+ calculateItemsInView();
2148
+ }
2149
+ if (onMomentumScrollEnd) {
2150
+ onMomentumScrollEnd(event);
2151
+ }
2152
+ },
1481
2153
  onLayout,
1482
2154
  recycleItems,
1483
2155
  alignItemsAtEnd,
1484
- ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
2156
+ ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
1485
2157
  maintainVisibleContentPosition,
1486
2158
  scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : Platform.OS === "web" ? 16 : void 0,
1487
2159
  waitForInitialLayout,
1488
- style
2160
+ refreshControl: refreshControl != null ? refreshControl : onRefresh && /* @__PURE__ */ React6.createElement(
2161
+ RefreshControl,
2162
+ {
2163
+ refreshing: !!refreshing,
2164
+ onRefresh,
2165
+ progressViewOffset
2166
+ }
2167
+ ),
2168
+ style,
2169
+ contentContainerStyle
1489
2170
  }
1490
- );
2171
+ ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React6.createElement(DebugView, { state: refState.current }));
1491
2172
  });
1492
2173
 
1493
2174
  export { LegendList, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };