@r0b0t3d/react-native-collapsible 1.5.3 → 1.6.0-alpha.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/lib/commonjs/components/CollapsibleContainer.js +19 -21
- package/lib/commonjs/components/CollapsibleContainer.js.map +1 -1
- package/lib/commonjs/components/CollapsibleView.js +38 -30
- package/lib/commonjs/components/CollapsibleView.js.map +1 -1
- package/lib/commonjs/components/header/AnimatedTopView.js +5 -7
- package/lib/commonjs/components/header/AnimatedTopView.js.map +1 -1
- package/lib/commonjs/components/header/CollapsibleHeaderConsumer.js +10 -13
- package/lib/commonjs/components/header/CollapsibleHeaderConsumer.js.map +1 -1
- package/lib/commonjs/components/header/CollapsibleHeaderContainer.js +22 -17
- package/lib/commonjs/components/header/CollapsibleHeaderContainer.js.map +1 -1
- package/lib/commonjs/components/header/CollapsibleHeaderContainerProvider.js +12 -15
- package/lib/commonjs/components/header/CollapsibleHeaderContainerProvider.js.map +1 -1
- package/lib/commonjs/components/header/StickyView.js +18 -25
- package/lib/commonjs/components/header/StickyView.js.map +1 -1
- package/lib/commonjs/components/scrollable/CollapsibleFlatList.js +27 -41
- package/lib/commonjs/components/scrollable/CollapsibleFlatList.js.map +1 -1
- package/lib/commonjs/components/scrollable/CollapsibleScrollView.js +12 -15
- package/lib/commonjs/components/scrollable/CollapsibleScrollView.js.map +1 -1
- package/lib/commonjs/components/scrollable/CollapsibleSectionList.js +22 -36
- package/lib/commonjs/components/scrollable/CollapsibleSectionList.js.map +1 -1
- package/lib/commonjs/components/scrollable/useAnimatedScroll.js +44 -24
- package/lib/commonjs/components/scrollable/useAnimatedScroll.js.map +1 -1
- package/lib/commonjs/hooks/useCollapsibleContext.js +1 -2
- package/lib/commonjs/hooks/useCollapsibleContext.js.map +1 -1
- package/lib/commonjs/hooks/useCollapsibleHeaderConsumerContext.js +1 -2
- package/lib/commonjs/hooks/useCollapsibleHeaderConsumerContext.js.map +1 -1
- package/lib/commonjs/hooks/useCollapsibleHeaderContext.js +1 -2
- package/lib/commonjs/hooks/useCollapsibleHeaderContext.js.map +1 -1
- package/lib/commonjs/hooks/useInternalCollapsibleContext.js +1 -2
- package/lib/commonjs/hooks/useInternalCollapsibleContext.js.map +1 -1
- package/lib/commonjs/hooks/useKeyboardShowEvent.js.map +1 -1
- package/lib/commonjs/index.js +3 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/plugins/CollapsibleFlashList.js +14 -17
- package/lib/commonjs/plugins/CollapsibleFlashList.js.map +1 -1
- package/lib/commonjs/plugins/CollapsibleLegendList.js +15 -15
- package/lib/commonjs/plugins/CollapsibleLegendList.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/utils/debounce.js +2 -6
- package/lib/commonjs/utils/debounce.js.map +1 -1
- package/lib/commonjs/utils/useSharedValueRef.js +33 -3
- package/lib/commonjs/utils/useSharedValueRef.js.map +1 -1
- package/lib/commonjs/withCollapsibleContext.js +3 -4
- package/lib/commonjs/withCollapsibleContext.js.map +1 -1
- package/lib/module/components/CollapsibleContainer.js +18 -19
- package/lib/module/components/CollapsibleContainer.js.map +1 -1
- package/lib/module/components/CollapsibleView.js +38 -29
- package/lib/module/components/CollapsibleView.js.map +1 -1
- package/lib/module/components/header/AnimatedTopView.js +3 -4
- package/lib/module/components/header/AnimatedTopView.js.map +1 -1
- package/lib/module/components/header/CollapsibleHeaderConsumer.js +8 -10
- package/lib/module/components/header/CollapsibleHeaderConsumer.js.map +1 -1
- package/lib/module/components/header/CollapsibleHeaderContainer.js +21 -15
- package/lib/module/components/header/CollapsibleHeaderContainer.js.map +1 -1
- package/lib/module/components/header/CollapsibleHeaderContainerProvider.js +10 -12
- package/lib/module/components/header/CollapsibleHeaderContainerProvider.js.map +1 -1
- package/lib/module/components/header/StickyView.js +18 -23
- package/lib/module/components/header/StickyView.js.map +1 -1
- package/lib/module/components/scrollable/CollapsibleFlatList.js +27 -41
- package/lib/module/components/scrollable/CollapsibleFlatList.js.map +1 -1
- package/lib/module/components/scrollable/CollapsibleScrollView.js +10 -12
- package/lib/module/components/scrollable/CollapsibleScrollView.js.map +1 -1
- package/lib/module/components/scrollable/CollapsibleSectionList.js +22 -35
- package/lib/module/components/scrollable/CollapsibleSectionList.js.map +1 -1
- package/lib/module/components/scrollable/useAnimatedScroll.js +44 -23
- package/lib/module/components/scrollable/useAnimatedScroll.js.map +1 -1
- package/lib/module/hooks/useCollapsibleContext.js.map +1 -1
- package/lib/module/hooks/useCollapsibleHeaderConsumerContext.js.map +1 -1
- package/lib/module/hooks/useCollapsibleHeaderContext.js.map +1 -1
- package/lib/module/hooks/useInternalCollapsibleContext.js.map +1 -1
- package/lib/module/hooks/useKeyboardShowEvent.js.map +1 -1
- package/lib/module/index.js +0 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/plugins/CollapsibleFlashList.js +12 -14
- package/lib/module/plugins/CollapsibleFlashList.js.map +1 -1
- package/lib/module/plugins/CollapsibleLegendList.js +13 -12
- package/lib/module/plugins/CollapsibleLegendList.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/debounce.js +2 -6
- package/lib/module/utils/debounce.js.map +1 -1
- package/lib/module/utils/useSharedValueRef.js +33 -3
- package/lib/module/utils/useSharedValueRef.js.map +1 -1
- package/lib/module/withCollapsibleContext.js +2 -2
- package/lib/module/withCollapsibleContext.js.map +1 -1
- package/lib/typescript/components/CollapsibleContainer.d.ts.map +1 -1
- package/lib/typescript/components/CollapsibleView.d.ts +4 -4
- package/lib/typescript/components/CollapsibleView.d.ts.map +1 -1
- package/lib/typescript/components/header/AnimatedTopView.d.ts +2 -2
- package/lib/typescript/components/header/AnimatedTopView.d.ts.map +1 -1
- package/lib/typescript/components/header/CollapsibleHeaderContainer.d.ts.map +1 -1
- package/lib/typescript/components/header/StickyView.d.ts.map +1 -1
- package/lib/typescript/components/scrollable/CollapsibleFlatList.d.ts.map +1 -1
- package/lib/typescript/components/scrollable/CollapsibleSectionList.d.ts.map +1 -1
- package/lib/typescript/components/scrollable/useAnimatedScroll.d.ts +2 -2
- package/lib/typescript/components/scrollable/useAnimatedScroll.d.ts.map +1 -1
- package/lib/typescript/hooks/useCollapsibleContext.d.ts +0 -1
- package/lib/typescript/hooks/useCollapsibleContext.d.ts.map +1 -1
- package/lib/typescript/hooks/useCollapsibleHeaderContext.d.ts +2 -3
- package/lib/typescript/hooks/useCollapsibleHeaderContext.d.ts.map +1 -1
- package/lib/typescript/hooks/useInternalCollapsibleContext.d.ts +0 -1
- package/lib/typescript/hooks/useInternalCollapsibleContext.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +0 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/plugins/CollapsibleFlashList.d.ts.map +1 -1
- package/lib/typescript/plugins/CollapsibleLegendList.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +12 -12
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/utils/debounce.d.ts.map +1 -1
- package/lib/typescript/utils/useSharedValueRef.d.ts +2 -2
- package/lib/typescript/utils/useSharedValueRef.d.ts.map +1 -1
- package/lib/typescript/withCollapsibleContext.d.ts.map +1 -1
- package/package.json +54 -18
- package/src/components/CollapsibleContainer.tsx +12 -3
- package/src/components/CollapsibleView.tsx +31 -10
- package/src/components/header/AnimatedTopView.tsx +5 -2
- package/src/components/header/CollapsibleHeaderContainer.tsx +18 -13
- package/src/components/header/StickyView.tsx +8 -15
- package/src/components/scrollable/CollapsibleFlatList.tsx +27 -46
- package/src/components/scrollable/CollapsibleSectionList.tsx +21 -41
- package/src/components/scrollable/useAnimatedScroll.ts +36 -22
- package/src/hooks/useCollapsibleHeaderContext.ts +2 -2
- package/src/index.tsx +0 -2
- package/src/plugins/CollapsibleFlashList.tsx +9 -9
- package/src/plugins/CollapsibleLegendList.tsx +8 -2
- package/src/types.ts +12 -12
- package/src/utils/useSharedValueRef.ts +37 -6
- package/src/withCollapsibleContext.tsx +7 -4
package/package.json
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@r0b0t3d/react-native-collapsible",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0-alpha.0",
|
|
4
4
|
"description": "Fully customizable collapsible views",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
8
8
|
"react-native": "src/index",
|
|
9
9
|
"source": "src/index",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./lib/typescript/index.d.ts",
|
|
13
|
+
"react-native": "./src/index.tsx",
|
|
14
|
+
"import": "./lib/module/index.js",
|
|
15
|
+
"require": "./lib/commonjs/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./plugins/CollapsibleFlashList": {
|
|
18
|
+
"types": "./lib/typescript/plugins/CollapsibleFlashList.d.ts",
|
|
19
|
+
"react-native": "./src/plugins/CollapsibleFlashList.tsx",
|
|
20
|
+
"import": "./lib/module/plugins/CollapsibleFlashList.js",
|
|
21
|
+
"require": "./lib/commonjs/plugins/CollapsibleFlashList.js"
|
|
22
|
+
},
|
|
23
|
+
"./plugins/CollapsibleLegendList": {
|
|
24
|
+
"types": "./lib/typescript/plugins/CollapsibleLegendList.d.ts",
|
|
25
|
+
"react-native": "./src/plugins/CollapsibleLegendList.tsx",
|
|
26
|
+
"import": "./lib/module/plugins/CollapsibleLegendList.js",
|
|
27
|
+
"require": "./lib/commonjs/plugins/CollapsibleLegendList.js"
|
|
28
|
+
},
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
10
31
|
"files": [
|
|
11
32
|
"src",
|
|
12
33
|
"lib",
|
|
@@ -30,6 +51,7 @@
|
|
|
30
51
|
"test": "jest",
|
|
31
52
|
"typecheck": "tsc --noEmit",
|
|
32
53
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
54
|
+
"lint:fix": "eslint \"**/*.{js,ts,tsx}\" --fix",
|
|
33
55
|
"prepack": "bob build",
|
|
34
56
|
"release": "release-it",
|
|
35
57
|
"example": "yarn --cwd example",
|
|
@@ -53,42 +75,55 @@
|
|
|
53
75
|
"devDependencies": {
|
|
54
76
|
"@commitlint/config-conventional": "^17.0.2",
|
|
55
77
|
"@evilmartians/lefthook": "^1.2.2",
|
|
56
|
-
"@legendapp/list": "^
|
|
57
|
-
"@react-native-community/eslint-config": "^3.0
|
|
78
|
+
"@legendapp/list": "^3.0.6",
|
|
79
|
+
"@react-native-community/eslint-config": "^3.2.0",
|
|
58
80
|
"@release-it/conventional-changelog": "^5.0.0",
|
|
59
|
-
"@shopify/flash-list": "^
|
|
81
|
+
"@shopify/flash-list": "^2.3.2",
|
|
60
82
|
"@types/jest": "^28.1.2",
|
|
61
|
-
"@types/react": "~
|
|
83
|
+
"@types/react": "~19.2.17",
|
|
62
84
|
"@types/react-native": "0.70.0",
|
|
63
85
|
"commitlint": "^17.0.2",
|
|
64
86
|
"del-cli": "^5.0.0",
|
|
65
87
|
"eslint": "^8.4.1",
|
|
66
|
-
"eslint-config-prettier": "^
|
|
67
|
-
"eslint-plugin-prettier": "^
|
|
88
|
+
"eslint-config-prettier": "^10.1.8",
|
|
89
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
68
90
|
"jest": "^28.1.1",
|
|
69
91
|
"pod-install": "^0.1.0",
|
|
70
|
-
"prettier": "^
|
|
71
|
-
"react": "
|
|
72
|
-
"react-native": "0.
|
|
92
|
+
"prettier": "^3.8.4",
|
|
93
|
+
"react": "19.2.3",
|
|
94
|
+
"react-native": "0.86.0",
|
|
73
95
|
"react-native-builder-bob": "^0.20.0",
|
|
74
|
-
"react-native-reanimated": "^
|
|
75
|
-
"release-it": "^
|
|
76
|
-
"typescript": "^
|
|
77
|
-
},
|
|
78
|
-
"resolutions": {
|
|
79
|
-
"@types/react": "17.0.21"
|
|
96
|
+
"react-native-reanimated": "^4.4.1",
|
|
97
|
+
"release-it": "^20.2.0",
|
|
98
|
+
"typescript": "^6.0.3"
|
|
80
99
|
},
|
|
81
100
|
"peerDependencies": {
|
|
82
|
-
"
|
|
101
|
+
"@legendapp/list": "*",
|
|
102
|
+
"@shopify/flash-list": "*",
|
|
103
|
+
"react": ">=18.0.0",
|
|
83
104
|
"react-native": "*",
|
|
84
105
|
"react-native-gesture-handler": "*",
|
|
85
106
|
"react-native-reanimated": ">=3.0.0"
|
|
86
107
|
},
|
|
108
|
+
"peerDependenciesMeta": {
|
|
109
|
+
"@legendapp/list": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
112
|
+
"@shopify/flash-list": {
|
|
113
|
+
"optional": true
|
|
114
|
+
}
|
|
115
|
+
},
|
|
87
116
|
"engines": {
|
|
88
117
|
"node": ">= 16.0.0"
|
|
89
118
|
},
|
|
90
119
|
"jest": {
|
|
91
120
|
"preset": "react-native",
|
|
121
|
+
"setupFiles": [
|
|
122
|
+
"<rootDir>/jest.setup.js"
|
|
123
|
+
],
|
|
124
|
+
"moduleNameMapper": {
|
|
125
|
+
"^react-native-reanimated$": "<rootDir>/jest.setup.js"
|
|
126
|
+
},
|
|
92
127
|
"modulePathIgnorePatterns": [
|
|
93
128
|
"<rootDir>/example/node_modules",
|
|
94
129
|
"<rootDir>/lib/"
|
|
@@ -155,7 +190,8 @@
|
|
|
155
190
|
[
|
|
156
191
|
"typescript",
|
|
157
192
|
{
|
|
158
|
-
"project": "tsconfig.build.json"
|
|
193
|
+
"project": "tsconfig.build.json",
|
|
194
|
+
"tsc": "node_modules/.bin/tsc"
|
|
159
195
|
}
|
|
160
196
|
]
|
|
161
197
|
]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
|
-
import React, { useLayoutEffect } from 'react';
|
|
2
|
+
import React, { useCallback, useLayoutEffect } from 'react';
|
|
3
3
|
import {
|
|
4
4
|
KeyboardAvoidingView,
|
|
5
5
|
KeyboardAvoidingViewProps,
|
|
@@ -28,7 +28,7 @@ function CollapsibleContainer({
|
|
|
28
28
|
const { containerHeight, containerRef } = useInternalCollapsibleContext();
|
|
29
29
|
const { scrollY, scrollTo } = useCollapsibleContext();
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
const handleKeyboardShow = useCallback(() => {
|
|
32
32
|
textInputRefs.some((ref) => {
|
|
33
33
|
const isFocusedFunc = ref.current.isFocused;
|
|
34
34
|
const isFocused =
|
|
@@ -51,7 +51,16 @@ function CollapsibleContainer({
|
|
|
51
51
|
}
|
|
52
52
|
return isFocused;
|
|
53
53
|
});
|
|
54
|
-
}
|
|
54
|
+
}, [
|
|
55
|
+
textInputRefs,
|
|
56
|
+
containerRef,
|
|
57
|
+
containerHeight,
|
|
58
|
+
scrollY,
|
|
59
|
+
scrollTo,
|
|
60
|
+
keyboardAvoidingViewProps?.keyboardVerticalOffset,
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
useKeyboardShowEvent(handleKeyboardShow);
|
|
55
64
|
|
|
56
65
|
return (
|
|
57
66
|
<KeyboardAvoidingViewComponent
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import React, {
|
|
3
|
+
ReactNode,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useId,
|
|
7
|
+
useMemo,
|
|
8
|
+
} from 'react';
|
|
4
9
|
import {
|
|
5
10
|
LayoutChangeEvent,
|
|
6
11
|
Pressable,
|
|
@@ -19,16 +24,17 @@ import Animated, {
|
|
|
19
24
|
interpolate,
|
|
20
25
|
useAnimatedReaction,
|
|
21
26
|
runOnJS,
|
|
27
|
+
SharedValue,
|
|
22
28
|
} from 'react-native-reanimated';
|
|
23
29
|
|
|
24
30
|
export type CollapsibleHeaderProps = {
|
|
25
31
|
onToggle: () => void;
|
|
26
|
-
collapsed:
|
|
32
|
+
collapsed: SharedValue<number>;
|
|
27
33
|
};
|
|
28
34
|
|
|
29
35
|
type Props = {
|
|
30
36
|
initialState?: 'collapsed' | 'expanded';
|
|
31
|
-
collapseState?:
|
|
37
|
+
collapseState?: SharedValue<number>;
|
|
32
38
|
renderHeader: (props: CollapsibleHeaderProps) => ReactNode;
|
|
33
39
|
children: ReactNode;
|
|
34
40
|
containerStyle?: StyleProp<ViewStyle>;
|
|
@@ -38,10 +44,9 @@ type Props = {
|
|
|
38
44
|
onToggle?: (isExpand: boolean) => void;
|
|
39
45
|
};
|
|
40
46
|
|
|
41
|
-
let key = 0;
|
|
42
47
|
export default function CollapsibleView({
|
|
43
48
|
initialState = 'collapsed',
|
|
44
|
-
collapseState
|
|
49
|
+
collapseState: collapseStateProp,
|
|
45
50
|
renderHeader,
|
|
46
51
|
children,
|
|
47
52
|
containerStyle,
|
|
@@ -49,16 +54,32 @@ export default function CollapsibleView({
|
|
|
49
54
|
expandedBackgroundColor,
|
|
50
55
|
onToggle,
|
|
51
56
|
}: Props) {
|
|
57
|
+
// Shared value is always allocated; unused when caller provides one.
|
|
58
|
+
// The allocation cost is negligible and this keeps hook order stable
|
|
59
|
+
// across renders and React StrictMode double-invocation.
|
|
60
|
+
const collapseStateInternal = useSharedValue(0);
|
|
61
|
+
const collapseState = collapseStateProp ?? collapseStateInternal;
|
|
52
62
|
const actualHeight = useSharedValue(11110);
|
|
53
|
-
const
|
|
63
|
+
const instanceId = useId();
|
|
64
|
+
const contentKey = useMemo(
|
|
65
|
+
() => `collapsible-view-${instanceId}`,
|
|
66
|
+
[instanceId]
|
|
67
|
+
);
|
|
54
68
|
|
|
55
69
|
useEffect(() => {
|
|
70
|
+
// `initialState` is only honored when the caller does not pass a
|
|
71
|
+
// `collapseState` of their own. When a caller-controlled SV is
|
|
72
|
+
// provided, mutating it from here would silently clobber whatever
|
|
73
|
+
// value the caller is driving from gesture state or animations.
|
|
74
|
+
if (collapseStateProp) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
56
77
|
const newValue = initialState === 'collapsed' ? 0 : 1;
|
|
57
78
|
if (newValue === collapseState.value) {
|
|
58
79
|
return;
|
|
59
80
|
}
|
|
60
81
|
collapseState.value = newValue;
|
|
61
|
-
}, [initialState]);
|
|
82
|
+
}, [initialState, collapseStateProp]);
|
|
62
83
|
|
|
63
84
|
const handleToggle = useCallback(() => {
|
|
64
85
|
collapseState.value = withSpring(collapseState.value === 0 ? 1 : 0, {
|
|
@@ -80,7 +101,7 @@ export default function CollapsibleView({
|
|
|
80
101
|
overshootClamping: true,
|
|
81
102
|
}),
|
|
82
103
|
}),
|
|
83
|
-
[actualHeight, contentKey]
|
|
104
|
+
[actualHeight, contentKey, collapseState]
|
|
84
105
|
);
|
|
85
106
|
|
|
86
107
|
const contentHeight = useAnimatedStyle(
|
|
@@ -114,7 +135,7 @@ export default function CollapsibleView({
|
|
|
114
135
|
};
|
|
115
136
|
}
|
|
116
137
|
return {};
|
|
117
|
-
}, []);
|
|
138
|
+
}, [collapseState, collapsedBackgroundColor, expandedBackgroundColor]);
|
|
118
139
|
|
|
119
140
|
const headerProps = useMemo(
|
|
120
141
|
() => ({ onToggle: handleToggle, collapsed: collapseState }),
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import Animated, {
|
|
2
|
+
import Animated, {
|
|
3
|
+
SharedValue,
|
|
4
|
+
useAnimatedStyle,
|
|
5
|
+
} from 'react-native-reanimated';
|
|
3
6
|
|
|
4
7
|
type Props = {
|
|
5
|
-
height:
|
|
8
|
+
height: SharedValue<number>;
|
|
6
9
|
};
|
|
7
10
|
|
|
8
11
|
export default function AnimatedTopView({ height }: Props) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { ReactNode, useEffect, useMemo } from 'react';
|
|
1
|
+
import React, { ReactNode, useEffect, useId, useMemo, useRef } from 'react';
|
|
2
2
|
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
3
|
import useCollapsibleHeaderConsumerContext from '../../hooks/useCollapsibleHeaderConsumerContext';
|
|
4
4
|
import CollapsibleHeaderContainerProvider from './CollapsibleHeaderContainerProvider';
|
|
@@ -8,25 +8,30 @@ type Props = {
|
|
|
8
8
|
containerStyle?: StyleProp<ViewStyle>;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// Module-scoped monotonic counter used only to assign zIndex to *new* instances
|
|
12
|
+
// (see useInstanceZIndex below). Bounded by app lifetime; wraparound after ~100k
|
|
13
|
+
// instances may cause one frame of incorrect layering.
|
|
14
|
+
let nextZIndex = 0;
|
|
15
|
+
function useInstanceZIndex(): number {
|
|
16
|
+
const ref = useRef<number | null>(null);
|
|
17
|
+
if (ref.current === null) {
|
|
18
|
+
ref.current = nextZIndex++;
|
|
19
|
+
}
|
|
20
|
+
return ref.current;
|
|
21
|
+
}
|
|
12
22
|
|
|
13
23
|
export default function CollapsibleHeaderContainer({
|
|
14
24
|
children,
|
|
15
25
|
containerStyle,
|
|
16
26
|
}: Props) {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
() => `collapsible-header-${originalKey}`,
|
|
20
|
-
[originalKey]
|
|
21
|
-
);
|
|
27
|
+
const contentKey = `collapsible-header-${useId()}`;
|
|
28
|
+
const instanceZIndex = useInstanceZIndex();
|
|
22
29
|
const { mount, unmount, update } = useCollapsibleHeaderConsumerContext();
|
|
23
30
|
|
|
24
|
-
const internalStyle = useMemo(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
29
|
-
}, [key, originalKey]);
|
|
31
|
+
const internalStyle = useMemo(
|
|
32
|
+
() => ({ zIndex: 100000 - instanceZIndex }),
|
|
33
|
+
[instanceZIndex]
|
|
34
|
+
);
|
|
30
35
|
|
|
31
36
|
const content = useMemo(() => {
|
|
32
37
|
return (
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import React, { useCallback, useEffect, useMemo } from 'react';
|
|
1
|
+
import React, { useEffect, useId } from 'react';
|
|
3
2
|
import {
|
|
4
3
|
LayoutChangeEvent,
|
|
5
4
|
LayoutRectangle,
|
|
@@ -8,7 +7,6 @@ import {
|
|
|
8
7
|
ViewStyle,
|
|
9
8
|
} from 'react-native';
|
|
10
9
|
import Animated, {
|
|
11
|
-
Extrapolate,
|
|
12
10
|
interpolate,
|
|
13
11
|
useAnimatedStyle,
|
|
14
12
|
useDerivedValue,
|
|
@@ -22,24 +20,20 @@ type Props = {
|
|
|
22
20
|
stickyRef?: React.MutableRefObject<any>;
|
|
23
21
|
};
|
|
24
22
|
|
|
25
|
-
let stickyKey = 0;
|
|
26
|
-
|
|
27
23
|
export default function StickyView({ children, style, stickyRef }: Props) {
|
|
28
|
-
const key =
|
|
24
|
+
const key = `sticky_${useId()}`;
|
|
29
25
|
const { handleStickyViewLayout, animatedY } = useCollapsibleHeaderContext();
|
|
30
26
|
const currentLayout = useSharedValue<LayoutRectangle | undefined>(undefined);
|
|
31
27
|
|
|
32
28
|
useEffect(() => {
|
|
33
29
|
return () => handleStickyViewLayout(key, undefined);
|
|
30
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
34
31
|
}, [key]);
|
|
35
32
|
|
|
36
|
-
const handleLayout =
|
|
37
|
-
(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
[key, handleStickyViewLayout]
|
|
42
|
-
);
|
|
33
|
+
const handleLayout = ({ nativeEvent: { layout } }: LayoutChangeEvent) => {
|
|
34
|
+
currentLayout.set(layout);
|
|
35
|
+
handleStickyViewLayout(key, layout);
|
|
36
|
+
};
|
|
43
37
|
|
|
44
38
|
const translateY = useDerivedValue(() => {
|
|
45
39
|
if (!currentLayout.value) {
|
|
@@ -52,7 +46,7 @@ export default function StickyView({ children, style, stickyRef }: Props) {
|
|
|
52
46
|
animatedY.value,
|
|
53
47
|
[0, topValue, topValue + stickyHeight + 100000],
|
|
54
48
|
[0, 0, stickyHeight + 100000],
|
|
55
|
-
|
|
49
|
+
'clamp'
|
|
56
50
|
);
|
|
57
51
|
}, []);
|
|
58
52
|
|
|
@@ -69,7 +63,6 @@ export default function StickyView({ children, style, stickyRef }: Props) {
|
|
|
69
63
|
return (
|
|
70
64
|
<Animated.View
|
|
71
65
|
key={key}
|
|
72
|
-
// @ts-ignore
|
|
73
66
|
ref={stickyRef}
|
|
74
67
|
onLayout={handleLayout}
|
|
75
68
|
style={[styles.container, style, animatedStyle]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
1
|
import React, {
|
|
3
2
|
useCallback,
|
|
4
3
|
useEffect,
|
|
@@ -6,10 +5,12 @@ import React, {
|
|
|
6
5
|
useRef,
|
|
7
6
|
useState,
|
|
8
7
|
} from 'react';
|
|
9
|
-
import { FlatListProps,
|
|
8
|
+
import { FlatListProps, StyleSheet } from 'react-native';
|
|
9
|
+
import type { ScrollToIndexParams } from '../../types';
|
|
10
10
|
import Animated, {
|
|
11
11
|
runOnJS,
|
|
12
12
|
useAnimatedReaction,
|
|
13
|
+
useAnimatedStyle,
|
|
13
14
|
} from 'react-native-reanimated';
|
|
14
15
|
import useAnimatedScroll from './useAnimatedScroll';
|
|
15
16
|
import useInternalCollapsibleContext from '../../hooks/useInternalCollapsibleContext';
|
|
@@ -17,8 +18,6 @@ import type { CollapsibleProps } from '../../types';
|
|
|
17
18
|
import AnimatedTopView from '../header/AnimatedTopView';
|
|
18
19
|
import useCollapsibleContext from '../../hooks/useCollapsibleContext';
|
|
19
20
|
|
|
20
|
-
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
|
|
21
|
-
|
|
22
21
|
type Props<Data> = Omit<FlatListProps<Data>, 'scrollEnabled'> &
|
|
23
22
|
CollapsibleProps;
|
|
24
23
|
|
|
@@ -30,10 +29,6 @@ export default function CollapsibleFlatList<Data>({
|
|
|
30
29
|
const { contentMinHeight, scrollViewRef, fixedHeaderHeight } =
|
|
31
30
|
useInternalCollapsibleContext();
|
|
32
31
|
const mounted = useRef(true);
|
|
33
|
-
const contentHeight = useRef(0);
|
|
34
|
-
const [internalContentMinHeight, setInternalContentMinHeight] = useState(
|
|
35
|
-
contentMinHeight.value
|
|
36
|
-
);
|
|
37
32
|
const [internalProgressViewOffset, setInternalProgressViewOffset] =
|
|
38
33
|
useState(0);
|
|
39
34
|
|
|
@@ -48,10 +43,12 @@ export default function CollapsibleFlatList<Data>({
|
|
|
48
43
|
offset: yValue,
|
|
49
44
|
animated,
|
|
50
45
|
});
|
|
46
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
47
|
}, []);
|
|
52
48
|
|
|
53
|
-
const scrollToIndex = useCallback((params) => {
|
|
49
|
+
const scrollToIndex = useCallback((params: ScrollToIndexParams) => {
|
|
54
50
|
scrollViewRef.current?.scrollToIndex?.(params);
|
|
51
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
55
52
|
}, []);
|
|
56
53
|
|
|
57
54
|
const scrollToLocation = useCallback(() => {
|
|
@@ -65,34 +62,12 @@ export default function CollapsibleFlatList<Data>({
|
|
|
65
62
|
scrollToLocation,
|
|
66
63
|
});
|
|
67
64
|
|
|
68
|
-
const handleInternalContentHeight = useCallback((value: number) => {
|
|
69
|
-
if (mounted.current) {
|
|
70
|
-
setInternalContentMinHeight(value);
|
|
71
|
-
}
|
|
72
|
-
}, []);
|
|
73
|
-
|
|
74
65
|
const handleInternalProgressViewOffset = useCallback((value: number) => {
|
|
75
66
|
if (mounted.current) {
|
|
76
67
|
setInternalProgressViewOffset(value);
|
|
77
68
|
}
|
|
78
69
|
}, []);
|
|
79
70
|
|
|
80
|
-
useAnimatedReaction(
|
|
81
|
-
() => {
|
|
82
|
-
return contentMinHeight.value;
|
|
83
|
-
},
|
|
84
|
-
(result, previous) => {
|
|
85
|
-
if (result !== previous) {
|
|
86
|
-
if (
|
|
87
|
-
contentHeight.current < result &&
|
|
88
|
-
internalContentMinHeight !== result
|
|
89
|
-
) {
|
|
90
|
-
runOnJS(handleInternalContentHeight)(result);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
);
|
|
95
|
-
|
|
96
71
|
useAnimatedReaction(
|
|
97
72
|
() => {
|
|
98
73
|
return fixedHeaderHeight.value;
|
|
@@ -104,32 +79,39 @@ export default function CollapsibleFlatList<Data>({
|
|
|
104
79
|
}
|
|
105
80
|
);
|
|
106
81
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
82
|
+
// Animate minHeight on the UI thread instead of bouncing through React state.
|
|
83
|
+
// Skip applying minHeight when the value is 0 to avoid forcing a 0-height
|
|
84
|
+
// layout on the first frame before the container has been measured.
|
|
85
|
+
const listHeaderStyle = useAnimatedStyle(
|
|
86
|
+
() => ({
|
|
87
|
+
minHeight:
|
|
88
|
+
contentMinHeight.value > 0 ? contentMinHeight.value : undefined,
|
|
89
|
+
}),
|
|
90
|
+
[]
|
|
114
91
|
);
|
|
115
92
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
93
|
+
const contentContainerStyle = useMemo(
|
|
94
|
+
() => [styles.contentContainer, props.contentContainerStyle],
|
|
95
|
+
[props.contentContainerStyle]
|
|
96
|
+
);
|
|
119
97
|
|
|
120
98
|
const handleScrollToIndexFailed = useCallback(() => {}, []);
|
|
121
99
|
|
|
100
|
+
const ListHeaderComponent = props.ListHeaderComponent;
|
|
122
101
|
function renderListHeader() {
|
|
123
102
|
return (
|
|
124
|
-
<View>
|
|
103
|
+
<Animated.View style={listHeaderStyle}>
|
|
125
104
|
<AnimatedTopView height={headerHeight} />
|
|
126
|
-
{
|
|
127
|
-
|
|
105
|
+
{ListHeaderComponent &&
|
|
106
|
+
(React.isValidElement(ListHeaderComponent)
|
|
107
|
+
? ListHeaderComponent
|
|
108
|
+
: React.createElement(ListHeaderComponent as React.ComponentType))}
|
|
109
|
+
</Animated.View>
|
|
128
110
|
);
|
|
129
111
|
}
|
|
130
112
|
|
|
131
113
|
return (
|
|
132
|
-
<
|
|
114
|
+
<Animated.FlatList
|
|
133
115
|
ref={scrollViewRef}
|
|
134
116
|
keyboardDismissMode="on-drag"
|
|
135
117
|
keyboardShouldPersistTaps="handled"
|
|
@@ -140,7 +122,6 @@ export default function CollapsibleFlatList<Data>({
|
|
|
140
122
|
contentContainerStyle={contentContainerStyle}
|
|
141
123
|
onScroll={scrollHandler}
|
|
142
124
|
ListHeaderComponent={renderListHeader()}
|
|
143
|
-
onContentSizeChange={handleContentSizeChange}
|
|
144
125
|
//@ts-ignore
|
|
145
126
|
simultaneousHandlers={[]}
|
|
146
127
|
progressViewOffset={internalProgressViewOffset}
|
|
@@ -6,7 +6,6 @@ import React, {
|
|
|
6
6
|
useState,
|
|
7
7
|
} from 'react';
|
|
8
8
|
import {
|
|
9
|
-
View,
|
|
10
9
|
StyleSheet,
|
|
11
10
|
SectionList,
|
|
12
11
|
SectionListProps,
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
import Animated, {
|
|
16
15
|
runOnJS,
|
|
17
16
|
useAnimatedReaction,
|
|
17
|
+
useAnimatedStyle,
|
|
18
18
|
} from 'react-native-reanimated';
|
|
19
19
|
import useAnimatedScroll from './useAnimatedScroll';
|
|
20
20
|
import useInternalCollapsibleContext from '../../hooks/useInternalCollapsibleContext';
|
|
@@ -35,10 +35,6 @@ export default function CollapsibleSectionList<Data>({
|
|
|
35
35
|
const { contentMinHeight, scrollViewRef, fixedHeaderHeight } =
|
|
36
36
|
useInternalCollapsibleContext();
|
|
37
37
|
const mounted = useRef(true);
|
|
38
|
-
const contentHeight = useRef(0);
|
|
39
|
-
const [internalContentMinHeight, setInternalContentMinHeight] = useState(
|
|
40
|
-
contentMinHeight.value
|
|
41
|
-
);
|
|
42
38
|
const [internalProgressViewOffset, setInternalProgressViewOffset] =
|
|
43
39
|
useState(0);
|
|
44
40
|
|
|
@@ -70,34 +66,12 @@ export default function CollapsibleSectionList<Data>({
|
|
|
70
66
|
scrollToLocation,
|
|
71
67
|
});
|
|
72
68
|
|
|
73
|
-
const handleInternalContentHeight = useCallback((value: number) => {
|
|
74
|
-
if (mounted.current) {
|
|
75
|
-
setInternalContentMinHeight(value);
|
|
76
|
-
}
|
|
77
|
-
}, []);
|
|
78
|
-
|
|
79
69
|
const handleInternalProgressViewOffset = useCallback((value: number) => {
|
|
80
70
|
if (mounted.current) {
|
|
81
71
|
setInternalProgressViewOffset(value);
|
|
82
72
|
}
|
|
83
73
|
}, []);
|
|
84
74
|
|
|
85
|
-
useAnimatedReaction(
|
|
86
|
-
() => {
|
|
87
|
-
return contentMinHeight.value;
|
|
88
|
-
},
|
|
89
|
-
(result, previous) => {
|
|
90
|
-
if (result !== previous) {
|
|
91
|
-
if (
|
|
92
|
-
contentHeight.current < result &&
|
|
93
|
-
internalContentMinHeight !== result
|
|
94
|
-
) {
|
|
95
|
-
runOnJS(handleInternalContentHeight)(result);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
);
|
|
100
|
-
|
|
101
75
|
useAnimatedReaction(
|
|
102
76
|
() => {
|
|
103
77
|
return fixedHeaderHeight.value;
|
|
@@ -109,27 +83,34 @@ export default function CollapsibleSectionList<Data>({
|
|
|
109
83
|
}
|
|
110
84
|
);
|
|
111
85
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
86
|
+
// Animate minHeight on the UI thread instead of bouncing through React state.
|
|
87
|
+
// Skip applying minHeight when the value is 0 to avoid forcing a 0-height
|
|
88
|
+
// layout on the first frame before the container has been measured.
|
|
89
|
+
const listHeaderStyle = useAnimatedStyle(
|
|
90
|
+
() => ({
|
|
91
|
+
minHeight:
|
|
92
|
+
contentMinHeight.value > 0 ? contentMinHeight.value : undefined,
|
|
93
|
+
}),
|
|
94
|
+
[]
|
|
119
95
|
);
|
|
120
96
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
97
|
+
const contentContainerStyle = useMemo(
|
|
98
|
+
() => [styles.contentContainer, props.contentContainerStyle],
|
|
99
|
+
[props.contentContainerStyle]
|
|
100
|
+
);
|
|
124
101
|
|
|
125
102
|
const handleScrollToIndexFailed = useCallback(() => {}, []);
|
|
126
103
|
|
|
104
|
+
const ListHeaderComponent = props.ListHeaderComponent;
|
|
127
105
|
function renderListHeader() {
|
|
128
106
|
return (
|
|
129
|
-
<View>
|
|
107
|
+
<Animated.View style={listHeaderStyle}>
|
|
130
108
|
<AnimatedTopView height={headerHeight} />
|
|
131
|
-
{
|
|
132
|
-
|
|
109
|
+
{ListHeaderComponent &&
|
|
110
|
+
(React.isValidElement(ListHeaderComponent)
|
|
111
|
+
? ListHeaderComponent
|
|
112
|
+
: React.createElement(ListHeaderComponent as React.ComponentType))}
|
|
113
|
+
</Animated.View>
|
|
133
114
|
);
|
|
134
115
|
}
|
|
135
116
|
|
|
@@ -145,7 +126,6 @@ export default function CollapsibleSectionList<Data>({
|
|
|
145
126
|
contentContainerStyle={contentContainerStyle}
|
|
146
127
|
onScroll={scrollHandler}
|
|
147
128
|
ListHeaderComponent={renderListHeader()}
|
|
148
|
-
onContentSizeChange={handleContentSizeChange}
|
|
149
129
|
//@ts-ignore
|
|
150
130
|
simultaneousHandlers={[]}
|
|
151
131
|
progressViewOffset={internalProgressViewOffset}
|