@umituz/react-native-bottom-sheet 1.3.3 → 1.3.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/package.json +10 -5
- package/src/domain/entities/BottomSheet.ts +60 -3
- package/src/presentation/components/BottomSheet.tsx +92 -250
- package/src/presentation/components/BottomSheetModal.tsx +92 -256
- package/src/presentation/components/SafeBottomSheetModalProvider.tsx +5 -30
- package/src/presentation/hooks/useBottomSheet.ts +9 -126
- package/src/presentation/hooks/useBottomSheetModal.ts +9 -126
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-bottom-sheet",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.5",
|
|
4
4
|
"description": "Modern, performant bottom sheets for React Native with preset configurations, keyboard handling, and smooth animations",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"typecheck": "
|
|
9
|
-
"lint": "
|
|
8
|
+
"typecheck": "tsc --noEmit",
|
|
9
|
+
"lint": "eslint . --ext .ts,.tsx",
|
|
10
10
|
"version:patch": "npm version patch -m 'chore: release v%s'",
|
|
11
11
|
"version:minor": "npm version minor -m 'chore: release v%s'",
|
|
12
12
|
"version:major": "npm version major -m 'chore: release v%s'"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"@gorhom/bottom-sheet": ">=4.6.0",
|
|
29
|
-
"@umituz/react-native-design-system": "
|
|
29
|
+
"@umituz/react-native-design-system": "2.0.16",
|
|
30
30
|
"react": ">=18.2.0",
|
|
31
31
|
"react-native": ">=0.74.0",
|
|
32
32
|
"react-native-gesture-handler": ">=2.0.0",
|
|
@@ -34,8 +34,13 @@
|
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@gorhom/bottom-sheet": "^5.0.0",
|
|
37
|
-
"@umituz/react-native-design-system": "latest",
|
|
38
37
|
"@types/react": "~19.1.0",
|
|
38
|
+
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
|
39
|
+
"@typescript-eslint/parser": "^8.50.1",
|
|
40
|
+
"@umituz/react-native-design-system": "2.0.16",
|
|
41
|
+
"eslint": "^9.39.2",
|
|
42
|
+
"eslint-plugin-react": "^7.37.5",
|
|
43
|
+
"eslint-plugin-react-native": "^5.0.0",
|
|
39
44
|
"react": "19.1.0",
|
|
40
45
|
"react-native": "0.81.5",
|
|
41
46
|
"react-native-gesture-handler": "^2.0.0",
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
2
|
|
|
3
3
|
export type SnapPoint = string | number;
|
|
4
|
-
|
|
5
4
|
export type BottomSheetPreset = 'small' | 'medium' | 'large' | 'full' | 'custom';
|
|
6
|
-
|
|
7
5
|
export type KeyboardBehavior = 'interactive' | 'extend' | 'fillParent';
|
|
8
6
|
|
|
9
7
|
export interface BottomSheetConfig {
|
|
@@ -18,6 +16,65 @@ export interface BottomSheetConfig {
|
|
|
18
16
|
enableDynamicSizing?: boolean;
|
|
19
17
|
}
|
|
20
18
|
|
|
19
|
+
export interface BottomSheetRef {
|
|
20
|
+
snapToIndex: (index: number) => void;
|
|
21
|
+
snapToPosition: (position: string | number) => void;
|
|
22
|
+
expand: () => void;
|
|
23
|
+
collapse: () => void;
|
|
24
|
+
close: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface BottomSheetModalRef {
|
|
28
|
+
present: () => void;
|
|
29
|
+
dismiss: () => void;
|
|
30
|
+
snapToIndex: (index: number) => void;
|
|
31
|
+
snapToPosition: (position: string | number) => void;
|
|
32
|
+
expand: () => void;
|
|
33
|
+
collapse: () => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface BottomSheetProps {
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
preset?: BottomSheetPreset;
|
|
39
|
+
snapPoints?: SnapPoint[];
|
|
40
|
+
initialIndex?: number;
|
|
41
|
+
enableBackdrop?: boolean;
|
|
42
|
+
backdropAppearsOnIndex?: number;
|
|
43
|
+
backdropDisappearsOnIndex?: number;
|
|
44
|
+
keyboardBehavior?: KeyboardBehavior;
|
|
45
|
+
enableHandleIndicator?: boolean;
|
|
46
|
+
enablePanDownToClose?: boolean;
|
|
47
|
+
enableDynamicSizing?: boolean;
|
|
48
|
+
onChange?: (index: number) => void;
|
|
49
|
+
onClose?: () => void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface BottomSheetModalProps extends Omit<BottomSheetProps, 'onClose'> {
|
|
53
|
+
onDismiss?: () => void;
|
|
54
|
+
backgroundColor?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface UseBottomSheetReturn {
|
|
58
|
+
sheetRef: React.RefObject<BottomSheetRef | null>;
|
|
59
|
+
open: () => void;
|
|
60
|
+
close: () => void;
|
|
61
|
+
expand: () => void;
|
|
62
|
+
collapse: () => void;
|
|
63
|
+
snapToIndex: (index: number) => void;
|
|
64
|
+
snapToPosition: (position: string | number) => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface UseBottomSheetModalReturn {
|
|
68
|
+
modalRef: React.RefObject<BottomSheetModalRef | null>;
|
|
69
|
+
present: () => void;
|
|
70
|
+
dismiss: () => void;
|
|
71
|
+
snapToIndex: (index: number) => void;
|
|
72
|
+
snapToPosition: (position: string | number) => void;
|
|
73
|
+
expand: () => void;
|
|
74
|
+
collapse: () => void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
21
78
|
export const BottomSheetUtils = {
|
|
22
79
|
getPreset: (preset: BottomSheetPreset): BottomSheetConfig => {
|
|
23
80
|
switch (preset) {
|
|
@@ -1,35 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Bottom Sheet Component
|
|
3
|
-
*
|
|
4
|
-
* Main bottom sheet wrapper using @gorhom/bottom-sheet library.
|
|
5
|
-
* Provides a clean, theme-aware API for bottom sheets across the app.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Snap point support (percentage, fixed, dynamic)
|
|
9
|
-
* - Backdrop with tap-to-close
|
|
10
|
-
* - Keyboard handling (interactive, fillParent, extend)
|
|
11
|
-
* - Theme-aware colors
|
|
12
|
-
* - Handle indicator
|
|
13
|
-
* - Pan down to close
|
|
14
|
-
* - Smooth animations with Reanimated v3
|
|
15
|
-
*
|
|
16
|
-
* Usage:
|
|
17
|
-
* ```tsx
|
|
18
|
-
* const sheetRef = useRef<BottomSheetRef>(null);
|
|
19
|
-
*
|
|
20
|
-
* <BottomSheet
|
|
21
|
-
* ref={sheetRef}
|
|
22
|
-
* preset="medium"
|
|
23
|
-
* onClose={() => console.log('Closed')}
|
|
24
|
-
* >
|
|
25
|
-
* <View>
|
|
26
|
-
* <Text>Bottom Sheet Content</Text>
|
|
27
|
-
* </View>
|
|
28
|
-
* </BottomSheet>
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
import React, { forwardRef, useCallback, useMemo } from 'react';
|
|
1
|
+
import React, { forwardRef, useCallback, useMemo, useImperativeHandle, useRef } from 'react';
|
|
33
2
|
import { StyleSheet } from 'react-native';
|
|
34
3
|
import GorhomBottomSheet, {
|
|
35
4
|
BottomSheetView,
|
|
@@ -39,228 +8,100 @@ import GorhomBottomSheet, {
|
|
|
39
8
|
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
40
9
|
import type {
|
|
41
10
|
BottomSheetConfig,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
KeyboardBehavior,
|
|
11
|
+
BottomSheetRef,
|
|
12
|
+
BottomSheetProps,
|
|
45
13
|
} from '../../domain/entities/BottomSheet';
|
|
46
14
|
import { BottomSheetUtils } from '../../domain/entities/BottomSheet';
|
|
47
15
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*/
|
|
82
|
-
initialIndex?: number;
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Enable backdrop
|
|
86
|
-
*/
|
|
87
|
-
enableBackdrop?: boolean;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Backdrop appears at this snap index
|
|
91
|
-
*/
|
|
92
|
-
backdropAppearsOnIndex?: number;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Backdrop disappears at this snap index
|
|
96
|
-
*/
|
|
97
|
-
backdropDisappearsOnIndex?: number;
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Keyboard behavior strategy
|
|
101
|
-
*/
|
|
102
|
-
keyboardBehavior?: KeyboardBehavior;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Enable handle indicator
|
|
106
|
-
*/
|
|
107
|
-
enableHandleIndicator?: boolean;
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Enable pan down to close
|
|
111
|
-
*/
|
|
112
|
-
enablePanDownToClose?: boolean;
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Enable dynamic sizing
|
|
116
|
-
*/
|
|
117
|
-
enableDynamicSizing?: boolean;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Callback when sheet changes position
|
|
121
|
-
*/
|
|
122
|
-
onChange?: (index: number) => void;
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Callback when sheet closes
|
|
126
|
-
*/
|
|
127
|
-
onClose?: () => void;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Bottom Sheet Component
|
|
132
|
-
*
|
|
133
|
-
* Modern, performant bottom sheet for React Native.
|
|
134
|
-
* Uses @gorhom/bottom-sheet with Reanimated v3.
|
|
135
|
-
*/
|
|
136
|
-
export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>(
|
|
137
|
-
(
|
|
138
|
-
{
|
|
139
|
-
children,
|
|
140
|
-
preset = 'medium',
|
|
141
|
-
snapPoints: customSnapPoints,
|
|
142
|
-
initialIndex,
|
|
143
|
-
enableBackdrop = true,
|
|
144
|
-
backdropAppearsOnIndex,
|
|
145
|
-
backdropDisappearsOnIndex,
|
|
146
|
-
keyboardBehavior = 'interactive',
|
|
147
|
-
enableHandleIndicator = true,
|
|
148
|
-
enablePanDownToClose = true,
|
|
149
|
-
enableDynamicSizing = false,
|
|
150
|
-
onChange,
|
|
151
|
-
onClose,
|
|
152
|
-
},
|
|
153
|
-
ref
|
|
154
|
-
) => {
|
|
155
|
-
const tokens = useAppDesignTokens();
|
|
156
|
-
const sheetRef = React.useRef<GorhomBottomSheet>(null);
|
|
157
|
-
|
|
158
|
-
// Get configuration from preset or custom (must be before useImperativeHandle)
|
|
159
|
-
const config: BottomSheetConfig = useMemo(() => {
|
|
160
|
-
if (customSnapPoints) {
|
|
161
|
-
return BottomSheetUtils.createConfig({
|
|
162
|
-
snapPoints: customSnapPoints,
|
|
163
|
-
initialIndex,
|
|
164
|
-
enableBackdrop,
|
|
165
|
-
backdropAppearsOnIndex,
|
|
166
|
-
backdropDisappearsOnIndex,
|
|
167
|
-
keyboardBehavior,
|
|
168
|
-
enableHandleIndicator,
|
|
169
|
-
enablePanDownToClose,
|
|
170
|
-
enableDynamicSizing,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
return BottomSheetUtils.getPreset(preset);
|
|
174
|
-
}, [
|
|
175
|
-
preset,
|
|
176
|
-
customSnapPoints,
|
|
177
|
-
initialIndex,
|
|
178
|
-
enableBackdrop,
|
|
179
|
-
backdropAppearsOnIndex,
|
|
180
|
-
backdropDisappearsOnIndex,
|
|
181
|
-
keyboardBehavior,
|
|
182
|
-
enableHandleIndicator,
|
|
183
|
-
enablePanDownToClose,
|
|
184
|
-
enableDynamicSizing,
|
|
185
|
-
]);
|
|
186
|
-
|
|
187
|
-
// Render backdrop component (must be before early return to maintain hook order)
|
|
188
|
-
const renderBackdrop = useCallback(
|
|
189
|
-
(props: BottomSheetBackdropProps) =>
|
|
190
|
-
enableBackdrop ? (
|
|
191
|
-
<BottomSheetBackdrop
|
|
192
|
-
{...props}
|
|
193
|
-
appearsOnIndex={config.backdropAppearsOnIndex ?? 0}
|
|
194
|
-
disappearsOnIndex={config.backdropDisappearsOnIndex ?? -1}
|
|
195
|
-
opacity={0.5}
|
|
196
|
-
pressBehavior="close"
|
|
197
|
-
/>
|
|
198
|
-
) : null,
|
|
199
|
-
[enableBackdrop, config.backdropAppearsOnIndex, config.backdropDisappearsOnIndex]
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
// Handle sheet changes (must be before early return to maintain hook order)
|
|
203
|
-
const handleSheetChange = useCallback(
|
|
204
|
-
(index: number) => {
|
|
205
|
-
onChange?.(index);
|
|
206
|
-
if (index === -1) {
|
|
207
|
-
onClose?.();
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
[onChange, onClose]
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
// Expose ref methods
|
|
214
|
-
React.useImperativeHandle(ref, () => ({
|
|
215
|
-
snapToIndex: (index: number) => {
|
|
216
|
-
sheetRef.current?.snapToIndex(index);
|
|
217
|
-
},
|
|
218
|
-
snapToPosition: (position: string | number) => {
|
|
219
|
-
sheetRef.current?.snapToPosition(position);
|
|
220
|
-
},
|
|
221
|
-
expand: () => {
|
|
222
|
-
sheetRef.current?.expand();
|
|
223
|
-
},
|
|
224
|
-
collapse: () => {
|
|
225
|
-
sheetRef.current?.collapse();
|
|
226
|
-
},
|
|
227
|
-
close: () => {
|
|
228
|
-
sheetRef.current?.close();
|
|
229
|
-
},
|
|
230
|
-
}), []);
|
|
231
|
-
|
|
232
|
-
// Ensure valid config
|
|
233
|
-
if (!config.snapPoints || config.snapPoints.length === 0) {
|
|
234
|
-
return null;
|
|
16
|
+
export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>((props, ref) => {
|
|
17
|
+
const {
|
|
18
|
+
children,
|
|
19
|
+
preset = 'medium',
|
|
20
|
+
snapPoints: customSnapPoints,
|
|
21
|
+
initialIndex,
|
|
22
|
+
enableBackdrop = true,
|
|
23
|
+
backdropAppearsOnIndex,
|
|
24
|
+
backdropDisappearsOnIndex,
|
|
25
|
+
keyboardBehavior = 'interactive',
|
|
26
|
+
enableHandleIndicator = true,
|
|
27
|
+
enablePanDownToClose = true,
|
|
28
|
+
enableDynamicSizing = false,
|
|
29
|
+
onChange,
|
|
30
|
+
onClose,
|
|
31
|
+
} = props;
|
|
32
|
+
|
|
33
|
+
const tokens = useAppDesignTokens();
|
|
34
|
+
const sheetRef = useRef<GorhomBottomSheet>(null);
|
|
35
|
+
|
|
36
|
+
const config: BottomSheetConfig = useMemo(() => {
|
|
37
|
+
if (customSnapPoints) {
|
|
38
|
+
return BottomSheetUtils.createConfig({
|
|
39
|
+
snapPoints: customSnapPoints,
|
|
40
|
+
initialIndex,
|
|
41
|
+
enableBackdrop,
|
|
42
|
+
backdropAppearsOnIndex,
|
|
43
|
+
backdropDisappearsOnIndex,
|
|
44
|
+
keyboardBehavior,
|
|
45
|
+
enableHandleIndicator,
|
|
46
|
+
enablePanDownToClose,
|
|
47
|
+
enableDynamicSizing,
|
|
48
|
+
});
|
|
235
49
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
)
|
|
50
|
+
return BottomSheetUtils.getPreset(preset);
|
|
51
|
+
}, [preset, customSnapPoints, initialIndex, enableBackdrop, backdropAppearsOnIndex, backdropDisappearsOnIndex, keyboardBehavior, enableHandleIndicator, enablePanDownToClose, enableDynamicSizing]);
|
|
52
|
+
|
|
53
|
+
const renderBackdrop = useCallback(
|
|
54
|
+
(backdropProps: BottomSheetBackdropProps) =>
|
|
55
|
+
enableBackdrop ? (
|
|
56
|
+
<BottomSheetBackdrop
|
|
57
|
+
{...backdropProps}
|
|
58
|
+
appearsOnIndex={config.backdropAppearsOnIndex ?? 0}
|
|
59
|
+
disappearsOnIndex={config.backdropDisappearsOnIndex ?? -1}
|
|
60
|
+
opacity={0.5}
|
|
61
|
+
pressBehavior="close"
|
|
62
|
+
/>
|
|
63
|
+
) : null,
|
|
64
|
+
[enableBackdrop, config.backdropAppearsOnIndex, config.backdropDisappearsOnIndex]
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const handleSheetChange = useCallback(
|
|
68
|
+
(index: number) => {
|
|
69
|
+
onChange?.(index);
|
|
70
|
+
if (index === -1) onClose?.();
|
|
71
|
+
},
|
|
72
|
+
[onChange, onClose]
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
useImperativeHandle(ref, () => ({
|
|
76
|
+
snapToIndex: (index: number) => sheetRef.current?.snapToIndex(index),
|
|
77
|
+
snapToPosition: (pos: string | number) => sheetRef.current?.snapToPosition(pos),
|
|
78
|
+
expand: () => sheetRef.current?.expand(),
|
|
79
|
+
collapse: () => sheetRef.current?.collapse(),
|
|
80
|
+
close: () => sheetRef.current?.close(),
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
if (!config.snapPoints || config.snapPoints.length === 0) return null;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<GorhomBottomSheet
|
|
87
|
+
ref={sheetRef}
|
|
88
|
+
index={-1}
|
|
89
|
+
snapPoints={config.snapPoints}
|
|
90
|
+
enableDynamicSizing={config.enableDynamicSizing}
|
|
91
|
+
backdropComponent={renderBackdrop}
|
|
92
|
+
keyboardBehavior={config.keyboardBehavior}
|
|
93
|
+
enableHandlePanningGesture={config.enableHandleIndicator}
|
|
94
|
+
enablePanDownToClose={config.enablePanDownToClose}
|
|
95
|
+
onChange={handleSheetChange}
|
|
96
|
+
backgroundStyle={[styles.background, { backgroundColor: tokens.colors.surface }]}
|
|
97
|
+
handleIndicatorStyle={[styles.handleIndicator, { backgroundColor: tokens.colors.border }]}
|
|
98
|
+
>
|
|
99
|
+
<BottomSheetView style={styles.contentContainer}>
|
|
100
|
+
{children}
|
|
101
|
+
</BottomSheetView>
|
|
102
|
+
</GorhomBottomSheet>
|
|
103
|
+
);
|
|
104
|
+
});
|
|
264
105
|
|
|
265
106
|
BottomSheet.displayName = 'BottomSheet';
|
|
266
107
|
|
|
@@ -278,3 +119,4 @@ const styles = StyleSheet.create({
|
|
|
278
119
|
flex: 1,
|
|
279
120
|
},
|
|
280
121
|
});
|
|
122
|
+
|
|
@@ -1,36 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* BottomSheetModal Component
|
|
3
|
-
*
|
|
4
|
-
* Modal version of bottom sheet using @gorhom/bottom-sheet.
|
|
5
|
-
* Provides a clean, theme-aware API for modal bottom sheets.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Modal presentation (overlays entire screen)
|
|
9
|
-
* - Snap point support (percentage, fixed, dynamic)
|
|
10
|
-
* - Backdrop with tap-to-close
|
|
11
|
-
* - Keyboard handling (interactive, fillParent, extend)
|
|
12
|
-
* - Theme-aware colors
|
|
13
|
-
* - Handle indicator
|
|
14
|
-
* - Pan down to close
|
|
15
|
-
* - Smooth animations with Reanimated v3
|
|
16
|
-
*
|
|
17
|
-
* Usage:
|
|
18
|
-
* ```tsx
|
|
19
|
-
* const { modalRef, present, dismiss } = useBottomSheetModal();
|
|
20
|
-
*
|
|
21
|
-
* <BottomSheetModal
|
|
22
|
-
* ref={modalRef}
|
|
23
|
-
* preset="medium"
|
|
24
|
-
* onDismiss={() => console.log('Dismissed')}
|
|
25
|
-
* >
|
|
26
|
-
* <View>
|
|
27
|
-
* <Text>Modal Content</Text>
|
|
28
|
-
* </View>
|
|
29
|
-
* </BottomSheetModal>
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
import React, { forwardRef, useCallback, useMemo } from 'react';
|
|
1
|
+
import React, { forwardRef, useCallback, useMemo, useImperativeHandle, useRef } from 'react';
|
|
34
2
|
import { StyleSheet } from 'react-native';
|
|
35
3
|
import {
|
|
36
4
|
BottomSheetModal as GorhomBottomSheetModal,
|
|
@@ -41,233 +9,101 @@ import {
|
|
|
41
9
|
import { useAppDesignTokens } from '@umituz/react-native-design-system';
|
|
42
10
|
import type {
|
|
43
11
|
BottomSheetConfig,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
KeyboardBehavior,
|
|
12
|
+
BottomSheetModalRef,
|
|
13
|
+
BottomSheetModalProps,
|
|
47
14
|
} from '../../domain/entities/BottomSheet';
|
|
48
15
|
import { BottomSheetUtils } from '../../domain/entities/BottomSheet';
|
|
49
16
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Enable handle indicator
|
|
108
|
-
*/
|
|
109
|
-
enableHandleIndicator?: boolean;
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Enable pan down to close
|
|
113
|
-
*/
|
|
114
|
-
enablePanDownToClose?: boolean;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Enable dynamic sizing
|
|
118
|
-
*/
|
|
119
|
-
enableDynamicSizing?: boolean;
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Callback when sheet changes position
|
|
123
|
-
*/
|
|
124
|
-
onChange?: (index: number) => void;
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Callback when sheet is dismissed
|
|
128
|
-
*/
|
|
129
|
-
onDismiss?: () => void;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Background color of the sheet
|
|
133
|
-
*/
|
|
134
|
-
backgroundColor?: string;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Bottom Sheet Modal Component
|
|
139
|
-
*
|
|
140
|
-
* Modal version of bottom sheet for React Native.
|
|
141
|
-
* Uses @gorhom/bottom-sheet with Reanimated v3.
|
|
142
|
-
*/
|
|
143
|
-
export const BottomSheetModal = forwardRef<BottomSheetModalRef, BottomSheetModalProps>(
|
|
144
|
-
(
|
|
145
|
-
{
|
|
146
|
-
children,
|
|
147
|
-
preset = 'medium',
|
|
148
|
-
snapPoints: customSnapPoints,
|
|
149
|
-
initialIndex,
|
|
150
|
-
enableBackdrop = true,
|
|
151
|
-
backdropAppearsOnIndex,
|
|
152
|
-
backdropDisappearsOnIndex,
|
|
153
|
-
keyboardBehavior = 'interactive',
|
|
154
|
-
enableHandleIndicator = true,
|
|
155
|
-
enablePanDownToClose = true,
|
|
156
|
-
enableDynamicSizing = false,
|
|
157
|
-
onChange,
|
|
158
|
-
onDismiss,
|
|
159
|
-
backgroundColor,
|
|
17
|
+
export const BottomSheetModal = forwardRef<BottomSheetModalRef, BottomSheetModalProps>((props, ref) => {
|
|
18
|
+
const {
|
|
19
|
+
children,
|
|
20
|
+
preset = 'medium',
|
|
21
|
+
snapPoints: customSnapPoints,
|
|
22
|
+
initialIndex,
|
|
23
|
+
enableBackdrop = true,
|
|
24
|
+
backdropAppearsOnIndex,
|
|
25
|
+
backdropDisappearsOnIndex,
|
|
26
|
+
keyboardBehavior = 'interactive',
|
|
27
|
+
enableHandleIndicator = true,
|
|
28
|
+
enablePanDownToClose = true,
|
|
29
|
+
enableDynamicSizing = false,
|
|
30
|
+
onChange,
|
|
31
|
+
onDismiss,
|
|
32
|
+
backgroundColor,
|
|
33
|
+
} = props;
|
|
34
|
+
|
|
35
|
+
const tokens = useAppDesignTokens();
|
|
36
|
+
const modalRef = useRef<GorhomBottomSheetModal>(null);
|
|
37
|
+
|
|
38
|
+
const config: BottomSheetConfig = useMemo(() => {
|
|
39
|
+
if (customSnapPoints) {
|
|
40
|
+
return BottomSheetUtils.createConfig({
|
|
41
|
+
snapPoints: customSnapPoints,
|
|
42
|
+
initialIndex,
|
|
43
|
+
enableBackdrop,
|
|
44
|
+
backdropAppearsOnIndex,
|
|
45
|
+
backdropDisappearsOnIndex,
|
|
46
|
+
keyboardBehavior,
|
|
47
|
+
enableHandleIndicator,
|
|
48
|
+
enablePanDownToClose,
|
|
49
|
+
enableDynamicSizing,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return BottomSheetUtils.getPreset(preset);
|
|
53
|
+
}, [preset, customSnapPoints, initialIndex, enableBackdrop, backdropAppearsOnIndex, backdropDisappearsOnIndex, keyboardBehavior, enableHandleIndicator, enablePanDownToClose, enableDynamicSizing]);
|
|
54
|
+
|
|
55
|
+
const renderBackdrop = useCallback(
|
|
56
|
+
(backdropProps: BottomSheetBackdropProps) =>
|
|
57
|
+
enableBackdrop ? (
|
|
58
|
+
<BottomSheetBackdrop
|
|
59
|
+
{...backdropProps}
|
|
60
|
+
appearsOnIndex={config.backdropAppearsOnIndex ?? 0}
|
|
61
|
+
disappearsOnIndex={config.backdropDisappearsOnIndex ?? -1}
|
|
62
|
+
opacity={0.5}
|
|
63
|
+
pressBehavior="close"
|
|
64
|
+
/>
|
|
65
|
+
) : null,
|
|
66
|
+
[enableBackdrop, config.backdropAppearsOnIndex, config.backdropDisappearsOnIndex]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const handleSheetChange = useCallback(
|
|
70
|
+
(index: number) => {
|
|
71
|
+
onChange?.(index);
|
|
72
|
+
if (index === -1) onDismiss?.();
|
|
160
73
|
},
|
|
161
|
-
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// Render backdrop component (must be before early return to maintain hook order)
|
|
196
|
-
const renderBackdrop = useCallback(
|
|
197
|
-
(props: BottomSheetBackdropProps) =>
|
|
198
|
-
enableBackdrop ? (
|
|
199
|
-
<BottomSheetBackdrop
|
|
200
|
-
{...props}
|
|
201
|
-
appearsOnIndex={config.backdropAppearsOnIndex ?? 0}
|
|
202
|
-
disappearsOnIndex={config.backdropDisappearsOnIndex ?? -1}
|
|
203
|
-
opacity={0.5}
|
|
204
|
-
pressBehavior="close"
|
|
205
|
-
/>
|
|
206
|
-
) : null,
|
|
207
|
-
[enableBackdrop, config.backdropAppearsOnIndex, config.backdropDisappearsOnIndex]
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
// Handle sheet changes (must be before early return to maintain hook order)
|
|
211
|
-
const handleSheetChange = useCallback(
|
|
212
|
-
(index: number) => {
|
|
213
|
-
onChange?.(index);
|
|
214
|
-
if (index === -1) {
|
|
215
|
-
onDismiss?.();
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
[onChange, onDismiss]
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
// Expose ref methods
|
|
222
|
-
React.useImperativeHandle(ref, () => ({
|
|
223
|
-
present: () => {
|
|
224
|
-
modalRef.current?.present();
|
|
225
|
-
},
|
|
226
|
-
dismiss: () => {
|
|
227
|
-
modalRef.current?.dismiss();
|
|
228
|
-
},
|
|
229
|
-
snapToIndex: (index: number) => {
|
|
230
|
-
modalRef.current?.snapToIndex(index);
|
|
231
|
-
},
|
|
232
|
-
snapToPosition: (position: string | number) => {
|
|
233
|
-
modalRef.current?.snapToPosition(position);
|
|
234
|
-
},
|
|
235
|
-
expand: () => {
|
|
236
|
-
modalRef.current?.expand();
|
|
237
|
-
},
|
|
238
|
-
collapse: () => {
|
|
239
|
-
modalRef.current?.collapse();
|
|
240
|
-
},
|
|
241
|
-
}), []);
|
|
242
|
-
|
|
243
|
-
return (
|
|
244
|
-
<GorhomBottomSheetModal
|
|
245
|
-
ref={modalRef}
|
|
246
|
-
index={-1}
|
|
247
|
-
snapPoints={config.snapPoints}
|
|
248
|
-
enableDynamicSizing={config.enableDynamicSizing}
|
|
249
|
-
backdropComponent={renderBackdrop}
|
|
250
|
-
keyboardBehavior={config.keyboardBehavior}
|
|
251
|
-
enableHandlePanningGesture={config.enableHandleIndicator}
|
|
252
|
-
enablePanDownToClose={config.enablePanDownToClose}
|
|
253
|
-
onChange={handleSheetChange}
|
|
254
|
-
onDismiss={onDismiss}
|
|
255
|
-
backgroundStyle={[
|
|
256
|
-
styles.background,
|
|
257
|
-
{ backgroundColor: backgroundColor || tokens.colors.surface },
|
|
258
|
-
]}
|
|
259
|
-
handleIndicatorStyle={[
|
|
260
|
-
styles.handleIndicator,
|
|
261
|
-
{ backgroundColor: tokens.colors.border },
|
|
262
|
-
]}
|
|
263
|
-
>
|
|
264
|
-
<BottomSheetView style={styles.contentContainer}>
|
|
265
|
-
{children}
|
|
266
|
-
</BottomSheetView>
|
|
267
|
-
</GorhomBottomSheetModal>
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
);
|
|
74
|
+
[onChange, onDismiss]
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
useImperativeHandle(ref, () => ({
|
|
78
|
+
present: () => modalRef.current?.present(),
|
|
79
|
+
dismiss: () => modalRef.current?.dismiss(),
|
|
80
|
+
snapToIndex: (index: number) => modalRef.current?.snapToIndex(index),
|
|
81
|
+
snapToPosition: (pos: string | number) => modalRef.current?.snapToPosition(pos),
|
|
82
|
+
expand: () => modalRef.current?.expand(),
|
|
83
|
+
collapse: () => modalRef.current?.collapse(),
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<GorhomBottomSheetModal
|
|
88
|
+
ref={modalRef}
|
|
89
|
+
index={-1}
|
|
90
|
+
snapPoints={config.snapPoints}
|
|
91
|
+
enableDynamicSizing={config.enableDynamicSizing}
|
|
92
|
+
backdropComponent={renderBackdrop}
|
|
93
|
+
keyboardBehavior={config.keyboardBehavior}
|
|
94
|
+
enableHandlePanningGesture={config.enableHandleIndicator}
|
|
95
|
+
enablePanDownToClose={config.enablePanDownToClose}
|
|
96
|
+
onChange={handleSheetChange}
|
|
97
|
+
onDismiss={onDismiss}
|
|
98
|
+
backgroundStyle={[styles.background, { backgroundColor: backgroundColor || tokens.colors.surface }]}
|
|
99
|
+
handleIndicatorStyle={[styles.handleIndicator, { backgroundColor: tokens.colors.border }]}
|
|
100
|
+
>
|
|
101
|
+
<BottomSheetView style={styles.contentContainer}>
|
|
102
|
+
{children}
|
|
103
|
+
</BottomSheetView>
|
|
104
|
+
</GorhomBottomSheetModal>
|
|
105
|
+
);
|
|
106
|
+
});
|
|
271
107
|
|
|
272
108
|
BottomSheetModal.displayName = 'BottomSheetModal';
|
|
273
109
|
|
|
@@ -1,36 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
* SafeBottomSheetModalProvider
|
|
3
|
-
*
|
|
4
|
-
* Enhanced BottomSheetModalProvider that ensures Reanimated is fully
|
|
5
|
-
* initialized before rendering. This prevents "containerLayoutState.get
|
|
6
|
-
* is not a function" errors that occur when @gorhom/bottom-sheet's
|
|
7
|
-
* internal hooks access Reanimated's layoutState before it's ready.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* ```tsx
|
|
11
|
-
* import { SafeBottomSheetModalProvider } from '@umituz/react-native-bottom-sheet';
|
|
12
|
-
*
|
|
13
|
-
* <SafeBottomSheetModalProvider>
|
|
14
|
-
* <App />
|
|
15
|
-
* </SafeBottomSheetModalProvider>
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import React from 'react';
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
20
2
|
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
|
|
21
3
|
|
|
22
4
|
interface SafeBottomSheetModalProviderProps {
|
|
23
|
-
children:
|
|
5
|
+
children: ReactNode;
|
|
24
6
|
}
|
|
25
7
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* Wrapper around BottomSheetModalProvider for consistency.
|
|
30
|
-
*/
|
|
31
|
-
export const SafeBottomSheetModalProvider: React.FC<SafeBottomSheetModalProviderProps> = ({
|
|
32
|
-
children,
|
|
33
|
-
}) => {
|
|
34
|
-
return <BottomSheetModalProvider>{children}</BottomSheetModalProvider>;
|
|
35
|
-
};
|
|
8
|
+
export const SafeBottomSheetModalProvider = ({ children }: SafeBottomSheetModalProviderProps) => (
|
|
9
|
+
<BottomSheetModalProvider>{children}</BottomSheetModalProvider>
|
|
10
|
+
);
|
|
36
11
|
|
|
@@ -1,133 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useBottomSheet Hook
|
|
3
|
-
*
|
|
4
|
-
* React hook for managing bottom sheet state and interactions.
|
|
5
|
-
* Provides a clean API for common bottom sheet operations.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Open/close bottom sheet
|
|
9
|
-
* - Snap to specific index
|
|
10
|
-
* - Expand to max height
|
|
11
|
-
* - Collapse to min height
|
|
12
|
-
* - Track current position
|
|
13
|
-
* - Preset configurations
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* ```tsx
|
|
17
|
-
* const { sheetRef, open, close, expand, collapse } = useBottomSheet();
|
|
18
|
-
*
|
|
19
|
-
* return (
|
|
20
|
-
* <>
|
|
21
|
-
* <Button onPress={open}>Open Sheet</Button>
|
|
22
|
-
* <BottomSheet ref={sheetRef} preset="medium">
|
|
23
|
-
* <Text>Content</Text>
|
|
24
|
-
* </BottomSheet>
|
|
25
|
-
* </>
|
|
26
|
-
* );
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
1
|
import { useRef, useCallback } from 'react';
|
|
31
|
-
import type { BottomSheetRef } from '
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Return type for useBottomSheet hook
|
|
35
|
-
*/
|
|
36
|
-
export interface UseBottomSheetReturn {
|
|
37
|
-
/**
|
|
38
|
-
* Ref to attach to BottomSheet component
|
|
39
|
-
*/
|
|
40
|
-
sheetRef: React.RefObject<BottomSheetRef | null>;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Open bottom sheet to initial index
|
|
44
|
-
*/
|
|
45
|
-
open: () => void;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Close bottom sheet completely
|
|
49
|
-
*/
|
|
50
|
-
close: () => void;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Expand to maximum height
|
|
54
|
-
*/
|
|
55
|
-
expand: () => void;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Collapse to minimum height
|
|
59
|
-
*/
|
|
60
|
-
collapse: () => void;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Snap to specific index
|
|
64
|
-
*/
|
|
65
|
-
snapToIndex: (index: number) => void;
|
|
2
|
+
import type { BottomSheetRef, UseBottomSheetReturn } from '../../domain/entities/BottomSheet';
|
|
66
3
|
|
|
67
|
-
/**
|
|
68
|
-
* Snap to specific position
|
|
69
|
-
*/
|
|
70
|
-
snapToPosition: (position: string | number) => void;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* useBottomSheet Hook
|
|
75
|
-
*
|
|
76
|
-
* Hook for managing bottom sheet state and interactions.
|
|
77
|
-
* Provides imperative methods for controlling the sheet.
|
|
78
|
-
*/
|
|
79
4
|
export const useBottomSheet = (): UseBottomSheetReturn => {
|
|
80
5
|
const sheetRef = useRef<BottomSheetRef>(null);
|
|
81
6
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Close bottom sheet completely
|
|
91
|
-
*/
|
|
92
|
-
const close = useCallback(() => {
|
|
93
|
-
sheetRef.current?.close();
|
|
94
|
-
}, []);
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Expand to maximum height (last snap point)
|
|
98
|
-
*/
|
|
99
|
-
const expand = useCallback(() => {
|
|
100
|
-
sheetRef.current?.expand();
|
|
101
|
-
}, []);
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Collapse to minimum height (first snap point)
|
|
105
|
-
*/
|
|
106
|
-
const collapse = useCallback(() => {
|
|
107
|
-
sheetRef.current?.collapse();
|
|
108
|
-
}, []);
|
|
7
|
+
const open = useCallback(() => sheetRef.current?.snapToIndex(0), []);
|
|
8
|
+
const close = useCallback(() => sheetRef.current?.close(), []);
|
|
9
|
+
const expand = useCallback(() => sheetRef.current?.expand(), []);
|
|
10
|
+
const collapse = useCallback(() => sheetRef.current?.collapse(), []);
|
|
11
|
+
const snapToIndex = useCallback((index: number) => sheetRef.current?.snapToIndex(index), []);
|
|
12
|
+
const snapToPosition = useCallback((pos: string | number) => sheetRef.current?.snapToPosition(pos), []);
|
|
109
13
|
|
|
110
|
-
|
|
111
|
-
* Snap to specific index
|
|
112
|
-
*/
|
|
113
|
-
const snapToIndex = useCallback((index: number) => {
|
|
114
|
-
sheetRef.current?.snapToIndex(index);
|
|
115
|
-
}, []);
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Snap to specific position (percentage or fixed)
|
|
119
|
-
*/
|
|
120
|
-
const snapToPosition = useCallback((position: string | number) => {
|
|
121
|
-
sheetRef.current?.snapToPosition(position);
|
|
122
|
-
}, []);
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
sheetRef,
|
|
126
|
-
open,
|
|
127
|
-
close,
|
|
128
|
-
expand,
|
|
129
|
-
collapse,
|
|
130
|
-
snapToIndex,
|
|
131
|
-
snapToPosition,
|
|
132
|
-
};
|
|
14
|
+
return { sheetRef, open, close, expand, collapse, snapToIndex, snapToPosition };
|
|
133
15
|
};
|
|
16
|
+
|
|
@@ -1,134 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useBottomSheetModal Hook
|
|
3
|
-
*
|
|
4
|
-
* React hook for managing bottom sheet modal state and interactions.
|
|
5
|
-
* Provides a clean API for common bottom sheet modal operations.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Present/dismiss bottom sheet modal
|
|
9
|
-
* - Snap to specific index
|
|
10
|
-
* - Expand to max height
|
|
11
|
-
* - Collapse to min height
|
|
12
|
-
* - Track current position
|
|
13
|
-
* - Preset configurations
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* ```tsx
|
|
17
|
-
* const { modalRef, present, dismiss, expand, collapse } = useBottomSheetModal();
|
|
18
|
-
*
|
|
19
|
-
* return (
|
|
20
|
-
* <>
|
|
21
|
-
* <Button onPress={present}>Open Modal</Button>
|
|
22
|
-
* <BottomSheetModal ref={modalRef} preset="medium">
|
|
23
|
-
* <Text>Content</Text>
|
|
24
|
-
* </BottomSheetModal>
|
|
25
|
-
* </>
|
|
26
|
-
* );
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
1
|
import { useRef, useCallback } from 'react';
|
|
31
|
-
import type { BottomSheetModalRef } from '
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Return type for useBottomSheetModal hook
|
|
35
|
-
*/
|
|
36
|
-
export interface UseBottomSheetModalReturn {
|
|
37
|
-
/**
|
|
38
|
-
* Ref to attach to BottomSheetModal component
|
|
39
|
-
*/
|
|
40
|
-
modalRef: React.RefObject<BottomSheetModalRef | null>;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Present bottom sheet modal
|
|
44
|
-
*/
|
|
45
|
-
present: () => void;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Dismiss bottom sheet modal
|
|
49
|
-
*/
|
|
50
|
-
dismiss: () => void;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Expand to maximum height
|
|
54
|
-
*/
|
|
55
|
-
expand: () => void;
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Collapse to minimum height
|
|
59
|
-
*/
|
|
60
|
-
collapse: () => void;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Snap to specific index
|
|
64
|
-
*/
|
|
65
|
-
snapToIndex: (index: number) => void;
|
|
2
|
+
import type { BottomSheetModalRef, UseBottomSheetModalReturn } from '../../domain/entities/BottomSheet';
|
|
66
3
|
|
|
67
|
-
/**
|
|
68
|
-
* Snap to specific position
|
|
69
|
-
*/
|
|
70
|
-
snapToPosition: (position: string | number) => void;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* useBottomSheetModal Hook
|
|
75
|
-
*
|
|
76
|
-
* Hook for managing bottom sheet modal state and interactions.
|
|
77
|
-
* Provides imperative methods for controlling the modal.
|
|
78
|
-
*/
|
|
79
4
|
export const useBottomSheetModal = (): UseBottomSheetModalReturn => {
|
|
80
5
|
const modalRef = useRef<BottomSheetModalRef>(null);
|
|
81
6
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Dismiss bottom sheet modal
|
|
91
|
-
*/
|
|
92
|
-
const dismiss = useCallback(() => {
|
|
93
|
-
modalRef.current?.dismiss();
|
|
94
|
-
}, []);
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Expand to maximum height (last snap point)
|
|
98
|
-
*/
|
|
99
|
-
const expand = useCallback(() => {
|
|
100
|
-
modalRef.current?.expand();
|
|
101
|
-
}, []);
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Collapse to minimum height (first snap point)
|
|
105
|
-
*/
|
|
106
|
-
const collapse = useCallback(() => {
|
|
107
|
-
modalRef.current?.collapse();
|
|
108
|
-
}, []);
|
|
7
|
+
const present = useCallback(() => modalRef.current?.present(), []);
|
|
8
|
+
const dismiss = useCallback(() => modalRef.current?.dismiss(), []);
|
|
9
|
+
const expand = useCallback(() => modalRef.current?.expand(), []);
|
|
10
|
+
const collapse = useCallback(() => modalRef.current?.collapse(), []);
|
|
11
|
+
const snapToIndex = useCallback((index: number) => modalRef.current?.snapToIndex(index), []);
|
|
12
|
+
const snapToPosition = useCallback((pos: string | number) => modalRef.current?.snapToPosition(pos), []);
|
|
109
13
|
|
|
110
|
-
|
|
111
|
-
* Snap to specific index
|
|
112
|
-
*/
|
|
113
|
-
const snapToIndex = useCallback((index: number) => {
|
|
114
|
-
modalRef.current?.snapToIndex(index);
|
|
115
|
-
}, []);
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Snap to specific position (percentage or fixed)
|
|
119
|
-
*/
|
|
120
|
-
const snapToPosition = useCallback((position: string | number) => {
|
|
121
|
-
modalRef.current?.snapToPosition(position);
|
|
122
|
-
}, []);
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
modalRef,
|
|
126
|
-
present,
|
|
127
|
-
dismiss,
|
|
128
|
-
expand,
|
|
129
|
-
collapse,
|
|
130
|
-
snapToIndex,
|
|
131
|
-
snapToPosition,
|
|
132
|
-
};
|
|
14
|
+
return { modalRef, present, dismiss, expand, collapse, snapToIndex, snapToPosition };
|
|
133
15
|
};
|
|
134
16
|
|
|
17
|
+
|