@legendapp/list 0.6.3 → 1.0.0-beta.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 +4 -1
- package/index.d.mts +14 -6
- package/index.d.ts +14 -6
- package/index.js +72 -59
- package/index.mjs +74 -61
- package/package.json +1 -1
- package/reanimated.d.mts +1 -1
- package/reanimated.d.ts +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,10 @@ In addition to normal FlatList features:
|
|
|
14
14
|
- `maintainScrollAtEnd`: If true and scroll is within `maintainScrollAtEndThreshold * screen height` then changing items or heights will scroll to the bottom. This can be useful for chat interfaces.
|
|
15
15
|
- `recycleItems` prop enables toggling recycling of list items. If enabled it will reuse item components for improved performance, but it will reuse any local state in items. So if you have local state in items you likely want this disabled.
|
|
16
16
|
|
|
17
|
+
## Documentation
|
|
18
|
+
|
|
19
|
+
See the [documentation site](https://www.legendapp.com/open-source/list) for the full documentation.
|
|
20
|
+
|
|
17
21
|
## Usage
|
|
18
22
|
|
|
19
23
|
## Install
|
|
@@ -41,7 +45,6 @@ interface PropsOptional {
|
|
|
41
45
|
initialScrollOffset?: number;
|
|
42
46
|
initialScrollIndex?: number;
|
|
43
47
|
drawDistance?: number;
|
|
44
|
-
initialContainers?: number;
|
|
45
48
|
recycleItems?: boolean;
|
|
46
49
|
onEndReachedThreshold?: number | null | undefined;
|
|
47
50
|
maintainScrollAtEnd?: boolean;
|
package/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ComponentProps, ReactNode, ForwardedRef, ReactElement } from 'react';
|
|
2
|
-
import { ScrollView, StyleProp, ViewStyle, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
|
|
2
|
+
import { ScrollView, StyleProp, ViewStyle, ScrollViewProps, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
|
|
3
3
|
import Animated from 'react-native-reanimated';
|
|
4
4
|
|
|
5
5
|
declare class ScrollAdjustHandler {
|
|
@@ -19,7 +19,6 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
|
|
|
19
19
|
initialScrollOffset?: number;
|
|
20
20
|
initialScrollIndex?: number;
|
|
21
21
|
drawDistance?: number;
|
|
22
|
-
initialNumContainers?: number;
|
|
23
22
|
recycleItems?: boolean;
|
|
24
23
|
onEndReachedThreshold?: number | null | undefined;
|
|
25
24
|
onStartReachedThreshold?: number | null | undefined;
|
|
@@ -29,6 +28,7 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
|
|
|
29
28
|
maintainVisibleContentPosition?: boolean;
|
|
30
29
|
numColumns?: number;
|
|
31
30
|
refScrollView?: React.Ref<ScrollView>;
|
|
31
|
+
waitForInitialLayout?: boolean;
|
|
32
32
|
estimatedItemSize?: number;
|
|
33
33
|
getEstimatedItemSize?: (index: number, item: ItemT) => number;
|
|
34
34
|
onStartReached?: ((info: {
|
|
@@ -49,6 +49,18 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
|
|
|
49
49
|
viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
|
|
50
50
|
viewabilityConfig?: ViewabilityConfig;
|
|
51
51
|
onViewableItemsChanged?: OnViewableItemsChanged | undefined;
|
|
52
|
+
onItemSizeChanged?: (info: {
|
|
53
|
+
size: number;
|
|
54
|
+
previous: number;
|
|
55
|
+
index: number;
|
|
56
|
+
itemKey: string;
|
|
57
|
+
itemData: ItemT;
|
|
58
|
+
}) => void;
|
|
59
|
+
/**
|
|
60
|
+
* Render custom ScrollView component.
|
|
61
|
+
* @default (props) => <ScrollView {...props} />
|
|
62
|
+
*/
|
|
63
|
+
renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
|
|
52
64
|
};
|
|
53
65
|
type LegendListProps<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof ScrollView>>;
|
|
54
66
|
interface InternalState {
|
|
@@ -89,10 +101,6 @@ interface InternalState {
|
|
|
89
101
|
timeoutSizeMessage: any;
|
|
90
102
|
nativeMarginTop: number;
|
|
91
103
|
indexByKey: Map<string, number>;
|
|
92
|
-
contentSize: {
|
|
93
|
-
width: number;
|
|
94
|
-
height: number;
|
|
95
|
-
};
|
|
96
104
|
viewabilityConfigCallbackPairs: ViewabilityConfigCallbackPairs | undefined;
|
|
97
105
|
renderItem: (props: LegendListRenderItemProps<any>) => ReactNode;
|
|
98
106
|
scrollHistory: Array<{
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ComponentProps, ReactNode, ForwardedRef, ReactElement } from 'react';
|
|
2
|
-
import { ScrollView, StyleProp, ViewStyle, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
|
|
2
|
+
import { ScrollView, StyleProp, ViewStyle, ScrollViewProps, ScrollViewComponent, ScrollResponderMixin } from 'react-native';
|
|
3
3
|
import Animated from 'react-native-reanimated';
|
|
4
4
|
|
|
5
5
|
declare class ScrollAdjustHandler {
|
|
@@ -19,7 +19,6 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
|
|
|
19
19
|
initialScrollOffset?: number;
|
|
20
20
|
initialScrollIndex?: number;
|
|
21
21
|
drawDistance?: number;
|
|
22
|
-
initialNumContainers?: number;
|
|
23
22
|
recycleItems?: boolean;
|
|
24
23
|
onEndReachedThreshold?: number | null | undefined;
|
|
25
24
|
onStartReachedThreshold?: number | null | undefined;
|
|
@@ -29,6 +28,7 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
|
|
|
29
28
|
maintainVisibleContentPosition?: boolean;
|
|
30
29
|
numColumns?: number;
|
|
31
30
|
refScrollView?: React.Ref<ScrollView>;
|
|
31
|
+
waitForInitialLayout?: boolean;
|
|
32
32
|
estimatedItemSize?: number;
|
|
33
33
|
getEstimatedItemSize?: (index: number, item: ItemT) => number;
|
|
34
34
|
onStartReached?: ((info: {
|
|
@@ -49,6 +49,18 @@ type LegendListPropsBase<ItemT, TScrollView extends ComponentProps<typeof Scroll
|
|
|
49
49
|
viewabilityConfigCallbackPairs?: ViewabilityConfigCallbackPairs | undefined;
|
|
50
50
|
viewabilityConfig?: ViewabilityConfig;
|
|
51
51
|
onViewableItemsChanged?: OnViewableItemsChanged | undefined;
|
|
52
|
+
onItemSizeChanged?: (info: {
|
|
53
|
+
size: number;
|
|
54
|
+
previous: number;
|
|
55
|
+
index: number;
|
|
56
|
+
itemKey: string;
|
|
57
|
+
itemData: ItemT;
|
|
58
|
+
}) => void;
|
|
59
|
+
/**
|
|
60
|
+
* Render custom ScrollView component.
|
|
61
|
+
* @default (props) => <ScrollView {...props} />
|
|
62
|
+
*/
|
|
63
|
+
renderScrollComponent?: (props: ScrollViewProps) => React.ReactElement<ScrollViewProps>;
|
|
52
64
|
};
|
|
53
65
|
type LegendListProps<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof ScrollView>>;
|
|
54
66
|
interface InternalState {
|
|
@@ -89,10 +101,6 @@ interface InternalState {
|
|
|
89
101
|
timeoutSizeMessage: any;
|
|
90
102
|
nativeMarginTop: number;
|
|
91
103
|
indexByKey: Map<string, number>;
|
|
92
|
-
contentSize: {
|
|
93
|
-
width: number;
|
|
94
|
-
height: number;
|
|
95
|
-
};
|
|
96
104
|
viewabilityConfigCallbackPairs: ViewabilityConfigCallbackPairs | undefined;
|
|
97
105
|
renderItem: (props: LegendListRenderItemProps<any>) => ReactNode;
|
|
98
106
|
scrollHistory: Array<{
|
package/index.js
CHANGED
|
@@ -77,10 +77,13 @@ function set$(ctx, signalName, value) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
// src/Container.tsx
|
|
80
82
|
var Container = ({
|
|
81
83
|
id,
|
|
82
84
|
recycleItems,
|
|
83
85
|
horizontal,
|
|
86
|
+
waitForInitialLayout,
|
|
84
87
|
getRenderedItem,
|
|
85
88
|
updateItemSize,
|
|
86
89
|
ItemSeparatorComponent
|
|
@@ -88,11 +91,10 @@ var Container = ({
|
|
|
88
91
|
const ctx = useStateContext();
|
|
89
92
|
const position = use$(`containerPosition${id}`);
|
|
90
93
|
const column = use$(`containerColumn${id}`) || 0;
|
|
91
|
-
const visible = use$(`containerDidLayout${id}`);
|
|
92
94
|
const numColumns = use$("numColumns");
|
|
93
95
|
const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
|
|
94
96
|
const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
|
|
95
|
-
|
|
97
|
+
const style = horizontal ? {
|
|
96
98
|
flexDirection: "row",
|
|
97
99
|
position: "absolute",
|
|
98
100
|
top: otherAxisPos,
|
|
@@ -106,7 +108,8 @@ var Container = ({
|
|
|
106
108
|
width: otherAxisSize,
|
|
107
109
|
top: position
|
|
108
110
|
};
|
|
109
|
-
{
|
|
111
|
+
if (waitForInitialLayout) {
|
|
112
|
+
const visible = use$(`containerDidLayout${id}`);
|
|
110
113
|
style.opacity = visible ? 1 : 0;
|
|
111
114
|
}
|
|
112
115
|
const lastItemKey = use$("lastItemKey");
|
|
@@ -146,6 +149,7 @@ var Containers = React4__namespace.memo(function Containers2({
|
|
|
146
149
|
horizontal,
|
|
147
150
|
recycleItems,
|
|
148
151
|
ItemSeparatorComponent,
|
|
152
|
+
waitForInitialLayout,
|
|
149
153
|
updateItemSize,
|
|
150
154
|
getRenderedItem
|
|
151
155
|
}) {
|
|
@@ -161,6 +165,7 @@ var Containers = React4__namespace.memo(function Containers2({
|
|
|
161
165
|
key: i,
|
|
162
166
|
recycleItems,
|
|
163
167
|
horizontal,
|
|
168
|
+
waitForInitialLayout,
|
|
164
169
|
getRenderedItem,
|
|
165
170
|
updateItemSize,
|
|
166
171
|
ItemSeparatorComponent
|
|
@@ -190,6 +195,7 @@ var ListComponent = React4__namespace.memo(function ListComponent2({
|
|
|
190
195
|
recycleItems,
|
|
191
196
|
ItemSeparatorComponent,
|
|
192
197
|
alignItemsAtEnd,
|
|
198
|
+
waitForInitialLayout,
|
|
193
199
|
handleScroll,
|
|
194
200
|
onLayout,
|
|
195
201
|
ListHeaderComponent,
|
|
@@ -202,14 +208,19 @@ var ListComponent = React4__namespace.memo(function ListComponent2({
|
|
|
202
208
|
updateItemSize,
|
|
203
209
|
refScrollView,
|
|
204
210
|
maintainVisibleContentPosition,
|
|
211
|
+
renderScrollComponent,
|
|
205
212
|
...rest
|
|
206
213
|
}) {
|
|
207
214
|
const ctx = useStateContext();
|
|
208
215
|
const animPaddingTop = useValue$("paddingTop");
|
|
209
216
|
const animScrollAdjust = useValue$("scrollAdjust");
|
|
217
|
+
const ScrollComponent = renderScrollComponent ? React4.useMemo(
|
|
218
|
+
() => React4__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
|
|
219
|
+
[renderScrollComponent]
|
|
220
|
+
) : reactNative.ScrollView;
|
|
210
221
|
const additionalSize = { marginTop: animScrollAdjust, paddingTop: animPaddingTop };
|
|
211
222
|
return /* @__PURE__ */ React4__namespace.createElement(
|
|
212
|
-
|
|
223
|
+
ScrollComponent,
|
|
213
224
|
{
|
|
214
225
|
...rest,
|
|
215
226
|
style,
|
|
@@ -247,6 +258,7 @@ var ListComponent = React4__namespace.memo(function ListComponent2({
|
|
|
247
258
|
{
|
|
248
259
|
horizontal,
|
|
249
260
|
recycleItems,
|
|
261
|
+
waitForInitialLayout,
|
|
250
262
|
getRenderedItem,
|
|
251
263
|
ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
|
|
252
264
|
updateItemSize
|
|
@@ -462,7 +474,6 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
462
474
|
initialScrollIndex,
|
|
463
475
|
initialScrollOffset,
|
|
464
476
|
horizontal,
|
|
465
|
-
initialNumContainers,
|
|
466
477
|
drawDistance = 250,
|
|
467
478
|
recycleItems = false,
|
|
468
479
|
onEndReachedThreshold = 0.5,
|
|
@@ -480,6 +491,8 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
480
491
|
onEndReached,
|
|
481
492
|
onStartReached,
|
|
482
493
|
ListEmptyComponent,
|
|
494
|
+
onItemSizeChanged,
|
|
495
|
+
scrollEventThrottle,
|
|
483
496
|
refScrollView,
|
|
484
497
|
...rest
|
|
485
498
|
} = props;
|
|
@@ -557,7 +570,6 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
557
570
|
indexByKey: /* @__PURE__ */ new Map(),
|
|
558
571
|
scrollHistory: [],
|
|
559
572
|
scrollVelocity: 0,
|
|
560
|
-
contentSize: { width: 0, height: 0 },
|
|
561
573
|
sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
|
|
562
574
|
timeoutSizeMessage: 0,
|
|
563
575
|
scrollTimer: void 0,
|
|
@@ -619,21 +631,18 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
619
631
|
state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
|
|
620
632
|
state.rowHeights.clear();
|
|
621
633
|
}
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
doAdd();
|
|
634
|
+
const totalSize = state.totalSize;
|
|
635
|
+
let resultSize = totalSize;
|
|
636
|
+
if (applyAdjustValue !== void 0) {
|
|
637
|
+
resultSize -= applyAdjustValue;
|
|
638
|
+
refState.current.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
|
|
639
|
+
state.scroll -= diff;
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
set$(ctx, "totalSize", resultSize);
|
|
643
|
+
if (alignItemsAtEnd) {
|
|
644
|
+
doUpdatePaddingTop();
|
|
645
|
+
}
|
|
637
646
|
}, []);
|
|
638
647
|
const getRowHeight = (n) => {
|
|
639
648
|
const { rowHeights } = refState.current;
|
|
@@ -864,7 +873,7 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
864
873
|
set$(ctx, `containerColumn${containerId}`, -1);
|
|
865
874
|
if (__DEV__ && numContainers > peek$(ctx, "numContainersPooled")) {
|
|
866
875
|
console.warn(
|
|
867
|
-
"[legend-list] No container to recycle,
|
|
876
|
+
"[legend-list] No container to recycle, so creating one on demand. This can be a performance issue and is likely caused by the estimatedItemSize being too small. Consider increasing estimatedItemSize. numContainers:",
|
|
868
877
|
numContainers
|
|
869
878
|
);
|
|
870
879
|
}
|
|
@@ -936,25 +945,25 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
936
945
|
}
|
|
937
946
|
};
|
|
938
947
|
const doMaintainScrollAtEnd = (animated) => {
|
|
939
|
-
|
|
940
|
-
if ((
|
|
941
|
-
|
|
948
|
+
const state = refState.current;
|
|
949
|
+
if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd) {
|
|
950
|
+
state.scroll = state.totalSize - state.scrollLength + peek$(ctx, "paddingTop");
|
|
942
951
|
requestAnimationFrame(() => {
|
|
943
|
-
var
|
|
944
|
-
(
|
|
952
|
+
var _a2;
|
|
953
|
+
(_a2 = refScroller.current) == null ? void 0 : _a2.scrollToEnd({
|
|
945
954
|
animated
|
|
946
955
|
});
|
|
947
956
|
});
|
|
957
|
+
return true;
|
|
948
958
|
}
|
|
949
959
|
};
|
|
950
960
|
const checkAtBottom = () => {
|
|
951
961
|
if (!refState.current) {
|
|
952
962
|
return;
|
|
953
963
|
}
|
|
954
|
-
const { scrollLength, scroll,
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const distanceFromEnd = contentLength - scroll - scrollLength;
|
|
964
|
+
const { scrollLength, scroll, totalSize } = refState.current;
|
|
965
|
+
if (totalSize > 0) {
|
|
966
|
+
const distanceFromEnd = totalSize - scroll - scrollLength;
|
|
958
967
|
if (refState.current) {
|
|
959
968
|
refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
|
|
960
969
|
}
|
|
@@ -996,29 +1005,31 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
996
1005
|
}
|
|
997
1006
|
}
|
|
998
1007
|
};
|
|
999
|
-
const
|
|
1008
|
+
const checkResetContainers = (reset) => {
|
|
1000
1009
|
const state = refState.current;
|
|
1001
1010
|
if (state) {
|
|
1002
|
-
if (data.length > state.data.length) {
|
|
1003
|
-
state.isEndReached = false;
|
|
1004
|
-
}
|
|
1005
|
-
refState.current.scrollForNextCalculateItemsInView = void 0;
|
|
1006
1011
|
state.data = data;
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
const
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1012
|
+
if (reset) {
|
|
1013
|
+
refState.current.scrollForNextCalculateItemsInView = void 0;
|
|
1014
|
+
const numContainers = peek$(ctx, "numContainers");
|
|
1015
|
+
for (let i = 0; i < numContainers; i++) {
|
|
1016
|
+
const itemKey = peek$(ctx, `containerItemKey${i}`);
|
|
1017
|
+
if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
|
|
1018
|
+
set$(ctx, `containerItemKey${i}`, void 0);
|
|
1019
|
+
set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
|
|
1020
|
+
set$(ctx, `containerColumn${i}`, -1);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (!keyExtractorProp) {
|
|
1024
|
+
state.sizes.clear();
|
|
1025
|
+
state.positions;
|
|
1014
1026
|
}
|
|
1027
|
+
calculateItemsInView(state.scrollVelocity);
|
|
1015
1028
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
state.
|
|
1029
|
+
const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
|
|
1030
|
+
if (!didMaintainScrollAtEnd && data.length > state.data.length) {
|
|
1031
|
+
state.isEndReached = false;
|
|
1019
1032
|
}
|
|
1020
|
-
calculateItemsInView(state.scrollVelocity);
|
|
1021
|
-
doMaintainScrollAtEnd(false);
|
|
1022
1033
|
checkAtTop();
|
|
1023
1034
|
checkAtBottom();
|
|
1024
1035
|
}
|
|
@@ -1058,9 +1069,10 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
1058
1069
|
addTotalSize(null, totalSize, totalSizeBelowIndex);
|
|
1059
1070
|
}
|
|
1060
1071
|
React4.useEffect(() => {
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1072
|
+
checkResetContainers(
|
|
1073
|
+
/*reset*/
|
|
1074
|
+
!isFirst
|
|
1075
|
+
);
|
|
1064
1076
|
}, [isFirst, data, numColumnsProp]);
|
|
1065
1077
|
refState.current.renderItem = renderItem;
|
|
1066
1078
|
const lastItemKey = getId(data[data.length - 1]);
|
|
@@ -1174,7 +1186,7 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
1174
1186
|
refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
|
|
1175
1187
|
const scrollLength = refState.current.scrollLength;
|
|
1176
1188
|
const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
|
|
1177
|
-
const numContainers =
|
|
1189
|
+
const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
|
|
1178
1190
|
for (let i = 0; i < numContainers; i++) {
|
|
1179
1191
|
set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
|
|
1180
1192
|
set$(ctx, `containerColumn${i}`, -1);
|
|
@@ -1250,6 +1262,9 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
1250
1262
|
calculateItemsInView(state.scrollVelocity);
|
|
1251
1263
|
}
|
|
1252
1264
|
}
|
|
1265
|
+
if (onItemSizeChanged) {
|
|
1266
|
+
onItemSizeChanged({ size, previous: prevSize, index, itemKey, itemData: data2[index] });
|
|
1267
|
+
}
|
|
1253
1268
|
} else {
|
|
1254
1269
|
set$(ctx, `containerDidLayout${containerId}`, true);
|
|
1255
1270
|
}
|
|
@@ -1262,12 +1277,10 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
1262
1277
|
const onLayout = React4.useCallback((event) => {
|
|
1263
1278
|
const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
|
|
1264
1279
|
refState.current.scrollLength = scrollLength;
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
checkAtTop();
|
|
1270
|
-
}
|
|
1280
|
+
doMaintainScrollAtEnd(false);
|
|
1281
|
+
doUpdatePaddingTop();
|
|
1282
|
+
checkAtBottom();
|
|
1283
|
+
checkAtTop();
|
|
1271
1284
|
if (__DEV__) {
|
|
1272
1285
|
const isWidthZero = event.nativeEvent.layout.width === 0;
|
|
1273
1286
|
const isHeightZero = event.nativeEvent.layout.height === 0;
|
|
@@ -1286,7 +1299,6 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
1286
1299
|
}
|
|
1287
1300
|
const state = refState.current;
|
|
1288
1301
|
state.hasScrolled = true;
|
|
1289
|
-
state.contentSize = event.nativeEvent.contentSize;
|
|
1290
1302
|
const currentTime = performance.now();
|
|
1291
1303
|
const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
|
|
1292
1304
|
if (!(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
|
|
@@ -1374,6 +1386,7 @@ var LegendListInner = React4.forwardRef(function LegendListInner2(props, forward
|
|
|
1374
1386
|
alignItemsAtEnd,
|
|
1375
1387
|
ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
|
|
1376
1388
|
maintainVisibleContentPosition,
|
|
1389
|
+
scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : reactNative.Platform.OS === "web" ? 16 : void 0,
|
|
1377
1390
|
style
|
|
1378
1391
|
}
|
|
1379
1392
|
);
|
package/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React4 from 'react';
|
|
2
|
-
import React4__default, { forwardRef, useRef,
|
|
3
|
-
import { Animated, ScrollView, View, Dimensions, StyleSheet, useAnimatedValue as useAnimatedValue$1 } from 'react-native';
|
|
2
|
+
import React4__default, { useMemo, forwardRef, useRef, useCallback, useEffect, useImperativeHandle, useSyncExternalStore, useState } from 'react';
|
|
3
|
+
import { Animated, ScrollView, View, Dimensions, StyleSheet, Platform, useAnimatedValue as useAnimatedValue$1 } from 'react-native';
|
|
4
4
|
|
|
5
5
|
// src/LegendList.tsx
|
|
6
6
|
var ContextState = React4.createContext(null);
|
|
@@ -56,10 +56,13 @@ function set$(ctx, signalName, value) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
// src/Container.tsx
|
|
59
61
|
var Container = ({
|
|
60
62
|
id,
|
|
61
63
|
recycleItems,
|
|
62
64
|
horizontal,
|
|
65
|
+
waitForInitialLayout,
|
|
63
66
|
getRenderedItem,
|
|
64
67
|
updateItemSize,
|
|
65
68
|
ItemSeparatorComponent
|
|
@@ -67,11 +70,10 @@ var Container = ({
|
|
|
67
70
|
const ctx = useStateContext();
|
|
68
71
|
const position = use$(`containerPosition${id}`);
|
|
69
72
|
const column = use$(`containerColumn${id}`) || 0;
|
|
70
|
-
const visible = use$(`containerDidLayout${id}`);
|
|
71
73
|
const numColumns = use$("numColumns");
|
|
72
74
|
const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
|
|
73
75
|
const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
|
|
74
|
-
|
|
76
|
+
const style = horizontal ? {
|
|
75
77
|
flexDirection: "row",
|
|
76
78
|
position: "absolute",
|
|
77
79
|
top: otherAxisPos,
|
|
@@ -85,7 +87,8 @@ var Container = ({
|
|
|
85
87
|
width: otherAxisSize,
|
|
86
88
|
top: position
|
|
87
89
|
};
|
|
88
|
-
{
|
|
90
|
+
if (waitForInitialLayout) {
|
|
91
|
+
const visible = use$(`containerDidLayout${id}`);
|
|
89
92
|
style.opacity = visible ? 1 : 0;
|
|
90
93
|
}
|
|
91
94
|
const lastItemKey = use$("lastItemKey");
|
|
@@ -125,6 +128,7 @@ var Containers = React4.memo(function Containers2({
|
|
|
125
128
|
horizontal,
|
|
126
129
|
recycleItems,
|
|
127
130
|
ItemSeparatorComponent,
|
|
131
|
+
waitForInitialLayout,
|
|
128
132
|
updateItemSize,
|
|
129
133
|
getRenderedItem
|
|
130
134
|
}) {
|
|
@@ -140,6 +144,7 @@ var Containers = React4.memo(function Containers2({
|
|
|
140
144
|
key: i,
|
|
141
145
|
recycleItems,
|
|
142
146
|
horizontal,
|
|
147
|
+
waitForInitialLayout,
|
|
143
148
|
getRenderedItem,
|
|
144
149
|
updateItemSize,
|
|
145
150
|
ItemSeparatorComponent
|
|
@@ -169,6 +174,7 @@ var ListComponent = React4.memo(function ListComponent2({
|
|
|
169
174
|
recycleItems,
|
|
170
175
|
ItemSeparatorComponent,
|
|
171
176
|
alignItemsAtEnd,
|
|
177
|
+
waitForInitialLayout,
|
|
172
178
|
handleScroll,
|
|
173
179
|
onLayout,
|
|
174
180
|
ListHeaderComponent,
|
|
@@ -181,14 +187,19 @@ var ListComponent = React4.memo(function ListComponent2({
|
|
|
181
187
|
updateItemSize,
|
|
182
188
|
refScrollView,
|
|
183
189
|
maintainVisibleContentPosition,
|
|
190
|
+
renderScrollComponent,
|
|
184
191
|
...rest
|
|
185
192
|
}) {
|
|
186
193
|
const ctx = useStateContext();
|
|
187
194
|
const animPaddingTop = useValue$("paddingTop");
|
|
188
195
|
const animScrollAdjust = useValue$("scrollAdjust");
|
|
196
|
+
const ScrollComponent = renderScrollComponent ? useMemo(
|
|
197
|
+
() => React4.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
|
|
198
|
+
[renderScrollComponent]
|
|
199
|
+
) : ScrollView;
|
|
189
200
|
const additionalSize = { marginTop: animScrollAdjust, paddingTop: animPaddingTop };
|
|
190
201
|
return /* @__PURE__ */ React4.createElement(
|
|
191
|
-
|
|
202
|
+
ScrollComponent,
|
|
192
203
|
{
|
|
193
204
|
...rest,
|
|
194
205
|
style,
|
|
@@ -226,6 +237,7 @@ var ListComponent = React4.memo(function ListComponent2({
|
|
|
226
237
|
{
|
|
227
238
|
horizontal,
|
|
228
239
|
recycleItems,
|
|
240
|
+
waitForInitialLayout,
|
|
229
241
|
getRenderedItem,
|
|
230
242
|
ItemSeparatorComponent: ItemSeparatorComponent && getComponent(ItemSeparatorComponent),
|
|
231
243
|
updateItemSize
|
|
@@ -441,7 +453,6 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
441
453
|
initialScrollIndex,
|
|
442
454
|
initialScrollOffset,
|
|
443
455
|
horizontal,
|
|
444
|
-
initialNumContainers,
|
|
445
456
|
drawDistance = 250,
|
|
446
457
|
recycleItems = false,
|
|
447
458
|
onEndReachedThreshold = 0.5,
|
|
@@ -459,6 +470,8 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
459
470
|
onEndReached,
|
|
460
471
|
onStartReached,
|
|
461
472
|
ListEmptyComponent,
|
|
473
|
+
onItemSizeChanged,
|
|
474
|
+
scrollEventThrottle,
|
|
462
475
|
refScrollView,
|
|
463
476
|
...rest
|
|
464
477
|
} = props;
|
|
@@ -536,7 +549,6 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
536
549
|
indexByKey: /* @__PURE__ */ new Map(),
|
|
537
550
|
scrollHistory: [],
|
|
538
551
|
scrollVelocity: 0,
|
|
539
|
-
contentSize: { width: 0, height: 0 },
|
|
540
552
|
sizesLaidOut: __DEV__ ? /* @__PURE__ */ new Map() : void 0,
|
|
541
553
|
timeoutSizeMessage: 0,
|
|
542
554
|
scrollTimer: void 0,
|
|
@@ -598,21 +610,18 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
598
610
|
state.belowAnchorElementPositions = buildElementPositionsBelowAnchor();
|
|
599
611
|
state.rowHeights.clear();
|
|
600
612
|
}
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
}
|
|
614
|
-
};
|
|
615
|
-
doAdd();
|
|
613
|
+
const totalSize = state.totalSize;
|
|
614
|
+
let resultSize = totalSize;
|
|
615
|
+
if (applyAdjustValue !== void 0) {
|
|
616
|
+
resultSize -= applyAdjustValue;
|
|
617
|
+
refState.current.scrollAdjustHandler.requestAdjust(applyAdjustValue, (diff) => {
|
|
618
|
+
state.scroll -= diff;
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
set$(ctx, "totalSize", resultSize);
|
|
622
|
+
if (alignItemsAtEnd) {
|
|
623
|
+
doUpdatePaddingTop();
|
|
624
|
+
}
|
|
616
625
|
}, []);
|
|
617
626
|
const getRowHeight = (n) => {
|
|
618
627
|
const { rowHeights } = refState.current;
|
|
@@ -843,7 +852,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
843
852
|
set$(ctx, `containerColumn${containerId}`, -1);
|
|
844
853
|
if (__DEV__ && numContainers > peek$(ctx, "numContainersPooled")) {
|
|
845
854
|
console.warn(
|
|
846
|
-
"[legend-list] No container to recycle,
|
|
855
|
+
"[legend-list] No container to recycle, so creating one on demand. This can be a performance issue and is likely caused by the estimatedItemSize being too small. Consider increasing estimatedItemSize. numContainers:",
|
|
847
856
|
numContainers
|
|
848
857
|
);
|
|
849
858
|
}
|
|
@@ -915,25 +924,25 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
915
924
|
}
|
|
916
925
|
};
|
|
917
926
|
const doMaintainScrollAtEnd = (animated) => {
|
|
918
|
-
|
|
919
|
-
if ((
|
|
920
|
-
|
|
927
|
+
const state = refState.current;
|
|
928
|
+
if ((state == null ? void 0 : state.isAtBottom) && maintainScrollAtEnd) {
|
|
929
|
+
state.scroll = state.totalSize - state.scrollLength + peek$(ctx, "paddingTop");
|
|
921
930
|
requestAnimationFrame(() => {
|
|
922
|
-
var
|
|
923
|
-
(
|
|
931
|
+
var _a2;
|
|
932
|
+
(_a2 = refScroller.current) == null ? void 0 : _a2.scrollToEnd({
|
|
924
933
|
animated
|
|
925
934
|
});
|
|
926
935
|
});
|
|
936
|
+
return true;
|
|
927
937
|
}
|
|
928
938
|
};
|
|
929
939
|
const checkAtBottom = () => {
|
|
930
940
|
if (!refState.current) {
|
|
931
941
|
return;
|
|
932
942
|
}
|
|
933
|
-
const { scrollLength, scroll,
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
const distanceFromEnd = contentLength - scroll - scrollLength;
|
|
943
|
+
const { scrollLength, scroll, totalSize } = refState.current;
|
|
944
|
+
if (totalSize > 0) {
|
|
945
|
+
const distanceFromEnd = totalSize - scroll - scrollLength;
|
|
937
946
|
if (refState.current) {
|
|
938
947
|
refState.current.isAtBottom = distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
|
|
939
948
|
}
|
|
@@ -975,29 +984,31 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
975
984
|
}
|
|
976
985
|
}
|
|
977
986
|
};
|
|
978
|
-
const
|
|
987
|
+
const checkResetContainers = (reset) => {
|
|
979
988
|
const state = refState.current;
|
|
980
989
|
if (state) {
|
|
981
|
-
if (data.length > state.data.length) {
|
|
982
|
-
state.isEndReached = false;
|
|
983
|
-
}
|
|
984
|
-
refState.current.scrollForNextCalculateItemsInView = void 0;
|
|
985
990
|
state.data = data;
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
const
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
991
|
+
if (reset) {
|
|
992
|
+
refState.current.scrollForNextCalculateItemsInView = void 0;
|
|
993
|
+
const numContainers = peek$(ctx, "numContainers");
|
|
994
|
+
for (let i = 0; i < numContainers; i++) {
|
|
995
|
+
const itemKey = peek$(ctx, `containerItemKey${i}`);
|
|
996
|
+
if (!keyExtractorProp || itemKey && state.indexByKey.get(itemKey) === void 0) {
|
|
997
|
+
set$(ctx, `containerItemKey${i}`, void 0);
|
|
998
|
+
set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
|
|
999
|
+
set$(ctx, `containerColumn${i}`, -1);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
if (!keyExtractorProp) {
|
|
1003
|
+
state.sizes.clear();
|
|
1004
|
+
state.positions;
|
|
993
1005
|
}
|
|
1006
|
+
calculateItemsInView(state.scrollVelocity);
|
|
994
1007
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
state.
|
|
1008
|
+
const didMaintainScrollAtEnd = doMaintainScrollAtEnd(false);
|
|
1009
|
+
if (!didMaintainScrollAtEnd && data.length > state.data.length) {
|
|
1010
|
+
state.isEndReached = false;
|
|
998
1011
|
}
|
|
999
|
-
calculateItemsInView(state.scrollVelocity);
|
|
1000
|
-
doMaintainScrollAtEnd(false);
|
|
1001
1012
|
checkAtTop();
|
|
1002
1013
|
checkAtBottom();
|
|
1003
1014
|
}
|
|
@@ -1037,9 +1048,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
1037
1048
|
addTotalSize(null, totalSize, totalSizeBelowIndex);
|
|
1038
1049
|
}
|
|
1039
1050
|
useEffect(() => {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1051
|
+
checkResetContainers(
|
|
1052
|
+
/*reset*/
|
|
1053
|
+
!isFirst
|
|
1054
|
+
);
|
|
1043
1055
|
}, [isFirst, data, numColumnsProp]);
|
|
1044
1056
|
refState.current.renderItem = renderItem;
|
|
1045
1057
|
const lastItemKey = getId(data[data.length - 1]);
|
|
@@ -1153,7 +1165,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
1153
1165
|
refState.current.viewabilityConfigCallbackPairs = setupViewability(props);
|
|
1154
1166
|
const scrollLength = refState.current.scrollLength;
|
|
1155
1167
|
const averageItemSize = (_a2 = estimatedItemSize != null ? estimatedItemSize : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(0, data[0])) != null ? _a2 : DEFAULT_ITEM_SIZE;
|
|
1156
|
-
const numContainers =
|
|
1168
|
+
const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize) * numColumnsProp;
|
|
1157
1169
|
for (let i = 0; i < numContainers; i++) {
|
|
1158
1170
|
set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
|
|
1159
1171
|
set$(ctx, `containerColumn${i}`, -1);
|
|
@@ -1229,6 +1241,9 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
1229
1241
|
calculateItemsInView(state.scrollVelocity);
|
|
1230
1242
|
}
|
|
1231
1243
|
}
|
|
1244
|
+
if (onItemSizeChanged) {
|
|
1245
|
+
onItemSizeChanged({ size, previous: prevSize, index, itemKey, itemData: data2[index] });
|
|
1246
|
+
}
|
|
1232
1247
|
} else {
|
|
1233
1248
|
set$(ctx, `containerDidLayout${containerId}`, true);
|
|
1234
1249
|
}
|
|
@@ -1241,12 +1256,10 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
1241
1256
|
const onLayout = useCallback((event) => {
|
|
1242
1257
|
const scrollLength = event.nativeEvent.layout[horizontal ? "width" : "height"];
|
|
1243
1258
|
refState.current.scrollLength = scrollLength;
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
checkAtTop();
|
|
1249
|
-
}
|
|
1259
|
+
doMaintainScrollAtEnd(false);
|
|
1260
|
+
doUpdatePaddingTop();
|
|
1261
|
+
checkAtBottom();
|
|
1262
|
+
checkAtTop();
|
|
1250
1263
|
if (__DEV__) {
|
|
1251
1264
|
const isWidthZero = event.nativeEvent.layout.width === 0;
|
|
1252
1265
|
const isHeightZero = event.nativeEvent.layout.height === 0;
|
|
@@ -1265,7 +1278,6 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
1265
1278
|
}
|
|
1266
1279
|
const state = refState.current;
|
|
1267
1280
|
state.hasScrolled = true;
|
|
1268
|
-
state.contentSize = event.nativeEvent.contentSize;
|
|
1269
1281
|
const currentTime = performance.now();
|
|
1270
1282
|
const newScroll = event.nativeEvent.contentOffset[horizontal ? "x" : "y"];
|
|
1271
1283
|
if (!(state.scrollHistory.length === 0 && newScroll === initialContentOffset)) {
|
|
@@ -1353,6 +1365,7 @@ var LegendListInner = forwardRef(function LegendListInner2(props, forwardedRef)
|
|
|
1353
1365
|
alignItemsAtEnd,
|
|
1354
1366
|
ListEmptyComponent: data.length === 0 ? ListEmptyComponent : void 0,
|
|
1355
1367
|
maintainVisibleContentPosition,
|
|
1368
|
+
scrollEventThrottle: scrollEventThrottle != null ? scrollEventThrottle : Platform.OS === "web" ? 16 : void 0,
|
|
1356
1369
|
style
|
|
1357
1370
|
}
|
|
1358
1371
|
);
|
package/package.json
CHANGED
package/reanimated.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import { LegendListRef, LegendListPropsBase } from '@legendapp/list';
|
|
|
2
2
|
import React__default, { ComponentProps } from 'react';
|
|
3
3
|
import Animated from 'react-native-reanimated';
|
|
4
4
|
|
|
5
|
-
type KeysToOmit = "getEstimatedItemSize" | "keyExtractor" | "animatedProps" | "renderItem";
|
|
5
|
+
type KeysToOmit = "getEstimatedItemSize" | "keyExtractor" | "animatedProps" | "renderItem" | "onItemSizeChanged";
|
|
6
6
|
type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Animated.ScrollView>>;
|
|
7
7
|
interface AnimatedLegendListProps<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
|
|
8
8
|
refScrollView?: React__default.Ref<Animated.ScrollView>;
|
package/reanimated.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { LegendListRef, LegendListPropsBase } from '@legendapp/list';
|
|
|
2
2
|
import React__default, { ComponentProps } from 'react';
|
|
3
3
|
import Animated from 'react-native-reanimated';
|
|
4
4
|
|
|
5
|
-
type KeysToOmit = "getEstimatedItemSize" | "keyExtractor" | "animatedProps" | "renderItem";
|
|
5
|
+
type KeysToOmit = "getEstimatedItemSize" | "keyExtractor" | "animatedProps" | "renderItem" | "onItemSizeChanged";
|
|
6
6
|
type PropsBase<ItemT> = LegendListPropsBase<ItemT, ComponentProps<typeof Animated.ScrollView>>;
|
|
7
7
|
interface AnimatedLegendListProps<ItemT> extends Omit<PropsBase<ItemT>, KeysToOmit> {
|
|
8
8
|
refScrollView?: React__default.Ref<Animated.ScrollView>;
|