@r0b0t3d/react-native-collapsible 1.3.5-beta.4 → 1.3.5-beta.5
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 +3 -2
- package/lib/commonjs/components/CollapsibleContainer.js.map +1 -1
- package/lib/commonjs/components/header/CollapsibleHeaderConsumer.js +65 -0
- package/lib/commonjs/components/header/CollapsibleHeaderConsumer.js.map +1 -0
- package/lib/commonjs/components/header/CollapsibleHeaderContainer.js +24 -85
- package/lib/commonjs/components/header/CollapsibleHeaderContainer.js.map +1 -1
- package/lib/commonjs/components/header/CollapsibleHeaderContainerProvider.js +105 -0
- package/lib/commonjs/components/header/CollapsibleHeaderContainerProvider.js.map +1 -0
- package/lib/commonjs/hooks/useCollapsibleHeaderConsumerContext.js +21 -0
- package/lib/commonjs/hooks/useCollapsibleHeaderConsumerContext.js.map +1 -0
- package/lib/commonjs/withCollapsibleContext.js +0 -2
- package/lib/commonjs/withCollapsibleContext.js.map +1 -1
- package/lib/module/components/CollapsibleContainer.js +3 -2
- package/lib/module/components/CollapsibleContainer.js.map +1 -1
- package/lib/module/components/header/CollapsibleHeaderConsumer.js +57 -0
- package/lib/module/components/header/CollapsibleHeaderConsumer.js.map +1 -0
- package/lib/module/components/header/CollapsibleHeaderContainer.js +25 -86
- package/lib/module/components/header/CollapsibleHeaderContainer.js.map +1 -1
- package/lib/module/components/header/CollapsibleHeaderContainerProvider.js +96 -0
- package/lib/module/components/header/CollapsibleHeaderContainerProvider.js.map +1 -0
- package/lib/module/hooks/useCollapsibleHeaderConsumerContext.js +13 -0
- package/lib/module/hooks/useCollapsibleHeaderConsumerContext.js.map +1 -0
- package/lib/module/withCollapsibleContext.js +0 -2
- package/lib/module/withCollapsibleContext.js.map +1 -1
- package/lib/typescript/components/CollapsibleContainer.d.ts.map +1 -1
- package/lib/typescript/components/header/CollapsibleHeaderConsumer.d.ts +9 -0
- package/lib/typescript/components/header/CollapsibleHeaderConsumer.d.ts.map +1 -0
- package/lib/typescript/components/header/CollapsibleHeaderContainer.d.ts +2 -2
- package/lib/typescript/components/header/CollapsibleHeaderContainer.d.ts.map +1 -1
- package/lib/typescript/components/header/CollapsibleHeaderContainerProvider.d.ts +10 -0
- package/lib/typescript/components/header/CollapsibleHeaderContainerProvider.d.ts.map +1 -0
- package/lib/typescript/hooks/useCollapsibleHeaderConsumerContext.d.ts +15 -0
- package/lib/typescript/hooks/useCollapsibleHeaderConsumerContext.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +1 -2
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/withCollapsibleContext.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/CollapsibleContainer.tsx +4 -3
- package/src/components/header/CollapsibleHeaderConsumer.tsx +78 -0
- package/src/components/header/CollapsibleHeaderContainer.tsx +27 -143
- package/src/components/header/CollapsibleHeaderContainerProvider.tsx +162 -0
- package/src/hooks/useCollapsibleHeaderConsumerContext.ts +22 -0
- package/src/types.ts +1 -2
- package/src/withCollapsibleContext.tsx +0 -2
|
@@ -1,27 +1,7 @@
|
|
|
1
|
-
import React, { ReactNode,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
LayoutChangeEvent,
|
|
6
|
-
LayoutRectangle,
|
|
7
|
-
StyleSheet,
|
|
8
|
-
} from 'react-native';
|
|
9
|
-
import useInternalCollapsibleContext from '../../hooks/useInternalCollapsibleContext';
|
|
10
|
-
import useCollapsibleContext from '../../hooks/useCollapsibleContext';
|
|
11
|
-
import Animated, {
|
|
12
|
-
Extrapolate,
|
|
13
|
-
interpolate,
|
|
14
|
-
runOnJS,
|
|
15
|
-
useAnimatedReaction,
|
|
16
|
-
useAnimatedStyle,
|
|
17
|
-
useDerivedValue,
|
|
18
|
-
useSharedValue,
|
|
19
|
-
} from 'react-native-reanimated';
|
|
20
|
-
import {
|
|
21
|
-
CollapsibleContextHeaderType,
|
|
22
|
-
CollapsibleHeaderContext,
|
|
23
|
-
} from '../../hooks/useCollapsibleHeaderContext';
|
|
24
|
-
import useSharedValueRef from '../../utils/useSharedValueRef';
|
|
1
|
+
import React, { ReactNode, useEffect, useMemo } from 'react';
|
|
2
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
+
import useCollapsibleHeaderConsumerContext from '../../hooks/useCollapsibleHeaderConsumerContext';
|
|
4
|
+
import CollapsibleHeaderContainerProvider from './CollapsibleHeaderContainerProvider';
|
|
25
5
|
|
|
26
6
|
type Props = {
|
|
27
7
|
children: ReactNode;
|
|
@@ -35,63 +15,7 @@ export default function CollapsibleHeaderContainer({
|
|
|
35
15
|
containerStyle,
|
|
36
16
|
}: Props) {
|
|
37
17
|
const contentKey = useMemo(() => `collapsible-header-${key++}`, []);
|
|
38
|
-
const {
|
|
39
|
-
useInternalCollapsibleContext();
|
|
40
|
-
const { scrollY } = useCollapsibleContext();
|
|
41
|
-
const currentLayout = useSharedValue<LayoutRectangle | undefined>(undefined);
|
|
42
|
-
const [stickyLayouts, setStickyLayouts] = useSharedValueRef<
|
|
43
|
-
Record<string, LayoutRectangle | undefined>
|
|
44
|
-
>({});
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
return () => {
|
|
48
|
-
handleHeaderContainerLayout(contentKey);
|
|
49
|
-
};
|
|
50
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
|
-
}, [contentKey]);
|
|
52
|
-
|
|
53
|
-
const stickyHeight = useDerivedValue(
|
|
54
|
-
() =>
|
|
55
|
-
Object.values(stickyLayouts.value).reduce(
|
|
56
|
-
(acc, value) => acc + (value?.height ?? 0),
|
|
57
|
-
0
|
|
58
|
-
),
|
|
59
|
-
[]
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
useAnimatedReaction(
|
|
63
|
-
() => {
|
|
64
|
-
if (!currentLayout.value) {
|
|
65
|
-
return -1;
|
|
66
|
-
}
|
|
67
|
-
return currentLayout.value.height - stickyHeight.value;
|
|
68
|
-
},
|
|
69
|
-
(result, previous) => {
|
|
70
|
-
if (result !== -1 && result !== previous) {
|
|
71
|
-
runOnJS(handleHeaderContainerLayout)(
|
|
72
|
-
contentKey,
|
|
73
|
-
currentLayout.value,
|
|
74
|
-
stickyHeight.value
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const handleLayout = useCallback(
|
|
81
|
-
({ nativeEvent: { layout } }: LayoutChangeEvent) => {
|
|
82
|
-
currentLayout.value = layout;
|
|
83
|
-
},
|
|
84
|
-
[currentLayout]
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
const handleStickyViewLayout = useCallback(
|
|
88
|
-
(stickyKey: string, layout?: LayoutRectangle) => {
|
|
89
|
-
setStickyLayouts({
|
|
90
|
-
[stickyKey]: layout,
|
|
91
|
-
});
|
|
92
|
-
},
|
|
93
|
-
[setStickyLayouts]
|
|
94
|
-
);
|
|
18
|
+
const { mount, unmount, update } = useCollapsibleHeaderConsumerContext();
|
|
95
19
|
|
|
96
20
|
const internalStyle = useMemo(() => {
|
|
97
21
|
return {
|
|
@@ -99,71 +23,31 @@ export default function CollapsibleHeaderContainer({
|
|
|
99
23
|
};
|
|
100
24
|
}, []);
|
|
101
25
|
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
position.stickyHeight;
|
|
112
|
-
|
|
113
|
-
return interpolate(
|
|
114
|
-
scrollY.value,
|
|
115
|
-
[0, topPosition, 10000],
|
|
116
|
-
[0, -topPosition, -topPosition],
|
|
117
|
-
Extrapolate.CLAMP
|
|
26
|
+
const content = useMemo(() => {
|
|
27
|
+
return (
|
|
28
|
+
<CollapsibleHeaderContainerProvider
|
|
29
|
+
containerStyle={[containerStyle, internalStyle]}
|
|
30
|
+
contentKey={contentKey}
|
|
31
|
+
key={contentKey}
|
|
32
|
+
>
|
|
33
|
+
{children}
|
|
34
|
+
</CollapsibleHeaderContainerProvider>
|
|
118
35
|
);
|
|
119
|
-
});
|
|
36
|
+
}, [children, containerStyle, contentKey, internalStyle]);
|
|
120
37
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
transform: [
|
|
124
|
-
{
|
|
125
|
-
translateY: translateY.value,
|
|
126
|
-
},
|
|
127
|
-
],
|
|
128
|
-
};
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const animatedY = useDerivedValue(() => {
|
|
132
|
-
const position = headerViewPositions.value[contentKey];
|
|
133
|
-
if (!currentLayout.value || !position) {
|
|
134
|
-
return 0;
|
|
135
|
-
}
|
|
136
|
-
const value = scrollY.value - currentLayout.value.y + position.top;
|
|
137
|
-
const maxV = currentLayout.value.height - stickyHeight.value;
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
mount(contentKey, content);
|
|
138
40
|
|
|
139
|
-
return
|
|
140
|
-
|
|
41
|
+
return () => {
|
|
42
|
+
unmount(contentKey);
|
|
43
|
+
};
|
|
44
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
45
|
+
}, [contentKey]);
|
|
141
46
|
|
|
142
|
-
|
|
143
|
-
()
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}),
|
|
147
|
-
[handleStickyViewLayout, animatedY]
|
|
148
|
-
);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
update(contentKey, content);
|
|
49
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
50
|
+
}, [content]);
|
|
149
51
|
|
|
150
|
-
return
|
|
151
|
-
<CollapsibleHeaderContext.Provider value={value}>
|
|
152
|
-
<Animated.View
|
|
153
|
-
key={contentKey}
|
|
154
|
-
style={[styles.container, containerStyle, internalStyle, animatedStyle]}
|
|
155
|
-
pointerEvents="box-none"
|
|
156
|
-
onLayout={handleLayout}
|
|
157
|
-
>
|
|
158
|
-
{children}
|
|
159
|
-
</Animated.View>
|
|
160
|
-
</CollapsibleHeaderContext.Provider>
|
|
161
|
-
);
|
|
52
|
+
return null;
|
|
162
53
|
}
|
|
163
|
-
|
|
164
|
-
const styles = StyleSheet.create({
|
|
165
|
-
container: {
|
|
166
|
-
overflow: 'hidden',
|
|
167
|
-
backgroundColor: 'white',
|
|
168
|
-
},
|
|
169
|
-
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import React, { ReactNode, useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
StyleProp,
|
|
4
|
+
ViewStyle,
|
|
5
|
+
LayoutChangeEvent,
|
|
6
|
+
LayoutRectangle,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
} from 'react-native';
|
|
9
|
+
import useInternalCollapsibleContext from '../../hooks/useInternalCollapsibleContext';
|
|
10
|
+
import useCollapsibleContext from '../../hooks/useCollapsibleContext';
|
|
11
|
+
import Animated, {
|
|
12
|
+
Extrapolate,
|
|
13
|
+
interpolate,
|
|
14
|
+
runOnJS,
|
|
15
|
+
useAnimatedReaction,
|
|
16
|
+
useAnimatedStyle,
|
|
17
|
+
useDerivedValue,
|
|
18
|
+
useSharedValue,
|
|
19
|
+
} from 'react-native-reanimated';
|
|
20
|
+
import {
|
|
21
|
+
CollapsibleContextHeaderType,
|
|
22
|
+
CollapsibleHeaderContext,
|
|
23
|
+
} from '../../hooks/useCollapsibleHeaderContext';
|
|
24
|
+
import useSharedValueRef from '../../utils/useSharedValueRef';
|
|
25
|
+
|
|
26
|
+
type Props = {
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
containerStyle?: StyleProp<ViewStyle>;
|
|
29
|
+
contentKey: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default function CollapsibleHeaderContainerProvider({
|
|
33
|
+
children,
|
|
34
|
+
containerStyle,
|
|
35
|
+
contentKey,
|
|
36
|
+
}: Props) {
|
|
37
|
+
const { handleHeaderContainerLayout, headerViewPositions } =
|
|
38
|
+
useInternalCollapsibleContext();
|
|
39
|
+
const { scrollY } = useCollapsibleContext();
|
|
40
|
+
const currentLayout = useSharedValue<LayoutRectangle | undefined>(undefined);
|
|
41
|
+
const [stickyLayouts, setStickyLayouts] = useSharedValueRef<
|
|
42
|
+
Record<string, LayoutRectangle | undefined>
|
|
43
|
+
>({});
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
return () => {
|
|
47
|
+
handleHeaderContainerLayout(contentKey);
|
|
48
|
+
};
|
|
49
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
50
|
+
}, [contentKey]);
|
|
51
|
+
|
|
52
|
+
const stickyHeight = useDerivedValue(
|
|
53
|
+
() =>
|
|
54
|
+
Object.values(stickyLayouts.value).reduce(
|
|
55
|
+
(acc, value) => acc + (value?.height ?? 0),
|
|
56
|
+
0
|
|
57
|
+
),
|
|
58
|
+
[]
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
useAnimatedReaction(
|
|
62
|
+
() => {
|
|
63
|
+
if (!currentLayout.value) {
|
|
64
|
+
return -1;
|
|
65
|
+
}
|
|
66
|
+
return currentLayout.value.height - stickyHeight.value;
|
|
67
|
+
},
|
|
68
|
+
(result, previous) => {
|
|
69
|
+
if (result !== -1 && result !== previous) {
|
|
70
|
+
runOnJS(handleHeaderContainerLayout)(
|
|
71
|
+
contentKey,
|
|
72
|
+
currentLayout.value,
|
|
73
|
+
stickyHeight.value
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const handleLayout = useCallback(
|
|
80
|
+
({ nativeEvent: { layout } }: LayoutChangeEvent) => {
|
|
81
|
+
currentLayout.value = layout;
|
|
82
|
+
},
|
|
83
|
+
[currentLayout]
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const handleStickyViewLayout = useCallback(
|
|
87
|
+
(stickyKey: string, layout?: LayoutRectangle) => {
|
|
88
|
+
setStickyLayouts({
|
|
89
|
+
[stickyKey]: layout,
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
[setStickyLayouts]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const translateY = useDerivedValue(() => {
|
|
96
|
+
const position = headerViewPositions.value[contentKey];
|
|
97
|
+
if (!currentLayout.value || !position) {
|
|
98
|
+
return scrollY.value;
|
|
99
|
+
}
|
|
100
|
+
const topPosition =
|
|
101
|
+
currentLayout.value.height +
|
|
102
|
+
currentLayout.value.y -
|
|
103
|
+
position.top -
|
|
104
|
+
position.stickyHeight;
|
|
105
|
+
|
|
106
|
+
return interpolate(
|
|
107
|
+
scrollY.value,
|
|
108
|
+
[0, topPosition, 10000],
|
|
109
|
+
[0, -topPosition, -topPosition],
|
|
110
|
+
Extrapolate.CLAMP
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
115
|
+
return {
|
|
116
|
+
transform: [
|
|
117
|
+
{
|
|
118
|
+
translateY: translateY.value,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const animatedY = useDerivedValue(() => {
|
|
125
|
+
const position = headerViewPositions.value[contentKey];
|
|
126
|
+
if (!currentLayout.value || !position) {
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
|
129
|
+
const value = scrollY.value - currentLayout.value.y + position.top;
|
|
130
|
+
const maxV = currentLayout.value.height - stickyHeight.value;
|
|
131
|
+
|
|
132
|
+
return Math.max(0, Math.min(value, maxV));
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const value: CollapsibleContextHeaderType = useMemo(
|
|
136
|
+
() => ({
|
|
137
|
+
handleStickyViewLayout,
|
|
138
|
+
animatedY,
|
|
139
|
+
}),
|
|
140
|
+
[handleStickyViewLayout, animatedY]
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<CollapsibleHeaderContext.Provider value={value}>
|
|
145
|
+
<Animated.View
|
|
146
|
+
key={contentKey}
|
|
147
|
+
style={[styles.container, containerStyle, animatedStyle]}
|
|
148
|
+
pointerEvents="box-none"
|
|
149
|
+
onLayout={handleLayout}
|
|
150
|
+
>
|
|
151
|
+
{children}
|
|
152
|
+
</Animated.View>
|
|
153
|
+
</CollapsibleHeaderContext.Provider>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const styles = StyleSheet.create({
|
|
158
|
+
container: {
|
|
159
|
+
overflow: 'hidden',
|
|
160
|
+
backgroundColor: 'white',
|
|
161
|
+
},
|
|
162
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createContext, ReactNode, useContext } from 'react';
|
|
2
|
+
|
|
3
|
+
export type HeaderItem = { key: string; children: ReactNode };
|
|
4
|
+
|
|
5
|
+
type CollapsibleContextHeaderConsumerType = {
|
|
6
|
+
headers: HeaderItem[];
|
|
7
|
+
mount: (key: string, header: ReactNode) => void;
|
|
8
|
+
update: (key: string, header: ReactNode) => void;
|
|
9
|
+
unmount: (key: string) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const CollapsibleHeaderConsumerContext =
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
createContext<CollapsibleContextHeaderConsumerType>({});
|
|
15
|
+
|
|
16
|
+
export default function useCollapsibleHeaderConsumerContext() {
|
|
17
|
+
const ctx = useContext(CollapsibleHeaderConsumerContext);
|
|
18
|
+
if (!ctx) {
|
|
19
|
+
throw new Error('Component should be wrapped CollapsibleHeaderProvider');
|
|
20
|
+
}
|
|
21
|
+
return ctx;
|
|
22
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import type { LayoutRectangle
|
|
2
|
+
import type { LayoutRectangle } from 'react-native';
|
|
3
3
|
import type Animated from 'react-native-reanimated';
|
|
4
4
|
|
|
5
5
|
export type ScrollToIndexParams = {
|
|
@@ -31,7 +31,6 @@ export type LayoutParams = {
|
|
|
31
31
|
|
|
32
32
|
export type CollapsibleContextInternalType = {
|
|
33
33
|
scrollViewRef: React.RefObject<any>;
|
|
34
|
-
containerRef: React.RefObject<View>;
|
|
35
34
|
contentMinHeight: Animated.SharedValue<number>;
|
|
36
35
|
headerViewPositions: Animated.SharedValue<
|
|
37
36
|
Record<string, { top: number; stickyHeight: number }>
|
|
@@ -17,7 +17,6 @@ export default function withCollapsibleContext<T>(Component: FC<T>) {
|
|
|
17
17
|
const scrollY = useSharedValue(0);
|
|
18
18
|
const fixedHeaderHeight = useSharedValue(0);
|
|
19
19
|
const containerHeight = useSharedValue(0);
|
|
20
|
-
const containerRef = useRef<View>(null);
|
|
21
20
|
const scrollViewRef = useRef<View>(null);
|
|
22
21
|
const headerContainerLayouts = useRef<
|
|
23
22
|
Record<string, (LayoutRectangle & { stickyHeight?: number }) | undefined>
|
|
@@ -104,7 +103,6 @@ export default function withCollapsibleContext<T>(Component: FC<T>) {
|
|
|
104
103
|
const internalContext = useMemo(
|
|
105
104
|
() => ({
|
|
106
105
|
scrollViewRef,
|
|
107
|
-
containerRef,
|
|
108
106
|
handleHeaderContainerLayout,
|
|
109
107
|
setCollapsibleHandlers,
|
|
110
108
|
handleContainerHeight,
|