@legendapp/list 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -24,10 +24,10 @@ function _interopNamespace(e) {
24
24
  var React6__namespace = /*#__PURE__*/_interopNamespace(React6);
25
25
 
26
26
  // src/LegendList.tsx
27
-
28
- // src/constants.ts
29
- var DEFAULT_SCROLL_BUFFER = 0;
30
- var POSITION_OUT_OF_VIEW = -1e4;
27
+ var LeanView = React6__namespace.forwardRef((props, ref) => {
28
+ return React6__namespace.createElement("RCTView", { ...props, ref });
29
+ });
30
+ LeanView.displayName = "RCTView";
31
31
  var ContextListener = React6__namespace.createContext(null);
32
32
  function StateProvider({ children }) {
33
33
  const [value] = React6__namespace.useState(() => ({
@@ -57,309 +57,6 @@ function set$(ctx, signalName, value) {
57
57
  (_a = listeners.get(signalName)) == null ? void 0 : _a();
58
58
  }
59
59
  }
60
- function applyDefaultProps(props) {
61
- return {
62
- ...props,
63
- recycleItems: props.recycleItems || false,
64
- onEndReachedThreshold: props.onEndReachedThreshold || 0.5,
65
- maintainScrollAtEndThreshold: props.maintainScrollAtEndThreshold || 0.1,
66
- maintainScrollAtEnd: props.maintainScrollAtEnd || false,
67
- alignItemsAtEnd: props.alignItemsAtEnd || false
68
- };
69
- }
70
- function allocateContainers(state) {
71
- var _a, _b;
72
- const { scrollLength, props, ctx, scrollBuffer } = state;
73
- const averageItemSize = (_b = props.estimatedItemSize) != null ? _b : (_a = props.getEstimatedItemSize) == null ? void 0 : _a.call(props, 0, props.data[0]);
74
- const numContainers = props.initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) + 4;
75
- for (let i = 0; i < numContainers; i++) {
76
- set$(ctx, `containerIndex${i}`, -1);
77
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
78
- }
79
- set$(ctx, `numContainers`, numContainers);
80
- }
81
- function getId(state, index) {
82
- const { data, keyExtractor } = state.props;
83
- if (!data) {
84
- return "";
85
- }
86
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
87
- return ret + "";
88
- }
89
- function calculateInitialOffset(props) {
90
- const { data, initialScrollIndex, estimatedItemSize, getEstimatedItemSize } = props;
91
- if (initialScrollIndex) {
92
- if (getEstimatedItemSize) {
93
- let offset = 0;
94
- for (let i = 0; i < initialScrollIndex; i++) {
95
- offset += getEstimatedItemSize(i, data[i]);
96
- }
97
- return offset;
98
- } else if (estimatedItemSize) {
99
- return initialScrollIndex * estimatedItemSize;
100
- }
101
- }
102
- return void 0;
103
- }
104
- function addTotalLength(state, add) {
105
- const { ctx, props } = state;
106
- const totalLength = (peek$(ctx, `totalLength`) || 0) + add;
107
- set$(ctx, `totalLength`, totalLength);
108
- const screenLength = state.scrollLength;
109
- if (props.alignItemsAtEnd) {
110
- const listPaddingTop = peek$(ctx, `stylePaddingTop`);
111
- set$(ctx, `paddingTop`, Math.max(0, screenLength - totalLength - listPaddingTop));
112
- }
113
- }
114
- function getRenderedItem(state, index) {
115
- const { data, renderItem } = state.props;
116
- if (!data) {
117
- return null;
118
- }
119
- const renderedItem = renderItem == null ? void 0 : renderItem({
120
- item: data[index],
121
- index
122
- });
123
- return renderedItem;
124
- }
125
- var getItemSize = (state, index, data) => {
126
- const { getEstimatedItemSize, estimatedItemSize } = state.props;
127
- return getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize;
128
- };
129
- function calculateItemsInView(state) {
130
- reactNative.unstable_batchedUpdates(() => {
131
- var _a, _b, _c;
132
- const {
133
- props: { data, onViewableRangeChanged },
134
- scrollLength,
135
- scroll: scrollState,
136
- startNoBuffer: startNoBufferState,
137
- startBuffered: startBufferedState,
138
- endNoBuffer: endNoBufferState,
139
- endBuffered: endBufferedState,
140
- lengths,
141
- positions,
142
- scrollBuffer,
143
- ctx
144
- } = state;
145
- if (!data) {
146
- return;
147
- }
148
- const topPad = (peek$(ctx, `stylePaddingTop`) || 0) + (peek$(ctx, `headerSize`) || 0);
149
- const scroll = scrollState - topPad;
150
- scroll > state.scrollPrevious ? 1 : -1;
151
- const scrollBufferTop = scrollBuffer;
152
- const scrollBufferBottom = scrollBuffer;
153
- let startNoBuffer = null;
154
- let startBuffered = null;
155
- let endNoBuffer = null;
156
- let endBuffered = null;
157
- let loopStart = startBufferedState || 0;
158
- if (startBufferedState) {
159
- for (let i = startBufferedState; i >= 0; i--) {
160
- const id = getId(state, i);
161
- const top2 = positions.get(id);
162
- if (top2 !== void 0) {
163
- const length = (_a = lengths.get(id)) != null ? _a : getItemSize(state, i, data[i]);
164
- const bottom = top2 + length;
165
- if (bottom > scroll - scrollBufferTop) {
166
- loopStart = i;
167
- } else {
168
- break;
169
- }
170
- }
171
- }
172
- }
173
- let top = loopStart > 0 ? positions.get(getId(state, loopStart)) : 0;
174
- for (let i = loopStart; i < data.length; i++) {
175
- const id = getId(state, i);
176
- const length = (_b = lengths.get(id)) != null ? _b : getItemSize(state, i, data[i]);
177
- if (positions.get(id) !== top) {
178
- positions.set(id, top);
179
- }
180
- if (startNoBuffer === null && top + length > scroll) {
181
- startNoBuffer = i;
182
- }
183
- if (startBuffered === null && top + length > scroll - scrollBufferTop) {
184
- startBuffered = i;
185
- }
186
- if (startNoBuffer !== null) {
187
- if (top <= scroll + scrollLength) {
188
- endNoBuffer = i;
189
- }
190
- if (top <= scroll + scrollLength + scrollBufferBottom) {
191
- endBuffered = i;
192
- } else {
193
- break;
194
- }
195
- }
196
- top += length;
197
- }
198
- Object.assign(state, {
199
- startBuffered,
200
- startNoBuffer,
201
- endBuffered,
202
- endNoBuffer
203
- });
204
- if (startBuffered !== null && endBuffered !== null) {
205
- const prevNumContainers = ctx.values.get("numContainers");
206
- let numContainers = prevNumContainers;
207
- for (let i = startBuffered; i <= endBuffered; i++) {
208
- let isContained = false;
209
- for (let j = 0; j < numContainers; j++) {
210
- const index = peek$(ctx, `containerIndex${j}`);
211
- if (index === i) {
212
- isContained = true;
213
- break;
214
- }
215
- }
216
- if (!isContained) {
217
- const id = getId(state, i);
218
- const top2 = positions.get(id) || 0;
219
- let furthestIndex = -1;
220
- let furthestDistance = 0;
221
- for (let u = 0; u < numContainers; u++) {
222
- const index = peek$(ctx, `containerIndex${u}`);
223
- if (index < 0) {
224
- furthestIndex = u;
225
- break;
226
- }
227
- const pos = peek$(ctx, `containerPosition${u}`);
228
- if (index < startBuffered || index > endBuffered) {
229
- const distance = Math.abs(pos - top2);
230
- if (index < 0 || distance > furthestDistance) {
231
- furthestDistance = distance;
232
- furthestIndex = u;
233
- }
234
- }
235
- }
236
- if (furthestIndex >= 0) {
237
- set$(ctx, `containerIndex${furthestIndex}`, i);
238
- } else {
239
- if (__DEV__) {
240
- console.warn(
241
- "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemLength",
242
- i
243
- );
244
- }
245
- const containerId = numContainers;
246
- numContainers++;
247
- set$(ctx, `containerIndex${containerId}`, i);
248
- set$(ctx, `containerPosition${containerId}`, POSITION_OUT_OF_VIEW);
249
- }
250
- }
251
- }
252
- if (numContainers !== prevNumContainers) {
253
- set$(ctx, `numContainers`, numContainers);
254
- }
255
- for (let i = 0; i < numContainers; i++) {
256
- const itemIndex = peek$(ctx, `containerIndex${i}`);
257
- const item = data[itemIndex];
258
- if (item) {
259
- const id = getId(state, itemIndex);
260
- if (itemIndex < startBuffered || itemIndex > endBuffered) ; else {
261
- const pos = (_c = positions.get(id)) != null ? _c : -1;
262
- const prevPos = peek$(ctx, `containerPosition${i}`);
263
- if (pos >= 0 && pos !== prevPos) {
264
- set$(ctx, `containerPosition${i}`, pos);
265
- }
266
- }
267
- }
268
- }
269
- if (onViewableRangeChanged) {
270
- if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
271
- onViewableRangeChanged({
272
- start: startNoBuffer,
273
- startBuffered,
274
- end: endNoBuffer,
275
- endBuffered,
276
- items: data.slice(startNoBuffer, endNoBuffer + 1)
277
- });
278
- }
279
- }
280
- }
281
- });
282
- }
283
- function updateItemSize(state, refScroller, index, length) {
284
- const {
285
- props: { data, maintainScrollAtEnd },
286
- ctx,
287
- lengths,
288
- idsInFirstRender,
289
- isAtBottom
290
- } = state;
291
- if (!data) {
292
- return;
293
- }
294
- const id = getId(state, index);
295
- const wasInFirstRender = idsInFirstRender.has(id);
296
- const prevLength = lengths.get(id) || (wasInFirstRender ? getItemSize(state, index, data[index]) : 0);
297
- if (!prevLength || prevLength !== length) {
298
- lengths.set(id, length);
299
- addTotalLength(state, length - prevLength);
300
- if (isAtBottom && maintainScrollAtEnd) {
301
- requestAnimationFrame(() => {
302
- var _a;
303
- (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
304
- animated: true
305
- });
306
- });
307
- }
308
- if (!state.animFrameScroll && !state.animFrameLayout) {
309
- state.animFrameLayout = requestAnimationFrame(() => {
310
- state.animFrameLayout = null;
311
- if (!state.animFrameScroll) {
312
- calculateItemsInView(state);
313
- }
314
- });
315
- }
316
- }
317
- }
318
- function checkAtBottom(state) {
319
- const {
320
- ctx,
321
- scrollLength,
322
- scroll,
323
- props: { maintainScrollAtEndThreshold, onEndReached, onEndReachedThreshold }
324
- } = state;
325
- const totalLength = peek$(ctx, "totalLength");
326
- const distanceFromEnd = totalLength - scroll - scrollLength;
327
- state.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
328
- if (onEndReached && !state.isEndReached) {
329
- if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
330
- state.isEndReached = true;
331
- onEndReached({ distanceFromEnd });
332
- }
333
- }
334
- }
335
- function handleScrollDebounced(state) {
336
- calculateItemsInView(state);
337
- checkAtBottom(state);
338
- state.animFrameScroll = null;
339
- }
340
- function handleScroll(state, onScrollDebounced, event) {
341
- var _a, _b, _c;
342
- 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) {
343
- return;
344
- }
345
- const { horizontal, onScroll } = state.props;
346
- state.hasScrolled = true;
347
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
348
- state.scrollPrevious = state.scroll;
349
- state.scroll = newScroll;
350
- if (state && !state.animFrameScroll) {
351
- state.animFrameScroll = requestAnimationFrame(onScrollDebounced);
352
- }
353
- onScroll == null ? void 0 : onScroll(event);
354
- }
355
- function onLayout(state, event) {
356
- const scrollLength = event.nativeEvent.layout[state.props.horizontal ? "width" : "height"];
357
- state.scrollLength = scrollLength;
358
- }
359
- var LeanView = React6__namespace.forwardRef((props, ref) => {
360
- return React6__namespace.createElement("RCTView", { ...props, ref });
361
- });
362
- LeanView.displayName = "RCTView";
363
60
 
364
61
  // src/$View.tsx
365
62
  function $View({ $key, $style, ...rest }) {
@@ -367,27 +64,28 @@ function $View({ $key, $style, ...rest }) {
367
64
  const style = $style();
368
65
  return /* @__PURE__ */ React6__namespace.createElement(LeanView, { style, ...rest });
369
66
  }
370
- function InnerContainer({ id, getRenderedItem: getRenderedItem2, recycleItems, ItemSeparatorComponent }) {
67
+ function InnerContainer({ id, getRenderedItem, recycleItems, ItemSeparatorComponent }) {
371
68
  const itemIndex = use$(`containerIndex${id}`);
372
69
  const numItems = ItemSeparatorComponent ? use$("numItems") : 0;
373
70
  if (itemIndex < 0) {
374
71
  return null;
375
72
  }
376
- const renderedItem = getRenderedItem2(itemIndex);
73
+ const renderedItem = getRenderedItem(itemIndex);
377
74
  return /* @__PURE__ */ React6__namespace.createElement(React6__namespace.Fragment, { key: recycleItems ? void 0 : itemIndex }, renderedItem, ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent);
378
75
  }
379
76
  var Container = ({
380
77
  id,
381
78
  recycleItems,
382
79
  horizontal,
383
- getRenderedItem: getRenderedItem2,
384
- onLayout: onLayout2,
80
+ getRenderedItem,
81
+ onLayout,
385
82
  ItemSeparatorComponent
386
83
  }) => {
387
84
  const ctx = useStateContext();
388
85
  const createStyle = () => {
389
86
  const position = peek$(ctx, `containerPosition${id}`);
390
87
  return horizontal ? {
88
+ flexDirection: "row",
391
89
  position: "absolute",
392
90
  top: 0,
393
91
  bottom: 0,
@@ -410,7 +108,7 @@ var Container = ({
410
108
  const index = peek$(ctx, `containerIndex${id}`);
411
109
  if (index >= 0) {
412
110
  const length = Math.round(event.nativeEvent.layout[horizontal ? "width" : "height"]);
413
- onLayout2(index, length);
111
+ onLayout(index, length);
414
112
  }
415
113
  }
416
114
  },
@@ -418,7 +116,7 @@ var Container = ({
418
116
  InnerContainer,
419
117
  {
420
118
  id,
421
- getRenderedItem: getRenderedItem2,
119
+ getRenderedItem,
422
120
  recycleItems,
423
121
  ItemSeparatorComponent
424
122
  }
@@ -431,8 +129,8 @@ var Containers = React6__namespace.memo(function Containers2({
431
129
  horizontal,
432
130
  recycleItems,
433
131
  ItemSeparatorComponent,
434
- updateItemSize: updateItemSize2,
435
- getRenderedItem: getRenderedItem2
132
+ updateItemSize,
133
+ getRenderedItem
436
134
  }) {
437
135
  const ctx = useStateContext();
438
136
  const numContainers = use$("numContainers");
@@ -446,8 +144,8 @@ var Containers = React6__namespace.memo(function Containers2({
446
144
  key: i,
447
145
  recycleItems,
448
146
  horizontal,
449
- getRenderedItem: getRenderedItem2,
450
- onLayout: updateItemSize2,
147
+ getRenderedItem,
148
+ onLayout: updateItemSize,
451
149
  ItemSeparatorComponent
452
150
  }
453
151
  )
@@ -485,15 +183,14 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
485
183
  recycleItems,
486
184
  ItemSeparatorComponent,
487
185
  alignItemsAtEnd,
488
- handleScroll: handleScroll2,
489
- onLayout: onLayout2,
186
+ handleScroll,
187
+ onLayout,
490
188
  ListHeaderComponent,
491
189
  ListHeaderComponentStyle,
492
190
  ListFooterComponent,
493
191
  ListFooterComponentStyle,
494
- getRenderedItem: getRenderedItem2,
495
- updateItemSize: updateItemSize2,
496
- addTotalLength: addTotalLength2,
192
+ getRenderedItem,
193
+ updateItemSize,
497
194
  refScroller,
498
195
  ...rest
499
196
  }) {
@@ -509,64 +206,38 @@ var ListComponent = React6__namespace.memo(function ListComponent2({
509
206
  height: "100%"
510
207
  } : {}
511
208
  ],
512
- onScroll: handleScroll2,
513
- onLayout: onLayout2,
209
+ onScroll: handleScroll,
210
+ onLayout,
211
+ scrollEventThrottle: 32,
514
212
  horizontal,
515
213
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
516
214
  ref: refScroller
517
215
  },
518
216
  alignItemsAtEnd && /* @__PURE__ */ React6__namespace.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$(ctx, "paddingTop") }) }),
519
- ListHeaderComponent && /* @__PURE__ */ React6__namespace.createElement(
520
- reactNative.View,
521
- {
522
- style: ListHeaderComponentStyle,
523
- onLayout: (event) => {
524
- const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
525
- const prevSize = peek$(ctx, "headerSize") || 0;
526
- if (size !== prevSize) {
527
- set$(ctx, "headerSize", size);
528
- addTotalLength2(size - prevSize);
529
- }
530
- }
531
- },
532
- getComponent(ListHeaderComponent)
533
- ),
217
+ ListHeaderComponent && /* @__PURE__ */ React6__namespace.createElement(reactNative.View, { style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
534
218
  /* @__PURE__ */ React6__namespace.createElement(
535
219
  Containers,
536
220
  {
537
221
  horizontal,
538
222
  recycleItems,
539
- getRenderedItem: getRenderedItem2,
223
+ getRenderedItem,
540
224
  ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
541
- updateItemSize: updateItemSize2
225
+ updateItemSize
542
226
  }
543
227
  ),
544
- ListFooterComponent && /* @__PURE__ */ React6__namespace.createElement(
545
- reactNative.View,
546
- {
547
- style: ListFooterComponentStyle,
548
- onLayout: (event) => {
549
- const size = event.nativeEvent.layout[horizontal ? "width" : "height"];
550
- const prevSize = peek$(ctx, "footerSize") || 0;
551
- if (size !== prevSize) {
552
- set$(ctx, "footerSize", size);
553
- addTotalLength2(size - prevSize);
554
- }
555
- }
556
- },
557
- getComponent(ListFooterComponent)
558
- )
228
+ ListFooterComponent && /* @__PURE__ */ React6__namespace.createElement(reactNative.View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
559
229
  );
560
230
  });
561
231
 
562
232
  // src/LegendList.tsx
233
+ var DEFAULT_SCROLL_BUFFER = 0;
234
+ var POSITION_OUT_OF_VIEW = -1e4;
563
235
  var LegendList = React6.forwardRef(function LegendList2(props, forwardedRef) {
564
236
  return /* @__PURE__ */ React6__namespace.createElement(StateProvider, null, /* @__PURE__ */ React6__namespace.createElement(LegendListInner, { ...props, ref: forwardedRef }));
565
237
  });
566
238
  var LegendListInner = React6.forwardRef(
567
- function LegendListInner2(props_, forwardedRef) {
239
+ function LegendListInner2(props, forwardedRef) {
568
240
  var _a, _b;
569
- const props = applyDefaultProps(props_);
570
241
  const {
571
242
  data,
572
243
  initialScrollIndex,
@@ -574,14 +245,61 @@ var LegendListInner = React6.forwardRef(
574
245
  horizontal,
575
246
  style: styleProp,
576
247
  contentContainerStyle: contentContainerStyleProp,
248
+ initialNumContainers,
577
249
  drawDistance,
250
+ recycleItems = true,
251
+ onEndReachedThreshold = 0.5,
252
+ maintainScrollAtEnd = false,
253
+ maintainScrollAtEndThreshold = 0.1,
254
+ alignItemsAtEnd = false,
255
+ onScroll: onScrollProp,
256
+ keyExtractor,
257
+ renderItem,
258
+ estimatedItemSize,
259
+ getEstimatedItemSize,
260
+ onEndReached,
261
+ onViewableRangeChanged,
578
262
  ...rest
579
263
  } = props;
580
264
  const ctx = useStateContext();
581
- const refScroller = forwardedRef || React6.useRef(null);
265
+ const internalRef = React6.useRef(null);
266
+ const refScroller = forwardedRef || internalRef;
582
267
  const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_SCROLL_BUFFER;
268
+ const styleFlattened = reactNative.StyleSheet.flatten(styleProp);
269
+ const style = React6.useMemo(() => styleFlattened, [JSON.stringify(styleProp)]);
270
+ const contentContainerStyleFlattened = reactNative.StyleSheet.flatten(contentContainerStyleProp);
271
+ const contentContainerStyle = React6.useMemo(
272
+ () => contentContainerStyleFlattened,
273
+ [JSON.stringify(contentContainerStyleProp)]
274
+ );
583
275
  const refState = React6.useRef();
584
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : React6.useMemo(calculateInitialOffset.bind(void 0, props), []);
276
+ const getId = (index) => {
277
+ var _a2;
278
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
279
+ if (!data2) {
280
+ return "";
281
+ }
282
+ const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
283
+ return ret + "";
284
+ };
285
+ const getItemLength = (index, data2) => {
286
+ return getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
287
+ };
288
+ const calculateInitialOffset = () => {
289
+ if (initialScrollIndex) {
290
+ if (getEstimatedItemSize) {
291
+ let offset = 0;
292
+ for (let i = 0; i < initialScrollIndex; i++) {
293
+ offset += getEstimatedItemSize(i, data[i]);
294
+ }
295
+ return offset;
296
+ } else if (estimatedItemSize) {
297
+ return initialScrollIndex * estimatedItemSize;
298
+ }
299
+ }
300
+ return void 0;
301
+ };
302
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : React6.useMemo(calculateInitialOffset, [initialScrollIndex, estimatedItemSize]);
585
303
  if (!refState.current) {
586
304
  refState.current = {
587
305
  lengths: /* @__PURE__ */ new Map(),
@@ -592,6 +310,7 @@ var LegendListInner = React6.forwardRef(
592
310
  isStartReached: false,
593
311
  isEndReached: false,
594
312
  isAtBottom: false,
313
+ data,
595
314
  idsInFirstRender: void 0,
596
315
  hasScrolled: false,
597
316
  scrollLength: reactNative.Dimensions.get("window")[horizontal ? "width" : "height"],
@@ -599,91 +318,295 @@ var LegendListInner = React6.forwardRef(
599
318
  startNoBuffer: 0,
600
319
  endBuffered: 0,
601
320
  endNoBuffer: 0,
602
- scroll: initialContentOffset || 0,
603
- scrollPrevious: initialContentOffset || 0,
604
- previousViewableItems: /* @__PURE__ */ new Set(),
605
- props,
606
- ctx,
607
- scrollBuffer
321
+ scroll: initialContentOffset || 0
608
322
  };
609
- refState.current.idsInFirstRender = new Set(
610
- data.map((_, i) => getId(refState.current, i))
611
- );
323
+ refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
612
324
  }
613
- refState.current.props = props;
614
- refState.current.ctx = ctx;
615
- const styleFlattened = reactNative.StyleSheet.flatten(styleProp);
616
- const style = React6.useMemo(() => styleFlattened, [JSON.stringify(styleProp)]);
617
- const contentContainerStyleFlattened = reactNative.StyleSheet.flatten(contentContainerStyleProp);
618
- const contentContainerStyle = React6.useMemo(
619
- () => contentContainerStyleFlattened,
620
- [JSON.stringify(contentContainerStyleProp)]
621
- );
622
- const fns = React6.useMemo(
623
- () => ({
624
- getRenderedItem: getRenderedItem.bind(void 0, refState.current),
625
- getId: getId.bind(void 0, refState.current),
626
- getItemSize: getItemSize.bind(void 0, refState.current),
627
- calculateItemsInView: calculateItemsInView.bind(void 0, refState.current),
628
- updateItemSize: updateItemSize.bind(void 0, refState.current, refScroller),
629
- checkAtBottom: checkAtBottom.bind(void 0, refState.current),
630
- handleScrollDebounced: handleScrollDebounced.bind(void 0, refState.current),
631
- onLayout: onLayout.bind(void 0, refState.current),
632
- addTotalLength: addTotalLength.bind(void 0, refState.current),
633
- handleScroll: handleScroll.bind(
634
- void 0,
635
- refState.current,
636
- handleScrollDebounced.bind(void 0, refState.current)
637
- )
638
- }),
639
- []
640
- );
641
- const {
642
- calculateItemsInView: calculateItemsInView2,
643
- getId: getId2,
644
- getItemSize: getItemSize2,
645
- checkAtBottom: checkAtBottom2,
646
- updateItemSize: updateItemSize2,
647
- getRenderedItem: getRenderedItem2,
648
- onLayout: onLayout2,
649
- handleScroll: handleScroll2,
650
- addTotalLength: addTotalLength2
651
- } = fns;
325
+ refState.current.data = data;
652
326
  set$(ctx, `numItems`, data.length);
653
327
  set$(ctx, `stylePaddingTop`, (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
328
+ const addTotalSize = (add) => {
329
+ const length = (peek$(ctx, `totalLength`) || 0) + add;
330
+ set$(ctx, `totalLength`, length);
331
+ const screenLength = refState.current.scrollLength;
332
+ if (alignItemsAtEnd) {
333
+ const listPaddingTop = peek$(ctx, `stylePaddingTop`);
334
+ set$(ctx, `paddingTop`, Math.max(0, screenLength - length - listPaddingTop));
335
+ }
336
+ };
337
+ const allocateContainers = React6.useCallback(() => {
338
+ const scrollLength = refState.current.scrollLength;
339
+ const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
340
+ const numContainers = initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) + 4;
341
+ for (let i = 0; i < numContainers; i++) {
342
+ set$(ctx, `containerIndex${i}`, -1);
343
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
344
+ }
345
+ set$(ctx, `numContainers`, numContainers);
346
+ }, []);
347
+ const getRenderedItem = React6.useCallback(
348
+ (index) => {
349
+ var _a2;
350
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
351
+ if (!data2) {
352
+ return null;
353
+ }
354
+ const renderedItem = renderItem == null ? void 0 : renderItem({
355
+ item: data2[index],
356
+ index
357
+ });
358
+ return renderedItem;
359
+ },
360
+ [renderItem]
361
+ );
362
+ const calculateItemsInView = React6.useCallback(() => {
363
+ reactNative.unstable_batchedUpdates(() => {
364
+ var _a2, _b2, _c;
365
+ const {
366
+ data: data2,
367
+ scrollLength,
368
+ scroll: scrollState,
369
+ startNoBuffer: startNoBufferState,
370
+ startBuffered: startBufferedState,
371
+ endNoBuffer: endNoBufferState,
372
+ endBuffered: endBufferedState
373
+ } = refState.current;
374
+ if (!data2) {
375
+ return;
376
+ }
377
+ const topPad = (peek$(ctx, `stylePaddingTop`) || 0) + (peek$(ctx, `headerSize`) || 0);
378
+ const scroll = scrollState - topPad;
379
+ const { lengths, positions } = refState.current;
380
+ let startNoBuffer = null;
381
+ let startBuffered = null;
382
+ let endNoBuffer = null;
383
+ let endBuffered = null;
384
+ let loopStart = startBufferedState || 0;
385
+ if (startBufferedState) {
386
+ for (let i = startBufferedState; i >= 0; i--) {
387
+ const id = getId(i);
388
+ const top2 = positions.get(id);
389
+ if (top2 !== void 0) {
390
+ const length = (_a2 = lengths.get(id)) != null ? _a2 : getItemLength(i, data2[i]);
391
+ const bottom = top2 + length;
392
+ if (bottom > scroll - scrollBuffer) {
393
+ loopStart = i;
394
+ } else {
395
+ break;
396
+ }
397
+ }
398
+ }
399
+ }
400
+ let top = loopStart > 0 ? positions.get(getId(loopStart)) : 0;
401
+ for (let i = loopStart; i < data2.length; i++) {
402
+ const id = getId(i);
403
+ const length = (_b2 = lengths.get(id)) != null ? _b2 : getItemLength(i, data2[i]);
404
+ if (positions.get(id) !== top) {
405
+ positions.set(id, top);
406
+ }
407
+ if (startNoBuffer === null && top + length > scroll) {
408
+ startNoBuffer = i;
409
+ }
410
+ if (startBuffered === null && top + length > scroll - scrollBuffer) {
411
+ startBuffered = i;
412
+ }
413
+ if (startNoBuffer !== null) {
414
+ if (top <= scroll + scrollLength) {
415
+ endNoBuffer = i;
416
+ }
417
+ if (top <= scroll + scrollLength + scrollBuffer) {
418
+ endBuffered = i;
419
+ } else {
420
+ break;
421
+ }
422
+ }
423
+ top += length;
424
+ }
425
+ Object.assign(refState.current, {
426
+ startBuffered,
427
+ startNoBuffer,
428
+ endBuffered,
429
+ endNoBuffer
430
+ });
431
+ if (startBuffered !== null && endBuffered !== null) {
432
+ const prevNumContainers = ctx.values.get("numContainers");
433
+ let numContainers = prevNumContainers;
434
+ for (let i = startBuffered; i <= endBuffered; i++) {
435
+ let isContained = false;
436
+ for (let j = 0; j < numContainers; j++) {
437
+ const index = peek$(ctx, `containerIndex${j}`);
438
+ if (index === i) {
439
+ isContained = true;
440
+ break;
441
+ }
442
+ }
443
+ if (!isContained) {
444
+ let didRecycle = false;
445
+ for (let u = 0; u < numContainers; u++) {
446
+ const index = peek$(ctx, `containerIndex${u}`);
447
+ if (index < startBuffered || index > endBuffered) {
448
+ set$(ctx, `containerIndex${u}`, i);
449
+ didRecycle = true;
450
+ break;
451
+ }
452
+ }
453
+ if (!didRecycle) {
454
+ if (__DEV__) {
455
+ console.warn(
456
+ "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemSize",
457
+ i
458
+ );
459
+ }
460
+ const id = numContainers;
461
+ numContainers++;
462
+ set$(ctx, `containerIndex${id}`, i);
463
+ set$(ctx, `containerPosition${id}`, POSITION_OUT_OF_VIEW);
464
+ }
465
+ }
466
+ }
467
+ if (numContainers !== prevNumContainers) {
468
+ set$(ctx, `numContainers`, numContainers);
469
+ }
470
+ for (let i = 0; i < numContainers; i++) {
471
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
472
+ const item = data2[itemIndex];
473
+ if (item) {
474
+ const id = getId(itemIndex);
475
+ if (itemIndex < startBuffered || itemIndex > endBuffered) {
476
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
477
+ } else {
478
+ const pos = (_c = positions.get(id)) != null ? _c : -1;
479
+ const prevPos = peek$(ctx, `containerPosition${i}`);
480
+ if (pos >= 0 && pos !== prevPos) {
481
+ set$(ctx, `containerPosition${i}`, pos);
482
+ }
483
+ }
484
+ }
485
+ }
486
+ if (onViewableRangeChanged) {
487
+ if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
488
+ onViewableRangeChanged({
489
+ start: startNoBuffer,
490
+ startBuffered,
491
+ end: endNoBuffer,
492
+ endBuffered,
493
+ items: data2.slice(startNoBuffer, endNoBuffer + 1)
494
+ });
495
+ }
496
+ }
497
+ }
498
+ });
499
+ }, [data]);
654
500
  React6.useMemo(() => {
655
501
  var _a2, _b2;
656
- allocateContainers(refState.current);
657
- calculateItemsInView2();
502
+ allocateContainers();
503
+ calculateItemsInView();
658
504
  const lengths = (_a2 = refState.current) == null ? void 0 : _a2.lengths;
659
- let totalLength = (peek$(ctx, `headerSize`) || 0) + (peek$(ctx, `footerSize`) || 0);
505
+ let totalLength = 0;
660
506
  for (let i = 0; i < data.length; i++) {
661
- const id = getId2(i);
662
- totalLength += (_b2 = lengths.get(id)) != null ? _b2 : getItemSize2(i, data[i]);
507
+ const id = getId(i);
508
+ totalLength += (_b2 = lengths.get(id)) != null ? _b2 : getItemLength(i, data[i]);
663
509
  }
664
- addTotalLength2(totalLength);
510
+ addTotalSize(totalLength);
665
511
  }, []);
512
+ const checkAtBottom = () => {
513
+ var _a2;
514
+ const { scrollLength, scroll } = refState.current;
515
+ const totalLength = peek$(ctx, "totalLength");
516
+ const distanceFromEnd = totalLength - scroll - scrollLength;
517
+ if (refState.current) {
518
+ refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
519
+ }
520
+ if (onEndReached && !((_a2 = refState.current) == null ? void 0 : _a2.isEndReached)) {
521
+ if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
522
+ if (refState.current) {
523
+ refState.current.isEndReached = true;
524
+ }
525
+ onEndReached({ distanceFromEnd });
526
+ }
527
+ }
528
+ };
666
529
  React6.useMemo(() => {
667
530
  if (refState.current) {
668
531
  refState.current.isEndReached = false;
669
532
  }
670
- calculateItemsInView2();
671
- checkAtBottom2();
533
+ calculateItemsInView();
534
+ checkAtBottom();
672
535
  }, [data]);
536
+ const updateItemSize = React6.useCallback((index, length) => {
537
+ var _a2, _b2, _c, _d;
538
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
539
+ if (!data2) {
540
+ return;
541
+ }
542
+ const lengths = (_b2 = refState.current) == null ? void 0 : _b2.lengths;
543
+ const id = getId(index);
544
+ const wasInFirstRender = (_c = refState.current) == null ? void 0 : _c.idsInFirstRender.has(id);
545
+ const prevLength = lengths.get(id) || (wasInFirstRender ? getItemLength(index, data2[index]) : 0);
546
+ if (!prevLength || prevLength !== length) {
547
+ lengths.set(id, length);
548
+ addTotalSize(length - prevLength);
549
+ if (((_d = refState.current) == null ? void 0 : _d.isAtBottom) && maintainScrollAtEnd) {
550
+ requestAnimationFrame(() => {
551
+ var _a3;
552
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
553
+ animated: true
554
+ });
555
+ });
556
+ }
557
+ const state = refState.current;
558
+ if (!state.animFrameScroll && !state.animFrameLayout) {
559
+ state.animFrameLayout = requestAnimationFrame(() => {
560
+ state.animFrameLayout = null;
561
+ calculateItemsInView();
562
+ });
563
+ }
564
+ }
565
+ }, []);
566
+ const handleScrollDebounced = React6.useCallback(() => {
567
+ calculateItemsInView();
568
+ checkAtBottom();
569
+ if (refState.current) {
570
+ refState.current.animFrameScroll = null;
571
+ }
572
+ }, []);
573
+ const onLayout = React6.useCallback((event) => {
574
+ const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
575
+ refState.current.scrollLength = scrollLength;
576
+ }, []);
577
+ const handleScroll = React6.useCallback(
578
+ (event, fromSelf) => {
579
+ var _a2, _b2, _c;
580
+ 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) {
581
+ return;
582
+ }
583
+ refState.current.hasScrolled = true;
584
+ const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
585
+ refState.current.scroll = newScroll;
586
+ if (refState.current && !refState.current.animFrameScroll) {
587
+ refState.current.animFrameScroll = requestAnimationFrame(handleScrollDebounced);
588
+ }
589
+ if (!fromSelf) {
590
+ onScrollProp == null ? void 0 : onScrollProp(event);
591
+ }
592
+ },
593
+ []
594
+ );
673
595
  return /* @__PURE__ */ React6__namespace.createElement(
674
596
  ListComponent,
675
597
  {
676
598
  ...rest,
677
- addTotalLength: addTotalLength2,
678
599
  contentContainerStyle,
679
600
  style,
680
601
  horizontal,
681
602
  refScroller,
682
603
  initialContentOffset,
683
- getRenderedItem: getRenderedItem2,
684
- updateItemSize: updateItemSize2,
685
- handleScroll: handleScroll2,
686
- onLayout: onLayout2
604
+ getRenderedItem,
605
+ updateItemSize,
606
+ handleScroll,
607
+ onLayout,
608
+ recycleItems,
609
+ alignItemsAtEnd
687
610
  }
688
611
  );
689
612
  }