@legendapp/list 1.0.0-beta.4 → 1.0.0-beta.41

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