@legendapp/list 1.0.0-beta.9 → 1.0.1

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