@legendapp/list 0.2.2 → 0.3.1

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