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