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