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

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