@legendapp/list 0.2.1 → 0.3.0

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,410 +1,539 @@
1
1
  import * as React2 from 'react';
2
- import { forwardRef, useRef, useCallback, useMemo, useEffect } from 'react';
3
- import { beginBatch, endBatch } from '@legendapp/state';
4
- import { enableReactNativeComponents } from '@legendapp/state/config/enableReactNativeComponents';
5
- import { useObservable, use$, Reactive } from '@legendapp/state/react';
6
- import { Dimensions, View } from 'react-native';
2
+ import { forwardRef, useRef, useMemo, useCallback, useEffect } from 'react';
3
+ import { ScrollView, View, StyleSheet, Dimensions } from 'react-native';
7
4
 
8
5
  // src/LegendList.tsx
9
- enableReactNativeComponents();
6
+ var LeanView = React2.forwardRef((props, ref) => {
7
+ return React2.createElement("RCTView", { ...props, ref });
8
+ });
9
+ LeanView.displayName = "RCTView";
10
+ var ContextListener = React2.createContext(null);
11
+ function StateProvider({ children }) {
12
+ const [value] = React2.useState(() => ({
13
+ listeners: /* @__PURE__ */ new Map(),
14
+ values: /* @__PURE__ */ new Map()
15
+ }));
16
+ return /* @__PURE__ */ React2.createElement(ContextListener.Provider, { value }, children);
17
+ }
18
+ function useStateContext() {
19
+ return React2.useContext(ContextListener);
20
+ }
21
+ function use$(signalName) {
22
+ const { listeners, values } = React2.useContext(ContextListener);
23
+ const [_, setState] = React2.useState(0);
24
+ React2.useMemo(() => {
25
+ const render = () => setState((prev) => prev > 1e4 ? 0 : prev + 1);
26
+ listeners.set(signalName, render);
27
+ }, []);
28
+ return values.get(signalName);
29
+ }
30
+ function peek$(signalName, ctx) {
31
+ const { values } = ctx || React2.useContext(ContextListener);
32
+ return values.get(signalName);
33
+ }
34
+ function set$(signalName, ctx, value) {
35
+ var _a;
36
+ const { listeners, values } = ctx || React2.useContext(ContextListener);
37
+ if (values.get(signalName) !== value) {
38
+ values.set(signalName, value);
39
+ (_a = listeners.get(signalName)) == null ? void 0 : _a();
40
+ }
41
+ }
42
+
43
+ // src/$View.tsx
44
+ function $View({ $key, $style, ...rest }) {
45
+ use$($key);
46
+ const style = $style();
47
+ return /* @__PURE__ */ React2.createElement(LeanView, { style, ...rest });
48
+ }
10
49
  var Container = ({
11
- $container,
50
+ id,
12
51
  recycleItems,
13
- listProps,
52
+ horizontal,
14
53
  getRenderedItem,
15
54
  onLayout,
16
55
  ItemSeparatorComponent
17
56
  }) => {
18
- const { horizontal } = listProps;
19
- const { id } = $container.peek();
20
- const itemIndex = use$($container.itemIndex);
21
- const key = recycleItems ? void 0 : itemIndex;
22
- const createStyle = () => horizontal ? {
23
- flexDirection: "row",
24
- position: "absolute",
25
- top: 0,
26
- bottom: 0,
27
- left: $container.position.get(),
28
- opacity: $container.position.get() < 0 ? 0 : 1
29
- } : {
30
- position: "absolute",
31
- left: 0,
32
- right: 0,
33
- top: $container.position.get(),
34
- opacity: $container.position.get() < 0 ? 0 : 1
57
+ const ctx = useStateContext();
58
+ const numItems = ItemSeparatorComponent ? use$("numItems") : 0;
59
+ const itemIndex = use$(`containerIndex${id}`);
60
+ if (itemIndex < 0) {
61
+ return null;
62
+ }
63
+ const createStyle = () => {
64
+ const position = peek$(`containerPosition${id}`, ctx);
65
+ return horizontal ? {
66
+ flexDirection: "row",
67
+ position: "absolute",
68
+ top: 0,
69
+ bottom: 0,
70
+ left: position,
71
+ opacity: position < 0 ? 0 : 1
72
+ } : {
73
+ position: "absolute",
74
+ left: 0,
75
+ right: 0,
76
+ top: position,
77
+ opacity: position < 0 ? 0 : 1
78
+ };
35
79
  };
36
- return itemIndex < 0 ? null : /* @__PURE__ */ React2.createElement(
37
- Reactive.View,
80
+ const renderedItem = getRenderedItem(itemIndex);
81
+ return /* @__PURE__ */ React2.createElement(
82
+ $View,
38
83
  {
39
- key: id,
84
+ $key: `containerPosition${id}`,
40
85
  $style: createStyle,
41
86
  onLayout: (event) => {
42
- const index = $container.itemIndex.peek();
87
+ const index = peek$(`containerIndex${id}`, ctx);
43
88
  const length = Math.round(event.nativeEvent.layout[horizontal ? "width" : "height"]);
44
89
  onLayout(index, length);
45
90
  }
46
91
  },
47
- /* @__PURE__ */ React2.createElement(View, { key }, getRenderedItem(itemIndex)),
48
- ItemSeparatorComponent && itemIndex !== listProps.data.length - 1 && /* @__PURE__ */ React2.createElement(Reactive.View, null, ItemSeparatorComponent)
92
+ recycleItems ? renderedItem : /* @__PURE__ */ React2.createElement(React2.Fragment, { key: itemIndex }, renderedItem),
93
+ ItemSeparatorComponent && itemIndex < numItems - 1 && ItemSeparatorComponent
49
94
  );
50
95
  };
51
96
 
97
+ // src/Containers.tsx
98
+ var Containers = React2.memo(function Containers2({
99
+ horizontal,
100
+ recycleItems,
101
+ ItemSeparatorComponent,
102
+ updateItemLength,
103
+ getRenderedItem
104
+ }) {
105
+ const ctx = useStateContext();
106
+ const numContainers = use$("numContainers");
107
+ const containers = [];
108
+ for (let i = 0; i < numContainers; i++) {
109
+ containers.push(
110
+ /* @__PURE__ */ React2.createElement(
111
+ Container,
112
+ {
113
+ id: i,
114
+ key: i,
115
+ recycleItems,
116
+ horizontal,
117
+ getRenderedItem,
118
+ onLayout: updateItemLength,
119
+ ItemSeparatorComponent
120
+ }
121
+ )
122
+ );
123
+ }
124
+ return /* @__PURE__ */ React2.createElement(
125
+ $View,
126
+ {
127
+ $key: "totalLength",
128
+ $style: () => horizontal ? {
129
+ width: peek$("totalLength", ctx)
130
+ } : {
131
+ height: peek$("totalLength", ctx)
132
+ }
133
+ },
134
+ containers
135
+ );
136
+ });
137
+
138
+ // src/ListComponent.tsx
139
+ var getComponent = (Component) => {
140
+ if (React2.isValidElement(Component)) {
141
+ return Component;
142
+ }
143
+ if (Component) {
144
+ return /* @__PURE__ */ React2.createElement(Component, null);
145
+ }
146
+ return null;
147
+ };
148
+ var ListComponent = React2.memo(function ListComponent2({
149
+ style,
150
+ contentContainerStyle,
151
+ horizontal,
152
+ initialContentOffset,
153
+ recycleItems,
154
+ ItemSeparatorComponent,
155
+ alignItemsAtEnd,
156
+ handleScroll,
157
+ onLayout,
158
+ ListHeaderComponent,
159
+ ListHeaderComponentStyle,
160
+ ListFooterComponent,
161
+ ListFooterComponentStyle,
162
+ getRenderedItem,
163
+ updateItemLength,
164
+ refScroller,
165
+ ...rest
166
+ }) {
167
+ const ctx = useStateContext();
168
+ return /* @__PURE__ */ React2.createElement(
169
+ ScrollView,
170
+ {
171
+ style,
172
+ contentContainerStyle: [
173
+ contentContainerStyle,
174
+ horizontal ? {
175
+ height: "100%"
176
+ } : {}
177
+ ],
178
+ onScroll: handleScroll,
179
+ onLayout,
180
+ scrollEventThrottle: 32,
181
+ horizontal,
182
+ contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
183
+ ...rest,
184
+ ref: refScroller
185
+ },
186
+ alignItemsAtEnd && /* @__PURE__ */ React2.createElement($View, { $key: "paddingTop", $style: () => ({ height: peek$("paddingTop", ctx) }) }),
187
+ ListHeaderComponent && /* @__PURE__ */ React2.createElement(View, { style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
188
+ /* @__PURE__ */ React2.createElement(
189
+ Containers,
190
+ {
191
+ horizontal,
192
+ recycleItems,
193
+ getRenderedItem,
194
+ ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
195
+ updateItemLength
196
+ }
197
+ ),
198
+ ListFooterComponent && /* @__PURE__ */ React2.createElement(View, { style: ListFooterComponentStyle }, getComponent(ListFooterComponent))
199
+ );
200
+ });
201
+
52
202
  // src/LegendList.tsx
53
- enableReactNativeComponents();
54
203
  var DEFAULT_SCROLL_BUFFER = 0;
55
204
  var POSITION_OUT_OF_VIEW = -1e4;
56
205
  var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
57
- const {
58
- data,
59
- initialScrollIndex,
60
- initialScrollOffset,
61
- horizontal,
62
- style,
63
- contentContainerStyle,
64
- initialContainers,
65
- drawDistance,
66
- recycleItems = true,
67
- onEndReachedThreshold,
68
- autoScrollToBottom = false,
69
- autoScrollToBottomThreshold = 0.1,
70
- startAtBottom = false,
71
- keyExtractor,
72
- renderItem,
73
- estimatedItemLength,
74
- onEndReached,
75
- onViewableRangeChanged,
76
- ListHeaderComponent,
77
- ListHeaderComponentStyle,
78
- ListFooterComponent,
79
- ListFooterComponentStyle,
80
- ItemSeparatorComponent,
81
- ...rest
82
- } = props;
83
- const internalRef = useRef(null);
84
- const refScroller = forwardedRef || internalRef;
85
- const containers$ = useObservable(() => []);
86
- const paddingTop$ = useObservable(0);
87
- const visibleRange$ = useObservable(() => ({
88
- start: 0,
89
- end: 0,
90
- totalLength: 0,
91
- scroll: 0,
92
- topPad: 0
93
- }));
94
- const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_SCROLL_BUFFER;
95
- const refPositions = useRef();
96
- const getId = (index) => {
97
- var _a;
98
- const data2 = (_a = refPositions.current) == null ? void 0 : _a.data;
99
- if (!data2) {
100
- return "";
101
- }
102
- const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
103
- return ret + "";
104
- };
105
- if (!refPositions.current) {
106
- refPositions.current = {
107
- lengths: /* @__PURE__ */ new Map(),
108
- positions: /* @__PURE__ */ new Map(),
109
- pendingAdjust: 0,
110
- animFrame: null,
111
- isStartReached: false,
112
- isEndReached: false,
113
- isAtBottom: false,
206
+ return /* @__PURE__ */ React2.createElement(StateProvider, null, /* @__PURE__ */ React2.createElement(LegendListInner, { ...props, ref: forwardedRef }));
207
+ });
208
+ var LegendListInner = forwardRef(
209
+ function LegendListInner2(props, forwardedRef) {
210
+ const {
114
211
  data,
115
- idsInFirstRender: void 0,
116
- hasScrolled: false,
117
- scrollLength: Dimensions.get("window")[horizontal ? "width" : "height"]
118
- };
119
- refPositions.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
120
- }
121
- refPositions.current.data = data;
122
- const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : initialScrollIndex ? initialScrollIndex * estimatedItemLength(initialScrollIndex) : void 0;
123
- const setTotalLength = (length) => {
124
- visibleRange$.totalLength.set(length);
125
- const screenLength = refPositions.current.scrollLength;
126
- if (startAtBottom) {
127
- const listPaddingTop = ((style == null ? void 0 : style.paddingTop) || 0) + ((contentContainerStyle == null ? void 0 : contentContainerStyle.paddingTop) || 0);
128
- paddingTop$.set(Math.max(0, screenLength - length - listPaddingTop));
129
- }
130
- };
131
- const allocateContainers = useCallback(() => {
132
- const scrollLength = refPositions.current.scrollLength;
133
- const numContainers = initialContainers || Math.ceil((scrollLength + scrollBuffer * 2) / estimatedItemLength(0)) + 4;
134
- const containers2 = [];
135
- for (let i = 0; i < numContainers; i++) {
136
- containers2.push({
137
- id: i,
138
- itemIndex: -1,
139
- position: POSITION_OUT_OF_VIEW
140
- });
141
- }
142
- containers$.set(containers2);
143
- }, []);
144
- const getRenderedItem = useCallback(
145
- (index) => {
212
+ initialScrollIndex,
213
+ initialScrollOffset,
214
+ horizontal,
215
+ style: styleProp,
216
+ contentContainerStyle: contentContainerStyleProp,
217
+ initialContainers,
218
+ drawDistance,
219
+ recycleItems = true,
220
+ onEndReachedThreshold = 0.5,
221
+ maintainScrollAtEnd = false,
222
+ maintainScrollAtEndThreshold = 0.1,
223
+ alignItemsAtEnd = false,
224
+ keyExtractor,
225
+ renderItem,
226
+ estimatedItemLength,
227
+ onEndReached,
228
+ onViewableRangeChanged,
229
+ ...rest
230
+ } = props;
231
+ const ctx = useStateContext();
232
+ const internalRef = useRef(null);
233
+ const refScroller = forwardedRef || internalRef;
234
+ const scrollBuffer = drawDistance != null ? drawDistance : DEFAULT_SCROLL_BUFFER;
235
+ const styleFlattened = StyleSheet.flatten(styleProp);
236
+ const style = useMemo(() => styleFlattened, [JSON.stringify(styleProp)]);
237
+ const contentContainerStyleFlattened = StyleSheet.flatten(contentContainerStyleProp);
238
+ const contentContainerStyle = useMemo(
239
+ () => contentContainerStyleFlattened,
240
+ [JSON.stringify(contentContainerStyleProp)]
241
+ );
242
+ const refState = useRef();
243
+ const getId = (index) => {
146
244
  var _a;
147
- const data2 = (_a = refPositions.current) == null ? void 0 : _a.data;
245
+ const data2 = (_a = refState.current) == null ? void 0 : _a.data;
148
246
  if (!data2) {
149
- return null;
247
+ return "";
150
248
  }
151
- const renderedItem = renderItem == null ? void 0 : renderItem({
152
- item: data2[index],
153
- index
154
- });
155
- return renderedItem;
156
- },
157
- [renderItem]
158
- );
159
- const calculateItemsInView = useCallback(() => {
160
- var _a, _b;
161
- const { data: data2, scrollLength } = refPositions.current;
162
- if (!data2) {
163
- return;
249
+ const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
250
+ return ret + "";
251
+ };
252
+ if (!refState.current) {
253
+ refState.current = {
254
+ lengths: /* @__PURE__ */ new Map(),
255
+ positions: /* @__PURE__ */ new Map(),
256
+ pendingAdjust: 0,
257
+ animFrame: null,
258
+ isStartReached: false,
259
+ isEndReached: false,
260
+ isAtBottom: false,
261
+ data,
262
+ idsInFirstRender: void 0,
263
+ hasScrolled: false,
264
+ scrollLength: Dimensions.get("window")[horizontal ? "width" : "height"],
265
+ startBuffered: 0,
266
+ startNoBuffer: 0,
267
+ endBuffered: 0,
268
+ endNoBuffer: 0,
269
+ scroll: 0,
270
+ topPad: 0
271
+ };
272
+ refState.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
164
273
  }
165
- const scroll = visibleRange$.scroll.peek() - visibleRange$.topPad.peek();
166
- const containers2 = containers$.peek();
167
- const { lengths, positions } = refPositions.current;
168
- let top = 0;
169
- let startNoBuffer = null;
170
- let startBuffered = null;
171
- let endNoBuffer = null;
172
- let endBuffered = null;
173
- const prevRange = onViewableRangeChanged ? { ...visibleRange$.peek() } : void 0;
174
- for (let i = 0; i < data2.length; i++) {
175
- const id = getId(i);
176
- const length = (_a = lengths.get(id)) != null ? _a : estimatedItemLength(i);
177
- if (positions.get(id) !== top) {
178
- positions.set(id, top);
274
+ refState.current.data = data;
275
+ set$(`numItems`, ctx, data.length);
276
+ const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : initialScrollIndex ? initialScrollIndex * estimatedItemLength(initialScrollIndex) : void 0;
277
+ const setTotalLength = (length) => {
278
+ set$(`totalLength`, ctx, length);
279
+ const screenLength = refState.current.scrollLength;
280
+ if (alignItemsAtEnd) {
281
+ const listPaddingTop = ((style == null ? void 0 : style.paddingTop) || 0) + ((contentContainerStyle == null ? void 0 : contentContainerStyle.paddingTop) || 0);
282
+ set$(`paddingTop`, ctx, Math.max(0, screenLength - length - listPaddingTop));
179
283
  }
180
- if (startNoBuffer === null && top + length > scroll) {
181
- startNoBuffer = i;
284
+ };
285
+ const allocateContainers = useCallback(() => {
286
+ const scrollLength = refState.current.scrollLength;
287
+ const numContainers = initialContainers || Math.ceil((scrollLength + scrollBuffer * 2) / estimatedItemLength(0)) + 4;
288
+ for (let i = 0; i < numContainers; i++) {
289
+ set$(`containerIndex${i}`, ctx, -1);
290
+ set$(`containerPosition${i}`, ctx, POSITION_OUT_OF_VIEW);
182
291
  }
183
- if (startBuffered === null && top + length > scroll - scrollBuffer) {
184
- startBuffered = i;
292
+ set$(`numContainers`, ctx, numContainers);
293
+ }, []);
294
+ const getRenderedItem = useCallback(
295
+ (index) => {
296
+ var _a;
297
+ const data2 = (_a = refState.current) == null ? void 0 : _a.data;
298
+ if (!data2) {
299
+ return null;
300
+ }
301
+ const renderedItem = renderItem == null ? void 0 : renderItem({
302
+ item: data2[index],
303
+ index
304
+ });
305
+ return renderedItem;
306
+ },
307
+ [renderItem]
308
+ );
309
+ const calculateItemsInView = useCallback(() => {
310
+ var _a, _b;
311
+ const {
312
+ data: data2,
313
+ scrollLength,
314
+ scroll: scrollState,
315
+ topPad,
316
+ startNoBuffer: startNoBufferState,
317
+ startBuffered: startBufferedState,
318
+ endNoBuffer: endNoBufferState,
319
+ endBuffered: endBufferedState
320
+ } = refState.current;
321
+ if (!data2) {
322
+ return;
185
323
  }
186
- if (startNoBuffer !== null) {
187
- if (top <= scroll + scrollLength) {
188
- endNoBuffer = i;
324
+ const scroll = scrollState - topPad;
325
+ const { lengths, positions } = refState.current;
326
+ let top = 0;
327
+ let startNoBuffer = null;
328
+ let startBuffered = null;
329
+ let endNoBuffer = null;
330
+ let endBuffered = null;
331
+ for (let i = 0; i < data2.length; i++) {
332
+ const id = getId(i);
333
+ const length = (_a = lengths.get(id)) != null ? _a : estimatedItemLength(i);
334
+ if (positions.get(id) !== top) {
335
+ positions.set(id, top);
189
336
  }
190
- if (top <= scroll + scrollLength + scrollBuffer) {
191
- endBuffered = i;
192
- } else {
193
- break;
337
+ if (startNoBuffer === null && top + length > scroll) {
338
+ startNoBuffer = i;
194
339
  }
195
- }
196
- top += length;
197
- }
198
- visibleRange$.assign({
199
- startBuffered,
200
- startNoBuffer,
201
- endBuffered,
202
- endNoBuffer
203
- });
204
- beginBatch();
205
- if (startBuffered !== null && endBuffered !== null) {
206
- for (let i = startBuffered; i <= endBuffered; i++) {
207
- let isContained = false;
208
- for (let j = 0; j < containers2.length; j++) {
209
- const container = containers2[j];
210
- if (container.itemIndex === i) {
211
- isContained = true;
340
+ if (startBuffered === null && top + length > scroll - scrollBuffer) {
341
+ startBuffered = i;
342
+ }
343
+ if (startNoBuffer !== null) {
344
+ if (top <= scroll + scrollLength) {
345
+ endNoBuffer = i;
346
+ }
347
+ if (top <= scroll + scrollLength + scrollBuffer) {
348
+ endBuffered = i;
349
+ } else {
212
350
  break;
213
351
  }
214
352
  }
215
- if (!isContained) {
216
- let didRecycle = false;
217
- for (let u = 0; u < containers2.length; u++) {
218
- const container = containers2[u];
219
- if (container.itemIndex < startBuffered || container.itemIndex > endBuffered) {
220
- containers$[u].itemIndex.set(i);
221
- didRecycle = true;
353
+ top += length;
354
+ }
355
+ Object.assign(refState.current, {
356
+ startBuffered,
357
+ startNoBuffer,
358
+ endBuffered,
359
+ endNoBuffer
360
+ });
361
+ if (startBuffered !== null && endBuffered !== null) {
362
+ const prevNumContainers = ctx.values.get("numContainers");
363
+ let numContainers = prevNumContainers;
364
+ for (let i = startBuffered; i <= endBuffered; i++) {
365
+ let isContained = false;
366
+ for (let j = 0; j < numContainers; j++) {
367
+ const index = peek$(`containerIndex${j}`, ctx);
368
+ if (index === i) {
369
+ isContained = true;
222
370
  break;
223
371
  }
224
372
  }
225
- if (!didRecycle) {
226
- if (__DEV__) {
227
- console.warn(
228
- "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemLength",
229
- i
230
- );
373
+ if (!isContained) {
374
+ let didRecycle = false;
375
+ for (let u = 0; u < numContainers; u++) {
376
+ const index = peek$(`containerIndex${u}`, ctx);
377
+ if (index < startBuffered || index > endBuffered) {
378
+ set$(`containerIndex${u}`, ctx, i);
379
+ didRecycle = true;
380
+ break;
381
+ }
382
+ }
383
+ if (!didRecycle) {
384
+ if (__DEV__) {
385
+ console.warn(
386
+ "[legend-list] No container to recycle, consider increasing initialContainers or estimatedItemLength",
387
+ i
388
+ );
389
+ }
390
+ const id = numContainers;
391
+ numContainers++;
392
+ set$(`containerIndex${id}`, ctx, i);
393
+ set$(`containerPosition${id}`, ctx, POSITION_OUT_OF_VIEW);
394
+ }
395
+ }
396
+ }
397
+ if (numContainers !== prevNumContainers) {
398
+ set$(`numContainers`, ctx, numContainers);
399
+ }
400
+ for (let i = 0; i < numContainers; i++) {
401
+ const itemIndex = peek$(`containerIndex${i}`, ctx);
402
+ const item = data2[itemIndex];
403
+ if (item) {
404
+ const id = getId(itemIndex);
405
+ if (itemIndex < startBuffered || itemIndex > endBuffered) {
406
+ set$(`containerPosition${i}`, ctx, POSITION_OUT_OF_VIEW);
407
+ } else {
408
+ const pos = (_b = positions.get(id)) != null ? _b : -1;
409
+ const prevPos = peek$(`containerPosition${i}`, ctx);
410
+ if (pos >= 0 && pos !== prevPos) {
411
+ set$(`containerPosition${i}`, ctx, pos);
412
+ }
231
413
  }
232
- containers$.push({
233
- id: containers$.peek().length,
234
- itemIndex: i,
235
- position: POSITION_OUT_OF_VIEW
414
+ }
415
+ }
416
+ if (onViewableRangeChanged) {
417
+ if (startNoBuffer !== startNoBufferState || startBuffered !== startBufferedState || endNoBuffer !== endNoBufferState || endBuffered !== endBufferedState) {
418
+ onViewableRangeChanged({
419
+ start: startNoBuffer,
420
+ startBuffered,
421
+ end: endNoBuffer,
422
+ endBuffered,
423
+ items: data2.slice(startNoBuffer, endNoBuffer + 1)
236
424
  });
237
425
  }
238
426
  }
239
427
  }
240
- for (let i = 0; i < containers2.length; i++) {
241
- const container = containers2[i];
242
- const item = data2[container.itemIndex];
243
- if (item) {
244
- const id = getId(container.itemIndex);
245
- if (container.itemIndex < startBuffered || container.itemIndex > endBuffered) {
246
- containers$[i].position.set(POSITION_OUT_OF_VIEW);
247
- } else {
248
- const pos = (_b = positions.get(id)) != null ? _b : -1;
249
- if (pos >= 0 && pos !== containers$[i].position.peek()) {
250
- containers$[i].position.set(pos);
251
- }
428
+ }, [data]);
429
+ useMemo(() => {
430
+ var _a, _b;
431
+ allocateContainers();
432
+ calculateItemsInView();
433
+ const lengths = (_a = refState.current) == null ? void 0 : _a.lengths;
434
+ let totalLength = 0;
435
+ for (let i = 0; i < data.length; i++) {
436
+ const id = getId(i);
437
+ totalLength += (_b = lengths.get(id)) != null ? _b : estimatedItemLength(i);
438
+ }
439
+ setTotalLength(totalLength);
440
+ }, []);
441
+ const checkAtBottom = () => {
442
+ var _a;
443
+ const { scrollLength, scroll } = refState.current;
444
+ const totalLength = peek$("totalLength", ctx);
445
+ const distanceFromEnd = totalLength - scroll - scrollLength;
446
+ if (refState.current) {
447
+ refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
448
+ }
449
+ if (onEndReached && !((_a = refState.current) == null ? void 0 : _a.isEndReached)) {
450
+ if (distanceFromEnd < onEndReachedThreshold * scrollLength) {
451
+ if (refState.current) {
452
+ refState.current.isEndReached = true;
252
453
  }
454
+ onEndReached({ distanceFromEnd });
253
455
  }
254
456
  }
255
- if (onViewableRangeChanged) {
256
- if (startNoBuffer !== (prevRange == null ? void 0 : prevRange.startNoBuffer) || startBuffered !== (prevRange == null ? void 0 : prevRange.startBuffered) || endNoBuffer !== (prevRange == null ? void 0 : prevRange.endNoBuffer) || endBuffered !== (prevRange == null ? void 0 : prevRange.endBuffered)) {
257
- onViewableRangeChanged({
258
- start: startNoBuffer,
259
- startBuffered,
260
- end: endNoBuffer,
261
- endBuffered,
262
- items: data2.slice(startNoBuffer, endNoBuffer + 1)
457
+ };
458
+ useMemo(() => {
459
+ if (refState.current) {
460
+ refState.current.isEndReached = false;
461
+ }
462
+ calculateItemsInView();
463
+ checkAtBottom();
464
+ }, [data]);
465
+ const updateItemLength = useCallback((index, length) => {
466
+ var _a, _b, _c, _d, _e;
467
+ const data2 = (_a = refState.current) == null ? void 0 : _a.data;
468
+ if (!data2) {
469
+ return;
470
+ }
471
+ const lengths = (_b = refState.current) == null ? void 0 : _b.lengths;
472
+ const id = getId(index);
473
+ const wasInFirstRender = (_c = refState.current) == null ? void 0 : _c.idsInFirstRender.has(id);
474
+ const prevLength = lengths.get(id) || (wasInFirstRender ? estimatedItemLength(index) : 0);
475
+ if (!prevLength || prevLength !== length) {
476
+ lengths.set(id, length);
477
+ const totalLength = peek$("totalLength", ctx);
478
+ setTotalLength(totalLength + (length - prevLength));
479
+ if (((_d = refState.current) == null ? void 0 : _d.isAtBottom) && maintainScrollAtEnd) {
480
+ requestAnimationFrame(() => {
481
+ var _a2;
482
+ (_a2 = refScroller.current) == null ? void 0 : _a2.scrollToEnd({
483
+ animated: true
484
+ });
263
485
  });
264
486
  }
265
- }
266
- }
267
- endBatch();
268
- }, [data]);
269
- useMemo(() => {
270
- var _a, _b;
271
- allocateContainers();
272
- calculateItemsInView();
273
- const lengths = (_a = refPositions.current) == null ? void 0 : _a.lengths;
274
- let totalLength = 0;
275
- for (let i = 0; i < data.length; i++) {
276
- const id = getId(i);
277
- totalLength += (_b = lengths.get(id)) != null ? _b : estimatedItemLength(i);
278
- }
279
- setTotalLength(totalLength);
280
- }, []);
281
- const checkAtBottom = () => {
282
- var _a;
283
- const scrollLength = refPositions.current.scrollLength;
284
- const newScroll = visibleRange$.scroll.peek();
285
- const distanceFromEnd = visibleRange$.totalLength.peek() - newScroll - scrollLength;
286
- if (refPositions.current) {
287
- refPositions.current.isAtBottom = distanceFromEnd < scrollLength * autoScrollToBottomThreshold;
288
- }
289
- if (onEndReached && !((_a = refPositions.current) == null ? void 0 : _a.isEndReached)) {
290
- if (distanceFromEnd < (onEndReachedThreshold || 0.5) * scrollLength) {
291
- if (refPositions.current) {
292
- refPositions.current.isEndReached = true;
487
+ if (!((_e = refState.current) == null ? void 0 : _e.animFrame)) {
488
+ calculateItemsInView();
293
489
  }
294
- onEndReached({ distanceFromEnd });
295
490
  }
296
- }
297
- };
298
- useMemo(() => {
299
- var _a;
300
- if (refPositions.current) {
301
- if (!((_a = refPositions.current) == null ? void 0 : _a.isAtBottom)) {
302
- refPositions.current.isEndReached = false;
491
+ }, []);
492
+ const handleScrollDebounced = useCallback(() => {
493
+ calculateItemsInView();
494
+ checkAtBottom();
495
+ if (refState.current) {
496
+ refState.current.animFrame = null;
303
497
  }
304
- }
305
- calculateItemsInView();
306
- checkAtBottom();
307
- }, [data]);
308
- const containers = use$(containers$, { shallow: true });
309
- const updateItemLength = useCallback((index, length) => {
310
- var _a, _b, _c, _d, _e;
311
- const data2 = (_a = refPositions.current) == null ? void 0 : _a.data;
312
- if (!data2) {
313
- return;
314
- }
315
- const lengths = (_b = refPositions.current) == null ? void 0 : _b.lengths;
316
- const id = getId(index);
317
- const wasInFirstRender = (_c = refPositions.current) == null ? void 0 : _c.idsInFirstRender.has(id);
318
- const prevLength = lengths.get(id) || (wasInFirstRender ? estimatedItemLength(index) : 0);
319
- if (!prevLength || prevLength !== length) {
320
- beginBatch();
321
- lengths.set(id, length);
322
- setTotalLength(visibleRange$.totalLength.peek() + (length - prevLength));
323
- if (((_d = refPositions.current) == null ? void 0 : _d.isAtBottom) && autoScrollToBottom) {
324
- requestAnimationFrame(() => {
325
- var _a2;
326
- (_a2 = refScroller.current) == null ? void 0 : _a2.scrollToEnd({
327
- animated: true
328
- });
329
- });
498
+ }, []);
499
+ const onLayout = useCallback((event) => {
500
+ const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
501
+ refState.current.scrollLength = scrollLength;
502
+ }, []);
503
+ const handleScroll = useCallback((event) => {
504
+ refState.current.hasScrolled = true;
505
+ const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
506
+ refState.current.scroll = newScroll;
507
+ if (refState.current && !refState.current.animFrame) {
508
+ refState.current.animFrame = requestAnimationFrame(handleScrollDebounced);
330
509
  }
331
- if (!((_e = refPositions.current) == null ? void 0 : _e.animFrame)) {
510
+ }, []);
511
+ useEffect(() => {
512
+ if (initialContentOffset) {
513
+ handleScroll({
514
+ nativeEvent: { contentOffset: { y: initialContentOffset } }
515
+ });
332
516
  calculateItemsInView();
333
517
  }
334
- endBatch();
335
- }
336
- }, []);
337
- const handleScrollDebounced = useCallback(() => {
338
- calculateItemsInView();
339
- checkAtBottom();
340
- if (refPositions.current) {
341
- refPositions.current.animFrame = null;
342
- }
343
- }, []);
344
- const onLayout = (event) => {
345
- const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
346
- refPositions.current.scrollLength = scrollLength;
347
- };
348
- const handleScroll = useCallback((event) => {
349
- refPositions.current.hasScrolled = true;
350
- const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
351
- visibleRange$.scroll.set(newScroll);
352
- if (refPositions.current && !refPositions.current.animFrame) {
353
- refPositions.current.animFrame = requestAnimationFrame(handleScrollDebounced);
354
- }
355
- }, []);
356
- useEffect(() => {
357
- if (initialContentOffset) {
358
- handleScroll({
359
- nativeEvent: { contentOffset: { y: initialContentOffset } }
360
- });
361
- calculateItemsInView();
362
- }
363
- }, []);
364
- return /* @__PURE__ */ React2.createElement(
365
- Reactive.ScrollView,
366
- {
367
- style,
368
- contentContainerStyle: [
369
- contentContainerStyle,
370
- horizontal ? {
371
- height: "100%"
372
- } : {}
373
- ],
374
- onScroll: handleScroll,
375
- onLayout,
376
- scrollEventThrottle: 32,
377
- horizontal,
378
- contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
379
- ...rest,
380
- ref: refScroller
381
- },
382
- startAtBottom && /* @__PURE__ */ React2.createElement(Reactive.View, { $style: () => ({ height: paddingTop$.get() }) }),
383
- ListHeaderComponent && /* @__PURE__ */ React2.createElement(Reactive.View, { $style: ListHeaderComponentStyle }, ListHeaderComponent),
384
- /* @__PURE__ */ React2.createElement(
385
- Reactive.View,
518
+ }, []);
519
+ return /* @__PURE__ */ React2.createElement(
520
+ ListComponent,
386
521
  {
387
- $style: () => horizontal ? {
388
- width: visibleRange$.totalLength.get()
389
- } : {
390
- height: visibleRange$.totalLength.get()
391
- }
392
- },
393
- containers.map((container, i) => /* @__PURE__ */ React2.createElement(
394
- Container,
395
- {
396
- key: container.id,
397
- recycleItems,
398
- $container: containers$[i],
399
- listProps: props,
400
- getRenderedItem,
401
- onLayout: updateItemLength,
402
- ItemSeparatorComponent
403
- }
404
- ))
405
- ),
406
- ListFooterComponent && /* @__PURE__ */ React2.createElement(Reactive.View, { $style: ListFooterComponentStyle }, ListFooterComponent)
407
- );
408
- });
522
+ ...rest,
523
+ contentContainerStyle,
524
+ style,
525
+ horizontal,
526
+ refScroller,
527
+ initialContentOffset,
528
+ getRenderedItem,
529
+ updateItemLength,
530
+ handleScroll,
531
+ onLayout,
532
+ recycleItems,
533
+ alignItemsAtEnd
534
+ }
535
+ );
536
+ }
537
+ );
409
538
 
410
539
  export { LegendList };