@legendapp/list 1.1.4 → 2.0.0-beta.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.mjs CHANGED
@@ -1,47 +1,38 @@
1
- import * as React2 from 'react';
2
- import React2__default, { useReducer, useEffect, createContext, useMemo, useRef, useCallback, useLayoutEffect, useImperativeHandle, useContext, useState, forwardRef, memo } from 'react';
3
- import { View, Text, Platform, Animated, ScrollView, StyleSheet, Dimensions, RefreshControl } from 'react-native';
1
+ import * as React3 from 'react';
2
+ import React3__default, { useReducer, useEffect, createContext, useRef, useState, useMemo, useLayoutEffect, useCallback, useImperativeHandle, forwardRef, memo, useContext } from 'react';
3
+ import { View, Text, Platform, Animated, StyleSheet, Dimensions, RefreshControl, unstable_batchedUpdates } from 'react-native';
4
4
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
5
- import { LegendList as LegendList$1 } from '@legendapp/list';
6
5
 
7
- // src/LegendList.tsx
8
- var ContextState = React2.createContext(null);
6
+ // src/components/LegendList.tsx
7
+ var ContextState = React3.createContext(null);
9
8
  function StateProvider({ children }) {
10
- const [value] = React2.useState(() => ({
9
+ const [value] = React3.useState(() => ({
10
+ animatedScrollY: new Animated.Value(0),
11
+ columnWrapperStyle: void 0,
11
12
  listeners: /* @__PURE__ */ new Map(),
13
+ mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
14
+ mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
15
+ mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
16
+ mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
17
+ mapViewabilityValues: /* @__PURE__ */ new Map(),
12
18
  values: /* @__PURE__ */ new Map([
13
- ["paddingTop", 0],
14
19
  ["alignItemsPaddingTop", 0],
15
20
  ["stylePaddingTop", 0],
16
- ["headerSize", 0]
21
+ ["headerSize", 0],
22
+ ["numContainers", 0],
23
+ ["totalSize", 0]
17
24
  ]),
18
- mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
19
- mapViewabilityValues: /* @__PURE__ */ new Map(),
20
- mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
21
- mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
22
- columnWrapperStyle: void 0,
23
25
  viewRefs: /* @__PURE__ */ new Map()
24
26
  }));
25
- return /* @__PURE__ */ React2.createElement(ContextState.Provider, { value }, children);
27
+ return /* @__PURE__ */ React3.createElement(ContextState.Provider, { value }, children);
26
28
  }
27
29
  function useStateContext() {
28
- return React2.useContext(ContextState);
30
+ return React3.useContext(ContextState);
29
31
  }
30
32
  function createSelectorFunctionsArr(ctx, signalNames) {
31
33
  let lastValues = [];
32
34
  let lastSignalValues = [];
33
35
  return {
34
- subscribe: (cb) => {
35
- const listeners = [];
36
- for (const signalName of signalNames) {
37
- listeners.push(listen$(ctx, signalName, cb));
38
- }
39
- return () => {
40
- for (const listener of listeners) {
41
- listener();
42
- }
43
- };
44
- },
45
36
  get: () => {
46
37
  const currentValues = [];
47
38
  let hasChanged = false;
@@ -57,6 +48,17 @@ function createSelectorFunctionsArr(ctx, signalNames) {
57
48
  lastValues = currentValues;
58
49
  }
59
50
  return lastValues;
51
+ },
52
+ subscribe: (cb) => {
53
+ const listeners = [];
54
+ for (const signalName of signalNames) {
55
+ listeners.push(listen$(ctx, signalName, cb));
56
+ }
57
+ return () => {
58
+ for (const listener of listeners) {
59
+ listener();
60
+ }
61
+ };
60
62
  }
61
63
  };
62
64
  }
@@ -91,39 +93,30 @@ function getContentSize(ctx) {
91
93
  const stylePaddingTop = values.get("stylePaddingTop") || 0;
92
94
  const headerSize = values.get("headerSize") || 0;
93
95
  const footerSize = values.get("footerSize") || 0;
94
- const totalSize = values.get("totalSize") || 0;
96
+ const totalSize = values.get("totalSize");
95
97
  return headerSize + footerSize + totalSize + stylePaddingTop;
96
98
  }
97
99
  function useArr$(signalNames) {
98
- const ctx = React2.useContext(ContextState);
99
- const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
100
+ const ctx = React3.useContext(ContextState);
101
+ const { subscribe, get } = React3.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
100
102
  const value = useSyncExternalStore(subscribe, get);
101
103
  return value;
102
104
  }
103
105
  function useSelector$(signalName, selector) {
104
- const ctx = React2.useContext(ContextState);
105
- const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
106
+ const ctx = React3.useContext(ContextState);
107
+ const { subscribe, get } = React3.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
106
108
  const value = useSyncExternalStore(subscribe, () => selector(get()[0]));
107
109
  return value;
108
110
  }
109
111
 
110
- // src/DebugView.tsx
112
+ // src/components/DebugView.tsx
111
113
  var DebugRow = ({ children }) => {
112
- return /* @__PURE__ */ React2.createElement(View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" } }, children);
114
+ return /* @__PURE__ */ React3.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
113
115
  };
114
- var DebugView = React2.memo(function DebugView2({ state }) {
116
+ var DebugView = React3.memo(function DebugView2({ state }) {
115
117
  const ctx = useStateContext();
116
- const [
117
- totalSize = 0,
118
- totalSizeWithScrollAdjust = 0,
119
- scrollAdjust = 0,
120
- rawScroll = 0,
121
- scroll = 0,
122
- numContainers = 0,
123
- numContainersPooled = 0
124
- ] = useArr$([
118
+ const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
125
119
  "totalSize",
126
- "totalSizeWithScrollAdjust",
127
120
  "scrollAdjust",
128
121
  "debugRawScroll",
129
122
  "debugComputedScroll",
@@ -135,31 +128,30 @@ var DebugView = React2.memo(function DebugView2({ state }) {
135
128
  useInterval(() => {
136
129
  forceUpdate();
137
130
  }, 100);
138
- return /* @__PURE__ */ React2.createElement(
131
+ return /* @__PURE__ */ React3.createElement(
139
132
  View,
140
133
  {
134
+ pointerEvents: "none",
141
135
  style: {
142
- position: "absolute",
143
- top: 0,
144
- right: 0,
145
- paddingLeft: 4,
146
- paddingBottom: 4,
147
136
  // height: 100,
148
137
  backgroundColor: "#FFFFFFCC",
138
+ borderRadius: 4,
149
139
  padding: 4,
150
- borderRadius: 4
151
- },
152
- pointerEvents: "none"
140
+ paddingBottom: 4,
141
+ paddingLeft: 4,
142
+ position: "absolute",
143
+ right: 0,
144
+ top: 0
145
+ }
153
146
  },
154
- /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React2.createElement(Text, null, totalSize.toFixed(2))),
155
- /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React2.createElement(Text, null, contentSize.toFixed(2))),
156
- /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "At end:"), /* @__PURE__ */ React2.createElement(Text, null, String(state.isAtEnd))),
157
- /* @__PURE__ */ React2.createElement(Text, null),
158
- /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React2.createElement(Text, null, scrollAdjust.toFixed(2))),
159
- /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "TotalSizeReal: "), /* @__PURE__ */ React2.createElement(Text, null, totalSizeWithScrollAdjust.toFixed(2))),
160
- /* @__PURE__ */ React2.createElement(Text, null),
161
- /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React2.createElement(Text, null, rawScroll.toFixed(2))),
162
- /* @__PURE__ */ React2.createElement(DebugRow, null, /* @__PURE__ */ React2.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React2.createElement(Text, null, scroll.toFixed(2)))
147
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React3.createElement(Text, null, totalSize.toFixed(2))),
148
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React3.createElement(Text, null, contentSize.toFixed(2))),
149
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "At end:"), /* @__PURE__ */ React3.createElement(Text, null, String(state.isAtEnd))),
150
+ /* @__PURE__ */ React3.createElement(Text, null),
151
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React3.createElement(Text, null, scrollAdjust.toFixed(2))),
152
+ /* @__PURE__ */ React3.createElement(Text, null),
153
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React3.createElement(Text, null, rawScroll.toFixed(2))),
154
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React3.createElement(Text, null, scroll.toFixed(2)))
163
155
  );
164
156
  });
165
157
  function useInterval(callback, delay) {
@@ -168,8 +160,143 @@ function useInterval(callback, delay) {
168
160
  return () => clearInterval(interval);
169
161
  }, [delay]);
170
162
  }
163
+ var LeanViewComponent = React3.forwardRef((props, ref) => {
164
+ return React3.createElement("RCTView", { ...props, ref });
165
+ });
166
+ LeanViewComponent.displayName = "RCTView";
167
+ var LeanView = Platform.OS === "android" || Platform.OS === "ios" ? LeanViewComponent : View;
168
+
169
+ // src/constants.ts
170
+ var POSITION_OUT_OF_VIEW = -1e7;
171
+ var ENABLE_DEVMODE = __DEV__ && false;
172
+ var ENABLE_DEBUG_VIEW = __DEV__ && false;
173
+ var IsNewArchitecture = global.nativeFabricUIManager != null;
174
+ var useAnimatedValue = (initialValue) => {
175
+ return useRef(new Animated.Value(initialValue)).current;
176
+ };
177
+
178
+ // src/hooks/useValue$.ts
179
+ function useValue$(key, params) {
180
+ var _a;
181
+ const { getValue, delay } = params || {};
182
+ const ctx = useStateContext();
183
+ const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
184
+ useMemo(() => {
185
+ let newValue;
186
+ let prevValue;
187
+ let didQueueTask = false;
188
+ listen$(ctx, key, (v) => {
189
+ newValue = getValue ? getValue(v) : v;
190
+ if (delay !== void 0) {
191
+ const fn = () => {
192
+ didQueueTask = false;
193
+ if (newValue !== void 0) {
194
+ animValue.setValue(newValue);
195
+ }
196
+ };
197
+ const delayValue = typeof delay === "function" ? delay(newValue, prevValue) : delay;
198
+ prevValue = newValue;
199
+ if (!didQueueTask) {
200
+ didQueueTask = true;
201
+ if (delayValue === 0) {
202
+ queueMicrotask(fn);
203
+ } else {
204
+ setTimeout(fn, delayValue);
205
+ }
206
+ }
207
+ } else {
208
+ animValue.setValue(newValue);
209
+ }
210
+ });
211
+ }, []);
212
+ return animValue;
213
+ }
214
+ var typedForwardRef = forwardRef;
215
+ var typedMemo = memo;
216
+
217
+ // src/components/PositionView.tsx
218
+ var PositionViewState = typedMemo(function PositionView({
219
+ id,
220
+ horizontal,
221
+ style,
222
+ refView,
223
+ ...rest
224
+ }) {
225
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
226
+ return /* @__PURE__ */ React3.createElement(
227
+ LeanView,
228
+ {
229
+ ref: refView,
230
+ style: [
231
+ style,
232
+ horizontal ? { transform: [{ translateX: position }] } : { transform: [{ translateY: position }] }
233
+ ],
234
+ ...rest
235
+ }
236
+ );
237
+ });
238
+ var PositionViewAnimated = typedMemo(function PositionView2({
239
+ id,
240
+ horizontal,
241
+ style,
242
+ refView,
243
+ ...rest
244
+ }) {
245
+ const position$ = useValue$(`containerPosition${id}`, {
246
+ getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
247
+ });
248
+ return /* @__PURE__ */ React3.createElement(
249
+ Animated.View,
250
+ {
251
+ ref: refView,
252
+ style: [
253
+ style,
254
+ horizontal ? { transform: [{ translateX: position$ }] } : { transform: [{ translateY: position$ }] }
255
+ ],
256
+ ...rest
257
+ }
258
+ );
259
+ });
260
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
261
+ id,
262
+ horizontal,
263
+ style,
264
+ refView,
265
+ animatedScrollY,
266
+ stickyOffset,
267
+ index,
268
+ ...rest
269
+ }) {
270
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
271
+ const transform = React3.useMemo(() => {
272
+ if (animatedScrollY && stickyOffset) {
273
+ const stickyPosition = animatedScrollY.interpolate({
274
+ extrapolate: "clamp",
275
+ inputRange: [position, position + 5e3],
276
+ outputRange: [position, position + 5e3]
277
+ });
278
+ return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
279
+ }
280
+ }, [position, horizontal, animatedScrollY, stickyOffset]);
281
+ const viewStyle = React3.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
282
+ return /* @__PURE__ */ React3.createElement(Animated.View, { ref: refView, style: viewStyle, ...rest });
283
+ });
284
+ var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
285
+ function Separator({ ItemSeparatorComponent, itemKey, leadingItem }) {
286
+ const [lastItemKeys] = useArr$(["lastItemKeys"]);
287
+ const isALastItem = lastItemKeys.includes(itemKey);
288
+ return isALastItem ? null : /* @__PURE__ */ React3.createElement(ItemSeparatorComponent, { leadingItem });
289
+ }
290
+ var symbolFirst = Symbol();
291
+ function useInit(cb) {
292
+ const refValue = useRef(symbolFirst);
293
+ if (refValue.current === symbolFirst) {
294
+ refValue.current = cb();
295
+ }
296
+ return refValue.current;
297
+ }
171
298
 
172
- // src/helpers.ts
299
+ // src/utils/helpers.ts
173
300
  function isFunction(obj) {
174
301
  return typeof obj === "function";
175
302
  }
@@ -183,12 +310,12 @@ function warnDevOnce(id, text) {
183
310
  console.warn(`[legend-list] ${text}`);
184
311
  }
185
312
  }
313
+ function roundSize(size) {
314
+ return Math.floor(size * 8) / 8;
315
+ }
186
316
  function isNullOrUndefined(value) {
187
317
  return value === null || value === void 0;
188
318
  }
189
- function comparatorByDistance(a, b) {
190
- return b.distance - a.distance;
191
- }
192
319
  function comparatorDefault(a, b) {
193
320
  return a - b;
194
321
  }
@@ -199,16 +326,8 @@ function getPadding(s, type) {
199
326
  function extractPadding(style, contentContainerStyle, type) {
200
327
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
201
328
  }
202
- var symbolFirst = Symbol();
203
- function useInit(cb) {
204
- const refValue = useRef(symbolFirst);
205
- if (refValue.current === symbolFirst) {
206
- refValue.current = cb();
207
- }
208
- return refValue.current;
209
- }
210
329
 
211
- // src/ContextContainer.ts
330
+ // src/state/ContextContainer.ts
212
331
  var ContextContainer = createContext(null);
213
332
  function useViewability(callback, configId) {
214
333
  const ctx = useStateContext();
@@ -252,7 +371,7 @@ function useRecyclingEffect(effect) {
252
371
  prevItem: void 0
253
372
  });
254
373
  useEffect(() => {
255
- let ret = void 0;
374
+ let ret;
256
375
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
257
376
  ret = effect({
258
377
  index,
@@ -266,7 +385,7 @@ function useRecyclingEffect(effect) {
266
385
  prevItem: value
267
386
  };
268
387
  return ret;
269
- }, [index, value]);
388
+ }, [index, value, effect]);
270
389
  }
271
390
  function useRecyclingState(valueOrFun) {
272
391
  const { index, value, itemKey, triggerLayout } = useContext(ContextContainer);
@@ -275,9 +394,10 @@ function useRecyclingState(valueOrFun) {
275
394
  value: null
276
395
  });
277
396
  const [_, setRenderNum] = useState(0);
278
- if (refState.current.itemKey !== itemKey) {
279
- refState.current.itemKey = itemKey;
280
- refState.current.value = isFunction(valueOrFun) ? valueOrFun({
397
+ const state = refState.current;
398
+ if (state.itemKey !== itemKey) {
399
+ state.itemKey = itemKey;
400
+ state.value = isFunction(valueOrFun) ? valueOrFun({
281
401
  index,
282
402
  item: value,
283
403
  prevIndex: void 0,
@@ -286,13 +406,13 @@ function useRecyclingState(valueOrFun) {
286
406
  }
287
407
  const setState = useCallback(
288
408
  (newState) => {
289
- refState.current.value = isFunction(newState) ? newState(refState.current.value) : newState;
409
+ state.value = isFunction(newState) ? newState(state.value) : newState;
290
410
  setRenderNum((v) => v + 1);
291
411
  triggerLayout();
292
412
  },
293
- [triggerLayout]
413
+ [triggerLayout, state]
294
414
  );
295
- return [refState.current.value, setState];
415
+ return [state.value, setState];
296
416
  }
297
417
  function useIsLastItem() {
298
418
  const { itemKey } = useContext(ContextContainer);
@@ -303,111 +423,107 @@ function useListScrollSize() {
303
423
  const [scrollSize] = useArr$(["scrollSize"]);
304
424
  return scrollSize;
305
425
  }
306
- var LeanViewComponent = React2.forwardRef((props, ref) => {
307
- return React2.createElement("RCTView", { ...props, ref });
308
- });
309
- LeanViewComponent.displayName = "RCTView";
310
- var LeanView = Platform.OS === "android" || Platform.OS === "ios" ? LeanViewComponent : View;
311
-
312
- // src/constants.ts
313
- var POSITION_OUT_OF_VIEW = -1e7;
314
- var ANCHORED_POSITION_OUT_OF_VIEW = {
315
- type: "top",
316
- relativeCoordinate: POSITION_OUT_OF_VIEW,
317
- top: POSITION_OUT_OF_VIEW
426
+ var noop = () => {
318
427
  };
319
- var ENABLE_DEVMODE = __DEV__ && false;
320
- var ENABLE_DEBUG_VIEW = __DEV__ && false;
321
- var IsNewArchitecture = global.nativeFabricUIManager != null;
428
+ function useSyncLayout() {
429
+ if (IsNewArchitecture) {
430
+ const { triggerLayout: syncLayout } = useContext(ContextContainer);
431
+ return syncLayout;
432
+ } else {
433
+ return noop;
434
+ }
435
+ }
322
436
 
323
- // src/Container.tsx
324
- var Container = ({
437
+ // src/components/Container.tsx
438
+ var Container = typedMemo(function Container2({
325
439
  id,
326
440
  recycleItems,
327
441
  horizontal,
328
- getRenderedItem,
329
- updateItemSize,
442
+ getRenderedItem: getRenderedItem2,
443
+ updateItemSize: updateItemSize2,
330
444
  ItemSeparatorComponent
331
- }) => {
445
+ }) {
332
446
  const ctx = useStateContext();
333
- const columnWrapperStyle = ctx.columnWrapperStyle;
334
- const [
335
- maintainVisibleContentPosition,
336
- position = ANCHORED_POSITION_OUT_OF_VIEW,
337
- column = 0,
338
- numColumns,
339
- lastItemKeys,
340
- itemKey,
341
- data,
342
- extraData
343
- ] = useArr$([
344
- "maintainVisibleContentPosition",
345
- `containerPosition${id}`,
447
+ const { columnWrapperStyle, animatedScrollY } = ctx;
448
+ const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
346
449
  `containerColumn${id}`,
347
- "numColumns",
348
- "lastItemKeys",
349
- `containerItemKey${id}`,
350
450
  `containerItemData${id}`,
351
- "extraData"
451
+ `containerItemKey${id}`,
452
+ "numColumns",
453
+ "extraData",
454
+ `containerSticky${id}`,
455
+ `containerStickyOffset${id}`
352
456
  ]);
353
457
  const refLastSize = useRef();
354
458
  const ref = useRef(null);
355
459
  const [layoutRenderCount, forceLayoutRender] = useState(0);
356
460
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
357
461
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
358
- const isALastItem = lastItemKeys.includes(itemKey);
359
- let paddingStyles;
360
- if (columnWrapperStyle) {
361
- const { columnGap, rowGap, gap } = columnWrapperStyle;
362
- if (horizontal) {
363
- paddingStyles = {
364
- paddingRight: !isALastItem ? columnGap || gap || void 0 : void 0,
365
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
366
- };
367
- } else {
368
- paddingStyles = {
369
- paddingBottom: !isALastItem ? rowGap || gap || void 0 : void 0,
370
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
371
- };
462
+ let didLayout = false;
463
+ const style = useMemo(() => {
464
+ let paddingStyles;
465
+ if (columnWrapperStyle) {
466
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
467
+ if (horizontal) {
468
+ paddingStyles = {
469
+ paddingRight: columnGap || gap || void 0,
470
+ paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
471
+ };
472
+ } else {
473
+ paddingStyles = {
474
+ paddingBottom: rowGap || gap || void 0,
475
+ paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
476
+ };
477
+ }
372
478
  }
373
- }
374
- const style = horizontal ? {
375
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
376
- position: "absolute",
377
- top: otherAxisPos,
378
- height: otherAxisSize,
379
- left: position.relativeCoordinate,
380
- ...paddingStyles || {}
381
- } : {
382
- position: "absolute",
383
- left: otherAxisPos,
384
- right: numColumns > 1 ? null : 0,
385
- width: otherAxisSize,
386
- top: position.relativeCoordinate,
387
- ...paddingStyles || {}
388
- };
479
+ return horizontal ? {
480
+ flexDirection: ItemSeparatorComponent ? "row" : void 0,
481
+ height: otherAxisSize,
482
+ left: 0,
483
+ position: "absolute",
484
+ top: otherAxisPos,
485
+ ...paddingStyles || {}
486
+ } : {
487
+ left: otherAxisPos,
488
+ position: "absolute",
489
+ right: numColumns > 1 ? null : 0,
490
+ top: 0,
491
+ width: otherAxisSize,
492
+ ...paddingStyles || {}
493
+ };
494
+ }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
389
495
  const renderedItemInfo = useMemo(
390
- () => itemKey !== void 0 ? getRenderedItem(itemKey) : null,
496
+ () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
391
497
  [itemKey, data, extraData]
392
498
  );
393
499
  const { index, renderedItem } = renderedItemInfo || {};
394
- const triggerLayout = useCallback(() => {
395
- forceLayoutRender((v) => v + 1);
396
- }, []);
500
+ const contextValue = useMemo(() => {
501
+ ctx.viewRefs.set(id, ref);
502
+ return {
503
+ containerId: id,
504
+ index,
505
+ itemKey,
506
+ triggerLayout: () => {
507
+ forceLayoutRender((v) => v + 1);
508
+ },
509
+ value: data
510
+ };
511
+ }, [id, itemKey, index, data]);
397
512
  const onLayout = (event) => {
398
513
  var _a, _b;
399
514
  if (!isNullOrUndefined(itemKey)) {
515
+ didLayout = true;
400
516
  let layout = event.nativeEvent.layout;
401
517
  const size = layout[horizontal ? "width" : "height"];
402
518
  const doUpdate = () => {
403
- refLastSize.current = { width: layout.width, height: layout.height };
404
- updateItemSize(itemKey, layout);
519
+ refLastSize.current = { height: layout.height, width: layout.width };
520
+ updateItemSize2(itemKey, layout);
405
521
  };
406
522
  if (IsNewArchitecture || size > 0) {
407
523
  doUpdate();
408
524
  } else {
409
- (_b = (_a = ref.current) == null ? void 0 : _a.measure) == null ? void 0 : _b.call(_a, (x, y, width, height) => {
410
- layout = { width, height };
525
+ (_b = (_a = ref.current) == null ? void 0 : _a.measure) == null ? void 0 : _b.call(_a, (_x, _y, width, height) => {
526
+ layout = { height, width };
411
527
  doUpdate();
412
528
  });
413
529
  }
@@ -421,17 +537,17 @@ var Container = ({
421
537
  if (measured) {
422
538
  const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
423
539
  if (size) {
424
- updateItemSize(itemKey, measured);
540
+ updateItemSize2(itemKey, measured);
425
541
  }
426
542
  }
427
543
  }
428
- }, [itemKey, layoutRenderCount, isALastItem]);
544
+ }, [itemKey, layoutRenderCount]);
429
545
  } else {
430
546
  useEffect(() => {
431
547
  if (!isNullOrUndefined(itemKey)) {
432
548
  const timeout = setTimeout(() => {
433
- if (refLastSize.current) {
434
- updateItemSize(itemKey, refLastSize.current);
549
+ if (!didLayout && refLastSize.current) {
550
+ updateItemSize2(itemKey, refLastSize.current);
435
551
  }
436
552
  }, 16);
437
553
  return () => {
@@ -440,200 +556,204 @@ var Container = ({
440
556
  }
441
557
  }, [itemKey]);
442
558
  }
443
- const contextValue = useMemo(() => {
444
- ctx.viewRefs.set(id, ref);
445
- return { containerId: id, itemKey, index, value: data, triggerLayout };
446
- }, [id, itemKey, index, data]);
447
- const contentFragment = /* @__PURE__ */ React2__default.createElement(React2__default.Fragment, { key: recycleItems ? void 0 : itemKey }, /* @__PURE__ */ React2__default.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && !isALastItem && /* @__PURE__ */ React2__default.createElement(ItemSeparatorComponent, { leadingItem: renderedItemInfo.item })));
448
- if (maintainVisibleContentPosition) {
449
- const anchorStyle = horizontal ? position.type === "top" ? { position: "absolute", left: 0, top: 0, bottom: 0, flexDirection: "row", alignItems: "stretch" } : { position: "absolute", right: 0, top: 0, bottom: 0, flexDirection: "row", alignItems: "stretch" } : position.type === "top" ? { position: "absolute", top: 0, left: 0, right: 0 } : { position: "absolute", bottom: 0, left: 0, right: 0 };
450
- if (__DEV__ && ENABLE_DEVMODE) {
451
- anchorStyle.borderColor = position.type === "top" ? "red" : "blue";
452
- anchorStyle.borderWidth = 1;
453
- }
454
- return /* @__PURE__ */ React2__default.createElement(LeanView, { style }, /* @__PURE__ */ React2__default.createElement(LeanView, { style: [anchorStyle, paddingStyles], onLayout, ref }, contentFragment, __DEV__ && ENABLE_DEVMODE && /* @__PURE__ */ React2__default.createElement(Text, { style: { position: "absolute", top: 0, left: 0, zIndex: 1e3 } }, position.top)));
455
- }
456
- return /* @__PURE__ */ React2__default.createElement(LeanView, { style, onLayout, ref }, contentFragment);
457
- };
458
- var typedForwardRef = forwardRef;
459
- var typedMemo = memo;
460
- var useAnimatedValue = (initialValue) => {
461
- return useRef(new Animated.Value(initialValue)).current;
462
- };
463
-
464
- // src/useValue$.ts
465
- function useValue$(key, getValue, useMicrotask) {
466
- var _a;
467
- const ctx = useStateContext();
468
- const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
469
- useMemo(() => {
470
- let newValue = void 0;
471
- listen$(ctx, key, (v) => {
472
- if (useMicrotask && newValue === void 0) {
473
- queueMicrotask(() => {
474
- animValue.setValue(newValue);
475
- newValue = void 0;
476
- });
477
- }
478
- newValue = getValue ? getValue(v) : v;
479
- if (!useMicrotask) {
480
- animValue.setValue(newValue);
559
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView3;
560
+ return /* @__PURE__ */ React3.createElement(
561
+ PositionComponent,
562
+ {
563
+ animatedScrollY: isSticky ? animatedScrollY : void 0,
564
+ horizontal,
565
+ id,
566
+ index,
567
+ key: recycleItems ? void 0 : itemKey,
568
+ onLayout,
569
+ refView: ref,
570
+ stickyOffset: isSticky ? stickyOffset : void 0,
571
+ style
572
+ },
573
+ /* @__PURE__ */ React3.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3.createElement(
574
+ Separator,
575
+ {
576
+ ItemSeparatorComponent,
577
+ itemKey,
578
+ leadingItem: renderedItemInfo.item
481
579
  }
482
- });
483
- }, []);
484
- return animValue;
485
- }
580
+ ))
581
+ );
582
+ });
486
583
 
487
- // src/Containers.tsx
584
+ // src/components/Containers.tsx
488
585
  var Containers = typedMemo(function Containers2({
489
586
  horizontal,
490
587
  recycleItems,
491
588
  ItemSeparatorComponent,
492
589
  waitForInitialLayout,
493
- updateItemSize,
494
- getRenderedItem
590
+ updateItemSize: updateItemSize2,
591
+ getRenderedItem: getRenderedItem2
495
592
  }) {
496
593
  const ctx = useStateContext();
497
594
  const columnWrapperStyle = ctx.columnWrapperStyle;
498
595
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
499
- const animSize = useValue$(
500
- "totalSizeWithScrollAdjust",
501
- void 0,
502
- /*useMicrotask*/
503
- true
504
- );
505
- const animOpacity = waitForInitialLayout ? useValue$("containersDidLayout", (value) => value ? 1 : 0) : void 0;
506
- const otherAxisSize = useValue$(
507
- "otherAxisSize",
508
- void 0,
509
- /*useMicrotask*/
510
- true
511
- );
596
+ const animSize = useValue$("totalSize", {
597
+ // Use a microtask if increasing the size significantly, otherwise use a timeout
598
+ delay: (value, prevValue) => !prevValue || value - prevValue > 20 ? 0 : 200
599
+ });
600
+ const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("containersDidLayout", { getValue: (value) => value ? 1 : 0 }) : void 0;
601
+ const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
512
602
  const containers = [];
513
603
  for (let i = 0; i < numContainers; i++) {
514
604
  containers.push(
515
- /* @__PURE__ */ React2.createElement(
605
+ /* @__PURE__ */ React3.createElement(
516
606
  Container,
517
607
  {
608
+ getRenderedItem: getRenderedItem2,
609
+ horizontal,
610
+ ItemSeparatorComponent,
518
611
  id: i,
519
612
  key: i,
520
613
  recycleItems,
521
- horizontal,
522
- getRenderedItem,
523
- updateItemSize,
524
- ItemSeparatorComponent
614
+ updateItemSize: updateItemSize2
525
615
  }
526
616
  )
527
617
  );
528
618
  }
529
- const style = horizontal ? { width: animSize, opacity: animOpacity, minHeight: otherAxisSize } : { height: animSize, opacity: animOpacity, minWidth: otherAxisSize };
619
+ const style = horizontal ? { minHeight: otherAxisSize, opacity: animOpacity, width: animSize } : { height: animSize, minWidth: otherAxisSize, opacity: animOpacity };
530
620
  if (columnWrapperStyle && numColumns > 1) {
531
621
  const { columnGap, rowGap, gap } = columnWrapperStyle;
622
+ const gapX = columnGap || gap || 0;
623
+ const gapY = rowGap || gap || 0;
532
624
  if (horizontal) {
533
- const my = (rowGap || gap || 0) / 2;
534
- if (my) {
535
- style.marginVertical = -my;
625
+ if (gapY) {
626
+ style.marginVertical = -gapY / 2;
627
+ }
628
+ if (gapX) {
629
+ style.marginRight = -gapX;
536
630
  }
537
631
  } else {
538
- const mx = (columnGap || gap || 0) / 2;
539
- if (mx) {
540
- style.marginHorizontal = -mx;
632
+ if (gapX) {
633
+ style.marginHorizontal = -gapX;
634
+ }
635
+ if (gapY) {
636
+ style.marginBottom = -gapY;
541
637
  }
542
638
  }
543
639
  }
544
- return /* @__PURE__ */ React2.createElement(Animated.View, { style }, containers);
640
+ return /* @__PURE__ */ React3.createElement(Animated.View, { style }, containers);
545
641
  });
546
- function ListHeaderComponentContainer({
547
- children,
548
- style,
549
- ctx,
550
- horizontal,
551
- waitForInitialLayout
552
- }) {
553
- var _a;
554
- const hasData = ((_a = peek$(ctx, "lastItemKeys")) == null ? void 0 : _a.length) > 0;
555
- const scrollAdjust = useValue$("scrollAdjust", (v) => v != null ? v : 0, true);
556
- const animOpacity = waitForInitialLayout ? useValue$("containersDidLayout", (value) => value ? 1 : 0) : void 0;
557
- const additionalSize = {
558
- transform: [{ translateY: Animated.multiply(scrollAdjust, -1) }],
559
- // Header should show if there's no data yet, but containersDidLayout will be false until it has some data
560
- opacity: hasData ? animOpacity : 1
642
+ function ScrollAdjust() {
643
+ const bias = 1e7;
644
+ const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
645
+ const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
646
+ return /* @__PURE__ */ React3.createElement(
647
+ View,
648
+ {
649
+ style: {
650
+ height: 0,
651
+ left: 0,
652
+ position: "absolute",
653
+ top: scrollOffset,
654
+ width: 0
655
+ }
656
+ }
657
+ );
658
+ }
659
+
660
+ // src/components/SnapWrapper.tsx
661
+ function SnapWrapper({ ScrollComponent, ...props }) {
662
+ const [snapToOffsets] = useArr$(["snapToOffsets"]);
663
+ return /* @__PURE__ */ React.createElement(ScrollComponent, { ...props, snapToOffsets });
664
+ }
665
+ function useThrottleDebounce(mode) {
666
+ const timeoutRef = useRef(null);
667
+ const lastCallTimeRef = useRef(0);
668
+ const lastArgsRef = useRef(null);
669
+ const clearTimeoutRef = () => {
670
+ if (timeoutRef.current) {
671
+ clearTimeout(timeoutRef.current);
672
+ timeoutRef.current = null;
673
+ }
561
674
  };
562
- return /* @__PURE__ */ React2.createElement(
563
- Animated.View,
675
+ const execute = useCallback((callback, delay, ...args) => {
564
676
  {
565
- style: [style, additionalSize],
566
- onLayout: (event) => {
567
- const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
568
- set$(ctx, "headerSize", size);
677
+ const now = Date.now();
678
+ lastArgsRef.current = args;
679
+ if (now - lastCallTimeRef.current >= delay) {
680
+ lastCallTimeRef.current = now;
681
+ callback(...args);
682
+ clearTimeoutRef();
683
+ } else {
684
+ clearTimeoutRef();
685
+ timeoutRef.current = setTimeout(
686
+ () => {
687
+ if (lastArgsRef.current) {
688
+ lastCallTimeRef.current = Date.now();
689
+ callback(...lastArgsRef.current);
690
+ timeoutRef.current = null;
691
+ lastArgsRef.current = null;
692
+ }
693
+ },
694
+ delay - (now - lastCallTimeRef.current)
695
+ );
569
696
  }
697
+ }
698
+ }, [mode]);
699
+ return execute;
700
+ }
701
+
702
+ // src/hooks/useSyncLayout.tsx
703
+ function useSyncLayout2({
704
+ onChange
705
+ }) {
706
+ const ref = useRef(null);
707
+ const onLayout = useCallback(
708
+ (event) => {
709
+ onChange(event.nativeEvent.layout, false);
570
710
  },
571
- children
711
+ [onChange]
572
712
  );
713
+ if (IsNewArchitecture) {
714
+ useLayoutEffect(() => {
715
+ if (ref.current) {
716
+ ref.current.measure((x, y, width, height) => {
717
+ onChange({ height, width, x, y }, true);
718
+ });
719
+ }
720
+ }, []);
721
+ }
722
+ return { onLayout, ref };
573
723
  }
574
724
 
575
- // src/ListComponent.tsx
725
+ // src/components/ListComponent.tsx
576
726
  var getComponent = (Component) => {
577
- if (React2.isValidElement(Component)) {
727
+ if (React3.isValidElement(Component)) {
578
728
  return Component;
579
729
  }
580
730
  if (Component) {
581
- return /* @__PURE__ */ React2.createElement(Component, null);
731
+ return /* @__PURE__ */ React3.createElement(Component, null);
582
732
  }
583
733
  return null;
584
734
  };
585
- var PaddingAndAdjust = () => {
586
- const animPaddingTop = useValue$("paddingTop", (v) => v, true);
587
- const animScrollAdjust = useValue$("scrollAdjust", (v) => v, true);
588
- const additionalSize = { marginTop: animScrollAdjust, paddingTop: animPaddingTop };
589
- return /* @__PURE__ */ React2.createElement(Animated.View, { style: additionalSize });
735
+ var Padding = () => {
736
+ const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
737
+ return /* @__PURE__ */ React3.createElement(Animated.View, { style: { paddingTop: animPaddingTop } });
590
738
  };
591
- var PaddingAndAdjustDevMode = () => {
592
- const animPaddingTop = useValue$("paddingTop", (v) => v, true);
593
- const animScrollAdjust = useValue$("scrollAdjust", (v) => v, true);
594
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Animated.View, { style: { marginTop: animScrollAdjust } }), /* @__PURE__ */ React2.createElement(Animated.View, { style: { paddingTop: animPaddingTop } }), /* @__PURE__ */ React2.createElement(
739
+ var PaddingDevMode = () => {
740
+ const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
741
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(Animated.View, { style: { paddingTop: animPaddingTop } }), /* @__PURE__ */ React3.createElement(
595
742
  Animated.View,
596
743
  {
597
744
  style: {
598
- position: "absolute",
599
- top: Animated.add(animScrollAdjust, Animated.multiply(animScrollAdjust, -1)),
745
+ backgroundColor: "green",
600
746
  height: animPaddingTop,
601
747
  left: 0,
602
- right: 0,
603
- backgroundColor: "green"
604
- }
605
- }
606
- ), /* @__PURE__ */ React2.createElement(
607
- Animated.View,
608
- {
609
- style: {
610
748
  position: "absolute",
611
- top: animPaddingTop,
612
- height: animScrollAdjust,
613
- left: -16,
614
- right: -16,
615
- backgroundColor: "lightblue"
616
- }
617
- }
618
- ), /* @__PURE__ */ React2.createElement(
619
- Animated.View,
620
- {
621
- style: {
622
- position: "absolute",
623
- top: animPaddingTop,
624
- height: Animated.multiply(animScrollAdjust, -1),
625
- width: 8,
626
- right: 4,
627
- borderStyle: "dashed",
628
- borderColor: "blue",
629
- borderWidth: 1,
630
- backgroundColor: "lightblue"
631
- //backgroundColor: "blue",
749
+ right: 0,
750
+ top: 0
632
751
  }
633
752
  }
634
753
  ));
635
754
  };
636
755
  var ListComponent = typedMemo(function ListComponent2({
756
+ canRender,
637
757
  style,
638
758
  contentContainerStyle,
639
759
  horizontal,
@@ -642,194 +762,616 @@ var ListComponent = typedMemo(function ListComponent2({
642
762
  ItemSeparatorComponent,
643
763
  alignItemsAtEnd,
644
764
  waitForInitialLayout,
645
- handleScroll,
765
+ onScroll: onScroll2,
646
766
  onLayout,
647
767
  ListHeaderComponent,
648
768
  ListHeaderComponentStyle,
649
769
  ListFooterComponent,
650
770
  ListFooterComponentStyle,
651
771
  ListEmptyComponent,
652
- getRenderedItem,
653
- updateItemSize,
772
+ getRenderedItem: getRenderedItem2,
773
+ updateItemSize: updateItemSize2,
654
774
  refScrollView,
655
775
  maintainVisibleContentPosition,
656
776
  renderScrollComponent,
777
+ scrollAdjustHandler,
778
+ onLayoutHeader,
779
+ snapToIndices,
780
+ stickyIndices,
657
781
  ...rest
658
782
  }) {
659
783
  const ctx = useStateContext();
784
+ const { onLayout: onLayoutHeaderSync, ref: refHeader } = useSyncLayout2({
785
+ onChange: onLayoutHeader
786
+ });
660
787
  const ScrollComponent = renderScrollComponent ? useMemo(
661
- () => React2.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
788
+ () => React3.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
662
789
  [renderScrollComponent]
663
- ) : ScrollView;
664
- return /* @__PURE__ */ React2.createElement(
665
- ScrollComponent,
790
+ ) : Animated.ScrollView;
791
+ React3.useEffect(() => {
792
+ if (canRender) {
793
+ setTimeout(() => {
794
+ scrollAdjustHandler.setMounted();
795
+ }, 0);
796
+ }
797
+ }, [canRender]);
798
+ const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
799
+ return /* @__PURE__ */ React3.createElement(
800
+ SnapOrScroll,
666
801
  {
667
802
  ...rest,
668
- style,
669
- maintainVisibleContentPosition: maintainVisibleContentPosition && !ListEmptyComponent ? { minIndexForVisible: 0 } : void 0,
670
803
  contentContainerStyle: [
671
804
  contentContainerStyle,
672
805
  horizontal ? {
673
806
  height: "100%"
674
807
  } : {}
675
808
  ],
676
- onScroll: handleScroll,
677
- onLayout,
678
- horizontal,
679
809
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
680
- ref: refScrollView
810
+ horizontal,
811
+ maintainVisibleContentPosition: maintainVisibleContentPosition && !ListEmptyComponent ? { minIndexForVisible: 0 } : void 0,
812
+ onLayout,
813
+ onScroll: onScroll2,
814
+ ref: refScrollView,
815
+ ScrollComponent: snapToIndices ? ScrollComponent : void 0,
816
+ style
681
817
  },
682
- ENABLE_DEVMODE ? /* @__PURE__ */ React2.createElement(PaddingAndAdjustDevMode, null) : /* @__PURE__ */ React2.createElement(PaddingAndAdjust, null),
683
- ListHeaderComponent && /* @__PURE__ */ React2.createElement(
684
- ListHeaderComponentContainer,
685
- {
686
- style: ListHeaderComponentStyle,
687
- ctx,
688
- horizontal,
689
- waitForInitialLayout
690
- },
691
- getComponent(ListHeaderComponent)
692
- ),
818
+ maintainVisibleContentPosition && /* @__PURE__ */ React3.createElement(ScrollAdjust, null),
819
+ ENABLE_DEVMODE ? /* @__PURE__ */ React3.createElement(PaddingDevMode, null) : /* @__PURE__ */ React3.createElement(Padding, null),
820
+ ListHeaderComponent && /* @__PURE__ */ React3.createElement(View, { onLayout: onLayoutHeaderSync, ref: refHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
693
821
  ListEmptyComponent && getComponent(ListEmptyComponent),
694
- /* @__PURE__ */ React2.createElement(
822
+ canRender && /* @__PURE__ */ React3.createElement(
695
823
  Containers,
696
824
  {
825
+ getRenderedItem: getRenderedItem2,
697
826
  horizontal,
698
- recycleItems,
699
- waitForInitialLayout,
700
- getRenderedItem,
701
827
  ItemSeparatorComponent,
702
- updateItemSize
828
+ recycleItems,
829
+ updateItemSize: updateItemSize2,
830
+ waitForInitialLayout
703
831
  }
704
832
  ),
705
- ListFooterComponent && /* @__PURE__ */ React2.createElement(
833
+ ListFooterComponent && /* @__PURE__ */ React3.createElement(
706
834
  View,
707
835
  {
708
- style: ListFooterComponentStyle,
709
836
  onLayout: (event) => {
710
837
  const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
711
838
  set$(ctx, "footerSize", size);
712
- }
839
+ },
840
+ style: ListFooterComponentStyle
713
841
  },
714
842
  getComponent(ListFooterComponent)
715
- )
843
+ ),
844
+ __DEV__ && ENABLE_DEVMODE && /* @__PURE__ */ React3.createElement(DevNumbers, null)
716
845
  );
717
846
  });
847
+ var DevNumbers = __DEV__ && React3.memo(function DevNumbers2() {
848
+ return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3.createElement(
849
+ View,
850
+ {
851
+ key: index,
852
+ style: {
853
+ height: 100,
854
+ pointerEvents: "none",
855
+ position: "absolute",
856
+ top: index * 100,
857
+ width: "100%"
858
+ }
859
+ },
860
+ /* @__PURE__ */ React3.createElement(Text, { style: { color: "red" } }, index * 100)
861
+ ));
862
+ });
718
863
 
719
- // src/ScrollAdjustHandler.ts
720
- var ScrollAdjustHandler = class {
721
- constructor(ctx) {
722
- this.ctx = ctx;
723
- this.appliedAdjust = 0;
724
- this.busy = false;
725
- this.isPaused = false;
726
- this.isDisabled = false;
727
- this.context = ctx;
728
- }
729
- doAjdust() {
730
- set$(this.context, "scrollAdjust", this.appliedAdjust);
731
- this.busy = false;
864
+ // src/utils/getId.ts
865
+ function getId(state, index) {
866
+ const { data, keyExtractor } = state.props;
867
+ if (!data) {
868
+ return "";
732
869
  }
733
- requestAdjust(adjust, onAdjusted) {
734
- if (this.isDisabled) {
735
- return;
870
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
871
+ const id = ret;
872
+ state.idCache.set(index, id);
873
+ return id;
874
+ }
875
+
876
+ // src/core/calculateOffsetForIndex.ts
877
+ function calculateOffsetForIndex(ctx, state, index) {
878
+ let position = 0;
879
+ if (index !== void 0) {
880
+ position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
881
+ const paddingTop = peek$(ctx, "stylePaddingTop");
882
+ if (paddingTop) {
883
+ position += paddingTop;
884
+ }
885
+ const headerSize = peek$(ctx, "headerSize");
886
+ if (headerSize) {
887
+ position += headerSize;
736
888
  }
737
- const oldAdjustTop = peek$(this.context, "scrollAdjust");
738
- if (oldAdjustTop === adjust) {
739
- return;
889
+ }
890
+ return position;
891
+ }
892
+
893
+ // src/utils/getItemSize.ts
894
+ function getItemSize(state, key, index, data, useAverageSize, defaultAverageSize, preferRenderedCache) {
895
+ var _a, _b;
896
+ const {
897
+ sizesKnown,
898
+ sizes,
899
+ scrollingTo,
900
+ averageSizes,
901
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
902
+ } = state;
903
+ const sizeKnown = sizesKnown.get(key);
904
+ if (sizeKnown !== void 0) {
905
+ return sizeKnown;
906
+ }
907
+ let size;
908
+ const itemType = getItemType ? (_a = getItemType(data, index)) != null ? _a : "" : "";
909
+ if (getFixedItemSize) {
910
+ size = getFixedItemSize(index, data, itemType);
911
+ if (size !== void 0) {
912
+ sizesKnown.set(key, size);
740
913
  }
741
- this.appliedAdjust = adjust;
742
- if (!this.busy && !this.isPaused) {
743
- this.busy = true;
744
- this.doAjdust();
745
- onAdjusted(oldAdjustTop - adjust);
914
+ }
915
+ const renderedSize = sizes.get(key);
916
+ if (size === void 0 && preferRenderedCache && renderedSize !== void 0) {
917
+ return renderedSize;
918
+ }
919
+ if (size === void 0 && useAverageSize && !scrollingTo) {
920
+ if (itemType === "") {
921
+ size = defaultAverageSize;
922
+ } else {
923
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
924
+ if (averageSizeForType !== void 0) {
925
+ size = roundSize(averageSizeForType);
926
+ }
746
927
  }
747
928
  }
748
- getAppliedAdjust() {
749
- return this.appliedAdjust;
929
+ if (size === void 0 && renderedSize !== void 0) {
930
+ return renderedSize;
931
+ }
932
+ if (size === void 0) {
933
+ size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
750
934
  }
751
- pauseAdjust() {
752
- this.isPaused = true;
935
+ sizes.set(key, size);
936
+ return size;
937
+ }
938
+
939
+ // src/core/calculateOffsetWithOffsetPosition.ts
940
+ function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
941
+ const { index, viewOffset, viewPosition } = params;
942
+ let offset = offsetParam;
943
+ if (viewOffset) {
944
+ offset -= viewOffset;
753
945
  }
754
- setDisableAdjust(disable) {
755
- this.isDisabled = disable;
946
+ if (viewPosition !== void 0 && index !== void 0) {
947
+ offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
756
948
  }
757
- // return true if it was paused
758
- unPauseAdjust() {
759
- if (this.isPaused) {
760
- this.isPaused = false;
761
- this.doAjdust();
762
- return true;
763
- }
764
- return false;
949
+ return offset;
950
+ }
951
+
952
+ // src/core/finishScrollTo.ts
953
+ var finishScrollTo = (state) => {
954
+ if (state) {
955
+ state.scrollingTo = void 0;
956
+ state.scrollHistory.length = 0;
765
957
  }
766
958
  };
767
- var useCombinedRef = (...refs) => {
768
- const callback = useCallback((element) => {
769
- for (const ref of refs) {
770
- if (!ref) {
771
- continue;
959
+
960
+ // src/core/scrollTo.ts
961
+ function scrollTo(state, params = {}) {
962
+ var _a;
963
+ const { animated, noScrollingTo } = params;
964
+ const {
965
+ refScroller,
966
+ props: { horizontal }
967
+ } = state;
968
+ const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
969
+ state.scrollHistory.length = 0;
970
+ if (!noScrollingTo) {
971
+ state.scrollingTo = params;
972
+ }
973
+ state.scrollPending = offset;
974
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
975
+ animated: !!animated,
976
+ x: horizontal ? offset : 0,
977
+ y: horizontal ? 0 : offset
978
+ });
979
+ if (!animated) {
980
+ state.scroll = offset;
981
+ setTimeout(() => finishScrollTo(state), 100);
982
+ }
983
+ }
984
+
985
+ // src/utils/requestAdjust.ts
986
+ function requestAdjust(ctx, state, positionDiff, dataChanged) {
987
+ if (Math.abs(positionDiff) > 0.1) {
988
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
989
+ const doit = () => {
990
+ if (needsScrollWorkaround) {
991
+ scrollTo(state, {
992
+ noScrollingTo: true,
993
+ offset: state.scroll
994
+ });
995
+ } else {
996
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
772
997
  }
773
- if (isFunction(ref)) {
774
- ref(element);
998
+ };
999
+ state.scroll += positionDiff;
1000
+ state.scrollForNextCalculateItemsInView = void 0;
1001
+ const didLayout = peek$(ctx, "containersDidLayout");
1002
+ if (didLayout) {
1003
+ doit();
1004
+ const threshold = state.scroll - positionDiff / 2;
1005
+ if (!state.ignoreScrollFromMVCP) {
1006
+ state.ignoreScrollFromMVCP = {};
1007
+ }
1008
+ if (positionDiff > 0) {
1009
+ state.ignoreScrollFromMVCP.lt = threshold;
775
1010
  } else {
776
- ref.current = element;
1011
+ state.ignoreScrollFromMVCP.gt = threshold;
777
1012
  }
1013
+ if (state.ignoreScrollFromMVCPTimeout) {
1014
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
1015
+ }
1016
+ state.ignoreScrollFromMVCPTimeout = setTimeout(
1017
+ () => {
1018
+ state.ignoreScrollFromMVCP = void 0;
1019
+ },
1020
+ needsScrollWorkaround ? 250 : 100
1021
+ );
1022
+ } else {
1023
+ requestAnimationFrame(doit);
778
1024
  }
779
- }, refs);
780
- return callback;
1025
+ }
1026
+ }
1027
+
1028
+ // src/core/mvcp.ts
1029
+ function prepareMVCP(ctx, state, dataChanged) {
1030
+ const {
1031
+ idsInView,
1032
+ positions,
1033
+ scrollingTo,
1034
+ props: { maintainVisibleContentPosition }
1035
+ } = state;
1036
+ let prevPosition;
1037
+ let targetId;
1038
+ const idsInViewWithPositions = [];
1039
+ const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1040
+ if (maintainVisibleContentPosition) {
1041
+ const indexByKey = state.indexByKey;
1042
+ if (scrollTarget !== void 0) {
1043
+ targetId = getId(state, scrollTarget);
1044
+ } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1045
+ if (dataChanged) {
1046
+ for (let i = 0; i < idsInView.length; i++) {
1047
+ const id = idsInView[i];
1048
+ const index = indexByKey.get(id);
1049
+ if (index !== void 0) {
1050
+ idsInViewWithPositions.push({ id, position: positions.get(id) });
1051
+ }
1052
+ }
1053
+ } else {
1054
+ targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
1055
+ }
1056
+ }
1057
+ if (targetId !== void 0) {
1058
+ prevPosition = positions.get(targetId);
1059
+ }
1060
+ }
1061
+ return () => {
1062
+ let positionDiff;
1063
+ if (dataChanged && targetId === void 0) {
1064
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
1065
+ const { id, position } = idsInViewWithPositions[i];
1066
+ const newPosition = positions.get(id);
1067
+ if (newPosition !== void 0) {
1068
+ positionDiff = newPosition - position;
1069
+ break;
1070
+ }
1071
+ }
1072
+ }
1073
+ if (targetId !== void 0 && prevPosition !== void 0) {
1074
+ const newPosition = positions.get(targetId);
1075
+ if (newPosition !== void 0) {
1076
+ positionDiff = newPosition - prevPosition;
1077
+ }
1078
+ }
1079
+ if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1080
+ requestAdjust(ctx, state, positionDiff, dataChanged);
1081
+ }
1082
+ };
1083
+ }
1084
+
1085
+ // src/utils/setPaddingTop.ts
1086
+ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1087
+ if (stylePaddingTop !== void 0) {
1088
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1089
+ if (stylePaddingTop < prevStylePaddingTop) {
1090
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
1091
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1092
+ state.timeoutSetPaddingTop = setTimeout(() => {
1093
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
1094
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1095
+ }, 16);
1096
+ }
1097
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1098
+ }
1099
+ if (alignItemsPaddingTop !== void 0) {
1100
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1101
+ }
1102
+ }
1103
+
1104
+ // src/utils/updateAlignItemsPaddingTop.ts
1105
+ function updateAlignItemsPaddingTop(ctx, state) {
1106
+ const {
1107
+ scrollLength,
1108
+ props: { alignItemsAtEnd, data }
1109
+ } = state;
1110
+ if (alignItemsAtEnd) {
1111
+ let alignItemsPaddingTop = 0;
1112
+ if ((data == null ? void 0 : data.length) > 0) {
1113
+ const contentSize = getContentSize(ctx);
1114
+ alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1115
+ }
1116
+ setPaddingTop(ctx, state, { alignItemsPaddingTop });
1117
+ }
1118
+ }
1119
+
1120
+ // src/core/updateTotalSize.ts
1121
+ function updateTotalSize(ctx, state) {
1122
+ const {
1123
+ positions,
1124
+ props: { data }
1125
+ } = state;
1126
+ if (data.length === 0) {
1127
+ addTotalSize(ctx, state, null, 0);
1128
+ } else {
1129
+ const lastId = getId(state, data.length - 1);
1130
+ if (lastId !== void 0) {
1131
+ const lastPosition = positions.get(lastId);
1132
+ if (lastPosition !== void 0) {
1133
+ const lastSize = getItemSize(state, lastId, data.length - 1, data[data.length - 1]);
1134
+ if (lastSize !== void 0) {
1135
+ const totalSize = lastPosition + lastSize;
1136
+ addTotalSize(ctx, state, null, totalSize);
1137
+ }
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+ function addTotalSize(ctx, state, key, add) {
1143
+ const { alignItemsAtEnd } = state.props;
1144
+ {
1145
+ state.totalSize = add;
1146
+ if (state.timeoutSetPaddingTop) {
1147
+ clearTimeout(state.timeoutSetPaddingTop);
1148
+ state.timeoutSetPaddingTop = void 0;
1149
+ }
1150
+ }
1151
+ set$(ctx, "totalSize", state.totalSize);
1152
+ if (alignItemsAtEnd) {
1153
+ updateAlignItemsPaddingTop(ctx, state);
1154
+ }
1155
+ }
1156
+
1157
+ // src/utils/getScrollVelocity.ts
1158
+ var getScrollVelocity = (state) => {
1159
+ const { scrollHistory } = state;
1160
+ let velocity = 0;
1161
+ if (scrollHistory.length >= 1) {
1162
+ const newest = scrollHistory[scrollHistory.length - 1];
1163
+ let oldest;
1164
+ let start = 0;
1165
+ const now = Date.now();
1166
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1167
+ const entry = scrollHistory[i];
1168
+ const nextEntry = scrollHistory[i + 1];
1169
+ if (i > 0) {
1170
+ const prevEntry = scrollHistory[i - 1];
1171
+ const prevDirection = entry.scroll - prevEntry.scroll;
1172
+ const currentDirection = nextEntry.scroll - entry.scroll;
1173
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1174
+ start = i;
1175
+ break;
1176
+ }
1177
+ }
1178
+ }
1179
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1180
+ const entry = scrollHistory[i];
1181
+ if (now - entry.time <= 1e3) {
1182
+ oldest = entry;
1183
+ break;
1184
+ }
1185
+ }
1186
+ if (oldest && oldest !== newest) {
1187
+ const scrollDiff = newest.scroll - oldest.scroll;
1188
+ const timeDiff = newest.time - oldest.time;
1189
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1190
+ }
1191
+ }
1192
+ return velocity;
781
1193
  };
782
1194
 
783
- // src/viewability.ts
784
- var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
1195
+ // src/utils/updateSnapToOffsets.ts
1196
+ function updateSnapToOffsets(ctx, state) {
1197
+ const {
1198
+ positions,
1199
+ props: { snapToIndices }
1200
+ } = state;
1201
+ const snapToOffsets = Array(snapToIndices.length);
1202
+ for (let i = 0; i < snapToIndices.length; i++) {
1203
+ const idx = snapToIndices[i];
1204
+ const key = getId(state, idx);
1205
+ snapToOffsets[i] = positions.get(key);
1206
+ }
1207
+ set$(ctx, "snapToOffsets", snapToOffsets);
1208
+ }
1209
+
1210
+ // src/core/updateAllPositions.ts
1211
+ function updateAllPositions(ctx, state, dataChanged) {
1212
+ var _a, _b, _c, _d, _e;
1213
+ const {
1214
+ averageSizes,
1215
+ columns,
1216
+ indexByKey,
1217
+ positions,
1218
+ firstFullyOnScreenIndex,
1219
+ idCache,
1220
+ sizesKnown,
1221
+ props: { getEstimatedItemSize, snapToIndices }
1222
+ } = state;
1223
+ const data = state.props.data;
1224
+ const numColumns = peek$(ctx, "numColumns");
1225
+ const indexByKeyForChecking = __DEV__ ? /* @__PURE__ */ new Map() : void 0;
1226
+ const scrollVelocity = getScrollVelocity(state);
1227
+ const useAverageSize = !getEstimatedItemSize;
1228
+ const itemType = "";
1229
+ let averageSize = (_a = averageSizes[itemType]) == null ? void 0 : _a.avg;
1230
+ if (averageSize !== void 0) {
1231
+ averageSize = roundSize(averageSize);
1232
+ }
1233
+ const shouldUseBackwards = !dataChanged && scrollVelocity < 0 && firstFullyOnScreenIndex > 5 && firstFullyOnScreenIndex < data.length;
1234
+ if (shouldUseBackwards && firstFullyOnScreenIndex !== void 0) {
1235
+ const anchorId = getId(state, firstFullyOnScreenIndex);
1236
+ const anchorPosition = positions.get(anchorId);
1237
+ if (anchorPosition !== void 0) {
1238
+ let currentRowTop2 = anchorPosition;
1239
+ let maxSizeInRow2 = 0;
1240
+ let bailout = false;
1241
+ for (let i = firstFullyOnScreenIndex - 1; i >= 0; i--) {
1242
+ const id = (_b = idCache.get(i)) != null ? _b : getId(state, i);
1243
+ const size = (_c = sizesKnown.get(id)) != null ? _c : getItemSize(
1244
+ state,
1245
+ id,
1246
+ i,
1247
+ data[i],
1248
+ useAverageSize,
1249
+ averageSize,
1250
+ /*preferRenderedCache*/
1251
+ !!dataChanged
1252
+ );
1253
+ const itemColumn = columns.get(id);
1254
+ maxSizeInRow2 = Math.max(maxSizeInRow2, size);
1255
+ if (itemColumn === 1) {
1256
+ currentRowTop2 -= maxSizeInRow2;
1257
+ maxSizeInRow2 = 0;
1258
+ }
1259
+ if (currentRowTop2 < -2e3) {
1260
+ bailout = true;
1261
+ break;
1262
+ }
1263
+ positions.set(id, currentRowTop2);
1264
+ }
1265
+ if (!bailout) {
1266
+ updateTotalSize(ctx, state);
1267
+ return;
1268
+ }
1269
+ }
1270
+ }
1271
+ let currentRowTop = 0;
1272
+ let column = 1;
1273
+ let maxSizeInRow = 0;
1274
+ const hasColumns = numColumns > 1;
1275
+ const needsIndexByKey = dataChanged || indexByKey.size === 0;
1276
+ const dataLength = data.length;
1277
+ for (let i = 0; i < dataLength; i++) {
1278
+ const id = (_d = idCache.get(i)) != null ? _d : getId(state, i);
1279
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(
1280
+ state,
1281
+ id,
1282
+ i,
1283
+ data[i],
1284
+ useAverageSize,
1285
+ averageSize,
1286
+ /*preferRenderedCache*/
1287
+ !!dataChanged
1288
+ );
1289
+ if (__DEV__ && needsIndexByKey) {
1290
+ if (indexByKeyForChecking.has(id)) {
1291
+ console.error(
1292
+ `[legend-list] Error: Detected overlapping key (${id}) which causes missing items and gaps and other terrrible things. Check that keyExtractor returns unique values.`
1293
+ );
1294
+ }
1295
+ indexByKeyForChecking.set(id, i);
1296
+ }
1297
+ positions.set(id, currentRowTop);
1298
+ if (needsIndexByKey) {
1299
+ indexByKey.set(id, i);
1300
+ }
1301
+ columns.set(id, column);
1302
+ if (hasColumns) {
1303
+ if (size > maxSizeInRow) {
1304
+ maxSizeInRow = size;
1305
+ }
1306
+ column++;
1307
+ if (column > numColumns) {
1308
+ currentRowTop += maxSizeInRow;
1309
+ column = 1;
1310
+ maxSizeInRow = 0;
1311
+ }
1312
+ } else {
1313
+ currentRowTop += size;
1314
+ }
1315
+ }
1316
+ updateTotalSize(ctx, state);
1317
+ if (snapToIndices) {
1318
+ updateSnapToOffsets(ctx, state);
1319
+ }
1320
+ }
1321
+
1322
+ // src/core/viewability.ts
1323
+ function ensureViewabilityState(ctx, configId) {
1324
+ let map = ctx.mapViewabilityConfigStates;
1325
+ if (!map) {
1326
+ map = /* @__PURE__ */ new Map();
1327
+ ctx.mapViewabilityConfigStates = map;
1328
+ }
1329
+ let state = map.get(configId);
1330
+ if (!state) {
1331
+ state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
1332
+ map.set(configId, state);
1333
+ }
1334
+ return state;
1335
+ }
785
1336
  function setupViewability(props) {
786
1337
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
787
1338
  if (viewabilityConfig || onViewableItemsChanged) {
788
1339
  viewabilityConfigCallbackPairs = [
789
1340
  ...viewabilityConfigCallbackPairs || [],
790
1341
  {
1342
+ onViewableItemsChanged,
791
1343
  viewabilityConfig: viewabilityConfig || {
792
1344
  viewAreaCoveragePercentThreshold: 0
793
- },
794
- onViewableItemsChanged
1345
+ }
795
1346
  }
796
1347
  ];
797
1348
  }
798
- if (viewabilityConfigCallbackPairs) {
799
- for (const pair of viewabilityConfigCallbackPairs) {
800
- mapViewabilityConfigCallbackPairs.set(pair.viewabilityConfig.id, {
801
- viewableItems: [],
802
- start: -1,
803
- end: -1,
804
- previousStart: -1,
805
- previousEnd: -1
806
- });
807
- }
808
- }
809
1349
  return viewabilityConfigCallbackPairs;
810
1350
  }
811
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, getId, scrollSize, start, end) {
1351
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
1352
+ const {
1353
+ timeouts,
1354
+ props: { data }
1355
+ } = state;
812
1356
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
813
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(
814
- viewabilityConfigCallbackPair.viewabilityConfig.id
815
- );
1357
+ const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
816
1358
  viewabilityState.start = start;
817
1359
  viewabilityState.end = end;
818
1360
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
819
1361
  const timer = setTimeout(() => {
820
- state.timeouts.delete(timer);
821
- updateViewableItemsWithConfig(state.data, viewabilityConfigCallbackPair, getId, state, ctx, scrollSize);
1362
+ timeouts.delete(timer);
1363
+ updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
822
1364
  }, viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime);
823
- state.timeouts.add(timer);
1365
+ timeouts.add(timer);
824
1366
  } else {
825
- updateViewableItemsWithConfig(state.data, viewabilityConfigCallbackPair, getId, state, ctx, scrollSize);
1367
+ updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
826
1368
  }
827
1369
  }
828
1370
  }
829
- function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getId, state, ctx, scrollSize) {
1371
+ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize) {
830
1372
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
831
1373
  const configId = viewabilityConfig.id;
832
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
1374
+ const viewabilityState = ensureViewabilityState(ctx, configId);
833
1375
  const { viewableItems: previousViewableItems, start, end } = viewabilityState;
834
1376
  const viewabilityTokens = /* @__PURE__ */ new Map();
835
1377
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -870,15 +1412,15 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
870
1412
  for (let i = start; i <= end; i++) {
871
1413
  const item = data[i];
872
1414
  if (item) {
873
- const key = getId(i);
1415
+ const key = getId(state, i);
874
1416
  const containerId = findContainerId(ctx, key);
875
1417
  if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
876
1418
  const viewToken = {
877
- item,
878
- key,
1419
+ containerId,
879
1420
  index: i,
880
1421
  isViewable: true,
881
- containerId
1422
+ item,
1423
+ key
882
1424
  };
883
1425
  viewableItems.push(viewToken);
884
1426
  if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
@@ -888,9 +1430,9 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
888
1430
  }
889
1431
  }
890
1432
  Object.assign(viewabilityState, {
891
- viewableItems,
1433
+ previousEnd: end,
892
1434
  previousStart: start,
893
- previousEnd: end
1435
+ viewableItems
894
1436
  });
895
1437
  if (changed.length > 0) {
896
1438
  viewabilityState.viewableItems = viewableItems;
@@ -899,7 +1441,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
899
1441
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
900
1442
  }
901
1443
  if (onViewableItemsChanged) {
902
- onViewableItemsChanged({ viewableItems, changed });
1444
+ onViewableItemsChanged({ changed, viewableItems });
903
1445
  }
904
1446
  }
905
1447
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -908,14 +1450,22 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, getI
908
1450
  }
909
1451
  }
910
1452
  }
1453
+ function shallowEqual(prev, next) {
1454
+ if (!prev) return false;
1455
+ const keys = Object.keys(next);
1456
+ for (let i = 0; i < keys.length; i++) {
1457
+ const k = keys[i];
1458
+ if (prev[k] !== next[k]) return false;
1459
+ }
1460
+ return true;
1461
+ }
911
1462
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
912
- const { sizes, positions, scroll: scrollState, scrollAdjustHandler } = state;
1463
+ const { sizes, positions, scroll: scrollState } = state;
913
1464
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
914
1465
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
915
1466
  const viewAreaMode = viewAreaCoveragePercentThreshold != null;
916
1467
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
917
- const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
918
- const scroll = scrollState - previousScrollAdjust - topPad;
1468
+ const scroll = scrollState - topPad;
919
1469
  const top = positions.get(key) - scroll;
920
1470
  const size = sizes.get(key) || 0;
921
1471
  const bottom = top + size;
@@ -926,18 +1476,19 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
926
1476
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
927
1477
  const isViewable2 = percent >= viewablePercentThreshold;
928
1478
  const value = {
1479
+ containerId,
929
1480
  index,
930
1481
  isViewable: isViewable2,
931
1482
  item,
932
1483
  key,
933
- percentVisible,
934
1484
  percentOfScroller,
935
- sizeVisible,
936
- size,
1485
+ percentVisible,
937
1486
  scrollSize,
938
- containerId
1487
+ size,
1488
+ sizeVisible
939
1489
  };
940
- if (JSON.stringify(value) !== JSON.stringify(ctx.mapViewabilityAmountValues.get(containerId))) {
1490
+ const prev = ctx.mapViewabilityAmountValues.get(containerId);
1491
+ if (!shallowEqual(prev, value)) {
941
1492
  ctx.mapViewabilityAmountValues.set(containerId, value);
942
1493
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
943
1494
  if (cb) {
@@ -967,507 +1518,357 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
967
1518
  cb == null ? void 0 : cb(viewToken);
968
1519
  }
969
1520
 
970
- // src/LegendList.tsx
971
- var DEFAULT_DRAW_DISTANCE = 250;
972
- var DEFAULT_ITEM_SIZE = 100;
973
- function createColumnWrapperStyle(contentContainerStyle) {
974
- const { gap, columnGap, rowGap } = contentContainerStyle;
975
- if (gap || columnGap || rowGap) {
976
- contentContainerStyle.gap = void 0;
977
- contentContainerStyle.columnGap = void 0;
978
- contentContainerStyle.rowGap = void 0;
979
- return {
980
- gap,
981
- columnGap,
982
- rowGap
983
- };
1521
+ // src/utils/checkAllSizesKnown.ts
1522
+ function checkAllSizesKnown(state) {
1523
+ const { startBuffered, endBuffered, sizesKnown } = state;
1524
+ if (endBuffered !== null) {
1525
+ let areAllKnown = true;
1526
+ for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1527
+ const key = getId(state, i);
1528
+ areAllKnown && (areAllKnown = sizesKnown.has(key));
1529
+ }
1530
+ return areAllKnown;
984
1531
  }
1532
+ return false;
985
1533
  }
986
- var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
987
- return /* @__PURE__ */ React2.createElement(StateProvider, null, /* @__PURE__ */ React2.createElement(LegendListInner, { ...props, ref: forwardedRef }));
988
- });
989
- var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
990
- const {
991
- data: dataProp = [],
992
- initialScrollIndex: initialScrollIndexProp,
993
- initialScrollOffset,
994
- horizontal,
995
- drawDistance = 250,
996
- recycleItems = false,
997
- onEndReachedThreshold = 0.5,
998
- onStartReachedThreshold = 0.5,
999
- maintainScrollAtEnd = false,
1000
- maintainScrollAtEndThreshold = 0.1,
1001
- alignItemsAtEnd = false,
1002
- maintainVisibleContentPosition = false,
1003
- onScroll: onScrollProp,
1004
- onMomentumScrollEnd,
1005
- numColumns: numColumnsProp = 1,
1006
- columnWrapperStyle,
1007
- keyExtractor: keyExtractorProp,
1008
- renderItem: renderItem2,
1009
- estimatedListSize,
1010
- estimatedItemSize: estimatedItemSizeProp,
1011
- getEstimatedItemSize,
1012
- suggestEstimatedItemSize,
1013
- ListHeaderComponent,
1014
- ListEmptyComponent,
1015
- onItemSizeChanged,
1016
- refScrollView,
1017
- waitForInitialLayout = true,
1018
- extraData,
1019
- contentContainerStyle: contentContainerStyleProp,
1020
- style: styleProp,
1021
- onLayout: onLayoutProp,
1022
- onRefresh,
1023
- refreshing,
1024
- progressViewOffset,
1025
- refreshControl,
1026
- initialContainerPoolRatio = 2,
1027
- viewabilityConfig,
1028
- viewabilityConfigCallbackPairs,
1029
- onViewableItemsChanged,
1030
- ...rest
1031
- } = props;
1032
- const initialScroll = typeof initialScrollIndexProp === "number" ? { index: initialScrollIndexProp } : initialScrollIndexProp;
1033
- const initialScrollIndex = initialScroll == null ? void 0 : initialScroll.index;
1034
- const refLoadStartTime = useRef(Date.now());
1035
- const callbacks = useRef({
1036
- onStartReached: rest.onStartReached,
1037
- onEndReached: rest.onEndReached
1038
- });
1039
- callbacks.current.onStartReached = rest.onStartReached;
1040
- callbacks.current.onEndReached = rest.onEndReached;
1041
- const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
1042
- const style = { ...StyleSheet.flatten(styleProp) };
1043
- const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
1044
- const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
1045
- if (style == null ? void 0 : style.paddingTop) {
1046
- style.paddingTop = void 0;
1047
- }
1048
- if (contentContainerStyle == null ? void 0 : contentContainerStyle.paddingTop) {
1049
- contentContainerStyle.paddingTop = void 0;
1534
+
1535
+ // src/utils/findAvailableContainers.ts
1536
+ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1537
+ const numContainers = peek$(ctx, "numContainers");
1538
+ const { stickyContainerPool, containerItemTypes } = state;
1539
+ const result = [];
1540
+ const availableContainers = [];
1541
+ const stickyIndicesSet = state.props.stickyIndicesSet;
1542
+ const stickyItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyIndicesSet.has(index))) || [];
1543
+ const canReuseContainer = (containerIndex, requiredType) => {
1544
+ if (!requiredType) return true;
1545
+ const existingType = containerItemTypes.get(containerIndex);
1546
+ if (!existingType) return true;
1547
+ return existingType === requiredType;
1548
+ };
1549
+ const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
1550
+ let typeIndex = 0;
1551
+ for (let i = 0; i < stickyItemIndices.length; i++) {
1552
+ const requiredType = neededTypes[typeIndex];
1553
+ let foundContainer = false;
1554
+ for (const containerIndex of stickyContainerPool) {
1555
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
1556
+ const isPendingRemoval = pendingRemoval.includes(containerIndex);
1557
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
1558
+ result.push(containerIndex);
1559
+ if (isPendingRemoval) {
1560
+ const index = pendingRemoval.indexOf(containerIndex);
1561
+ pendingRemoval.splice(index, 1);
1562
+ }
1563
+ foundContainer = true;
1564
+ if (requiredItemTypes) typeIndex++;
1565
+ break;
1566
+ }
1567
+ }
1568
+ if (!foundContainer) {
1569
+ const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
1570
+ result.push(newContainerIndex);
1571
+ stickyContainerPool.add(newContainerIndex);
1572
+ if (requiredItemTypes) typeIndex++;
1573
+ }
1050
1574
  }
1051
- const ctx = useStateContext();
1052
- ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
1053
- const refScroller = useRef(null);
1054
- const combinedRef = useCombinedRef(refScroller, refScrollView);
1055
- const estimatedItemSize = estimatedItemSizeProp != null ? estimatedItemSizeProp : DEFAULT_ITEM_SIZE;
1056
- const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
1057
- const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (item, index) => index.toString();
1058
- const refState = useRef();
1059
- const getId = (index) => {
1060
- var _a;
1061
- const data = (_a = refState.current) == null ? void 0 : _a.data;
1062
- if (!data) {
1063
- return "";
1064
- }
1065
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1066
- return `${ret}`;
1067
- };
1068
- const getItemSize = (key, index, data, useAverageSize = false) => {
1069
- const state = refState.current;
1070
- state.sizesKnown.get(key);
1071
- const sizePrevious = state.sizes.get(key);
1072
- let size;
1073
- peek$(ctx, "numColumns");
1074
- if (size === void 0 && sizePrevious !== void 0) {
1075
- return sizePrevious;
1076
- }
1077
- if (size === void 0) {
1078
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize;
1079
- }
1080
- state.sizes.set(key, size);
1081
- return size;
1082
- };
1083
- const calculateOffsetForIndex = (indexParam) => {
1084
- var _a;
1085
- const isFromInit = indexParam === void 0;
1086
- const index = isFromInit ? initialScrollIndex : indexParam;
1087
- const data = dataProp;
1088
- if (index !== void 0) {
1089
- let offset = 0;
1090
- const canGetSize = !!refState.current;
1091
- if (canGetSize || getEstimatedItemSize) {
1092
- const sizeFn = (index2) => {
1093
- if (canGetSize) {
1094
- return getItemSize(getId(index2), index2, data[index2], true);
1095
- }
1096
- return getEstimatedItemSize(index2, data[index2]);
1097
- };
1098
- for (let i = 0; i < index; i++) {
1099
- offset += sizeFn(i);
1100
- }
1101
- } else {
1102
- offset = index * estimatedItemSize;
1575
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1576
+ if (stickyContainerPool.has(u)) {
1577
+ continue;
1578
+ }
1579
+ const key = peek$(ctx, `containerItemKey${u}`);
1580
+ let isOk = key === void 0;
1581
+ if (!isOk) {
1582
+ const index = pendingRemoval.indexOf(u);
1583
+ if (index !== -1) {
1584
+ pendingRemoval.splice(index, 1);
1585
+ const requiredType = neededTypes[typeIndex];
1586
+ isOk = canReuseContainer(u, requiredType);
1103
1587
  }
1104
- const adjust = peek$(ctx, "containersDidLayout") ? ((_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler.getAppliedAdjust()) || 0 : 0;
1105
- const stylePaddingTop = isFromInit ? stylePaddingTopState : peek$(ctx, "stylePaddingTop");
1106
- const topPad = (stylePaddingTop != null ? stylePaddingTop : 0) + peek$(ctx, "headerSize");
1107
- return offset / numColumnsProp - adjust + topPad;
1108
1588
  }
1109
- return 0;
1110
- };
1111
- const calculateOffsetWithOffsetPosition = (offsetParam, params) => {
1112
- const { index, viewOffset, viewPosition } = params;
1113
- let offset = offsetParam;
1114
- const state = refState.current;
1115
- if (viewOffset) {
1116
- offset -= viewOffset;
1589
+ if (isOk) {
1590
+ result.push(u);
1591
+ if (requiredItemTypes) {
1592
+ typeIndex++;
1593
+ }
1117
1594
  }
1118
- if (viewPosition !== void 0 && index !== void 0) {
1119
- offset -= viewPosition * (state.scrollLength - getItemSize(getId(index), index, state.data[index]));
1595
+ }
1596
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1597
+ if (stickyContainerPool.has(u)) {
1598
+ continue;
1120
1599
  }
1121
- return offset;
1122
- };
1123
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : useMemo(() => calculateOffsetForIndex(void 0), []);
1124
- if (!refState.current) {
1125
- const initialScrollLength = (estimatedListSize != null ? estimatedListSize : Dimensions.get("window"))[horizontal ? "width" : "height"];
1126
- refState.current = {
1127
- sizes: /* @__PURE__ */ new Map(),
1128
- positions: /* @__PURE__ */ new Map(),
1129
- columns: /* @__PURE__ */ new Map(),
1130
- pendingAdjust: 0,
1131
- isStartReached: initialContentOffset < initialScrollLength * onStartReachedThreshold,
1132
- isEndReached: false,
1133
- isAtEnd: false,
1134
- isAtStart: false,
1135
- data: dataProp,
1136
- scrollLength: initialScrollLength,
1137
- startBuffered: -1,
1138
- startNoBuffer: -1,
1139
- endBuffered: -1,
1140
- endNoBuffer: -1,
1141
- scroll: initialContentOffset || 0,
1142
- totalSize: 0,
1143
- totalSizeBelowAnchor: 0,
1144
- timeouts: /* @__PURE__ */ new Set(),
1145
- viewabilityConfigCallbackPairs: void 0,
1146
- renderItem: void 0,
1147
- scrollAdjustHandler: new ScrollAdjustHandler(ctx),
1148
- nativeMarginTop: 0,
1149
- scrollPrev: 0,
1150
- scrollPrevTime: 0,
1151
- scrollTime: 0,
1152
- scrollPending: 0,
1153
- indexByKey: /* @__PURE__ */ new Map(),
1154
- scrollHistory: [],
1155
- scrollVelocity: 0,
1156
- sizesKnown: /* @__PURE__ */ new Map(),
1157
- timeoutSizeMessage: 0,
1158
- scrollTimer: void 0,
1159
- belowAnchorElementPositions: void 0,
1160
- rowHeights: /* @__PURE__ */ new Map(),
1161
- startReachedBlockedByTimer: false,
1162
- endReachedBlockedByTimer: false,
1163
- scrollForNextCalculateItemsInView: void 0,
1164
- enableScrollForNextCalculateItemsInView: true,
1165
- minIndexSizeChanged: 0,
1166
- queuedCalculateItemsInView: 0,
1167
- lastBatchingAction: Date.now(),
1168
- averageSizes: {},
1169
- onScroll: onScrollProp
1170
- };
1171
- const dataLength = dataProp.length;
1172
- if (maintainVisibleContentPosition && dataLength > 0) {
1173
- if (initialScrollIndex && initialScrollIndex < dataLength) {
1174
- refState.current.anchorElement = {
1175
- coordinate: initialContentOffset,
1176
- id: getId(initialScrollIndex)
1177
- };
1178
- } else if (dataLength > 0) {
1179
- refState.current.anchorElement = {
1180
- coordinate: initialContentOffset,
1181
- id: getId(0)
1182
- };
1183
- } else {
1184
- __DEV__ && warnDevOnce(
1185
- "maintainVisibleContentPosition",
1186
- "[legend-list] maintainVisibleContentPosition was not able to find an anchor element"
1187
- );
1600
+ const key = peek$(ctx, `containerItemKey${u}`);
1601
+ if (key === void 0) continue;
1602
+ const index = state.indexByKey.get(key);
1603
+ const isOutOfView = index < startBuffered || index > endBuffered;
1604
+ if (isOutOfView) {
1605
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
1606
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
1607
+ availableContainers.push({ distance, index: u });
1188
1608
  }
1189
1609
  }
1190
- set$(ctx, "scrollAdjust", 0);
1191
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
1192
- set$(ctx, "extraData", extraData);
1193
1610
  }
1194
- const didDataChange = refState.current.data !== dataProp;
1195
- refState.current.data = dataProp;
1196
- refState.current.onScroll = onScrollProp;
1197
- const getAnchorElementIndex = () => {
1198
- const state = refState.current;
1199
- if (state.anchorElement) {
1200
- const el = state.indexByKey.get(state.anchorElement.id);
1201
- return el;
1611
+ const remaining = numNeeded - result.length;
1612
+ if (remaining > 0) {
1613
+ if (availableContainers.length > 0) {
1614
+ if (availableContainers.length > remaining) {
1615
+ availableContainers.sort(comparatorByDistance);
1616
+ availableContainers.length = remaining;
1617
+ }
1618
+ for (const container of availableContainers) {
1619
+ result.push(container.index);
1620
+ if (requiredItemTypes) {
1621
+ typeIndex++;
1622
+ }
1623
+ }
1202
1624
  }
1203
- return void 0;
1204
- };
1205
- const scrollToIndex = ({
1625
+ const stillNeeded = numNeeded - result.length;
1626
+ if (stillNeeded > 0) {
1627
+ for (let i = 0; i < stillNeeded; i++) {
1628
+ result.push(numContainers + i);
1629
+ }
1630
+ if (__DEV__ && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
1631
+ console.warn(
1632
+ "[legend-list] No unused container available, 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 or increasing initialContainerPoolRatio.",
1633
+ {
1634
+ debugInfo: {
1635
+ numContainers,
1636
+ numContainersPooled: peek$(ctx, "numContainersPooled"),
1637
+ numNeeded,
1638
+ stillNeeded
1639
+ }
1640
+ }
1641
+ );
1642
+ }
1643
+ }
1644
+ }
1645
+ return result.sort(comparatorDefault);
1646
+ }
1647
+ function comparatorByDistance(a, b) {
1648
+ return b.distance - a.distance;
1649
+ }
1650
+
1651
+ // src/core/scrollToIndex.ts
1652
+ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
1653
+ if (index >= state.props.data.length) {
1654
+ index = state.props.data.length - 1;
1655
+ } else if (index < 0) {
1656
+ index = 0;
1657
+ }
1658
+ const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
1659
+ const isLast = index === state.props.data.length - 1;
1660
+ if (isLast && viewPosition === void 0) {
1661
+ viewPosition = 1;
1662
+ }
1663
+ const firstIndexScrollPostion = firstIndexOffset - viewOffset;
1664
+ state.scrollForNextCalculateItemsInView = void 0;
1665
+ scrollTo(state, {
1666
+ animated,
1206
1667
  index,
1207
- viewOffset = 0,
1208
- animated = true,
1209
- viewPosition
1210
- }) => {
1211
- var _a;
1212
- const state = refState.current;
1213
- if (index >= state.data.length) {
1214
- index = state.data.length - 1;
1215
- } else if (index < 0) {
1216
- index = 0;
1217
- }
1218
- const firstIndexOffset = calculateOffsetForIndex(index);
1219
- const isLast = index === state.data.length - 1;
1220
- if (isLast && viewPosition !== void 0) {
1221
- viewPosition = 1;
1222
- }
1223
- let firstIndexScrollPostion = firstIndexOffset - viewOffset;
1224
- const diff = Math.abs(state.scroll - firstIndexScrollPostion);
1225
- const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
1226
- const needsReanchoring = maintainVisibleContentPosition && diff > 100;
1227
- state.scrollForNextCalculateItemsInView = void 0;
1228
- if (needsReanchoring) {
1229
- const id = getId(index);
1230
- state.anchorElement = { id, coordinate: firstIndexOffset - topPad };
1231
- (_a = state.belowAnchorElementPositions) == null ? void 0 : _a.clear();
1232
- state.positions.clear();
1233
- calcTotalSizesAndPositions({ forgetPositions: true });
1234
- state.startBufferedId = id;
1235
- state.minIndexSizeChanged = index;
1236
- firstIndexScrollPostion = firstIndexOffset - viewOffset + state.scrollAdjustHandler.getAppliedAdjust();
1237
- }
1238
- scrollTo({ offset: firstIndexScrollPostion, animated, index, viewPosition: viewPosition != null ? viewPosition : 0, viewOffset });
1239
- };
1240
- const setDidLayout = () => {
1241
- refState.current.queuedInitialLayout = true;
1242
- checkAtBottom();
1243
- const setIt = () => {
1244
- set$(ctx, "containersDidLayout", true);
1245
- if (props.onLoad) {
1246
- props.onLoad({ elapsedTimeInMs: Date.now() - refLoadStartTime.current });
1668
+ offset: firstIndexScrollPostion,
1669
+ viewOffset,
1670
+ viewPosition: viewPosition != null ? viewPosition : 0
1671
+ });
1672
+ }
1673
+
1674
+ // src/utils/checkThreshold.ts
1675
+ var checkThreshold = (distance, atThreshold, threshold, isReached, isBlockedByTimer, onReached, blockTimer) => {
1676
+ const distanceAbs = Math.abs(distance);
1677
+ const isAtThreshold = atThreshold || distanceAbs < threshold;
1678
+ if (!isReached && !isBlockedByTimer) {
1679
+ if (isAtThreshold) {
1680
+ onReached == null ? void 0 : onReached(distance);
1681
+ blockTimer == null ? void 0 : blockTimer(true);
1682
+ setTimeout(() => {
1683
+ blockTimer == null ? void 0 : blockTimer(false);
1684
+ }, 700);
1685
+ return true;
1686
+ }
1687
+ } else {
1688
+ if (distance >= 1.3 * threshold) {
1689
+ return false;
1690
+ }
1691
+ }
1692
+ return isReached;
1693
+ };
1694
+
1695
+ // src/utils/checkAtBottom.ts
1696
+ function checkAtBottom(ctx, state) {
1697
+ if (!state) {
1698
+ return;
1699
+ }
1700
+ const {
1701
+ queuedInitialLayout,
1702
+ scrollLength,
1703
+ scroll,
1704
+ maintainingScrollAtEnd,
1705
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1706
+ } = state;
1707
+ const contentSize = getContentSize(ctx);
1708
+ if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1709
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1710
+ const isContentLess = contentSize < scrollLength;
1711
+ state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1712
+ state.isEndReached = checkThreshold(
1713
+ distanceFromEnd,
1714
+ isContentLess,
1715
+ onEndReachedThreshold * scrollLength,
1716
+ state.isEndReached,
1717
+ state.endReachedBlockedByTimer,
1718
+ (distance) => {
1719
+ var _a, _b;
1720
+ return (_b = (_a = state.props).onEndReached) == null ? void 0 : _b.call(_a, { distanceFromEnd: distance });
1721
+ },
1722
+ (block) => {
1723
+ state.endReachedBlockedByTimer = block;
1247
1724
  }
1248
- };
1725
+ );
1726
+ }
1727
+ }
1728
+
1729
+ // src/utils/setDidLayout.ts
1730
+ function setDidLayout(ctx, state) {
1731
+ const {
1732
+ loadStartTime,
1733
+ initialScroll,
1734
+ props: { onLoad }
1735
+ } = state;
1736
+ state.queuedInitialLayout = true;
1737
+ checkAtBottom(ctx, state);
1738
+ const setIt = () => {
1739
+ set$(ctx, "containersDidLayout", true);
1740
+ if (onLoad) {
1741
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1742
+ }
1743
+ };
1744
+ if (Platform.OS === "android" || !IsNewArchitecture) {
1249
1745
  if (initialScroll) {
1250
1746
  queueMicrotask(() => {
1251
- scrollToIndex({ ...initialScroll, animated: false });
1747
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1252
1748
  requestAnimationFrame(() => {
1253
- if (!IsNewArchitecture) {
1254
- scrollToIndex({ ...initialScroll, animated: false });
1255
- }
1749
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1256
1750
  setIt();
1257
1751
  });
1258
1752
  });
1259
1753
  } else {
1260
1754
  queueMicrotask(setIt);
1261
1755
  }
1262
- };
1263
- const addTotalSize = useCallback((key, add, totalSizeBelowAnchor) => {
1264
- const state = refState.current;
1265
- const { indexByKey, anchorElement } = state;
1266
- const index = key === null ? 0 : indexByKey.get(key);
1267
- let isAboveAnchor = false;
1268
- if (maintainVisibleContentPosition) {
1269
- if (anchorElement && index < getAnchorElementIndex()) {
1270
- isAboveAnchor = true;
1271
- }
1272
- }
1273
- if (key === null) {
1274
- state.totalSize = add;
1275
- state.totalSizeBelowAnchor = totalSizeBelowAnchor;
1276
- } else {
1277
- state.totalSize += add;
1278
- if (isAboveAnchor) {
1279
- state.totalSizeBelowAnchor += add;
1280
- }
1281
- }
1282
- let applyAdjustValue = 0;
1283
- let resultSize = state.totalSize;
1284
- if (maintainVisibleContentPosition && anchorElement !== void 0) {
1285
- const newAdjust = anchorElement.coordinate - state.totalSizeBelowAnchor;
1286
- applyAdjustValue = -newAdjust;
1287
- state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
1288
- state.rowHeights.clear();
1289
- if (applyAdjustValue !== void 0) {
1290
- resultSize -= applyAdjustValue;
1291
- state.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
1292
- state.scroll -= diff;
1293
- });
1294
- }
1295
- }
1296
- set$(ctx, "totalSize", state.totalSize);
1297
- set$(ctx, "totalSizeWithScrollAdjust", resultSize);
1298
- if (alignItemsAtEnd) {
1299
- updateAlignItemsPaddingTop();
1300
- }
1301
- }, []);
1302
- const getRowHeight = (n) => {
1303
- const { rowHeights, data } = refState.current;
1304
- const numColumns = peek$(ctx, "numColumns");
1305
- if (numColumns === 1) {
1306
- const id = getId(n);
1307
- return getItemSize(id, n, data[n]);
1308
- }
1309
- if (rowHeights.has(n)) {
1310
- return rowHeights.get(n) || 0;
1311
- }
1312
- let rowHeight = 0;
1313
- const startEl = n * numColumns;
1314
- for (let i = startEl; i < startEl + numColumns && i < data.length; i++) {
1315
- const id = getId(i);
1316
- const size = getItemSize(id, i, data[i]);
1317
- rowHeight = Math.max(rowHeight, size);
1318
- }
1319
- rowHeights.set(n, rowHeight);
1320
- return rowHeight;
1321
- };
1322
- const buildElementPositionsBelowAnchor = () => {
1323
- const state = refState.current;
1324
- if (!state.anchorElement) {
1325
- return /* @__PURE__ */ new Map();
1326
- }
1327
- const anchorIndex = state.indexByKey.get(state.anchorElement.id);
1328
- if (anchorIndex === 0) {
1329
- return /* @__PURE__ */ new Map();
1330
- }
1331
- const map = state.belowAnchorElementPositions || /* @__PURE__ */ new Map();
1332
- const numColumns = peek$(ctx, "numColumns");
1333
- let top = state.anchorElement.coordinate;
1334
- for (let i = anchorIndex - 1; i >= 0; i--) {
1335
- const id = getId(i);
1336
- const rowNumber = Math.floor(i / numColumns);
1337
- if (i % numColumns === 0) {
1338
- top -= getRowHeight(rowNumber);
1339
- }
1340
- map.set(id, top);
1341
- }
1342
- return map;
1343
- };
1344
- const disableScrollJumps = (timeout) => {
1345
- const state = refState.current;
1346
- if (state.scrollingTo === void 0) {
1347
- state.disableScrollJumpsFrom = state.scroll - state.scrollAdjustHandler.getAppliedAdjust();
1348
- state.scrollHistory.length = 0;
1349
- setTimeout(() => {
1350
- state.disableScrollJumpsFrom = void 0;
1351
- if (state.scrollPending !== void 0 && state.scrollPending !== state.scroll) {
1352
- updateScroll(state.scrollPending);
1353
- }
1354
- }, timeout);
1355
- }
1356
- };
1357
- const getElementPositionBelowAchor = (id) => {
1358
- var _a;
1359
- const state = refState.current;
1360
- if (!refState.current.belowAnchorElementPositions) {
1361
- state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
1362
- }
1363
- const res = state.belowAnchorElementPositions.get(id);
1364
- if (res === void 0) {
1365
- console.warn(`Undefined position below anchor ${id} ${(_a = state.anchorElement) == null ? void 0 : _a.id}`);
1366
- return 0;
1367
- }
1368
- return res;
1369
- };
1370
- const fixGaps = useCallback(() => {
1371
- var _a;
1372
- const state = refState.current;
1373
- const { data, scrollLength, positions, startBuffered, endBuffered } = state;
1374
- const numColumns = peek$(ctx, "numColumns");
1375
- if (!data || scrollLength === 0 || numColumns > 1) {
1376
- return;
1756
+ } else {
1757
+ setIt();
1758
+ }
1759
+ }
1760
+
1761
+ // src/core/calculateItemsInView.ts
1762
+ function findCurrentStickyIndex(stickyArray, scroll, state) {
1763
+ var _a;
1764
+ const idCache = state.idCache;
1765
+ const positions = state.positions;
1766
+ for (let i = stickyArray.length - 1; i >= 0; i--) {
1767
+ const stickyId = (_a = idCache.get(stickyArray[i])) != null ? _a : getId(state, stickyArray[i]);
1768
+ const stickyPos = stickyId ? positions.get(stickyId) : void 0;
1769
+ if (stickyPos !== void 0 && scroll >= stickyPos) {
1770
+ return i;
1377
1771
  }
1378
- const numContainers = ctx.values.get("numContainers");
1379
- let numMeasurements = 0;
1380
- for (let i = 0; i < numContainers; i++) {
1381
- const itemKey = peek$(ctx, `containerItemKey${i}`);
1382
- const isSizeKnown = state.sizesKnown.get(itemKey);
1383
- if (itemKey && !isSizeKnown) {
1384
- const containerRef = ctx.viewRefs.get(i);
1385
- if (containerRef) {
1386
- let measured;
1387
- (_a = containerRef.current) == null ? void 0 : _a.measure((x, y, width, height) => {
1388
- measured = { width, height };
1389
- });
1390
- numMeasurements++;
1391
- if (measured) {
1392
- updateItemSize(
1393
- itemKey,
1394
- measured,
1395
- /*fromFixGaps*/
1396
- true
1397
- );
1398
- }
1399
- }
1400
- }
1772
+ }
1773
+ return -1;
1774
+ }
1775
+ function getActiveStickyIndices(ctx, state, stickyIndices) {
1776
+ return new Set(
1777
+ Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyIndices.has(idx))
1778
+ );
1779
+ }
1780
+ function handleStickyActivation(ctx, state, stickyIndices, stickyArray, scroll, needNewContainers, startBuffered, endBuffered) {
1781
+ var _a;
1782
+ const activeIndices = getActiveStickyIndices(ctx, state, stickyIndices);
1783
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1784
+ for (let offset = 0; offset <= 1; offset++) {
1785
+ const idx = currentStickyIdx - offset;
1786
+ if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
1787
+ const stickyIndex = stickyArray[idx];
1788
+ const stickyId = (_a = state.idCache.get(stickyIndex)) != null ? _a : getId(state, stickyIndex);
1789
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
1790
+ needNewContainers.push(stickyIndex);
1401
1791
  }
1402
- if (numMeasurements > 0) {
1403
- let top;
1404
- const diffs = /* @__PURE__ */ new Map();
1405
- for (let i = startBuffered; i <= endBuffered; i++) {
1406
- const id = getId(i);
1407
- if (top === void 0) {
1408
- top = positions.get(id);
1409
- }
1410
- if (positions.get(id) !== top) {
1411
- diffs.set(id, top - positions.get(id));
1412
- positions.set(id, top);
1413
- }
1414
- const size = getItemSize(id, i, data[i]);
1415
- const bottom = top + size;
1416
- top = bottom;
1417
- }
1418
- for (let i = 0; i < numContainers; i++) {
1419
- const itemKey = peek$(ctx, `containerItemKey${i}`);
1420
- const diff = diffs.get(itemKey);
1421
- if (diff) {
1422
- const prevPos = peek$(ctx, `containerPosition${i}`);
1423
- const newPos = prevPos.top + diff;
1424
- if (prevPos.top !== newPos) {
1425
- const pos = { ...prevPos };
1426
- pos.relativeCoordinate += diff;
1427
- pos.top += diff;
1428
- set$(ctx, `containerPosition${i}`, pos);
1429
- }
1430
- }
1792
+ }
1793
+ }
1794
+ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, pendingRemoval) {
1795
+ var _a, _b, _c;
1796
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1797
+ for (const containerIndex of state.stickyContainerPool) {
1798
+ const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
1799
+ const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
1800
+ if (itemIndex === void 0) continue;
1801
+ const arrayIdx = stickyArray.indexOf(itemIndex);
1802
+ if (arrayIdx === -1) continue;
1803
+ const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
1804
+ if (isRecentSticky) continue;
1805
+ const nextIndex = stickyArray[arrayIdx + 1];
1806
+ let shouldRecycle = false;
1807
+ if (nextIndex) {
1808
+ const nextId = (_a = state.idCache.get(nextIndex)) != null ? _a : getId(state, nextIndex);
1809
+ const nextPos = nextId ? state.positions.get(nextId) : void 0;
1810
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
1811
+ } else {
1812
+ const currentId = (_b = state.idCache.get(itemIndex)) != null ? _b : getId(state, itemIndex);
1813
+ if (currentId) {
1814
+ const currentPos = state.positions.get(currentId);
1815
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
1816
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
1431
1817
  }
1432
1818
  }
1433
- }, []);
1434
- const checkAllSizesKnown = useCallback(() => {
1435
- const { startBuffered, endBuffered, sizesKnown } = refState.current;
1436
- if (endBuffered !== null) {
1437
- let areAllKnown = true;
1438
- for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1439
- const key = getId(i);
1440
- areAllKnown && (areAllKnown = sizesKnown.has(key));
1441
- }
1442
- return areAllKnown;
1819
+ if (shouldRecycle) {
1820
+ pendingRemoval.push(containerIndex);
1443
1821
  }
1444
- return false;
1445
- }, []);
1446
- const calculateItemsInView = useCallback((isReset) => {
1447
- var _a;
1448
- const state = refState.current;
1822
+ }
1823
+ }
1824
+ function calculateItemsInView(ctx, state, params = {}) {
1825
+ unstable_batchedUpdates(() => {
1826
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1449
1827
  const {
1450
- data,
1828
+ columns,
1829
+ containerItemKeys,
1830
+ enableScrollForNextCalculateItemsInView,
1831
+ idCache,
1832
+ indexByKey,
1833
+ minIndexSizeChanged,
1834
+ positions,
1835
+ scrollForNextCalculateItemsInView,
1451
1836
  scrollLength,
1837
+ sizes,
1452
1838
  startBufferedId: startBufferedIdOrig,
1453
- positions,
1454
- columns,
1455
- scrollAdjustHandler,
1456
- scrollVelocity: speed
1839
+ viewabilityConfigCallbackPairs,
1840
+ props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, scrollBuffer }
1457
1841
  } = state;
1458
- if (!data || scrollLength === 0) {
1842
+ const { data } = state.props;
1843
+ const stickyIndicesArr = state.props.stickyIndicesArr || [];
1844
+ const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
1845
+ const prevNumContainers = peek$(ctx, "numContainers");
1846
+ if (!data || scrollLength === 0 || !prevNumContainers) {
1459
1847
  return;
1460
1848
  }
1461
- const totalSize = peek$(ctx, "totalSizeWithScrollAdjust");
1849
+ const totalSize = peek$(ctx, "totalSize");
1462
1850
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
1463
1851
  const numColumns = peek$(ctx, "numColumns");
1464
- const previousScrollAdjust = scrollAdjustHandler.getAppliedAdjust();
1465
- let scrollState = state.scroll;
1852
+ const previousScrollAdjust = 0;
1853
+ const { dataChanged, doMVCP } = params;
1854
+ const speed = getScrollVelocity(state);
1855
+ if (doMVCP || dataChanged) {
1856
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
1857
+ if (dataChanged) {
1858
+ indexByKey.clear();
1859
+ idCache.clear();
1860
+ positions.clear();
1861
+ }
1862
+ updateAllPositions(ctx, state, dataChanged);
1863
+ checkMVCP == null ? void 0 : checkMVCP();
1864
+ }
1466
1865
  const scrollExtra = 0;
1467
- const useAverageSize = !state.disableScrollJumpsFrom && speed >= 0 && peek$(ctx, "containersDidLayout");
1468
- if (!state.queuedInitialLayout && initialScroll) {
1866
+ const { queuedInitialLayout } = state;
1867
+ let { scroll: scrollState } = state;
1868
+ if (!queuedInitialLayout && initialScroll) {
1469
1869
  const updatedOffset = calculateOffsetWithOffsetPosition(
1470
- calculateOffsetForIndex(initialScroll.index),
1870
+ state,
1871
+ calculateOffsetForIndex(ctx, state, initialScroll.index),
1471
1872
  initialScroll
1472
1873
  );
1473
1874
  scrollState = updatedOffset;
@@ -1475,7 +1876,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1475
1876
  const scrollAdjustPad = -previousScrollAdjust - topPad;
1476
1877
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
1477
1878
  if (scroll + scrollLength > totalSize) {
1478
- scroll = totalSize - scrollLength;
1879
+ scroll = Math.max(0, totalSize - scrollLength);
1479
1880
  }
1480
1881
  if (ENABLE_DEBUG_VIEW) {
1481
1882
  set$(ctx, "debugRawScroll", scrollState);
@@ -1483,21 +1884,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1483
1884
  }
1484
1885
  let scrollBufferTop = scrollBuffer;
1485
1886
  let scrollBufferBottom = scrollBuffer;
1486
- if (Math.abs(speed) > 4) {
1487
- if (speed > 0) {
1488
- scrollBufferTop = scrollBuffer * 0.1;
1489
- scrollBufferBottom = scrollBuffer * 1.9;
1490
- } else {
1491
- scrollBufferTop = scrollBuffer * 1.9;
1492
- scrollBufferBottom = scrollBuffer * 0.1;
1493
- }
1887
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
1888
+ scrollBufferTop = scrollBuffer * 0.5;
1889
+ scrollBufferBottom = scrollBuffer * 1.5;
1890
+ } else {
1891
+ scrollBufferTop = scrollBuffer * 1.5;
1892
+ scrollBufferBottom = scrollBuffer * 0.5;
1494
1893
  }
1495
1894
  const scrollTopBuffered = scroll - scrollBufferTop;
1496
- const scrollBottom = scroll + scrollLength;
1895
+ const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
1497
1896
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
1498
- if (state.scrollForNextCalculateItemsInView) {
1499
- const { top: top2, bottom } = state.scrollForNextCalculateItemsInView;
1500
- if (scrollTopBuffered > top2 && scrollBottomBuffered < bottom) {
1897
+ if (scrollForNextCalculateItemsInView) {
1898
+ const { top, bottom } = scrollForNextCalculateItemsInView;
1899
+ if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
1501
1900
  return;
1502
1901
  }
1503
1902
  }
@@ -1506,80 +1905,50 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1506
1905
  let startBufferedId = null;
1507
1906
  let endNoBuffer = null;
1508
1907
  let endBuffered = null;
1509
- let loopStart = startBufferedIdOrig ? state.indexByKey.get(startBufferedIdOrig) || 0 : 0;
1510
- if (state.minIndexSizeChanged !== void 0) {
1511
- loopStart = Math.min(state.minIndexSizeChanged, loopStart);
1908
+ let loopStart = startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
1909
+ if (minIndexSizeChanged !== void 0) {
1910
+ loopStart = Math.min(minIndexSizeChanged, loopStart);
1512
1911
  state.minIndexSizeChanged = void 0;
1513
1912
  }
1514
- const anchorElementIndex = getAnchorElementIndex();
1515
1913
  for (let i = loopStart; i >= 0; i--) {
1516
- const id = getId(i);
1517
- let newPosition;
1518
- if (maintainVisibleContentPosition && anchorElementIndex && i < anchorElementIndex) {
1519
- newPosition = getElementPositionBelowAchor(id);
1520
- if (newPosition !== void 0) {
1521
- positions.set(id, newPosition);
1522
- }
1523
- }
1524
- const top2 = newPosition || positions.get(id);
1525
- if (top2 !== void 0) {
1526
- const size = getItemSize(id, i, data[i], useAverageSize);
1527
- const bottom = top2 + size;
1528
- if (bottom > scroll - scrollBuffer) {
1529
- loopStart = i;
1530
- } else {
1531
- break;
1532
- }
1914
+ const id = (_a = idCache.get(i)) != null ? _a : getId(state, i);
1915
+ const top = positions.get(id);
1916
+ const size = (_b = sizes.get(id)) != null ? _b : getItemSize(state, id, i, data[i]);
1917
+ const bottom = top + size;
1918
+ if (bottom > scroll - scrollBuffer) {
1919
+ loopStart = i;
1920
+ } else {
1921
+ break;
1533
1922
  }
1534
1923
  }
1535
1924
  const loopStartMod = loopStart % numColumns;
1536
1925
  if (loopStartMod > 0) {
1537
1926
  loopStart -= loopStartMod;
1538
1927
  }
1539
- let top = void 0;
1540
- let column = 1;
1541
- let maxSizeInRow = 0;
1542
- const getInitialTop = (i) => {
1543
- var _a2;
1544
- const id = getId(i);
1545
- let topOffset = 0;
1546
- if (positions.get(id)) {
1547
- topOffset = positions.get(id);
1548
- }
1549
- if (id === ((_a2 = state.anchorElement) == null ? void 0 : _a2.id)) {
1550
- topOffset = state.anchorElement.coordinate;
1551
- }
1552
- return topOffset;
1553
- };
1554
1928
  let foundEnd = false;
1555
1929
  let nextTop;
1556
1930
  let nextBottom;
1557
- const prevNumContainers = ctx.values.get("numContainers");
1558
1931
  let maxIndexRendered = 0;
1559
1932
  for (let i = 0; i < prevNumContainers; i++) {
1560
1933
  const key = peek$(ctx, `containerItemKey${i}`);
1561
1934
  if (key !== void 0) {
1562
- const index = state.indexByKey.get(key);
1935
+ const index = indexByKey.get(key);
1563
1936
  maxIndexRendered = Math.max(maxIndexRendered, index);
1564
1937
  }
1565
1938
  }
1566
- for (let i = Math.max(0, loopStart); i < data.length && (!foundEnd || i <= maxIndexRendered); i++) {
1567
- const id = getId(i);
1568
- const size = getItemSize(id, i, data[i], useAverageSize);
1569
- maxSizeInRow = Math.max(maxSizeInRow, size);
1570
- if (top === void 0 || id === ((_a = state.anchorElement) == null ? void 0 : _a.id)) {
1571
- top = getInitialTop(i);
1572
- }
1573
- if (positions.get(id) !== top) {
1574
- positions.set(id, top);
1575
- }
1576
- if (columns.get(id) !== column) {
1577
- columns.set(id, column);
1578
- }
1939
+ let firstFullyOnScreenIndex;
1940
+ const dataLength = data.length;
1941
+ for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
1942
+ const id = (_c = idCache.get(i)) != null ? _c : getId(state, i);
1943
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(state, id, i, data[i]);
1944
+ const top = positions.get(id);
1579
1945
  if (!foundEnd) {
1580
1946
  if (startNoBuffer === null && top + size > scroll) {
1581
1947
  startNoBuffer = i;
1582
1948
  }
1949
+ if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10) {
1950
+ firstFullyOnScreenIndex = i;
1951
+ }
1583
1952
  if (startBuffered === null && top + size > scrollTopBuffered) {
1584
1953
  startBuffered = i;
1585
1954
  startBufferedId = id;
@@ -1591,202 +1960,213 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1591
1960
  }
1592
1961
  if (top <= scrollBottomBuffered) {
1593
1962
  endBuffered = i;
1594
- nextBottom = top + maxSizeInRow - scrollLength;
1963
+ nextBottom = top + size;
1595
1964
  } else {
1596
1965
  foundEnd = true;
1597
1966
  }
1598
1967
  }
1599
1968
  }
1600
- column++;
1601
- if (column > numColumns) {
1602
- top += maxSizeInRow;
1603
- column = 1;
1604
- maxSizeInRow = 0;
1605
- }
1969
+ }
1970
+ const idsInView = [];
1971
+ for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
1972
+ const id = (_e = idCache.get(i)) != null ? _e : getId(state, i);
1973
+ idsInView.push(id);
1606
1974
  }
1607
1975
  Object.assign(state, {
1976
+ endBuffered,
1977
+ endNoBuffer,
1978
+ firstFullyOnScreenIndex,
1979
+ idsInView,
1608
1980
  startBuffered,
1609
1981
  startBufferedId,
1610
- startNoBuffer,
1611
- endBuffered,
1612
- endNoBuffer
1982
+ startNoBuffer
1613
1983
  });
1614
- if (state.enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0 && state.disableScrollJumpsFrom === void 0) {
1984
+ if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
1615
1985
  state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
1616
- top: nextTop,
1617
- bottom: nextBottom
1986
+ bottom: nextBottom,
1987
+ top: nextTop
1618
1988
  } : void 0;
1619
1989
  }
1990
+ const numContainers = peek$(ctx, "numContainers");
1991
+ const pendingRemoval = [];
1992
+ if (dataChanged) {
1993
+ for (let i = 0; i < numContainers; i++) {
1994
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1995
+ if (!keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
1996
+ pendingRemoval.push(i);
1997
+ }
1998
+ }
1999
+ }
1620
2000
  if (startBuffered !== null && endBuffered !== null) {
1621
- let numContainers = prevNumContainers;
2001
+ let numContainers2 = prevNumContainers;
1622
2002
  const needNewContainers = [];
1623
- const isContained = (i) => {
1624
- const id = getId(i);
1625
- for (let j = 0; j < numContainers; j++) {
1626
- const key = peek$(ctx, `containerItemKey${j}`);
1627
- if (key === id) {
1628
- return true;
1629
- }
1630
- }
1631
- };
1632
2003
  for (let i = startBuffered; i <= endBuffered; i++) {
1633
- if (!isContained(i)) {
2004
+ const id = (_f = idCache.get(i)) != null ? _f : getId(state, i);
2005
+ if (!containerItemKeys.has(id)) {
1634
2006
  needNewContainers.push(i);
1635
2007
  }
1636
2008
  }
2009
+ if (stickyIndicesArr.length > 0) {
2010
+ handleStickyActivation(
2011
+ ctx,
2012
+ state,
2013
+ stickyIndicesSet,
2014
+ stickyIndicesArr,
2015
+ scroll,
2016
+ needNewContainers,
2017
+ startBuffered,
2018
+ endBuffered
2019
+ );
2020
+ }
1637
2021
  if (needNewContainers.length > 0) {
2022
+ const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2023
+ const itemType = getItemType(data[i], i);
2024
+ return itemType ? String(itemType) : "";
2025
+ }) : void 0;
1638
2026
  const availableContainers = findAvailableContainers(
2027
+ ctx,
2028
+ state,
1639
2029
  needNewContainers.length,
1640
2030
  startBuffered,
1641
- endBuffered
2031
+ endBuffered,
2032
+ pendingRemoval,
2033
+ requiredItemTypes,
2034
+ needNewContainers
1642
2035
  );
1643
2036
  for (let idx = 0; idx < needNewContainers.length; idx++) {
1644
2037
  const i = needNewContainers[idx];
1645
2038
  const containerIndex = availableContainers[idx];
1646
- const id = getId(i);
2039
+ const id = (_g = idCache.get(i)) != null ? _g : getId(state, i);
2040
+ const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2041
+ if (oldKey && oldKey !== id) {
2042
+ containerItemKeys.delete(oldKey);
2043
+ }
1647
2044
  set$(ctx, `containerItemKey${containerIndex}`, id);
1648
2045
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
1649
- if (containerIndex >= numContainers) {
1650
- numContainers = containerIndex + 1;
2046
+ if (requiredItemTypes) {
2047
+ state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2048
+ }
2049
+ containerItemKeys.add(id);
2050
+ if (stickyIndicesSet.has(i)) {
2051
+ set$(ctx, `containerSticky${containerIndex}`, true);
2052
+ const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2053
+ set$(ctx, `containerStickyOffset${containerIndex}`, new Animated.Value(topPadding));
2054
+ state.stickyContainerPool.add(containerIndex);
2055
+ } else {
2056
+ state.stickyContainerPool.delete(containerIndex);
2057
+ }
2058
+ if (containerIndex >= numContainers2) {
2059
+ numContainers2 = containerIndex + 1;
1651
2060
  }
1652
2061
  }
1653
- if (numContainers !== prevNumContainers) {
1654
- set$(ctx, "numContainers", numContainers);
1655
- if (numContainers > peek$(ctx, "numContainersPooled")) {
1656
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2062
+ if (numContainers2 !== prevNumContainers) {
2063
+ set$(ctx, "numContainers", numContainers2);
2064
+ if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2065
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
1657
2066
  }
1658
2067
  }
1659
2068
  }
1660
- for (let i = 0; i < numContainers; i++) {
1661
- const itemKey = peek$(ctx, `containerItemKey${i}`);
1662
- const itemIndex = state.indexByKey.get(itemKey);
2069
+ }
2070
+ if (stickyIndicesArr.length > 0) {
2071
+ handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, pendingRemoval);
2072
+ }
2073
+ for (let i = 0; i < numContainers; i++) {
2074
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
2075
+ if (pendingRemoval.includes(i)) {
2076
+ if (itemKey) {
2077
+ containerItemKeys.delete(itemKey);
2078
+ }
2079
+ state.containerItemTypes.delete(i);
2080
+ if (state.stickyContainerPool.has(i)) {
2081
+ set$(ctx, `containerSticky${i}`, false);
2082
+ set$(ctx, `containerStickyOffset${i}`, void 0);
2083
+ state.stickyContainerPool.delete(i);
2084
+ }
2085
+ set$(ctx, `containerItemKey${i}`, void 0);
2086
+ set$(ctx, `containerItemData${i}`, void 0);
2087
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2088
+ set$(ctx, `containerColumn${i}`, -1);
2089
+ } else {
2090
+ const itemIndex = indexByKey.get(itemKey);
1663
2091
  const item = data[itemIndex];
1664
2092
  if (item !== void 0) {
1665
- const id = getId(itemIndex);
2093
+ const id = (_h = idCache.get(itemIndex)) != null ? _h : getId(state, itemIndex);
1666
2094
  const position = positions.get(id);
1667
2095
  if (position === void 0) {
1668
- set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
2096
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
1669
2097
  } else {
1670
- const pos = {
1671
- type: "top",
1672
- relativeCoordinate: positions.get(id),
1673
- top: positions.get(id)
1674
- };
1675
- const column2 = columns.get(id) || 1;
1676
- if (maintainVisibleContentPosition && itemIndex < anchorElementIndex) {
1677
- const currentRow = Math.floor(itemIndex / numColumns);
1678
- const rowHeight = getRowHeight(currentRow);
1679
- const elementHeight = getItemSize(id, itemIndex, data[i]);
1680
- const diff = rowHeight - elementHeight;
1681
- pos.relativeCoordinate = pos.top + getRowHeight(currentRow) - diff;
1682
- pos.type = "bottom";
1683
- }
2098
+ const column = columns.get(id) || 1;
1684
2099
  const prevPos = peek$(ctx, `containerPosition${i}`);
1685
2100
  const prevColumn = peek$(ctx, `containerColumn${i}`);
1686
2101
  const prevData = peek$(ctx, `containerItemData${i}`);
1687
- if (!prevPos || pos.relativeCoordinate > POSITION_OUT_OF_VIEW && pos.top !== prevPos.top) {
1688
- set$(ctx, `containerPosition${i}`, pos);
2102
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
2103
+ set$(ctx, `containerPosition${i}`, position);
1689
2104
  }
1690
- if (column2 >= 0 && column2 !== prevColumn) {
1691
- set$(ctx, `containerColumn${i}`, column2);
2105
+ if (column >= 0 && column !== prevColumn) {
2106
+ set$(ctx, `containerColumn${i}`, column);
1692
2107
  }
1693
- if (prevData !== item) {
2108
+ if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
1694
2109
  set$(ctx, `containerItemData${i}`, data[itemIndex]);
1695
2110
  }
1696
2111
  }
1697
2112
  }
1698
2113
  }
1699
2114
  }
1700
- if (!state.queuedInitialLayout && endBuffered !== null) {
1701
- if (checkAllSizesKnown()) {
1702
- setDidLayout();
2115
+ if (!queuedInitialLayout && endBuffered !== null) {
2116
+ if (checkAllSizesKnown(state)) {
2117
+ setDidLayout(ctx, state);
1703
2118
  }
1704
2119
  }
1705
- if (state.viewabilityConfigCallbackPairs) {
1706
- updateViewableItems(
1707
- state,
1708
- ctx,
1709
- state.viewabilityConfigCallbackPairs,
1710
- getId,
1711
- scrollLength,
1712
- startNoBuffer,
1713
- endNoBuffer
1714
- );
1715
- }
1716
- }, []);
1717
- const setPaddingTop = ({
1718
- stylePaddingTop,
1719
- alignItemsPaddingTop
1720
- }) => {
1721
- if (stylePaddingTop !== void 0) {
1722
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1723
- if (stylePaddingTop < prevStylePaddingTop) {
1724
- const prevTotalSize = peek$(ctx, "totalSizeWithScrollAdjust") || 0;
1725
- set$(ctx, "totalSizeWithScrollAdjust", prevTotalSize + prevStylePaddingTop);
1726
- setTimeout(() => {
1727
- set$(ctx, "totalSizeWithScrollAdjust", prevTotalSize);
1728
- }, 16);
1729
- }
1730
- set$(ctx, "stylePaddingTop", stylePaddingTop);
2120
+ if (viewabilityConfigCallbackPairs) {
2121
+ updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
1731
2122
  }
1732
- if (alignItemsPaddingTop !== void 0) {
1733
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
2123
+ });
2124
+ }
2125
+
2126
+ // src/core/doInitialAllocateContainers.ts
2127
+ function doInitialAllocateContainers(ctx, state) {
2128
+ var _a;
2129
+ const {
2130
+ scrollLength,
2131
+ props: { data, getEstimatedItemSize, getItemType, scrollBuffer, numColumns, estimatedItemSize }
2132
+ } = state;
2133
+ if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
2134
+ const averageItemSize = getEstimatedItemSize ? getEstimatedItemSize(0, data[0], getItemType ? (_a = getItemType(data[0], 0)) != null ? _a : "" : "") : estimatedItemSize;
2135
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
2136
+ for (let i = 0; i < numContainers; i++) {
2137
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2138
+ set$(ctx, `containerColumn${i}`, -1);
1734
2139
  }
1735
- set$(
1736
- ctx,
1737
- "paddingTop",
1738
- (stylePaddingTop != null ? stylePaddingTop : peek$(ctx, "stylePaddingTop")) + (alignItemsPaddingTop != null ? alignItemsPaddingTop : peek$(ctx, "alignItemsPaddingTop"))
1739
- );
1740
- };
1741
- const updateAlignItemsPaddingTop = () => {
1742
- if (alignItemsAtEnd) {
1743
- const { data, scrollLength } = refState.current;
1744
- let alignItemsPaddingTop = 0;
1745
- if ((data == null ? void 0 : data.length) > 0) {
1746
- const contentSize = getContentSize(ctx);
1747
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
2140
+ set$(ctx, "numContainers", numContainers);
2141
+ set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2142
+ if (!IsNewArchitecture) {
2143
+ if (state.props.initialScroll) {
2144
+ requestAnimationFrame(() => {
2145
+ calculateItemsInView(ctx, state);
2146
+ });
2147
+ } else {
2148
+ calculateItemsInView(ctx, state);
1748
2149
  }
1749
- setPaddingTop({ alignItemsPaddingTop });
1750
- }
1751
- };
1752
- const finishScrollTo = () => {
1753
- const state = refState.current;
1754
- if (state) {
1755
- state.scrollingTo = void 0;
1756
- state.scrollAdjustHandler.setDisableAdjust(false);
1757
- state.scrollHistory.length = 0;
1758
- calculateItemsInView();
1759
2150
  }
1760
- };
1761
- const scrollTo = (params = {}) => {
1762
- var _a;
1763
- const state = refState.current;
1764
- const { animated } = params;
1765
- const offset = calculateOffsetWithOffsetPosition(params.offset, params);
1766
- state.scrollAdjustHandler.setDisableAdjust(true);
1767
- state.scrollHistory.length = 0;
1768
- state.scrollingTo = params;
1769
- state.scrollPending = offset;
1770
- (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1771
- x: horizontal ? offset : 0,
1772
- y: horizontal ? 0 : offset,
1773
- animated: !!animated
1774
- });
1775
- if (!animated) {
1776
- requestAnimationFrame(finishScrollTo);
2151
+ return true;
2152
+ }
2153
+ }
2154
+
2155
+ // src/core/doMaintainScrollAtEnd.ts
2156
+ function doMaintainScrollAtEnd(ctx, state, animated) {
2157
+ const {
2158
+ refScroller,
2159
+ props: { maintainScrollAtEnd }
2160
+ } = state;
2161
+ if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2162
+ const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2163
+ if (paddingTop > 0) {
2164
+ state.scroll = 0;
1777
2165
  }
1778
- };
1779
- const doMaintainScrollAtEnd = (animated) => {
1780
- const state = refState.current;
1781
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
1782
- const paddingTop = peek$(ctx, "alignItemsPaddingTop");
1783
- if (paddingTop > 0) {
1784
- state.scroll = 0;
1785
- }
1786
- state.disableScrollJumpsFrom = void 0;
1787
- requestAnimationFrame(() => {
1788
- var _a;
1789
- state.maintainingScrollAtEnd = true;
2166
+ requestAnimationFrame(() => {
2167
+ var _a;
2168
+ if (state == null ? void 0 : state.isAtEnd) {
2169
+ state.maintainingScrollAtEnd = true;
1790
2170
  (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
1791
2171
  animated
1792
2172
  });
@@ -1796,290 +2176,661 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
1796
2176
  },
1797
2177
  0
1798
2178
  );
1799
- });
1800
- return true;
1801
- }
1802
- };
1803
- const checkThreshold = (distance, atThreshold, threshold, isReached, isBlockedByTimer, onReached, blockTimer) => {
1804
- const distanceAbs = Math.abs(distance);
1805
- const isAtThreshold = atThreshold || distanceAbs < threshold;
1806
- if (!isReached && !isBlockedByTimer) {
1807
- if (isAtThreshold) {
1808
- onReached == null ? void 0 : onReached(distance);
1809
- blockTimer == null ? void 0 : blockTimer(true);
1810
- setTimeout(() => {
1811
- blockTimer == null ? void 0 : blockTimer(false);
1812
- }, 700);
1813
- return true;
1814
- }
1815
- } else {
1816
- if (distance >= 1.3 * threshold) {
1817
- return false;
1818
2179
  }
2180
+ });
2181
+ return true;
2182
+ }
2183
+ }
2184
+
2185
+ // src/utils/checkAtTop.ts
2186
+ function checkAtTop(state) {
2187
+ if (!state) {
2188
+ return;
2189
+ }
2190
+ const {
2191
+ scrollLength,
2192
+ scroll,
2193
+ props: { onStartReachedThreshold }
2194
+ } = state;
2195
+ const distanceFromTop = scroll;
2196
+ state.isAtStart = distanceFromTop <= 0;
2197
+ state.isStartReached = checkThreshold(
2198
+ distanceFromTop,
2199
+ false,
2200
+ onStartReachedThreshold * scrollLength,
2201
+ state.isStartReached,
2202
+ state.startReachedBlockedByTimer,
2203
+ (distance) => {
2204
+ var _a, _b;
2205
+ return (_b = (_a = state.props).onStartReached) == null ? void 0 : _b.call(_a, { distanceFromStart: distance });
2206
+ },
2207
+ (block) => {
2208
+ state.startReachedBlockedByTimer = block;
1819
2209
  }
1820
- return isReached;
1821
- };
1822
- const checkAtBottom = () => {
1823
- if (!refState.current) {
2210
+ );
2211
+ }
2212
+
2213
+ // src/core/handleLayout.ts
2214
+ function handleLayout(ctx, state, layout, setCanRender) {
2215
+ const { maintainScrollAtEnd } = state.props;
2216
+ const scrollLength = layout[state.props.horizontal ? "width" : "height"];
2217
+ const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
2218
+ const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
2219
+ state.lastLayout = layout;
2220
+ const didChange = scrollLength !== state.scrollLength;
2221
+ const prevOtherAxisSize = state.otherAxisSize;
2222
+ state.scrollLength = scrollLength;
2223
+ state.otherAxisSize = otherAxisSize;
2224
+ state.lastBatchingAction = Date.now();
2225
+ state.scrollForNextCalculateItemsInView = void 0;
2226
+ doInitialAllocateContainers(ctx, state);
2227
+ if (needsCalculate) {
2228
+ calculateItemsInView(ctx, state, { doMVCP: true });
2229
+ }
2230
+ if (didChange || otherAxisSize !== prevOtherAxisSize) {
2231
+ set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2232
+ }
2233
+ if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2234
+ doMaintainScrollAtEnd(ctx, state, false);
2235
+ }
2236
+ updateAlignItemsPaddingTop(ctx, state);
2237
+ checkAtBottom(ctx, state);
2238
+ checkAtTop(state);
2239
+ if (state) {
2240
+ state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2241
+ }
2242
+ if (__DEV__ && scrollLength === 0) {
2243
+ warnDevOnce(
2244
+ "height0",
2245
+ `List ${state.props.horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
2246
+ );
2247
+ }
2248
+ setCanRender(true);
2249
+ }
2250
+
2251
+ // src/core/onScroll.ts
2252
+ function onScroll(ctx, state, event) {
2253
+ var _a, _b, _c;
2254
+ const {
2255
+ scrollProcessingEnabled,
2256
+ props: { onScroll: onScrollProp }
2257
+ } = state;
2258
+ if (scrollProcessingEnabled === false) {
2259
+ return;
2260
+ }
2261
+ 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) {
2262
+ return;
2263
+ }
2264
+ const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2265
+ const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
2266
+ if (ignoreScrollFromMVCP && !state.scrollingTo) {
2267
+ const { lt, gt } = ignoreScrollFromMVCP;
2268
+ if (lt && newScroll < lt || gt && newScroll > gt) {
1824
2269
  return;
1825
2270
  }
1826
- const { queuedInitialLayout, scrollLength, scroll, maintainingScrollAtEnd } = refState.current;
1827
- const contentSize = getContentSize(ctx);
1828
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1829
- const distanceFromEnd = contentSize - scroll - scrollLength;
1830
- const isContentLess = contentSize < scrollLength;
1831
- refState.current.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1832
- refState.current.isEndReached = checkThreshold(
1833
- distanceFromEnd,
1834
- isContentLess,
1835
- onEndReachedThreshold * scrollLength,
1836
- refState.current.isEndReached,
1837
- refState.current.endReachedBlockedByTimer,
1838
- (distance) => {
1839
- var _a, _b;
1840
- return (_b = (_a = callbacks.current).onEndReached) == null ? void 0 : _b.call(_a, { distanceFromEnd: distance });
1841
- },
1842
- (block) => {
1843
- refState.current.endReachedBlockedByTimer = block;
1844
- }
1845
- );
1846
- }
1847
- };
1848
- const checkAtTop = () => {
1849
- if (!refState.current) {
1850
- return;
2271
+ }
2272
+ state.scrollPending = newScroll;
2273
+ updateScroll(ctx, state, newScroll);
2274
+ onScrollProp == null ? void 0 : onScrollProp(event);
2275
+ }
2276
+ function updateScroll(ctx, state, newScroll) {
2277
+ const scrollingTo = state.scrollingTo;
2278
+ state.hasScrolled = true;
2279
+ state.lastBatchingAction = Date.now();
2280
+ const currentTime = Date.now();
2281
+ if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
2282
+ state.scrollHistory.push({ scroll: newScroll, time: currentTime });
2283
+ }
2284
+ if (state.scrollHistory.length > 5) {
2285
+ state.scrollHistory.shift();
2286
+ }
2287
+ state.scrollPrev = state.scroll;
2288
+ state.scrollPrevTime = state.scrollTime;
2289
+ state.scroll = newScroll;
2290
+ state.scrollTime = currentTime;
2291
+ if (Math.abs(state.scroll - state.scrollPrev) > 2) {
2292
+ calculateItemsInView(ctx, state);
2293
+ checkAtBottom(ctx, state);
2294
+ checkAtTop(state);
2295
+ }
2296
+ }
2297
+
2298
+ // src/core/ScrollAdjustHandler.ts
2299
+ var ScrollAdjustHandler = class {
2300
+ constructor(ctx) {
2301
+ this.appliedAdjust = 0;
2302
+ this.mounted = false;
2303
+ this.context = ctx;
2304
+ }
2305
+ requestAdjust(add) {
2306
+ const oldAdjustTop = peek$(this.context, "scrollAdjust") || 0;
2307
+ this.appliedAdjust = add + oldAdjustTop;
2308
+ const set = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2309
+ if (this.mounted) {
2310
+ set();
2311
+ } else {
2312
+ requestAnimationFrame(set);
1851
2313
  }
1852
- const { scrollLength, scroll } = refState.current;
1853
- const distanceFromTop = scroll;
1854
- refState.current.isAtStart = distanceFromTop <= 0;
1855
- refState.current.isStartReached = checkThreshold(
1856
- distanceFromTop,
1857
- false,
1858
- onStartReachedThreshold * scrollLength,
1859
- refState.current.isStartReached,
1860
- refState.current.startReachedBlockedByTimer,
1861
- (distance) => {
1862
- var _a, _b;
1863
- return (_b = (_a = callbacks.current).onStartReached) == null ? void 0 : _b.call(_a, { distanceFromStart: distance });
1864
- },
1865
- (block) => {
1866
- refState.current.startReachedBlockedByTimer = block;
1867
- }
1868
- );
1869
- };
1870
- const checkResetContainers = (isFirst2) => {
1871
- const state = refState.current;
1872
- if (state) {
1873
- state.data = dataProp;
1874
- if (!isFirst2) {
1875
- const totalSizeBefore = state.previousTotalSize;
1876
- const totalSizeAfter = state.totalSize;
1877
- const scrollDiff = state.scroll - state.scrollPrev;
1878
- const sizeDiff = totalSizeAfter - totalSizeBefore;
1879
- if (Math.abs(scrollDiff - sizeDiff) < 10) {
1880
- disableScrollJumps(1e3);
1881
- }
1882
- const numContainers = peek$(ctx, "numContainers");
2314
+ }
2315
+ setMounted() {
2316
+ this.mounted = true;
2317
+ }
2318
+ };
2319
+
2320
+ // src/core/updateItemSize.ts
2321
+ function updateItemSizes(ctx, state, itemUpdates) {
2322
+ var _a;
2323
+ const {
2324
+ props: {
2325
+ horizontal,
2326
+ maintainVisibleContentPosition,
2327
+ suggestEstimatedItemSize,
2328
+ onItemSizeChanged,
2329
+ data,
2330
+ maintainScrollAtEnd
2331
+ }
2332
+ } = state;
2333
+ if (!data) return;
2334
+ const containersDidLayout = peek$(ctx, "containersDidLayout");
2335
+ let needsRecalculate = !containersDidLayout;
2336
+ let shouldMaintainScrollAtEnd = false;
2337
+ let minIndexSizeChanged;
2338
+ let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2339
+ for (const { itemKey, sizeObj } of itemUpdates) {
2340
+ const index = state.indexByKey.get(itemKey);
2341
+ const prevSizeKnown = state.sizesKnown.get(itemKey);
2342
+ const diff = updateOneItemSize(state, itemKey, sizeObj);
2343
+ const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2344
+ if (diff !== 0) {
2345
+ minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2346
+ if (((_a = state.scrollingTo) == null ? void 0 : _a.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
2347
+ requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
2348
+ }
2349
+ const { startBuffered, endBuffered } = state;
2350
+ needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2351
+ if (!needsRecalculate) {
2352
+ const numContainers = ctx.values.get("numContainers");
1883
2353
  for (let i = 0; i < numContainers; i++) {
1884
- const itemKey = peek$(ctx, `containerItemKey${i}`);
1885
- if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
1886
- set$(ctx, `containerItemKey${i}`, void 0);
1887
- set$(ctx, `containerItemData${i}`, void 0);
1888
- set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
1889
- set$(ctx, `containerColumn${i}`, -1);
2354
+ if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2355
+ needsRecalculate = true;
2356
+ break;
1890
2357
  }
1891
2358
  }
1892
- if (!keyExtractorProp) {
1893
- state.positions.clear();
1894
- }
1895
- calculateItemsInView(
1896
- /*isReset*/
1897
- true
1898
- );
1899
- const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
1900
- if (!didMaintainScrollAtEnd && dataProp.length > state.data.length) {
1901
- state.isEndReached = false;
1902
- }
1903
- if (!didMaintainScrollAtEnd) {
1904
- checkAtTop();
1905
- checkAtBottom();
1906
- }
2359
+ }
2360
+ if (state.needsOtherAxisSize) {
2361
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
2362
+ maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
2363
+ }
2364
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2365
+ shouldMaintainScrollAtEnd = true;
2366
+ }
2367
+ onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2368
+ index,
2369
+ itemData: state.props.data[index],
2370
+ itemKey,
2371
+ previous: size - diff,
2372
+ size
2373
+ });
2374
+ }
2375
+ }
2376
+ if (minIndexSizeChanged !== void 0) {
2377
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
2378
+ }
2379
+ if (__DEV__ && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
2380
+ if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
2381
+ state.timeoutSizeMessage = setTimeout(() => {
2382
+ var _a2;
2383
+ state.timeoutSizeMessage = void 0;
2384
+ const num = state.sizesKnown.size;
2385
+ const avg = (_a2 = state.averageSizes[""]) == null ? void 0 : _a2.avg;
2386
+ console.warn(
2387
+ `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
2388
+ );
2389
+ }, 1e3);
2390
+ }
2391
+ const cur = peek$(ctx, "otherAxisSize");
2392
+ if (!cur || maxOtherAxisSize > cur) {
2393
+ set$(ctx, "otherAxisSize", maxOtherAxisSize);
2394
+ }
2395
+ if (containersDidLayout || checkAllSizesKnown(state)) {
2396
+ if (needsRecalculate) {
2397
+ state.scrollForNextCalculateItemsInView = void 0;
2398
+ calculateItemsInView(ctx, state, { doMVCP: true });
2399
+ }
2400
+ if (shouldMaintainScrollAtEnd) {
2401
+ if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
2402
+ doMaintainScrollAtEnd(ctx, state, false);
1907
2403
  }
1908
2404
  }
1909
- };
1910
- const calcTotalSizesAndPositions = ({ forgetPositions = false }) => {
1911
- var _a, _b;
1912
- const state = refState.current;
1913
- let totalSize = 0;
1914
- let totalSizeBelowIndex = 0;
1915
- const indexByKey = /* @__PURE__ */ new Map();
1916
- const newPositions = /* @__PURE__ */ new Map();
1917
- let column = 1;
1918
- let maxSizeInRow = 0;
1919
- const numColumns = (_a = peek$(ctx, "numColumns")) != null ? _a : numColumnsProp;
1920
- if (!state) {
2405
+ }
2406
+ }
2407
+ function updateItemSize(ctx, state, itemKey, sizeObj) {
2408
+ var _a;
2409
+ const {
2410
+ queuedItemSizeUpdates,
2411
+ queuedItemSizeUpdatesWaiting,
2412
+ sizesKnown,
2413
+ props: { getFixedItemSize, getItemType }
2414
+ } = state;
2415
+ if (getFixedItemSize) {
2416
+ const index = state.indexByKey.get(itemKey);
2417
+ if (index === void 0) {
1921
2418
  return;
1922
2419
  }
1923
- for (let i = 0; i < dataProp.length; i++) {
1924
- const key = getId(i);
1925
- if (__DEV__) {
1926
- if (indexByKey.has(key)) {
1927
- console.error(
1928
- `[legend-list] Error: Detected overlapping key (${key}) which causes missing items and gaps and other terrrible things. Check that keyExtractor returns unique values.`
1929
- );
1930
- }
2420
+ const itemData = state.props.data[index];
2421
+ if (itemData === void 0) {
2422
+ return;
2423
+ }
2424
+ const type = getItemType ? (_a = getItemType(itemData, index)) != null ? _a : "" : "";
2425
+ const size = getFixedItemSize(index, itemData, type);
2426
+ if (size !== void 0 && size === sizesKnown.get(itemKey)) {
2427
+ return;
2428
+ }
2429
+ }
2430
+ const containersDidLayout = peek$(ctx, "containersDidLayout");
2431
+ const speed = getScrollVelocity(state);
2432
+ if (!containersDidLayout || !queuedItemSizeUpdatesWaiting || Math.abs(speed) < 1) {
2433
+ updateItemSizes(ctx, state, [{ itemKey, sizeObj }]);
2434
+ if (containersDidLayout) {
2435
+ state.queuedItemSizeUpdatesWaiting = true;
2436
+ requestAnimationFrame(() => {
2437
+ state.queuedItemSizeUpdatesWaiting = false;
2438
+ updateItemSizes(ctx, state, queuedItemSizeUpdates);
2439
+ queuedItemSizeUpdates.length = 0;
2440
+ });
2441
+ }
2442
+ } else {
2443
+ queuedItemSizeUpdates.push({ itemKey, sizeObj });
2444
+ }
2445
+ }
2446
+ function updateOneItemSize(state, itemKey, sizeObj) {
2447
+ var _a;
2448
+ const {
2449
+ sizes,
2450
+ indexByKey,
2451
+ sizesKnown,
2452
+ averageSizes,
2453
+ props: { data, horizontal, getEstimatedItemSize, getItemType, getFixedItemSize }
2454
+ } = state;
2455
+ if (!data) return 0;
2456
+ const index = indexByKey.get(itemKey);
2457
+ const prevSize = getItemSize(state, itemKey, index, data);
2458
+ const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2459
+ sizesKnown.set(itemKey, size);
2460
+ if (!getEstimatedItemSize && !getFixedItemSize) {
2461
+ const itemType = getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : "";
2462
+ let averages = averageSizes[itemType];
2463
+ if (!averages) {
2464
+ averages = averageSizes[itemType] = { avg: 0, num: 0 };
2465
+ }
2466
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2467
+ averages.num++;
2468
+ }
2469
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2470
+ sizes.set(itemKey, size);
2471
+ return size - prevSize;
2472
+ }
2473
+ return 0;
2474
+ }
2475
+ var useCombinedRef = (...refs) => {
2476
+ const callback = useCallback((element) => {
2477
+ for (const ref of refs) {
2478
+ if (!ref) {
2479
+ continue;
1931
2480
  }
1932
- indexByKey.set(key, i);
1933
- if (!forgetPositions && state.positions.get(key) != null && state.indexByKey.get(key) === i) {
1934
- newPositions.set(key, state.positions.get(key));
1935
- }
1936
- }
1937
- state.indexByKey = indexByKey;
1938
- state.positions = newPositions;
1939
- if (!forgetPositions && !isFirst) {
1940
- if (maintainVisibleContentPosition) {
1941
- if (state.anchorElement == null || indexByKey.get(state.anchorElement.id) == null) {
1942
- if (dataProp.length) {
1943
- const newAnchorElement = {
1944
- coordinate: 0,
1945
- id: getId(0)
1946
- };
1947
- state.anchorElement = newAnchorElement;
1948
- (_b = state.belowAnchorElementPositions) == null ? void 0 : _b.clear();
1949
- scrollTo({ offset: 0, animated: false });
1950
- setTimeout(() => {
1951
- calculateItemsInView(
1952
- /*reset*/
1953
- true
1954
- );
1955
- }, 0);
1956
- } else {
1957
- state.startBufferedId = void 0;
1958
- }
1959
- }
2481
+ if (isFunction(ref)) {
2482
+ ref(element);
1960
2483
  } else {
1961
- if (state.startBufferedId != null && newPositions.get(state.startBufferedId) == null) {
1962
- if (dataProp.length) {
1963
- state.startBufferedId = getId(0);
1964
- } else {
1965
- state.startBufferedId = void 0;
1966
- }
1967
- scrollTo({ offset: 0, animated: false });
1968
- setTimeout(() => {
1969
- calculateItemsInView(
1970
- /*reset*/
1971
- true
1972
- );
1973
- }, 0);
1974
- }
1975
- }
1976
- }
1977
- const anchorElementIndex = getAnchorElementIndex();
1978
- for (let i = 0; i < dataProp.length; i++) {
1979
- const key = getId(i);
1980
- const size = getItemSize(key, i, dataProp[i]);
1981
- maxSizeInRow = Math.max(maxSizeInRow, size);
1982
- column++;
1983
- if (column > numColumns) {
1984
- if (maintainVisibleContentPosition && anchorElementIndex !== void 0 && i < anchorElementIndex) {
1985
- totalSizeBelowIndex += maxSizeInRow;
1986
- }
1987
- totalSize += maxSizeInRow;
1988
- column = 1;
1989
- maxSizeInRow = 0;
2484
+ ref.current = element;
1990
2485
  }
1991
2486
  }
1992
- if (maxSizeInRow > 0) {
1993
- totalSize += maxSizeInRow;
1994
- }
1995
- state.ignoreScrollFromCalcTotal = true;
1996
- requestAnimationFrame(() => {
1997
- state.ignoreScrollFromCalcTotal = false;
1998
- });
1999
- addTotalSize(null, totalSize, totalSizeBelowIndex);
2487
+ }, refs);
2488
+ return callback;
2489
+ };
2490
+
2491
+ // src/utils/createColumnWrapperStyle.ts
2492
+ function createColumnWrapperStyle(contentContainerStyle) {
2493
+ const { gap, columnGap, rowGap } = contentContainerStyle;
2494
+ if (gap || columnGap || rowGap) {
2495
+ contentContainerStyle.gap = void 0;
2496
+ contentContainerStyle.columnGap = void 0;
2497
+ contentContainerStyle.rowGap = void 0;
2498
+ return {
2499
+ columnGap,
2500
+ gap,
2501
+ rowGap
2502
+ };
2503
+ }
2504
+ }
2505
+ function getRenderedItem(ctx, state, key) {
2506
+ var _a;
2507
+ if (!state) {
2508
+ return null;
2509
+ }
2510
+ const {
2511
+ indexByKey,
2512
+ props: { data, getItemType, renderItem }
2513
+ } = state;
2514
+ const index = indexByKey.get(key);
2515
+ if (index === void 0) {
2516
+ return null;
2517
+ }
2518
+ let renderedItem = null;
2519
+ if (renderItem && data[index]) {
2520
+ const itemProps = {
2521
+ data,
2522
+ extraData: peek$(ctx, "extraData"),
2523
+ index,
2524
+ item: data[index],
2525
+ type: getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : ""
2526
+ };
2527
+ renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__default.createElement(renderItem, itemProps);
2528
+ }
2529
+ return { index, item: data[index], renderedItem };
2530
+ }
2531
+
2532
+ // src/utils/throttledOnScroll.ts
2533
+ function throttledOnScroll(originalHandler, scrollEventThrottle) {
2534
+ const throttle = useThrottleDebounce("throttle");
2535
+ return (event) => {
2536
+ throttle(originalHandler, scrollEventThrottle, event);
2000
2537
  };
2001
- const findAvailableContainers = (numNeeded, startBuffered, endBuffered) => {
2002
- const state = refState.current;
2003
- const numContainers = peek$(ctx, "numContainers");
2004
- if (numNeeded === 0) return [];
2005
- const result = [];
2006
- const availableContainers = [];
2007
- for (let u = 0; u < numContainers; u++) {
2008
- const key = peek$(ctx, `containerItemKey${u}`);
2009
- if (key === void 0) {
2010
- result.push(u);
2011
- if (result.length >= numNeeded) {
2012
- return result;
2538
+ }
2539
+
2540
+ // src/utils/updateAveragesOnDataChange.ts
2541
+ function updateAveragesOnDataChange(state, oldData, newData) {
2542
+ var _a;
2543
+ const {
2544
+ averageSizes,
2545
+ sizesKnown,
2546
+ indexByKey,
2547
+ props: { itemsAreEqual, getItemType, keyExtractor }
2548
+ } = state;
2549
+ if (!itemsAreEqual || !oldData.length || !newData.length) {
2550
+ for (const key in averageSizes) {
2551
+ delete averageSizes[key];
2552
+ }
2553
+ return;
2554
+ }
2555
+ const itemTypesToPreserve = {};
2556
+ const newDataLength = newData.length;
2557
+ const oldDataLength = oldData.length;
2558
+ for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
2559
+ const newItem = newData[newIndex];
2560
+ const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
2561
+ const oldIndex = indexByKey.get(id);
2562
+ if (oldIndex !== void 0 && oldIndex < oldDataLength) {
2563
+ const knownSize = sizesKnown.get(id);
2564
+ if (knownSize === void 0) continue;
2565
+ const oldItem = oldData[oldIndex];
2566
+ const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
2567
+ if (areEqual) {
2568
+ const itemType = getItemType ? (_a = getItemType(newItem, newIndex)) != null ? _a : "" : "";
2569
+ let typeData = itemTypesToPreserve[itemType];
2570
+ if (!typeData) {
2571
+ typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
2013
2572
  }
2573
+ typeData.totalSize += knownSize;
2574
+ typeData.count++;
2014
2575
  }
2015
2576
  }
2016
- for (let u = 0; u < numContainers; u++) {
2017
- const key = peek$(ctx, `containerItemKey${u}`);
2018
- if (key === void 0) continue;
2019
- const index = state.indexByKey.get(key);
2020
- if (index < startBuffered) {
2021
- availableContainers.push({ index: u, distance: startBuffered - index });
2022
- } else if (index > endBuffered) {
2023
- availableContainers.push({ index: u, distance: index - endBuffered });
2024
- }
2577
+ }
2578
+ for (const key in averageSizes) {
2579
+ delete averageSizes[key];
2580
+ }
2581
+ for (const itemType in itemTypesToPreserve) {
2582
+ const { totalSize, count } = itemTypesToPreserve[itemType];
2583
+ if (count > 0) {
2584
+ averageSizes[itemType] = {
2585
+ avg: totalSize / count,
2586
+ num: count
2587
+ };
2025
2588
  }
2026
- const remaining = numNeeded - result.length;
2027
- if (remaining > 0) {
2028
- if (availableContainers.length > 0) {
2029
- if (availableContainers.length > remaining) {
2030
- availableContainers.sort(comparatorByDistance);
2031
- availableContainers.length = remaining;
2032
- }
2033
- for (const container of availableContainers) {
2034
- result.push(container.index);
2035
- }
2589
+ }
2590
+ }
2591
+
2592
+ // src/components/LegendList.tsx
2593
+ var DEFAULT_DRAW_DISTANCE = 250;
2594
+ var DEFAULT_ITEM_SIZE = 100;
2595
+ var LegendList = typedMemo(
2596
+ typedForwardRef(function LegendList2(props, forwardedRef) {
2597
+ const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
2598
+ const isChildrenMode = children !== void 0 && dataProp === void 0;
2599
+ const processedProps = isChildrenMode ? {
2600
+ ...restProps,
2601
+ data: (isArray(children) ? children : React3.Children.toArray(children)).flat(1),
2602
+ renderItem: ({ item }) => item
2603
+ } : {
2604
+ ...restProps,
2605
+ data: dataProp || [],
2606
+ renderItem: renderItemProp
2607
+ };
2608
+ return /* @__PURE__ */ React3.createElement(StateProvider, null, /* @__PURE__ */ React3.createElement(LegendListInner, { ...processedProps, ref: forwardedRef }));
2609
+ })
2610
+ );
2611
+ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
2612
+ var _a;
2613
+ const {
2614
+ alignItemsAtEnd = false,
2615
+ columnWrapperStyle,
2616
+ contentContainerStyle: contentContainerStyleProp,
2617
+ data: dataProp = [],
2618
+ drawDistance = 250,
2619
+ estimatedItemSize: estimatedItemSizeProp,
2620
+ estimatedListSize,
2621
+ extraData,
2622
+ getEstimatedItemSize,
2623
+ getFixedItemSize,
2624
+ getItemType,
2625
+ horizontal,
2626
+ initialContainerPoolRatio = 2,
2627
+ initialScrollIndex: initialScrollIndexProp,
2628
+ initialScrollOffset: initialScrollOffsetProp,
2629
+ itemsAreEqual,
2630
+ keyExtractor: keyExtractorProp,
2631
+ ListEmptyComponent,
2632
+ ListHeaderComponent,
2633
+ maintainScrollAtEnd = false,
2634
+ maintainScrollAtEndThreshold = 0.1,
2635
+ maintainVisibleContentPosition = true,
2636
+ numColumns: numColumnsProp = 1,
2637
+ onEndReached,
2638
+ onEndReachedThreshold = 0.5,
2639
+ onItemSizeChanged,
2640
+ onLayout: onLayoutProp,
2641
+ onLoad,
2642
+ onMomentumScrollEnd,
2643
+ onRefresh,
2644
+ onScroll: onScrollProp,
2645
+ onStartReached,
2646
+ onStartReachedThreshold = 0.5,
2647
+ onViewableItemsChanged,
2648
+ progressViewOffset,
2649
+ recycleItems = false,
2650
+ refreshControl,
2651
+ refreshing,
2652
+ refScrollView,
2653
+ renderItem,
2654
+ scrollEventThrottle,
2655
+ snapToIndices,
2656
+ stickyIndices,
2657
+ style: styleProp,
2658
+ suggestEstimatedItemSize,
2659
+ viewabilityConfig,
2660
+ viewabilityConfigCallbackPairs,
2661
+ waitForInitialLayout = true,
2662
+ ...rest
2663
+ } = props;
2664
+ const [renderNum, setRenderNum] = useState(0);
2665
+ const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
2666
+ const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
2667
+ const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
2668
+ const style = { ...StyleSheet.flatten(styleProp) };
2669
+ const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
2670
+ const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
2671
+ const ctx = useStateContext();
2672
+ ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
2673
+ const refScroller = useRef(null);
2674
+ const combinedRef = useCombinedRef(refScroller, refScrollView);
2675
+ const estimatedItemSize = estimatedItemSizeProp != null ? estimatedItemSizeProp : DEFAULT_ITEM_SIZE;
2676
+ const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
2677
+ const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
2678
+ const refState = useRef();
2679
+ if (!refState.current) {
2680
+ const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : Dimensions.get("window"))[horizontal ? "width" : "height"];
2681
+ refState.current = {
2682
+ activeStickyIndex: void 0,
2683
+ averageSizes: {},
2684
+ columns: /* @__PURE__ */ new Map(),
2685
+ containerItemKeys: /* @__PURE__ */ new Set(),
2686
+ containerItemTypes: /* @__PURE__ */ new Map(),
2687
+ enableScrollForNextCalculateItemsInView: true,
2688
+ endBuffered: -1,
2689
+ endNoBuffer: -1,
2690
+ endReachedBlockedByTimer: false,
2691
+ firstFullyOnScreenIndex: -1,
2692
+ idCache: /* @__PURE__ */ new Map(),
2693
+ idsInView: [],
2694
+ indexByKey: /* @__PURE__ */ new Map(),
2695
+ initialScroll,
2696
+ isAtEnd: false,
2697
+ isAtStart: false,
2698
+ isEndReached: false,
2699
+ isStartReached: false,
2700
+ lastBatchingAction: Date.now(),
2701
+ lastLayout: void 0,
2702
+ loadStartTime: Date.now(),
2703
+ minIndexSizeChanged: 0,
2704
+ nativeMarginTop: 0,
2705
+ pendingAdjust: 0,
2706
+ positions: /* @__PURE__ */ new Map(),
2707
+ props: {},
2708
+ queuedCalculateItemsInView: 0,
2709
+ queuedItemSizeUpdates: [],
2710
+ refScroller: void 0,
2711
+ scroll: 0,
2712
+ scrollAdjustHandler: new ScrollAdjustHandler(ctx),
2713
+ scrollForNextCalculateItemsInView: void 0,
2714
+ scrollHistory: [],
2715
+ scrollLength: initialScrollLength,
2716
+ scrollPending: 0,
2717
+ scrollPrev: 0,
2718
+ scrollPrevTime: 0,
2719
+ scrollProcessingEnabled: true,
2720
+ scrollTime: 0,
2721
+ sizes: /* @__PURE__ */ new Map(),
2722
+ sizesKnown: /* @__PURE__ */ new Map(),
2723
+ startBuffered: -1,
2724
+ startNoBuffer: -1,
2725
+ startReachedBlockedByTimer: false,
2726
+ stickyContainerPool: /* @__PURE__ */ new Set(),
2727
+ stickyContainers: /* @__PURE__ */ new Map(),
2728
+ timeoutSizeMessage: 0,
2729
+ timeouts: /* @__PURE__ */ new Set(),
2730
+ totalSize: 0,
2731
+ viewabilityConfigCallbackPairs: void 0
2732
+ };
2733
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
2734
+ set$(ctx, "extraData", extraData);
2735
+ }
2736
+ const state = refState.current;
2737
+ const isFirst = !state.props.renderItem;
2738
+ const didDataChange = state.props.data !== dataProp;
2739
+ state.props = {
2740
+ alignItemsAtEnd,
2741
+ data: dataProp,
2742
+ estimatedItemSize,
2743
+ getEstimatedItemSize,
2744
+ getFixedItemSize,
2745
+ getItemType,
2746
+ horizontal: !!horizontal,
2747
+ initialContainerPoolRatio,
2748
+ initialScroll,
2749
+ itemsAreEqual,
2750
+ keyExtractor,
2751
+ maintainScrollAtEnd,
2752
+ maintainScrollAtEndThreshold,
2753
+ maintainVisibleContentPosition,
2754
+ numColumns: numColumnsProp,
2755
+ onEndReached,
2756
+ onEndReachedThreshold,
2757
+ onItemSizeChanged,
2758
+ onLoad,
2759
+ onScroll: onScrollProp,
2760
+ onStartReached,
2761
+ onStartReachedThreshold,
2762
+ recycleItems: !!recycleItems,
2763
+ renderItem,
2764
+ scrollBuffer,
2765
+ snapToIndices,
2766
+ stickyIndicesArr: stickyIndices != null ? stickyIndices : [],
2767
+ stickyIndicesSet: useMemo(() => new Set(stickyIndices != null ? stickyIndices : []), [stickyIndices == null ? void 0 : stickyIndices.join(",")]),
2768
+ stylePaddingBottom: stylePaddingBottomState,
2769
+ stylePaddingTop: stylePaddingTopState,
2770
+ suggestEstimatedItemSize: !!suggestEstimatedItemSize
2771
+ };
2772
+ state.refScroller = refScroller;
2773
+ const checkResetContainers = (isFirst2) => {
2774
+ const state2 = refState.current;
2775
+ if (state2) {
2776
+ if (!isFirst2 && state2.props.data !== dataProp) {
2777
+ updateAveragesOnDataChange(state2, state2.props.data, dataProp);
2036
2778
  }
2037
- const stillNeeded = numNeeded - result.length;
2038
- if (stillNeeded > 0) {
2039
- for (let i = 0; i < stillNeeded; i++) {
2040
- result.push(numContainers + i);
2779
+ state2.props.data = dataProp;
2780
+ if (!isFirst2) {
2781
+ calculateItemsInView(ctx, state2, { dataChanged: true, doMVCP: true });
2782
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2783
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state2, false);
2784
+ if (!didMaintainScrollAtEnd && dataProp.length > state2.props.data.length) {
2785
+ state2.isEndReached = false;
2041
2786
  }
2042
- if (__DEV__ && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
2043
- console.warn(
2044
- "[legend-list] No unused container available, 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 or increasing initialContainerPoolRatio.",
2045
- {
2046
- debugInfo: {
2047
- numContainers,
2048
- numNeeded,
2049
- stillNeeded,
2050
- numContainersPooled: peek$(ctx, "numContainersPooled")
2051
- }
2052
- }
2053
- );
2787
+ if (!didMaintainScrollAtEnd) {
2788
+ checkAtTop(state2);
2789
+ checkAtBottom(ctx, state2);
2054
2790
  }
2055
2791
  }
2056
2792
  }
2057
- return result.sort(comparatorDefault);
2058
2793
  };
2059
- const isFirst = !refState.current.renderItem;
2060
2794
  const memoizedLastItemKeys = useMemo(() => {
2061
2795
  if (!dataProp.length) return [];
2062
2796
  return Array.from(
2063
2797
  { length: Math.min(numColumnsProp, dataProp.length) },
2064
- (_, i) => getId(dataProp.length - 1 - i)
2798
+ (_, i) => getId(state, dataProp.length - 1 - i)
2065
2799
  );
2066
2800
  }, [dataProp, numColumnsProp]);
2067
- const initalizeStateVars = () => {
2801
+ const initializeStateVars = () => {
2068
2802
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
2069
2803
  set$(ctx, "numColumns", numColumnsProp);
2070
2804
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
2071
- setPaddingTop({ stylePaddingTop: stylePaddingTopState });
2072
- refState.current.stylePaddingBottom = stylePaddingBottomState;
2073
- const paddingDiff = stylePaddingTopState - prevPaddingTop;
2074
- if (paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
2075
- queueMicrotask(() => {
2076
- scrollTo({ offset: refState.current.scrollPending + paddingDiff, animated: false });
2077
- });
2805
+ setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
2806
+ refState.current.props.stylePaddingBottom = stylePaddingBottomState;
2807
+ let paddingDiff = stylePaddingTopState - prevPaddingTop;
2808
+ if (maintainVisibleContentPosition && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
2809
+ if (state.scroll < 0) {
2810
+ paddingDiff += state.scroll;
2811
+ }
2812
+ requestAdjust(ctx, state, paddingDiff);
2078
2813
  }
2079
2814
  };
2080
2815
  if (isFirst) {
2081
- initalizeStateVars();
2816
+ initializeStateVars();
2817
+ updateAllPositions(ctx, state);
2082
2818
  }
2819
+ const initialContentOffset = useMemo(() => {
2820
+ if (initialScroll) {
2821
+ const { index, viewOffset } = initialScroll;
2822
+ let initialContentOffset2 = viewOffset || 0;
2823
+ if (index !== void 0) {
2824
+ initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
2825
+ }
2826
+ refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2827
+ if (initialContentOffset2 > 0) {
2828
+ scrollTo(state, { animated: false, index, offset: initialContentOffset2 });
2829
+ }
2830
+ return initialContentOffset2;
2831
+ }
2832
+ return 0;
2833
+ }, [renderNum]);
2083
2834
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
2084
2835
  refState.current.lastBatchingAction = Date.now();
2085
2836
  if (!keyExtractorProp && !isFirst && didDataChange) {
@@ -2090,23 +2841,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2090
2841
  refState.current.sizes.clear();
2091
2842
  refState.current.positions.clear();
2092
2843
  }
2093
- refState.current.previousTotalSize = peek$(ctx, "totalSize");
2094
- calcTotalSizesAndPositions({ forgetPositions: false });
2095
2844
  }
2096
- useEffect(() => {
2097
- if (initialScroll && ListHeaderComponent) {
2098
- const dispose = listen$(ctx, "headerSize", (size) => {
2099
- if (size > 0) {
2100
- scrollToIndex({ ...initialScroll, animated: false });
2101
- dispose == null ? void 0 : dispose();
2845
+ const onLayoutHeader = useCallback((rect, fromLayoutEffect) => {
2846
+ const size = rect[horizontal ? "width" : "height"];
2847
+ set$(ctx, "headerSize", size);
2848
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2849
+ if (IsNewArchitecture && Platform.OS !== "android") {
2850
+ if (fromLayoutEffect) {
2851
+ setRenderNum((v) => v + 1);
2102
2852
  }
2103
- });
2104
- setTimeout(dispose, 0);
2105
- return dispose;
2853
+ } else {
2854
+ setTimeout(() => {
2855
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2856
+ }, 17);
2857
+ }
2106
2858
  }
2107
2859
  }, []);
2108
- useEffect(() => {
2109
- const didAllocateContainers = doInitialAllocateContainers();
2860
+ useLayoutEffect(() => {
2861
+ if (snapToIndices) {
2862
+ updateSnapToOffsets(ctx, state);
2863
+ }
2864
+ }, [snapToIndices]);
2865
+ useLayoutEffect(() => {
2866
+ const didAllocateContainers = doInitialAllocateContainersCallback();
2110
2867
  if (!didAllocateContainers) {
2111
2868
  checkResetContainers(
2112
2869
  /*isFirst*/
@@ -2114,470 +2871,211 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2114
2871
  );
2115
2872
  }
2116
2873
  }, [dataProp, numColumnsProp]);
2117
- useEffect(() => {
2874
+ useLayoutEffect(() => {
2118
2875
  set$(ctx, "extraData", extraData);
2119
2876
  }, [extraData]);
2120
- refState.current.renderItem = renderItem2;
2121
- useEffect(initalizeStateVars, [
2877
+ useLayoutEffect(() => {
2878
+ if (IsNewArchitecture) {
2879
+ let measured;
2880
+ refScroller.current.measure((x, y, width, height) => {
2881
+ measured = { height, width, x, y };
2882
+ });
2883
+ if (measured) {
2884
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2885
+ if (size) {
2886
+ handleLayout(ctx, state, measured, setCanRender);
2887
+ }
2888
+ }
2889
+ }
2890
+ }, []);
2891
+ useLayoutEffect(initializeStateVars, [
2122
2892
  memoizedLastItemKeys.join(","),
2123
2893
  numColumnsProp,
2124
2894
  stylePaddingTopState,
2125
2895
  stylePaddingBottomState
2126
2896
  ]);
2127
- const getRenderedItem = useCallback((key) => {
2128
- const state = refState.current;
2129
- if (!state) {
2130
- return null;
2131
- }
2132
- const { data, indexByKey } = state;
2133
- const index = indexByKey.get(key);
2134
- if (index === void 0) {
2135
- return null;
2136
- }
2137
- const renderItemProp = refState.current.renderItem;
2138
- let renderedItem = null;
2139
- if (renderItemProp) {
2140
- const itemProps = {
2141
- item: data[index],
2142
- index,
2143
- extraData: peek$(ctx, "extraData")
2144
- };
2145
- renderedItem = React2.createElement(renderItemProp, itemProps);
2146
- }
2147
- return { index, item: data[index], renderedItem };
2148
- }, []);
2149
- const doInitialAllocateContainers = () => {
2150
- const state = refState.current;
2151
- const { scrollLength, data } = state;
2152
- if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
2153
- const averageItemSize = getEstimatedItemSize ? getEstimatedItemSize(0, data[0]) : estimatedItemSize;
2154
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
2155
- for (let i = 0; i < numContainers; i++) {
2156
- set$(ctx, `containerPosition${i}`, ANCHORED_POSITION_OUT_OF_VIEW);
2157
- set$(ctx, `containerColumn${i}`, -1);
2158
- }
2159
- set$(ctx, "numContainers", numContainers);
2160
- set$(ctx, "numContainersPooled", numContainers * initialContainerPoolRatio);
2161
- if (initialScroll) {
2162
- requestAnimationFrame(() => {
2163
- calculateItemsInView(
2164
- /*isReset*/
2165
- true
2166
- );
2167
- });
2168
- } else {
2169
- calculateItemsInView(
2170
- /*isReset*/
2171
- true
2172
- );
2173
- }
2174
- return true;
2175
- }
2897
+ const doInitialAllocateContainersCallback = () => {
2898
+ return doInitialAllocateContainers(ctx, state);
2176
2899
  };
2177
2900
  useEffect(() => {
2178
- const state = refState.current;
2179
2901
  const viewability = setupViewability({
2902
+ onViewableItemsChanged,
2180
2903
  viewabilityConfig,
2181
- viewabilityConfigCallbackPairs,
2182
- onViewableItemsChanged
2904
+ viewabilityConfigCallbackPairs
2183
2905
  });
2184
2906
  state.viewabilityConfigCallbackPairs = viewability;
2185
2907
  state.enableScrollForNextCalculateItemsInView = !viewability;
2186
2908
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
2187
- useInit(() => {
2188
- doInitialAllocateContainers();
2189
- });
2190
- const updateItemSize = useCallback(
2191
- (itemKey, sizeObj, fromFixGaps) => {
2192
- const state = refState.current;
2193
- const {
2194
- sizes,
2195
- indexByKey,
2196
- sizesKnown,
2197
- data,
2198
- rowHeights,
2199
- startBuffered,
2200
- endBuffered,
2201
- averageSizes,
2202
- queuedInitialLayout
2203
- } = state;
2204
- if (!data) {
2205
- return;
2206
- }
2207
- const index = indexByKey.get(itemKey);
2208
- const numColumns = peek$(ctx, "numColumns");
2209
- state.scrollForNextCalculateItemsInView = void 0;
2210
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, index) : index;
2211
- const prevSize = getItemSize(itemKey, index, data);
2212
- const prevSizeKnown = sizesKnown.get(itemKey);
2213
- let needsCalculate = false;
2214
- let needsUpdateContainersDidLayout = false;
2215
- const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2216
- sizesKnown.set(itemKey, size);
2217
- const itemType = "";
2218
- let averages = averageSizes[itemType];
2219
- if (!averages) {
2220
- averages = averageSizes[itemType] = {
2221
- num: 0,
2222
- avg: 0
2223
- };
2224
- }
2225
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2226
- averages.num++;
2227
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2228
- let diff;
2229
- needsCalculate = true;
2230
- if (numColumns > 1) {
2231
- const rowNumber = Math.floor(index / numColumnsProp);
2232
- const prevSizeInRow = getRowHeight(rowNumber);
2233
- sizes.set(itemKey, size);
2234
- rowHeights.delete(rowNumber);
2235
- const sizeInRow = getRowHeight(rowNumber);
2236
- diff = sizeInRow - prevSizeInRow;
2237
- } else {
2238
- sizes.set(itemKey, size);
2239
- diff = size - prevSize;
2240
- }
2241
- if (__DEV__ && suggestEstimatedItemSize) {
2242
- if (state.timeoutSizeMessage) {
2243
- clearTimeout(state.timeoutSizeMessage);
2244
- }
2245
- state.timeoutSizeMessage = setTimeout(() => {
2246
- state.timeoutSizeMessage = void 0;
2247
- const num = sizesKnown.size;
2248
- const avg = state.averageSizes[""].avg;
2249
- console.warn(
2250
- `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
2251
- );
2252
- }, 1e3);
2253
- }
2254
- state.scrollForNextCalculateItemsInView = void 0;
2255
- addTotalSize(itemKey, diff, 0);
2256
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2257
- doMaintainScrollAtEnd(false);
2258
- }
2259
- if (onItemSizeChanged) {
2260
- onItemSizeChanged({
2261
- size,
2262
- previous: prevSize,
2263
- index,
2264
- itemKey,
2265
- itemData: data[index]
2266
- });
2267
- }
2268
- }
2269
- if (!queuedInitialLayout && checkAllSizesKnown()) {
2270
- needsUpdateContainersDidLayout = true;
2271
- }
2272
- let isInView = index >= startBuffered && index <= endBuffered;
2273
- if (!isInView) {
2274
- const numContainers = ctx.values.get("numContainers");
2275
- for (let i = 0; i < numContainers; i++) {
2276
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2277
- isInView = true;
2278
- break;
2279
- }
2280
- }
2281
- }
2282
- if (needsUpdateContainersDidLayout || !fromFixGaps && needsCalculate && (isInView || !queuedInitialLayout)) {
2283
- const scrollVelocity = state.scrollVelocity;
2284
- let didCalculate = false;
2285
- if ((Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1 || state.scrollingTo !== void 0) && (!waitForInitialLayout || needsUpdateContainersDidLayout || queuedInitialLayout)) {
2286
- if (Date.now() - state.lastBatchingAction < 500) {
2287
- if (!state.queuedCalculateItemsInView) {
2288
- state.queuedCalculateItemsInView = requestAnimationFrame(() => {
2289
- state.queuedCalculateItemsInView = void 0;
2290
- calculateItemsInView();
2291
- });
2292
- }
2293
- } else {
2294
- calculateItemsInView();
2295
- didCalculate = true;
2296
- }
2297
- }
2298
- if (!didCalculate && !needsUpdateContainersDidLayout && IsNewArchitecture) {
2299
- fixGaps();
2300
- }
2301
- }
2302
- if (state.needsOtherAxisSize) {
2303
- const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
2304
- const cur = peek$(ctx, "otherAxisSize");
2305
- if (!cur || otherAxisSize > cur) {
2306
- set$(ctx, "otherAxisSize", otherAxisSize);
2307
- }
2308
- }
2309
- },
2310
- []
2311
- );
2312
- const handleLayout = useCallback((size) => {
2313
- const scrollLength = size[horizontal ? "width" : "height"];
2314
- const otherAxisSize = size[horizontal ? "height" : "width"];
2315
- const state = refState.current;
2316
- const didChange = scrollLength !== state.scrollLength;
2317
- const prevOtherAxisSize = state.otherAxisSize;
2318
- state.scrollLength = scrollLength;
2319
- state.otherAxisSize = otherAxisSize;
2320
- state.lastBatchingAction = Date.now();
2321
- state.scrollForNextCalculateItemsInView = void 0;
2322
- doInitialAllocateContainers();
2323
- doMaintainScrollAtEnd(false);
2324
- updateAlignItemsPaddingTop();
2325
- checkAtBottom();
2326
- checkAtTop();
2327
- if (didChange) {
2328
- calculateItemsInView();
2329
- }
2330
- if (didChange || otherAxisSize !== prevOtherAxisSize) {
2331
- set$(ctx, "scrollSize", { width: size.width, height: size.height });
2332
- }
2333
- if (refState.current) {
2334
- refState.current.needsOtherAxisSize = otherAxisSize - (stylePaddingTopState || 0) < 10;
2335
- }
2336
- if (__DEV__ && scrollLength === 0) {
2337
- warnDevOnce(
2338
- "height0",
2339
- `List ${horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
2340
- );
2341
- }
2342
- }, []);
2909
+ if (!IsNewArchitecture) {
2910
+ useInit(() => {
2911
+ doInitialAllocateContainersCallback();
2912
+ });
2913
+ }
2343
2914
  const onLayout = useCallback((event) => {
2344
2915
  const layout = event.nativeEvent.layout;
2345
- handleLayout(layout);
2346
- layout[horizontal ? "width" : "height"];
2347
- layout[horizontal ? "height" : "width"];
2916
+ handleLayout(ctx, state, layout, setCanRender);
2348
2917
  if (onLayoutProp) {
2349
2918
  onLayoutProp(event);
2350
2919
  }
2351
2920
  }, []);
2352
- if (IsNewArchitecture) {
2353
- useLayoutEffect(() => {
2354
- var _a, _b;
2355
- const measured = (_b = (_a = refScroller.current) == null ? void 0 : _a.unstable_getBoundingClientRect) == null ? void 0 : _b.call(_a);
2356
- if (measured) {
2357
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2358
- if (size) {
2359
- handleLayout(measured);
2921
+ useImperativeHandle(forwardedRef, () => {
2922
+ const scrollIndexIntoView = (options) => {
2923
+ const state2 = refState.current;
2924
+ if (state2) {
2925
+ const { index, ...rest2 } = options;
2926
+ const { startNoBuffer, endNoBuffer } = state2;
2927
+ if (index < startNoBuffer || index > endNoBuffer) {
2928
+ const viewPosition = index < startNoBuffer ? 0 : 1;
2929
+ scrollToIndex(ctx, state2, {
2930
+ ...rest2,
2931
+ index,
2932
+ viewPosition
2933
+ });
2360
2934
  }
2361
2935
  }
2362
- }, []);
2363
- }
2364
- const handleScroll = useCallback(
2365
- (event) => {
2366
- var _a, _b, _c, _d;
2367
- 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) {
2368
- return;
2369
- }
2370
- const state = refState.current;
2371
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
2372
- state.scrollPending = newScroll;
2373
- if (state.ignoreScrollFromCalcTotal && newScroll !== 0) {
2374
- return;
2375
- }
2376
- updateScroll(newScroll);
2377
- (_d = state.onScroll) == null ? void 0 : _d.call(state, event);
2378
- },
2379
- []
2380
- );
2381
- const updateScroll = useCallback((newScroll) => {
2382
- const state = refState.current;
2383
- const scrollingTo = state.scrollingTo;
2384
- if (scrollingTo !== void 0 && Math.abs(newScroll - scrollingTo.offset) < 10) {
2385
- finishScrollTo();
2386
- }
2387
- if (state.disableScrollJumpsFrom !== void 0) {
2388
- const scrollMinusAdjust = newScroll - state.scrollAdjustHandler.getAppliedAdjust();
2389
- if (Math.abs(scrollMinusAdjust - state.disableScrollJumpsFrom) > 200) {
2390
- return;
2391
- }
2392
- state.disableScrollJumpsFrom = void 0;
2393
- }
2394
- state.hasScrolled = true;
2395
- state.lastBatchingAction = Date.now();
2396
- const currentTime = performance.now();
2397
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
2398
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
2399
- }
2400
- if (state.scrollHistory.length > 5) {
2401
- state.scrollHistory.shift();
2402
- }
2403
- if (state.scrollTimer !== void 0) {
2404
- clearTimeout(state.scrollTimer);
2405
- }
2406
- state.scrollTimer = setTimeout(() => {
2407
- state.scrollVelocity = 0;
2408
- }, 500);
2409
- let velocity = 0;
2410
- if (state.scrollHistory.length >= 2) {
2411
- const newest = state.scrollHistory[state.scrollHistory.length - 1];
2412
- let oldest;
2413
- for (let i = 0; i < state.scrollHistory.length - 1; i++) {
2414
- const entry = state.scrollHistory[i];
2415
- if (newest.time - entry.time <= 100) {
2416
- oldest = entry;
2417
- break;
2936
+ };
2937
+ return {
2938
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
2939
+ getNativeScrollRef: () => refScroller.current,
2940
+ getScrollableNode: () => refScroller.current.getScrollableNode(),
2941
+ getScrollResponder: () => refScroller.current.getScrollResponder(),
2942
+ getState: () => {
2943
+ const state2 = refState.current;
2944
+ return state2 ? {
2945
+ contentLength: state2.totalSize,
2946
+ data: state2.props.data,
2947
+ end: state2.endNoBuffer,
2948
+ endBuffered: state2.endBuffered,
2949
+ isAtEnd: state2.isAtEnd,
2950
+ isAtStart: state2.isAtStart,
2951
+ positionAtIndex: (index) => state2.positions.get(getId(state2, index)),
2952
+ positions: state2.positions,
2953
+ scroll: state2.scroll,
2954
+ scrollLength: state2.scrollLength,
2955
+ sizeAtIndex: (index) => state2.sizesKnown.get(getId(state2, index)),
2956
+ sizes: state2.sizesKnown,
2957
+ start: state2.startNoBuffer,
2958
+ startBuffered: state2.startBuffered
2959
+ } : {};
2960
+ },
2961
+ scrollIndexIntoView,
2962
+ scrollItemIntoView: ({ item, ...props2 }) => {
2963
+ const data = refState.current.props.data;
2964
+ const index = data.indexOf(item);
2965
+ if (index !== -1) {
2966
+ scrollIndexIntoView({ index, ...props2 });
2418
2967
  }
2419
- }
2420
- if (oldest) {
2421
- const scrollDiff = newest.scroll - oldest.scroll;
2422
- const timeDiff = newest.time - oldest.time;
2423
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
2424
- }
2425
- }
2426
- state.scrollPrev = state.scroll;
2427
- state.scrollPrevTime = state.scrollTime;
2428
- state.scroll = newScroll;
2429
- state.scrollTime = currentTime;
2430
- state.scrollVelocity = velocity;
2431
- calculateItemsInView();
2432
- checkAtBottom();
2433
- checkAtTop();
2434
- }, []);
2435
- useImperativeHandle(
2436
- forwardedRef,
2437
- () => {
2438
- const scrollIndexIntoView = (options) => {
2439
- if (refState.current) {
2440
- const { index, ...rest2 } = options;
2441
- const { startNoBuffer, endNoBuffer } = refState.current;
2442
- if (index < startNoBuffer || index > endNoBuffer) {
2443
- const viewPosition = index < startNoBuffer ? 0 : 1;
2444
- scrollToIndex({
2445
- ...rest2,
2446
- viewPosition,
2447
- index
2448
- });
2449
- }
2968
+ },
2969
+ scrollToEnd: (options) => {
2970
+ const data = refState.current.props.data;
2971
+ const stylePaddingBottom = refState.current.props.stylePaddingBottom;
2972
+ const index = data.length - 1;
2973
+ if (index !== -1) {
2974
+ const paddingBottom = stylePaddingBottom || 0;
2975
+ const footerSize = peek$(ctx, "footerSize") || 0;
2976
+ scrollToIndex(ctx, state, {
2977
+ index,
2978
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
2979
+ viewPosition: 1,
2980
+ ...options
2981
+ });
2450
2982
  }
2451
- };
2452
- return {
2453
- flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
2454
- getNativeScrollRef: () => refScroller.current,
2455
- getScrollableNode: () => refScroller.current.getScrollableNode(),
2456
- getScrollResponder: () => refScroller.current.getScrollResponder(),
2457
- getState: () => {
2458
- const state = refState.current;
2459
- return state ? {
2460
- contentLength: state.totalSize,
2461
- end: state.endNoBuffer,
2462
- endBuffered: state.endBuffered,
2463
- isAtEnd: state.isAtEnd,
2464
- isAtStart: state.isAtStart,
2465
- scroll: state.scroll,
2466
- scrollLength: state.scrollLength,
2467
- start: state.startNoBuffer,
2468
- startBuffered: state.startBuffered,
2469
- sizes: state.sizesKnown,
2470
- sizeAtIndex: (index) => state.sizesKnown.get(getId(index))
2471
- } : {};
2472
- },
2473
- scrollIndexIntoView,
2474
- scrollItemIntoView: ({ item, ...props2 }) => {
2475
- const { data } = refState.current;
2476
- const index = data.indexOf(item);
2477
- if (index !== -1) {
2478
- scrollIndexIntoView({ index, ...props2 });
2479
- }
2480
- },
2481
- scrollToIndex,
2482
- scrollToItem: ({ item, ...props2 }) => {
2483
- const { data } = refState.current;
2484
- const index = data.indexOf(item);
2485
- if (index !== -1) {
2486
- scrollToIndex({ index, ...props2 });
2487
- }
2488
- },
2489
- scrollToOffset: (params) => scrollTo(params),
2490
- scrollToEnd: (options) => {
2491
- const { data, stylePaddingBottom } = refState.current;
2492
- const index = data.length - 1;
2493
- if (index !== -1) {
2494
- const paddingBottom = stylePaddingBottom || 0;
2495
- const footerSize = peek$(ctx, "footerSize") || 0;
2496
- scrollToIndex({ index, viewPosition: 1, viewOffset: -paddingBottom - footerSize, ...options });
2497
- }
2983
+ },
2984
+ scrollToIndex: (params) => scrollToIndex(ctx, state, params),
2985
+ scrollToItem: ({ item, ...props2 }) => {
2986
+ const data = refState.current.props.data;
2987
+ const index = data.indexOf(item);
2988
+ if (index !== -1) {
2989
+ scrollToIndex(ctx, state, { index, ...props2 });
2498
2990
  }
2499
- };
2500
- },
2501
- []
2502
- );
2991
+ },
2992
+ scrollToOffset: (params) => scrollTo(state, params),
2993
+ setScrollProcessingEnabled: (enabled) => {
2994
+ refState.current.scrollProcessingEnabled = enabled;
2995
+ },
2996
+ setVisibleContentAnchorOffset: (value) => {
2997
+ const val = typeof value === "function" ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
2998
+ set$(ctx, "scrollAdjustUserOffset", val);
2999
+ }
3000
+ };
3001
+ }, []);
2503
3002
  if (Platform.OS === "web") {
2504
3003
  useEffect(() => {
2505
- var _a;
2506
3004
  if (initialContentOffset) {
2507
- (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler.setDisableAdjust(true);
2508
- scrollTo({ offset: initialContentOffset, animated: false });
2509
- setTimeout(() => {
2510
- var _a2;
2511
- (_a2 = refState.current) == null ? void 0 : _a2.scrollAdjustHandler.setDisableAdjust(false);
2512
- }, 0);
3005
+ scrollTo(state, { animated: false, offset: initialContentOffset });
2513
3006
  }
2514
3007
  }, []);
2515
3008
  }
2516
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
3009
+ const fns = useMemo(
3010
+ () => ({
3011
+ getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3012
+ onScroll: (event) => onScroll(ctx, state, event),
3013
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
3014
+ }),
3015
+ []
3016
+ );
3017
+ const animatedScrollHandler = useMemo(() => {
3018
+ const onScrollFn = scrollEventThrottle && scrollEventThrottle > 0 ? throttledOnScroll(fns.onScroll, scrollEventThrottle) : fns.onScroll;
3019
+ if (stickyIndices == null ? void 0 : stickyIndices.length) {
3020
+ const { animatedScrollY } = ctx;
3021
+ return Animated.event([{ nativeEvent: { contentOffset: { [horizontal ? "x" : "y"]: animatedScrollY } } }], {
3022
+ listener: onScrollFn,
3023
+ useNativeDriver: true
3024
+ });
3025
+ }
3026
+ return onScrollFn;
3027
+ }, [stickyIndices == null ? void 0 : stickyIndices.length, horizontal, scrollEventThrottle]);
3028
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(
2517
3029
  ListComponent,
2518
3030
  {
2519
3031
  ...rest,
3032
+ alignItemsAtEnd,
3033
+ canRender,
3034
+ contentContainerStyle,
3035
+ getRenderedItem: fns.getRenderedItem,
2520
3036
  horizontal,
2521
- refScrollView: combinedRef,
2522
3037
  initialContentOffset,
2523
- getRenderedItem,
2524
- updateItemSize,
2525
- handleScroll,
3038
+ ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
3039
+ ListHeaderComponent,
3040
+ maintainVisibleContentPosition,
3041
+ onLayout,
3042
+ onLayoutHeader,
2526
3043
  onMomentumScrollEnd: (event) => {
2527
- var _a;
2528
- const scrollingTo = (_a = refState.current) == null ? void 0 : _a.scrollingTo;
2529
- if (scrollingTo !== void 0) {
3044
+ if (IsNewArchitecture) {
2530
3045
  requestAnimationFrame(() => {
2531
- scrollTo({ ...scrollingTo, animated: false });
2532
- refState.current.scrollingTo = void 0;
2533
- requestAnimationFrame(() => {
2534
- refState.current.scrollAdjustHandler.setDisableAdjust(false);
2535
- });
3046
+ finishScrollTo(refState.current);
2536
3047
  });
2537
- }
2538
- const wasPaused = refState.current.scrollAdjustHandler.unPauseAdjust();
2539
- if (wasPaused) {
2540
- refState.current.scrollVelocity = 0;
2541
- refState.current.scrollHistory = [];
2542
- calculateItemsInView();
3048
+ } else {
3049
+ setTimeout(() => {
3050
+ finishScrollTo(refState.current);
3051
+ }, 1e3);
2543
3052
  }
2544
3053
  if (onMomentumScrollEnd) {
2545
3054
  onMomentumScrollEnd(event);
2546
3055
  }
2547
3056
  },
2548
- onLayout,
3057
+ onScroll: animatedScrollHandler,
2549
3058
  recycleItems,
2550
- alignItemsAtEnd,
2551
- ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
2552
- ListHeaderComponent,
2553
- maintainVisibleContentPosition,
2554
- scrollEventThrottle: Platform.OS === "web" ? 16 : void 0,
2555
- waitForInitialLayout,
2556
- refreshControl: refreshControl ? stylePaddingTopState > 0 ? React2.cloneElement(refreshControl, {
3059
+ refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControl, {
2557
3060
  progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
2558
- }) : refreshControl : onRefresh && /* @__PURE__ */ React2.createElement(
3061
+ }) : refreshControl : onRefresh && /* @__PURE__ */ React3.createElement(
2559
3062
  RefreshControl,
2560
3063
  {
2561
- refreshing: !!refreshing,
2562
3064
  onRefresh,
2563
- progressViewOffset: (progressViewOffset || 0) + stylePaddingTopState
3065
+ progressViewOffset: (progressViewOffset || 0) + stylePaddingTopState,
3066
+ refreshing: !!refreshing
2564
3067
  }
2565
3068
  ),
3069
+ refScrollView: combinedRef,
3070
+ scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
3071
+ scrollEventThrottle: Platform.OS === "web" ? 16 : void 0,
3072
+ snapToIndices,
3073
+ stickyIndices,
2566
3074
  style,
2567
- contentContainerStyle
3075
+ updateItemSize: fns.updateItemSize,
3076
+ waitForInitialLayout
2568
3077
  }
2569
- ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React2.createElement(DebugView, { state: refState.current }));
2570
- });
2571
- var typedForwardRef2 = forwardRef;
2572
- var renderItem = ({ item }) => item;
2573
- var LazyLegendList = typedForwardRef2(function LazyLegendList2(props, forwardedRef) {
2574
- const { LegendList: LegendListProp, children, ...rest } = props;
2575
- const LegendListComponent = LegendListProp != null ? LegendListProp : LegendList$1;
2576
- const data = (isArray(children) ? children : React2.Children.toArray(children)).flat(1);
2577
- return (
2578
- // @ts-expect-error TODO: Fix this type
2579
- /* @__PURE__ */ React2.createElement(LegendListComponent, { ...rest, data, renderItem, ref: forwardedRef })
2580
- );
3078
+ ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3.createElement(DebugView, { state: refState.current }));
2581
3079
  });
2582
3080
 
2583
- export { LazyLegendList, LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
3081
+ export { LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };