@legendapp/list 1.0.0-beta.9 → 1.0.1

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, _f, _g;
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,113 @@ 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);
651
- if (sizeKnown !== void 0) {
652
- return sizeKnown;
653
- }
654
- const size = (_a2 = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize) != null ? _a2 : DEFAULT_ITEM_SIZE;
655
- refState.current.sizes.set(key, size);
960
+ const getItemSize = (key, index, data, useAverageSize = false) => {
961
+ var _a;
962
+ const state = refState.current;
963
+ const sizeKnown = state.sizesKnown.get(key);
964
+ const sizePrevious = state.sizes.get(key);
965
+ let size;
966
+ const numColumns = peek$(ctx, "numColumns");
967
+ if (sizeKnown === void 0 && !getEstimatedItemSize && numColumns === 1 && useAverageSize) {
968
+ const itemType = "";
969
+ const average = state.averageSizes[itemType];
970
+ if (average) {
971
+ size = roundSize(average.avg);
972
+ if (size !== sizePrevious) {
973
+ addTotalSize(key, size - sizePrevious, 0);
974
+ }
975
+ }
976
+ }
977
+ if (size === void 0 && sizePrevious !== void 0) {
978
+ return sizePrevious;
979
+ }
980
+ if (size === void 0) {
981
+ size = (_a = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize) != null ? _a : DEFAULT_ITEM_SIZE;
982
+ }
983
+ state.sizes.set(key, size);
656
984
  return size;
657
985
  };
658
- const calculateInitialOffset = (index = initialScrollIndex) => {
659
- if (index) {
986
+ const calculateOffsetForIndex = (index = initialScrollIndex) => {
987
+ var _a;
988
+ const data = dataProp;
989
+ if (index !== void 0) {
660
990
  let offset = 0;
661
- if (getEstimatedItemSize) {
991
+ const canGetSize = !!refState.current;
992
+ if (canGetSize || getEstimatedItemSize) {
993
+ const sizeFn = (index2) => {
994
+ if (canGetSize) {
995
+ return getItemSize(getId(index2), index2, data[index2]);
996
+ }
997
+ return getEstimatedItemSize(index2, data[index2]);
998
+ };
662
999
  for (let i = 0; i < index; i++) {
663
- offset += getEstimatedItemSize(i, data[i]);
1000
+ offset += sizeFn(i);
664
1001
  }
665
1002
  } else if (estimatedItemSize) {
666
1003
  offset = index * estimatedItemSize;
667
1004
  }
668
- return offset / numColumnsProp;
1005
+ return offset / numColumnsProp - (((_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler.getAppliedAdjust()) || 0);
669
1006
  }
670
1007
  return 0;
671
1008
  };
672
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : React5.useMemo(calculateInitialOffset, []);
1009
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : React2.useMemo(calculateOffsetForIndex, []);
673
1010
  if (!refState.current) {
674
1011
  const initialScrollLength = reactNative.Dimensions.get("window")[horizontal ? "width" : "height"];
675
1012
  refState.current = {
@@ -677,14 +1014,11 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
677
1014
  positions: /* @__PURE__ */ new Map(),
678
1015
  columns: /* @__PURE__ */ new Map(),
679
1016
  pendingAdjust: 0,
680
- waitingForMicrotask: false,
681
1017
  isStartReached: initialContentOffset < initialScrollLength * onStartReachedThreshold,
682
1018
  isEndReached: false,
683
1019
  isAtBottom: false,
684
1020
  isAtTop: false,
685
- data,
686
- idsInFirstRender: void 0,
687
- hasScrolled: false,
1021
+ data: dataProp,
688
1022
  scrollLength: initialScrollLength,
689
1023
  startBuffered: 0,
690
1024
  startNoBuffer: 0,
@@ -704,36 +1038,48 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
704
1038
  indexByKey: /* @__PURE__ */ new Map(),
705
1039
  scrollHistory: [],
706
1040
  scrollVelocity: 0,
707
- sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
1041
+ sizesKnown: /* @__PURE__ */ new Map(),
708
1042
  timeoutSizeMessage: 0,
709
1043
  scrollTimer: void 0,
710
1044
  belowAnchorElementPositions: void 0,
711
1045
  rowHeights: /* @__PURE__ */ new Map(),
712
1046
  startReachedBlockedByTimer: false,
1047
+ endReachedBlockedByTimer: false,
713
1048
  scrollForNextCalculateItemsInView: void 0,
714
1049
  enableScrollForNextCalculateItemsInView: true,
715
- minIndexSizeChanged: 0
1050
+ minIndexSizeChanged: 0,
1051
+ numPendingInitialLayout: 0,
1052
+ queuedCalculateItemsInView: 0,
1053
+ lastBatchingAction: Date.now(),
1054
+ averageSizes: {},
1055
+ onScroll: onScrollProp
716
1056
  };
717
- refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
718
- if (maintainVisibleContentPosition) {
719
- if (initialScrollIndex) {
1057
+ const dataLength = dataProp.length;
1058
+ if (maintainVisibleContentPosition && dataLength > 0) {
1059
+ if (initialScrollIndex && initialScrollIndex < dataLength) {
720
1060
  refState.current.anchorElement = {
721
1061
  coordinate: initialContentOffset,
722
1062
  id: getId(initialScrollIndex)
723
1063
  };
724
- } else if (data.length) {
1064
+ } else if (dataLength > 0) {
725
1065
  refState.current.anchorElement = {
726
1066
  coordinate: initialContentOffset,
727
1067
  id: getId(0)
728
1068
  };
729
1069
  } else {
730
- console.warn("[legend-list] maintainVisibleContentPosition was not able to find an anchor element");
1070
+ __DEV__ && warnDevOnce(
1071
+ "maintainVisibleContentPosition",
1072
+ "[legend-list] maintainVisibleContentPosition was not able to find an anchor element"
1073
+ );
731
1074
  }
732
1075
  }
733
1076
  set$(ctx, "scrollAdjust", 0);
734
1077
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
735
1078
  set$(ctx, "extraData", extraData);
736
1079
  }
1080
+ const didDataChange = refState.current.data !== dataProp;
1081
+ refState.current.data = dataProp;
1082
+ refState.current.onScroll = onScrollProp;
737
1083
  const getAnchorElementIndex = () => {
738
1084
  const state = refState.current;
739
1085
  if (state.anchorElement) {
@@ -742,12 +1088,65 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
742
1088
  }
743
1089
  return void 0;
744
1090
  };
745
- const addTotalSize = React5.useCallback((key, add, totalSizeBelowAnchor) => {
1091
+ const scrollToIndex = ({
1092
+ index,
1093
+ viewOffset = 0,
1094
+ animated = true,
1095
+ viewPosition = 0
1096
+ }) => {
1097
+ var _a;
746
1098
  const state = refState.current;
747
- const index = key === null ? 0 : state.indexByKey.get(key);
1099
+ const firstIndexOffset = calculateOffsetForIndex(index);
1100
+ let firstIndexScrollPostion = firstIndexOffset - viewOffset;
1101
+ const diff = Math.abs(state.scroll - firstIndexScrollPostion);
1102
+ const needsReanchoring = maintainVisibleContentPosition && diff > 100;
1103
+ state.scrollForNextCalculateItemsInView = void 0;
1104
+ if (needsReanchoring) {
1105
+ const id = getId(index);
1106
+ state.anchorElement = { id, coordinate: firstIndexOffset };
1107
+ (_a = state.belowAnchorElementPositions) == null ? void 0 : _a.clear();
1108
+ state.positions.clear();
1109
+ calcTotalSizesAndPositions({ forgetPositions: true });
1110
+ state.startBufferedId = id;
1111
+ state.minIndexSizeChanged = index;
1112
+ firstIndexScrollPostion = firstIndexOffset - viewOffset + state.scrollAdjustHandler.getAppliedAdjust();
1113
+ }
1114
+ if (viewPosition) {
1115
+ firstIndexScrollPostion -= viewPosition * (state.scrollLength - getItemSize(getId(index), index, state.data[index]));
1116
+ }
1117
+ state.scrollAdjustHandler.setDisableAdjust(true);
1118
+ state.scrollingToOffset = firstIndexScrollPostion;
1119
+ scrollTo(firstIndexScrollPostion, animated);
1120
+ if (!animated) {
1121
+ requestAnimationFrame(() => {
1122
+ state.scrollingToOffset = void 0;
1123
+ state.scrollAdjustHandler.setDisableAdjust(false);
1124
+ });
1125
+ }
1126
+ };
1127
+ const setDidLayout = () => {
1128
+ refState.current.queuedInitialLayout = true;
1129
+ checkAtBottom();
1130
+ if (initialScrollIndex) {
1131
+ queueMicrotask(() => {
1132
+ scrollToIndex({ index: initialScrollIndex, animated: false });
1133
+ requestAnimationFrame(() => {
1134
+ set$(ctx, "containersDidLayout", true);
1135
+ });
1136
+ });
1137
+ } else {
1138
+ queueMicrotask(() => {
1139
+ set$(ctx, "containersDidLayout", true);
1140
+ });
1141
+ }
1142
+ };
1143
+ const addTotalSize = React2.useCallback((key, add, totalSizeBelowAnchor) => {
1144
+ const state = refState.current;
1145
+ const { indexByKey, anchorElement } = state;
1146
+ const index = key === null ? 0 : indexByKey.get(key);
748
1147
  let isAboveAnchor = false;
749
1148
  if (maintainVisibleContentPosition) {
750
- if (state.anchorElement && index < getAnchorElementIndex()) {
1149
+ if (anchorElement && index < getAnchorElementIndex()) {
751
1150
  isAboveAnchor = true;
752
1151
  }
753
1152
  }
@@ -760,29 +1159,30 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
760
1159
  state.totalSizeBelowAnchor += add;
761
1160
  }
762
1161
  }
763
- let applyAdjustValue = void 0;
764
- if (maintainVisibleContentPosition) {
765
- const newAdjust = state.anchorElement.coordinate - state.totalSizeBelowAnchor;
1162
+ let applyAdjustValue = 0;
1163
+ let resultSize = state.totalSize;
1164
+ if (maintainVisibleContentPosition && anchorElement !== void 0) {
1165
+ const newAdjust = anchorElement.coordinate - state.totalSizeBelowAnchor;
766
1166
  applyAdjustValue = -newAdjust;
767
1167
  state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
768
1168
  state.rowHeights.clear();
1169
+ if (applyAdjustValue !== void 0) {
1170
+ resultSize -= applyAdjustValue;
1171
+ state.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
1172
+ state.scroll -= diff;
1173
+ });
1174
+ }
769
1175
  }
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);
1176
+ set$(ctx, "totalSize", state.totalSize);
1177
+ set$(ctx, "totalSizeWithScrollAdjust", resultSize);
779
1178
  if (alignItemsAtEnd) {
780
- doUpdatePaddingTop();
1179
+ updateAlignItemsPaddingTop();
781
1180
  }
782
1181
  }, []);
783
1182
  const getRowHeight = (n) => {
784
- const { rowHeights } = refState.current;
785
- if (numColumnsProp === 1) {
1183
+ const { rowHeights, data } = refState.current;
1184
+ const numColumns = peek$(ctx, "numColumns");
1185
+ if (numColumns === 1) {
786
1186
  const id = getId(n);
787
1187
  return getItemSize(id, n, data[n]);
788
1188
  }
@@ -790,8 +1190,8 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
790
1190
  return rowHeights.get(n) || 0;
791
1191
  }
792
1192
  let rowHeight = 0;
793
- const startEl = n * numColumnsProp;
794
- for (let i = startEl; i < startEl + numColumnsProp && i < data.length; i++) {
1193
+ const startEl = n * numColumns;
1194
+ for (let i = startEl; i < startEl + numColumns && i < data.length; i++) {
795
1195
  const id = getId(i);
796
1196
  const size = getItemSize(id, i, data[i]);
797
1197
  rowHeight = Math.max(rowHeight, size);
@@ -804,16 +1204,17 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
804
1204
  if (!state.anchorElement) {
805
1205
  return /* @__PURE__ */ new Map();
806
1206
  }
807
- let top = state.anchorElement.coordinate;
808
1207
  const anchorIndex = state.indexByKey.get(state.anchorElement.id);
809
1208
  if (anchorIndex === 0) {
810
1209
  return /* @__PURE__ */ new Map();
811
1210
  }
812
1211
  const map = state.belowAnchorElementPositions || /* @__PURE__ */ new Map();
1212
+ const numColumns = peek$(ctx, "numColumns");
1213
+ let top = state.anchorElement.coordinate;
813
1214
  for (let i = anchorIndex - 1; i >= 0; i--) {
814
1215
  const id = getId(i);
815
- const rowNumber = Math.floor(i / numColumnsProp);
816
- if (i % numColumnsProp === 0) {
1216
+ const rowNumber = Math.floor(i / numColumns);
1217
+ if (i % numColumns === 0) {
817
1218
  top -= getRowHeight(rowNumber);
818
1219
  }
819
1220
  map.set(id, top);
@@ -821,37 +1222,116 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
821
1222
  return map;
822
1223
  };
823
1224
  const getElementPositionBelowAchor = (id) => {
1225
+ var _a;
824
1226
  const state = refState.current;
825
1227
  if (!refState.current.belowAnchorElementPositions) {
826
1228
  state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
827
1229
  }
828
1230
  const res = state.belowAnchorElementPositions.get(id);
829
1231
  if (res === void 0) {
830
- throw new Error("Undefined position below achor");
1232
+ console.warn(`Undefined position below anchor ${id} ${(_a = state.anchorElement) == null ? void 0 : _a.id}`);
1233
+ return 0;
831
1234
  }
832
1235
  return res;
833
1236
  };
834
- const calculateItemsInView = React5.useCallback((speed) => {
1237
+ const fixGaps = React2.useCallback(() => {
1238
+ var _a;
1239
+ const state = refState.current;
1240
+ const { data, scrollLength, positions, startBuffered, endBuffered } = state;
1241
+ const numColumns = peek$(ctx, "numColumns");
1242
+ if (!data || scrollLength === 0 || numColumns > 1) {
1243
+ return;
1244
+ }
1245
+ const numContainers = ctx.values.get("numContainers");
1246
+ let numMeasurements = 0;
1247
+ for (let i = 0; i < numContainers; i++) {
1248
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1249
+ const isSizeKnown = state.sizesKnown.get(itemKey);
1250
+ if (itemKey && !isSizeKnown) {
1251
+ const containerRef = ctx.viewRefs.get(i);
1252
+ if (containerRef) {
1253
+ let measured;
1254
+ (_a = containerRef.current) == null ? void 0 : _a.measure((x, y, width, height) => {
1255
+ measured = { width, height };
1256
+ });
1257
+ numMeasurements++;
1258
+ if (measured) {
1259
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
1260
+ updateItemSize(
1261
+ itemKey,
1262
+ size,
1263
+ /*fromFixGaps*/
1264
+ true
1265
+ );
1266
+ }
1267
+ }
1268
+ }
1269
+ }
1270
+ if (numMeasurements > 0) {
1271
+ let top;
1272
+ const diffs = /* @__PURE__ */ new Map();
1273
+ for (let i = startBuffered; i <= endBuffered; i++) {
1274
+ const id = getId(i);
1275
+ if (top === void 0) {
1276
+ top = positions.get(id);
1277
+ }
1278
+ if (positions.get(id) !== top) {
1279
+ diffs.set(id, top - positions.get(id));
1280
+ positions.set(id, top);
1281
+ }
1282
+ const size = getItemSize(id, i, data[i]);
1283
+ const bottom = top + size;
1284
+ top = bottom;
1285
+ }
1286
+ for (let i = 0; i < numContainers; i++) {
1287
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1288
+ const diff = diffs.get(itemKey);
1289
+ if (diff) {
1290
+ const prevPos = peek$(ctx, `containerPosition${i}`);
1291
+ const newPos = prevPos.top + diff;
1292
+ if (prevPos.top !== newPos) {
1293
+ const pos = { ...prevPos };
1294
+ pos.relativeCoordinate += diff;
1295
+ pos.top += diff;
1296
+ set$(ctx, `containerPosition${i}`, pos);
1297
+ }
1298
+ }
1299
+ }
1300
+ }
1301
+ }, []);
1302
+ const calculateItemsInView = React2.useCallback(() => {
1303
+ var _a;
835
1304
  const state = refState.current;
836
1305
  const {
837
- data: data2,
1306
+ data,
838
1307
  scrollLength,
839
- scroll: scrollState,
840
1308
  startBufferedId: startBufferedIdOrig,
841
1309
  positions,
842
1310
  columns,
843
- scrollAdjustHandler
1311
+ scrollAdjustHandler,
1312
+ scrollVelocity: speed
844
1313
  } = state;
845
- if (state.waitingForMicrotask) {
846
- state.waitingForMicrotask = false;
847
- }
848
- if (!data2) {
1314
+ if (!data || scrollLength === 0) {
849
1315
  return;
850
1316
  }
851
- const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1317
+ const totalSize = peek$(ctx, "totalSizeWithScrollAdjust");
1318
+ const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
1319
+ const numColumns = peek$(ctx, "numColumns");
852
1320
  const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
853
1321
  const scrollExtra = Math.max(-16, Math.min(16, speed)) * 16;
854
- const scroll = scrollState - previousScrollAdjust - topPad;
1322
+ let scrollState = state.scroll;
1323
+ if (!state.queuedInitialLayout && initialScrollIndex) {
1324
+ const updatedOffset = calculateOffsetForIndex(initialScrollIndex);
1325
+ scrollState = updatedOffset;
1326
+ }
1327
+ let scroll = scrollState - previousScrollAdjust - topPad;
1328
+ if (scroll + scrollLength > totalSize) {
1329
+ scroll = totalSize - scrollLength;
1330
+ }
1331
+ if (ENABLE_DEBUG_VIEW) {
1332
+ set$(ctx, "debugRawScroll", scrollState);
1333
+ set$(ctx, "debugComputedScroll", scroll);
1334
+ }
855
1335
  let scrollBufferTop = scrollBuffer;
856
1336
  let scrollBufferBottom = scrollBuffer;
857
1337
  if (scrollExtra > 8) {
@@ -891,7 +1371,13 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
891
1371
  }
892
1372
  const top2 = newPosition || positions.get(id);
893
1373
  if (top2 !== void 0) {
894
- const size = getItemSize(id, i, data2[i]);
1374
+ const size = getItemSize(
1375
+ id,
1376
+ i,
1377
+ data[i],
1378
+ /*useAverageSize*/
1379
+ true
1380
+ );
895
1381
  const bottom = top2 + size;
896
1382
  if (bottom > scroll - scrollBuffer) {
897
1383
  loopStart = i;
@@ -900,7 +1386,6 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
900
1386
  }
901
1387
  }
902
1388
  }
903
- const numColumns = peek$(ctx, "numColumns");
904
1389
  const loopStartMod = loopStart % numColumns;
905
1390
  if (loopStartMod > 0) {
906
1391
  loopStart -= loopStartMod;
@@ -916,15 +1401,21 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
916
1401
  topOffset = positions.get(id);
917
1402
  }
918
1403
  if (id === ((_a2 = state.anchorElement) == null ? void 0 : _a2.id)) {
919
- topOffset = initialContentOffset || 0;
1404
+ topOffset = state.anchorElement.coordinate;
920
1405
  }
921
1406
  return topOffset;
922
1407
  };
923
- for (let i = loopStart; i < data2.length; i++) {
1408
+ for (let i = Math.max(0, loopStart); i < data.length; i++) {
924
1409
  const id = getId(i);
925
- const size = getItemSize(id, i, data2[i]);
1410
+ const size = getItemSize(
1411
+ id,
1412
+ i,
1413
+ data[i],
1414
+ /*useAverageSize*/
1415
+ true
1416
+ );
926
1417
  maxSizeInRow = Math.max(maxSizeInRow, size);
927
- if (top === void 0) {
1418
+ if (top === void 0 || id === ((_a = state.anchorElement) == null ? void 0 : _a.id)) {
928
1419
  top = getInitialTop(i);
929
1420
  }
930
1421
  if (positions.get(id) !== top) {
@@ -977,6 +1468,7 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
977
1468
  if (startBuffered !== null && endBuffered !== null) {
978
1469
  const prevNumContainers = ctx.values.get("numContainers");
979
1470
  let numContainers = prevNumContainers;
1471
+ let didWarnMoreContainers = false;
980
1472
  for (let i = startBuffered; i <= endBuffered; i++) {
981
1473
  let isContained = false;
982
1474
  const id = getId(i);
@@ -1010,18 +1502,19 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1010
1502
  if (furthestIndex >= 0) {
1011
1503
  set$(ctx, `containerItemKey${furthestIndex}`, id);
1012
1504
  const index = state.indexByKey.get(id);
1013
- set$(ctx, `containerItemData${furthestIndex}`, data2[index]);
1505
+ set$(ctx, `containerItemData${furthestIndex}`, data[index]);
1014
1506
  } else {
1015
1507
  const containerId = numContainers;
1016
1508
  numContainers++;
1017
1509
  set$(ctx, `containerItemKey${containerId}`, id);
1018
1510
  const index = state.indexByKey.get(id);
1019
- set$(ctx, `containerItemData${containerId}`, data2[index]);
1511
+ set$(ctx, `containerItemData${containerId}`, data[index]);
1020
1512
  set$(ctx, `containerPosition${containerId}`, ANCHORED_POSITION_OUT_OF_VIEW);
1021
1513
  set$(ctx, `containerColumn${containerId}`, -1);
1022
- if (__DEV__ && numContainers > peek$(ctx, "numContainersPooled")) {
1514
+ if (__DEV__ && !didWarnMoreContainers && numContainers > peek$(ctx, "numContainersPooled")) {
1515
+ didWarnMoreContainers = true;
1023
1516
  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:",
1517
+ "[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
1518
  numContainers
1026
1519
  );
1027
1520
  }
@@ -1031,19 +1524,19 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1031
1524
  if (numContainers !== prevNumContainers) {
1032
1525
  set$(ctx, "numContainers", numContainers);
1033
1526
  if (numContainers > peek$(ctx, "numContainersPooled")) {
1034
- set$(ctx, "numContainersPooled", numContainers);
1527
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
1035
1528
  }
1036
1529
  }
1037
1530
  for (let i = 0; i < numContainers; i++) {
1038
1531
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1039
1532
  const itemIndex = state.indexByKey.get(itemKey);
1040
- const item = data2[itemIndex];
1041
- if (item) {
1533
+ const item = data[itemIndex];
1534
+ if (item !== void 0) {
1042
1535
  const id = getId(itemIndex);
1043
1536
  if (itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered) {
1044
- const prevPos = peek$(ctx, `containerPosition${i}`).top;
1537
+ const prevPos = peek$(ctx, `containerPosition${i}`);
1045
1538
  const pos = positions.get(id) || 0;
1046
- const size = getItemSize(id, itemIndex, data2[i]);
1539
+ const size = getItemSize(id, itemIndex, data[i]);
1047
1540
  if (pos + size >= scroll && pos <= scrollBottom || prevPos + size >= scroll && prevPos <= scrollBottom) {
1048
1541
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1049
1542
  }
@@ -1055,9 +1548,9 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1055
1548
  };
1056
1549
  const column2 = columns.get(id) || 1;
1057
1550
  if (maintainVisibleContentPosition && itemIndex < anchorElementIndex) {
1058
- const currentRow = Math.floor(itemIndex / numColumnsProp);
1551
+ const currentRow = Math.floor(itemIndex / numColumns);
1059
1552
  const rowHeight = getRowHeight(currentRow);
1060
- const elementHeight = getItemSize(id, itemIndex, data2[i]);
1553
+ const elementHeight = getItemSize(id, itemIndex, data[i]);
1061
1554
  const diff = rowHeight - elementHeight;
1062
1555
  pos.relativeCoordinate = pos.top + getRowHeight(currentRow) - diff;
1063
1556
  pos.type = "bottom";
@@ -1072,13 +1565,25 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1072
1565
  set$(ctx, `containerColumn${i}`, column2);
1073
1566
  }
1074
1567
  if (prevData !== item) {
1075
- set$(ctx, `containerItemData${i}`, data2[itemIndex]);
1568
+ set$(ctx, `containerItemData${i}`, data[itemIndex]);
1076
1569
  }
1077
1570
  }
1078
1571
  }
1079
1572
  }
1080
1573
  }
1081
- set$(ctx, "containersDidLayout", true);
1574
+ if (state.numPendingInitialLayout === 0) {
1575
+ state.numPendingInitialLayout = state.endBuffered - state.startBuffered + 1;
1576
+ }
1577
+ if (!state.queuedInitialLayout && endBuffered !== null) {
1578
+ let areAllKnown = true;
1579
+ for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1580
+ const key = getId(i);
1581
+ areAllKnown && (areAllKnown = state.sizesKnown.has(key));
1582
+ }
1583
+ if (areAllKnown) {
1584
+ setDidLayout();
1585
+ }
1586
+ }
1082
1587
  if (state.viewabilityConfigCallbackPairs) {
1083
1588
  updateViewableItems(
1084
1589
  state,
@@ -1091,50 +1596,113 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1091
1596
  );
1092
1597
  }
1093
1598
  }, []);
1094
- const doUpdatePaddingTop = () => {
1599
+ const setPaddingTop = ({
1600
+ stylePaddingTop,
1601
+ alignItemsPaddingTop
1602
+ }) => {
1603
+ if (stylePaddingTop !== void 0) {
1604
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1605
+ if (stylePaddingTop < prevStylePaddingTop) {
1606
+ const prevTotalSize = peek$(ctx, "totalSizeWithScrollAdjust") || 0;
1607
+ set$(ctx, "totalSizeWithScrollAdjust", prevTotalSize + prevStylePaddingTop);
1608
+ setTimeout(() => {
1609
+ set$(ctx, "totalSizeWithScrollAdjust", prevTotalSize);
1610
+ }, 16);
1611
+ }
1612
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1613
+ }
1614
+ if (alignItemsPaddingTop !== void 0) {
1615
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1616
+ }
1617
+ set$(
1618
+ ctx,
1619
+ "paddingTop",
1620
+ (stylePaddingTop != null ? stylePaddingTop : peek$(ctx, "stylePaddingTop")) + (alignItemsPaddingTop != null ? alignItemsPaddingTop : peek$(ctx, "alignItemsPaddingTop"))
1621
+ );
1622
+ };
1623
+ const updateAlignItemsPaddingTop = () => {
1095
1624
  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);
1625
+ const { scrollLength } = refState.current;
1626
+ const contentSize = getContentSize(ctx);
1627
+ const paddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1628
+ setPaddingTop({ alignItemsPaddingTop: paddingTop });
1100
1629
  }
1101
1630
  };
1631
+ const scrollTo = (offset, animated) => {
1632
+ var _a;
1633
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1634
+ x: horizontal ? offset : 0,
1635
+ y: horizontal ? 0 : offset,
1636
+ animated: !!animated
1637
+ });
1638
+ };
1102
1639
  const doMaintainScrollAtEnd = (animated) => {
1103
1640
  const state = refState.current;
1104
- if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd) {
1105
- state.scroll = state.totalSize - state.scrollLength + peek$(ctx, "paddingTop");
1641
+ if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
1642
+ const paddingTop = peek$(ctx, "alignItemsPaddingTop");
1643
+ if (paddingTop > 0) {
1644
+ state.scroll = 0;
1645
+ }
1106
1646
  requestAnimationFrame(() => {
1107
- var _a2;
1108
- (_a2 = refScroller.current) == null ? void 0 : _a2.scrollToEnd({
1647
+ var _a;
1648
+ state.maintainingScrollAtEnd = true;
1649
+ (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
1109
1650
  animated
1110
1651
  });
1652
+ setTimeout(
1653
+ () => {
1654
+ state.maintainingScrollAtEnd = false;
1655
+ },
1656
+ 0
1657
+ );
1111
1658
  });
1112
1659
  return true;
1113
1660
  }
1114
1661
  };
1662
+ const checkThreshold = (distance, atThreshold, threshold, isReached, isBlockedByTimer, onReached, blockTimer) => {
1663
+ const distanceAbs = Math.abs(distance);
1664
+ const isAtThreshold = atThreshold || distanceAbs < threshold;
1665
+ if (!isReached && !isBlockedByTimer) {
1666
+ if (isAtThreshold) {
1667
+ onReached == null ? void 0 : onReached(distance);
1668
+ blockTimer == null ? void 0 : blockTimer(true);
1669
+ setTimeout(() => {
1670
+ blockTimer == null ? void 0 : blockTimer(false);
1671
+ }, 700);
1672
+ return true;
1673
+ }
1674
+ } else {
1675
+ if (distance >= 1.3 * threshold) {
1676
+ return false;
1677
+ }
1678
+ }
1679
+ return isReached;
1680
+ };
1115
1681
  const checkAtBottom = () => {
1116
1682
  if (!refState.current) {
1117
1683
  return;
1118
1684
  }
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
- }
1685
+ const { queuedInitialLayout, scrollLength, scroll, maintainingScrollAtEnd } = refState.current;
1686
+ const contentSize = getContentSize(ctx);
1687
+ if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1688
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1689
+ const distanceFromEndAbs = Math.abs(distanceFromEnd);
1690
+ const isContentLess = contentSize < scrollLength;
1691
+ refState.current.isAtBottom = isContentLess || distanceFromEndAbs < scrollLength * maintainScrollAtEndThreshold;
1692
+ refState.current.isEndReached = checkThreshold(
1693
+ distanceFromEnd,
1694
+ isContentLess,
1695
+ onEndReachedThreshold * scrollLength,
1696
+ refState.current.isEndReached,
1697
+ refState.current.endReachedBlockedByTimer,
1698
+ (distance) => {
1699
+ var _a, _b;
1700
+ return (_b = (_a = callbacks.current).onEndReached) == null ? void 0 : _b.call(_a, { distanceFromEnd: distance });
1701
+ },
1702
+ (block) => {
1703
+ refState.current.endReachedBlockedByTimer = block;
1136
1704
  }
1137
- }
1705
+ );
1138
1706
  }
1139
1707
  };
1140
1708
  const checkAtTop = () => {
@@ -1143,37 +1711,35 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1143
1711
  }
1144
1712
  const { scrollLength, scroll } = refState.current;
1145
1713
  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
- }
1714
+ const distanceFromTopAbs = Math.abs(distanceFromTop);
1715
+ refState.current.isAtTop = distanceFromTopAbs < 0;
1716
+ refState.current.isStartReached = checkThreshold(
1717
+ distanceFromTop,
1718
+ false,
1719
+ onStartReachedThreshold * scrollLength,
1720
+ refState.current.isStartReached,
1721
+ refState.current.startReachedBlockedByTimer,
1722
+ (distance) => {
1723
+ var _a, _b;
1724
+ return (_b = (_a = callbacks.current).onStartReached) == null ? void 0 : _b.call(_a, { distanceFromStart: distance });
1725
+ },
1726
+ (block) => {
1727
+ refState.current.startReachedBlockedByTimer = block;
1162
1728
  }
1163
- }
1729
+ );
1164
1730
  };
1165
- const checkResetContainers = (reset) => {
1731
+ const checkResetContainers = (isFirst2) => {
1166
1732
  const state = refState.current;
1167
1733
  if (state) {
1168
- state.data = data;
1169
- if (reset) {
1734
+ state.data = dataProp;
1735
+ if (!isFirst2) {
1170
1736
  refState.current.scrollForNextCalculateItemsInView = void 0;
1171
1737
  const numContainers = peek$(ctx, "numContainers");
1172
1738
  for (let i = 0; i < numContainers; i++) {
1173
1739
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1174
1740
  if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
1175
- set$(ctx, `containerItemKey${i}`, void 0);
1176
- set$(ctx, `containerItemData${i}`, void 0);
1741
+ set$(ctx, `containerItemKey${i}`, null);
1742
+ set$(ctx, `containerItemData${i}`, null);
1177
1743
  set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1178
1744
  set$(ctx, `containerColumn${i}`, -1);
1179
1745
  }
@@ -1181,29 +1747,32 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1181
1747
  if (!keyExtractorProp) {
1182
1748
  state.positions.clear();
1183
1749
  }
1184
- calculateItemsInView(state.scrollVelocity);
1185
- }
1186
- const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1187
- if (!didMaintainScrollAtEnd && data.length > state.data.length) {
1188
- state.isEndReached = false;
1750
+ calculateItemsInView();
1751
+ const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1752
+ if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1753
+ state.isEndReached = false;
1754
+ }
1755
+ if (!didMaintainScrollAtEnd) {
1756
+ checkAtTop();
1757
+ checkAtBottom();
1758
+ }
1189
1759
  }
1190
- checkAtTop();
1191
- checkAtBottom();
1192
1760
  }
1193
1761
  };
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;
1762
+ const calcTotalSizesAndPositions = ({ forgetPositions = false }) => {
1763
+ var _a, _b;
1764
+ const state = refState.current;
1200
1765
  let totalSize = 0;
1201
1766
  let totalSizeBelowIndex = 0;
1202
1767
  const indexByKey = /* @__PURE__ */ new Map();
1203
1768
  const newPositions = /* @__PURE__ */ new Map();
1204
1769
  let column = 1;
1205
1770
  let maxSizeInRow = 0;
1206
- for (let i = 0; i < data.length; i++) {
1771
+ const numColumns = (_a = peek$(ctx, "numColumns")) != null ? _a : numColumnsProp;
1772
+ if (!state) {
1773
+ return;
1774
+ }
1775
+ for (let i = 0; i < dataProp.length; i++) {
1207
1776
  const key = getId(i);
1208
1777
  if (__DEV__) {
1209
1778
  if (indexByKey.has(key)) {
@@ -1213,51 +1782,51 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1213
1782
  }
1214
1783
  }
1215
1784
  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));
1785
+ if (!forgetPositions && state.positions.get(key) != null && state.indexByKey.get(key) === i) {
1786
+ newPositions.set(key, state.positions.get(key));
1218
1787
  }
1219
1788
  }
1220
- refState.current.indexByKey = indexByKey;
1221
- refState.current.positions = newPositions;
1222
- if (!isFirst) {
1789
+ state.indexByKey = indexByKey;
1790
+ state.positions = newPositions;
1791
+ if (!forgetPositions && !isFirst) {
1223
1792
  if (maintainVisibleContentPosition) {
1224
- if (refState.current.anchorElement == null || indexByKey.get(refState.current.anchorElement.id) == null) {
1225
- if (data.length) {
1793
+ if (state.anchorElement == null || indexByKey.get(state.anchorElement.id) == null) {
1794
+ if (dataProp.length) {
1226
1795
  const newAnchorElement = {
1227
1796
  coordinate: 0,
1228
1797
  id: getId(0)
1229
1798
  };
1230
- refState.current.anchorElement = newAnchorElement;
1231
- (_a = refState.current.belowAnchorElementPositions) == null ? void 0 : _a.clear();
1232
- (_b = refScroller.current) == null ? void 0 : _b.scrollTo({ x: 0, y: 0, animated: false });
1799
+ state.anchorElement = newAnchorElement;
1800
+ (_b = state.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1801
+ scrollTo(0, false);
1233
1802
  setTimeout(() => {
1234
- calculateItemsInView(0);
1803
+ calculateItemsInView();
1235
1804
  }, 0);
1236
1805
  } else {
1237
- refState.current.startBufferedId = void 0;
1806
+ state.startBufferedId = void 0;
1238
1807
  }
1239
1808
  }
1240
1809
  } else {
1241
- if (refState.current.startBufferedId != null && newPositions.get(refState.current.startBufferedId) == null) {
1242
- if (data.length) {
1243
- refState.current.startBufferedId = getId(0);
1810
+ if (state.startBufferedId != null && newPositions.get(state.startBufferedId) == null) {
1811
+ if (dataProp.length) {
1812
+ state.startBufferedId = getId(0);
1244
1813
  } else {
1245
- refState.current.startBufferedId = void 0;
1814
+ state.startBufferedId = void 0;
1246
1815
  }
1247
- (_c = refScroller.current) == null ? void 0 : _c.scrollTo({ x: 0, y: 0, animated: false });
1816
+ scrollTo(0, false);
1248
1817
  setTimeout(() => {
1249
- calculateItemsInView(0);
1818
+ calculateItemsInView();
1250
1819
  }, 0);
1251
1820
  }
1252
1821
  }
1253
1822
  }
1254
1823
  const anchorElementIndex = getAnchorElementIndex();
1255
- for (let i = 0; i < data.length; i++) {
1824
+ for (let i = 0; i < dataProp.length; i++) {
1256
1825
  const key = getId(i);
1257
- const size = getItemSize(key, i, data[i]);
1826
+ const size = getItemSize(key, i, dataProp[i]);
1258
1827
  maxSizeInRow = Math.max(maxSizeInRow, size);
1259
1828
  column++;
1260
- if (column > numColumnsProp) {
1829
+ if (column > numColumns) {
1261
1830
  if (maintainVisibleContentPosition && anchorElementIndex !== void 0 && i < anchorElementIndex) {
1262
1831
  totalSizeBelowIndex += maxSizeInRow;
1263
1832
  }
@@ -1269,120 +1838,168 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1269
1838
  if (maxSizeInRow > 0) {
1270
1839
  totalSize += maxSizeInRow;
1271
1840
  }
1841
+ state.ignoreScrollFromCalcTotal = true;
1842
+ requestAnimationFrame(() => {
1843
+ state.ignoreScrollFromCalcTotal = false;
1844
+ });
1272
1845
  addTotalSize(null, totalSize, totalSizeBelowIndex);
1273
- }
1274
- React5.useEffect(() => {
1275
- checkResetContainers(
1276
- /*reset*/
1277
- !isFirst
1846
+ };
1847
+ const isFirst = !refState.current.renderItem;
1848
+ const memoizedLastItemKeys = React2.useMemo(() => {
1849
+ if (!dataProp.length) return [];
1850
+ return Array.from(
1851
+ { length: Math.min(numColumnsProp, dataProp.length) },
1852
+ (_, i) => getId(dataProp.length - 1 - i)
1278
1853
  );
1279
- }, [isFirst, data, numColumnsProp]);
1280
- React5.useEffect(() => {
1281
- set$(ctx, "extraData", extraData);
1282
- }, [extraData]);
1283
- refState.current.renderItem = renderItem;
1284
- const lastItemKey = data.length > 0 ? getId(data.length - 1) : void 0;
1285
- const stylePaddingTop = (_g = (_f = (_d = reactNative.StyleSheet.flatten(style)) == null ? void 0 : _d.paddingTop) != null ? _f : (_e = reactNative.StyleSheet.flatten(contentContainerStyle)) == null ? void 0 : _e.paddingTop) != null ? _g : 0;
1854
+ }, [dataProp, numColumnsProp]);
1286
1855
  const initalizeStateVars = () => {
1287
- set$(ctx, "lastItemKey", lastItemKey);
1856
+ set$(ctx, "lastItemKeys", memoizedLastItemKeys);
1288
1857
  set$(ctx, "numColumns", numColumnsProp);
1289
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1858
+ const prevPaddingTop = peek$(ctx, "stylePaddingTop");
1859
+ setPaddingTop({ stylePaddingTop: stylePaddingTopState });
1860
+ const paddingDiff = stylePaddingTopState - prevPaddingTop;
1861
+ if (paddingDiff && prevPaddingTop !== void 0 && reactNative.Platform.OS === "ios") {
1862
+ queueMicrotask(() => {
1863
+ scrollTo(refState.current.scroll + paddingDiff, false);
1864
+ });
1865
+ }
1290
1866
  };
1291
1867
  if (isFirst) {
1292
1868
  initalizeStateVars();
1293
1869
  }
1294
- React5.useEffect(initalizeStateVars, [lastItemKey, numColumnsProp, stylePaddingTop]);
1295
- const getRenderedItem = React5.useCallback((key) => {
1296
- var _a2, _b2;
1870
+ if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
1871
+ refState.current.lastBatchingAction = Date.now();
1872
+ if (!keyExtractorProp && !isFirst && didDataChange) {
1873
+ __DEV__ && warnDevOnce(
1874
+ "keyExtractor",
1875
+ "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."
1876
+ );
1877
+ refState.current.sizes.clear();
1878
+ refState.current.positions.clear();
1879
+ }
1880
+ calcTotalSizesAndPositions({ forgetPositions: false });
1881
+ }
1882
+ React2.useEffect(() => {
1883
+ const didAllocateContainers = doInitialAllocateContainers();
1884
+ if (!didAllocateContainers) {
1885
+ checkResetContainers(
1886
+ /*isFirst*/
1887
+ isFirst
1888
+ );
1889
+ }
1890
+ }, [isFirst, dataProp, numColumnsProp]);
1891
+ React2.useEffect(() => {
1892
+ set$(ctx, "extraData", extraData);
1893
+ }, [extraData]);
1894
+ refState.current.renderItem = renderItem;
1895
+ React2.useEffect(initalizeStateVars, [memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingTopState]);
1896
+ const getRenderedItem = React2.useCallback((key) => {
1897
+ var _a, _b;
1297
1898
  const state = refState.current;
1298
1899
  if (!state) {
1299
1900
  return null;
1300
1901
  }
1301
- const { data: data2, indexByKey } = state;
1902
+ const { data, indexByKey } = state;
1302
1903
  const index = indexByKey.get(key);
1303
1904
  if (index === void 0) {
1304
1905
  return null;
1305
1906
  }
1306
- const useViewability2 = (configId, callback) => {
1307
- useViewability(configId, callback);
1308
- };
1309
- const useViewabilityAmount2 = (callback) => {
1310
- useViewabilityAmount(callback);
1311
- };
1312
- const useRecyclingEffect2 = (effect) => {
1313
- useRecyclingEffect(effect);
1314
- };
1315
- const useRecyclingState2 = (valueOrFun) => {
1316
- return useRecyclingState(valueOrFun);
1317
- };
1318
- const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
1319
- item: data2[index],
1907
+ const renderedItem = (_b = (_a = refState.current).renderItem) == null ? void 0 : _b.call(_a, {
1908
+ item: data[index],
1320
1909
  index,
1321
- useViewability: useViewability2,
1322
- useViewabilityAmount: useViewabilityAmount2,
1323
- useRecyclingEffect: useRecyclingEffect2,
1324
- useRecyclingState: useRecyclingState2
1910
+ extraData: peek$(ctx, "extraData")
1325
1911
  });
1326
- return { index, renderedItem };
1912
+ return { index, item: data[index], renderedItem };
1327
1913
  }, []);
1328
- useInit(() => {
1329
- var _a2;
1914
+ const doInitialAllocateContainers = () => {
1915
+ var _a;
1916
+ const state = refState.current;
1917
+ const { scrollLength, data } = state;
1918
+ if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
1919
+ const averageItemSize = (_a = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a : DEFAULT_ITEM_SIZE;
1920
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1921
+ for (let i = 0; i < numContainers; i++) {
1922
+ set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1923
+ set$(ctx, `containerColumn${i}`, -1);
1924
+ }
1925
+ set$(ctx, "numContainers", numContainers);
1926
+ set$(ctx, "numContainersPooled", numContainers * initialContainerPoolRatio);
1927
+ if (initialScrollIndex) {
1928
+ requestAnimationFrame(() => {
1929
+ calculateItemsInView();
1930
+ });
1931
+ } else {
1932
+ calculateItemsInView();
1933
+ }
1934
+ return true;
1935
+ }
1936
+ };
1937
+ React2.useEffect(() => {
1330
1938
  const state = refState.current;
1331
- const viewability = setupViewability(props);
1939
+ const viewability = setupViewability({
1940
+ viewabilityConfig,
1941
+ viewabilityConfigCallbackPairs,
1942
+ onViewableItemsChanged
1943
+ });
1332
1944
  state.viewabilityConfigCallbackPairs = viewability;
1333
1945
  state.enableScrollForNextCalculateItemsInView = !viewability;
1334
- const scrollLength = state.scrollLength;
1335
- const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
1336
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
1337
- for (let i = 0; i < numContainers; i++) {
1338
- set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1339
- set$(ctx, `containerColumn${i}`, -1);
1340
- }
1341
- set$(ctx, "numContainers", numContainers);
1342
- set$(ctx, "numContainersPooled", numContainers * 2);
1343
- calculateItemsInView(state.scrollVelocity);
1946
+ }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
1947
+ useInit(() => {
1948
+ doInitialAllocateContainers();
1344
1949
  });
1345
- const updateItemSize = React5.useCallback((containerId, itemKey, size) => {
1950
+ const updateItemSize = React2.useCallback((itemKey, size, fromFixGaps) => {
1346
1951
  const state = refState.current;
1347
- const { sizes, indexByKey, columns, sizesLaidOut, data: data2 } = state;
1348
- if (!data2) {
1952
+ const { sizes, indexByKey, sizesKnown, data, rowHeights, startBuffered, endBuffered, averageSizes } = state;
1953
+ if (!data) {
1349
1954
  return;
1350
1955
  }
1351
1956
  const index = indexByKey.get(itemKey);
1352
1957
  const numColumns = peek$(ctx, "numColumns");
1353
1958
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
1354
- const prevSize = getItemSize(itemKey, index, data2);
1959
+ const prevSize = getItemSize(itemKey, index, data);
1960
+ let needsCalculate = false;
1961
+ let needsUpdateContainersDidLayout = false;
1962
+ if (state.numPendingInitialLayout > 0) {
1963
+ state.numPendingInitialLayout--;
1964
+ if (state.numPendingInitialLayout === 0) {
1965
+ needsCalculate = true;
1966
+ state.numPendingInitialLayout = -1;
1967
+ needsUpdateContainersDidLayout = true;
1968
+ }
1969
+ }
1970
+ sizesKnown.set(itemKey, size);
1971
+ const itemType = "";
1972
+ let averages = averageSizes[itemType];
1973
+ if (!averages) {
1974
+ averages = averageSizes[itemType] = {
1975
+ num: 0,
1976
+ avg: 0
1977
+ };
1978
+ }
1979
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
1980
+ averages.num++;
1355
1981
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
1356
1982
  let diff;
1983
+ needsCalculate = true;
1357
1984
  if (numColumns > 1) {
1985
+ const rowNumber = Math.floor(index / numColumnsProp);
1986
+ const prevSizeInRow = getRowHeight(rowNumber);
1358
1987
  sizes.set(itemKey, size);
1359
- const column = columns.get(itemKey);
1360
- const loopStart = index - (column - 1);
1361
- let nextMaxSizeInRow = 0;
1362
- for (let i = loopStart; i < loopStart + numColumns && i < data2.length; i++) {
1363
- const id = getId(i);
1364
- const size2 = getItemSize(id, i, data2[i]);
1365
- nextMaxSizeInRow = Math.max(nextMaxSizeInRow, size2);
1366
- }
1367
- diff = nextMaxSizeInRow - prevSize;
1988
+ rowHeights.delete(rowNumber);
1989
+ const sizeInRow = getRowHeight(rowNumber);
1990
+ diff = sizeInRow - prevSizeInRow;
1368
1991
  } else {
1369
1992
  sizes.set(itemKey, size);
1370
1993
  diff = size - prevSize;
1371
1994
  }
1372
- if (__DEV__ && !estimatedItemSize && !getEstimatedItemSize) {
1373
- sizesLaidOut.set(itemKey, size);
1995
+ if (__DEV__ && suggestEstimatedItemSize) {
1374
1996
  if (state.timeoutSizeMessage) {
1375
1997
  clearTimeout(state.timeoutSizeMessage);
1376
1998
  }
1377
1999
  state.timeoutSizeMessage = setTimeout(() => {
1378
2000
  state.timeoutSizeMessage = void 0;
1379
- let total = 0;
1380
- let num = 0;
1381
- for (const [key, size2] of sizesLaidOut) {
1382
- num++;
1383
- total += size2;
1384
- }
1385
- const avg = Math.round(total / num);
2001
+ const num = sizesKnown.size;
2002
+ const avg = state.averageSizes[""].avg;
1386
2003
  console.warn(
1387
2004
  `[legend-list] estimatedItemSize or getEstimatedItemSize are not defined. Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
1388
2005
  );
@@ -1390,47 +2007,60 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1390
2007
  }
1391
2008
  refState.current.scrollForNextCalculateItemsInView = void 0;
1392
2009
  addTotalSize(itemKey, diff, 0);
1393
- doMaintainScrollAtEnd(true);
2010
+ doMaintainScrollAtEnd(false);
2011
+ if (onItemSizeChanged) {
2012
+ onItemSizeChanged({
2013
+ size,
2014
+ previous: prevSize,
2015
+ index,
2016
+ itemKey,
2017
+ itemData: data[index]
2018
+ });
2019
+ }
2020
+ }
2021
+ const isInView = index >= startBuffered && index <= endBuffered;
2022
+ if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && isInView) {
1394
2023
  const scrollVelocity = state.scrollVelocity;
1395
- if (!state.waitingForMicrotask && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
1396
- if (!peek$(ctx, "containersDidLayout")) {
1397
- state.waitingForMicrotask = true;
1398
- queueMicrotask(() => {
1399
- if (state.waitingForMicrotask) {
1400
- state.waitingForMicrotask = false;
1401
- calculateItemsInView(state.scrollVelocity);
1402
- }
1403
- });
2024
+ let didCalculate = false;
2025
+ if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1) && (!waitForInitialLayout || state.numPendingInitialLayout < 0)) {
2026
+ if (Date.now() - state.lastBatchingAction < 500) {
2027
+ if (!state.queuedCalculateItemsInView) {
2028
+ state.queuedCalculateItemsInView = requestAnimationFrame(() => {
2029
+ state.queuedCalculateItemsInView = void 0;
2030
+ calculateItemsInView();
2031
+ });
2032
+ }
1404
2033
  } else {
1405
- calculateItemsInView(state.scrollVelocity);
2034
+ calculateItemsInView();
2035
+ didCalculate = true;
1406
2036
  }
1407
2037
  }
1408
- if (onItemSizeChanged) {
1409
- onItemSizeChanged({ size, previous: prevSize, index, itemKey, itemData: data2[index] });
2038
+ if (!didCalculate && IsNewArchitecture) {
2039
+ fixGaps();
1410
2040
  }
1411
2041
  }
1412
2042
  }, []);
1413
- const handleScrollDebounced = React5.useCallback((velocity) => {
1414
- calculateItemsInView(velocity);
1415
- checkAtBottom();
1416
- checkAtTop();
1417
- }, []);
1418
- const onLayout = React5.useCallback((event) => {
2043
+ const onLayout = React2.useCallback((event) => {
2044
+ const state = refState.current;
1419
2045
  const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
1420
- const didChange = scrollLength !== refState.current.scrollLength;
1421
- refState.current.scrollLength = scrollLength;
2046
+ const didChange = scrollLength !== state.scrollLength;
2047
+ state.scrollLength = scrollLength;
2048
+ state.lastBatchingAction = Date.now();
2049
+ state.scrollForNextCalculateItemsInView = void 0;
2050
+ doInitialAllocateContainers();
1422
2051
  doMaintainScrollAtEnd(false);
1423
- doUpdatePaddingTop();
2052
+ updateAlignItemsPaddingTop();
1424
2053
  checkAtBottom();
1425
2054
  checkAtTop();
1426
2055
  if (didChange) {
1427
- calculateItemsInView(0);
2056
+ calculateItemsInView();
1428
2057
  }
1429
2058
  if (__DEV__) {
1430
2059
  const isWidthZero = event.nativeEvent.layout.width === 0;
1431
2060
  const isHeightZero = event.nativeEvent.layout.height === 0;
1432
2061
  if (isWidthZero || isHeightZero) {
1433
- console.warn(
2062
+ warnDevOnce(
2063
+ "height0",
1434
2064
  `[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.`
1435
2065
  );
1436
2066
  }
@@ -1439,16 +2069,20 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1439
2069
  onLayoutProp(event);
1440
2070
  }
1441
2071
  }, []);
1442
- const handleScroll = React5.useCallback(
2072
+ const handleScroll = React2.useCallback(
1443
2073
  (event, fromSelf) => {
1444
- var _a2, _b2, _c2;
1445
- 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) {
2074
+ var _a, _b, _c, _d;
2075
+ 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) {
1446
2076
  return;
1447
2077
  }
1448
2078
  const state = refState.current;
2079
+ const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
2080
+ if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
2081
+ return;
2082
+ }
1449
2083
  state.hasScrolled = true;
2084
+ state.lastBatchingAction = Date.now();
1450
2085
  const currentTime = performance.now();
1451
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
1452
2086
  if (!(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
1453
2087
  state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1454
2088
  }
@@ -1474,71 +2108,139 @@ var LegendListInner = React5.forwardRef(function LegendListInner2(props, forward
1474
2108
  state.scroll = newScroll;
1475
2109
  state.scrollTime = currentTime;
1476
2110
  state.scrollVelocity = velocity;
1477
- handleScrollDebounced(velocity);
2111
+ calculateItemsInView();
2112
+ checkAtBottom();
2113
+ checkAtTop();
1478
2114
  if (!fromSelf) {
1479
- onScrollProp == null ? void 0 : onScrollProp(event);
2115
+ (_d = state.onScroll) == null ? void 0 : _d.call(state, event);
1480
2116
  }
1481
2117
  },
1482
2118
  []
1483
2119
  );
1484
- React5.useImperativeHandle(
2120
+ React2.useImperativeHandle(
1485
2121
  forwardedRef,
1486
2122
  () => {
1487
- const scrollToIndex = ({ index, animated }) => {
1488
- const offsetObj = calculateInitialOffset(index);
1489
- const offset = horizontal ? { x: offsetObj, y: 0 } : { x: 0, y: offsetObj };
1490
- refScroller.current.scrollTo({ ...offset, animated });
2123
+ const scrollIndexIntoView = (options) => {
2124
+ if (refState.current) {
2125
+ const { index, ...rest2 } = options;
2126
+ const { startNoBuffer, endNoBuffer } = refState.current;
2127
+ if (index < startNoBuffer || index > endNoBuffer) {
2128
+ const viewPosition = index < startNoBuffer ? 0 : 1;
2129
+ scrollToIndex({
2130
+ ...rest2,
2131
+ viewPosition,
2132
+ index
2133
+ });
2134
+ }
2135
+ }
1491
2136
  };
1492
2137
  return {
2138
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
1493
2139
  getNativeScrollRef: () => refScroller.current,
1494
2140
  getScrollableNode: () => refScroller.current.getScrollableNode(),
1495
2141
  getScrollResponder: () => refScroller.current.getScrollResponder(),
1496
- flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
1497
- scrollToIndex,
1498
- scrollToOffset: ({ offset, animated }) => {
1499
- const offsetObj = horizontal ? { x: offset, y: 0 } : { x: 0, y: offset };
1500
- refScroller.current.scrollTo({ ...offsetObj, animated });
2142
+ getState: () => {
2143
+ const state = refState.current;
2144
+ return state ? {
2145
+ contentLength: state.totalSize,
2146
+ end: state.endNoBuffer,
2147
+ endBuffered: state.endBuffered,
2148
+ isAtEnd: state.isAtBottom,
2149
+ isAtStart: state.isAtTop,
2150
+ scroll: state.scroll,
2151
+ scrollLength: state.scrollLength,
2152
+ start: state.startNoBuffer,
2153
+ startBuffered: state.startBuffered
2154
+ } : {};
2155
+ },
2156
+ scrollIndexIntoView,
2157
+ scrollItemIntoView: ({ item, ...props2 }) => {
2158
+ const { data } = refState.current;
2159
+ const index = data.indexOf(item);
2160
+ if (index !== -1) {
2161
+ scrollIndexIntoView({ index, ...props2 });
2162
+ }
1501
2163
  },
1502
- scrollToItem: ({ item, animated }) => {
2164
+ scrollToIndex,
2165
+ scrollToItem: ({ item, ...props2 }) => {
2166
+ const { data } = refState.current;
1503
2167
  const index = data.indexOf(item);
1504
2168
  if (index !== -1) {
1505
- scrollToIndex({ index, animated });
2169
+ scrollToIndex({ index, ...props2 });
1506
2170
  }
1507
2171
  },
1508
- scrollToEnd: () => refScroller.current.scrollToEnd()
2172
+ scrollToOffset: ({ offset, animated }) => {
2173
+ scrollTo(offset, animated);
2174
+ },
2175
+ scrollToEnd: (options) => refScroller.current.scrollToEnd(options)
1509
2176
  };
1510
2177
  },
1511
2178
  []
1512
2179
  );
1513
- return /* @__PURE__ */ React5__namespace.createElement(
2180
+ if (reactNative.Platform.OS === "web") {
2181
+ React2.useEffect(() => {
2182
+ var _a;
2183
+ if (initialContentOffset) {
2184
+ (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler.setDisableAdjust(true);
2185
+ scrollTo(initialContentOffset, false);
2186
+ setTimeout(() => {
2187
+ var _a2;
2188
+ (_a2 = refState.current) == null ? void 0 : _a2.scrollAdjustHandler.setDisableAdjust(false);
2189
+ }, 0);
2190
+ }
2191
+ }, []);
2192
+ }
2193
+ return /* @__PURE__ */ React2__namespace.createElement(React2__namespace.Fragment, null, /* @__PURE__ */ React2__namespace.createElement(
1514
2194
  ListComponent,
1515
2195
  {
1516
2196
  ...rest,
1517
2197
  horizontal,
1518
- refScrollView: (r) => {
1519
- refScroller.current = r;
1520
- if (refScrollView) {
1521
- if (typeof refScrollView === "function") {
1522
- refScrollView(r);
1523
- } else {
1524
- refScrollView.current = r;
1525
- }
1526
- }
1527
- },
2198
+ refScrollView: combinedRef,
1528
2199
  initialContentOffset,
1529
2200
  getRenderedItem,
1530
2201
  updateItemSize,
1531
2202
  handleScroll,
2203
+ onMomentumScrollEnd: (event) => {
2204
+ var _a;
2205
+ const scrollingToOffset = (_a = refState.current) == null ? void 0 : _a.scrollingToOffset;
2206
+ if (scrollingToOffset !== void 0) {
2207
+ requestAnimationFrame(() => {
2208
+ scrollTo(scrollingToOffset, false);
2209
+ refState.current.scrollingToOffset = void 0;
2210
+ requestAnimationFrame(() => {
2211
+ refState.current.scrollAdjustHandler.setDisableAdjust(false);
2212
+ });
2213
+ });
2214
+ }
2215
+ const wasPaused = refState.current.scrollAdjustHandler.unPauseAdjust();
2216
+ if (wasPaused) {
2217
+ refState.current.scrollVelocity = 0;
2218
+ refState.current.scrollHistory = [];
2219
+ calculateItemsInView();
2220
+ }
2221
+ if (onMomentumScrollEnd) {
2222
+ onMomentumScrollEnd(event);
2223
+ }
2224
+ },
1532
2225
  onLayout,
1533
2226
  recycleItems,
1534
2227
  alignItemsAtEnd,
1535
- ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
2228
+ ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
1536
2229
  maintainVisibleContentPosition,
1537
- scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : reactNative.Platform.OS === "web" ? 16 : void 0,
2230
+ scrollEventThrottle: reactNative.Platform.OS === "web" ? 16 : void 0,
1538
2231
  waitForInitialLayout,
1539
- style
2232
+ refreshControl: refreshControl != null ? refreshControl : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
2233
+ reactNative.RefreshControl,
2234
+ {
2235
+ refreshing: !!refreshing,
2236
+ onRefresh,
2237
+ progressViewOffset
2238
+ }
2239
+ ),
2240
+ style,
2241
+ contentContainerStyle
1540
2242
  }
1541
- );
2243
+ ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React2__namespace.createElement(DebugView, { state: refState.current }));
1542
2244
  });
1543
2245
 
1544
2246
  exports.LegendList = LegendList;