@legendapp/list 0.4.5 → 0.5.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.
Files changed (5) hide show
  1. package/index.d.mts +20 -4
  2. package/index.d.ts +20 -4
  3. package/index.js +516 -369
  4. package/index.mjs +504 -357
  5. package/package.json +1 -1
package/index.mjs CHANGED
@@ -1,15 +1,12 @@
1
- import * as React6 from 'react';
2
- import { forwardRef, useRef, useMemo, useCallback, useEffect, useImperativeHandle, useState } from 'react';
3
- import { ScrollView, View, StyleSheet, Dimensions, unstable_batchedUpdates } from 'react-native';
1
+ import * as React7 from 'react';
2
+ import React7__default, { forwardRef, useRef, useMemo, useCallback, useImperativeHandle, useEffect, useState } from 'react';
3
+ import { Platform, StyleSheet, ScrollView, View, Dimensions } from 'react-native';
4
4
 
5
5
  // src/LegendList.tsx
6
- var LeanView = React6.forwardRef((props, ref) => {
7
- return React6.createElement("RCTView", { ...props, ref });
8
- });
9
- LeanView.displayName = "RCTView";
10
- var ContextState = React6.createContext(null);
6
+ var USE_CONTENT_INSET = Platform.OS === "ios";
7
+ var ContextState = React7.createContext(null);
11
8
  function StateProvider({ children }) {
12
- const [value] = React6.useState(() => ({
9
+ const [value] = React7.useState(() => ({
13
10
  hooks: /* @__PURE__ */ new Map(),
14
11
  listeners: /* @__PURE__ */ new Map(),
15
12
  values: /* @__PURE__ */ new Map(),
@@ -18,14 +15,14 @@ function StateProvider({ children }) {
18
15
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
19
16
  mapViewabilityAmountValues: /* @__PURE__ */ new Map()
20
17
  }));
21
- return /* @__PURE__ */ React6.createElement(ContextState.Provider, { value }, children);
18
+ return /* @__PURE__ */ React7.createElement(ContextState.Provider, { value }, children);
22
19
  }
23
20
  function useStateContext() {
24
- return React6.useContext(ContextState);
21
+ return React7.useContext(ContextState);
25
22
  }
26
23
  function use$(signalName) {
27
- const { hooks, values } = React6.useContext(ContextState);
28
- const [, forceUpdate] = React6.useReducer((x) => x + 1, 0);
24
+ const { hooks, values } = React7.useContext(ContextState);
25
+ const [, forceUpdate] = React7.useReducer((x) => x + 1, 0);
29
26
  hooks.set(signalName, forceUpdate);
30
27
  return values.get(signalName);
31
28
  }
@@ -58,27 +55,46 @@ function set$(ctx, signalName, value) {
58
55
  }
59
56
  }
60
57
 
58
+ // src/$ScrollView.tsx
59
+ var OFFSET_TEST = 0;
60
+ var $ScrollView = React7.forwardRef(function $ScrollView2(props, ref) {
61
+ const { style, horizontal, ...rest } = props;
62
+ const scrollAdjust = use$("scrollAdjust");
63
+ const adjustProps = {};
64
+ if (scrollAdjust !== 0) {
65
+ if (USE_CONTENT_INSET) {
66
+ adjustProps.contentInset = horizontal ? { left: -scrollAdjust } : { top: -scrollAdjust + OFFSET_TEST };
67
+ } else {
68
+ adjustProps.style = horizontal ? { marginLeft: -scrollAdjust } : { marginTop: -scrollAdjust };
69
+ if (style) {
70
+ adjustProps.style = StyleSheet.compose(style, adjustProps.style);
71
+ }
72
+ }
73
+ }
74
+ return /* @__PURE__ */ React7.createElement(ScrollView, { ...rest, style, horizontal, ...adjustProps, ref });
75
+ });
76
+ var LeanView = React7.forwardRef((props, ref) => {
77
+ return React7.createElement("RCTView", { ...props, ref });
78
+ });
79
+ LeanView.displayName = "RCTView";
80
+
61
81
  // src/$View.tsx
62
- function $View({ $key, $style, ...rest }) {
82
+ function $View({ $key, $key2, $style, ...rest }) {
63
83
  use$($key);
84
+ if ($key2) {
85
+ use$($key2);
86
+ }
64
87
  const style = $style();
65
- return /* @__PURE__ */ React6.createElement(LeanView, { style, ...rest });
88
+ return /* @__PURE__ */ React7.createElement(LeanView, { style, ...rest });
66
89
  }
67
- function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorComponent }) {
68
- const itemIndex = use$(`containerIndex${id}`);
69
- const numItems = ItemSeparatorComponent ? use$("numItems") : 0;
70
- if (itemIndex < 0) {
90
+ function InnerContainer({ containerId, getRenderedItem, recycleItems, ItemSeparatorComponent }) {
91
+ const lastItemKey = use$("lastItemKey");
92
+ const itemKey = use$(`containerItemKey${containerId}`);
93
+ if (itemKey === void 0) {
71
94
  return null;
72
95
  }
73
- return /* @__PURE__ */ React6.createElement(React6.Fragment, { key: recycleItems ? void 0 : itemIndex }, /* @__PURE__ */ React6.createElement(RenderedItem, { itemIndex, id, getRenderedItem }), ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
74
- }
75
- function RenderedItem({
76
- itemIndex,
77
- id,
78
- getRenderedItem
79
- }) {
80
- const renderedItem = getRenderedItem(itemIndex, id);
81
- return renderedItem;
96
+ const renderedItem = getRenderedItem(itemKey, containerId);
97
+ return /* @__PURE__ */ React7__default.createElement(React7__default.Fragment, { key: recycleItems ? void 0 : itemKey }, renderedItem, ItemSeparatorComponent && itemKey !== lastItemKey && ItemSeparatorComponent);
82
98
  }
83
99
  var Container = ({
84
100
  id,
@@ -91,38 +107,44 @@ var Container = ({
91
107
  const ctx = useStateContext();
92
108
  const createStyle = () => {
93
109
  const position = peek$(ctx, `containerPosition${id}`);
110
+ const visible = peek$(ctx, `containerDidLayout${id}`);
94
111
  return horizontal ? {
95
112
  flexDirection: "row",
96
113
  position: "absolute",
97
- top: 0,
114
+ top: visible ? 0 : -1e7,
98
115
  bottom: 0,
99
- left: position,
100
- opacity: position < 0 ? 0 : 1
116
+ left: position
101
117
  } : {
102
118
  position: "absolute",
103
- left: 0,
119
+ left: visible ? 0 : -1e7,
104
120
  right: 0,
105
- top: position,
106
- opacity: position < 0 ? 0 : 1
121
+ top: position
107
122
  };
108
123
  };
109
- return /* @__PURE__ */ React6.createElement(
124
+ return /* @__PURE__ */ React7__default.createElement(
110
125
  $View,
111
126
  {
112
127
  $key: `containerPosition${id}`,
128
+ $key2: `containerDidLayout${id}`,
113
129
  $style: createStyle,
114
130
  onLayout: (event) => {
115
- const index = peek$(ctx, `containerIndex${id}`);
116
- if (index >= 0) {
131
+ const key = peek$(ctx, `containerItemKey${id}`);
132
+ if (key !== void 0) {
117
133
  const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
118
- onLayout(index, size);
134
+ onLayout(key, size);
135
+ const measured = peek$(ctx, `containerDidLayout${id}`);
136
+ if (!measured) {
137
+ requestAnimationFrame(() => {
138
+ set$(ctx, `containerDidLayout${id}`, true);
139
+ });
140
+ }
119
141
  }
120
142
  }
121
143
  },
122
- /* @__PURE__ */ React6.createElement(
144
+ /* @__PURE__ */ React7__default.createElement(
123
145
  InnerContainer,
124
146
  {
125
- id,
147
+ containerId: id,
126
148
  getRenderedItem,
127
149
  recycleItems,
128
150
  ItemSeparatorComponent
@@ -132,7 +154,7 @@ var Container = ({
132
154
  };
133
155
 
134
156
  // src/Containers.tsx
135
- var Containers = React6.memo(function Containers2({
157
+ var Containers = React7.memo(function Containers2({
136
158
  horizontal,
137
159
  recycleItems,
138
160
  ItemSeparatorComponent,
@@ -140,11 +162,11 @@ var Containers = React6.memo(function Containers2({
140
162
  getRenderedItem
141
163
  }) {
142
164
  const ctx = useStateContext();
143
- const numContainers = use$("numContainers");
165
+ const numContainers = use$("numContainersPooled");
144
166
  const containers = [];
145
167
  for (let i = 0; i < numContainers; i++) {
146
168
  containers.push(
147
- /* @__PURE__ */ React6.createElement(
169
+ /* @__PURE__ */ React7.createElement(
148
170
  Container,
149
171
  {
150
172
  id: i,
@@ -158,14 +180,18 @@ var Containers = React6.memo(function Containers2({
158
180
  )
159
181
  );
160
182
  }
161
- return /* @__PURE__ */ React6.createElement(
183
+ return /* @__PURE__ */ React7.createElement(
162
184
  $View,
163
185
  {
164
186
  $key: "totalSize",
165
- $style: () => horizontal ? {
166
- width: peek$(ctx, "totalSize")
167
- } : {
168
- height: peek$(ctx, "totalSize")
187
+ $key2: "scrollAdjust",
188
+ $style: () => {
189
+ const size = peek$(ctx, "totalSize") + peek$(ctx, "scrollAdjust");
190
+ return horizontal ? {
191
+ width: size
192
+ } : {
193
+ height: size
194
+ };
169
195
  }
170
196
  },
171
197
  containers
@@ -174,15 +200,15 @@ var Containers = React6.memo(function Containers2({
174
200
 
175
201
  // src/ListComponent.tsx
176
202
  var getComponent = (Component) => {
177
- if (React6.isValidElement(Component)) {
203
+ if (React7.isValidElement(Component)) {
178
204
  return Component;
179
205
  }
180
206
  if (Component) {
181
- return /* @__PURE__ */ React6.createElement(Component, null);
207
+ return /* @__PURE__ */ React7.createElement(Component, null);
182
208
  }
183
209
  return null;
184
210
  };
185
- var ListComponent = React6.memo(function ListComponent2({
211
+ var ListComponent = React7.memo(function ListComponent2({
186
212
  style,
187
213
  contentContainerStyle,
188
214
  horizontal,
@@ -205,8 +231,8 @@ var ListComponent = React6.memo(function ListComponent2({
205
231
  ...rest
206
232
  }) {
207
233
  const ctx = useStateContext();
208
- return /* @__PURE__ */ React6.createElement(
209
- ScrollView,
234
+ return /* @__PURE__ */ React7.createElement(
235
+ $ScrollView,
210
236
  {
211
237
  ...rest,
212
238
  style,
@@ -218,29 +244,35 @@ var ListComponent = React6.memo(function ListComponent2({
218
244
  ],
219
245
  onScroll: handleScroll,
220
246
  onLayout,
221
- scrollEventThrottle: 32,
222
247
  horizontal,
223
248
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
224
249
  ref: refScroller
225
250
  },
226
- alignItemsAtEnd && /* @__PURE__ */ React6.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
227
- ListHeaderComponent && /* @__PURE__ */ React6.createElement(
228
- View,
251
+ alignItemsAtEnd && /* @__PURE__ */ React7.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
252
+ ListHeaderComponent && /* @__PURE__ */ React7.createElement(
253
+ $View,
229
254
  {
230
- style: ListHeaderComponentStyle,
255
+ $key: "scrollAdjust",
256
+ $style: () => StyleSheet.compose(ListHeaderComponentStyle, { top: peek$(ctx, "scrollAdjust") }),
231
257
  onLayout: (event) => {
232
258
  const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
233
259
  const prevSize = peek$(ctx, "headerSize") || 0;
234
260
  if (size !== prevSize) {
235
261
  set$(ctx, "headerSize", size);
236
- addTotalSize(size - prevSize);
237
262
  }
238
263
  }
239
264
  },
240
265
  getComponent(ListHeaderComponent)
241
266
  ),
242
- ListEmptyComponent && /* @__PURE__ */ React6.createElement(View, { style: ListEmptyComponentStyle }, getComponent(ListEmptyComponent)),
243
- /* @__PURE__ */ React6.createElement(
267
+ ListEmptyComponent && /* @__PURE__ */ React7.createElement(
268
+ $View,
269
+ {
270
+ $key: "scrollAdjust",
271
+ $style: () => StyleSheet.compose(ListEmptyComponentStyle, { top: peek$(ctx, "scrollAdjust") })
272
+ },
273
+ getComponent(ListEmptyComponent)
274
+ ),
275
+ /* @__PURE__ */ React7.createElement(
244
276
  Containers,
245
277
  {
246
278
  horizontal,
@@ -250,7 +282,7 @@ var ListComponent = React6.memo(function ListComponent2({
250
282
  updateItemSize
251
283
  }
252
284
  ),
253
- ListFooterComponent && /* @__PURE__ */ React6.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
285
+ ListFooterComponent && /* @__PURE__ */ React7.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
254
286
  );
255
287
  });
256
288
  var symbolFirst = Symbol();
@@ -266,14 +298,17 @@ function useInit(cb) {
266
298
  var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
267
299
  function setupViewability(props) {
268
300
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
269
- viewabilityConfigCallbackPairs = viewabilityConfigCallbackPairs || [
270
- {
271
- viewabilityConfig: viewabilityConfig || {
272
- viewAreaCoveragePercentThreshold: 0
273
- },
274
- onViewableItemsChanged
275
- }
276
- ];
301
+ if (viewabilityConfig || onViewableItemsChanged) {
302
+ viewabilityConfigCallbackPairs = [
303
+ ...viewabilityConfigCallbackPairs || [],
304
+ {
305
+ viewabilityConfig: viewabilityConfig || {
306
+ viewAreaCoveragePercentThreshold: 0
307
+ },
308
+ onViewableItemsChanged
309
+ }
310
+ ];
311
+ }
277
312
  if (viewabilityConfigCallbackPairs) {
278
313
  for (const pair of viewabilityConfigCallbackPairs) {
279
314
  mapViewabilityConfigCallbackPairs.set(pair.viewabilityConfig.id, {
@@ -369,7 +404,7 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
369
404
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
370
405
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
371
406
  const isViewable2 = percent >= viewablePercentThreshold;
372
- const containerId = findContainerId(state, ctx, index);
407
+ const containerId = findContainerId(ctx, key);
373
408
  const value = {
374
409
  index,
375
410
  isViewable: isViewable2,
@@ -389,11 +424,11 @@ function isViewable(state, ctx, viewabilityConfig, key, scrollSize, item, index)
389
424
  }
390
425
  return isViewable2;
391
426
  }
392
- function findContainerId(state, ctx, index) {
427
+ function findContainerId(ctx, key) {
393
428
  const numContainers = peek$(ctx, "numContainers");
394
429
  for (let i = 0; i < numContainers; i++) {
395
- const itemIndex = peek$(ctx, `containerIndex${i}`);
396
- if (itemIndex === index) {
430
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
431
+ if (itemKey === key) {
397
432
  return i;
398
433
  }
399
434
  }
@@ -407,47 +442,43 @@ function maybeUpdateViewabilityCallback(ctx, configId, viewToken) {
407
442
  }
408
443
 
409
444
  // src/LegendList.tsx
410
- var DEFAULT_SCROLL_BUFFER = 0;
411
- var POSITION_OUT_OF_VIEW = -1e4;
445
+ var DEFAULT_DRAW_DISTANCE = 250;
446
+ var INITIAL_SCROLL_ADJUST = 1e4;
447
+ var POSITION_OUT_OF_VIEW = -1e7;
412
448
  var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
413
- return /* @__PURE__ */ React6.createElement(StateProvider, null, /* @__PURE__ */ React6.createElement(LegendListInner, { ...props, ref: forwardedRef }));
449
+ return /* @__PURE__ */ React7.createElement(StateProvider, null, /* @__PURE__ */ React7.createElement(LegendListInner, { ...props, ref: forwardedRef }));
414
450
  });
415
451
  var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef) {
416
- var _a, _b;
452
+ var _a, _b, _c, _d, _e, _f;
417
453
  const {
418
454
  data,
419
455
  initialScrollIndex,
420
456
  initialScrollOffset,
421
457
  horizontal,
422
- style: styleProp,
423
- contentContainerStyle: contentContainerStyleProp,
424
458
  initialNumContainers,
425
459
  drawDistance = 250,
426
460
  recycleItems = false,
427
461
  onEndReachedThreshold = 0.5,
462
+ onStartReachedThreshold = 0.5,
428
463
  maintainScrollAtEnd = false,
429
464
  maintainScrollAtEndThreshold = 0.1,
430
465
  alignItemsAtEnd = false,
466
+ maintainVisibleContentPosition = false,
431
467
  onScroll: onScrollProp,
432
468
  keyExtractor,
433
469
  renderItem,
434
470
  estimatedItemSize,
435
471
  getEstimatedItemSize,
436
472
  onEndReached,
473
+ onStartReached,
437
474
  ListEmptyComponent,
438
475
  ...rest
439
476
  } = props;
477
+ const { style, contentContainerStyle } = rest;
440
478
  const ctx = useStateContext();
441
479
  const internalRef = useRef(null);
442
480
  const refScroller = internalRef;
443
- const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_SCROLL_BUFFER;
444
- const styleFlattened = StyleSheet.flatten(styleProp);
445
- const style = useMemo(() => styleFlattened, [JSON.stringify(styleFlattened)]);
446
- const contentContainerStyleFlattened = StyleSheet.flatten(contentContainerStyleProp);
447
- const contentContainerStyle = useMemo(
448
- () => contentContainerStyleFlattened,
449
- [JSON.stringify(contentContainerStyleProp)]
450
- );
481
+ const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE;
451
482
  const refState = useRef();
452
483
  const getId = (index) => {
453
484
  var _a2;
@@ -458,21 +489,26 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
458
489
  const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
459
490
  return `${ret}`;
460
491
  };
461
- const getItemSize = (index, data2) => {
462
- return getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
492
+ const getItemSize = (key, index, data2) => {
493
+ const sizeKnown = refState.current.sizes.get(key);
494
+ if (sizeKnown !== void 0) {
495
+ return sizeKnown;
496
+ }
497
+ const size = getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
498
+ refState.current.sizes.set(key, size);
499
+ return size;
463
500
  };
464
501
  const calculateInitialOffset = (index = initialScrollIndex) => {
465
502
  if (index) {
503
+ let offset = 0;
466
504
  if (getEstimatedItemSize) {
467
- let offset = 0;
468
505
  for (let i = 0; i < index; i++) {
469
506
  offset += getEstimatedItemSize(i, data[i]);
470
507
  }
471
- return offset;
472
- }
473
- if (estimatedItemSize) {
474
- return index * estimatedItemSize;
508
+ } else if (estimatedItemSize) {
509
+ offset = index * estimatedItemSize;
475
510
  }
511
+ return offset + (maintainVisibleContentPosition ? INITIAL_SCROLL_ADJUST : 0);
476
512
  }
477
513
  return void 0;
478
514
  };
@@ -482,12 +518,12 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
482
518
  sizes: /* @__PURE__ */ new Map(),
483
519
  positions: /* @__PURE__ */ new Map(),
484
520
  pendingAdjust: 0,
485
- animFrameScroll: null,
486
521
  animFrameLayout: null,
487
522
  animFrameTotalSize: null,
488
523
  isStartReached: false,
489
524
  isEndReached: false,
490
525
  isAtBottom: false,
526
+ isAtTop: false,
491
527
  data,
492
528
  idsInFirstRender: void 0,
493
529
  hasScrolled: false,
@@ -500,80 +536,355 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
500
536
  totalSize: 0,
501
537
  timeouts: /* @__PURE__ */ new Set(),
502
538
  viewabilityConfigCallbackPairs: void 0,
503
- renderItem: void 0
539
+ renderItem: void 0,
540
+ scrollAdjustPending: maintainVisibleContentPosition ? INITIAL_SCROLL_ADJUST : 0,
541
+ nativeMarginTop: 0,
542
+ scrollPrev: 0,
543
+ scrollPrevTime: 0,
544
+ scrollTime: 0,
545
+ indexByKey: /* @__PURE__ */ new Map(),
546
+ scrollHistory: [],
547
+ scrollVelocity: 0
504
548
  };
505
549
  refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
550
+ set$(ctx, "scrollAdjust", refState.current.scrollAdjustPending);
506
551
  }
507
- refState.current.data = data;
508
- refState.current.renderItem = renderItem;
509
- set$(ctx, "numItems", data.length);
510
- set$(ctx, "stylePaddingTop", (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
511
- const addTotalSize = useCallback((add) => {
512
- const prev = refState.current.totalSize;
513
- refState.current.totalSize += add;
514
- const totalSize = refState.current.totalSize;
552
+ const adjustScroll = (diff) => {
553
+ if (maintainVisibleContentPosition && refScroller.current) {
554
+ refState.current.scrollAdjustPending -= diff;
555
+ }
556
+ };
557
+ const addTotalSize = useCallback((key, add, set) => {
558
+ const state = refState.current;
559
+ const index = key === null ? 0 : state.indexByKey.get(key);
560
+ const isAbove = index < (state.startNoBuffer || 0);
561
+ const prev = state.totalSize;
562
+ if (set) {
563
+ state.totalSize = add;
564
+ } else {
565
+ state.totalSize += add;
566
+ }
515
567
  const doAdd = () => {
516
- refState.current.animFrameTotalSize = null;
568
+ const totalSize = state.totalSize;
569
+ state.animFrameTotalSize = null;
517
570
  set$(ctx, "totalSize", totalSize);
518
- const screenLength = refState.current.scrollLength;
519
571
  if (alignItemsAtEnd) {
520
- const listPaddingTop = peek$(ctx, "stylePaddingTop");
521
- set$(ctx, "paddingTop", Math.max(0, screenLength - totalSize - listPaddingTop));
572
+ doUpdatePaddingTop();
522
573
  }
523
574
  };
524
- if (!prev) {
575
+ if (isAbove) {
576
+ adjustScroll(add);
577
+ }
578
+ if (!prev || set) {
525
579
  doAdd();
526
- } else if (!refState.current.animFrameTotalSize) {
527
- refState.current.animFrameTotalSize = requestAnimationFrame(doAdd);
580
+ } else if (!state.animFrameTotalSize) {
581
+ state.animFrameTotalSize = requestAnimationFrame(doAdd);
528
582
  }
529
583
  }, []);
530
- const getRenderedItem = useCallback((index, containerIndex) => {
531
- var _a2, _b2, _c;
532
- const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
584
+ const calculateItemsInView = useCallback((speed = 0) => {
585
+ var _a2, _b2, _c2;
586
+ const state = refState.current;
587
+ const { data: data2, scrollLength, scroll: scrollState, startBuffered: startBufferedState, positions } = state;
588
+ if (state.animFrameLayout) {
589
+ cancelAnimationFrame(state.animFrameLayout);
590
+ state.animFrameLayout = null;
591
+ }
533
592
  if (!data2) {
593
+ return;
594
+ }
595
+ const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
596
+ const scrollAdjustPending = (_a2 = state.scrollAdjustPending) != null ? _a2 : 0;
597
+ const scrollExtra = Math.max(-16, Math.min(16, speed)) * 32;
598
+ const scroll = Math.max(
599
+ 0,
600
+ scrollState - topPad - (USE_CONTENT_INSET ? scrollAdjustPending : 0) + scrollExtra
601
+ );
602
+ let startNoBuffer = null;
603
+ let startBuffered = null;
604
+ let endNoBuffer = null;
605
+ let endBuffered = null;
606
+ let loopStart = startBufferedState || 0;
607
+ if (startBufferedState) {
608
+ for (let i = startBufferedState; i >= 0; i--) {
609
+ const id = getId(i);
610
+ const top2 = positions.get(id);
611
+ if (top2 !== void 0) {
612
+ const size = getItemSize(id, i, data2[i]);
613
+ const bottom = top2 + size;
614
+ if (bottom > scroll - scrollBuffer) {
615
+ loopStart = i;
616
+ } else {
617
+ break;
618
+ }
619
+ }
620
+ }
621
+ }
622
+ let top = loopStart > 0 ? positions.get(getId(loopStart)) : 0;
623
+ for (let i = loopStart; i < data2.length; i++) {
624
+ const id = getId(i);
625
+ const size = getItemSize(id, i, data2[i]);
626
+ if (positions.get(id) !== top) {
627
+ positions.set(id, top);
628
+ }
629
+ if (startNoBuffer === null && top + size > scroll) {
630
+ startNoBuffer = i;
631
+ }
632
+ if (startBuffered === null && top + size > scroll - scrollBuffer) {
633
+ startBuffered = i;
634
+ }
635
+ if (startNoBuffer !== null) {
636
+ if (top <= scroll + scrollLength) {
637
+ endNoBuffer = i;
638
+ }
639
+ if (top <= scroll + scrollLength + scrollBuffer) {
640
+ endBuffered = i;
641
+ } else {
642
+ break;
643
+ }
644
+ }
645
+ top += size;
646
+ }
647
+ Object.assign(refState.current, {
648
+ startBuffered,
649
+ startNoBuffer,
650
+ endBuffered,
651
+ endNoBuffer
652
+ });
653
+ if (startBuffered !== null && endBuffered !== null) {
654
+ const prevNumContainers = ctx.values.get("numContainers");
655
+ let numContainers = prevNumContainers;
656
+ for (let i = startBuffered; i <= endBuffered; i++) {
657
+ let isContained = false;
658
+ const id = getId(i);
659
+ for (let j = 0; j < numContainers; j++) {
660
+ const key = peek$(ctx, `containerItemKey${j}`);
661
+ if (key === id) {
662
+ isContained = true;
663
+ break;
664
+ }
665
+ }
666
+ if (!isContained) {
667
+ const top2 = (positions.get(id) || 0) + scrollAdjustPending;
668
+ let furthestIndex = -1;
669
+ let furthestDistance = 0;
670
+ for (let u = 0; u < numContainers; u++) {
671
+ const key = peek$(ctx, `containerItemKey${u}`);
672
+ if (key === void 0) {
673
+ furthestIndex = u;
674
+ break;
675
+ }
676
+ const index = (_b2 = refState.current) == null ? void 0 : _b2.indexByKey.get(key);
677
+ const pos = peek$(ctx, `containerPosition${u}`);
678
+ if (index < startBuffered || index > endBuffered) {
679
+ const distance = Math.abs(pos - top2);
680
+ if (index < 0 || distance > furthestDistance) {
681
+ furthestDistance = distance;
682
+ furthestIndex = u;
683
+ }
684
+ }
685
+ }
686
+ if (furthestIndex >= 0) {
687
+ set$(ctx, `containerItemKey${furthestIndex}`, id);
688
+ } else {
689
+ const containerId = numContainers;
690
+ numContainers++;
691
+ set$(ctx, `containerItemKey${containerId}`, id);
692
+ set$(ctx, `containerPosition${containerId}`, POSITION_OUT_OF_VIEW);
693
+ if (__DEV__ && numContainers > peek$(ctx, "numContainersPooled")) {
694
+ console.warn(
695
+ "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemSize. numContainers:",
696
+ numContainers
697
+ );
698
+ }
699
+ }
700
+ }
701
+ }
702
+ if (numContainers !== prevNumContainers) {
703
+ set$(ctx, "numContainers", numContainers);
704
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
705
+ set$(ctx, "numContainersPooled", numContainers);
706
+ }
707
+ }
708
+ for (let i = 0; i < numContainers; i++) {
709
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
710
+ const itemIndex = (_c2 = refState.current) == null ? void 0 : _c2.indexByKey.get(itemKey);
711
+ const item = data2[itemIndex];
712
+ if (item) {
713
+ const id = getId(itemIndex);
714
+ if (!(itemKey !== id || itemIndex < startBuffered || itemIndex > endBuffered)) {
715
+ const pos = (positions.get(id) || 0) + scrollAdjustPending;
716
+ const prevPos = peek$(ctx, `containerPosition${i}`);
717
+ if (pos >= 0 && pos !== prevPos) {
718
+ set$(ctx, `containerPosition${i}`, pos);
719
+ }
720
+ }
721
+ }
722
+ }
723
+ }
724
+ if (refState.current.viewabilityConfigCallbackPairs) {
725
+ updateViewableItems(
726
+ refState.current,
727
+ ctx,
728
+ refState.current.viewabilityConfigCallbackPairs,
729
+ getId,
730
+ scrollLength,
731
+ startNoBuffer,
732
+ endNoBuffer
733
+ );
734
+ }
735
+ }, []);
736
+ const doUpdatePaddingTop = () => {
737
+ if (alignItemsAtEnd) {
738
+ const { scrollLength, totalSize } = refState.current;
739
+ const listPaddingTop = peek$(ctx, "stylePaddingTop") || 0;
740
+ const paddingTop = Math.max(0, Math.floor(scrollLength - totalSize - listPaddingTop));
741
+ set$(ctx, "paddingTop", paddingTop);
742
+ }
743
+ };
744
+ const doMaintainScrollAtEnd = (animated) => {
745
+ var _a2;
746
+ if (((_a2 = refState.current) == null ? void 0 : _a2.isAtBottom) && maintainScrollAtEnd) {
747
+ refState.current.scroll = refState.current.totalSize - refState.current.scrollLength;
748
+ requestAnimationFrame(() => {
749
+ var _a3;
750
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
751
+ animated
752
+ });
753
+ });
754
+ }
755
+ };
756
+ const checkAtBottom = () => {
757
+ var _a2;
758
+ const { scrollLength, scroll } = refState.current;
759
+ const totalSize = peek$(ctx, "totalSize");
760
+ const distanceFromEnd = totalSize - scroll - scrollLength;
761
+ if (refState.current) {
762
+ refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
763
+ }
764
+ if (onEndReached && !((_a2 = refState.current) == null ? void 0 : _a2.isEndReached)) {
765
+ if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
766
+ if (refState.current) {
767
+ refState.current.isEndReached = true;
768
+ }
769
+ onEndReached({ distanceFromEnd });
770
+ }
771
+ }
772
+ };
773
+ const checkAtTop = () => {
774
+ var _a2;
775
+ const { scrollLength, scroll } = refState.current;
776
+ if (refState.current) {
777
+ refState.current.isAtTop = scroll === 0;
778
+ }
779
+ if (onStartReached && !((_a2 = refState.current) == null ? void 0 : _a2.isStartReached)) {
780
+ if (scroll < onStartReachedThreshold * scrollLength) {
781
+ if (refState.current) {
782
+ refState.current.isStartReached = true;
783
+ }
784
+ onStartReached({ distanceFromStart: scroll });
785
+ }
786
+ }
787
+ };
788
+ const isFirst = !refState.current.renderItem;
789
+ if (isFirst || data !== refState.current.data) {
790
+ (data == null ? void 0 : data.length) > ((_a = refState.current.data) == null ? void 0 : _a.length);
791
+ refState.current.data = data;
792
+ let totalSize = 0;
793
+ const indexByKey = /* @__PURE__ */ new Map();
794
+ for (let i = 0; i < data.length; i++) {
795
+ const key = getId(i);
796
+ indexByKey.set(key, i);
797
+ totalSize += getItemSize(key, i, data[i]);
798
+ if (maintainVisibleContentPosition && i < refState.current.startNoBuffer && !refState.current.indexByKey.has(key)) {
799
+ const size = getItemSize(key, i, data[i]);
800
+ adjustScroll(size);
801
+ }
802
+ }
803
+ addTotalSize(null, totalSize, true);
804
+ if (maintainVisibleContentPosition) {
805
+ for (const [key, index] of refState.current.indexByKey) {
806
+ if (index < refState.current.startNoBuffer && !indexByKey.has(key)) {
807
+ const size = (_b = refState.current.sizes.get(key)) != null ? _b : 0;
808
+ if (size) {
809
+ adjustScroll(-size);
810
+ }
811
+ }
812
+ }
813
+ }
814
+ refState.current.indexByKey = indexByKey;
815
+ if (!isFirst) {
816
+ refState.current.isEndReached = false;
817
+ const numContainers = peek$(ctx, "numContainers");
818
+ for (let i = 0; i < numContainers; i++) {
819
+ set$(ctx, `containerItemKey${i}`, void 0);
820
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
821
+ }
822
+ calculateItemsInView();
823
+ doMaintainScrollAtEnd(false);
824
+ }
825
+ checkAtBottom();
826
+ checkAtTop();
827
+ }
828
+ refState.current.renderItem = renderItem;
829
+ set$(ctx, "lastItemKey", getId(data[data.length - 1]));
830
+ set$(
831
+ ctx,
832
+ "stylePaddingTop",
833
+ (_f = (_e = (_c = StyleSheet.flatten(style)) == null ? void 0 : _c.paddingTop) != null ? _e : (_d = StyleSheet.flatten(contentContainerStyle)) == null ? void 0 : _d.paddingTop) != null ? _f : 0
834
+ );
835
+ const getRenderedItem = useCallback((key, containerId) => {
836
+ var _a2, _b2;
837
+ const state = refState.current;
838
+ if (!state) {
839
+ return null;
840
+ }
841
+ const { data: data2, indexByKey } = state;
842
+ const index = indexByKey.get(key);
843
+ if (index === void 0) {
534
844
  return null;
535
845
  }
536
846
  const useViewability = (configId, callback) => {
537
- const key = containerIndex + configId;
847
+ const key2 = containerId + configId;
538
848
  useInit(() => {
539
- const value = ctx.mapViewabilityValues.get(key);
849
+ const value = ctx.mapViewabilityValues.get(key2);
540
850
  if (value) {
541
851
  callback(value);
542
852
  }
543
853
  });
544
- ctx.mapViewabilityCallbacks.set(key, callback);
854
+ ctx.mapViewabilityCallbacks.set(key2, callback);
545
855
  useEffect(
546
856
  () => () => {
547
- ctx.mapViewabilityCallbacks.delete(key);
857
+ ctx.mapViewabilityCallbacks.delete(key2);
548
858
  },
549
859
  []
550
860
  );
551
861
  };
552
862
  const useViewabilityAmount = (callback) => {
553
863
  useInit(() => {
554
- const value = ctx.mapViewabilityAmountValues.get(containerIndex);
864
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
555
865
  if (value) {
556
866
  callback(value);
557
867
  }
558
868
  });
559
- ctx.mapViewabilityAmountCallbacks.set(containerIndex, callback);
869
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
560
870
  useEffect(
561
871
  () => () => {
562
- ctx.mapViewabilityAmountCallbacks.delete(containerIndex);
872
+ ctx.mapViewabilityAmountCallbacks.delete(containerId);
563
873
  },
564
874
  []
565
875
  );
566
876
  };
567
877
  const useRecyclingEffect = (effect) => {
568
878
  useEffect(() => {
569
- const state = refState.current;
879
+ const state2 = refState.current;
570
880
  let prevIndex = index;
571
- let prevItem = state.data[index];
572
- const signal = `containerIndex${containerIndex}`;
573
- listen$(ctx, signal, () => {
574
- const data3 = state.data;
881
+ let prevItem = state2.data[index];
882
+ const signal = `containerItemKey${containerId}`;
883
+ const run = () => {
884
+ const data3 = state2.data;
575
885
  if (data3) {
576
- const newIndex = peek$(ctx, signal);
886
+ const newKey = peek$(ctx, signal);
887
+ const newIndex = state2.indexByKey.get(newKey);
577
888
  const newItem = data3[newIndex];
578
889
  if (newItem) {
579
890
  effect({
@@ -586,7 +897,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
586
897
  prevIndex = newIndex;
587
898
  prevItem = newItem;
588
899
  }
589
- });
900
+ };
901
+ run();
902
+ listen$(ctx, signal, run);
590
903
  }, []);
591
904
  };
592
905
  const useRecyclingState = (updateState) => {
@@ -598,13 +911,13 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
598
911
  prevItem: void 0
599
912
  })
600
913
  );
601
- useRecyclingEffect((state) => {
602
- const newState = updateState(state);
914
+ useRecyclingEffect((state2) => {
915
+ const newState = updateState(state2);
603
916
  stateInfo[1](newState);
604
917
  });
605
918
  return stateInfo;
606
919
  };
607
- const renderedItem = (_c = (_b2 = refState.current).renderItem) == null ? void 0 : _c.call(_b2, {
920
+ const renderedItem = (_b2 = (_a2 = refState.current).renderItem) == null ? void 0 : _b2.call(_a2, {
608
921
  item: data2[index],
609
922
  index,
610
923
  useViewability,
@@ -614,262 +927,98 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
614
927
  });
615
928
  return renderedItem;
616
929
  }, []);
617
- const calculateItemsInView = useCallback(() => {
618
- unstable_batchedUpdates(() => {
619
- var _a2, _b2, _c;
620
- const {
621
- data: data2,
622
- scrollLength,
623
- scroll: scrollState,
624
- startNoBuffer: startNoBufferState,
625
- startBuffered: startBufferedState,
626
- endNoBuffer: endNoBufferState,
627
- endBuffered: endBufferedState
628
- } = refState.current;
629
- if (!data2) {
630
- return;
631
- }
632
- const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
633
- const scroll = scrollState - topPad;
634
- const { sizes, positions } = refState.current;
635
- let startNoBuffer = null;
636
- let startBuffered = null;
637
- let endNoBuffer = null;
638
- let endBuffered = null;
639
- let loopStart = startBufferedState || 0;
640
- if (startBufferedState) {
641
- for (let i = startBufferedState; i >= 0; i--) {
642
- const id = getId(i);
643
- const top2 = positions.get(id);
644
- if (top2 !== void 0) {
645
- const size = (_a2 = sizes.get(id)) != null ? _a2 : getItemSize(i, data2[i]);
646
- const bottom = top2 + size;
647
- if (bottom > scroll - scrollBuffer) {
648
- loopStart = i;
649
- } else {
650
- break;
651
- }
652
- }
653
- }
654
- }
655
- let top = loopStart > 0 ? positions.get(getId(loopStart)) : 0;
656
- for (let i = loopStart; i < data2.length; i++) {
657
- const id = getId(i);
658
- const size = (_b2 = sizes.get(id)) != null ? _b2 : getItemSize(i, data2[i]);
659
- if (positions.get(id) !== top) {
660
- positions.set(id, top);
661
- }
662
- if (startNoBuffer === null && top + size > scroll) {
663
- startNoBuffer = i;
664
- }
665
- if (startBuffered === null && top + size > scroll - scrollBuffer) {
666
- startBuffered = i;
667
- }
668
- if (startNoBuffer !== null) {
669
- if (top <= scroll + scrollLength) {
670
- endNoBuffer = i;
671
- }
672
- if (top <= scroll + scrollLength + scrollBuffer) {
673
- endBuffered = i;
674
- } else {
675
- break;
676
- }
677
- }
678
- top += size;
679
- }
680
- Object.assign(refState.current, {
681
- startBuffered,
682
- startNoBuffer,
683
- endBuffered,
684
- endNoBuffer
685
- });
686
- if (startBuffered !== null && endBuffered !== null) {
687
- const prevNumContainers = ctx.values.get("numContainers");
688
- let numContainers = prevNumContainers;
689
- for (let i = startBuffered; i <= endBuffered; i++) {
690
- let isContained = false;
691
- for (let j = 0; j < numContainers; j++) {
692
- const index = peek$(ctx, `containerIndex${j}`);
693
- if (index === i) {
694
- isContained = true;
695
- break;
696
- }
697
- }
698
- if (!isContained) {
699
- const id = getId(i);
700
- const top2 = positions.get(id) || 0;
701
- let furthestIndex = -1;
702
- let furthestDistance = 0;
703
- for (let u = 0; u < numContainers; u++) {
704
- const index = peek$(ctx, `containerIndex${u}`);
705
- if (index < 0) {
706
- furthestIndex = u;
707
- break;
708
- }
709
- const pos = peek$(ctx, `containerPosition${u}`);
710
- if (index < startBuffered || index > endBuffered) {
711
- const distance = Math.abs(pos - top2);
712
- if (index < 0 || distance > furthestDistance) {
713
- furthestDistance = distance;
714
- furthestIndex = u;
715
- }
716
- }
717
- }
718
- if (furthestIndex >= 0) {
719
- set$(ctx, `containerIndex${furthestIndex}`, i);
720
- } else {
721
- if (__DEV__) {
722
- console.warn(
723
- "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemSize",
724
- i
725
- );
726
- }
727
- const containerId = numContainers;
728
- numContainers++;
729
- set$(ctx, `containerIndex${containerId}`, i);
730
- set$(ctx, `containerPosition${containerId}`, POSITION_OUT_OF_VIEW);
731
- }
732
- }
733
- }
734
- if (numContainers !== prevNumContainers) {
735
- set$(ctx, "numContainers", numContainers);
736
- }
737
- for (let i = 0; i < numContainers; i++) {
738
- const itemIndex = peek$(ctx, `containerIndex${i}`);
739
- const item = data2[itemIndex];
740
- if (item) {
741
- const id = getId(itemIndex);
742
- if (itemIndex < startBuffered || itemIndex > endBuffered) {
743
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
744
- } else {
745
- const pos = (_c = positions.get(id)) != null ? _c : -1;
746
- const prevPos = peek$(ctx, `containerPosition${i}`);
747
- if (pos >= 0 && pos !== prevPos) {
748
- set$(ctx, `containerPosition${i}`, pos);
749
- }
750
- }
751
- }
752
- }
753
- }
754
- if (refState.current.viewabilityConfigCallbackPairs) {
755
- updateViewableItems(
756
- refState.current,
757
- ctx,
758
- refState.current.viewabilityConfigCallbackPairs,
759
- getId,
760
- scrollLength,
761
- startNoBuffer,
762
- endNoBuffer
763
- );
764
- }
765
- });
766
- }, []);
767
930
  useInit(() => {
768
- var _a2, _b2;
769
931
  refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
770
932
  const scrollLength = refState.current.scrollLength;
771
933
  const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
772
- const numContainers = initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) + 4;
934
+ const numContainers = initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize);
773
935
  for (let i = 0; i < numContainers; i++) {
774
- set$(ctx, `containerIndex${i}`, -1);
775
936
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
776
937
  }
777
938
  set$(ctx, "numContainers", numContainers);
939
+ set$(ctx, "numContainersPooled", numContainers * 2);
778
940
  calculateItemsInView();
779
- const sizes = (_a2 = refState.current) == null ? void 0 : _a2.sizes;
780
- let totalSize = 0;
781
- for (let i = 0; i < data.length; i++) {
782
- const id = getId(i);
783
- totalSize += (_b2 = sizes.get(id)) != null ? _b2 : getItemSize(i, data[i]);
784
- }
785
- addTotalSize(totalSize);
786
941
  });
787
- const checkAtBottom = () => {
942
+ const updateItemSize = useCallback((key, size) => {
788
943
  var _a2;
789
- const { scrollLength, scroll } = refState.current;
790
- const totalSize = peek$(ctx, "totalSize");
791
- const distanceFromEnd = totalSize - scroll - scrollLength;
792
- if (refState.current) {
793
- refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
794
- }
795
- if (onEndReached && !((_a2 = refState.current) == null ? void 0 : _a2.isEndReached)) {
796
- if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
797
- if (refState.current) {
798
- refState.current.isEndReached = true;
799
- }
800
- onEndReached({ distanceFromEnd });
801
- }
802
- }
803
- };
804
- useEffect(() => {
805
- if (refState.current) {
806
- refState.current.isEndReached = false;
807
- }
808
- const numContainers = peek$(ctx, "numContainers");
809
- if (data.length < numContainers) {
810
- for (let i = 0; i < numContainers; i++) {
811
- const itemIndex = peek$(ctx, `containerIndex${i}`);
812
- if (itemIndex >= data.length) {
813
- set$(ctx, `containerIndex${i}`, -1);
814
- }
815
- }
816
- }
817
- calculateItemsInView();
818
- checkAtBottom();
819
- }, [data]);
820
- const updateItemSize = useCallback((index, size) => {
821
- var _a2, _b2, _c, _d;
822
944
  const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
823
945
  if (!data2) {
824
946
  return;
825
947
  }
826
- const sizes = (_b2 = refState.current) == null ? void 0 : _b2.sizes;
827
- const id = getId(index);
828
- const wasInFirstRender = (_c = refState.current) == null ? void 0 : _c.idsInFirstRender.has(id);
829
- const prevSize = sizes.get(id) || (wasInFirstRender ? getItemSize(index, data2[index]) : 0);
948
+ const { sizes, indexByKey, idsInFirstRender } = refState.current;
949
+ const index = indexByKey.get(key);
950
+ const wasInFirstRender = idsInFirstRender.has(key);
951
+ const prevSize = sizes.get(key) || (wasInFirstRender ? getItemSize(key, index, data2[index]) : 0);
830
952
  if (!prevSize || Math.abs(prevSize - size) > 0.5) {
831
- sizes.set(id, size);
832
- addTotalSize(size - prevSize);
833
- if (((_d = refState.current) == null ? void 0 : _d.isAtBottom) && maintainScrollAtEnd) {
834
- requestAnimationFrame(() => {
835
- var _a3;
836
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
837
- animated: true
838
- });
839
- });
840
- }
953
+ sizes.set(key, size);
954
+ addTotalSize(key, size - prevSize);
955
+ doMaintainScrollAtEnd(true);
841
956
  const state = refState.current;
842
- if (!state.animFrameScroll && !state.animFrameLayout) {
957
+ const scrollVelocity = state.scrollVelocity;
958
+ if (!state.animFrameLayout && (Number.isNaN(scrollVelocity) || Math.abs(scrollVelocity) < 1)) {
843
959
  state.animFrameLayout = requestAnimationFrame(() => {
844
960
  state.animFrameLayout = null;
845
- calculateItemsInView();
961
+ calculateItemsInView(state.scrollVelocity);
846
962
  });
847
963
  }
848
964
  }
849
965
  }, []);
850
- const handleScrollDebounced = useCallback(() => {
851
- calculateItemsInView();
966
+ const handleScrollDebounced = useCallback((velocity) => {
967
+ var _a2, _b2;
968
+ const scrollAdjustPending = (_b2 = (_a2 = refState.current) == null ? void 0 : _a2.scrollAdjustPending) != null ? _b2 : 0;
969
+ set$(ctx, "scrollAdjust", scrollAdjustPending);
970
+ calculateItemsInView(velocity);
852
971
  checkAtBottom();
853
- if (refState.current) {
854
- refState.current.animFrameScroll = null;
855
- }
972
+ checkAtTop();
856
973
  }, []);
857
974
  const onLayout = useCallback((event) => {
858
- const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
975
+ let scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
976
+ if (!USE_CONTENT_INSET) {
977
+ scrollLength += event.nativeEvent.layout[horizontal ? "x" : "y"];
978
+ }
859
979
  refState.current.scrollLength = scrollLength;
980
+ doMaintainScrollAtEnd(false);
981
+ doUpdatePaddingTop();
982
+ checkAtBottom();
983
+ checkAtTop();
984
+ if (__DEV__) {
985
+ const isWidthZero = event.nativeEvent.layout.width === 0;
986
+ const isHeightZero = event.nativeEvent.layout.height === 0;
987
+ if (isWidthZero || isHeightZero) {
988
+ console.warn(
989
+ `[legend-list] List ${isWidthZero ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
990
+ );
991
+ }
992
+ }
860
993
  }, []);
861
994
  const handleScroll = useCallback(
862
995
  (event, fromSelf) => {
863
- var _a2, _b2, _c;
864
- if (((_b2 = (_a2 = event.nativeEvent) == null ? void 0 : _a2.contentSize) == null ? void 0 : _b2.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
996
+ var _a2, _b2, _c2;
997
+ if (((_b2 = (_a2 = event.nativeEvent) == null ? void 0 : _a2.contentSize) == null ? void 0 : _b2.height) === 0 && ((_c2 = event.nativeEvent.contentSize) == null ? void 0 : _c2.width) === 0) {
865
998
  return;
866
999
  }
867
- refState.current.hasScrolled = true;
1000
+ const state = refState.current;
1001
+ state.hasScrolled = true;
1002
+ const currentTime = performance.now();
868
1003
  const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
869
- refState.current.scroll = newScroll;
870
- if (refState.current && !refState.current.animFrameScroll) {
871
- refState.current.animFrameScroll = requestAnimationFrame(handleScrollDebounced);
1004
+ state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1005
+ if (state.scrollHistory.length > 5) {
1006
+ state.scrollHistory.shift();
1007
+ }
1008
+ let velocity = 0;
1009
+ if (state.scrollHistory.length >= 2) {
1010
+ const newest = state.scrollHistory[state.scrollHistory.length - 1];
1011
+ const oldest = state.scrollHistory[0];
1012
+ const scrollDiff = newest.scroll - oldest.scroll;
1013
+ const timeDiff = newest.time - oldest.time;
1014
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
872
1015
  }
1016
+ state.scrollPrev = state.scroll;
1017
+ state.scrollPrevTime = state.scrollTime;
1018
+ state.scroll = newScroll;
1019
+ state.scrollTime = currentTime;
1020
+ state.scrollVelocity = velocity;
1021
+ handleScrollDebounced(velocity);
873
1022
  if (!fromSelf) {
874
1023
  onScrollProp == null ? void 0 : onScrollProp(event);
875
1024
  }
@@ -905,12 +1054,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
905
1054
  },
906
1055
  []
907
1056
  );
908
- return /* @__PURE__ */ React6.createElement(
1057
+ return /* @__PURE__ */ React7.createElement(
909
1058
  ListComponent,
910
1059
  {
911
1060
  ...rest,
912
- contentContainerStyle,
913
- style,
914
1061
  horizontal,
915
1062
  refScroller,
916
1063
  initialContentOffset,