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