@legendapp/list 2.1.0-next.1 → 3.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
- import * as React4 from 'react';
2
- import React4__default, { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useImperativeHandle, useCallback, useLayoutEffect, memo, useContext } from 'react';
1
+ import * as React3 from 'react';
2
+ import React3__default, { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useImperativeHandle, useLayoutEffect, memo, useContext } from 'react';
3
3
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
4
  import { unstable_batchedUpdates } from 'react-dom';
5
5
 
6
6
  // src/components/LegendList.tsx
7
- var AnimatedView = forwardRef(function AnimatedView2(props, ref) {
8
- return /* @__PURE__ */ React4.createElement("div", { ref, ...props });
7
+ forwardRef(function AnimatedView2(props, ref) {
8
+ return /* @__PURE__ */ React3.createElement("div", { ref, ...props });
9
9
  });
10
10
  var View = forwardRef(function View2(props, ref) {
11
- return /* @__PURE__ */ React4.createElement("div", { ref, ...props });
11
+ return /* @__PURE__ */ React3.createElement("div", { ref, ...props });
12
12
  });
13
13
  var Text = View;
14
14
 
@@ -16,9 +16,9 @@ var Text = View;
16
16
  var createAnimatedValue = (value) => value;
17
17
 
18
18
  // src/state/state.tsx
19
- var ContextState = React4.createContext(null);
19
+ var ContextState = React3.createContext(null);
20
20
  function StateProvider({ children }) {
21
- const [value] = React4.useState(() => ({
21
+ const [value] = React3.useState(() => ({
22
22
  animatedScrollY: createAnimatedValue(0),
23
23
  columnWrapperStyle: void 0,
24
24
  internalState: void 0,
@@ -33,14 +33,17 @@ function StateProvider({ children }) {
33
33
  ["stylePaddingTop", 0],
34
34
  ["headerSize", 0],
35
35
  ["numContainers", 0],
36
- ["totalSize", 0]
36
+ ["activeStickyIndex", void 0],
37
+ ["totalSize", 0],
38
+ ["scrollAdjustPending", 0],
39
+ ["scrollingTo", void 0]
37
40
  ]),
38
41
  viewRefs: /* @__PURE__ */ new Map()
39
42
  }));
40
- return /* @__PURE__ */ React4.createElement(ContextState.Provider, { value }, children);
43
+ return /* @__PURE__ */ React3.createElement(ContextState.Provider, { value }, children);
41
44
  }
42
45
  function useStateContext() {
43
- return React4.useContext(ContextState);
46
+ return React3.useContext(ContextState);
44
47
  }
45
48
  function createSelectorFunctionsArr(ctx, signalNames) {
46
49
  let lastValues = [];
@@ -102,31 +105,32 @@ function set$(ctx, signalName, value) {
102
105
  }
103
106
  }
104
107
  function getContentSize(ctx) {
108
+ var _a3, _b;
105
109
  const { values } = ctx;
106
110
  const stylePaddingTop = values.get("stylePaddingTop") || 0;
107
111
  const headerSize = values.get("headerSize") || 0;
108
112
  const footerSize = values.get("footerSize") || 0;
109
- const totalSize = values.get("totalSize");
113
+ const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
110
114
  return headerSize + footerSize + totalSize + stylePaddingTop;
111
115
  }
112
116
  function useArr$(signalNames) {
113
- const ctx = React4.useContext(ContextState);
114
- const { subscribe, get } = React4.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
117
+ const ctx = React3.useContext(ContextState);
118
+ const { subscribe, get } = React3.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
115
119
  const value = useSyncExternalStore(subscribe, get);
116
120
  return value;
117
121
  }
118
122
  function useSelector$(signalName, selector) {
119
- const ctx = React4.useContext(ContextState);
120
- const { subscribe, get } = React4.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
123
+ const ctx = React3.useContext(ContextState);
124
+ const { subscribe, get } = React3.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
121
125
  const value = useSyncExternalStore(subscribe, () => selector(get()[0]));
122
126
  return value;
123
127
  }
124
128
 
125
129
  // src/components/DebugView.tsx
126
130
  var DebugRow = ({ children }) => {
127
- return /* @__PURE__ */ React4.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
131
+ return /* @__PURE__ */ React3.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
128
132
  };
129
- var DebugView = React4.memo(function DebugView2({ state }) {
133
+ var DebugView = React3.memo(function DebugView2({ state }) {
130
134
  const ctx = useStateContext();
131
135
  const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
132
136
  "totalSize",
@@ -141,7 +145,7 @@ var DebugView = React4.memo(function DebugView2({ state }) {
141
145
  useInterval(() => {
142
146
  forceUpdate();
143
147
  }, 100);
144
- return /* @__PURE__ */ React4.createElement(
148
+ return /* @__PURE__ */ React3.createElement(
145
149
  View,
146
150
  {
147
151
  pointerEvents: "none",
@@ -157,12 +161,12 @@ var DebugView = React4.memo(function DebugView2({ state }) {
157
161
  top: 0
158
162
  }
159
163
  },
160
- /* @__PURE__ */ React4.createElement(DebugRow, null, /* @__PURE__ */ React4.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React4.createElement(Text, null, totalSize.toFixed(2))),
161
- /* @__PURE__ */ React4.createElement(DebugRow, null, /* @__PURE__ */ React4.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React4.createElement(Text, null, contentSize.toFixed(2))),
162
- /* @__PURE__ */ React4.createElement(DebugRow, null, /* @__PURE__ */ React4.createElement(Text, null, "At end:"), /* @__PURE__ */ React4.createElement(Text, null, String(state.isAtEnd))),
163
- /* @__PURE__ */ React4.createElement(DebugRow, null, /* @__PURE__ */ React4.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React4.createElement(Text, null, scrollAdjust.toFixed(2))),
164
- /* @__PURE__ */ React4.createElement(DebugRow, null, /* @__PURE__ */ React4.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React4.createElement(Text, null, rawScroll.toFixed(2))),
165
- /* @__PURE__ */ React4.createElement(DebugRow, null, /* @__PURE__ */ React4.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React4.createElement(Text, null, scroll.toFixed(2)))
164
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React3.createElement(Text, null, totalSize.toFixed(2))),
165
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React3.createElement(Text, null, contentSize.toFixed(2))),
166
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "At end:"), /* @__PURE__ */ React3.createElement(Text, null, String(state.isAtEnd))),
167
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React3.createElement(Text, null, scrollAdjust.toFixed(2))),
168
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React3.createElement(Text, null, rawScroll.toFixed(2))),
169
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React3.createElement(Text, null, scroll.toFixed(2)))
166
170
  );
167
171
  });
168
172
  function useInterval(callback, delay) {
@@ -171,93 +175,63 @@ function useInterval(callback, delay) {
171
175
  return () => clearInterval(interval);
172
176
  }, [delay]);
173
177
  }
174
- var globalResizeObserver = null;
175
- function getGlobalResizeObserver() {
176
- if (!globalResizeObserver) {
177
- globalResizeObserver = new ResizeObserver((entries) => {
178
- for (const entry of entries) {
179
- const callbacks = callbackMap.get(entry.target);
180
- if (callbacks) {
181
- for (const callback of callbacks) {
182
- callback(entry);
183
- }
184
- }
185
- }
186
- });
187
- }
188
- return globalResizeObserver;
189
- }
190
- var callbackMap = /* @__PURE__ */ new WeakMap();
191
- function useResizeObserver(element, callback) {
192
- useEffect(() => {
193
- if (!element) return;
194
- const observer = getGlobalResizeObserver();
195
- let callbacks = callbackMap.get(element);
196
- if (!callbacks) {
197
- callbacks = /* @__PURE__ */ new Set();
198
- callbackMap.set(element, callbacks);
199
- observer.observe(element);
200
- }
201
- callbacks.add(callback);
202
- return () => {
203
- const callbacks2 = callbackMap.get(element);
204
- if (callbacks2) {
205
- callbacks2.delete(callback);
206
- if (callbacks2.size === 0) {
207
- callbackMap.delete(element);
208
- observer.unobserve(element);
209
- }
210
- }
211
- };
212
- }, [element, callback]);
213
- }
214
-
215
- // src/hooks/useSyncLayout.tsx
216
- function useSyncLayout({
217
- ref,
218
- onLayoutChange
219
- }) {
220
- var _a, _b;
221
- useResizeObserver(
222
- ((_b = (_a = ref.current) == null ? void 0 : _a.getScrollableNode) == null ? void 0 : _b.call(_a)) || ref.current,
223
- useCallback(
224
- (entry) => {
225
- onLayoutChange(entry.contentRect, false);
226
- },
227
- [onLayoutChange]
228
- )
229
- );
230
- useLayoutEffect(() => {
231
- if (ref.current) {
232
- const rect = ref.current.getBoundingClientRect();
233
- onLayoutChange(
234
- {
235
- height: rect.height,
236
- width: rect.width,
237
- x: rect.left,
238
- y: rect.top
239
- },
240
- true
241
- );
242
- }
243
- }, []);
244
- return {};
245
- }
246
178
 
247
- // src/components/LayoutView.tsx
248
- var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
249
- const ref = refView != null ? refView : useRef();
250
- useSyncLayout({ onLayoutChange, ref });
251
- return /* @__PURE__ */ React4.createElement("div", { ...rest, ref }, children);
252
- };
179
+ // src/utils/devEnvironment.ts
180
+ var metroDev = typeof __DEV__ !== "undefined" ? __DEV__ : void 0;
181
+ var _a;
182
+ var envMode = typeof process !== "undefined" && typeof process.env === "object" && process.env ? (_a = process.env.NODE_ENV) != null ? _a : process.env.MODE : void 0;
183
+ var processDev = typeof envMode === "string" ? envMode.toLowerCase() !== "production" : void 0;
184
+ var _a2;
185
+ var IS_DEV = (_a2 = metroDev != null ? metroDev : processDev) != null ? _a2 : false;
253
186
 
254
187
  // src/constants.ts
255
188
  var POSITION_OUT_OF_VIEW = -1e7;
256
- var ENABLE_DEVMODE = __DEV__ && false;
257
- var ENABLE_DEBUG_VIEW = __DEV__ && false;
189
+ var ENABLE_DEVMODE = IS_DEV && false;
190
+ var ENABLE_DEBUG_VIEW = IS_DEV && false;
258
191
  var typedForwardRef = forwardRef;
259
192
  var typedMemo = memo;
260
193
 
194
+ // src/utils/helpers.ts
195
+ function isFunction(obj) {
196
+ return typeof obj === "function";
197
+ }
198
+ function isArray(obj) {
199
+ return Array.isArray(obj);
200
+ }
201
+ var warned = /* @__PURE__ */ new Set();
202
+ function warnDevOnce(id, text) {
203
+ if (IS_DEV && !warned.has(id)) {
204
+ warned.add(id);
205
+ console.warn(`[legend-list] ${text}`);
206
+ }
207
+ }
208
+ function roundSize(size) {
209
+ return Math.floor(size * 8) / 8;
210
+ }
211
+ function isNullOrUndefined(value) {
212
+ return value === null || value === void 0;
213
+ }
214
+ function comparatorDefault(a, b) {
215
+ return a - b;
216
+ }
217
+ function getPadding(s, type) {
218
+ var _a3, _b, _c;
219
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
220
+ }
221
+ function extractPadding(style, contentContainerStyle, type) {
222
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
223
+ }
224
+ function findContainerId(ctx, key) {
225
+ const numContainers = peek$(ctx, "numContainers");
226
+ for (let i = 0; i < numContainers; i++) {
227
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
228
+ if (itemKey === key) {
229
+ return i;
230
+ }
231
+ }
232
+ return -1;
233
+ }
234
+
261
235
  // src/components/PositionView.tsx
262
236
  var PositionViewState = typedMemo(function PositionView({
263
237
  id,
@@ -267,9 +241,12 @@ var PositionViewState = typedMemo(function PositionView({
267
241
  ...rest
268
242
  }) {
269
243
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
270
- const base = Array.isArray(style) ? Object.assign({}, ...style) : style;
271
- const combinedStyle = horizontal ? { ...base, left: position } : { ...base, top: position };
272
- return /* @__PURE__ */ React4.createElement(LayoutView, { refView, style: combinedStyle, ...rest });
244
+ const base = {
245
+ contain: "paint layout style"
246
+ };
247
+ const composed = isArray(style) ? Object.assign({}, ...style) : style;
248
+ const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
249
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: combinedStyle, ...rest });
273
250
  });
274
251
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
275
252
  id,
@@ -277,35 +254,47 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
277
254
  style,
278
255
  refView,
279
256
  index,
257
+ stickyOffset,
258
+ animatedScrollY: _animatedScrollY,
259
+ children,
280
260
  ...rest
281
261
  }) {
282
- const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
283
- const viewStyle = React4.useMemo(() => {
284
- const base = Array.isArray(style) ? Object.assign({}, ...style) : style;
285
- const axisStyle = horizontal ? { transform: `translateX(${position}px)` } : { top: position };
286
- return {
287
- ...base,
288
- zIndex: index + 1e3,
289
- ...axisStyle
290
- };
291
- }, [style, position, horizontal, index]);
292
- return /* @__PURE__ */ React4.createElement(LayoutView, { refView, style: viewStyle, ...rest });
262
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, activeStickyIndex] = useArr$([
263
+ `containerPosition${id}`,
264
+ "headerSize",
265
+ "activeStickyIndex"
266
+ ]);
267
+ const base = {
268
+ contain: "paint layout style"
269
+ };
270
+ const composed = React3.useMemo(
271
+ () => {
272
+ var _a3;
273
+ return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
274
+ },
275
+ [style]
276
+ );
277
+ const viewStyle = React3.useMemo(() => {
278
+ var _a3;
279
+ const styleBase = { ...base, ...composed };
280
+ delete styleBase.transform;
281
+ const offset = (_a3 = stickyOffset != null ? stickyOffset : headerSize) != null ? _a3 : 0;
282
+ const isActive = activeStickyIndex === index;
283
+ styleBase.position = isActive ? "sticky" : "absolute";
284
+ styleBase.zIndex = index + 1e3;
285
+ if (horizontal) {
286
+ styleBase.left = isActive ? offset : position;
287
+ } else {
288
+ styleBase.top = isActive ? offset : position;
289
+ }
290
+ return styleBase;
291
+ }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
292
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
293
293
  });
294
294
  var PositionView2 = PositionViewState;
295
- function Separator({ ItemSeparatorComponent, itemKey, leadingItem }) {
296
- const [lastItemKeys] = useArr$(["lastItemKeys"]);
297
- const isALastItem = lastItemKeys.includes(itemKey);
298
- return isALastItem ? null : /* @__PURE__ */ React4.createElement(ItemSeparatorComponent, { leadingItem });
299
- }
300
295
 
301
296
  // src/constants-platform.ts
302
297
  var IsNewArchitecture = true;
303
-
304
- // src/platform/Platform.ts
305
- var Platform = {
306
- // Widen the type to avoid unreachable-branch lints in cross-platform code that compares against other OSes
307
- OS: "web"
308
- };
309
298
  var symbolFirst = Symbol();
310
299
  function useInit(cb) {
311
300
  const refValue = useRef(symbolFirst);
@@ -315,37 +304,6 @@ function useInit(cb) {
315
304
  return refValue.current;
316
305
  }
317
306
 
318
- // src/utils/helpers.ts
319
- function isFunction(obj) {
320
- return typeof obj === "function";
321
- }
322
- function isArray(obj) {
323
- return Array.isArray(obj);
324
- }
325
- var warned = /* @__PURE__ */ new Set();
326
- function warnDevOnce(id, text) {
327
- if (__DEV__ && !warned.has(id)) {
328
- warned.add(id);
329
- console.warn(`[legend-list] ${text}`);
330
- }
331
- }
332
- function roundSize(size) {
333
- return Math.floor(size * 8) / 8;
334
- }
335
- function isNullOrUndefined(value) {
336
- return value === null || value === void 0;
337
- }
338
- function comparatorDefault(a, b) {
339
- return a - b;
340
- }
341
- function getPadding(s, type) {
342
- var _a, _b, _c;
343
- return (_c = (_b = (_a = s[`padding${type}`]) != null ? _a : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
344
- }
345
- function extractPadding(style, contentContainerStyle, type) {
346
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
347
- }
348
-
349
307
  // src/state/ContextContainer.ts
350
308
  var ContextContainer = createContext(null);
351
309
  function useViewability(callback, configId) {
@@ -442,13 +400,114 @@ function useListScrollSize() {
442
400
  const [scrollSize] = useArr$(["scrollSize"]);
443
401
  return scrollSize;
444
402
  }
445
- function useSyncLayout2() {
403
+ function useSyncLayout() {
446
404
  {
447
405
  const { triggerLayout: syncLayout } = useContext(ContextContainer);
448
406
  return syncLayout;
449
407
  }
450
408
  }
451
409
 
410
+ // src/components/Separator.tsx
411
+ function Separator({ ItemSeparatorComponent, leadingItem }) {
412
+ const isLastItem = useIsLastItem();
413
+ return isLastItem ? null : /* @__PURE__ */ React3.createElement(ItemSeparatorComponent, { leadingItem });
414
+ }
415
+
416
+ // src/hooks/createResizeObserver.ts
417
+ var globalResizeObserver = null;
418
+ function getGlobalResizeObserver() {
419
+ if (!globalResizeObserver) {
420
+ globalResizeObserver = new ResizeObserver((entries) => {
421
+ for (const entry of entries) {
422
+ const callbacks = callbackMap.get(entry.target);
423
+ if (callbacks) {
424
+ for (const callback of callbacks) {
425
+ callback(entry);
426
+ }
427
+ }
428
+ }
429
+ });
430
+ }
431
+ return globalResizeObserver;
432
+ }
433
+ var callbackMap = /* @__PURE__ */ new WeakMap();
434
+ function createResizeObserver(element, callback) {
435
+ if (typeof ResizeObserver === "undefined") {
436
+ return () => {
437
+ };
438
+ }
439
+ if (!element) {
440
+ return () => {
441
+ };
442
+ }
443
+ const observer = getGlobalResizeObserver();
444
+ let callbacks = callbackMap.get(element);
445
+ if (!callbacks) {
446
+ callbacks = /* @__PURE__ */ new Set();
447
+ callbackMap.set(element, callbacks);
448
+ observer.observe(element);
449
+ }
450
+ callbacks.add(callback);
451
+ return () => {
452
+ const callbacks2 = callbackMap.get(element);
453
+ if (callbacks2) {
454
+ callbacks2.delete(callback);
455
+ if (callbacks2.size === 0) {
456
+ callbackMap.delete(element);
457
+ observer.unobserve(element);
458
+ }
459
+ }
460
+ };
461
+ }
462
+
463
+ // src/hooks/useOnLayoutSync.tsx
464
+ function useOnLayoutSync({
465
+ ref,
466
+ onLayoutProp,
467
+ onLayoutChange
468
+ }, deps) {
469
+ useLayoutEffect(() => {
470
+ var _a3, _b;
471
+ const current = ref.current;
472
+ const scrollableNode = (_b = (_a3 = current == null ? void 0 : current.getScrollableNode) == null ? void 0 : _a3.call(current)) != null ? _b : null;
473
+ const element = scrollableNode || current;
474
+ if (!element) {
475
+ return;
476
+ }
477
+ const emit = (layout, fromLayoutEffect) => {
478
+ if (layout.height === 0 && layout.width === 0) {
479
+ return;
480
+ }
481
+ onLayoutChange(layout, fromLayoutEffect);
482
+ onLayoutProp == null ? void 0 : onLayoutProp({ nativeEvent: { layout } });
483
+ };
484
+ const rect = element.getBoundingClientRect();
485
+ emit(toLayout(rect), true);
486
+ let prevRect = rect;
487
+ return createResizeObserver(element, (entry) => {
488
+ var _a4;
489
+ const target = entry.target instanceof HTMLElement ? entry.target : void 0;
490
+ const rect2 = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
491
+ if (rect2.width !== prevRect.width || rect2.height !== prevRect.height) {
492
+ prevRect = rect2;
493
+ emit(toLayout(rect2), false);
494
+ }
495
+ });
496
+ }, deps || []);
497
+ return {};
498
+ }
499
+ function toLayout(rect) {
500
+ if (!rect) {
501
+ return { height: 0, width: 0, x: 0, y: 0 };
502
+ }
503
+ return {
504
+ height: rect.height,
505
+ width: rect.width,
506
+ x: rect.left,
507
+ y: rect.top
508
+ };
509
+ }
510
+
452
511
  // src/components/Container.tsx
453
512
  var Container = typedMemo(function Container2({
454
513
  id,
@@ -459,37 +518,42 @@ var Container = typedMemo(function Container2({
459
518
  ItemSeparatorComponent
460
519
  }) {
461
520
  const ctx = useStateContext();
462
- const { columnWrapperStyle } = ctx;
463
- const [column = 0, data, itemKey, numColumns, extraData, isSticky] = useArr$([
521
+ const { columnWrapperStyle, animatedScrollY } = ctx;
522
+ const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
464
523
  `containerColumn${id}`,
465
524
  `containerItemData${id}`,
466
525
  `containerItemKey${id}`,
467
526
  "numColumns",
468
527
  "extraData",
469
- `containerSticky${id}`
528
+ `containerSticky${id}`,
529
+ `containerStickyOffset${id}`
470
530
  ]);
471
- const refLastSize = useRef();
531
+ const itemLayoutRef = useRef({
532
+ horizontal,
533
+ itemKey,
534
+ updateItemSize: updateItemSize2
535
+ });
536
+ itemLayoutRef.current.horizontal = horizontal;
537
+ itemLayoutRef.current.itemKey = itemKey;
538
+ itemLayoutRef.current.updateItemSize = updateItemSize2;
472
539
  const ref = useRef(null);
473
- const [_, forceLayoutRender] = useState(0);
540
+ const [layoutRenderCount, forceLayoutRender] = useState(0);
474
541
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
475
542
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
543
+ const didLayoutRef = useRef(false);
476
544
  const style = useMemo(() => {
477
545
  let paddingStyles;
478
546
  if (columnWrapperStyle) {
479
547
  const { columnGap, rowGap, gap } = columnWrapperStyle;
480
548
  if (horizontal) {
481
- const py = numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0;
482
549
  paddingStyles = {
483
- paddingBottom: py,
484
550
  paddingRight: columnGap || gap || void 0,
485
- paddingTop: py
551
+ paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
486
552
  };
487
553
  } else {
488
- const px = numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0;
489
554
  paddingStyles = {
490
555
  paddingBottom: rowGap || gap || void 0,
491
- paddingLeft: px,
492
- paddingRight: px
556
+ paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
493
557
  };
494
558
  }
495
559
  }
@@ -503,7 +567,7 @@ var Container = typedMemo(function Container2({
503
567
  } : {
504
568
  left: otherAxisPos,
505
569
  position: "absolute",
506
- right: numColumns > 1 ? void 0 : 0,
570
+ right: numColumns > 1 ? null : 0,
507
571
  top: 0,
508
572
  width: otherAxisSize,
509
573
  ...paddingStyles || {}
@@ -526,49 +590,64 @@ var Container = typedMemo(function Container2({
526
590
  value: data
527
591
  };
528
592
  }, [id, itemKey, index, data]);
529
- const onLayoutChange = (rectangle) => {
530
- if (!isNullOrUndefined(itemKey)) {
531
- let layout = rectangle;
532
- layout[horizontal ? "width" : "height"];
533
- const doUpdate = () => {
534
- refLastSize.current = { height: layout.height, width: layout.width };
535
- updateItemSize2(itemKey, layout);
536
- };
537
- {
538
- doUpdate();
539
- }
593
+ const onLayoutChange = useCallback((rectangle) => {
594
+ const {
595
+ horizontal: currentHorizontal,
596
+ itemKey: currentItemKey,
597
+ updateItemSize: updateItemSizeFn
598
+ } = itemLayoutRef.current;
599
+ if (isNullOrUndefined(currentItemKey)) {
600
+ return;
540
601
  }
541
- };
602
+ didLayoutRef.current = true;
603
+ let layout = rectangle;
604
+ roundSize(rectangle[currentHorizontal ? "width" : "height"]);
605
+ const doUpdate = () => {
606
+ itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
607
+ updateItemSizeFn(currentItemKey, layout);
608
+ didLayoutRef.current = true;
609
+ };
610
+ {
611
+ doUpdate();
612
+ }
613
+ }, []);
614
+ const { onLayout } = useOnLayoutSync(
615
+ {
616
+ onLayoutChange,
617
+ ref
618
+ },
619
+ [itemKey, layoutRenderCount]
620
+ );
542
621
  const PositionComponent = isSticky ? PositionViewSticky : PositionView2;
543
- return /* @__PURE__ */ React4.createElement(ContextContainer.Provider, { value: contextValue }, /* @__PURE__ */ React4.createElement(
622
+ return /* @__PURE__ */ React3.createElement(
544
623
  PositionComponent,
545
624
  {
625
+ animatedScrollY: isSticky ? animatedScrollY : void 0,
546
626
  horizontal,
547
627
  id,
548
628
  index,
549
629
  key: recycleItems ? void 0 : itemKey,
550
- onLayoutChange,
630
+ onLayout,
551
631
  refView: ref,
632
+ stickyOffset: isSticky ? stickyOffset : void 0,
552
633
  style
553
634
  },
554
- renderedItem,
555
- renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React4.createElement(
556
- Separator,
557
- {
558
- ItemSeparatorComponent,
559
- itemKey,
560
- leadingItem: renderedItemInfo.item
561
- }
562
- )
563
- ));
635
+ /* @__PURE__ */ React3.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3.createElement(Separator, { ItemSeparatorComponent, leadingItem: renderedItemInfo.item }))
636
+ );
564
637
  });
565
638
 
639
+ // src/platform/Platform.ts
640
+ var Platform = {
641
+ // Widen the type to avoid unreachable-branch lints in cross-platform code that compares against other OSes
642
+ OS: "web"
643
+ };
644
+
566
645
  // src/utils/reordering.ts
567
646
  var mapFn = (element) => {
568
647
  const indexStr = element.getAttribute("index");
569
648
  return [element, indexStr === null ? null : parseInt(indexStr)];
570
649
  };
571
- function sortDOMElementsPatience(container) {
650
+ function sortDOMElements(container) {
572
651
  const elements = Array.from(container.children);
573
652
  if (elements.length <= 1) return elements;
574
653
  const items = elements.map(mapFn);
@@ -659,7 +738,7 @@ function useDOMOrder(ref) {
659
738
  debounceRef.current = setTimeout(() => {
660
739
  const parent = ref.current;
661
740
  if (parent) {
662
- sortDOMElementsPatience(parent);
741
+ sortDOMElements(parent);
663
742
  }
664
743
  debounceRef.current = void 0;
665
744
  }, 500);
@@ -680,7 +759,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
680
759
  const columnWrapperStyle = ctx.columnWrapperStyle;
681
760
  const [totalSize, otherAxisSize] = useArr$(["totalSize", "otherAxisSize"]);
682
761
  useDOMOrder(ref);
683
- const style = horizontal ? { minHeight: otherAxisSize, width: totalSize } : { height: totalSize, minWidth: otherAxisSize };
762
+ const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
684
763
  if (columnWrapperStyle && numColumns > 1) {
685
764
  const { columnGap, rowGap, gap } = columnWrapperStyle;
686
765
  const gapX = columnGap || gap || 0;
@@ -701,7 +780,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
701
780
  }
702
781
  }
703
782
  }
704
- return /* @__PURE__ */ React4.createElement("div", { ref, style }, children);
783
+ return /* @__PURE__ */ React3.createElement("div", { ref, style }, children);
705
784
  });
706
785
  var Containers = typedMemo(function Containers2({
707
786
  horizontal,
@@ -715,7 +794,7 @@ var Containers = typedMemo(function Containers2({
715
794
  const containers = [];
716
795
  for (let i = 0; i < numContainers; i++) {
717
796
  containers.push(
718
- /* @__PURE__ */ React4.createElement(
797
+ /* @__PURE__ */ React3.createElement(
719
798
  Container,
720
799
  {
721
800
  getRenderedItem: getRenderedItem2,
@@ -729,24 +808,40 @@ var Containers = typedMemo(function Containers2({
729
808
  )
730
809
  );
731
810
  }
732
- return /* @__PURE__ */ React4.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
733
- });
734
- var DevNumbers = __DEV__ && React4.memo(function DevNumbers2() {
735
- return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React4.createElement(
736
- View,
737
- {
738
- key: index,
739
- style: {
740
- height: 100,
741
- pointerEvents: "none",
742
- position: "absolute",
743
- top: index * 100,
744
- width: "100%"
745
- }
746
- },
747
- /* @__PURE__ */ React4.createElement(Text, { style: { color: "red" } }, index * 100)
748
- ));
811
+ return /* @__PURE__ */ React3.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
749
812
  });
813
+ function DevNumbers() {
814
+ return IS_DEV && React3.memo(function DevNumbers2() {
815
+ return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3.createElement(
816
+ "div",
817
+ {
818
+ key: index,
819
+ style: {
820
+ height: 100,
821
+ pointerEvents: "none",
822
+ position: "absolute",
823
+ top: index * 100,
824
+ width: "100%"
825
+ }
826
+ },
827
+ /* @__PURE__ */ React3.createElement("div", { style: { color: "red" } }, index * 100)
828
+ ));
829
+ });
830
+ }
831
+
832
+ // src/platform/StyleSheet.tsx
833
+ function flattenStyles(styles) {
834
+ if (isArray(styles)) {
835
+ return Object.assign({}, ...styles.filter(Boolean));
836
+ }
837
+ return styles;
838
+ }
839
+ var StyleSheet = {
840
+ create: (styles) => styles,
841
+ flatten: (style) => flattenStyles(style)
842
+ };
843
+
844
+ // src/components/ListComponentScrollView.tsx
750
845
  var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
751
846
  children,
752
847
  style,
@@ -760,7 +855,6 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
760
855
  showsVerticalScrollIndicator = true,
761
856
  refreshControl,
762
857
  onLayout,
763
- ScrollComponent,
764
858
  ...props
765
859
  }, ref) {
766
860
  const scrollRef = useRef(null);
@@ -769,16 +863,15 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
769
863
  useImperativeHandle(ref, () => {
770
864
  const api = {
771
865
  getBoundingClientRect: () => {
772
- var _a;
773
- return (_a = scrollRef.current) == null ? void 0 : _a.getBoundingClientRect();
866
+ var _a3;
867
+ return (_a3 = scrollRef.current) == null ? void 0 : _a3.getBoundingClientRect();
774
868
  },
775
869
  getScrollableNode: () => scrollRef.current,
776
870
  getScrollResponder: () => scrollRef.current,
777
- scrollBy: (options) => {
871
+ scrollBy: (x, y) => {
778
872
  const el = scrollRef.current;
779
873
  if (!el) return;
780
- const { x = 0, y = 0, animated = true } = options;
781
- el.scrollBy({ behavior: animated ? "smooth" : "auto", left: x, top: y });
874
+ el.scrollBy(x, y);
782
875
  },
783
876
  scrollTo: (options) => {
784
877
  const el = scrollRef.current;
@@ -848,17 +941,21 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
848
941
  useLayoutEffect(() => {
849
942
  const element = scrollRef.current;
850
943
  if (!element) return;
851
- element.addEventListener("scroll", handleScroll, { passive: true });
944
+ element.addEventListener("scroll", handleScroll);
852
945
  return () => {
853
946
  element.removeEventListener("scroll", handleScroll);
854
947
  };
855
948
  }, [handleScroll]);
856
- useLayoutEffect(() => {
857
- if (contentOffset && scrollRef.current) {
858
- scrollRef.current.scrollLeft = contentOffset.x || 0;
859
- scrollRef.current.scrollTop = contentOffset.y || 0;
860
- }
861
- }, [contentOffset]);
949
+ useEffect(() => {
950
+ const doScroll = () => {
951
+ if (contentOffset && scrollRef.current) {
952
+ scrollRef.current.scrollLeft = contentOffset.x || 0;
953
+ scrollRef.current.scrollTop = contentOffset.y || 0;
954
+ }
955
+ };
956
+ doScroll();
957
+ requestAnimationFrame(doScroll);
958
+ }, [contentOffset == null ? void 0 : contentOffset.x, contentOffset == null ? void 0 : contentOffset.y]);
862
959
  useLayoutEffect(() => {
863
960
  if (!onLayout || !scrollRef.current) return;
864
961
  const element = scrollRef.current;
@@ -890,41 +987,80 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
890
987
  // Ensure proper positioning context
891
988
  WebkitOverflowScrolling: "touch",
892
989
  // iOS momentum scrolling
893
- ...style
990
+ ...StyleSheet.flatten(style)
894
991
  };
895
992
  const contentStyle = {
896
993
  display: horizontal ? "flex" : "block",
897
994
  flexDirection: horizontal ? "row" : void 0,
898
995
  minHeight: horizontal ? void 0 : "100%",
899
996
  minWidth: horizontal ? "100%" : void 0,
900
- ...contentContainerStyle
997
+ ...StyleSheet.flatten(contentContainerStyle)
901
998
  };
902
- return /* @__PURE__ */ React4.createElement("div", { ref: scrollRef, style: scrollViewStyle, ...props }, refreshControl, /* @__PURE__ */ React4.createElement("div", { ref: contentRef, style: contentStyle }, children));
999
+ return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, style: scrollViewStyle, ...props }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
903
1000
  });
1001
+ function Padding() {
1002
+ const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
1003
+ return /* @__PURE__ */ React3.createElement("div", { style: { paddingTop } });
1004
+ }
1005
+ function PaddingDevMode() {
1006
+ const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
1007
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("div", { style: { paddingTop } }), /* @__PURE__ */ React3.createElement(
1008
+ "div",
1009
+ {
1010
+ style: {
1011
+ backgroundColor: "green",
1012
+ height: paddingTop,
1013
+ left: 0,
1014
+ position: "absolute",
1015
+ right: 0,
1016
+ top: 0
1017
+ }
1018
+ }
1019
+ ));
1020
+ }
904
1021
  function useValueListener$(key, callback) {
905
1022
  const ctx = useStateContext();
906
1023
  useLayoutEffect(() => {
907
- listen$(ctx, key, (value) => {
1024
+ const unsubscribe = listen$(ctx, key, (value) => {
908
1025
  callback(value);
909
1026
  });
910
- }, []);
1027
+ return unsubscribe;
1028
+ }, [callback, ctx, key]);
911
1029
  }
912
1030
 
913
1031
  // src/components/ScrollAdjust.tsx
914
1032
  function ScrollAdjust() {
915
1033
  const ctx = useStateContext();
916
- const lastScrollOffsetRef = React4.useRef(0);
917
- const callback = React4.useCallback(() => {
918
- var _a;
1034
+ const lastScrollOffsetRef = React3.useRef(0);
1035
+ const callback = React3.useCallback(() => {
1036
+ var _a3;
919
1037
  const scrollAdjust = peek$(ctx, "scrollAdjust");
920
1038
  const scrollAdjustUserOffset = peek$(ctx, "scrollAdjustUserOffset");
921
1039
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0);
922
- const scrollView = (_a = ctx.internalState) == null ? void 0 : _a.refScroller.current;
1040
+ const scrollView = (_a3 = ctx.internalState) == null ? void 0 : _a3.refScroller.current;
923
1041
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
924
1042
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
925
1043
  if (scrollDelta !== 0) {
926
- scrollView.scrollBy(0, scrollDelta);
927
- console.log("ScrollAdjust (web scrollBy)", scrollDelta, "total offset:", scrollOffset);
1044
+ const el = scrollView.getScrollableNode();
1045
+ const prevScroll = el.scrollTop;
1046
+ const nextScroll = prevScroll + scrollDelta;
1047
+ const totalSize = el.scrollHeight;
1048
+ if (scrollDelta > 0 && !ctx.internalState.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1049
+ const child = el.firstElementChild;
1050
+ const prevPaddingBottom = child.style.paddingBottom;
1051
+ const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1052
+ child.style.paddingBottom = `${pad}px`;
1053
+ void el.offsetHeight;
1054
+ scrollView.scrollBy(0, scrollDelta);
1055
+ setTimeout(() => {
1056
+ child.style.paddingBottom = prevPaddingBottom;
1057
+ }, 100);
1058
+ } else {
1059
+ scrollView.scrollBy(0, scrollDelta);
1060
+ }
1061
+ if (IS_DEV) {
1062
+ console.log("ScrollAdjust (web scrollBy)", scrollDelta, "total offset:", scrollOffset);
1063
+ }
928
1064
  }
929
1065
  lastScrollOffsetRef.current = scrollOffset;
930
1066
  }
@@ -933,49 +1069,26 @@ function ScrollAdjust() {
933
1069
  useValueListener$("scrollAdjustUserOffset", callback);
934
1070
  return null;
935
1071
  }
936
-
937
- // src/components/SnapWrapper.tsx
938
1072
  function SnapWrapper({ ScrollComponent, ...props }) {
939
1073
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
940
- return /* @__PURE__ */ React.createElement(ScrollComponent, { ...props, snapToOffsets });
941
- }
942
-
943
- // src/hooks/useValue$.ts
944
- function useValue$(key, params) {
945
- const [value] = useArr$([key]);
946
- return value;
1074
+ return /* @__PURE__ */ React3.createElement(ScrollComponent, { ...props, snapToOffsets });
947
1075
  }
1076
+ var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1077
+ const ref = refView != null ? refView : useRef();
1078
+ useOnLayoutSync({ onLayoutChange, ref });
1079
+ return /* @__PURE__ */ React3.createElement("div", { ...rest, ref }, children);
1080
+ };
948
1081
 
949
1082
  // src/components/ListComponent.tsx
950
1083
  var getComponent = (Component) => {
951
- if (React4.isValidElement(Component)) {
1084
+ if (React3.isValidElement(Component)) {
952
1085
  return Component;
953
1086
  }
954
1087
  if (Component) {
955
- return /* @__PURE__ */ React4.createElement(Component, null);
1088
+ return /* @__PURE__ */ React3.createElement(Component, null);
956
1089
  }
957
1090
  return null;
958
1091
  };
959
- var Padding = () => {
960
- const animPaddingTop = useValue$("alignItemsPaddingTop");
961
- return /* @__PURE__ */ React4.createElement(AnimatedView, { style: { paddingTop: animPaddingTop } });
962
- };
963
- var PaddingDevMode = () => {
964
- const animPaddingTop = useValue$("alignItemsPaddingTop");
965
- return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(AnimatedView, { style: { paddingTop: animPaddingTop } }), /* @__PURE__ */ React4.createElement(
966
- AnimatedView,
967
- {
968
- style: {
969
- backgroundColor: "green",
970
- height: animPaddingTop,
971
- left: 0,
972
- position: "absolute",
973
- right: 0,
974
- top: 0
975
- }
976
- }
977
- ));
978
- };
979
1092
  var ListComponent = typedMemo(function ListComponent2({
980
1093
  canRender,
981
1094
  style,
@@ -1001,16 +1114,15 @@ var ListComponent = typedMemo(function ListComponent2({
1001
1114
  scrollAdjustHandler,
1002
1115
  onLayoutHeader,
1003
1116
  snapToIndices,
1004
- stickyIndices,
1117
+ stickyHeaderIndices,
1005
1118
  ...rest
1006
1119
  }) {
1007
1120
  const ctx = useStateContext();
1008
- const refHeader = React4.useRef(null);
1009
1121
  const ScrollComponent = renderScrollComponent ? useMemo(
1010
- () => React4.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1122
+ () => React3.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1011
1123
  [renderScrollComponent]
1012
1124
  ) : ListComponentScrollView;
1013
- React4.useEffect(() => {
1125
+ React3.useEffect(() => {
1014
1126
  if (canRender) {
1015
1127
  setTimeout(() => {
1016
1128
  scrollAdjustHandler.setMounted();
@@ -1018,39 +1130,30 @@ var ListComponent = typedMemo(function ListComponent2({
1018
1130
  }
1019
1131
  }, [canRender]);
1020
1132
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1021
- const contentContainerStyleWeb = useMemo(() => {
1022
- const base = contentContainerStyle || void 0;
1023
- if (!horizontal) return base;
1024
- if (base && base.height === "100%") return base;
1025
- return { ...base || {}, height: "100%" };
1026
- }, [horizontal, (contentContainerStyle == null ? void 0 : contentContainerStyle.height) === "100%" ? 1 : 0]);
1027
- return /* @__PURE__ */ React4.createElement(
1133
+ return /* @__PURE__ */ React3.createElement(
1028
1134
  SnapOrScroll,
1029
1135
  {
1030
1136
  ...rest,
1031
- contentContainerStyle: contentContainerStyleWeb,
1137
+ contentContainerStyle: [
1138
+ contentContainerStyle,
1139
+ horizontal ? {
1140
+ height: "100%"
1141
+ } : {}
1142
+ ],
1032
1143
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1033
1144
  horizontal,
1034
- maintainVisibleContentPosition: maintainVisibleContentPosition && !ListEmptyComponent ? { minIndexForVisible: 0 } : void 0,
1145
+ maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
1035
1146
  onLayout,
1036
1147
  onScroll: onScroll2,
1037
1148
  ref: refScrollView,
1038
1149
  ScrollComponent: snapToIndices ? ScrollComponent : void 0,
1039
1150
  style
1040
1151
  },
1041
- maintainVisibleContentPosition && /* @__PURE__ */ React4.createElement(ScrollAdjust, null),
1042
- ENABLE_DEVMODE ? /* @__PURE__ */ React4.createElement(PaddingDevMode, null) : /* @__PURE__ */ React4.createElement(Padding, null),
1043
- ListHeaderComponent && /* @__PURE__ */ React4.createElement(
1044
- LayoutView,
1045
- {
1046
- onLayoutChange: onLayoutHeader,
1047
- refView: refHeader,
1048
- style: ListHeaderComponentStyle
1049
- },
1050
- getComponent(ListHeaderComponent)
1051
- ),
1152
+ /* @__PURE__ */ React3.createElement(ScrollAdjust, null),
1153
+ ENABLE_DEVMODE ? /* @__PURE__ */ React3.createElement(PaddingDevMode, null) : /* @__PURE__ */ React3.createElement(Padding, null),
1154
+ ListHeaderComponent && /* @__PURE__ */ React3.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
1052
1155
  ListEmptyComponent && getComponent(ListEmptyComponent),
1053
- canRender && /* @__PURE__ */ React4.createElement(
1156
+ canRender && !ListEmptyComponent && /* @__PURE__ */ React3.createElement(
1054
1157
  Containers,
1055
1158
  {
1056
1159
  getRenderedItem: getRenderedItem2,
@@ -1061,7 +1164,7 @@ var ListComponent = typedMemo(function ListComponent2({
1061
1164
  waitForInitialLayout
1062
1165
  }
1063
1166
  ),
1064
- ListFooterComponent && /* @__PURE__ */ React4.createElement(
1167
+ ListFooterComponent && /* @__PURE__ */ React3.createElement(
1065
1168
  LayoutView,
1066
1169
  {
1067
1170
  onLayoutChange: (layout) => {
@@ -1072,7 +1175,7 @@ var ListComponent = typedMemo(function ListComponent2({
1072
1175
  },
1073
1176
  getComponent(ListFooterComponent)
1074
1177
  ),
1075
- __DEV__ && ENABLE_DEVMODE && /* @__PURE__ */ React4.createElement(DevNumbers, null)
1178
+ IS_DEV && ENABLE_DEVMODE && /* @__PURE__ */ React3.createElement(DevNumbers, null)
1076
1179
  );
1077
1180
  });
1078
1181
 
@@ -1084,7 +1187,7 @@ function getId(state, index) {
1084
1187
  }
1085
1188
  const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1086
1189
  const id = ret;
1087
- state.idCache.set(index, id);
1190
+ state.idCache[index] = id;
1088
1191
  return id;
1089
1192
  }
1090
1193
 
@@ -1105,13 +1208,84 @@ function calculateOffsetForIndex(ctx, state, index) {
1105
1208
  return position;
1106
1209
  }
1107
1210
 
1211
+ // src/utils/setPaddingTop.ts
1212
+ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1213
+ if (stylePaddingTop !== void 0) {
1214
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1215
+ if (stylePaddingTop < prevStylePaddingTop) {
1216
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
1217
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1218
+ state.timeoutSetPaddingTop = setTimeout(() => {
1219
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
1220
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1221
+ }, 16);
1222
+ }
1223
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
1224
+ }
1225
+ if (alignItemsPaddingTop !== void 0) {
1226
+ set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1227
+ }
1228
+ }
1229
+
1230
+ // src/utils/updateAlignItemsPaddingTop.ts
1231
+ function updateAlignItemsPaddingTop(ctx, state) {
1232
+ const {
1233
+ scrollLength,
1234
+ props: { alignItemsAtEnd, data }
1235
+ } = state;
1236
+ if (alignItemsAtEnd) {
1237
+ let alignItemsPaddingTop = 0;
1238
+ if ((data == null ? void 0 : data.length) > 0) {
1239
+ const contentSize = getContentSize(ctx);
1240
+ alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1241
+ }
1242
+ setPaddingTop(ctx, state, { alignItemsPaddingTop });
1243
+ }
1244
+ }
1245
+
1246
+ // src/core/addTotalSize.ts
1247
+ function addTotalSize(ctx, state, key, add) {
1248
+ const { alignItemsAtEnd } = state.props;
1249
+ const prevTotalSize = state.totalSize;
1250
+ let totalSize = state.totalSize;
1251
+ if (key === null) {
1252
+ totalSize = add;
1253
+ if (state.timeoutSetPaddingTop) {
1254
+ clearTimeout(state.timeoutSetPaddingTop);
1255
+ state.timeoutSetPaddingTop = void 0;
1256
+ }
1257
+ } else {
1258
+ totalSize += add;
1259
+ }
1260
+ if (prevTotalSize !== totalSize) {
1261
+ {
1262
+ state.pendingTotalSize = void 0;
1263
+ state.totalSize = totalSize;
1264
+ set$(ctx, "totalSize", totalSize);
1265
+ if (alignItemsAtEnd) {
1266
+ updateAlignItemsPaddingTop(ctx, state);
1267
+ }
1268
+ }
1269
+ }
1270
+ }
1271
+
1272
+ // src/core/setSize.ts
1273
+ function setSize(ctx, state, itemKey, size) {
1274
+ const { sizes } = state;
1275
+ const previousSize = sizes.get(itemKey);
1276
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1277
+ if (diff !== 0) {
1278
+ addTotalSize(ctx, state, itemKey, diff);
1279
+ }
1280
+ sizes.set(itemKey, size);
1281
+ }
1282
+
1108
1283
  // src/utils/getItemSize.ts
1109
- function getItemSize(state, key, index, data, useAverageSize) {
1110
- var _a, _b;
1284
+ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1285
+ var _a3, _b;
1111
1286
  const {
1112
1287
  sizesKnown,
1113
1288
  sizes,
1114
- scrollingTo,
1115
1289
  averageSizes,
1116
1290
  props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1117
1291
  } = state;
@@ -1120,7 +1294,14 @@ function getItemSize(state, key, index, data, useAverageSize) {
1120
1294
  return sizeKnown;
1121
1295
  }
1122
1296
  let size;
1123
- const itemType = getItemType ? (_a = getItemType(data, index)) != null ? _a : "" : "";
1297
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1298
+ const scrollingTo = peek$(ctx, "scrollingTo");
1299
+ if (preferCachedSize) {
1300
+ const cachedSize = sizes.get(key);
1301
+ if (cachedSize !== void 0) {
1302
+ return cachedSize;
1303
+ }
1304
+ }
1124
1305
  if (getFixedItemSize) {
1125
1306
  size = getFixedItemSize(index, data, itemType);
1126
1307
  if (size !== void 0) {
@@ -1142,53 +1323,234 @@ function getItemSize(state, key, index, data, useAverageSize) {
1142
1323
  if (size === void 0) {
1143
1324
  size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1144
1325
  }
1145
- sizes.set(key, size);
1326
+ setSize(ctx, state, key, size);
1146
1327
  return size;
1147
1328
  }
1148
1329
 
1149
1330
  // src/core/calculateOffsetWithOffsetPosition.ts
1150
- function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
1331
+ function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1151
1332
  const { index, viewOffset, viewPosition } = params;
1152
1333
  let offset = offsetParam;
1153
1334
  if (viewOffset) {
1154
1335
  offset -= viewOffset;
1155
1336
  }
1156
1337
  if (viewPosition !== void 0 && index !== void 0) {
1157
- offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
1338
+ offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1158
1339
  }
1159
1340
  return offset;
1160
1341
  }
1161
1342
 
1162
1343
  // src/core/finishScrollTo.ts
1163
- var finishScrollTo = (state) => {
1344
+ function finishScrollTo(ctx, state) {
1345
+ var _a3, _b;
1164
1346
  if (state) {
1165
- state.scrollingTo = void 0;
1166
1347
  state.scrollHistory.length = 0;
1348
+ state.initialScroll = void 0;
1349
+ state.initialAnchor = void 0;
1350
+ set$(ctx, "scrollingTo", void 0);
1351
+ if (state.pendingTotalSize !== void 0) {
1352
+ addTotalSize(ctx, state, null, state.pendingTotalSize);
1353
+ }
1354
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1355
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1356
+ }
1167
1357
  }
1168
- };
1358
+ }
1169
1359
 
1170
1360
  // src/core/scrollTo.ts
1171
- function scrollTo(state, params = {}) {
1172
- var _a;
1173
- const { animated, noScrollingTo } = params;
1361
+ function scrollTo(ctx, state, params) {
1362
+ var _a3;
1363
+ const { noScrollingTo, ...scrollTarget } = params;
1364
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1174
1365
  const {
1175
1366
  refScroller,
1176
1367
  props: { horizontal }
1177
1368
  } = state;
1178
- const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
1369
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1370
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1371
+ const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1372
+ offset = Math.min(offset, maxOffset);
1373
+ }
1179
1374
  state.scrollHistory.length = 0;
1180
1375
  if (!noScrollingTo) {
1181
- state.scrollingTo = params;
1376
+ set$(ctx, "scrollingTo", scrollTarget);
1182
1377
  }
1183
1378
  state.scrollPending = offset;
1184
- (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1185
- animated: !!animated,
1186
- x: horizontal ? offset : 0,
1187
- y: horizontal ? 0 : offset
1188
- });
1379
+ if (!isInitialScroll || Platform.OS === "android") {
1380
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1381
+ animated: !!animated,
1382
+ x: horizontal ? offset : 0,
1383
+ y: horizontal ? 0 : offset
1384
+ });
1385
+ }
1189
1386
  if (!animated) {
1190
1387
  state.scroll = offset;
1191
- setTimeout(() => finishScrollTo(state), 100);
1388
+ {
1389
+ const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1390
+ if (value && peek$(ctx, "scrollingTo")) {
1391
+ finishScrollTo(ctx, state);
1392
+ unlisten();
1393
+ }
1394
+ });
1395
+ }
1396
+ if (isInitialScroll) {
1397
+ setTimeout(() => {
1398
+ state.initialScroll = void 0;
1399
+ }, 500);
1400
+ }
1401
+ }
1402
+ }
1403
+
1404
+ // src/utils/checkThreshold.ts
1405
+ var HYSTERESIS_MULTIPLIER = 1.3;
1406
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1407
+ const absDistance = Math.abs(distance);
1408
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1409
+ const updateSnapshot = () => {
1410
+ setSnapshot == null ? void 0 : setSnapshot({
1411
+ atThreshold,
1412
+ contentSize: context.contentSize,
1413
+ dataLength: context.dataLength,
1414
+ scrollPosition: context.scrollPosition
1415
+ });
1416
+ };
1417
+ if (!wasReached) {
1418
+ if (!within) {
1419
+ return false;
1420
+ }
1421
+ onReached == null ? void 0 : onReached(distance);
1422
+ updateSnapshot();
1423
+ return true;
1424
+ }
1425
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1426
+ if (reset) {
1427
+ setSnapshot == null ? void 0 : setSnapshot(void 0);
1428
+ return false;
1429
+ }
1430
+ if (within) {
1431
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1432
+ if (changed) {
1433
+ onReached == null ? void 0 : onReached(distance);
1434
+ updateSnapshot();
1435
+ }
1436
+ }
1437
+ return true;
1438
+ };
1439
+
1440
+ // src/utils/checkAtBottom.ts
1441
+ function checkAtBottom(ctx, state) {
1442
+ var _a3;
1443
+ if (!state) {
1444
+ return;
1445
+ }
1446
+ const {
1447
+ queuedInitialLayout,
1448
+ scrollLength,
1449
+ scroll,
1450
+ maintainingScrollAtEnd,
1451
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1452
+ } = state;
1453
+ const contentSize = getContentSize(ctx);
1454
+ if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1455
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1456
+ const isContentLess = contentSize < scrollLength;
1457
+ state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1458
+ state.isEndReached = checkThreshold(
1459
+ distanceFromEnd,
1460
+ isContentLess,
1461
+ onEndReachedThreshold * scrollLength,
1462
+ state.isEndReached,
1463
+ state.endReachedSnapshot,
1464
+ {
1465
+ contentSize,
1466
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1467
+ scrollPosition: scroll
1468
+ },
1469
+ (distance) => {
1470
+ var _a4, _b;
1471
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1472
+ },
1473
+ (snapshot) => {
1474
+ state.endReachedSnapshot = snapshot;
1475
+ }
1476
+ );
1477
+ }
1478
+ }
1479
+
1480
+ // src/utils/checkAtTop.ts
1481
+ function checkAtTop(state) {
1482
+ var _a3;
1483
+ if (!state) {
1484
+ return;
1485
+ }
1486
+ const {
1487
+ scrollLength,
1488
+ scroll,
1489
+ props: { onStartReachedThreshold }
1490
+ } = state;
1491
+ const distanceFromTop = scroll;
1492
+ state.isAtStart = distanceFromTop <= 0;
1493
+ state.isStartReached = checkThreshold(
1494
+ distanceFromTop,
1495
+ false,
1496
+ onStartReachedThreshold * scrollLength,
1497
+ state.isStartReached,
1498
+ state.startReachedSnapshot,
1499
+ {
1500
+ contentSize: state.totalSize,
1501
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1502
+ scrollPosition: scroll
1503
+ },
1504
+ (distance) => {
1505
+ var _a4, _b;
1506
+ return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1507
+ },
1508
+ (snapshot) => {
1509
+ state.startReachedSnapshot = snapshot;
1510
+ }
1511
+ );
1512
+ }
1513
+
1514
+ // src/core/updateScroll.ts
1515
+ function updateScroll(ctx, state, newScroll, forceUpdate) {
1516
+ var _a3;
1517
+ const scrollingTo = peek$(ctx, "scrollingTo");
1518
+ state.hasScrolled = true;
1519
+ state.lastBatchingAction = Date.now();
1520
+ const currentTime = Date.now();
1521
+ const adjust = state.scrollAdjustHandler.getAdjust();
1522
+ const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1523
+ const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1524
+ if (adjustChanged) {
1525
+ state.scrollHistory.length = 0;
1526
+ }
1527
+ state.lastScrollAdjustForHistory = adjust;
1528
+ if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1529
+ if (!adjustChanged) {
1530
+ state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1531
+ }
1532
+ }
1533
+ if (state.scrollHistory.length > 5) {
1534
+ state.scrollHistory.shift();
1535
+ }
1536
+ state.scrollPrev = state.scroll;
1537
+ state.scrollPrevTime = state.scrollTime;
1538
+ state.scroll = newScroll;
1539
+ state.scrollTime = currentTime;
1540
+ const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1541
+ if (ignoreScrollFromMVCP && !scrollingTo) {
1542
+ const { lt, gt } = ignoreScrollFromMVCP;
1543
+ if (lt && newScroll < lt || gt && newScroll > gt) {
1544
+ state.ignoreScrollFromMVCPIgnored = true;
1545
+ return;
1546
+ }
1547
+ }
1548
+ if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1549
+ state.ignoreScrollFromMVCPIgnored = false;
1550
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1551
+ checkAtBottom(ctx, state);
1552
+ checkAtTop(state);
1553
+ state.dataChangeNeedsScrollUpdate = false;
1192
1554
  }
1193
1555
  }
1194
1556
 
@@ -1198,6 +1560,9 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1198
1560
  const doit = () => {
1199
1561
  {
1200
1562
  state.scrollAdjustHandler.requestAdjust(positionDiff);
1563
+ if (state.adjustingFromInitialMount) {
1564
+ state.adjustingFromInitialMount--;
1565
+ }
1201
1566
  }
1202
1567
  };
1203
1568
  state.scroll += positionDiff;
@@ -1205,44 +1570,77 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1205
1570
  const didLayout = peek$(ctx, "containersDidLayout");
1206
1571
  if (didLayout) {
1207
1572
  doit();
1208
- const threshold = state.scroll - positionDiff / 2;
1209
- if (!state.ignoreScrollFromMVCP) {
1210
- state.ignoreScrollFromMVCP = {};
1211
- }
1212
- if (positionDiff > 0) {
1213
- state.ignoreScrollFromMVCP.lt = threshold;
1214
- } else {
1215
- state.ignoreScrollFromMVCP.gt = threshold;
1216
- }
1217
- if (state.ignoreScrollFromMVCPTimeout) {
1218
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
1219
- }
1220
- state.ignoreScrollFromMVCPTimeout = setTimeout(
1221
- () => {
1222
- state.ignoreScrollFromMVCP = void 0;
1223
- },
1224
- 100
1225
- );
1226
1573
  } else {
1574
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1227
1575
  requestAnimationFrame(doit);
1228
1576
  }
1229
1577
  }
1230
1578
  }
1231
1579
 
1580
+ // src/core/ensureInitialAnchor.ts
1581
+ var INITIAL_ANCHOR_TOLERANCE = 0.5;
1582
+ var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1583
+ var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1584
+ function ensureInitialAnchor(ctx, state) {
1585
+ var _a3, _b, _c, _d, _e;
1586
+ const anchor = state.initialAnchor;
1587
+ const item = state.props.data[anchor.index];
1588
+ const containersDidLayout = peek$(ctx, "containersDidLayout");
1589
+ if (!containersDidLayout) {
1590
+ return;
1591
+ }
1592
+ const id = getId(state, anchor.index);
1593
+ if (state.positions.get(id) === void 0) {
1594
+ return;
1595
+ }
1596
+ const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1597
+ if (size === void 0) {
1598
+ return;
1599
+ }
1600
+ const availableSpace = Math.max(0, state.scrollLength - size);
1601
+ const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1602
+ const contentSize = getContentSize(ctx);
1603
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1604
+ const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1605
+ const delta = clampedDesiredOffset - state.scroll;
1606
+ if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1607
+ const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1608
+ if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1609
+ state.initialAnchor = void 0;
1610
+ } else {
1611
+ anchor.settledTicks = settledTicks;
1612
+ }
1613
+ return;
1614
+ }
1615
+ if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1616
+ state.initialAnchor = void 0;
1617
+ return;
1618
+ }
1619
+ const lastDelta = anchor.lastDelta;
1620
+ if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1621
+ state.initialAnchor = void 0;
1622
+ return;
1623
+ }
1624
+ Object.assign(anchor, {
1625
+ attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1626
+ lastDelta: delta,
1627
+ settledTicks: 0
1628
+ });
1629
+ requestAdjust(ctx, state, delta);
1630
+ }
1631
+
1232
1632
  // src/core/mvcp.ts
1233
1633
  function prepareMVCP(ctx, state, dataChanged) {
1234
- const {
1235
- idsInView,
1236
- positions,
1237
- scrollingTo,
1238
- props: { maintainVisibleContentPosition }
1239
- } = state;
1634
+ const { idsInView, positions, props } = state;
1635
+ const { maintainVisibleContentPosition } = props;
1636
+ const scrollingTo = peek$(ctx, "scrollingTo");
1240
1637
  let prevPosition;
1241
1638
  let targetId;
1242
1639
  const idsInViewWithPositions = [];
1243
1640
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1244
- if (maintainVisibleContentPosition) {
1245
- const indexByKey = state.indexByKey;
1641
+ const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1642
+ const indexByKey = state.indexByKey;
1643
+ if (shouldMVCP) {
1246
1644
  if (scrollTarget !== void 0) {
1247
1645
  targetId = getId(state, scrollTarget);
1248
1646
  } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
@@ -1255,70 +1653,106 @@ function prepareMVCP(ctx, state, dataChanged) {
1255
1653
  }
1256
1654
  }
1257
1655
  } else {
1258
- targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
1656
+ targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
1259
1657
  }
1260
1658
  }
1261
1659
  if (targetId !== void 0) {
1262
1660
  prevPosition = positions.get(targetId);
1263
1661
  }
1264
- }
1265
- return () => {
1266
- let positionDiff;
1267
- if (dataChanged && targetId === void 0) {
1268
- for (let i = 0; i < idsInViewWithPositions.length; i++) {
1269
- const { id, position } = idsInViewWithPositions[i];
1270
- const newPosition = positions.get(id);
1662
+ return () => {
1663
+ let positionDiff;
1664
+ if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1665
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
1666
+ const { id, position } = idsInViewWithPositions[i];
1667
+ const newPosition = positions.get(id);
1668
+ if (newPosition !== void 0) {
1669
+ positionDiff = newPosition - position;
1670
+ break;
1671
+ }
1672
+ }
1673
+ }
1674
+ if (targetId !== void 0 && prevPosition !== void 0) {
1675
+ const newPosition = positions.get(targetId);
1271
1676
  if (newPosition !== void 0) {
1272
- positionDiff = newPosition - position;
1273
- break;
1677
+ const totalSize = getContentSize(ctx);
1678
+ let diff = newPosition - prevPosition;
1679
+ if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
1680
+ if (diff > 0) {
1681
+ diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1682
+ } else {
1683
+ diff = 0;
1684
+ }
1685
+ }
1686
+ positionDiff = diff;
1274
1687
  }
1275
1688
  }
1276
- }
1277
- if (targetId !== void 0 && prevPosition !== void 0) {
1278
- const newPosition = positions.get(targetId);
1279
- if (newPosition !== void 0) {
1280
- positionDiff = newPosition - prevPosition;
1689
+ if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1690
+ requestAdjust(ctx, state, positionDiff);
1281
1691
  }
1282
- }
1283
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1284
- requestAdjust(ctx, state, positionDiff);
1285
- }
1286
- };
1692
+ };
1693
+ }
1287
1694
  }
1288
1695
 
1289
- // src/utils/setPaddingTop.ts
1290
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1291
- if (stylePaddingTop !== void 0) {
1292
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1293
- if (stylePaddingTop < prevStylePaddingTop) {
1294
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
1295
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1296
- state.timeoutSetPaddingTop = setTimeout(() => {
1297
- prevTotalSize = peek$(ctx, "totalSize") || 0;
1298
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1299
- }, 16);
1300
- }
1301
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1696
+ // src/core/prepareColumnStartState.ts
1697
+ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1698
+ var _a3;
1699
+ const numColumns = peek$(ctx, "numColumns");
1700
+ let rowStartIndex = startIndex;
1701
+ const columnAtStart = state.columns.get(state.idCache[startIndex]);
1702
+ if (columnAtStart !== 1) {
1703
+ rowStartIndex = findRowStartIndex(state, numColumns, startIndex);
1302
1704
  }
1303
- if (alignItemsPaddingTop !== void 0) {
1304
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1705
+ let currentRowTop = 0;
1706
+ const curId = state.idCache[rowStartIndex];
1707
+ const column = state.columns.get(curId);
1708
+ if (rowStartIndex > 0) {
1709
+ const prevIndex = rowStartIndex - 1;
1710
+ const prevId = state.idCache[prevIndex];
1711
+ const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1712
+ const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1713
+ const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1714
+ currentRowTop = prevPosition + prevRowHeight;
1305
1715
  }
1716
+ return {
1717
+ column,
1718
+ currentRowTop,
1719
+ startIndex: rowStartIndex
1720
+ };
1306
1721
  }
1307
-
1308
- // src/utils/updateAlignItemsPaddingTop.ts
1309
- function updateAlignItemsPaddingTop(ctx, state) {
1310
- const {
1311
- scrollLength,
1312
- props: { alignItemsAtEnd, data }
1313
- } = state;
1314
- if (alignItemsAtEnd) {
1315
- let alignItemsPaddingTop = 0;
1316
- if ((data == null ? void 0 : data.length) > 0) {
1317
- const contentSize = getContentSize(ctx);
1318
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1722
+ function findRowStartIndex(state, numColumns, index) {
1723
+ if (numColumns <= 1) {
1724
+ return Math.max(0, index);
1725
+ }
1726
+ let rowStart = Math.max(0, index);
1727
+ while (rowStart > 0) {
1728
+ const columnForIndex = state.columns.get(state.idCache[rowStart]);
1729
+ if (columnForIndex === 1) {
1730
+ break;
1319
1731
  }
1320
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1732
+ rowStart--;
1321
1733
  }
1734
+ return rowStart;
1735
+ }
1736
+ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1737
+ if (endIndex < startIndex) {
1738
+ return 0;
1739
+ }
1740
+ const { data } = state.props;
1741
+ if (!data) {
1742
+ return 0;
1743
+ }
1744
+ let maxSize = 0;
1745
+ for (let i = startIndex; i <= endIndex; i++) {
1746
+ if (i < 0 || i >= data.length) {
1747
+ continue;
1748
+ }
1749
+ const id = state.idCache[i];
1750
+ const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1751
+ if (size > maxSize) {
1752
+ maxSize = size;
1753
+ }
1754
+ }
1755
+ return maxSize;
1322
1756
  }
1323
1757
 
1324
1758
  // src/core/updateTotalSize.ts
@@ -1334,29 +1768,53 @@ function updateTotalSize(ctx, state) {
1334
1768
  if (lastId !== void 0) {
1335
1769
  const lastPosition = positions.get(lastId);
1336
1770
  if (lastPosition !== void 0) {
1337
- const lastSize = getItemSize(state, lastId, data.length - 1, data[data.length - 1]);
1771
+ const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1338
1772
  if (lastSize !== void 0) {
1339
1773
  const totalSize = lastPosition + lastSize;
1340
1774
  addTotalSize(ctx, state, null, totalSize);
1341
1775
  }
1342
1776
  }
1343
1777
  }
1344
- }
1345
- }
1346
- function addTotalSize(ctx, state, key, add) {
1347
- const { alignItemsAtEnd } = state.props;
1348
- {
1349
- state.totalSize = add;
1350
- if (state.timeoutSetPaddingTop) {
1351
- clearTimeout(state.timeoutSetPaddingTop);
1352
- state.timeoutSetPaddingTop = void 0;
1778
+ }
1779
+ }
1780
+
1781
+ // src/utils/getScrollVelocity.ts
1782
+ var getScrollVelocity = (state) => {
1783
+ const { scrollHistory } = state;
1784
+ let velocity = 0;
1785
+ if (scrollHistory.length >= 1) {
1786
+ const newest = scrollHistory[scrollHistory.length - 1];
1787
+ let oldest;
1788
+ let start = 0;
1789
+ const now = Date.now();
1790
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1791
+ const entry = scrollHistory[i];
1792
+ const nextEntry = scrollHistory[i + 1];
1793
+ if (i > 0) {
1794
+ const prevEntry = scrollHistory[i - 1];
1795
+ const prevDirection = entry.scroll - prevEntry.scroll;
1796
+ const currentDirection = nextEntry.scroll - entry.scroll;
1797
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1798
+ start = i;
1799
+ break;
1800
+ }
1801
+ }
1802
+ }
1803
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1804
+ const entry = scrollHistory[i];
1805
+ if (now - entry.time <= 1e3) {
1806
+ oldest = entry;
1807
+ break;
1808
+ }
1809
+ }
1810
+ if (oldest && oldest !== newest) {
1811
+ const scrollDiff = newest.scroll - oldest.scroll;
1812
+ const timeDiff = newest.time - oldest.time;
1813
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1353
1814
  }
1354
1815
  }
1355
- set$(ctx, "totalSize", state.totalSize);
1356
- if (alignItemsAtEnd) {
1357
- updateAlignItemsPaddingTop(ctx, state);
1358
- }
1359
- }
1816
+ return velocity;
1817
+ };
1360
1818
 
1361
1819
  // src/utils/updateSnapToOffsets.ts
1362
1820
  function updateSnapToOffsets(ctx, state) {
@@ -1373,9 +1831,14 @@ function updateSnapToOffsets(ctx, state) {
1373
1831
  set$(ctx, "snapToOffsets", snapToOffsets);
1374
1832
  }
1375
1833
 
1376
- // src/core/updateAllPositions.ts
1377
- function updateAllPositions(ctx, state, dataChanged, startIndex = 0) {
1378
- var _a, _b, _c, _d, _e, _f;
1834
+ // src/core/updateItemPositions.ts
1835
+ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1836
+ doMVCP: false,
1837
+ forceFullUpdate: false,
1838
+ scrollBottomBuffered: -1,
1839
+ startIndex: 0
1840
+ }) {
1841
+ var _a3, _b, _c, _d, _e;
1379
1842
  const {
1380
1843
  columns,
1381
1844
  indexByKey,
@@ -1385,32 +1848,51 @@ function updateAllPositions(ctx, state, dataChanged, startIndex = 0) {
1385
1848
  props: { getEstimatedItemSize, snapToIndices, enableAverages }
1386
1849
  } = state;
1387
1850
  const data = state.props.data;
1851
+ const dataLength = data.length;
1388
1852
  const numColumns = peek$(ctx, "numColumns");
1389
- const indexByKeyForChecking = __DEV__ ? /* @__PURE__ */ new Map() : void 0;
1853
+ const scrollingTo = peek$(ctx, "scrollingTo");
1854
+ const hasColumns = numColumns > 1;
1855
+ const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1856
+ const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1857
+ const maxVisibleArea = scrollBottomBuffered + 1e3;
1390
1858
  const useAverageSize = enableAverages && !getEstimatedItemSize;
1859
+ const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
1391
1860
  let currentRowTop = 0;
1392
1861
  let column = 1;
1393
1862
  let maxSizeInRow = 0;
1394
- const hasColumns = numColumns > 1;
1395
1863
  if (startIndex > 0) {
1396
- const prevIndex = startIndex - 1;
1397
- const prevId = (_a = idCache.get(prevIndex)) != null ? _a : getId(state, prevIndex);
1398
- const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1399
1864
  if (hasColumns) {
1400
- const prevColumn = (_c = columns.get(prevId)) != null ? _c : 1;
1401
- currentRowTop = prevPosition;
1402
- column = prevColumn % numColumns + 1;
1403
- } else {
1404
- const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1865
+ const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1866
+ ctx,
1867
+ state,
1868
+ startIndex,
1869
+ useAverageSize
1870
+ );
1871
+ startIndex = processedStartIndex;
1872
+ currentRowTop = initialRowTop;
1873
+ } else if (startIndex < dataLength) {
1874
+ const prevIndex = startIndex - 1;
1875
+ const prevId = getId(state, prevIndex);
1876
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1877
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1405
1878
  currentRowTop = prevPosition + prevSize;
1406
1879
  }
1407
1880
  }
1408
1881
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
1409
- const dataLength = data.length;
1882
+ let didBreakEarly = false;
1883
+ let breakAt;
1410
1884
  for (let i = startIndex; i < dataLength; i++) {
1411
- const id = (_e = idCache.get(i)) != null ? _e : getId(state, i);
1412
- const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(state, id, i, data[i], useAverageSize);
1413
- if (__DEV__ && needsIndexByKey) {
1885
+ if (shouldOptimize && breakAt !== void 0 && i > breakAt) {
1886
+ didBreakEarly = true;
1887
+ break;
1888
+ }
1889
+ if (shouldOptimize && breakAt === void 0 && !scrollingTo && !dataChanged && currentRowTop > maxVisibleArea) {
1890
+ const itemsPerRow = hasColumns ? numColumns : 1;
1891
+ breakAt = i + itemsPerRow + 10;
1892
+ }
1893
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1894
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
1895
+ if (IS_DEV && needsIndexByKey) {
1414
1896
  if (indexByKeyForChecking.has(id)) {
1415
1897
  console.error(
1416
1898
  `[legend-list] Error: Detected overlapping key (${id}) which causes missing items and gaps and other terrrible things. Check that keyExtractor returns unique values.`
@@ -1437,7 +1919,9 @@ function updateAllPositions(ctx, state, dataChanged, startIndex = 0) {
1437
1919
  currentRowTop += size;
1438
1920
  }
1439
1921
  }
1440
- updateTotalSize(ctx, state);
1922
+ if (!didBreakEarly) {
1923
+ updateTotalSize(ctx, state);
1924
+ }
1441
1925
  if (snapToIndices) {
1442
1926
  updateSnapToOffsets(ctx, state);
1443
1927
  }
@@ -1457,6 +1941,21 @@ function ensureViewabilityState(ctx, configId) {
1457
1941
  }
1458
1942
  return state;
1459
1943
  }
1944
+ function setupViewability(props) {
1945
+ let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
1946
+ if (viewabilityConfig || onViewableItemsChanged) {
1947
+ viewabilityConfigCallbackPairs = [
1948
+ ...viewabilityConfigCallbackPairs || [],
1949
+ {
1950
+ onViewableItemsChanged,
1951
+ viewabilityConfig: viewabilityConfig || {
1952
+ viewAreaCoveragePercentThreshold: 0
1953
+ }
1954
+ }
1955
+ ];
1956
+ }
1957
+ return viewabilityConfigCallbackPairs;
1958
+ }
1460
1959
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
1461
1960
  const {
1462
1961
  timeouts,
@@ -1610,16 +2109,6 @@ function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize,
1610
2109
  const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
1611
2110
  return value.isViewable;
1612
2111
  }
1613
- function findContainerId(ctx, key) {
1614
- const numContainers = peek$(ctx, "numContainers");
1615
- for (let i = 0; i < numContainers; i++) {
1616
- const itemKey = peek$(ctx, `containerItemKey${i}`);
1617
- if (itemKey === key) {
1618
- return i;
1619
- }
1620
- }
1621
- return -1;
1622
- }
1623
2112
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1624
2113
  const key = containerId + configId;
1625
2114
  ctx.mapViewabilityValues.set(key, viewToken);
@@ -1628,9 +2117,12 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1628
2117
  }
1629
2118
 
1630
2119
  // src/utils/checkAllSizesKnown.ts
2120
+ function isNullOrUndefined2(value) {
2121
+ return value === null || value === void 0;
2122
+ }
1631
2123
  function checkAllSizesKnown(state) {
1632
2124
  const { startBuffered, endBuffered, sizesKnown } = state;
1633
- if (endBuffered !== null && startBuffered >= 0 && endBuffered >= 0) {
2125
+ if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
1634
2126
  let areAllKnown = true;
1635
2127
  for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
1636
2128
  const key = getId(state, i);
@@ -1647,6 +2139,8 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1647
2139
  const { stickyContainerPool, containerItemTypes } = state;
1648
2140
  const result = [];
1649
2141
  const availableContainers = [];
2142
+ const pendingRemovalSet = new Set(pendingRemoval);
2143
+ let pendingRemovalChanged = false;
1650
2144
  const stickyIndicesSet = state.props.stickyIndicesSet;
1651
2145
  const stickyItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyIndicesSet.has(index))) || [];
1652
2146
  const canReuseContainer = (containerIndex, requiredType) => {
@@ -1662,12 +2156,11 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1662
2156
  let foundContainer = false;
1663
2157
  for (const containerIndex of stickyContainerPool) {
1664
2158
  const key = peek$(ctx, `containerItemKey${containerIndex}`);
1665
- const isPendingRemoval = pendingRemoval.includes(containerIndex);
1666
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
2159
+ const isPendingRemoval = pendingRemovalSet.has(containerIndex);
2160
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType) && !result.includes(containerIndex)) {
1667
2161
  result.push(containerIndex);
1668
- if (isPendingRemoval) {
1669
- const index = pendingRemoval.indexOf(containerIndex);
1670
- pendingRemoval.splice(index, 1);
2162
+ if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
2163
+ pendingRemovalChanged = true;
1671
2164
  }
1672
2165
  foundContainer = true;
1673
2166
  if (requiredItemTypes) typeIndex++;
@@ -1687,13 +2180,11 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1687
2180
  }
1688
2181
  const key = peek$(ctx, `containerItemKey${u}`);
1689
2182
  let isOk = key === void 0;
1690
- if (!isOk) {
1691
- const index = pendingRemoval.indexOf(u);
1692
- if (index !== -1) {
1693
- pendingRemoval.splice(index, 1);
1694
- const requiredType = neededTypes[typeIndex];
1695
- isOk = canReuseContainer(u, requiredType);
1696
- }
2183
+ if (!isOk && pendingRemovalSet.has(u)) {
2184
+ pendingRemovalSet.delete(u);
2185
+ pendingRemovalChanged = true;
2186
+ const requiredType = neededTypes[typeIndex];
2187
+ isOk = canReuseContainer(u, requiredType);
1697
2188
  }
1698
2189
  if (isOk) {
1699
2190
  result.push(u);
@@ -1736,7 +2227,7 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1736
2227
  for (let i = 0; i < stillNeeded; i++) {
1737
2228
  result.push(numContainers + i);
1738
2229
  }
1739
- if (__DEV__ && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
2230
+ if (IS_DEV && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
1740
2231
  console.warn(
1741
2232
  "[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.",
1742
2233
  {
@@ -1751,50 +2242,18 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1751
2242
  }
1752
2243
  }
1753
2244
  }
2245
+ if (pendingRemovalChanged) {
2246
+ pendingRemoval.length = 0;
2247
+ for (const value of pendingRemovalSet) {
2248
+ pendingRemoval.push(value);
2249
+ }
2250
+ }
1754
2251
  return result.sort(comparatorDefault);
1755
2252
  }
1756
2253
  function comparatorByDistance(a, b) {
1757
2254
  return b.distance - a.distance;
1758
2255
  }
1759
2256
 
1760
- // src/utils/getScrollVelocity.ts
1761
- var getScrollVelocity = (state) => {
1762
- const { scrollHistory } = state;
1763
- let velocity = 0;
1764
- if (scrollHistory.length >= 1) {
1765
- const newest = scrollHistory[scrollHistory.length - 1];
1766
- let oldest;
1767
- let start = 0;
1768
- const now = Date.now();
1769
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1770
- const entry = scrollHistory[i];
1771
- const nextEntry = scrollHistory[i + 1];
1772
- if (i > 0) {
1773
- const prevEntry = scrollHistory[i - 1];
1774
- const prevDirection = entry.scroll - prevEntry.scroll;
1775
- const currentDirection = nextEntry.scroll - entry.scroll;
1776
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1777
- start = i;
1778
- break;
1779
- }
1780
- }
1781
- }
1782
- for (let i = start; i < scrollHistory.length - 1; i++) {
1783
- const entry = scrollHistory[i];
1784
- if (now - entry.time <= 1e3) {
1785
- oldest = entry;
1786
- break;
1787
- }
1788
- }
1789
- if (oldest && oldest !== newest) {
1790
- const scrollDiff = newest.scroll - oldest.scroll;
1791
- const timeDiff = newest.time - oldest.time;
1792
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1793
- }
1794
- }
1795
- return velocity;
1796
- };
1797
-
1798
2257
  // src/core/scrollToIndex.ts
1799
2258
  function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
1800
2259
  if (index >= state.props.data.length) {
@@ -1807,72 +2266,16 @@ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, vie
1807
2266
  if (isLast && viewPosition === void 0) {
1808
2267
  viewPosition = 1;
1809
2268
  }
1810
- const firstIndexScrollPostion = firstIndexOffset - viewOffset;
1811
2269
  state.scrollForNextCalculateItemsInView = void 0;
1812
- scrollTo(state, {
2270
+ scrollTo(ctx, state, {
1813
2271
  animated,
1814
2272
  index,
1815
- offset: firstIndexScrollPostion,
2273
+ offset: firstIndexOffset,
1816
2274
  viewOffset,
1817
2275
  viewPosition: viewPosition != null ? viewPosition : 0
1818
2276
  });
1819
2277
  }
1820
2278
 
1821
- // src/utils/checkThreshold.ts
1822
- var checkThreshold = (distance, atThreshold, threshold, isReached, isBlockedByTimer, onReached, blockTimer) => {
1823
- const distanceAbs = Math.abs(distance);
1824
- const isAtThreshold = atThreshold || distanceAbs < threshold;
1825
- if (!isReached && !isBlockedByTimer) {
1826
- if (isAtThreshold) {
1827
- onReached == null ? void 0 : onReached(distance);
1828
- blockTimer == null ? void 0 : blockTimer(true);
1829
- setTimeout(() => {
1830
- blockTimer == null ? void 0 : blockTimer(false);
1831
- }, 700);
1832
- return true;
1833
- }
1834
- } else {
1835
- if (distance >= 1.3 * threshold) {
1836
- return false;
1837
- }
1838
- }
1839
- return isReached;
1840
- };
1841
-
1842
- // src/utils/checkAtBottom.ts
1843
- function checkAtBottom(ctx, state) {
1844
- if (!state) {
1845
- return;
1846
- }
1847
- const {
1848
- queuedInitialLayout,
1849
- scrollLength,
1850
- scroll,
1851
- maintainingScrollAtEnd,
1852
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1853
- } = state;
1854
- const contentSize = getContentSize(ctx);
1855
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1856
- const distanceFromEnd = contentSize - scroll - scrollLength;
1857
- const isContentLess = contentSize < scrollLength;
1858
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1859
- state.isEndReached = checkThreshold(
1860
- distanceFromEnd,
1861
- isContentLess,
1862
- onEndReachedThreshold * scrollLength,
1863
- state.isEndReached,
1864
- state.endReachedBlockedByTimer,
1865
- (distance) => {
1866
- var _a, _b;
1867
- return (_b = (_a = state.props).onEndReached) == null ? void 0 : _b.call(_a, { distanceFromEnd: distance });
1868
- },
1869
- (block) => {
1870
- state.endReachedBlockedByTimer = block;
1871
- }
1872
- );
1873
- }
1874
- }
1875
-
1876
2279
  // src/utils/setDidLayout.ts
1877
2280
  function setDidLayout(ctx, state) {
1878
2281
  const {
@@ -1895,11 +2298,12 @@ function setDidLayout(ctx, state) {
1895
2298
 
1896
2299
  // src/core/calculateItemsInView.ts
1897
2300
  function findCurrentStickyIndex(stickyArray, scroll, state) {
1898
- var _a;
2301
+ var _a3;
1899
2302
  const idCache = state.idCache;
1900
2303
  const positions = state.positions;
1901
2304
  for (let i = stickyArray.length - 1; i >= 0; i--) {
1902
- const stickyId = (_a = idCache.get(stickyArray[i])) != null ? _a : getId(state, stickyArray[i]);
2305
+ const stickyIndex = stickyArray[i];
2306
+ const stickyId = (_a3 = idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
1903
2307
  const stickyPos = stickyId ? positions.get(stickyId) : void 0;
1904
2308
  if (stickyPos !== void 0 && scroll >= stickyPos) {
1905
2309
  return i;
@@ -1907,47 +2311,51 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
1907
2311
  }
1908
2312
  return -1;
1909
2313
  }
1910
- function getActiveStickyIndices(ctx, state, stickyIndices) {
2314
+ function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
1911
2315
  return new Set(
1912
- 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))
2316
+ Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyHeaderIndices.has(idx))
1913
2317
  );
1914
2318
  }
1915
- function handleStickyActivation(ctx, state, stickyIndices, stickyArray, scroll, needNewContainers, startBuffered, endBuffered) {
1916
- var _a;
1917
- const activeIndices = getActiveStickyIndices(ctx, state, stickyIndices);
1918
- const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
2319
+ function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2320
+ var _a3;
2321
+ const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2322
+ state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
1919
2323
  for (let offset = 0; offset <= 1; offset++) {
1920
2324
  const idx = currentStickyIdx - offset;
1921
2325
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
1922
2326
  const stickyIndex = stickyArray[idx];
1923
- const stickyId = (_a = state.idCache.get(stickyIndex)) != null ? _a : getId(state, stickyIndex);
2327
+ const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
1924
2328
  if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
1925
2329
  needNewContainers.push(stickyIndex);
1926
2330
  }
1927
2331
  }
1928
2332
  }
1929
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, pendingRemoval) {
1930
- var _a, _b, _c;
1931
- const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
2333
+ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2334
+ var _a3, _b, _c;
1932
2335
  for (const containerIndex of state.stickyContainerPool) {
1933
2336
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
1934
2337
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
1935
2338
  if (itemIndex === void 0) continue;
1936
2339
  const arrayIdx = stickyArray.indexOf(itemIndex);
1937
- if (arrayIdx === -1) continue;
2340
+ if (arrayIdx === -1) {
2341
+ state.stickyContainerPool.delete(containerIndex);
2342
+ set$(ctx, `containerSticky${containerIndex}`, false);
2343
+ set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2344
+ continue;
2345
+ }
1938
2346
  const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
1939
2347
  if (isRecentSticky) continue;
1940
2348
  const nextIndex = stickyArray[arrayIdx + 1];
1941
2349
  let shouldRecycle = false;
1942
2350
  if (nextIndex) {
1943
- const nextId = (_a = state.idCache.get(nextIndex)) != null ? _a : getId(state, nextIndex);
2351
+ const nextId = (_a3 = state.idCache[nextIndex]) != null ? _a3 : getId(state, nextIndex);
1944
2352
  const nextPos = nextId ? state.positions.get(nextId) : void 0;
1945
2353
  shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
1946
2354
  } else {
1947
- const currentId = (_b = state.idCache.get(itemIndex)) != null ? _b : getId(state, itemIndex);
2355
+ const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
1948
2356
  if (currentId) {
1949
2357
  const currentPos = state.positions.get(currentId);
1950
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
2358
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
1951
2359
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
1952
2360
  }
1953
2361
  }
@@ -1958,61 +2366,52 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, pe
1958
2366
  }
1959
2367
  function calculateItemsInView(ctx, state, params = {}) {
1960
2368
  unstable_batchedUpdates(() => {
1961
- var _a, _b, _c, _d, _e, _f, _g, _h;
2369
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1962
2370
  const {
1963
2371
  columns,
1964
2372
  containerItemKeys,
1965
2373
  enableScrollForNextCalculateItemsInView,
1966
2374
  idCache,
1967
2375
  indexByKey,
2376
+ initialScroll,
1968
2377
  minIndexSizeChanged,
1969
2378
  positions,
2379
+ props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
1970
2380
  scrollForNextCalculateItemsInView,
1971
2381
  scrollLength,
1972
2382
  sizes,
1973
2383
  startBufferedId: startBufferedIdOrig,
1974
- viewabilityConfigCallbackPairs,
1975
- props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, scrollBuffer }
2384
+ viewabilityConfigCallbackPairs
1976
2385
  } = state;
1977
2386
  const { data } = state.props;
1978
2387
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
1979
2388
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
1980
2389
  const prevNumContainers = peek$(ctx, "numContainers");
1981
2390
  if (!data || scrollLength === 0 || !prevNumContainers) {
2391
+ if (state.initialAnchor) {
2392
+ ensureInitialAnchor(ctx, state);
2393
+ }
1982
2394
  return;
1983
2395
  }
1984
- const totalSize = peek$(ctx, "totalSize");
2396
+ const totalSize = getContentSize(ctx);
1985
2397
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
1986
2398
  const numColumns = peek$(ctx, "numColumns");
1987
- const previousScrollAdjust = 0;
1988
- const { dataChanged, doMVCP } = params;
2399
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
1989
2400
  const speed = getScrollVelocity(state);
1990
- if (doMVCP || dataChanged) {
1991
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
1992
- if (dataChanged) {
1993
- indexByKey.clear();
1994
- idCache.clear();
1995
- positions.clear();
1996
- }
1997
- const startIndex = dataChanged ? 0 : minIndexSizeChanged != null ? minIndexSizeChanged : 0;
1998
- updateAllPositions(ctx, state, dataChanged, startIndex);
1999
- if (minIndexSizeChanged !== void 0) {
2000
- state.minIndexSizeChanged = void 0;
2001
- }
2002
- checkMVCP == null ? void 0 : checkMVCP();
2003
- }
2004
2401
  const scrollExtra = 0;
2005
2402
  const { queuedInitialLayout } = state;
2006
2403
  let { scroll: scrollState } = state;
2007
2404
  if (!queuedInitialLayout && initialScroll) {
2008
2405
  const updatedOffset = calculateOffsetWithOffsetPosition(
2406
+ ctx,
2009
2407
  state,
2010
2408
  calculateOffsetForIndex(ctx, state, initialScroll.index),
2011
2409
  initialScroll
2012
2410
  );
2013
2411
  scrollState = updatedOffset;
2014
2412
  }
2015
- const scrollAdjustPad = -previousScrollAdjust - topPad;
2413
+ const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2414
+ const scrollAdjustPad = scrollAdjustPending - topPad;
2016
2415
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
2017
2416
  if (scroll + scrollLength > totalSize) {
2018
2417
  scroll = Math.max(0, totalSize - scrollLength);
@@ -2021,6 +2420,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2021
2420
  set$(ctx, "debugRawScroll", scrollState);
2022
2421
  set$(ctx, "debugComputedScroll", scroll);
2023
2422
  }
2423
+ const previousStickyIndex = state.activeStickyIndex;
2424
+ const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2425
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2426
+ state.activeStickyIndex = nextActiveStickyIndex;
2427
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2024
2428
  let scrollBufferTop = scrollBuffer;
2025
2429
  let scrollBufferBottom = scrollBuffer;
2026
2430
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2033,22 +2437,42 @@ function calculateItemsInView(ctx, state, params = {}) {
2033
2437
  const scrollTopBuffered = scroll - scrollBufferTop;
2034
2438
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2035
2439
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2036
- if (scrollForNextCalculateItemsInView) {
2440
+ if (!dataChanged && scrollForNextCalculateItemsInView) {
2037
2441
  const { top, bottom } = scrollForNextCalculateItemsInView;
2038
2442
  if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2443
+ if (state.initialAnchor) {
2444
+ ensureInitialAnchor(ctx, state);
2445
+ }
2039
2446
  return;
2040
2447
  }
2041
2448
  }
2449
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2450
+ if (dataChanged) {
2451
+ indexByKey.clear();
2452
+ idCache.length = 0;
2453
+ positions.clear();
2454
+ }
2455
+ const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2456
+ updateItemPositions(ctx, state, dataChanged, {
2457
+ doMVCP,
2458
+ forceFullUpdate: !!forceFullItemPositions,
2459
+ scrollBottomBuffered,
2460
+ startIndex
2461
+ });
2462
+ if (minIndexSizeChanged !== void 0) {
2463
+ state.minIndexSizeChanged = void 0;
2464
+ }
2465
+ checkMVCP == null ? void 0 : checkMVCP();
2042
2466
  let startNoBuffer = null;
2043
2467
  let startBuffered = null;
2044
2468
  let startBufferedId = null;
2045
2469
  let endNoBuffer = null;
2046
2470
  let endBuffered = null;
2047
- let loopStart = startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2471
+ let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2048
2472
  for (let i = loopStart; i >= 0; i--) {
2049
- const id = (_a = idCache.get(i)) != null ? _a : getId(state, i);
2473
+ const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2050
2474
  const top = positions.get(id);
2051
- const size = (_b = sizes.get(id)) != null ? _b : getItemSize(state, id, i, data[i]);
2475
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2052
2476
  const bottom = top + size;
2053
2477
  if (bottom > scroll - scrollBuffer) {
2054
2478
  loopStart = i;
@@ -2074,8 +2498,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2074
2498
  let firstFullyOnScreenIndex;
2075
2499
  const dataLength = data.length;
2076
2500
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2077
- const id = (_c = idCache.get(i)) != null ? _c : getId(state, i);
2078
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(state, id, i, data[i]);
2501
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2502
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2079
2503
  const top = positions.get(id);
2080
2504
  if (!foundEnd) {
2081
2505
  if (startNoBuffer === null && top + size > scroll) {
@@ -2104,7 +2528,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2104
2528
  }
2105
2529
  const idsInView = [];
2106
2530
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2107
- const id = (_e = idCache.get(i)) != null ? _e : getId(state, i);
2531
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2108
2532
  idsInView.push(id);
2109
2533
  }
2110
2534
  Object.assign(state, {
@@ -2136,7 +2560,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2136
2560
  let numContainers2 = prevNumContainers;
2137
2561
  const needNewContainers = [];
2138
2562
  for (let i = startBuffered; i <= endBuffered; i++) {
2139
- const id = (_f = idCache.get(i)) != null ? _f : getId(state, i);
2563
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2140
2564
  if (!containerItemKeys.has(id)) {
2141
2565
  needNewContainers.push(i);
2142
2566
  }
@@ -2147,11 +2571,14 @@ function calculateItemsInView(ctx, state, params = {}) {
2147
2571
  state,
2148
2572
  stickyIndicesSet,
2149
2573
  stickyIndicesArr,
2150
- scroll,
2574
+ currentStickyIdx,
2151
2575
  needNewContainers,
2152
2576
  startBuffered,
2153
2577
  endBuffered
2154
2578
  );
2579
+ } else {
2580
+ state.activeStickyIndex = void 0;
2581
+ set$(ctx, "activeStickyIndex", void 0);
2155
2582
  }
2156
2583
  if (needNewContainers.length > 0) {
2157
2584
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
@@ -2171,7 +2598,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2171
2598
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2172
2599
  const i = needNewContainers[idx];
2173
2600
  const containerIndex = availableContainers[idx];
2174
- const id = (_g = idCache.get(i)) != null ? _g : getId(state, i);
2601
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2175
2602
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2176
2603
  if (oldKey && oldKey !== id) {
2177
2604
  containerItemKeys.delete(oldKey);
@@ -2185,9 +2612,10 @@ function calculateItemsInView(ctx, state, params = {}) {
2185
2612
  if (stickyIndicesSet.has(i)) {
2186
2613
  set$(ctx, `containerSticky${containerIndex}`, true);
2187
2614
  const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2188
- set$(ctx, `containerStickyOffset${containerIndex}`, createAnimatedValue(topPadding));
2615
+ set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2189
2616
  state.stickyContainerPool.add(containerIndex);
2190
2617
  } else {
2618
+ set$(ctx, `containerSticky${containerIndex}`, false);
2191
2619
  state.stickyContainerPool.delete(containerIndex);
2192
2620
  }
2193
2621
  if (containerIndex >= numContainers2) {
@@ -2203,13 +2631,13 @@ function calculateItemsInView(ctx, state, params = {}) {
2203
2631
  }
2204
2632
  }
2205
2633
  if (stickyIndicesArr.length > 0) {
2206
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, pendingRemoval);
2634
+ handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2207
2635
  }
2208
2636
  let didChangePositions = false;
2209
2637
  for (let i = 0; i < numContainers; i++) {
2210
2638
  const itemKey = peek$(ctx, `containerItemKey${i}`);
2211
2639
  if (pendingRemoval.includes(i)) {
2212
- if (itemKey) {
2640
+ if (itemKey !== void 0) {
2213
2641
  containerItemKeys.delete(itemKey);
2214
2642
  }
2215
2643
  state.containerItemTypes.delete(i);
@@ -2226,11 +2654,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2226
2654
  const itemIndex = indexByKey.get(itemKey);
2227
2655
  const item = data[itemIndex];
2228
2656
  if (item !== void 0) {
2229
- const id = (_h = idCache.get(itemIndex)) != null ? _h : getId(state, itemIndex);
2230
- const position = positions.get(id);
2231
- if (position === void 0) {
2657
+ const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2658
+ const positionValue = positions.get(id);
2659
+ if (positionValue === void 0) {
2232
2660
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2233
2661
  } else {
2662
+ const position = (positionValue || 0) - scrollAdjustPending;
2234
2663
  const column = columns.get(id) || 1;
2235
2664
  const prevPos = peek$(ctx, `containerPosition${i}`);
2236
2665
  const prevColumn = peek$(ctx, `containerColumn${i}`);
@@ -2243,29 +2672,161 @@ function calculateItemsInView(ctx, state, params = {}) {
2243
2672
  set$(ctx, `containerColumn${i}`, column);
2244
2673
  }
2245
2674
  if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
2246
- set$(ctx, `containerItemData${i}`, data[itemIndex]);
2675
+ set$(ctx, `containerItemData${i}`, item);
2247
2676
  }
2248
2677
  }
2249
2678
  }
2250
2679
  }
2251
2680
  }
2252
- if (didChangePositions) {
2253
- set$(ctx, "lastPositionUpdate", Date.now());
2254
- }
2255
- if (!queuedInitialLayout && endBuffered !== null) {
2256
- if (checkAllSizesKnown(state)) {
2257
- setDidLayout(ctx, state);
2258
- }
2259
- }
2260
- if (viewabilityConfigCallbackPairs) {
2261
- updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
2681
+ if (didChangePositions) {
2682
+ set$(ctx, "lastPositionUpdate", Date.now());
2683
+ }
2684
+ if (!queuedInitialLayout && endBuffered !== null) {
2685
+ if (checkAllSizesKnown(state)) {
2686
+ setDidLayout(ctx, state);
2687
+ }
2688
+ }
2689
+ if (viewabilityConfigCallbackPairs) {
2690
+ updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
2691
+ }
2692
+ if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
2693
+ const item = data[nextActiveStickyIndex];
2694
+ if (item !== void 0) {
2695
+ onStickyHeaderChange({ index: nextActiveStickyIndex, item });
2696
+ }
2697
+ }
2698
+ });
2699
+ if (state.initialAnchor) {
2700
+ ensureInitialAnchor(ctx, state);
2701
+ }
2702
+ }
2703
+
2704
+ // src/core/checkActualChange.ts
2705
+ function checkActualChange(state, dataProp, previousData) {
2706
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
2707
+ return true;
2708
+ }
2709
+ const {
2710
+ idCache,
2711
+ props: { keyExtractor }
2712
+ } = state;
2713
+ for (let i = 0; i < dataProp.length; i++) {
2714
+ if (dataProp[i] !== previousData[i]) {
2715
+ return true;
2716
+ }
2717
+ if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
2718
+ return true;
2719
+ }
2720
+ }
2721
+ return false;
2722
+ }
2723
+
2724
+ // src/core/doMaintainScrollAtEnd.ts
2725
+ function doMaintainScrollAtEnd(ctx, state, animated) {
2726
+ const {
2727
+ refScroller,
2728
+ props: { maintainScrollAtEnd }
2729
+ } = state;
2730
+ if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2731
+ const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2732
+ if (paddingTop > 0) {
2733
+ state.scroll = 0;
2734
+ }
2735
+ requestAnimationFrame(() => {
2736
+ var _a3;
2737
+ if (state == null ? void 0 : state.isAtEnd) {
2738
+ state.maintainingScrollAtEnd = true;
2739
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2740
+ animated
2741
+ });
2742
+ setTimeout(
2743
+ () => {
2744
+ state.maintainingScrollAtEnd = false;
2745
+ },
2746
+ 0
2747
+ );
2748
+ }
2749
+ });
2750
+ return true;
2751
+ }
2752
+ return false;
2753
+ }
2754
+
2755
+ // src/utils/updateAveragesOnDataChange.ts
2756
+ function updateAveragesOnDataChange(state, oldData, newData) {
2757
+ var _a3;
2758
+ const {
2759
+ averageSizes,
2760
+ sizesKnown,
2761
+ indexByKey,
2762
+ props: { itemsAreEqual, getItemType, keyExtractor }
2763
+ } = state;
2764
+ if (!itemsAreEqual || !oldData.length || !newData.length) {
2765
+ for (const key in averageSizes) {
2766
+ delete averageSizes[key];
2767
+ }
2768
+ return;
2769
+ }
2770
+ const itemTypesToPreserve = {};
2771
+ const newDataLength = newData.length;
2772
+ const oldDataLength = oldData.length;
2773
+ for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
2774
+ const newItem = newData[newIndex];
2775
+ const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
2776
+ const oldIndex = indexByKey.get(id);
2777
+ if (oldIndex !== void 0 && oldIndex < oldDataLength) {
2778
+ const knownSize = sizesKnown.get(id);
2779
+ if (knownSize === void 0) continue;
2780
+ const oldItem = oldData[oldIndex];
2781
+ const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
2782
+ if (areEqual) {
2783
+ const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
2784
+ let typeData = itemTypesToPreserve[itemType];
2785
+ if (!typeData) {
2786
+ typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
2787
+ }
2788
+ typeData.totalSize += knownSize;
2789
+ typeData.count++;
2790
+ }
2791
+ }
2792
+ }
2793
+ for (const key in averageSizes) {
2794
+ delete averageSizes[key];
2795
+ }
2796
+ for (const itemType in itemTypesToPreserve) {
2797
+ const { totalSize, count } = itemTypesToPreserve[itemType];
2798
+ if (count > 0) {
2799
+ averageSizes[itemType] = {
2800
+ avg: totalSize / count,
2801
+ num: count
2802
+ };
2262
2803
  }
2263
- });
2804
+ }
2805
+ }
2806
+
2807
+ // src/core/checkResetContainers.ts
2808
+ function checkResetContainers(ctx, state, dataProp) {
2809
+ const { previousData } = state;
2810
+ if (previousData) {
2811
+ updateAveragesOnDataChange(state, previousData, dataProp);
2812
+ }
2813
+ const { maintainScrollAtEnd } = state.props;
2814
+ calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2815
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2816
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2817
+ if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2818
+ state.isEndReached = false;
2819
+ }
2820
+ if (!didMaintainScrollAtEnd) {
2821
+ checkAtTop(state);
2822
+ checkAtBottom(ctx, state);
2823
+ }
2824
+ delete state.previousData;
2264
2825
  }
2265
2826
 
2266
2827
  // src/core/doInitialAllocateContainers.ts
2267
2828
  function doInitialAllocateContainers(ctx, state) {
2268
- var _a;
2829
+ var _a3, _b, _c;
2269
2830
  const {
2270
2831
  scrollLength,
2271
2832
  props: {
@@ -2281,12 +2842,13 @@ function doInitialAllocateContainers(ctx, state) {
2281
2842
  const hasContainers = peek$(ctx, "numContainers");
2282
2843
  if (scrollLength > 0 && data.length > 0 && !hasContainers) {
2283
2844
  let averageItemSize;
2284
- const fn = getFixedItemSize || getEstimatedItemSize;
2285
- if (fn) {
2845
+ if (getFixedItemSize || getEstimatedItemSize) {
2286
2846
  let totalSize = 0;
2287
2847
  const num = Math.min(20, data.length);
2288
2848
  for (let i = 0; i < num; i++) {
2289
- totalSize += fn(0, data[0], getItemType ? (_a = getItemType(data[0], 0)) != null ? _a : "" : "");
2849
+ const item = data[i];
2850
+ const itemType = getItemType ? (_a3 = getItemType(item, i)) != null ? _a3 : "" : "";
2851
+ totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(i, item, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(i, item, itemType)) != null ? _c : estimatedItemSize;
2290
2852
  }
2291
2853
  averageItemSize = totalSize / num;
2292
2854
  } else {
@@ -2300,76 +2862,18 @@ function doInitialAllocateContainers(ctx, state) {
2300
2862
  set$(ctx, "numContainers", numContainers);
2301
2863
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2302
2864
  if (state.lastLayout) {
2303
- if (state.props.initialScroll) {
2865
+ if (state.initialScroll) {
2304
2866
  requestAnimationFrame(() => {
2305
- calculateItemsInView(ctx, state, { dataChanged: true });
2867
+ calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2306
2868
  });
2307
2869
  } else {
2308
- calculateItemsInView(ctx, state, { dataChanged: true });
2870
+ calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2309
2871
  }
2310
2872
  }
2311
2873
  return true;
2312
2874
  }
2313
2875
  }
2314
2876
 
2315
- // src/core/doMaintainScrollAtEnd.ts
2316
- function doMaintainScrollAtEnd(ctx, state, animated) {
2317
- const {
2318
- refScroller,
2319
- props: { maintainScrollAtEnd }
2320
- } = state;
2321
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2322
- const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2323
- if (paddingTop > 0) {
2324
- state.scroll = 0;
2325
- }
2326
- requestAnimationFrame(() => {
2327
- var _a;
2328
- if (state == null ? void 0 : state.isAtEnd) {
2329
- state.maintainingScrollAtEnd = true;
2330
- (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
2331
- animated
2332
- });
2333
- setTimeout(
2334
- () => {
2335
- state.maintainingScrollAtEnd = false;
2336
- },
2337
- 0
2338
- );
2339
- }
2340
- });
2341
- return true;
2342
- }
2343
- }
2344
-
2345
- // src/utils/checkAtTop.ts
2346
- function checkAtTop(state) {
2347
- if (!state) {
2348
- return;
2349
- }
2350
- const {
2351
- scrollLength,
2352
- scroll,
2353
- props: { onStartReachedThreshold }
2354
- } = state;
2355
- const distanceFromTop = scroll;
2356
- state.isAtStart = distanceFromTop <= 0;
2357
- state.isStartReached = checkThreshold(
2358
- distanceFromTop,
2359
- false,
2360
- onStartReachedThreshold * scrollLength,
2361
- state.isStartReached,
2362
- state.startReachedBlockedByTimer,
2363
- (distance) => {
2364
- var _a, _b;
2365
- return (_b = (_a = state.props).onStartReached) == null ? void 0 : _b.call(_a, { distanceFromStart: distance });
2366
- },
2367
- (block) => {
2368
- state.startReachedBlockedByTimer = block;
2369
- }
2370
- );
2371
- }
2372
-
2373
2877
  // src/core/handleLayout.ts
2374
2878
  function handleLayout(ctx, state, layout, setCanRender) {
2375
2879
  const { maintainScrollAtEnd } = state.props;
@@ -2404,19 +2908,19 @@ function handleLayout(ctx, state, layout, setCanRender) {
2404
2908
  if (state) {
2405
2909
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2406
2910
  }
2407
- if (__DEV__ && measuredLength === 0) {
2911
+ if (IS_DEV && measuredLength === 0) {
2408
2912
  warnDevOnce(
2409
2913
  "height0",
2410
2914
  `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.`
2411
2915
  );
2412
2916
  }
2413
- setCanRender(true);
2414
2917
  }
2918
+ setCanRender(true);
2415
2919
  }
2416
2920
 
2417
2921
  // src/core/onScroll.ts
2418
2922
  function onScroll(ctx, state, event) {
2419
- var _a, _b, _c;
2923
+ var _a3, _b, _c;
2420
2924
  const {
2421
2925
  scrollProcessingEnabled,
2422
2926
  props: { onScroll: onScrollProp }
@@ -2424,67 +2928,51 @@ function onScroll(ctx, state, event) {
2424
2928
  if (scrollProcessingEnabled === false) {
2425
2929
  return;
2426
2930
  }
2427
- 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) {
2931
+ if (((_b = (_a3 = event.nativeEvent) == null ? void 0 : _a3.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
2428
2932
  return;
2429
2933
  }
2430
2934
  const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2431
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
2432
- if (ignoreScrollFromMVCP && !state.scrollingTo) {
2433
- const { lt, gt } = ignoreScrollFromMVCP;
2434
- if (lt && newScroll < lt || gt && newScroll > gt) {
2435
- return;
2436
- }
2437
- }
2438
2935
  state.scrollPending = newScroll;
2439
- {
2440
- if (!state.onScrollRafScheduled) {
2441
- state.onScrollRafScheduled = true;
2442
- requestAnimationFrame(() => {
2443
- state.onScrollRafScheduled = false;
2444
- updateScroll(ctx, state, newScroll);
2445
- });
2446
- }
2447
- }
2936
+ updateScroll(ctx, state, newScroll);
2448
2937
  onScrollProp == null ? void 0 : onScrollProp(event);
2449
2938
  }
2450
- function updateScroll(ctx, state, newScroll) {
2451
- const scrollingTo = state.scrollingTo;
2452
- state.hasScrolled = true;
2453
- state.lastBatchingAction = Date.now();
2454
- const currentTime = Date.now();
2455
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
2456
- const adjust = state.scrollAdjustHandler.getAdjust();
2457
- state.scrollHistory.push({ scroll: newScroll - adjust, time: currentTime });
2458
- }
2459
- if (state.scrollHistory.length > 5) {
2460
- state.scrollHistory.shift();
2461
- }
2462
- state.scrollPrev = state.scroll;
2463
- state.scrollPrevTime = state.scrollTime;
2464
- state.scroll = newScroll;
2465
- state.scrollTime = currentTime;
2466
- if (Math.abs(state.scroll - state.scrollPrev) > 2) {
2467
- calculateItemsInView(ctx, state);
2468
- checkAtBottom(ctx, state);
2469
- checkAtTop(state);
2470
- }
2471
- }
2472
2939
 
2473
2940
  // src/core/ScrollAdjustHandler.ts
2474
2941
  var ScrollAdjustHandler = class {
2475
2942
  constructor(ctx) {
2476
2943
  this.appliedAdjust = 0;
2944
+ this.pendingAdjust = 0;
2477
2945
  this.mounted = false;
2478
2946
  this.context = ctx;
2947
+ {
2948
+ const commitPendingAdjust = () => {
2949
+ const state = this.context.internalState;
2950
+ const pending = this.pendingAdjust;
2951
+ if (pending !== 0) {
2952
+ this.pendingAdjust = 0;
2953
+ this.appliedAdjust += pending;
2954
+ state.scroll += pending;
2955
+ state.scrollForNextCalculateItemsInView = void 0;
2956
+ set$(this.context, "scrollAdjustPending", 0);
2957
+ set$(this.context, "scrollAdjust", this.appliedAdjust);
2958
+ calculateItemsInView(this.context, this.context.internalState);
2959
+ }
2960
+ };
2961
+ listen$(this.context, "scrollingTo", (value) => {
2962
+ if (value === void 0) {
2963
+ commitPendingAdjust();
2964
+ }
2965
+ });
2966
+ }
2479
2967
  }
2480
2968
  requestAdjust(add) {
2481
- const oldAdjustTop = peek$(this.context, "scrollAdjust") || 0;
2482
- this.appliedAdjust = add + oldAdjustTop;
2483
- const set = () => set$(this.context, "scrollAdjust", this.appliedAdjust);
2484
- if (this.mounted) {
2485
- set();
2969
+ const scrollingTo = peek$(this.context, "scrollingTo");
2970
+ if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2971
+ this.pendingAdjust += add;
2972
+ set$(this.context, "scrollAdjustPending", this.pendingAdjust);
2486
2973
  } else {
2487
- requestAnimationFrame(set);
2974
+ this.appliedAdjust += add;
2975
+ set$(this.context, "scrollAdjust", this.appliedAdjust);
2488
2976
  }
2489
2977
  }
2490
2978
  setMounted() {
@@ -2497,14 +2985,13 @@ var ScrollAdjustHandler = class {
2497
2985
 
2498
2986
  // src/core/updateItemSize.ts
2499
2987
  function updateItemSize(ctx, state, itemKey, sizeObj) {
2500
- var _a, _b;
2988
+ var _a3;
2501
2989
  const {
2502
2990
  sizesKnown,
2503
2991
  props: {
2504
2992
  getFixedItemSize,
2505
2993
  getItemType,
2506
2994
  horizontal,
2507
- maintainVisibleContentPosition,
2508
2995
  suggestEstimatedItemSize,
2509
2996
  onItemSizeChanged,
2510
2997
  data,
@@ -2512,17 +2999,17 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2512
2999
  }
2513
3000
  } = state;
2514
3001
  if (!data) return;
3002
+ const index = state.indexByKey.get(itemKey);
2515
3003
  if (getFixedItemSize) {
2516
- const index2 = state.indexByKey.get(itemKey);
2517
- if (index2 === void 0) {
3004
+ if (index === void 0) {
2518
3005
  return;
2519
3006
  }
2520
- const itemData = state.props.data[index2];
3007
+ const itemData = state.props.data[index];
2521
3008
  if (itemData === void 0) {
2522
3009
  return;
2523
3010
  }
2524
- const type = getItemType ? (_a = getItemType(itemData, index2)) != null ? _a : "" : "";
2525
- const size2 = getFixedItemSize(index2, itemData, type);
3011
+ const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
3012
+ const size2 = getFixedItemSize(index, itemData, type);
2526
3013
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2527
3014
  return;
2528
3015
  }
@@ -2532,15 +3019,11 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2532
3019
  let shouldMaintainScrollAtEnd = false;
2533
3020
  let minIndexSizeChanged;
2534
3021
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2535
- const index = state.indexByKey.get(itemKey);
2536
3022
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2537
- const diff = updateOneItemSize(state, itemKey, sizeObj);
2538
- const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
3023
+ const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3024
+ const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
2539
3025
  if (diff !== 0) {
2540
3026
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2541
- if (((_b = state.scrollingTo) == null ? void 0 : _b.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
2542
- requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
2543
- }
2544
3027
  const { startBuffered, endBuffered } = state;
2545
3028
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2546
3029
  if (!needsRecalculate) {
@@ -2570,13 +3053,13 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2570
3053
  if (minIndexSizeChanged !== void 0) {
2571
3054
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
2572
3055
  }
2573
- if (__DEV__ && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
3056
+ if (IS_DEV && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
2574
3057
  if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
2575
3058
  state.timeoutSizeMessage = setTimeout(() => {
2576
- var _a2;
3059
+ var _a4;
2577
3060
  state.timeoutSizeMessage = void 0;
2578
3061
  const num = state.sizesKnown.size;
2579
- const avg = (_a2 = state.averageSizes[""]) == null ? void 0 : _a2.avg;
3062
+ const avg = (_a4 = state.averageSizes[""]) == null ? void 0 : _a4.avg;
2580
3063
  console.warn(
2581
3064
  `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
2582
3065
  );
@@ -2598,8 +3081,8 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2598
3081
  }
2599
3082
  }
2600
3083
  }
2601
- function updateOneItemSize(state, itemKey, sizeObj) {
2602
- var _a;
3084
+ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3085
+ var _a3;
2603
3086
  const {
2604
3087
  sizes,
2605
3088
  indexByKey,
@@ -2609,12 +3092,12 @@ function updateOneItemSize(state, itemKey, sizeObj) {
2609
3092
  } = state;
2610
3093
  if (!data) return 0;
2611
3094
  const index = indexByKey.get(itemKey);
2612
- const prevSize = getItemSize(state, itemKey, index, data);
3095
+ const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
2613
3096
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
2614
3097
  const size = Math.round(rawSize) ;
2615
3098
  sizesKnown.set(itemKey, size);
2616
3099
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2617
- const itemType = getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : "";
3100
+ const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
2618
3101
  let averages = averageSizes[itemType];
2619
3102
  if (!averages) {
2620
3103
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
@@ -2623,7 +3106,7 @@ function updateOneItemSize(state, itemKey, sizeObj) {
2623
3106
  averages.num++;
2624
3107
  }
2625
3108
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2626
- sizes.set(itemKey, size);
3109
+ setSize(ctx, state, itemKey, size);
2627
3110
  return size - prevSize;
2628
3111
  }
2629
3112
  return 0;
@@ -2645,18 +3128,12 @@ var useCombinedRef = (...refs) => {
2645
3128
  };
2646
3129
 
2647
3130
  // src/platform/RefreshControl.tsx
2648
- function RefreshControl(props) {
3131
+ function RefreshControl(_props) {
2649
3132
  return null;
2650
3133
  }
2651
3134
 
2652
- // src/platform/StyleSheet.tsx
2653
- var StyleSheet = {
2654
- create: (styles) => styles,
2655
- flatten: (style) => style
2656
- };
2657
-
2658
3135
  // src/platform/useStickyScrollHandler.ts
2659
- function useStickyScrollHandler(stickyIndices, horizontal, ctx, onScroll2) {
3136
+ function useStickyScrollHandler(_stickyHeaderIndices, _horizontal, _ctx, onScroll2) {
2660
3137
  return onScroll2;
2661
3138
  }
2662
3139
 
@@ -2674,8 +3151,93 @@ function createColumnWrapperStyle(contentContainerStyle) {
2674
3151
  };
2675
3152
  }
2676
3153
  }
3154
+
3155
+ // src/utils/createImperativeHandle.ts
3156
+ function createImperativeHandle(ctx, state) {
3157
+ const scrollIndexIntoView = (options) => {
3158
+ if (state) {
3159
+ const { index, ...rest } = options;
3160
+ const { startNoBuffer, endNoBuffer } = state;
3161
+ if (index < startNoBuffer || index > endNoBuffer) {
3162
+ const viewPosition = index < startNoBuffer ? 0 : 1;
3163
+ scrollToIndex(ctx, state, {
3164
+ ...rest,
3165
+ index,
3166
+ viewPosition
3167
+ });
3168
+ }
3169
+ }
3170
+ };
3171
+ const refScroller = state.refScroller;
3172
+ return {
3173
+ flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3174
+ getNativeScrollRef: () => refScroller.current,
3175
+ getScrollableNode: () => refScroller.current.getScrollableNode(),
3176
+ getScrollResponder: () => refScroller.current.getScrollResponder(),
3177
+ getState: () => ({
3178
+ activeStickyIndex: state.activeStickyIndex,
3179
+ contentLength: state.totalSize,
3180
+ data: state.props.data,
3181
+ elementAtIndex: (index) => {
3182
+ var _a3;
3183
+ return (_a3 = ctx.viewRefs.get(findContainerId(ctx, getId(state, index)))) == null ? void 0 : _a3.current;
3184
+ },
3185
+ end: state.endNoBuffer,
3186
+ endBuffered: state.endBuffered,
3187
+ isAtEnd: state.isAtEnd,
3188
+ isAtStart: state.isAtStart,
3189
+ positionAtIndex: (index) => state.positions.get(getId(state, index)),
3190
+ positions: state.positions,
3191
+ scroll: state.scroll,
3192
+ scrollLength: state.scrollLength,
3193
+ sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
3194
+ sizes: state.sizesKnown,
3195
+ start: state.startNoBuffer,
3196
+ startBuffered: state.startBuffered
3197
+ }),
3198
+ scrollIndexIntoView,
3199
+ scrollItemIntoView: ({ item, ...props }) => {
3200
+ const data = state.props.data;
3201
+ const index = data.indexOf(item);
3202
+ if (index !== -1) {
3203
+ scrollIndexIntoView({ index, ...props });
3204
+ }
3205
+ },
3206
+ scrollToEnd: (options) => {
3207
+ const data = state.props.data;
3208
+ const stylePaddingBottom = state.props.stylePaddingBottom;
3209
+ const index = data.length - 1;
3210
+ if (index !== -1) {
3211
+ const paddingBottom = stylePaddingBottom || 0;
3212
+ const footerSize = peek$(ctx, "footerSize") || 0;
3213
+ scrollToIndex(ctx, state, {
3214
+ index,
3215
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3216
+ viewPosition: 1,
3217
+ ...options
3218
+ });
3219
+ }
3220
+ },
3221
+ scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3222
+ scrollToItem: ({ item, ...props }) => {
3223
+ const data = state.props.data;
3224
+ const index = data.indexOf(item);
3225
+ if (index !== -1) {
3226
+ scrollToIndex(ctx, state, { index, ...props });
3227
+ }
3228
+ },
3229
+ scrollToOffset: (params) => scrollTo(ctx, state, params),
3230
+ setScrollProcessingEnabled: (enabled) => {
3231
+ state.scrollProcessingEnabled = enabled;
3232
+ },
3233
+ setVisibleContentAnchorOffset: (value) => {
3234
+ const val = isFunction(value) ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
3235
+ set$(ctx, "scrollAdjustUserOffset", val);
3236
+ }
3237
+ };
3238
+ }
2677
3239
  function getRenderedItem(ctx, state, key) {
2678
- var _a;
3240
+ var _a3;
2679
3241
  if (!state) {
2680
3242
  return null;
2681
3243
  }
@@ -2688,15 +3250,17 @@ function getRenderedItem(ctx, state, key) {
2688
3250
  return null;
2689
3251
  }
2690
3252
  let renderedItem = null;
2691
- if (renderItem && data[index]) {
3253
+ const extraData = peek$(ctx, "extraData");
3254
+ const item = data[index];
3255
+ if (renderItem && !isNullOrUndefined(item)) {
2692
3256
  const itemProps = {
2693
3257
  data,
2694
- extraData: peek$(ctx, "extraData"),
3258
+ extraData,
2695
3259
  index,
2696
- item: data[index],
2697
- type: getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : ""
3260
+ item,
3261
+ type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
2698
3262
  };
2699
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React4__default.createElement(renderItem, itemProps);
3263
+ renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__default.createElement(renderItem, itemProps);
2700
3264
  }
2701
3265
  return { index, item: data[index], renderedItem };
2702
3266
  }
@@ -2746,58 +3310,6 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
2746
3310
  return (event) => throttle(originalHandler, scrollEventThrottle, { nativeEvent: event.nativeEvent });
2747
3311
  }
2748
3312
 
2749
- // src/utils/updateAveragesOnDataChange.ts
2750
- function updateAveragesOnDataChange(state, oldData, newData) {
2751
- var _a;
2752
- const {
2753
- averageSizes,
2754
- sizesKnown,
2755
- indexByKey,
2756
- props: { itemsAreEqual, getItemType, keyExtractor }
2757
- } = state;
2758
- if (!itemsAreEqual || !oldData.length || !newData.length) {
2759
- for (const key in averageSizes) {
2760
- delete averageSizes[key];
2761
- }
2762
- return;
2763
- }
2764
- const itemTypesToPreserve = {};
2765
- const newDataLength = newData.length;
2766
- const oldDataLength = oldData.length;
2767
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
2768
- const newItem = newData[newIndex];
2769
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
2770
- const oldIndex = indexByKey.get(id);
2771
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
2772
- const knownSize = sizesKnown.get(id);
2773
- if (knownSize === void 0) continue;
2774
- const oldItem = oldData[oldIndex];
2775
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
2776
- if (areEqual) {
2777
- const itemType = getItemType ? (_a = getItemType(newItem, newIndex)) != null ? _a : "" : "";
2778
- let typeData = itemTypesToPreserve[itemType];
2779
- if (!typeData) {
2780
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
2781
- }
2782
- typeData.totalSize += knownSize;
2783
- typeData.count++;
2784
- }
2785
- }
2786
- }
2787
- for (const key in averageSizes) {
2788
- delete averageSizes[key];
2789
- }
2790
- for (const itemType in itemTypesToPreserve) {
2791
- const { totalSize, count } = itemTypesToPreserve[itemType];
2792
- if (count > 0) {
2793
- averageSizes[itemType] = {
2794
- avg: totalSize / count,
2795
- num: count
2796
- };
2797
- }
2798
- }
2799
- }
2800
-
2801
3313
  // src/components/LegendList.tsx
2802
3314
  var DEFAULT_DRAW_DISTANCE = 250;
2803
3315
  var DEFAULT_ITEM_SIZE = 100;
@@ -2807,22 +3319,24 @@ var LegendList = typedMemo(
2807
3319
  const isChildrenMode = children !== void 0 && dataProp === void 0;
2808
3320
  const processedProps = isChildrenMode ? {
2809
3321
  ...restProps,
2810
- data: (isArray(children) ? children : React4.Children.toArray(children)).flat(1),
3322
+ data: (isArray(children) ? children : React3.Children.toArray(children)).flat(1),
2811
3323
  renderItem: ({ item }) => item
2812
3324
  } : {
2813
3325
  ...restProps,
2814
3326
  data: dataProp || [],
2815
3327
  renderItem: renderItemProp
2816
3328
  };
2817
- return /* @__PURE__ */ React4.createElement(StateProvider, null, /* @__PURE__ */ React4.createElement(LegendListInner, { ...processedProps, ref: forwardedRef }));
3329
+ return /* @__PURE__ */ React3.createElement(StateProvider, null, /* @__PURE__ */ React3.createElement(LegendListInner, { ...processedProps, ref: forwardedRef }));
2818
3330
  })
2819
3331
  );
2820
3332
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3333
+ var _a3, _b;
2821
3334
  const {
2822
3335
  alignItemsAtEnd = false,
2823
3336
  columnWrapperStyle,
2824
3337
  contentContainerStyle: contentContainerStyleProp,
2825
3338
  data: dataProp = [],
3339
+ dataVersion,
2826
3340
  drawDistance = 250,
2827
3341
  enableAverages = true,
2828
3342
  estimatedItemSize: estimatedItemSizeProp,
@@ -2833,6 +3347,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2833
3347
  getItemType,
2834
3348
  horizontal,
2835
3349
  initialContainerPoolRatio = 2,
3350
+ initialScrollAtEnd = false,
2836
3351
  initialScrollIndex: initialScrollIndexProp,
2837
3352
  initialScrollOffset: initialScrollOffsetProp,
2838
3353
  itemsAreEqual,
@@ -2841,7 +3356,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2841
3356
  ListHeaderComponent,
2842
3357
  maintainScrollAtEnd = false,
2843
3358
  maintainScrollAtEndThreshold = 0.1,
2844
- maintainVisibleContentPosition = true,
3359
+ maintainVisibleContentPosition = false,
2845
3360
  numColumns: numColumnsProp = 1,
2846
3361
  onEndReached,
2847
3362
  onEndReachedThreshold = 0.5,
@@ -2853,6 +3368,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2853
3368
  onScroll: onScrollProp,
2854
3369
  onStartReached,
2855
3370
  onStartReachedThreshold = 0.5,
3371
+ onStickyHeaderChange,
3372
+ onViewableItemsChanged,
2856
3373
  progressViewOffset,
2857
3374
  recycleItems = false,
2858
3375
  refreshControl,
@@ -2861,19 +3378,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2861
3378
  renderItem,
2862
3379
  scrollEventThrottle,
2863
3380
  snapToIndices,
2864
- stickyIndices,
3381
+ stickyHeaderIndices: stickyHeaderIndicesProp,
3382
+ stickyIndices: stickyIndicesDeprecated,
2865
3383
  style: styleProp,
2866
3384
  suggestEstimatedItemSize,
3385
+ viewabilityConfig,
3386
+ viewabilityConfigCallbackPairs,
2867
3387
  waitForInitialLayout = true,
2868
3388
  ...rest
2869
3389
  } = props;
2870
- const [renderNum, setRenderNum] = useState(0);
2871
- const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
2872
- const [canRender, setCanRender] = React4.useState(!IsNewArchitecture);
2873
3390
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
2874
3391
  const style = { ...StyleSheet.flatten(styleProp) };
2875
3392
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
2876
3393
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3394
+ const [renderNum, setRenderNum] = useState(0);
3395
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3396
+ const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
2877
3397
  const ctx = useStateContext();
2878
3398
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
2879
3399
  const refScroller = useRef(null);
@@ -2881,6 +3401,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2881
3401
  const estimatedItemSize = estimatedItemSizeProp != null ? estimatedItemSizeProp : DEFAULT_ITEM_SIZE;
2882
3402
  const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
2883
3403
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
3404
+ const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
3405
+ if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3406
+ warnDevOnce(
3407
+ "stickyIndices",
3408
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
3409
+ );
3410
+ }
2884
3411
  const refState = useRef();
2885
3412
  if (!refState.current) {
2886
3413
  if (!ctx.internalState) {
@@ -2891,18 +3418,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2891
3418
  columns: /* @__PURE__ */ new Map(),
2892
3419
  containerItemKeys: /* @__PURE__ */ new Set(),
2893
3420
  containerItemTypes: /* @__PURE__ */ new Map(),
3421
+ dataChangeNeedsScrollUpdate: false,
3422
+ didColumnsChange: false,
3423
+ didDataChange: false,
2894
3424
  enableScrollForNextCalculateItemsInView: true,
2895
3425
  endBuffered: -1,
2896
3426
  endNoBuffer: -1,
2897
- endReachedBlockedByTimer: false,
3427
+ endReachedSnapshot: void 0,
2898
3428
  firstFullyOnScreenIndex: -1,
2899
- idCache: /* @__PURE__ */ new Map(),
3429
+ idCache: [],
2900
3430
  idsInView: [],
2901
3431
  indexByKey: /* @__PURE__ */ new Map(),
2902
- initialScroll,
3432
+ initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
3433
+ attempts: 0,
3434
+ index: initialScrollProp.index,
3435
+ settledTicks: 0,
3436
+ viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3437
+ viewPosition: initialScrollProp.viewPosition
3438
+ } : void 0,
3439
+ initialScroll: initialScrollProp,
2903
3440
  isAtEnd: false,
2904
3441
  isAtStart: false,
2905
3442
  isEndReached: false,
3443
+ isFirst: true,
2906
3444
  isStartReached: false,
2907
3445
  lastBatchingAction: Date.now(),
2908
3446
  lastLayout: void 0,
@@ -2927,7 +3465,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2927
3465
  sizesKnown: /* @__PURE__ */ new Map(),
2928
3466
  startBuffered: -1,
2929
3467
  startNoBuffer: -1,
2930
- startReachedBlockedByTimer: false,
3468
+ startReachedSnapshot: void 0,
2931
3469
  stickyContainerPool: /* @__PURE__ */ new Set(),
2932
3470
  stickyContainers: /* @__PURE__ */ new Map(),
2933
3471
  timeoutSizeMessage: 0,
@@ -2935,21 +3473,27 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2935
3473
  totalSize: 0,
2936
3474
  viewabilityConfigCallbackPairs: void 0
2937
3475
  };
3476
+ const internalState = ctx.internalState;
3477
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
2938
3478
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
2939
3479
  set$(ctx, "extraData", extraData);
2940
3480
  }
2941
3481
  refState.current = ctx.internalState;
2942
3482
  }
2943
3483
  const state = refState.current;
2944
- const isFirst = !state.props.renderItem;
2945
- const didDataChange = state.props.data !== dataProp;
2946
- const throttleScrollFn = (
2947
- // @ts-expect-error TODO Fix this
2948
- scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp
2949
- );
3484
+ const isFirstLocal = state.isFirst;
3485
+ state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3486
+ const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3487
+ if (didDataChangeLocal) {
3488
+ state.dataChangeNeedsScrollUpdate = true;
3489
+ state.didDataChange = true;
3490
+ state.previousData = state.props.data;
3491
+ }
3492
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
2950
3493
  state.props = {
2951
3494
  alignItemsAtEnd,
2952
3495
  data: dataProp,
3496
+ dataVersion,
2953
3497
  enableAverages,
2954
3498
  estimatedItemSize,
2955
3499
  getEstimatedItemSize,
@@ -2957,7 +3501,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2957
3501
  getItemType,
2958
3502
  horizontal: !!horizontal,
2959
3503
  initialContainerPoolRatio,
2960
- initialScroll,
2961
3504
  itemsAreEqual,
2962
3505
  keyExtractor,
2963
3506
  maintainScrollAtEnd,
@@ -2971,45 +3514,25 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2971
3514
  onScroll: throttleScrollFn,
2972
3515
  onStartReached,
2973
3516
  onStartReachedThreshold,
3517
+ onStickyHeaderChange,
2974
3518
  recycleItems: !!recycleItems,
2975
3519
  renderItem,
2976
3520
  scrollBuffer,
2977
3521
  snapToIndices,
2978
- stickyIndicesArr: stickyIndices != null ? stickyIndices : [],
2979
- stickyIndicesSet: useMemo(() => new Set(stickyIndices != null ? stickyIndices : []), [stickyIndices == null ? void 0 : stickyIndices.join(",")]),
3522
+ stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
3523
+ stickyIndicesSet: useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
2980
3524
  stylePaddingBottom: stylePaddingBottomState,
2981
3525
  stylePaddingTop: stylePaddingTopState,
2982
3526
  suggestEstimatedItemSize: !!suggestEstimatedItemSize
2983
3527
  };
2984
3528
  state.refScroller = refScroller;
2985
- const checkResetContainers = (isFirst2) => {
2986
- const state2 = refState.current;
2987
- if (state2) {
2988
- if (!isFirst2 && state2.props.data !== dataProp) {
2989
- updateAveragesOnDataChange(state2, state2.props.data, dataProp);
2990
- }
2991
- state2.props.data = dataProp;
2992
- if (!isFirst2) {
2993
- calculateItemsInView(ctx, state2, { dataChanged: true, doMVCP: true });
2994
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2995
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state2, false);
2996
- if (!didMaintainScrollAtEnd && dataProp.length > state2.props.data.length) {
2997
- state2.isEndReached = false;
2998
- }
2999
- if (!didMaintainScrollAtEnd) {
3000
- checkAtTop(state2);
3001
- checkAtBottom(ctx, state2);
3002
- }
3003
- }
3004
- }
3005
- };
3006
3529
  const memoizedLastItemKeys = useMemo(() => {
3007
3530
  if (!dataProp.length) return [];
3008
3531
  return Array.from(
3009
3532
  { length: Math.min(numColumnsProp, dataProp.length) },
3010
3533
  (_, i) => getId(state, dataProp.length - 1 - i)
3011
3534
  );
3012
- }, [dataProp, numColumnsProp]);
3535
+ }, [dataProp, dataVersion, numColumnsProp]);
3013
3536
  const initializeStateVars = () => {
3014
3537
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3015
3538
  set$(ctx, "numColumns", numColumnsProp);
@@ -3017,44 +3540,70 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3017
3540
  setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
3018
3541
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3019
3542
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3020
- if (maintainVisibleContentPosition && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
3543
+ if (paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
3021
3544
  if (state.scroll < 0) {
3022
3545
  paddingDiff += state.scroll;
3023
3546
  }
3024
3547
  requestAdjust(ctx, state, paddingDiff);
3025
3548
  }
3026
3549
  };
3027
- if (isFirst) {
3550
+ if (isFirstLocal) {
3028
3551
  initializeStateVars();
3029
- updateAllPositions(ctx, state);
3552
+ updateItemPositions(
3553
+ ctx,
3554
+ state,
3555
+ /*dataChanged*/
3556
+ true
3557
+ );
3030
3558
  }
3031
3559
  const initialContentOffset = useMemo(() => {
3032
- if (initialScroll) {
3033
- const { index, viewOffset } = initialScroll;
3034
- let initialContentOffset2 = viewOffset || 0;
3035
- if (index !== void 0) {
3036
- initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
3037
- }
3038
- refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
3039
- if (initialContentOffset2 > 0) {
3040
- scrollTo(state, { animated: false, index, offset: initialContentOffset2 });
3041
- }
3042
- return initialContentOffset2;
3560
+ var _a4, _b2;
3561
+ const { initialScroll } = refState.current;
3562
+ if (!initialScroll) {
3563
+ refState.current.initialAnchor = void 0;
3564
+ return 0;
3565
+ }
3566
+ if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3567
+ refState.current.initialAnchor = {
3568
+ attempts: 0,
3569
+ index: initialScroll.index,
3570
+ settledTicks: 0,
3571
+ viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3572
+ viewPosition: initialScroll.viewPosition
3573
+ };
3043
3574
  }
3044
- return 0;
3575
+ if (initialScroll.contentOffset !== void 0) {
3576
+ return initialScroll.contentOffset;
3577
+ }
3578
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3579
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3580
+ let clampedOffset = resolvedOffset;
3581
+ if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3582
+ const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3583
+ clampedOffset = Math.min(clampedOffset, maxOffset);
3584
+ }
3585
+ clampedOffset = Math.max(0, clampedOffset);
3586
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3587
+ refState.current.initialScroll = updatedInitialScroll;
3588
+ state.initialScroll = updatedInitialScroll;
3589
+ refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3590
+ return clampedOffset;
3045
3591
  }, [renderNum]);
3046
- if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
3047
- state.lastBatchingAction = Date.now();
3048
- if (!keyExtractorProp && !isFirst && didDataChange) {
3049
- __DEV__ && warnDevOnce(
3592
+ if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3593
+ refState.current.lastBatchingAction = Date.now();
3594
+ if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
3595
+ IS_DEV && warnDevOnce(
3050
3596
  "keyExtractor",
3051
3597
  "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
3052
3598
  );
3053
- state.sizes.clear();
3054
- state.positions.clear();
3599
+ refState.current.sizes.clear();
3600
+ refState.current.positions.clear();
3601
+ refState.current.totalSize = 0;
3602
+ set$(ctx, "totalSize", 0);
3055
3603
  }
3056
3604
  }
3057
3605
  const onLayoutHeader = useCallback((rect, fromLayoutEffect) => {
3606
+ const { initialScroll } = refState.current;
3058
3607
  const size = rect[horizontal ? "width" : "height"];
3059
3608
  set$(ctx, "headerSize", size);
3060
3609
  if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
@@ -3065,132 +3614,71 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3065
3614
  }
3066
3615
  }
3067
3616
  }, []);
3617
+ const doInitialScroll = useCallback(() => {
3618
+ var _a4;
3619
+ const initialScroll = state.initialScroll;
3620
+ if (initialScroll) {
3621
+ scrollTo(ctx, state, {
3622
+ animated: false,
3623
+ index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3624
+ isInitialScroll: true,
3625
+ offset: initialContentOffset,
3626
+ precomputedWithViewOffset: true
3627
+ });
3628
+ }
3629
+ }, [initialContentOffset]);
3630
+ const onLayoutChange = useCallback((layout) => {
3631
+ doInitialScroll();
3632
+ handleLayout(ctx, state, layout, setCanRender);
3633
+ }, []);
3634
+ const { onLayout } = useOnLayoutSync({
3635
+ onLayoutChange,
3636
+ onLayoutProp,
3637
+ ref: refScroller
3638
+ // the type of ScrollView doesn't include measure?
3639
+ });
3068
3640
  useLayoutEffect(() => {
3069
3641
  if (snapToIndices) {
3070
3642
  updateSnapToOffsets(ctx, state);
3071
3643
  }
3072
3644
  }, [snapToIndices]);
3073
3645
  useLayoutEffect(() => {
3074
- const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainersCallback();
3075
- if (!didAllocateContainers) {
3076
- checkResetContainers(
3077
- /*isFirst*/
3078
- isFirst
3079
- );
3080
- }
3081
- }, [dataProp, numColumnsProp]);
3646
+ const {
3647
+ didColumnsChange,
3648
+ didDataChange,
3649
+ isFirst,
3650
+ props: { data }
3651
+ } = state;
3652
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3653
+ if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3654
+ checkResetContainers(ctx, state, data);
3655
+ }
3656
+ state.didColumnsChange = false;
3657
+ state.didDataChange = false;
3658
+ state.isFirst = false;
3659
+ }, [dataProp, dataVersion, numColumnsProp]);
3082
3660
  useLayoutEffect(() => {
3083
3661
  set$(ctx, "extraData", extraData);
3084
3662
  }, [extraData]);
3085
- const { onLayout } = useSyncLayout({
3086
- onLayout: onLayoutProp,
3087
- onLayoutChange: useCallback(
3088
- (rectangle) => {
3089
- handleLayout(ctx, state, rectangle, setCanRender);
3090
- },
3091
- [ctx, state, setCanRender]
3092
- ),
3093
- ref: refScroller
3094
- });
3095
3663
  useLayoutEffect(initializeStateVars, [
3664
+ dataVersion,
3096
3665
  memoizedLastItemKeys.join(","),
3097
3666
  numColumnsProp,
3098
- stylePaddingTopState,
3099
- stylePaddingBottomState
3667
+ stylePaddingBottomState,
3668
+ stylePaddingTopState
3100
3669
  ]);
3101
- const doInitialAllocateContainersCallback = () => {
3102
- return doInitialAllocateContainers(ctx, state);
3103
- };
3104
- useImperativeHandle(forwardedRef, () => {
3105
- const scrollIndexIntoView = (options) => {
3106
- const state2 = refState.current;
3107
- if (state2) {
3108
- const { index, ...rest2 } = options;
3109
- const { startNoBuffer, endNoBuffer } = state2;
3110
- if (index < startNoBuffer || index > endNoBuffer) {
3111
- const viewPosition = index < startNoBuffer ? 0 : 1;
3112
- scrollToIndex(ctx, state2, {
3113
- ...rest2,
3114
- index,
3115
- viewPosition
3116
- });
3117
- }
3118
- }
3119
- };
3120
- return {
3121
- flashScrollIndicators: () => {
3122
- var _a, _b;
3123
- return (_b = (_a = refScroller.current) == null ? void 0 : _a.flashScrollIndicators) == null ? void 0 : _b.call(_a);
3124
- },
3125
- getNativeScrollRef: () => refScroller.current,
3126
- getScrollableNode: () => refScroller.current,
3127
- getScrollResponder: () => refScroller.current,
3128
- getState: () => {
3129
- const state2 = refState.current;
3130
- return state2 ? {
3131
- contentLength: state2.totalSize,
3132
- data: state2.props.data,
3133
- end: state2.endNoBuffer,
3134
- endBuffered: state2.endBuffered,
3135
- isAtEnd: state2.isAtEnd,
3136
- isAtStart: state2.isAtStart,
3137
- positionAtIndex: (index) => state2.positions.get(getId(state2, index)),
3138
- positions: state2.positions,
3139
- scroll: state2.scroll,
3140
- scrollLength: state2.scrollLength,
3141
- sizeAtIndex: (index) => state2.sizesKnown.get(getId(state2, index)),
3142
- sizes: state2.sizesKnown,
3143
- start: state2.startNoBuffer,
3144
- startBuffered: state2.startBuffered
3145
- } : {};
3146
- },
3147
- scrollIndexIntoView,
3148
- scrollItemIntoView: ({ item, ...props2 }) => {
3149
- const data = refState.current.props.data;
3150
- const index = data.indexOf(item);
3151
- if (index !== -1) {
3152
- scrollIndexIntoView({ index, ...props2 });
3153
- }
3154
- },
3155
- scrollToEnd: (options) => {
3156
- const data = refState.current.props.data;
3157
- const stylePaddingBottom = refState.current.props.stylePaddingBottom;
3158
- const index = data.length - 1;
3159
- if (index !== -1) {
3160
- const paddingBottom = stylePaddingBottom || 0;
3161
- const footerSize = peek$(ctx, "footerSize") || 0;
3162
- scrollToIndex(ctx, state, {
3163
- index,
3164
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3165
- viewPosition: 1,
3166
- ...options
3167
- });
3168
- }
3169
- },
3170
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3171
- scrollToItem: ({ item, ...props2 }) => {
3172
- const data = refState.current.props.data;
3173
- const index = data.indexOf(item);
3174
- if (index !== -1) {
3175
- scrollToIndex(ctx, state, { index, ...props2 });
3176
- }
3177
- },
3178
- scrollToOffset: (params) => scrollTo(state, params),
3179
- setScrollProcessingEnabled: (enabled) => {
3180
- refState.current.scrollProcessingEnabled = enabled;
3181
- },
3182
- setVisibleContentAnchorOffset: (value) => {
3183
- const val = typeof value === "function" ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
3184
- set$(ctx, "scrollAdjustUserOffset", val);
3185
- }
3186
- };
3187
- }, []);
3670
+ useEffect(() => {
3671
+ const viewability = setupViewability({
3672
+ onViewableItemsChanged,
3673
+ viewabilityConfig,
3674
+ viewabilityConfigCallbackPairs
3675
+ });
3676
+ state.viewabilityConfigCallbackPairs = viewability;
3677
+ state.enableScrollForNextCalculateItemsInView = !viewability;
3678
+ }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3679
+ useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
3188
3680
  {
3189
- useEffect(() => {
3190
- if (initialContentOffset) {
3191
- scrollTo(state, { animated: false, offset: initialContentOffset });
3192
- }
3193
- }, []);
3681
+ useEffect(doInitialScroll, []);
3194
3682
  }
3195
3683
  const fns = useMemo(
3196
3684
  () => ({
@@ -3200,8 +3688,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3200
3688
  }),
3201
3689
  []
3202
3690
  );
3203
- const onScrollHandler = useStickyScrollHandler(stickyIndices, horizontal, ctx, fns.onScroll);
3204
- return /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(
3691
+ const onScrollHandler = useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, fns.onScroll);
3692
+ return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(
3205
3693
  ListComponent,
3206
3694
  {
3207
3695
  ...rest,
@@ -3219,7 +3707,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3219
3707
  onMomentumScrollEnd: (event) => {
3220
3708
  {
3221
3709
  requestAnimationFrame(() => {
3222
- finishScrollTo(refState.current);
3710
+ finishScrollTo(ctx, refState.current);
3223
3711
  });
3224
3712
  }
3225
3713
  if (onMomentumScrollEnd) {
@@ -3228,9 +3716,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3228
3716
  },
3229
3717
  onScroll: onScrollHandler,
3230
3718
  recycleItems,
3231
- refreshControl: refreshControl ? stylePaddingTopState > 0 ? React4.cloneElement(refreshControl, {
3719
+ refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControl, {
3232
3720
  progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
3233
- }) : refreshControl : onRefresh && /* @__PURE__ */ React4.createElement(
3721
+ }) : refreshControl : onRefresh && /* @__PURE__ */ React3.createElement(
3234
3722
  RefreshControl,
3235
3723
  {
3236
3724
  onRefresh,
@@ -3239,14 +3727,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3239
3727
  }
3240
3728
  ),
3241
3729
  refScrollView: combinedRef,
3242
- scrollAdjustHandler: state.scrollAdjustHandler,
3730
+ scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3731
+ scrollEventThrottle: 16 ,
3243
3732
  snapToIndices,
3244
- stickyIndices,
3733
+ stickyHeaderIndices,
3245
3734
  style,
3246
3735
  updateItemSize: fns.updateItemSize,
3247
3736
  waitForInitialLayout
3248
3737
  }
3249
- ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React4.createElement(DebugView, { state: refState.current }));
3738
+ ), IS_DEV && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3.createElement(DebugView, { state: refState.current }));
3250
3739
  });
3251
3740
 
3252
- export { LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout2 as useSyncLayout, useViewability, useViewabilityAmount };
3741
+ export { LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };