@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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/mock.js +231 -0
  4. package/package.json +107 -0
  5. package/src/components/bottomSheet/BottomSheet.tsx +1885 -0
  6. package/src/components/bottomSheet/BottomSheetBody.tsx +44 -0
  7. package/src/components/bottomSheet/BottomSheetContent.tsx +261 -0
  8. package/src/components/bottomSheet/constants.ts +58 -0
  9. package/src/components/bottomSheet/index.ts +2 -0
  10. package/src/components/bottomSheet/styles.ts +11 -0
  11. package/src/components/bottomSheet/types.d.ts +358 -0
  12. package/src/components/bottomSheetBackdrop/BottomSheetBackdrop.tsx +165 -0
  13. package/src/components/bottomSheetBackdrop/constants.ts +22 -0
  14. package/src/components/bottomSheetBackdrop/index.ts +2 -0
  15. package/src/components/bottomSheetBackdrop/styles.ts +8 -0
  16. package/src/components/bottomSheetBackdrop/types.d.ts +58 -0
  17. package/src/components/bottomSheetBackground/BottomSheetBackground.tsx +20 -0
  18. package/src/components/bottomSheetBackground/BottomSheetBackgroundContainer.tsx +35 -0
  19. package/src/components/bottomSheetBackground/index.ts +2 -0
  20. package/src/components/bottomSheetBackground/styles.ts +9 -0
  21. package/src/components/bottomSheetBackground/types.d.ts +12 -0
  22. package/src/components/bottomSheetDebugView/BottomSheetDebugView.tsx +26 -0
  23. package/src/components/bottomSheetDebugView/ReText.tsx +72 -0
  24. package/src/components/bottomSheetDebugView/ReText.webx.tsx +55 -0
  25. package/src/components/bottomSheetDebugView/index.ts +1 -0
  26. package/src/components/bottomSheetDebugView/styles.ts +19 -0
  27. package/src/components/bottomSheetDebugView/styles.web.ts +20 -0
  28. package/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx +123 -0
  29. package/src/components/bottomSheetDraggableView/index.ts +1 -0
  30. package/src/components/bottomSheetDraggableView/types.d.ts +9 -0
  31. package/src/components/bottomSheetFooter/BottomSheetFooter.tsx +119 -0
  32. package/src/components/bottomSheetFooter/BottomSheetFooterContainer.tsx +43 -0
  33. package/src/components/bottomSheetFooter/index.ts +3 -0
  34. package/src/components/bottomSheetFooter/styles.ts +12 -0
  35. package/src/components/bottomSheetFooter/types.d.ts +41 -0
  36. package/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx +69 -0
  37. package/src/components/bottomSheetGestureHandlersProvider/index.ts +1 -0
  38. package/src/components/bottomSheetGestureHandlersProvider/types.d.ts +8 -0
  39. package/src/components/bottomSheetHandle/BottomSheetHandle.tsx +51 -0
  40. package/src/components/bottomSheetHandle/BottomSheetHandleContainer.tsx +187 -0
  41. package/src/components/bottomSheetHandle/constants.ts +12 -0
  42. package/src/components/bottomSheetHandle/index.ts +6 -0
  43. package/src/components/bottomSheetHandle/styles.ts +23 -0
  44. package/src/components/bottomSheetHandle/types.d.ts +52 -0
  45. package/src/components/bottomSheetHostingContainer/BottomSheetHostingContainer.tsx +130 -0
  46. package/src/components/bottomSheetHostingContainer/index.ts +2 -0
  47. package/src/components/bottomSheetHostingContainer/styles.ts +5 -0
  48. package/src/components/bottomSheetHostingContainer/styles.web.ts +11 -0
  49. package/src/components/bottomSheetHostingContainer/types.d.ts +17 -0
  50. package/src/components/bottomSheetModal/BottomSheetModal.tsx +482 -0
  51. package/src/components/bottomSheetModal/constants.ts +4 -0
  52. package/src/components/bottomSheetModal/index.ts +6 -0
  53. package/src/components/bottomSheetModal/types.d.ts +67 -0
  54. package/src/components/bottomSheetModalProvider/BottomSheetModalProvider.tsx +211 -0
  55. package/src/components/bottomSheetModalProvider/index.ts +1 -0
  56. package/src/components/bottomSheetModalProvider/types.d.ts +12 -0
  57. package/src/components/bottomSheetRefreshControl/BottomSheetRefreshControl.android.tsx +84 -0
  58. package/src/components/bottomSheetRefreshControl/BottomSheetRefreshControl.tsx +1 -0
  59. package/src/components/bottomSheetRefreshControl/index.ts +20 -0
  60. package/src/components/bottomSheetScrollable/BottomSheetDraggableScrollable.tsx +23 -0
  61. package/src/components/bottomSheetScrollable/BottomSheetFlashList.tsx +88 -0
  62. package/src/components/bottomSheetScrollable/BottomSheetFlashList.web.tsx +1 -0
  63. package/src/components/bottomSheetScrollable/BottomSheetFlatList.tsx +26 -0
  64. package/src/components/bottomSheetScrollable/BottomSheetScrollView.tsx +27 -0
  65. package/src/components/bottomSheetScrollable/BottomSheetSectionList.tsx +29 -0
  66. package/src/components/bottomSheetScrollable/BottomSheetVirtualizedList.tsx +27 -0
  67. package/src/components/bottomSheetScrollable/ScrollableContainer.android.tsx +55 -0
  68. package/src/components/bottomSheetScrollable/ScrollableContainer.tsx +22 -0
  69. package/src/components/bottomSheetScrollable/ScrollableContainer.web.tsx +102 -0
  70. package/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx +153 -0
  71. package/src/components/bottomSheetScrollable/index.ts +15 -0
  72. package/src/components/bottomSheetScrollable/styles.ts +8 -0
  73. package/src/components/bottomSheetScrollable/types.d.ts +280 -0
  74. package/src/components/bottomSheetScrollable/useBottomSheetContentSizeSetter.ts +32 -0
  75. package/src/components/bottomSheetTextInput/BottomSheetTextInput.tsx +127 -0
  76. package/src/components/bottomSheetTextInput/index.ts +2 -0
  77. package/src/components/bottomSheetTextInput/types.ts +3 -0
  78. package/src/components/bottomSheetView/BottomSheetView.tsx +93 -0
  79. package/src/components/bottomSheetView/index.ts +1 -0
  80. package/src/components/bottomSheetView/styles.ts +10 -0
  81. package/src/components/bottomSheetView/types.d.ts +24 -0
  82. package/src/components/touchables/Touchables.ios.tsx +5 -0
  83. package/src/components/touchables/Touchables.tsx +5 -0
  84. package/src/components/touchables/index.ts +20 -0
  85. package/src/constants.ts +159 -0
  86. package/src/contexts/external.ts +8 -0
  87. package/src/contexts/gesture.ts +13 -0
  88. package/src/contexts/index.ts +15 -0
  89. package/src/contexts/internal.ts +65 -0
  90. package/src/contexts/modal/external.ts +11 -0
  91. package/src/contexts/modal/internal.ts +25 -0
  92. package/src/hooks/index.ts +29 -0
  93. package/src/hooks/useAnimatedDetents.ts +119 -0
  94. package/src/hooks/useAnimatedKeyboard.ts +174 -0
  95. package/src/hooks/useAnimatedLayout.ts +109 -0
  96. package/src/hooks/useBottomSheet.ts +12 -0
  97. package/src/hooks/useBottomSheetContentContainerStyle.ts +88 -0
  98. package/src/hooks/useBottomSheetGestureHandlers.ts +12 -0
  99. package/src/hooks/useBottomSheetInternal.ts +25 -0
  100. package/src/hooks/useBottomSheetModal.ts +12 -0
  101. package/src/hooks/useBottomSheetModalInternal.ts +25 -0
  102. package/src/hooks/useBottomSheetScrollableCreator.tsx +60 -0
  103. package/src/hooks/useBottomSheetSpringConfigs.ts +11 -0
  104. package/src/hooks/useBottomSheetTimingConfigs.ts +36 -0
  105. package/src/hooks/useBoundingClientRect.ts +77 -0
  106. package/src/hooks/useGestureEventsHandlersDefault.tsx +436 -0
  107. package/src/hooks/useGestureEventsHandlersDefault.web.tsx +418 -0
  108. package/src/hooks/useGestureHandler.ts +90 -0
  109. package/src/hooks/usePropsValidator.ts +108 -0
  110. package/src/hooks/useReactiveSharedValue.ts +45 -0
  111. package/src/hooks/useScrollEventsHandlersDefault.ts +167 -0
  112. package/src/hooks/useScrollHandler.ts +72 -0
  113. package/src/hooks/useScrollHandler.web.ts +181 -0
  114. package/src/hooks/useScrollable.ts +131 -0
  115. package/src/hooks/useScrollableSetter.ts +56 -0
  116. package/src/hooks/useStableCallback.ts +26 -0
  117. package/src/index.ts +79 -0
  118. package/src/types.d.ts +336 -0
  119. package/src/utilities/animate.ts +56 -0
  120. package/src/utilities/clamp.ts +8 -0
  121. package/src/utilities/easingExp.ts +10 -0
  122. package/src/utilities/findNodeHandle.ts +1 -0
  123. package/src/utilities/findNodeHandle.web.ts +33 -0
  124. package/src/utilities/getKeyboardAnimationConfigs.ts +44 -0
  125. package/src/utilities/getRefNativeTag.web.ts +6 -0
  126. package/src/utilities/id.ts +6 -0
  127. package/src/utilities/index.ts +7 -0
  128. package/src/utilities/isFabricInstalled.ts +9 -0
  129. package/src/utilities/logger.ts +55 -0
  130. package/src/utilities/noop.ts +7 -0
  131. package/src/utilities/normalizeSnapPoint.ts +17 -0
  132. package/src/utilities/snapPoint.ts +11 -0
  133. 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,3 @@
1
+ export { BottomSheetFooter } from './BottomSheetFooter';
2
+ export { BottomSheetFooterContainer } from './BottomSheetFooterContainer';
3
+ export type { BottomSheetFooterProps } from './types';
@@ -0,0 +1,12 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ export const styles = StyleSheet.create({
4
+ container: {
5
+ position: 'absolute',
6
+ top: 0,
7
+ left: 0,
8
+ right: 0,
9
+ zIndex: 9999,
10
+ pointerEvents: 'box-none',
11
+ },
12
+ });
@@ -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'>> {}
@@ -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;