@legendapp/list 1.0.0-beta.8 → 1.0.0

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