@legendapp/list 1.0.0-beta.8 → 1.0.0

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