@legendapp/list 0.3.2 → 0.3.3

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,37 @@ 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,
514
211
  horizontal,
515
212
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
516
213
  ref: refScroller
517
214
  },
518
215
  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
- ),
216
+ ListHeaderComponent && /* @__PURE__ */ React6__namespace.createElement(reactNative.View, { style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
534
217
  /* @__PURE__ */ React6__namespace.createElement(
535
218
  Containers,
536
219
  {
537
220
  horizontal,
538
221
  recycleItems,
539
- getRenderedItem: getRenderedItem2,
222
+ getRenderedItem,
540
223
  ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
541
- updateItemSize: updateItemSize2
224
+ updateItemSize
542
225
  }
543
226
  ),
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
- )
227
+ ListFooterComponent && /* @__PURE__ */ React6__namespace.createElement(reactNative.View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
559
228
  );
560
229
  });
561
230
 
562
231
  // src/LegendList.tsx
232
+ var DEFAULT_SCROLL_BUFFER = 0;
233
+ var POSITION_OUT_OF_VIEW = -1e4;
563
234
  var LegendList = React6.forwardRef(function LegendList2(props, forwardedRef) {
564
235
  return /* @__PURE__ */ React6__namespace.createElement(StateProvider, null, /* @__PURE__ */ React6__namespace.createElement(LegendListInner, { ...props, ref: forwardedRef }));
565
236
  });
566
237
  var LegendListInner = React6.forwardRef(
567
- function LegendListInner2(props_, forwardedRef) {
238
+ function LegendListInner2(props, forwardedRef) {
568
239
  var _a, _b;
569
- const props = applyDefaultProps(props_);
570
240
  const {
571
241
  data,
572
242
  initialScrollIndex,
@@ -574,14 +244,61 @@ var LegendListInner = React6.forwardRef(
574
244
  horizontal,
575
245
  style: styleProp,
576
246
  contentContainerStyle: contentContainerStyleProp,
247
+ initialNumContainers,
577
248
  drawDistance,
249
+ recycleItems = true,
250
+ onEndReachedThreshold = 0.5,
251
+ maintainScrollAtEnd = false,
252
+ maintainScrollAtEndThreshold = 0.1,
253
+ alignItemsAtEnd = false,
254
+ onScroll: onScrollProp,
255
+ keyExtractor,
256
+ renderItem,
257
+ estimatedItemSize,
258
+ getEstimatedItemSize,
259
+ onEndReached,
260
+ onViewableRangeChanged,
578
261
  ...rest
579
262
  } = props;
580
263
  const ctx = useStateContext();
581
- const refScroller = forwardedRef || React6.useRef(null);
264
+ const internalRef = React6.useRef(null);
265
+ const refScroller = forwardedRef || internalRef;
582
266
  const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_SCROLL_BUFFER;
267
+ const styleFlattened = reactNative.StyleSheet.flatten(styleProp);
268
+ const style = React6.useMemo(() => styleFlattened, [JSON.stringify(styleProp)]);
269
+ const contentContainerStyleFlattened = reactNative.StyleSheet.flatten(contentContainerStyleProp);
270
+ const contentContainerStyle = React6.useMemo(
271
+ () => contentContainerStyleFlattened,
272
+ [JSON.stringify(contentContainerStyleProp)]
273
+ );
583
274
  const refState = React6.useRef();
584
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : React6.useMemo(calculateInitialOffset.bind(void 0, props), []);
275
+ const getId = (index) => {
276
+ var _a2;
277
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
278
+ if (!data2) {
279
+ return "";
280
+ }
281
+ const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
282
+ return ret + "";
283
+ };
284
+ const getItemLength = (index, data2) => {
285
+ return getEstimatedItemSize ? getEstimatedItemSize(index, data2) : estimatedItemSize;
286
+ };
287
+ const calculateInitialOffset = () => {
288
+ if (initialScrollIndex) {
289
+ if (getEstimatedItemSize) {
290
+ let offset = 0;
291
+ for (let i = 0; i < initialScrollIndex; i++) {
292
+ offset += getEstimatedItemSize(i, data[i]);
293
+ }
294
+ return offset;
295
+ } else if (estimatedItemSize) {
296
+ return initialScrollIndex * estimatedItemSize;
297
+ }
298
+ }
299
+ return void 0;
300
+ };
301
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : React6.useMemo(calculateInitialOffset, [initialScrollIndex, estimatedItemSize]);
585
302
  if (!refState.current) {
586
303
  refState.current = {
587
304
  lengths: /* @__PURE__ */ new Map(),
@@ -592,6 +309,7 @@ var LegendListInner = React6.forwardRef(
592
309
  isStartReached: false,
593
310
  isEndReached: false,
594
311
  isAtBottom: false,
312
+ data,
595
313
  idsInFirstRender: void 0,
596
314
  hasScrolled: false,
597
315
  scrollLength: reactNative.Dimensions.get("window")[horizontal ? "width" : "height"],
@@ -599,91 +317,295 @@ var LegendListInner = React6.forwardRef(
599
317
  startNoBuffer: 0,
600
318
  endBuffered: 0,
601
319
  endNoBuffer: 0,
602
- scroll: initialContentOffset || 0,
603
- scrollPrevious: initialContentOffset || 0,
604
- previousViewableItems: /* @__PURE__ */ new Set(),
605
- props,
606
- ctx,
607
- scrollBuffer
320
+ scroll: initialContentOffset || 0
608
321
  };
609
- refState.current.idsInFirstRender = new Set(
610
- data.map((_, i) => getId(refState.current, i))
611
- );
322
+ refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
612
323
  }
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;
324
+ refState.current.data = data;
652
325
  set$(ctx, `numItems`, data.length);
653
326
  set$(ctx, `stylePaddingTop`, (_b = (_a = styleFlattened == null ? void 0 : styleFlattened.paddingTop) != null ? _a : contentContainerStyleFlattened == null ? void 0 : contentContainerStyleFlattened.paddingTop) != null ? _b : 0);
327
+ const addTotalSize = (add) => {
328
+ const length = (peek$(ctx, `totalLength`) || 0) + add;
329
+ set$(ctx, `totalLength`, length);
330
+ const screenLength = refState.current.scrollLength;
331
+ if (alignItemsAtEnd) {
332
+ const listPaddingTop = peek$(ctx, `stylePaddingTop`);
333
+ set$(ctx, `paddingTop`, Math.max(0, screenLength - length - listPaddingTop));
334
+ }
335
+ };
336
+ const allocateContainers = React6.useCallback(() => {
337
+ const scrollLength = refState.current.scrollLength;
338
+ const averageItemSize = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0]);
339
+ const numContainers = initialNumContainers || Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) + 4;
340
+ for (let i = 0; i < numContainers; i++) {
341
+ set$(ctx, `containerIndex${i}`, -1);
342
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
343
+ }
344
+ set$(ctx, `numContainers`, numContainers);
345
+ }, []);
346
+ const getRenderedItem = React6.useCallback(
347
+ (index) => {
348
+ var _a2;
349
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
350
+ if (!data2) {
351
+ return null;
352
+ }
353
+ const renderedItem = renderItem == null ? void 0 : renderItem({
354
+ item: data2[index],
355
+ index
356
+ });
357
+ return renderedItem;
358
+ },
359
+ [renderItem]
360
+ );
361
+ const calculateItemsInView = React6.useCallback(() => {
362
+ reactNative.unstable_batchedUpdates(() => {
363
+ var _a2, _b2, _c;
364
+ const {
365
+ data: data2,
366
+ scrollLength,
367
+ scroll: scrollState,
368
+ startNoBuffer: startNoBufferState,
369
+ startBuffered: startBufferedState,
370
+ endNoBuffer: endNoBufferState,
371
+ endBuffered: endBufferedState
372
+ } = refState.current;
373
+ if (!data2) {
374
+ return;
375
+ }
376
+ const topPad = (peek$(ctx, `stylePaddingTop`) || 0) + (peek$(ctx, `headerSize`) || 0);
377
+ const scroll = scrollState - topPad;
378
+ const { lengths, positions } = refState.current;
379
+ let startNoBuffer = null;
380
+ let startBuffered = null;
381
+ let endNoBuffer = null;
382
+ let endBuffered = null;
383
+ let loopStart = startBufferedState || 0;
384
+ if (startBufferedState) {
385
+ for (let i = startBufferedState; i >= 0; i--) {
386
+ const id = getId(i);
387
+ const top2 = positions.get(id);
388
+ if (top2 !== void 0) {
389
+ const length = (_a2 = lengths.get(id)) != null ? _a2 : getItemLength(i, data2[i]);
390
+ const bottom = top2 + length;
391
+ if (bottom > scroll - scrollBuffer) {
392
+ loopStart = i;
393
+ } else {
394
+ break;
395
+ }
396
+ }
397
+ }
398
+ }
399
+ let top = loopStart > 0 ? positions.get(getId(loopStart)) : 0;
400
+ for (let i = loopStart; i < data2.length; i++) {
401
+ const id = getId(i);
402
+ const length = (_b2 = lengths.get(id)) != null ? _b2 : getItemLength(i, data2[i]);
403
+ if (positions.get(id) !== top) {
404
+ positions.set(id, top);
405
+ }
406
+ if (startNoBuffer === null && top + length > scroll) {
407
+ startNoBuffer = i;
408
+ }
409
+ if (startBuffered === null && top + length > scroll - scrollBuffer) {
410
+ startBuffered = i;
411
+ }
412
+ if (startNoBuffer !== null) {
413
+ if (top <= scroll + scrollLength) {
414
+ endNoBuffer = i;
415
+ }
416
+ if (top <= scroll + scrollLength + scrollBuffer) {
417
+ endBuffered = i;
418
+ } else {
419
+ break;
420
+ }
421
+ }
422
+ top += length;
423
+ }
424
+ Object.assign(refState.current, {
425
+ startBuffered,
426
+ startNoBuffer,
427
+ endBuffered,
428
+ endNoBuffer
429
+ });
430
+ if (startBuffered !== null && endBuffered !== null) {
431
+ const prevNumContainers = ctx.values.get("numContainers");
432
+ let numContainers = prevNumContainers;
433
+ for (let i = startBuffered; i <= endBuffered; i++) {
434
+ let isContained = false;
435
+ for (let j = 0; j < numContainers; j++) {
436
+ const index = peek$(ctx, `containerIndex${j}`);
437
+ if (index === i) {
438
+ isContained = true;
439
+ break;
440
+ }
441
+ }
442
+ if (!isContained) {
443
+ let didRecycle = false;
444
+ for (let u = 0; u < numContainers; u++) {
445
+ const index = peek$(ctx, `containerIndex${u}`);
446
+ if (index < startBuffered || index > endBuffered) {
447
+ set$(ctx, `containerIndex${u}`, i);
448
+ didRecycle = true;
449
+ break;
450
+ }
451
+ }
452
+ if (!didRecycle) {
453
+ if (__DEV__) {
454
+ console.warn(
455
+ "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemSize",
456
+ i
457
+ );
458
+ }
459
+ const id = numContainers;
460
+ numContainers++;
461
+ set$(ctx, `containerIndex${id}`, i);
462
+ set$(ctx, `containerPosition${id}`, POSITION_OUT_OF_VIEW);
463
+ }
464
+ }
465
+ }
466
+ if (numContainers !== prevNumContainers) {
467
+ set$(ctx, `numContainers`, numContainers);
468
+ }
469
+ for (let i = 0; i < numContainers; i++) {
470
+ const itemIndex = peek$(ctx, `containerIndex${i}`);
471
+ const item = data2[itemIndex];
472
+ if (item) {
473
+ const id = getId(itemIndex);
474
+ if (itemIndex < startBuffered || itemIndex > endBuffered) {
475
+ set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
476
+ } else {
477
+ const pos = (_c = positions.get(id)) != null ? _c : -1;
478
+ const prevPos = peek$(ctx, `containerPosition${i}`);
479
+ if (pos >= 0 && pos !== prevPos) {
480
+ set$(ctx, `containerPosition${i}`, pos);
481
+ }
482
+ }
483
+ }
484
+ }
485
+ if (onViewableRangeChanged) {
486
+ if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
487
+ onViewableRangeChanged({
488
+ start: startNoBuffer,
489
+ startBuffered,
490
+ end: endNoBuffer,
491
+ endBuffered,
492
+ items: data2.slice(startNoBuffer, endNoBuffer + 1)
493
+ });
494
+ }
495
+ }
496
+ }
497
+ });
498
+ }, [data]);
654
499
  React6.useMemo(() => {
655
500
  var _a2, _b2;
656
- allocateContainers(refState.current);
657
- calculateItemsInView2();
501
+ allocateContainers();
502
+ calculateItemsInView();
658
503
  const lengths = (_a2 = refState.current) == null ? void 0 : _a2.lengths;
659
- let totalLength = (peek$(ctx, `headerSize`) || 0) + (peek$(ctx, `footerSize`) || 0);
504
+ let totalLength = 0;
660
505
  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]);
506
+ const id = getId(i);
507
+ totalLength += (_b2 = lengths.get(id)) != null ? _b2 : getItemLength(i, data[i]);
663
508
  }
664
- addTotalLength2(totalLength);
509
+ addTotalSize(totalLength);
665
510
  }, []);
511
+ const checkAtBottom = () => {
512
+ var _a2;
513
+ const { scrollLength, scroll } = refState.current;
514
+ const totalLength = peek$(ctx, "totalLength");
515
+ const distanceFromEnd = totalLength - scroll - scrollLength;
516
+ if (refState.current) {
517
+ refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
518
+ }
519
+ if (onEndReached && !((_a2 = refState.current) == null ? void 0 : _a2.isEndReached)) {
520
+ if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
521
+ if (refState.current) {
522
+ refState.current.isEndReached = true;
523
+ }
524
+ onEndReached({ distanceFromEnd });
525
+ }
526
+ }
527
+ };
666
528
  React6.useMemo(() => {
667
529
  if (refState.current) {
668
530
  refState.current.isEndReached = false;
669
531
  }
670
- calculateItemsInView2();
671
- checkAtBottom2();
532
+ calculateItemsInView();
533
+ checkAtBottom();
672
534
  }, [data]);
535
+ const updateItemSize = React6.useCallback((index, length) => {
536
+ var _a2, _b2, _c, _d;
537
+ const data2 = (_a2 = refState.current) == null ? void 0 : _a2.data;
538
+ if (!data2) {
539
+ return;
540
+ }
541
+ const lengths = (_b2 = refState.current) == null ? void 0 : _b2.lengths;
542
+ const id = getId(index);
543
+ const wasInFirstRender = (_c = refState.current) == null ? void 0 : _c.idsInFirstRender.has(id);
544
+ const prevLength = lengths.get(id) || (wasInFirstRender ? getItemLength(index, data2[index]) : 0);
545
+ if (!prevLength || prevLength !== length) {
546
+ lengths.set(id, length);
547
+ addTotalSize(length - prevLength);
548
+ if (((_d = refState.current) == null ? void 0 : _d.isAtBottom) && maintainScrollAtEnd) {
549
+ requestAnimationFrame(() => {
550
+ var _a3;
551
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
552
+ animated: true
553
+ });
554
+ });
555
+ }
556
+ const state = refState.current;
557
+ if (!state.animFrameScroll && !state.animFrameLayout) {
558
+ state.animFrameLayout = requestAnimationFrame(() => {
559
+ state.animFrameLayout = null;
560
+ calculateItemsInView();
561
+ });
562
+ }
563
+ }
564
+ }, []);
565
+ const handleScrollDebounced = React6.useCallback(() => {
566
+ calculateItemsInView();
567
+ checkAtBottom();
568
+ if (refState.current) {
569
+ refState.current.animFrameScroll = null;
570
+ }
571
+ }, []);
572
+ const onLayout = React6.useCallback((event) => {
573
+ const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
574
+ refState.current.scrollLength = scrollLength;
575
+ }, []);
576
+ const handleScroll = React6.useCallback(
577
+ (event, fromSelf) => {
578
+ var _a2, _b2, _c;
579
+ 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) {
580
+ return;
581
+ }
582
+ refState.current.hasScrolled = true;
583
+ const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
584
+ refState.current.scroll = newScroll;
585
+ if (refState.current && !refState.current.animFrameScroll) {
586
+ refState.current.animFrameScroll = requestAnimationFrame(handleScrollDebounced);
587
+ }
588
+ if (!fromSelf) {
589
+ onScrollProp == null ? void 0 : onScrollProp(event);
590
+ }
591
+ },
592
+ []
593
+ );
673
594
  return /* @__PURE__ */ React6__namespace.createElement(
674
595
  ListComponent,
675
596
  {
676
597
  ...rest,
677
- addTotalLength: addTotalLength2,
678
598
  contentContainerStyle,
679
599
  style,
680
600
  horizontal,
681
601
  refScroller,
682
602
  initialContentOffset,
683
- getRenderedItem: getRenderedItem2,
684
- updateItemSize: updateItemSize2,
685
- handleScroll: handleScroll2,
686
- onLayout: onLayout2
603
+ getRenderedItem,
604
+ updateItemSize,
605
+ handleScroll,
606
+ onLayout,
607
+ recycleItems,
608
+ alignItemsAtEnd
687
609
  }
688
610
  );
689
611
  }