@legendapp/list 2.0.15 → 2.0.16
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/CHANGELOG.md +5 -0
- package/index.js +11 -9
- package/index.mjs +11 -9
- package/keyboard.d.mts +13 -0
- package/keyboard.d.ts +13 -0
- package/keyboard.js +113 -0
- package/keyboard.mjs +91 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
## 2.0.16
|
|
2
|
+
- Feat: Add KeyboardAvoidingLegendList component for better keyboard handling integration
|
|
3
|
+
- Fix: Stale containers are not being removed and overlap with new data when using getItemType #335
|
|
4
|
+
- Fix: Suppress keyExtractor warning when using lazy list mode #330
|
|
5
|
+
|
|
1
6
|
## 2.0.15
|
|
2
7
|
- Fix: Container allocation for sticky headers could duplicate containers, causing rendering issues
|
|
3
8
|
- Fix: Sticky positioned components scrolling out of viewport after scrolling distance exceeded 5000
|
package/index.js
CHANGED
|
@@ -1572,14 +1572,14 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
|
|
|
1572
1572
|
continue;
|
|
1573
1573
|
}
|
|
1574
1574
|
const key = peek$(ctx, `containerItemKey${u}`);
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1575
|
+
const requiredType = neededTypes[typeIndex];
|
|
1576
|
+
const isPending = key !== void 0 && pendingRemovalSet.has(u);
|
|
1577
|
+
const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
|
|
1578
|
+
if (canUse) {
|
|
1579
|
+
if (isPending) {
|
|
1580
|
+
pendingRemovalSet.delete(u);
|
|
1581
|
+
pendingRemovalChanged = true;
|
|
1582
|
+
}
|
|
1583
1583
|
result.push(u);
|
|
1584
1584
|
if (requiredItemTypes) {
|
|
1585
1585
|
typeIndex++;
|
|
@@ -2722,6 +2722,7 @@ var LegendList = typedMemo(
|
|
|
2722
2722
|
const isChildrenMode = children !== void 0 && dataProp === void 0;
|
|
2723
2723
|
const processedProps = isChildrenMode ? {
|
|
2724
2724
|
...restProps,
|
|
2725
|
+
childrenMode: true,
|
|
2725
2726
|
data: (isArray(children) ? children : React2__namespace.Children.toArray(children)).flat(1),
|
|
2726
2727
|
renderItem: ({ item }) => item
|
|
2727
2728
|
} : {
|
|
@@ -2788,6 +2789,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2788
2789
|
waitForInitialLayout = true,
|
|
2789
2790
|
...rest
|
|
2790
2791
|
} = props;
|
|
2792
|
+
const { childrenMode } = rest;
|
|
2791
2793
|
const [renderNum, setRenderNum] = React2.useState(0);
|
|
2792
2794
|
const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
|
|
2793
2795
|
const [canRender, setCanRender] = React2__namespace.useState(!IsNewArchitecture);
|
|
@@ -2960,7 +2962,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2960
2962
|
if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
|
|
2961
2963
|
refState.current.lastBatchingAction = Date.now();
|
|
2962
2964
|
if (!keyExtractorProp && !isFirst && didDataChange) {
|
|
2963
|
-
__DEV__ && warnDevOnce(
|
|
2965
|
+
__DEV__ && !childrenMode && warnDevOnce(
|
|
2964
2966
|
"keyExtractor",
|
|
2965
2967
|
"Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
|
|
2966
2968
|
);
|
package/index.mjs
CHANGED
|
@@ -1551,14 +1551,14 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
|
|
|
1551
1551
|
continue;
|
|
1552
1552
|
}
|
|
1553
1553
|
const key = peek$(ctx, `containerItemKey${u}`);
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1554
|
+
const requiredType = neededTypes[typeIndex];
|
|
1555
|
+
const isPending = key !== void 0 && pendingRemovalSet.has(u);
|
|
1556
|
+
const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
|
|
1557
|
+
if (canUse) {
|
|
1558
|
+
if (isPending) {
|
|
1559
|
+
pendingRemovalSet.delete(u);
|
|
1560
|
+
pendingRemovalChanged = true;
|
|
1561
|
+
}
|
|
1562
1562
|
result.push(u);
|
|
1563
1563
|
if (requiredItemTypes) {
|
|
1564
1564
|
typeIndex++;
|
|
@@ -2701,6 +2701,7 @@ var LegendList = typedMemo(
|
|
|
2701
2701
|
const isChildrenMode = children !== void 0 && dataProp === void 0;
|
|
2702
2702
|
const processedProps = isChildrenMode ? {
|
|
2703
2703
|
...restProps,
|
|
2704
|
+
childrenMode: true,
|
|
2704
2705
|
data: (isArray(children) ? children : React2.Children.toArray(children)).flat(1),
|
|
2705
2706
|
renderItem: ({ item }) => item
|
|
2706
2707
|
} : {
|
|
@@ -2767,6 +2768,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2767
2768
|
waitForInitialLayout = true,
|
|
2768
2769
|
...rest
|
|
2769
2770
|
} = props;
|
|
2771
|
+
const { childrenMode } = rest;
|
|
2770
2772
|
const [renderNum, setRenderNum] = useState(0);
|
|
2771
2773
|
const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
|
|
2772
2774
|
const [canRender, setCanRender] = React2.useState(!IsNewArchitecture);
|
|
@@ -2939,7 +2941,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
|
|
|
2939
2941
|
if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
|
|
2940
2942
|
refState.current.lastBatchingAction = Date.now();
|
|
2941
2943
|
if (!keyExtractorProp && !isFirst && didDataChange) {
|
|
2942
|
-
__DEV__ && warnDevOnce(
|
|
2944
|
+
__DEV__ && !childrenMode && warnDevOnce(
|
|
2943
2945
|
"keyExtractor",
|
|
2944
2946
|
"Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
|
|
2945
2947
|
);
|
package/keyboard.d.mts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Insets } from 'react-native';
|
|
3
|
+
import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/hook/commonTypes';
|
|
4
|
+
import { LegendListRef } from '@legendapp/list';
|
|
5
|
+
import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
|
|
6
|
+
|
|
7
|
+
declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "contentInset" | "onScroll"> & {
|
|
8
|
+
onScroll?: (event: ReanimatedScrollEvent) => void;
|
|
9
|
+
contentInset?: Insets;
|
|
10
|
+
safeAreaInsetBottom?: number;
|
|
11
|
+
} & React.RefAttributes<LegendListRef>) => React.ReactNode;
|
|
12
|
+
|
|
13
|
+
export { KeyboardAvoidingLegendList, KeyboardAvoidingLegendList as LegendList };
|
package/keyboard.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Insets } from 'react-native';
|
|
3
|
+
import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/hook/commonTypes';
|
|
4
|
+
import { LegendListRef } from '@legendapp/list';
|
|
5
|
+
import { AnimatedLegendListProps } from '@legendapp/list/reanimated';
|
|
6
|
+
|
|
7
|
+
declare const KeyboardAvoidingLegendList: <ItemT>(props: Omit<AnimatedLegendListProps<ItemT>, "contentInset" | "onScroll"> & {
|
|
8
|
+
onScroll?: (event: ReanimatedScrollEvent) => void;
|
|
9
|
+
contentInset?: Insets;
|
|
10
|
+
safeAreaInsetBottom?: number;
|
|
11
|
+
} & React.RefAttributes<LegendListRef>) => React.ReactNode;
|
|
12
|
+
|
|
13
|
+
export { KeyboardAvoidingLegendList, KeyboardAvoidingLegendList as LegendList };
|
package/keyboard.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var reactNative = require('react-native');
|
|
5
|
+
var reactNativeKeyboardController = require('react-native-keyboard-controller');
|
|
6
|
+
var reactNativeReanimated = require('react-native-reanimated');
|
|
7
|
+
var reanimated = require('@legendapp/list/reanimated');
|
|
8
|
+
|
|
9
|
+
function _interopNamespace(e) {
|
|
10
|
+
if (e && e.__esModule) return e;
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
28
|
+
|
|
29
|
+
// src/integrations/keyboard.tsx
|
|
30
|
+
var KeyboardAvoidingLegendList = React.forwardRef(function KeyboardAvoidingLegendList2(props, forwardedRef) {
|
|
31
|
+
const {
|
|
32
|
+
contentInset: contentInsetProp,
|
|
33
|
+
horizontal,
|
|
34
|
+
onScroll: onScrollProp,
|
|
35
|
+
scrollEventThrottle,
|
|
36
|
+
safeAreaInsetBottom = 0,
|
|
37
|
+
...rest
|
|
38
|
+
} = props;
|
|
39
|
+
const resolvedScrollEventThrottle = scrollEventThrottle != null ? scrollEventThrottle : 16;
|
|
40
|
+
const scrollViewRef = reactNativeReanimated.useAnimatedRef();
|
|
41
|
+
const scrollOffsetY = reactNativeReanimated.useSharedValue(0);
|
|
42
|
+
const scrollOffsetAtKeyboardStart = reactNativeReanimated.useSharedValue(0);
|
|
43
|
+
const keyboardInset = reactNativeReanimated.useSharedValue(0);
|
|
44
|
+
const scrollHandler = reactNativeReanimated.useAnimatedScrollHandler(
|
|
45
|
+
(event) => {
|
|
46
|
+
scrollOffsetY.value = event.contentOffset[horizontal ? "x" : "y"];
|
|
47
|
+
if (onScrollProp) {
|
|
48
|
+
reactNativeReanimated.runOnJS(onScrollProp)(event);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
[onScrollProp, horizontal]
|
|
52
|
+
);
|
|
53
|
+
reactNativeKeyboardController.useKeyboardHandler(
|
|
54
|
+
// biome-ignore assist/source/useSortedKeys: prefer start/move/end
|
|
55
|
+
{
|
|
56
|
+
onStart: () => {
|
|
57
|
+
"worklet";
|
|
58
|
+
scrollOffsetAtKeyboardStart.value = scrollOffsetY.value;
|
|
59
|
+
},
|
|
60
|
+
onMove: (event) => {
|
|
61
|
+
"worklet";
|
|
62
|
+
const targetOffset = scrollOffsetAtKeyboardStart.value + event.height;
|
|
63
|
+
scrollOffsetY.value = targetOffset;
|
|
64
|
+
reactNativeReanimated.scrollTo(scrollViewRef, 0, targetOffset, false);
|
|
65
|
+
if (!horizontal) {
|
|
66
|
+
keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
onEnd: (event) => {
|
|
70
|
+
"worklet";
|
|
71
|
+
const targetOffset = scrollOffsetAtKeyboardStart.value + event.height;
|
|
72
|
+
scrollOffsetY.value = targetOffset;
|
|
73
|
+
reactNativeReanimated.scrollTo(scrollViewRef, 0, targetOffset, false);
|
|
74
|
+
if (!horizontal) {
|
|
75
|
+
keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
[scrollViewRef, safeAreaInsetBottom]
|
|
80
|
+
);
|
|
81
|
+
const animatedProps = reactNative.Platform.OS === "ios" ? reactNativeReanimated.useAnimatedProps(() => {
|
|
82
|
+
"worklet";
|
|
83
|
+
var _a, _b, _c, _d;
|
|
84
|
+
return {
|
|
85
|
+
contentInset: {
|
|
86
|
+
bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.value),
|
|
87
|
+
left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
|
|
88
|
+
right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
|
|
89
|
+
top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}) : void 0;
|
|
93
|
+
const style = reactNative.Platform.OS !== "ios" ? reactNativeReanimated.useAnimatedStyle(() => ({
|
|
94
|
+
marginBottom: keyboardInset.value
|
|
95
|
+
})) : void 0;
|
|
96
|
+
return /* @__PURE__ */ React__namespace.createElement(
|
|
97
|
+
reanimated.AnimatedLegendList,
|
|
98
|
+
{
|
|
99
|
+
...rest,
|
|
100
|
+
animatedProps,
|
|
101
|
+
keyboardDismissMode: "interactive",
|
|
102
|
+
onScroll: scrollHandler,
|
|
103
|
+
ref: forwardedRef,
|
|
104
|
+
refScrollView: scrollViewRef,
|
|
105
|
+
scrollEventThrottle: resolvedScrollEventThrottle,
|
|
106
|
+
scrollIndicatorInsets: { bottom: 0, top: 0 },
|
|
107
|
+
style
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
exports.KeyboardAvoidingLegendList = KeyboardAvoidingLegendList;
|
|
113
|
+
exports.LegendList = KeyboardAvoidingLegendList;
|
package/keyboard.mjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { Platform } from 'react-native';
|
|
4
|
+
import { useKeyboardHandler } from 'react-native-keyboard-controller';
|
|
5
|
+
import { useAnimatedRef, useSharedValue, useAnimatedScrollHandler, runOnJS, scrollTo, useAnimatedProps, useAnimatedStyle } from 'react-native-reanimated';
|
|
6
|
+
import { AnimatedLegendList } from '@legendapp/list/reanimated';
|
|
7
|
+
|
|
8
|
+
// src/integrations/keyboard.tsx
|
|
9
|
+
var KeyboardAvoidingLegendList = forwardRef(function KeyboardAvoidingLegendList2(props, forwardedRef) {
|
|
10
|
+
const {
|
|
11
|
+
contentInset: contentInsetProp,
|
|
12
|
+
horizontal,
|
|
13
|
+
onScroll: onScrollProp,
|
|
14
|
+
scrollEventThrottle,
|
|
15
|
+
safeAreaInsetBottom = 0,
|
|
16
|
+
...rest
|
|
17
|
+
} = props;
|
|
18
|
+
const resolvedScrollEventThrottle = scrollEventThrottle != null ? scrollEventThrottle : 16;
|
|
19
|
+
const scrollViewRef = useAnimatedRef();
|
|
20
|
+
const scrollOffsetY = useSharedValue(0);
|
|
21
|
+
const scrollOffsetAtKeyboardStart = useSharedValue(0);
|
|
22
|
+
const keyboardInset = useSharedValue(0);
|
|
23
|
+
const scrollHandler = useAnimatedScrollHandler(
|
|
24
|
+
(event) => {
|
|
25
|
+
scrollOffsetY.value = event.contentOffset[horizontal ? "x" : "y"];
|
|
26
|
+
if (onScrollProp) {
|
|
27
|
+
runOnJS(onScrollProp)(event);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
[onScrollProp, horizontal]
|
|
31
|
+
);
|
|
32
|
+
useKeyboardHandler(
|
|
33
|
+
// biome-ignore assist/source/useSortedKeys: prefer start/move/end
|
|
34
|
+
{
|
|
35
|
+
onStart: () => {
|
|
36
|
+
"worklet";
|
|
37
|
+
scrollOffsetAtKeyboardStart.value = scrollOffsetY.value;
|
|
38
|
+
},
|
|
39
|
+
onMove: (event) => {
|
|
40
|
+
"worklet";
|
|
41
|
+
const targetOffset = scrollOffsetAtKeyboardStart.value + event.height;
|
|
42
|
+
scrollOffsetY.value = targetOffset;
|
|
43
|
+
scrollTo(scrollViewRef, 0, targetOffset, false);
|
|
44
|
+
if (!horizontal) {
|
|
45
|
+
keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
onEnd: (event) => {
|
|
49
|
+
"worklet";
|
|
50
|
+
const targetOffset = scrollOffsetAtKeyboardStart.value + event.height;
|
|
51
|
+
scrollOffsetY.value = targetOffset;
|
|
52
|
+
scrollTo(scrollViewRef, 0, targetOffset, false);
|
|
53
|
+
if (!horizontal) {
|
|
54
|
+
keyboardInset.value = Math.max(0, event.height - safeAreaInsetBottom);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
[scrollViewRef, safeAreaInsetBottom]
|
|
59
|
+
);
|
|
60
|
+
const animatedProps = Platform.OS === "ios" ? useAnimatedProps(() => {
|
|
61
|
+
"worklet";
|
|
62
|
+
var _a, _b, _c, _d;
|
|
63
|
+
return {
|
|
64
|
+
contentInset: {
|
|
65
|
+
bottom: ((_a = contentInsetProp == null ? void 0 : contentInsetProp.bottom) != null ? _a : 0) + (horizontal ? 0 : keyboardInset.value),
|
|
66
|
+
left: (_b = contentInsetProp == null ? void 0 : contentInsetProp.left) != null ? _b : 0,
|
|
67
|
+
right: (_c = contentInsetProp == null ? void 0 : contentInsetProp.right) != null ? _c : 0,
|
|
68
|
+
top: (_d = contentInsetProp == null ? void 0 : contentInsetProp.top) != null ? _d : 0
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}) : void 0;
|
|
72
|
+
const style = Platform.OS !== "ios" ? useAnimatedStyle(() => ({
|
|
73
|
+
marginBottom: keyboardInset.value
|
|
74
|
+
})) : void 0;
|
|
75
|
+
return /* @__PURE__ */ React.createElement(
|
|
76
|
+
AnimatedLegendList,
|
|
77
|
+
{
|
|
78
|
+
...rest,
|
|
79
|
+
animatedProps,
|
|
80
|
+
keyboardDismissMode: "interactive",
|
|
81
|
+
onScroll: scrollHandler,
|
|
82
|
+
ref: forwardedRef,
|
|
83
|
+
refScrollView: scrollViewRef,
|
|
84
|
+
scrollEventThrottle: resolvedScrollEventThrottle,
|
|
85
|
+
scrollIndicatorInsets: { bottom: 0, top: 0 },
|
|
86
|
+
style
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export { KeyboardAvoidingLegendList, KeyboardAvoidingLegendList as LegendList };
|
package/package.json
CHANGED