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

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