@mustmove/bottom-sheet 1.0.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/LICENSE +21 -0
- package/README.md +66 -0
- package/mock.js +231 -0
- package/package.json +107 -0
- package/src/components/bottomSheet/BottomSheet.tsx +1885 -0
- package/src/components/bottomSheet/BottomSheetBody.tsx +44 -0
- package/src/components/bottomSheet/BottomSheetContent.tsx +261 -0
- package/src/components/bottomSheet/constants.ts +58 -0
- package/src/components/bottomSheet/index.ts +2 -0
- package/src/components/bottomSheet/styles.ts +11 -0
- package/src/components/bottomSheet/types.d.ts +358 -0
- package/src/components/bottomSheetBackdrop/BottomSheetBackdrop.tsx +165 -0
- package/src/components/bottomSheetBackdrop/constants.ts +22 -0
- package/src/components/bottomSheetBackdrop/index.ts +2 -0
- package/src/components/bottomSheetBackdrop/styles.ts +8 -0
- package/src/components/bottomSheetBackdrop/types.d.ts +58 -0
- package/src/components/bottomSheetBackground/BottomSheetBackground.tsx +20 -0
- package/src/components/bottomSheetBackground/BottomSheetBackgroundContainer.tsx +35 -0
- package/src/components/bottomSheetBackground/index.ts +2 -0
- package/src/components/bottomSheetBackground/styles.ts +9 -0
- package/src/components/bottomSheetBackground/types.d.ts +12 -0
- package/src/components/bottomSheetDebugView/BottomSheetDebugView.tsx +26 -0
- package/src/components/bottomSheetDebugView/ReText.tsx +72 -0
- package/src/components/bottomSheetDebugView/ReText.webx.tsx +55 -0
- package/src/components/bottomSheetDebugView/index.ts +1 -0
- package/src/components/bottomSheetDebugView/styles.ts +19 -0
- package/src/components/bottomSheetDebugView/styles.web.ts +20 -0
- package/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx +123 -0
- package/src/components/bottomSheetDraggableView/index.ts +1 -0
- package/src/components/bottomSheetDraggableView/types.d.ts +9 -0
- package/src/components/bottomSheetFooter/BottomSheetFooter.tsx +119 -0
- package/src/components/bottomSheetFooter/BottomSheetFooterContainer.tsx +43 -0
- package/src/components/bottomSheetFooter/index.ts +3 -0
- package/src/components/bottomSheetFooter/styles.ts +12 -0
- package/src/components/bottomSheetFooter/types.d.ts +41 -0
- package/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx +69 -0
- package/src/components/bottomSheetGestureHandlersProvider/index.ts +1 -0
- package/src/components/bottomSheetGestureHandlersProvider/types.d.ts +8 -0
- package/src/components/bottomSheetHandle/BottomSheetHandle.tsx +51 -0
- package/src/components/bottomSheetHandle/BottomSheetHandleContainer.tsx +187 -0
- package/src/components/bottomSheetHandle/constants.ts +12 -0
- package/src/components/bottomSheetHandle/index.ts +6 -0
- package/src/components/bottomSheetHandle/styles.ts +23 -0
- package/src/components/bottomSheetHandle/types.d.ts +52 -0
- package/src/components/bottomSheetHostingContainer/BottomSheetHostingContainer.tsx +130 -0
- package/src/components/bottomSheetHostingContainer/index.ts +2 -0
- package/src/components/bottomSheetHostingContainer/styles.ts +5 -0
- package/src/components/bottomSheetHostingContainer/styles.web.ts +11 -0
- package/src/components/bottomSheetHostingContainer/types.d.ts +17 -0
- package/src/components/bottomSheetModal/BottomSheetModal.tsx +482 -0
- package/src/components/bottomSheetModal/constants.ts +4 -0
- package/src/components/bottomSheetModal/index.ts +6 -0
- package/src/components/bottomSheetModal/types.d.ts +67 -0
- package/src/components/bottomSheetModalProvider/BottomSheetModalProvider.tsx +211 -0
- package/src/components/bottomSheetModalProvider/index.ts +1 -0
- package/src/components/bottomSheetModalProvider/types.d.ts +12 -0
- package/src/components/bottomSheetRefreshControl/BottomSheetRefreshControl.android.tsx +84 -0
- package/src/components/bottomSheetRefreshControl/BottomSheetRefreshControl.tsx +1 -0
- package/src/components/bottomSheetRefreshControl/index.ts +20 -0
- package/src/components/bottomSheetScrollable/BottomSheetDraggableScrollable.tsx +23 -0
- package/src/components/bottomSheetScrollable/BottomSheetFlashList.tsx +88 -0
- package/src/components/bottomSheetScrollable/BottomSheetFlashList.web.tsx +1 -0
- package/src/components/bottomSheetScrollable/BottomSheetFlatList.tsx +26 -0
- package/src/components/bottomSheetScrollable/BottomSheetScrollView.tsx +27 -0
- package/src/components/bottomSheetScrollable/BottomSheetSectionList.tsx +29 -0
- package/src/components/bottomSheetScrollable/BottomSheetVirtualizedList.tsx +27 -0
- package/src/components/bottomSheetScrollable/ScrollableContainer.android.tsx +55 -0
- package/src/components/bottomSheetScrollable/ScrollableContainer.tsx +22 -0
- package/src/components/bottomSheetScrollable/ScrollableContainer.web.tsx +102 -0
- package/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx +153 -0
- package/src/components/bottomSheetScrollable/index.ts +15 -0
- package/src/components/bottomSheetScrollable/styles.ts +8 -0
- package/src/components/bottomSheetScrollable/types.d.ts +280 -0
- package/src/components/bottomSheetScrollable/useBottomSheetContentSizeSetter.ts +32 -0
- package/src/components/bottomSheetTextInput/BottomSheetTextInput.tsx +127 -0
- package/src/components/bottomSheetTextInput/index.ts +2 -0
- package/src/components/bottomSheetTextInput/types.ts +3 -0
- package/src/components/bottomSheetView/BottomSheetView.tsx +93 -0
- package/src/components/bottomSheetView/index.ts +1 -0
- package/src/components/bottomSheetView/styles.ts +10 -0
- package/src/components/bottomSheetView/types.d.ts +24 -0
- package/src/components/touchables/Touchables.ios.tsx +5 -0
- package/src/components/touchables/Touchables.tsx +5 -0
- package/src/components/touchables/index.ts +20 -0
- package/src/constants.ts +159 -0
- package/src/contexts/external.ts +8 -0
- package/src/contexts/gesture.ts +13 -0
- package/src/contexts/index.ts +15 -0
- package/src/contexts/internal.ts +65 -0
- package/src/contexts/modal/external.ts +11 -0
- package/src/contexts/modal/internal.ts +25 -0
- package/src/hooks/index.ts +29 -0
- package/src/hooks/useAnimatedDetents.ts +119 -0
- package/src/hooks/useAnimatedKeyboard.ts +174 -0
- package/src/hooks/useAnimatedLayout.ts +109 -0
- package/src/hooks/useBottomSheet.ts +12 -0
- package/src/hooks/useBottomSheetContentContainerStyle.ts +88 -0
- package/src/hooks/useBottomSheetGestureHandlers.ts +12 -0
- package/src/hooks/useBottomSheetInternal.ts +25 -0
- package/src/hooks/useBottomSheetModal.ts +12 -0
- package/src/hooks/useBottomSheetModalInternal.ts +25 -0
- package/src/hooks/useBottomSheetScrollableCreator.tsx +60 -0
- package/src/hooks/useBottomSheetSpringConfigs.ts +11 -0
- package/src/hooks/useBottomSheetTimingConfigs.ts +36 -0
- package/src/hooks/useBoundingClientRect.ts +77 -0
- package/src/hooks/useGestureEventsHandlersDefault.tsx +436 -0
- package/src/hooks/useGestureEventsHandlersDefault.web.tsx +418 -0
- package/src/hooks/useGestureHandler.ts +90 -0
- package/src/hooks/usePropsValidator.ts +108 -0
- package/src/hooks/useReactiveSharedValue.ts +45 -0
- package/src/hooks/useScrollEventsHandlersDefault.ts +167 -0
- package/src/hooks/useScrollHandler.ts +72 -0
- package/src/hooks/useScrollHandler.web.ts +181 -0
- package/src/hooks/useScrollable.ts +131 -0
- package/src/hooks/useScrollableSetter.ts +56 -0
- package/src/hooks/useStableCallback.ts +26 -0
- package/src/index.ts +79 -0
- package/src/types.d.ts +336 -0
- package/src/utilities/animate.ts +56 -0
- package/src/utilities/clamp.ts +8 -0
- package/src/utilities/easingExp.ts +10 -0
- package/src/utilities/findNodeHandle.ts +1 -0
- package/src/utilities/findNodeHandle.web.ts +33 -0
- package/src/utilities/getKeyboardAnimationConfigs.ts +44 -0
- package/src/utilities/getRefNativeTag.web.ts +6 -0
- package/src/utilities/id.ts +6 -0
- package/src/utilities/index.ts +7 -0
- package/src/utilities/isFabricInstalled.ts +9 -0
- package/src/utilities/logger.ts +55 -0
- package/src/utilities/noop.ts +7 -0
- package/src/utilities/normalizeSnapPoint.ts +17 -0
- package/src/utilities/snapPoint.ts +11 -0
- package/src/utilities/validateSnapPoint.ts +20 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
4
|
+
import ReText from './ReText';
|
|
5
|
+
import { styles } from './styles';
|
|
6
|
+
|
|
7
|
+
interface BottomSheetDebugViewProps {
|
|
8
|
+
values: Record<string, SharedValue<number | boolean | object> | number>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const BottomSheetDebugView = ({ values }: BottomSheetDebugViewProps) => {
|
|
12
|
+
return (
|
|
13
|
+
<View pointerEvents="none" style={styles.container}>
|
|
14
|
+
{Object.keys(values).map(key => (
|
|
15
|
+
<ReText
|
|
16
|
+
key={`item-${key}`}
|
|
17
|
+
value={values[key]}
|
|
18
|
+
style={styles.text}
|
|
19
|
+
text={key}
|
|
20
|
+
/>
|
|
21
|
+
))}
|
|
22
|
+
</View>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default BottomSheetDebugView;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type TextProps as RNTextProps, TextInput } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
type SharedValue,
|
|
5
|
+
type AnimatedProps,
|
|
6
|
+
useAnimatedProps,
|
|
7
|
+
useDerivedValue,
|
|
8
|
+
} from 'react-native-reanimated';
|
|
9
|
+
|
|
10
|
+
interface TextProps {
|
|
11
|
+
text: string;
|
|
12
|
+
value: SharedValue<number | boolean | object> | number;
|
|
13
|
+
style?: AnimatedProps<RNTextProps>['style'];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
|
|
17
|
+
|
|
18
|
+
Animated.addWhitelistedNativeProps({ text: true });
|
|
19
|
+
|
|
20
|
+
const ReText = ({ text, value: _providedValue, style }: TextProps) => {
|
|
21
|
+
const providedValue = useDerivedValue(() => {
|
|
22
|
+
if (!_providedValue) {
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let rawValue: number | string | object | boolean = '';
|
|
27
|
+
if (typeof _providedValue === 'number') {
|
|
28
|
+
rawValue = _providedValue as number;
|
|
29
|
+
} else if (typeof _providedValue.get === 'function') {
|
|
30
|
+
rawValue = _providedValue.get();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof rawValue === 'object') {
|
|
34
|
+
const rawValueObject = Object.entries(rawValue)
|
|
35
|
+
.map(item => `${item[0]}: ${item[1]}`)
|
|
36
|
+
.reduce((result, current, index) => {
|
|
37
|
+
if (index !== 0) {
|
|
38
|
+
result = `${result} \n`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
result = `${result}- ${current}`;
|
|
42
|
+
return result;
|
|
43
|
+
}, '');
|
|
44
|
+
|
|
45
|
+
return `${text}\n${rawValueObject}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof rawValue === 'number') {
|
|
49
|
+
rawValue = rawValue.toFixed(2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return `${text}: ${rawValue}`;
|
|
53
|
+
}, [text, _providedValue]);
|
|
54
|
+
const animatedProps = useAnimatedProps(() => {
|
|
55
|
+
return {
|
|
56
|
+
text: providedValue.get(),
|
|
57
|
+
};
|
|
58
|
+
}, [providedValue]);
|
|
59
|
+
return (
|
|
60
|
+
<AnimatedTextInput
|
|
61
|
+
underlineColorAndroid="transparent"
|
|
62
|
+
editable={false}
|
|
63
|
+
value={providedValue?.get() ?? ''}
|
|
64
|
+
style={style}
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
animatedProps={animatedProps}
|
|
67
|
+
multiline={true}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default ReText;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import { type TextProps as RNTextProps, TextInput } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
type SharedValue,
|
|
5
|
+
useAnimatedReaction,
|
|
6
|
+
useDerivedValue,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
8
|
+
|
|
9
|
+
interface TextProps {
|
|
10
|
+
text: string;
|
|
11
|
+
value: SharedValue<number | boolean> | number;
|
|
12
|
+
style?: Animated.AnimateProps<RNTextProps>['style'];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
|
|
16
|
+
|
|
17
|
+
const ReText = (props: TextProps) => {
|
|
18
|
+
const { text, value: _providedValue, style } = { style: {}, ...props };
|
|
19
|
+
const textRef = useRef<TextInput>(null);
|
|
20
|
+
|
|
21
|
+
const providedValue = useDerivedValue(() => {
|
|
22
|
+
const value =
|
|
23
|
+
typeof _providedValue === 'number'
|
|
24
|
+
? _providedValue
|
|
25
|
+
: typeof _providedValue.value === 'number'
|
|
26
|
+
? _providedValue.value.toFixed(2)
|
|
27
|
+
: _providedValue.value;
|
|
28
|
+
|
|
29
|
+
return `${text}: ${value}`;
|
|
30
|
+
}, [_providedValue, text]);
|
|
31
|
+
|
|
32
|
+
//region effects
|
|
33
|
+
useAnimatedReaction(
|
|
34
|
+
() => providedValue.value,
|
|
35
|
+
result => {
|
|
36
|
+
textRef.current?.setNativeProps({
|
|
37
|
+
text: result,
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
[]
|
|
41
|
+
);
|
|
42
|
+
//endregion
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<AnimatedTextInput
|
|
46
|
+
ref={textRef}
|
|
47
|
+
underlineColorAndroid="transparent"
|
|
48
|
+
editable={false}
|
|
49
|
+
value={providedValue.value}
|
|
50
|
+
style={style}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default ReText;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './BottomSheetDebugView';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export const styles = StyleSheet.create({
|
|
4
|
+
container: {
|
|
5
|
+
position: 'absolute',
|
|
6
|
+
right: 4,
|
|
7
|
+
top: 0,
|
|
8
|
+
paddingHorizontal: 4,
|
|
9
|
+
backgroundColor: 'rgba(0, 0,0,0.5)',
|
|
10
|
+
},
|
|
11
|
+
text: {
|
|
12
|
+
fontSize: 14,
|
|
13
|
+
lineHeight: 16,
|
|
14
|
+
textAlignVertical: 'center',
|
|
15
|
+
padding: 0,
|
|
16
|
+
marginVertical: 4,
|
|
17
|
+
color: 'white',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { StyleSheet } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export const styles = StyleSheet.create({
|
|
4
|
+
container: {
|
|
5
|
+
position: 'absolute',
|
|
6
|
+
left: 4,
|
|
7
|
+
top: 80,
|
|
8
|
+
padding: 2,
|
|
9
|
+
width: 400,
|
|
10
|
+
backgroundColor: 'rgba(0, 0,0,0.5)',
|
|
11
|
+
},
|
|
12
|
+
text: {
|
|
13
|
+
fontSize: 14,
|
|
14
|
+
lineHeight: 16,
|
|
15
|
+
textAlignVertical: 'center',
|
|
16
|
+
height: 20,
|
|
17
|
+
padding: 0,
|
|
18
|
+
color: 'white',
|
|
19
|
+
},
|
|
20
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React, { useMemo, memo } from 'react';
|
|
2
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
3
|
+
import Animated from 'react-native-reanimated';
|
|
4
|
+
import { BottomSheetDraggableContext } from '../../contexts/gesture';
|
|
5
|
+
import {
|
|
6
|
+
useBottomSheetGestureHandlers,
|
|
7
|
+
useBottomSheetInternal,
|
|
8
|
+
} from '../../hooks';
|
|
9
|
+
import type { BottomSheetDraggableViewProps } from './types';
|
|
10
|
+
|
|
11
|
+
const BottomSheetDraggableViewComponent = ({
|
|
12
|
+
nativeGestureRef,
|
|
13
|
+
refreshControlGestureRef,
|
|
14
|
+
style,
|
|
15
|
+
children,
|
|
16
|
+
...rest
|
|
17
|
+
}: BottomSheetDraggableViewProps) => {
|
|
18
|
+
//#region hooks
|
|
19
|
+
const {
|
|
20
|
+
enableContentPanningGesture,
|
|
21
|
+
simultaneousHandlers: _providedSimultaneousHandlers,
|
|
22
|
+
waitFor,
|
|
23
|
+
activeOffsetX,
|
|
24
|
+
activeOffsetY,
|
|
25
|
+
failOffsetX,
|
|
26
|
+
failOffsetY,
|
|
27
|
+
} = useBottomSheetInternal();
|
|
28
|
+
const { contentPanGestureHandler } = useBottomSheetGestureHandlers();
|
|
29
|
+
//#endregion
|
|
30
|
+
|
|
31
|
+
//#region variables
|
|
32
|
+
const simultaneousHandlers = useMemo(() => {
|
|
33
|
+
const refs = [];
|
|
34
|
+
|
|
35
|
+
if (nativeGestureRef) {
|
|
36
|
+
refs.push(nativeGestureRef);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (refreshControlGestureRef) {
|
|
40
|
+
refs.push(refreshControlGestureRef);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (_providedSimultaneousHandlers) {
|
|
44
|
+
if (Array.isArray(_providedSimultaneousHandlers)) {
|
|
45
|
+
refs.push(..._providedSimultaneousHandlers);
|
|
46
|
+
} else {
|
|
47
|
+
refs.push(_providedSimultaneousHandlers);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return refs;
|
|
52
|
+
}, [
|
|
53
|
+
_providedSimultaneousHandlers,
|
|
54
|
+
nativeGestureRef,
|
|
55
|
+
refreshControlGestureRef,
|
|
56
|
+
]);
|
|
57
|
+
const draggableGesture = useMemo(() => {
|
|
58
|
+
let gesture = Gesture.Pan()
|
|
59
|
+
.enabled(enableContentPanningGesture)
|
|
60
|
+
.shouldCancelWhenOutside(false)
|
|
61
|
+
.runOnJS(false)
|
|
62
|
+
.onStart(contentPanGestureHandler.handleOnStart)
|
|
63
|
+
.onChange(contentPanGestureHandler.handleOnChange)
|
|
64
|
+
.onEnd(contentPanGestureHandler.handleOnEnd)
|
|
65
|
+
.onFinalize(contentPanGestureHandler.handleOnFinalize);
|
|
66
|
+
|
|
67
|
+
if (waitFor) {
|
|
68
|
+
gesture = gesture.requireExternalGestureToFail(waitFor);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (simultaneousHandlers) {
|
|
72
|
+
gesture = gesture.simultaneousWithExternalGesture(
|
|
73
|
+
simultaneousHandlers as never
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (activeOffsetX) {
|
|
78
|
+
gesture = gesture.activeOffsetX(activeOffsetX);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (activeOffsetY) {
|
|
82
|
+
gesture = gesture.activeOffsetY(activeOffsetY);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (failOffsetX) {
|
|
86
|
+
gesture = gesture.failOffsetX(failOffsetX);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (failOffsetY) {
|
|
90
|
+
gesture = gesture.failOffsetY(failOffsetY);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return gesture;
|
|
94
|
+
}, [
|
|
95
|
+
activeOffsetX,
|
|
96
|
+
activeOffsetY,
|
|
97
|
+
enableContentPanningGesture,
|
|
98
|
+
failOffsetX,
|
|
99
|
+
failOffsetY,
|
|
100
|
+
simultaneousHandlers,
|
|
101
|
+
waitFor,
|
|
102
|
+
contentPanGestureHandler.handleOnChange,
|
|
103
|
+
contentPanGestureHandler.handleOnEnd,
|
|
104
|
+
contentPanGestureHandler.handleOnFinalize,
|
|
105
|
+
contentPanGestureHandler.handleOnStart,
|
|
106
|
+
]);
|
|
107
|
+
//#endregion
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<GestureDetector gesture={draggableGesture}>
|
|
111
|
+
<BottomSheetDraggableContext.Provider value={draggableGesture}>
|
|
112
|
+
<Animated.View style={style} {...rest}>
|
|
113
|
+
{children}
|
|
114
|
+
</Animated.View>
|
|
115
|
+
</BottomSheetDraggableContext.Provider>
|
|
116
|
+
</GestureDetector>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const BottomSheetDraggableView = memo(BottomSheetDraggableViewComponent);
|
|
121
|
+
BottomSheetDraggableView.displayName = 'BottomSheetDraggableView';
|
|
122
|
+
|
|
123
|
+
export default BottomSheetDraggableView;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './BottomSheetDraggableView';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ViewProps as RNViewProps } from 'react-native';
|
|
3
|
+
import type { GestureRef } from 'react-native-gesture-handler/lib/typescript/handlers/gestures/gesture';
|
|
4
|
+
|
|
5
|
+
export type BottomSheetDraggableViewProps = RNViewProps & {
|
|
6
|
+
nativeGestureRef?: Exclude<GestureRef, number>;
|
|
7
|
+
refreshControlGestureRef?: Exclude<GestureRef, number>;
|
|
8
|
+
children: ReactNode[] | ReactNode;
|
|
9
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React, { memo, useCallback, useMemo, useRef } from 'react';
|
|
2
|
+
import type { LayoutChangeEvent } from 'react-native';
|
|
3
|
+
import Animated, { useAnimatedStyle } from 'react-native-reanimated';
|
|
4
|
+
import { KEYBOARD_STATUS } from '../../constants';
|
|
5
|
+
import {
|
|
6
|
+
type BoundingClientRect,
|
|
7
|
+
useBottomSheetInternal,
|
|
8
|
+
useBoundingClientRect,
|
|
9
|
+
} from '../../hooks';
|
|
10
|
+
import { print } from '../../utilities';
|
|
11
|
+
import { styles } from './styles';
|
|
12
|
+
import type { BottomSheetDefaultFooterProps } from './types';
|
|
13
|
+
|
|
14
|
+
function BottomSheetFooterComponent({
|
|
15
|
+
animatedFooterPosition,
|
|
16
|
+
bottomInset = 0,
|
|
17
|
+
style,
|
|
18
|
+
children,
|
|
19
|
+
}: BottomSheetDefaultFooterProps) {
|
|
20
|
+
//#region refs
|
|
21
|
+
const ref = useRef<Animated.View>(null);
|
|
22
|
+
//#endregion
|
|
23
|
+
|
|
24
|
+
//#region hooks
|
|
25
|
+
const { animatedLayoutState, animatedKeyboardState } =
|
|
26
|
+
useBottomSheetInternal();
|
|
27
|
+
//#endregion
|
|
28
|
+
|
|
29
|
+
//#region styles
|
|
30
|
+
const containerAnimatedStyle = useAnimatedStyle(() => {
|
|
31
|
+
let footerTranslateY = animatedFooterPosition.get();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Offset the bottom inset only when keyboard is not shown
|
|
35
|
+
*/
|
|
36
|
+
if (animatedKeyboardState.get().status !== KEYBOARD_STATUS.SHOWN) {
|
|
37
|
+
footerTranslateY = footerTranslateY - bottomInset;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
transform: [
|
|
42
|
+
{
|
|
43
|
+
translateY: Math.max(0, footerTranslateY),
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
}, [bottomInset, animatedKeyboardState, animatedFooterPosition]);
|
|
48
|
+
const containerStyle = useMemo(
|
|
49
|
+
() => [styles.container, style, containerAnimatedStyle],
|
|
50
|
+
[style, containerAnimatedStyle]
|
|
51
|
+
);
|
|
52
|
+
//#endregion
|
|
53
|
+
|
|
54
|
+
//#region callbacks
|
|
55
|
+
const handleContainerLayout = useCallback(
|
|
56
|
+
({
|
|
57
|
+
nativeEvent: {
|
|
58
|
+
layout: { height },
|
|
59
|
+
},
|
|
60
|
+
}: LayoutChangeEvent) => {
|
|
61
|
+
animatedLayoutState.modify(state => {
|
|
62
|
+
'worklet';
|
|
63
|
+
state.footerHeight = height;
|
|
64
|
+
return state;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (__DEV__) {
|
|
68
|
+
print({
|
|
69
|
+
component: 'BottomSheetFooter',
|
|
70
|
+
method: 'handleContainerLayout',
|
|
71
|
+
category: 'layout',
|
|
72
|
+
params: {
|
|
73
|
+
height,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[animatedLayoutState]
|
|
79
|
+
);
|
|
80
|
+
const handleBoundingClientRect = useCallback(
|
|
81
|
+
({ height }: BoundingClientRect) => {
|
|
82
|
+
animatedLayoutState.modify(state => {
|
|
83
|
+
'worklet';
|
|
84
|
+
state.footerHeight = height;
|
|
85
|
+
return state;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (__DEV__) {
|
|
89
|
+
print({
|
|
90
|
+
component: 'BottomSheetFooter',
|
|
91
|
+
method: 'handleBoundingClientRect',
|
|
92
|
+
category: 'layout',
|
|
93
|
+
params: {
|
|
94
|
+
height,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
[animatedLayoutState]
|
|
100
|
+
);
|
|
101
|
+
//#endregion
|
|
102
|
+
|
|
103
|
+
//#region effects
|
|
104
|
+
useBoundingClientRect(ref, handleBoundingClientRect);
|
|
105
|
+
//#endregion
|
|
106
|
+
|
|
107
|
+
return children !== null ? (
|
|
108
|
+
<Animated.View
|
|
109
|
+
ref={ref}
|
|
110
|
+
onLayout={handleContainerLayout}
|
|
111
|
+
style={containerStyle}
|
|
112
|
+
>
|
|
113
|
+
{children}
|
|
114
|
+
</Animated.View>
|
|
115
|
+
) : null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const BottomSheetFooter = memo(BottomSheetFooterComponent);
|
|
119
|
+
BottomSheetFooter.displayName = 'BottomSheetFooter';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { memo } from 'react';
|
|
2
|
+
import { useDerivedValue } from 'react-native-reanimated';
|
|
3
|
+
import { INITIAL_LAYOUT_VALUE, KEYBOARD_STATUS } from '../../constants';
|
|
4
|
+
import { useBottomSheetInternal } from '../../hooks';
|
|
5
|
+
import type { BottomSheetFooterContainerProps } from './types';
|
|
6
|
+
|
|
7
|
+
const BottomSheetFooterContainerComponent = ({
|
|
8
|
+
footerComponent: FooterComponent,
|
|
9
|
+
}: BottomSheetFooterContainerProps) => {
|
|
10
|
+
//#region hooks
|
|
11
|
+
const { animatedLayoutState, animatedPosition, animatedKeyboardState } =
|
|
12
|
+
useBottomSheetInternal();
|
|
13
|
+
//#endregion
|
|
14
|
+
|
|
15
|
+
//#region variables
|
|
16
|
+
const animatedFooterPosition = useDerivedValue(() => {
|
|
17
|
+
const { handleHeight, footerHeight, containerHeight } =
|
|
18
|
+
animatedLayoutState.get();
|
|
19
|
+
if (handleHeight === INITIAL_LAYOUT_VALUE) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { status: keyboardStatus, heightWithinContainer: keyboardHeight } =
|
|
24
|
+
animatedKeyboardState.get();
|
|
25
|
+
const position = animatedPosition.get();
|
|
26
|
+
|
|
27
|
+
let footerTranslateY = Math.max(0, containerHeight - position);
|
|
28
|
+
if (keyboardStatus === KEYBOARD_STATUS.SHOWN) {
|
|
29
|
+
footerTranslateY = footerTranslateY - keyboardHeight;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
footerTranslateY = footerTranslateY - footerHeight - handleHeight;
|
|
33
|
+
return footerTranslateY;
|
|
34
|
+
}, [animatedKeyboardState, animatedPosition, animatedLayoutState]);
|
|
35
|
+
//#endregion
|
|
36
|
+
|
|
37
|
+
return <FooterComponent animatedFooterPosition={animatedFooterPosition} />;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const BottomSheetFooterContainer = memo(
|
|
41
|
+
BottomSheetFooterContainerComponent
|
|
42
|
+
);
|
|
43
|
+
BottomSheetFooterContainer.displayName = 'BottomSheetFooterContainer';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { ViewStyle } from 'react-native';
|
|
3
|
+
import type { SharedValue } from 'react-native-reanimated';
|
|
4
|
+
import type { BottomSheetProps } from '../bottomSheet/types';
|
|
5
|
+
|
|
6
|
+
export interface BottomSheetFooterProps {
|
|
7
|
+
/**
|
|
8
|
+
* Calculated footer animated position.
|
|
9
|
+
*
|
|
10
|
+
* @type SharedValue<number>
|
|
11
|
+
*/
|
|
12
|
+
animatedFooterPosition: SharedValue<number>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface BottomSheetDefaultFooterProps extends BottomSheetFooterProps {
|
|
16
|
+
/**
|
|
17
|
+
* Bottom inset to be added below the footer, usually comes
|
|
18
|
+
* from `react-native-safe-area-context` hook `useSafeArea`.
|
|
19
|
+
*
|
|
20
|
+
* @type number
|
|
21
|
+
* @default 0
|
|
22
|
+
*/
|
|
23
|
+
bottomInset?: number;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Container style.
|
|
27
|
+
*
|
|
28
|
+
* @type ViewStyle
|
|
29
|
+
*/
|
|
30
|
+
style?: ViewStyle;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Component to be placed in the footer.
|
|
34
|
+
*
|
|
35
|
+
* @type {ReactNode|ReactNode[]}
|
|
36
|
+
*/
|
|
37
|
+
children?: ReactNode | ReactNode[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface BottomSheetFooterContainerProps
|
|
41
|
+
extends Required<Pick<BottomSheetProps, 'footerComponent'>> {}
|
package/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { useSharedValue } from 'react-native-reanimated';
|
|
3
|
+
import { GESTURE_SOURCE } from '../../constants';
|
|
4
|
+
import { BottomSheetGestureHandlersContext } from '../../contexts';
|
|
5
|
+
import {
|
|
6
|
+
useBottomSheetInternal,
|
|
7
|
+
useGestureEventsHandlersDefault,
|
|
8
|
+
useGestureHandler,
|
|
9
|
+
} from '../../hooks';
|
|
10
|
+
import type { BottomSheetGestureHandlersProviderProps } from './types';
|
|
11
|
+
|
|
12
|
+
const BottomSheetGestureHandlersProvider = ({
|
|
13
|
+
gestureEventsHandlersHook:
|
|
14
|
+
useGestureEventsHandlers = useGestureEventsHandlersDefault,
|
|
15
|
+
children,
|
|
16
|
+
}: BottomSheetGestureHandlersProviderProps) => {
|
|
17
|
+
//#region variables
|
|
18
|
+
const animatedGestureSource = useSharedValue<GESTURE_SOURCE>(
|
|
19
|
+
GESTURE_SOURCE.UNDETERMINED
|
|
20
|
+
);
|
|
21
|
+
//#endregion
|
|
22
|
+
|
|
23
|
+
//#region hooks
|
|
24
|
+
const { animatedContentGestureState, animatedHandleGestureState } =
|
|
25
|
+
useBottomSheetInternal();
|
|
26
|
+
const { handleOnStart, handleOnChange, handleOnEnd, handleOnFinalize } =
|
|
27
|
+
useGestureEventsHandlers();
|
|
28
|
+
//#endregion
|
|
29
|
+
|
|
30
|
+
//#region gestures
|
|
31
|
+
const contentPanGestureHandler = useGestureHandler(
|
|
32
|
+
GESTURE_SOURCE.CONTENT,
|
|
33
|
+
animatedContentGestureState,
|
|
34
|
+
animatedGestureSource,
|
|
35
|
+
handleOnStart,
|
|
36
|
+
handleOnChange,
|
|
37
|
+
handleOnEnd,
|
|
38
|
+
handleOnFinalize
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const handlePanGestureHandler = useGestureHandler(
|
|
42
|
+
GESTURE_SOURCE.HANDLE,
|
|
43
|
+
animatedHandleGestureState,
|
|
44
|
+
animatedGestureSource,
|
|
45
|
+
handleOnStart,
|
|
46
|
+
handleOnChange,
|
|
47
|
+
handleOnEnd,
|
|
48
|
+
handleOnFinalize
|
|
49
|
+
);
|
|
50
|
+
//#endregion
|
|
51
|
+
|
|
52
|
+
//#region context
|
|
53
|
+
const contextValue = useMemo(
|
|
54
|
+
() => ({
|
|
55
|
+
contentPanGestureHandler,
|
|
56
|
+
handlePanGestureHandler,
|
|
57
|
+
animatedGestureSource,
|
|
58
|
+
}),
|
|
59
|
+
[contentPanGestureHandler, handlePanGestureHandler, animatedGestureSource]
|
|
60
|
+
);
|
|
61
|
+
//#endregion
|
|
62
|
+
return (
|
|
63
|
+
<BottomSheetGestureHandlersContext.Provider value={contextValue}>
|
|
64
|
+
{children}
|
|
65
|
+
</BottomSheetGestureHandlersContext.Provider>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export default BottomSheetGestureHandlersProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './BottomSheetGestureHandlersProvider';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ReactChild } from 'react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { BottomSheetProps } from '../bottomSheet/types';
|
|
4
|
+
|
|
5
|
+
export interface BottomSheetGestureHandlersProviderProps
|
|
6
|
+
extends Pick<BottomSheetProps, 'gestureEventsHandlersHook'> {
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { memo, useMemo } from 'react';
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_ACCESSIBILITY_HINT,
|
|
5
|
+
DEFAULT_ACCESSIBILITY_LABEL,
|
|
6
|
+
DEFAULT_ACCESSIBILITY_ROLE,
|
|
7
|
+
DEFAULT_ACCESSIBLE,
|
|
8
|
+
} from './constants';
|
|
9
|
+
import { styles } from './styles';
|
|
10
|
+
import type { BottomSheetDefaultHandleProps } from './types';
|
|
11
|
+
|
|
12
|
+
function BottomSheetHandleComponent({
|
|
13
|
+
style,
|
|
14
|
+
indicatorStyle: _indicatorStyle,
|
|
15
|
+
accessible = DEFAULT_ACCESSIBLE,
|
|
16
|
+
accessibilityRole = DEFAULT_ACCESSIBILITY_ROLE,
|
|
17
|
+
accessibilityLabel = DEFAULT_ACCESSIBILITY_LABEL,
|
|
18
|
+
accessibilityHint = DEFAULT_ACCESSIBILITY_HINT,
|
|
19
|
+
children,
|
|
20
|
+
}: BottomSheetDefaultHandleProps) {
|
|
21
|
+
//#region styles
|
|
22
|
+
const containerStyle = useMemo(
|
|
23
|
+
() => [styles.container, StyleSheet.flatten(style)],
|
|
24
|
+
[style]
|
|
25
|
+
);
|
|
26
|
+
const indicatorStyle = useMemo(
|
|
27
|
+
() => [styles.indicator, StyleSheet.flatten(_indicatorStyle)],
|
|
28
|
+
[_indicatorStyle]
|
|
29
|
+
);
|
|
30
|
+
//#endregion
|
|
31
|
+
|
|
32
|
+
// render
|
|
33
|
+
return (
|
|
34
|
+
<View
|
|
35
|
+
style={containerStyle}
|
|
36
|
+
accessible={accessible ?? undefined}
|
|
37
|
+
accessibilityRole={accessibilityRole ?? undefined}
|
|
38
|
+
accessibilityLabel={accessibilityLabel ?? undefined}
|
|
39
|
+
accessibilityHint={accessibilityHint ?? undefined}
|
|
40
|
+
collapsable={true}
|
|
41
|
+
>
|
|
42
|
+
<View style={indicatorStyle} />
|
|
43
|
+
{children}
|
|
44
|
+
</View>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const BottomSheetHandle = memo(BottomSheetHandleComponent);
|
|
49
|
+
BottomSheetHandle.displayName = 'BottomSheetHandle';
|
|
50
|
+
|
|
51
|
+
export default BottomSheetHandle;
|