@legendapp/list 0.3.1 → 0.3.2

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