@legendapp/list 0.1.1 → 0.2.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/README.md +8 -0
- package/index.d.mts +30 -2
- package/index.d.ts +30 -2
- package/index.js +64 -40
- package/index.mjs +65 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,3 +70,11 @@ There's not a lot of code here so hopefully it's easy to contribute. If you want
|
|
|
70
70
|
- Other important missing features from FlatList or other lists libraries
|
|
71
71
|
- Optimizations:
|
|
72
72
|
- Loop over only potentially changed items when adjusting heights rather than data array
|
|
73
|
+
|
|
74
|
+
## Community
|
|
75
|
+
|
|
76
|
+
Join us on [Discord](https://discord.gg/tuW2pAffjA) to get involved with the Legend community.
|
|
77
|
+
|
|
78
|
+
## 👩⚖️ License
|
|
79
|
+
|
|
80
|
+
[MIT](LICENSE)
|
package/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ComponentProps, ReactNode } from 'react';
|
|
3
|
-
import
|
|
3
|
+
import * as react_native from 'react-native';
|
|
4
|
+
import { ScrollView, StyleProp, ViewStyle } from 'react-native';
|
|
4
5
|
|
|
5
6
|
type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset'> & {
|
|
6
7
|
data: ArrayLike<any> & T[];
|
|
@@ -12,6 +13,7 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
|
|
|
12
13
|
onEndReachedThreshold?: number | null | undefined;
|
|
13
14
|
autoScrollToBottom?: boolean;
|
|
14
15
|
autoScrollToBottomThreshold?: number;
|
|
16
|
+
startAtBottom?: boolean;
|
|
15
17
|
estimatedItemLength: (index: number) => number;
|
|
16
18
|
onEndReached?: ((info: {
|
|
17
19
|
distanceFromEnd: number;
|
|
@@ -19,6 +21,10 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
|
|
|
19
21
|
keyExtractor?: (item: T, index: number) => string;
|
|
20
22
|
renderItem?: (props: LegendListRenderItemInfo<T>) => ReactNode;
|
|
21
23
|
onViewableRangeChanged?: (range: ViewableRange<T>) => void;
|
|
24
|
+
ListHeaderComponent?: ReactNode;
|
|
25
|
+
ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
26
|
+
ListFooterComponent?: ReactNode;
|
|
27
|
+
ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
22
28
|
};
|
|
23
29
|
interface ViewableRange<T> {
|
|
24
30
|
startBuffered: number;
|
|
@@ -32,6 +38,28 @@ interface LegendListRenderItemInfo<ItemT> {
|
|
|
32
38
|
index: number;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
declare
|
|
41
|
+
declare const LegendList: React.ForwardRefExoticComponent<Omit<react_native.ScrollViewProps, "contentOffset"> & {
|
|
42
|
+
data: ArrayLike<any> & unknown[];
|
|
43
|
+
initialScrollOffset?: number;
|
|
44
|
+
initialScrollIndex?: number;
|
|
45
|
+
drawDistance?: number;
|
|
46
|
+
initialContainers?: number;
|
|
47
|
+
recycleItems?: boolean;
|
|
48
|
+
onEndReachedThreshold?: number | null | undefined;
|
|
49
|
+
autoScrollToBottom?: boolean;
|
|
50
|
+
autoScrollToBottomThreshold?: number;
|
|
51
|
+
startAtBottom?: boolean;
|
|
52
|
+
estimatedItemLength: (index: number) => number;
|
|
53
|
+
onEndReached?: ((info: {
|
|
54
|
+
distanceFromEnd: number;
|
|
55
|
+
}) => void) | null | undefined;
|
|
56
|
+
keyExtractor?: ((item: unknown, index: number) => string) | undefined;
|
|
57
|
+
renderItem?: ((props: LegendListRenderItemInfo<unknown>) => React.ReactNode) | undefined;
|
|
58
|
+
onViewableRangeChanged?: ((range: ViewableRange<unknown>) => void) | undefined;
|
|
59
|
+
ListHeaderComponent?: React.ReactNode;
|
|
60
|
+
ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
61
|
+
ListFooterComponent?: React.ReactNode;
|
|
62
|
+
ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
63
|
+
} & React.RefAttributes<ScrollView>>;
|
|
36
64
|
|
|
37
65
|
export { LegendList, type LegendListProps, type LegendListRenderItemInfo, type ViewableRange };
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { ComponentProps, ReactNode } from 'react';
|
|
3
|
-
import
|
|
3
|
+
import * as react_native from 'react-native';
|
|
4
|
+
import { ScrollView, StyleProp, ViewStyle } from 'react-native';
|
|
4
5
|
|
|
5
6
|
type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset'> & {
|
|
6
7
|
data: ArrayLike<any> & T[];
|
|
@@ -12,6 +13,7 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
|
|
|
12
13
|
onEndReachedThreshold?: number | null | undefined;
|
|
13
14
|
autoScrollToBottom?: boolean;
|
|
14
15
|
autoScrollToBottomThreshold?: number;
|
|
16
|
+
startAtBottom?: boolean;
|
|
15
17
|
estimatedItemLength: (index: number) => number;
|
|
16
18
|
onEndReached?: ((info: {
|
|
17
19
|
distanceFromEnd: number;
|
|
@@ -19,6 +21,10 @@ type LegendListProps<T> = Omit<ComponentProps<typeof ScrollView>, 'contentOffset
|
|
|
19
21
|
keyExtractor?: (item: T, index: number) => string;
|
|
20
22
|
renderItem?: (props: LegendListRenderItemInfo<T>) => ReactNode;
|
|
21
23
|
onViewableRangeChanged?: (range: ViewableRange<T>) => void;
|
|
24
|
+
ListHeaderComponent?: ReactNode;
|
|
25
|
+
ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
26
|
+
ListFooterComponent?: ReactNode;
|
|
27
|
+
ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
22
28
|
};
|
|
23
29
|
interface ViewableRange<T> {
|
|
24
30
|
startBuffered: number;
|
|
@@ -32,6 +38,28 @@ interface LegendListRenderItemInfo<ItemT> {
|
|
|
32
38
|
index: number;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
|
-
declare
|
|
41
|
+
declare const LegendList: React.ForwardRefExoticComponent<Omit<react_native.ScrollViewProps, "contentOffset"> & {
|
|
42
|
+
data: ArrayLike<any> & unknown[];
|
|
43
|
+
initialScrollOffset?: number;
|
|
44
|
+
initialScrollIndex?: number;
|
|
45
|
+
drawDistance?: number;
|
|
46
|
+
initialContainers?: number;
|
|
47
|
+
recycleItems?: boolean;
|
|
48
|
+
onEndReachedThreshold?: number | null | undefined;
|
|
49
|
+
autoScrollToBottom?: boolean;
|
|
50
|
+
autoScrollToBottomThreshold?: number;
|
|
51
|
+
startAtBottom?: boolean;
|
|
52
|
+
estimatedItemLength: (index: number) => number;
|
|
53
|
+
onEndReached?: ((info: {
|
|
54
|
+
distanceFromEnd: number;
|
|
55
|
+
}) => void) | null | undefined;
|
|
56
|
+
keyExtractor?: ((item: unknown, index: number) => string) | undefined;
|
|
57
|
+
renderItem?: ((props: LegendListRenderItemInfo<unknown>) => React.ReactNode) | undefined;
|
|
58
|
+
onViewableRangeChanged?: ((range: ViewableRange<unknown>) => void) | undefined;
|
|
59
|
+
ListHeaderComponent?: React.ReactNode;
|
|
60
|
+
ListHeaderComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
61
|
+
ListFooterComponent?: React.ReactNode;
|
|
62
|
+
ListFooterComponentStyle?: StyleProp<ViewStyle> | undefined;
|
|
63
|
+
} & React.RefAttributes<ScrollView>>;
|
|
36
64
|
|
|
37
65
|
export { LegendList, type LegendListProps, type LegendListRenderItemInfo, type ViewableRange };
|
package/index.js
CHANGED
|
@@ -71,7 +71,7 @@ var Container = ({
|
|
|
71
71
|
enableReactNativeComponents.enableReactNativeComponents();
|
|
72
72
|
var DEFAULT_SCROLL_BUFFER = 0;
|
|
73
73
|
var POSITION_OUT_OF_VIEW = -1e4;
|
|
74
|
-
|
|
74
|
+
var LegendList = React2.forwardRef(function LegendList2(props, forwardedRef) {
|
|
75
75
|
const {
|
|
76
76
|
data,
|
|
77
77
|
initialScrollIndex,
|
|
@@ -81,21 +81,26 @@ function LegendList(props) {
|
|
|
81
81
|
contentContainerStyle,
|
|
82
82
|
initialContainers,
|
|
83
83
|
drawDistance,
|
|
84
|
-
recycleItems =
|
|
84
|
+
recycleItems = true,
|
|
85
85
|
onEndReachedThreshold,
|
|
86
86
|
autoScrollToBottom = false,
|
|
87
87
|
autoScrollToBottomThreshold = 0.1,
|
|
88
|
+
startAtBottom = false,
|
|
88
89
|
keyExtractor,
|
|
89
90
|
renderItem,
|
|
90
91
|
estimatedItemLength,
|
|
91
92
|
onEndReached,
|
|
92
93
|
onViewableRangeChanged,
|
|
93
|
-
|
|
94
|
+
ListHeaderComponent,
|
|
95
|
+
ListHeaderComponentStyle,
|
|
96
|
+
ListFooterComponent,
|
|
97
|
+
ListFooterComponentStyle,
|
|
94
98
|
...rest
|
|
95
99
|
} = props;
|
|
96
|
-
const
|
|
97
|
-
const refScroller =
|
|
100
|
+
const internalRef = React2.useRef(null);
|
|
101
|
+
const refScroller = forwardedRef || internalRef;
|
|
98
102
|
const containers$ = react.useObservable(() => []);
|
|
103
|
+
const paddingTop$ = react.useObservable(0);
|
|
99
104
|
const visibleRange$ = react.useObservable(() => ({
|
|
100
105
|
start: 0,
|
|
101
106
|
end: 0,
|
|
@@ -111,7 +116,7 @@ function LegendList(props) {
|
|
|
111
116
|
if (!data2) {
|
|
112
117
|
return "";
|
|
113
118
|
}
|
|
114
|
-
const ret = index <
|
|
119
|
+
const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
|
|
115
120
|
return ret + "";
|
|
116
121
|
};
|
|
117
122
|
if (!refPositions.current) {
|
|
@@ -126,16 +131,23 @@ function LegendList(props) {
|
|
|
126
131
|
data,
|
|
127
132
|
idsInFirstRender: void 0,
|
|
128
133
|
hasScrolled: false,
|
|
129
|
-
|
|
134
|
+
scrollLength: reactNative.Dimensions.get("window")[horizontal ? "width" : "height"]
|
|
130
135
|
};
|
|
131
|
-
refPositions.current.idsInFirstRender = new Set(
|
|
136
|
+
refPositions.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
|
|
132
137
|
}
|
|
133
138
|
refPositions.current.data = data;
|
|
134
|
-
refPositions.current.childrenArray = childrenArray;
|
|
135
|
-
const SCREEN_LENGTH = reactNative.Dimensions.get("window")[horizontal ? "width" : "height"];
|
|
136
139
|
const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : initialScrollIndex ? initialScrollIndex * estimatedItemLength(initialScrollIndex) : void 0;
|
|
140
|
+
const setTotalLength = (length) => {
|
|
141
|
+
visibleRange$.totalLength.set(length);
|
|
142
|
+
const screenLength = refPositions.current.scrollLength;
|
|
143
|
+
if (startAtBottom) {
|
|
144
|
+
const listPaddingTop = ((style == null ? void 0 : style.paddingTop) || 0) + ((contentContainerStyle == null ? void 0 : contentContainerStyle.paddingTop) || 0);
|
|
145
|
+
paddingTop$.set(Math.max(0, screenLength - length - listPaddingTop));
|
|
146
|
+
}
|
|
147
|
+
};
|
|
137
148
|
const allocateContainers = React2.useCallback(() => {
|
|
138
|
-
const
|
|
149
|
+
const scrollLength = refPositions.current.scrollLength;
|
|
150
|
+
const numContainers = initialContainers || Math.ceil((scrollLength + scrollBuffer * 2) / estimatedItemLength(0)) + 4;
|
|
139
151
|
const containers2 = [];
|
|
140
152
|
for (let i = 0; i < numContainers; i++) {
|
|
141
153
|
containers2.push({
|
|
@@ -148,15 +160,10 @@ function LegendList(props) {
|
|
|
148
160
|
}, []);
|
|
149
161
|
const getRenderedItem = React2.useCallback(
|
|
150
162
|
(index) => {
|
|
151
|
-
var _a
|
|
163
|
+
var _a;
|
|
152
164
|
const data2 = (_a = refPositions.current) == null ? void 0 : _a.data;
|
|
153
165
|
if (!data2) {
|
|
154
|
-
|
|
155
|
-
if (childrenArray2) {
|
|
156
|
-
return childrenArray2[index];
|
|
157
|
-
} else {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
166
|
+
return null;
|
|
160
167
|
}
|
|
161
168
|
const renderedItem = renderItem == null ? void 0 : renderItem({
|
|
162
169
|
item: data2[index],
|
|
@@ -168,7 +175,7 @@ function LegendList(props) {
|
|
|
168
175
|
);
|
|
169
176
|
const calculateItemsInView = React2.useCallback(() => {
|
|
170
177
|
var _a, _b;
|
|
171
|
-
const data2 = refPositions.current
|
|
178
|
+
const { data: data2, scrollLength } = refPositions.current;
|
|
172
179
|
if (!data2) {
|
|
173
180
|
return;
|
|
174
181
|
}
|
|
@@ -194,10 +201,10 @@ function LegendList(props) {
|
|
|
194
201
|
startBuffered = i;
|
|
195
202
|
}
|
|
196
203
|
if (startNoBuffer !== null) {
|
|
197
|
-
if (top <= scroll +
|
|
204
|
+
if (top <= scroll + scrollLength) {
|
|
198
205
|
endNoBuffer = i;
|
|
199
206
|
}
|
|
200
|
-
if (top <= scroll +
|
|
207
|
+
if (top <= scroll + scrollLength + scrollBuffer) {
|
|
201
208
|
endBuffered = i;
|
|
202
209
|
} else {
|
|
203
210
|
break;
|
|
@@ -286,12 +293,34 @@ function LegendList(props) {
|
|
|
286
293
|
const id = getId(i);
|
|
287
294
|
totalLength += (_b = lengths.get(id)) != null ? _b : estimatedItemLength(i);
|
|
288
295
|
}
|
|
289
|
-
|
|
296
|
+
setTotalLength(totalLength);
|
|
290
297
|
}, []);
|
|
298
|
+
const checkAtBottom = () => {
|
|
299
|
+
var _a;
|
|
300
|
+
const scrollLength = refPositions.current.scrollLength;
|
|
301
|
+
const newScroll = visibleRange$.scroll.peek();
|
|
302
|
+
const distanceFromEnd = visibleRange$.totalLength.peek() - newScroll - scrollLength;
|
|
303
|
+
if (refPositions.current) {
|
|
304
|
+
refPositions.current.isAtBottom = distanceFromEnd < scrollLength * autoScrollToBottomThreshold;
|
|
305
|
+
}
|
|
306
|
+
if (onEndReached && !((_a = refPositions.current) == null ? void 0 : _a.isEndReached)) {
|
|
307
|
+
if (distanceFromEnd < (onEndReachedThreshold || 0.5) * scrollLength) {
|
|
308
|
+
if (refPositions.current) {
|
|
309
|
+
refPositions.current.isEndReached = true;
|
|
310
|
+
}
|
|
311
|
+
onEndReached({ distanceFromEnd });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
};
|
|
291
315
|
React2.useMemo(() => {
|
|
316
|
+
var _a;
|
|
292
317
|
if (refPositions.current) {
|
|
293
|
-
refPositions.current.
|
|
318
|
+
if (!((_a = refPositions.current) == null ? void 0 : _a.isAtBottom)) {
|
|
319
|
+
refPositions.current.isEndReached = false;
|
|
320
|
+
}
|
|
294
321
|
}
|
|
322
|
+
calculateItemsInView();
|
|
323
|
+
checkAtBottom();
|
|
295
324
|
}, [data]);
|
|
296
325
|
const containers = react.use$(containers$, { shallow: true });
|
|
297
326
|
const updateItemLength = React2.useCallback((index, length) => {
|
|
@@ -307,7 +336,7 @@ function LegendList(props) {
|
|
|
307
336
|
if (!prevLength || prevLength !== length) {
|
|
308
337
|
state.beginBatch();
|
|
309
338
|
lengths.set(id, length);
|
|
310
|
-
visibleRange$.totalLength.
|
|
339
|
+
setTotalLength(visibleRange$.totalLength.peek() + (length - prevLength));
|
|
311
340
|
if (((_d = refPositions.current) == null ? void 0 : _d.isAtBottom) && autoScrollToBottom) {
|
|
312
341
|
requestAnimationFrame(() => {
|
|
313
342
|
var _a2;
|
|
@@ -323,25 +352,16 @@ function LegendList(props) {
|
|
|
323
352
|
}
|
|
324
353
|
}, []);
|
|
325
354
|
const handleScrollDebounced = React2.useCallback(() => {
|
|
326
|
-
var _a;
|
|
327
|
-
const newScroll = visibleRange$.scroll.peek();
|
|
328
355
|
calculateItemsInView();
|
|
329
|
-
|
|
330
|
-
if (refPositions.current) {
|
|
331
|
-
refPositions.current.isAtBottom = distanceFromEnd < SCREEN_LENGTH * autoScrollToBottomThreshold;
|
|
332
|
-
}
|
|
333
|
-
if (onEndReached && !((_a = refPositions.current) == null ? void 0 : _a.isEndReached)) {
|
|
334
|
-
if (distanceFromEnd < (onEndReachedThreshold || 0.5) * SCREEN_LENGTH) {
|
|
335
|
-
if (refPositions.current) {
|
|
336
|
-
refPositions.current.isEndReached = true;
|
|
337
|
-
}
|
|
338
|
-
onEndReached({ distanceFromEnd });
|
|
339
|
-
}
|
|
340
|
-
}
|
|
356
|
+
checkAtBottom();
|
|
341
357
|
if (refPositions.current) {
|
|
342
358
|
refPositions.current.animFrame = null;
|
|
343
359
|
}
|
|
344
360
|
}, []);
|
|
361
|
+
const onLayout = (event) => {
|
|
362
|
+
const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
|
|
363
|
+
refPositions.current.scrollLength = scrollLength;
|
|
364
|
+
};
|
|
345
365
|
const handleScroll = React2.useCallback((event) => {
|
|
346
366
|
refPositions.current.hasScrolled = true;
|
|
347
367
|
const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
|
|
@@ -369,12 +389,15 @@ function LegendList(props) {
|
|
|
369
389
|
} : {}
|
|
370
390
|
],
|
|
371
391
|
onScroll: handleScroll,
|
|
392
|
+
onLayout,
|
|
372
393
|
scrollEventThrottle: 32,
|
|
373
394
|
horizontal,
|
|
374
395
|
contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
|
|
375
396
|
...rest,
|
|
376
397
|
ref: refScroller
|
|
377
398
|
},
|
|
399
|
+
startAtBottom && /* @__PURE__ */ React2__namespace.createElement(react.Reactive.View, { $style: () => ({ height: paddingTop$.get() }) }),
|
|
400
|
+
ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(react.Reactive.View, { $style: ListHeaderComponentStyle }, ListHeaderComponent),
|
|
378
401
|
/* @__PURE__ */ React2__namespace.createElement(
|
|
379
402
|
react.Reactive.View,
|
|
380
403
|
{
|
|
@@ -395,8 +418,9 @@ function LegendList(props) {
|
|
|
395
418
|
onLayout: updateItemLength
|
|
396
419
|
}
|
|
397
420
|
))
|
|
398
|
-
)
|
|
421
|
+
),
|
|
422
|
+
ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(react.Reactive.View, { $style: ListFooterComponentStyle }, ListFooterComponent)
|
|
399
423
|
);
|
|
400
|
-
}
|
|
424
|
+
});
|
|
401
425
|
|
|
402
426
|
exports.LegendList = LegendList;
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React2 from 'react';
|
|
2
|
-
import { useRef, useCallback, useMemo, useEffect } from 'react';
|
|
2
|
+
import { forwardRef, useRef, useCallback, useMemo, useEffect } from 'react';
|
|
3
3
|
import { beginBatch, endBatch } from '@legendapp/state';
|
|
4
4
|
import { enableReactNativeComponents } from '@legendapp/state/config/enableReactNativeComponents';
|
|
5
5
|
import { useObservable, use$, Reactive } from '@legendapp/state/react';
|
|
@@ -50,7 +50,7 @@ var Container = ({
|
|
|
50
50
|
enableReactNativeComponents();
|
|
51
51
|
var DEFAULT_SCROLL_BUFFER = 0;
|
|
52
52
|
var POSITION_OUT_OF_VIEW = -1e4;
|
|
53
|
-
|
|
53
|
+
var LegendList = forwardRef(function LegendList2(props, forwardedRef) {
|
|
54
54
|
const {
|
|
55
55
|
data,
|
|
56
56
|
initialScrollIndex,
|
|
@@ -60,21 +60,26 @@ function LegendList(props) {
|
|
|
60
60
|
contentContainerStyle,
|
|
61
61
|
initialContainers,
|
|
62
62
|
drawDistance,
|
|
63
|
-
recycleItems =
|
|
63
|
+
recycleItems = true,
|
|
64
64
|
onEndReachedThreshold,
|
|
65
65
|
autoScrollToBottom = false,
|
|
66
66
|
autoScrollToBottomThreshold = 0.1,
|
|
67
|
+
startAtBottom = false,
|
|
67
68
|
keyExtractor,
|
|
68
69
|
renderItem,
|
|
69
70
|
estimatedItemLength,
|
|
70
71
|
onEndReached,
|
|
71
72
|
onViewableRangeChanged,
|
|
72
|
-
|
|
73
|
+
ListHeaderComponent,
|
|
74
|
+
ListHeaderComponentStyle,
|
|
75
|
+
ListFooterComponent,
|
|
76
|
+
ListFooterComponentStyle,
|
|
73
77
|
...rest
|
|
74
78
|
} = props;
|
|
75
|
-
const
|
|
76
|
-
const refScroller =
|
|
79
|
+
const internalRef = useRef(null);
|
|
80
|
+
const refScroller = forwardedRef || internalRef;
|
|
77
81
|
const containers$ = useObservable(() => []);
|
|
82
|
+
const paddingTop$ = useObservable(0);
|
|
78
83
|
const visibleRange$ = useObservable(() => ({
|
|
79
84
|
start: 0,
|
|
80
85
|
end: 0,
|
|
@@ -90,7 +95,7 @@ function LegendList(props) {
|
|
|
90
95
|
if (!data2) {
|
|
91
96
|
return "";
|
|
92
97
|
}
|
|
93
|
-
const ret = index <
|
|
98
|
+
const ret = index < data2.length ? keyExtractor ? keyExtractor(data2[index], index) : index : null;
|
|
94
99
|
return ret + "";
|
|
95
100
|
};
|
|
96
101
|
if (!refPositions.current) {
|
|
@@ -105,16 +110,23 @@ function LegendList(props) {
|
|
|
105
110
|
data,
|
|
106
111
|
idsInFirstRender: void 0,
|
|
107
112
|
hasScrolled: false,
|
|
108
|
-
|
|
113
|
+
scrollLength: Dimensions.get("window")[horizontal ? "width" : "height"]
|
|
109
114
|
};
|
|
110
|
-
refPositions.current.idsInFirstRender = new Set(
|
|
115
|
+
refPositions.current.idsInFirstRender = new Set(data.map((_, i) => getId(i)));
|
|
111
116
|
}
|
|
112
117
|
refPositions.current.data = data;
|
|
113
|
-
refPositions.current.childrenArray = childrenArray;
|
|
114
|
-
const SCREEN_LENGTH = Dimensions.get("window")[horizontal ? "width" : "height"];
|
|
115
118
|
const initialContentOffset = initialScrollOffset != null ? initialScrollOffset : initialScrollIndex ? initialScrollIndex * estimatedItemLength(initialScrollIndex) : void 0;
|
|
119
|
+
const setTotalLength = (length) => {
|
|
120
|
+
visibleRange$.totalLength.set(length);
|
|
121
|
+
const screenLength = refPositions.current.scrollLength;
|
|
122
|
+
if (startAtBottom) {
|
|
123
|
+
const listPaddingTop = ((style == null ? void 0 : style.paddingTop) || 0) + ((contentContainerStyle == null ? void 0 : contentContainerStyle.paddingTop) || 0);
|
|
124
|
+
paddingTop$.set(Math.max(0, screenLength - length - listPaddingTop));
|
|
125
|
+
}
|
|
126
|
+
};
|
|
116
127
|
const allocateContainers = useCallback(() => {
|
|
117
|
-
const
|
|
128
|
+
const scrollLength = refPositions.current.scrollLength;
|
|
129
|
+
const numContainers = initialContainers || Math.ceil((scrollLength + scrollBuffer * 2) / estimatedItemLength(0)) + 4;
|
|
118
130
|
const containers2 = [];
|
|
119
131
|
for (let i = 0; i < numContainers; i++) {
|
|
120
132
|
containers2.push({
|
|
@@ -127,15 +139,10 @@ function LegendList(props) {
|
|
|
127
139
|
}, []);
|
|
128
140
|
const getRenderedItem = useCallback(
|
|
129
141
|
(index) => {
|
|
130
|
-
var _a
|
|
142
|
+
var _a;
|
|
131
143
|
const data2 = (_a = refPositions.current) == null ? void 0 : _a.data;
|
|
132
144
|
if (!data2) {
|
|
133
|
-
|
|
134
|
-
if (childrenArray2) {
|
|
135
|
-
return childrenArray2[index];
|
|
136
|
-
} else {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
145
|
+
return null;
|
|
139
146
|
}
|
|
140
147
|
const renderedItem = renderItem == null ? void 0 : renderItem({
|
|
141
148
|
item: data2[index],
|
|
@@ -147,7 +154,7 @@ function LegendList(props) {
|
|
|
147
154
|
);
|
|
148
155
|
const calculateItemsInView = useCallback(() => {
|
|
149
156
|
var _a, _b;
|
|
150
|
-
const data2 = refPositions.current
|
|
157
|
+
const { data: data2, scrollLength } = refPositions.current;
|
|
151
158
|
if (!data2) {
|
|
152
159
|
return;
|
|
153
160
|
}
|
|
@@ -173,10 +180,10 @@ function LegendList(props) {
|
|
|
173
180
|
startBuffered = i;
|
|
174
181
|
}
|
|
175
182
|
if (startNoBuffer !== null) {
|
|
176
|
-
if (top <= scroll +
|
|
183
|
+
if (top <= scroll + scrollLength) {
|
|
177
184
|
endNoBuffer = i;
|
|
178
185
|
}
|
|
179
|
-
if (top <= scroll +
|
|
186
|
+
if (top <= scroll + scrollLength + scrollBuffer) {
|
|
180
187
|
endBuffered = i;
|
|
181
188
|
} else {
|
|
182
189
|
break;
|
|
@@ -265,12 +272,34 @@ function LegendList(props) {
|
|
|
265
272
|
const id = getId(i);
|
|
266
273
|
totalLength += (_b = lengths.get(id)) != null ? _b : estimatedItemLength(i);
|
|
267
274
|
}
|
|
268
|
-
|
|
275
|
+
setTotalLength(totalLength);
|
|
269
276
|
}, []);
|
|
277
|
+
const checkAtBottom = () => {
|
|
278
|
+
var _a;
|
|
279
|
+
const scrollLength = refPositions.current.scrollLength;
|
|
280
|
+
const newScroll = visibleRange$.scroll.peek();
|
|
281
|
+
const distanceFromEnd = visibleRange$.totalLength.peek() - newScroll - scrollLength;
|
|
282
|
+
if (refPositions.current) {
|
|
283
|
+
refPositions.current.isAtBottom = distanceFromEnd < scrollLength * autoScrollToBottomThreshold;
|
|
284
|
+
}
|
|
285
|
+
if (onEndReached && !((_a = refPositions.current) == null ? void 0 : _a.isEndReached)) {
|
|
286
|
+
if (distanceFromEnd < (onEndReachedThreshold || 0.5) * scrollLength) {
|
|
287
|
+
if (refPositions.current) {
|
|
288
|
+
refPositions.current.isEndReached = true;
|
|
289
|
+
}
|
|
290
|
+
onEndReached({ distanceFromEnd });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
270
294
|
useMemo(() => {
|
|
295
|
+
var _a;
|
|
271
296
|
if (refPositions.current) {
|
|
272
|
-
refPositions.current.
|
|
297
|
+
if (!((_a = refPositions.current) == null ? void 0 : _a.isAtBottom)) {
|
|
298
|
+
refPositions.current.isEndReached = false;
|
|
299
|
+
}
|
|
273
300
|
}
|
|
301
|
+
calculateItemsInView();
|
|
302
|
+
checkAtBottom();
|
|
274
303
|
}, [data]);
|
|
275
304
|
const containers = use$(containers$, { shallow: true });
|
|
276
305
|
const updateItemLength = useCallback((index, length) => {
|
|
@@ -286,7 +315,7 @@ function LegendList(props) {
|
|
|
286
315
|
if (!prevLength || prevLength !== length) {
|
|
287
316
|
beginBatch();
|
|
288
317
|
lengths.set(id, length);
|
|
289
|
-
visibleRange$.totalLength.
|
|
318
|
+
setTotalLength(visibleRange$.totalLength.peek() + (length - prevLength));
|
|
290
319
|
if (((_d = refPositions.current) == null ? void 0 : _d.isAtBottom) && autoScrollToBottom) {
|
|
291
320
|
requestAnimationFrame(() => {
|
|
292
321
|
var _a2;
|
|
@@ -302,25 +331,16 @@ function LegendList(props) {
|
|
|
302
331
|
}
|
|
303
332
|
}, []);
|
|
304
333
|
const handleScrollDebounced = useCallback(() => {
|
|
305
|
-
var _a;
|
|
306
|
-
const newScroll = visibleRange$.scroll.peek();
|
|
307
334
|
calculateItemsInView();
|
|
308
|
-
|
|
309
|
-
if (refPositions.current) {
|
|
310
|
-
refPositions.current.isAtBottom = distanceFromEnd < SCREEN_LENGTH * autoScrollToBottomThreshold;
|
|
311
|
-
}
|
|
312
|
-
if (onEndReached && !((_a = refPositions.current) == null ? void 0 : _a.isEndReached)) {
|
|
313
|
-
if (distanceFromEnd < (onEndReachedThreshold || 0.5) * SCREEN_LENGTH) {
|
|
314
|
-
if (refPositions.current) {
|
|
315
|
-
refPositions.current.isEndReached = true;
|
|
316
|
-
}
|
|
317
|
-
onEndReached({ distanceFromEnd });
|
|
318
|
-
}
|
|
319
|
-
}
|
|
335
|
+
checkAtBottom();
|
|
320
336
|
if (refPositions.current) {
|
|
321
337
|
refPositions.current.animFrame = null;
|
|
322
338
|
}
|
|
323
339
|
}, []);
|
|
340
|
+
const onLayout = (event) => {
|
|
341
|
+
const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
|
|
342
|
+
refPositions.current.scrollLength = scrollLength;
|
|
343
|
+
};
|
|
324
344
|
const handleScroll = useCallback((event) => {
|
|
325
345
|
refPositions.current.hasScrolled = true;
|
|
326
346
|
const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
|
|
@@ -348,12 +368,15 @@ function LegendList(props) {
|
|
|
348
368
|
} : {}
|
|
349
369
|
],
|
|
350
370
|
onScroll: handleScroll,
|
|
371
|
+
onLayout,
|
|
351
372
|
scrollEventThrottle: 32,
|
|
352
373
|
horizontal,
|
|
353
374
|
contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
|
|
354
375
|
...rest,
|
|
355
376
|
ref: refScroller
|
|
356
377
|
},
|
|
378
|
+
startAtBottom && /* @__PURE__ */ React2.createElement(Reactive.View, { $style: () => ({ height: paddingTop$.get() }) }),
|
|
379
|
+
ListHeaderComponent && /* @__PURE__ */ React2.createElement(Reactive.View, { $style: ListHeaderComponentStyle }, ListHeaderComponent),
|
|
357
380
|
/* @__PURE__ */ React2.createElement(
|
|
358
381
|
Reactive.View,
|
|
359
382
|
{
|
|
@@ -374,8 +397,9 @@ function LegendList(props) {
|
|
|
374
397
|
onLayout: updateItemLength
|
|
375
398
|
}
|
|
376
399
|
))
|
|
377
|
-
)
|
|
400
|
+
),
|
|
401
|
+
ListFooterComponent && /* @__PURE__ */ React2.createElement(Reactive.View, { $style: ListFooterComponentStyle }, ListFooterComponent)
|
|
378
402
|
);
|
|
379
|
-
}
|
|
403
|
+
});
|
|
380
404
|
|
|
381
405
|
export { LegendList };
|