@umituz/react-native-bottom-sheet 1.0.1 → 1.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-bottom-sheet",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
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",
@@ -22,7 +22,7 @@
22
22
  "reanimated",
23
23
  "gesture-handler"
24
24
  ],
25
- "author": "\u00dcmit UZ <umit@umituz.com>",
25
+ "author": "Ümit UZ <umit@umituz.com>",
26
26
  "license": "MIT",
27
27
  "repository": {
28
28
  "type": "git",
@@ -39,6 +39,7 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@gorhom/bottom-sheet": "^5",
42
+ "@react-native-async-storage/async-storage": "^2.2.0",
42
43
  "@types/react": "^18.2.45",
43
44
  "@types/react-native": "^0.73.0",
44
45
  "@umituz/react-native-design-system": "latest",
@@ -47,7 +48,8 @@
47
48
  "react-native": "^0.74.0",
48
49
  "react-native-gesture-handler": "~2.16.1",
49
50
  "react-native-reanimated": "^3.10.1",
50
- "typescript": "^5.3.3"
51
+ "typescript": "^5.3.3",
52
+ "zustand": "^5.0.8"
51
53
  },
52
54
  "publishConfig": {
53
55
  "access": "public"
package/src/index.ts CHANGED
@@ -90,9 +90,21 @@ export {
90
90
  type BottomSheetProps,
91
91
  type BottomSheetRef,
92
92
  } from './presentation/components/BottomSheet';
93
+ export {
94
+ BottomSheetModal,
95
+ type BottomSheetModalProps,
96
+ type BottomSheetModalRef,
97
+ } from './presentation/components/BottomSheetModal';
93
98
 
94
99
  // Presentation Hooks
95
100
  export {
96
101
  useBottomSheet,
97
102
  type UseBottomSheetReturn,
98
103
  } from './presentation/hooks/useBottomSheet';
104
+ export {
105
+ useBottomSheetModal,
106
+ type UseBottomSheetModalReturn,
107
+ } from './presentation/hooks/useBottomSheetModal';
108
+
109
+ // Re-export BottomSheetModalProvider for convenience
110
+ export { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
@@ -56,6 +56,7 @@ export interface BottomSheetRef {
56
56
  close: () => void;
57
57
  }
58
58
 
59
+
59
60
  /**
60
61
  * Bottom sheet component props
61
62
  */
@@ -152,6 +153,26 @@ export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>(
152
153
  ref
153
154
  ) => {
154
155
  const tokens = useAppDesignTokens();
156
+ const sheetRef = React.useRef<GorhomBottomSheet>(null);
157
+
158
+ // Expose ref methods
159
+ React.useImperativeHandle(ref, () => ({
160
+ snapToIndex: (index: number) => {
161
+ sheetRef.current?.snapToIndex(index);
162
+ },
163
+ snapToPosition: (position: string | number) => {
164
+ sheetRef.current?.snapToPosition(position);
165
+ },
166
+ expand: () => {
167
+ sheetRef.current?.expand();
168
+ },
169
+ collapse: () => {
170
+ sheetRef.current?.collapse();
171
+ },
172
+ close: () => {
173
+ sheetRef.current?.close();
174
+ },
175
+ }));
155
176
 
156
177
  // Get configuration from preset or custom
157
178
  const config: BottomSheetConfig = useMemo(() => {
@@ -210,8 +231,8 @@ export const BottomSheet = forwardRef<BottomSheetRef, BottomSheetProps>(
210
231
 
211
232
  return (
212
233
  <GorhomBottomSheet
213
- ref={ref as any}
214
- index={config.initialIndex ?? 0}
234
+ ref={sheetRef}
235
+ index={-1}
215
236
  snapPoints={config.snapPoints}
216
237
  enableDynamicSizing={config.enableDynamicSizing}
217
238
  backdropComponent={renderBackdrop}
@@ -0,0 +1,282 @@
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';
34
+ import { StyleSheet } from 'react-native';
35
+ import {
36
+ BottomSheetModal as GorhomBottomSheetModal,
37
+ BottomSheetView,
38
+ BottomSheetBackdrop,
39
+ type BottomSheetBackdropProps,
40
+ } from '@gorhom/bottom-sheet';
41
+ import { useAppDesignTokens } from '@umituz/react-native-theme';
42
+ import type {
43
+ BottomSheetConfig,
44
+ BottomSheetPreset,
45
+ SnapPoint,
46
+ KeyboardBehavior,
47
+ } from '../../domain/entities/BottomSheet';
48
+ import { BottomSheetUtils } from '../../domain/entities/BottomSheet';
49
+
50
+ /**
51
+ * Bottom sheet modal ref methods
52
+ */
53
+ export interface BottomSheetModalRef {
54
+ present: () => void;
55
+ dismiss: () => void;
56
+ snapToIndex: (index: number) => void;
57
+ snapToPosition: (position: string | number) => void;
58
+ expand: () => void;
59
+ collapse: () => void;
60
+ }
61
+
62
+ /**
63
+ * Bottom sheet modal component props
64
+ */
65
+ export interface BottomSheetModalProps {
66
+ /**
67
+ * Bottom sheet content
68
+ */
69
+ children: React.ReactNode;
70
+
71
+ /**
72
+ * Preset configuration (small, medium, large, full)
73
+ */
74
+ preset?: BottomSheetPreset;
75
+
76
+ /**
77
+ * Custom snap points (overrides preset)
78
+ */
79
+ snapPoints?: SnapPoint[];
80
+
81
+ /**
82
+ * Initial snap point index
83
+ */
84
+ initialIndex?: number;
85
+
86
+ /**
87
+ * Enable backdrop
88
+ */
89
+ enableBackdrop?: boolean;
90
+
91
+ /**
92
+ * Backdrop appears at this snap index
93
+ */
94
+ backdropAppearsOnIndex?: number;
95
+
96
+ /**
97
+ * Backdrop disappears at this snap index
98
+ */
99
+ backdropDisappearsOnIndex?: number;
100
+
101
+ /**
102
+ * Keyboard behavior strategy
103
+ */
104
+ keyboardBehavior?: KeyboardBehavior;
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
+ /**
133
+ * Bottom Sheet Modal Component
134
+ *
135
+ * Modal version of bottom sheet for React Native.
136
+ * Uses @gorhom/bottom-sheet with Reanimated v3.
137
+ */
138
+ export const BottomSheetModal = forwardRef<BottomSheetModalRef, BottomSheetModalProps>(
139
+ (
140
+ {
141
+ children,
142
+ preset = 'medium',
143
+ snapPoints: customSnapPoints,
144
+ initialIndex,
145
+ enableBackdrop = true,
146
+ backdropAppearsOnIndex,
147
+ backdropDisappearsOnIndex,
148
+ keyboardBehavior = 'interactive',
149
+ enableHandleIndicator = true,
150
+ enablePanDownToClose = true,
151
+ enableDynamicSizing = false,
152
+ onChange,
153
+ onDismiss,
154
+ },
155
+ ref
156
+ ) => {
157
+ const tokens = useAppDesignTokens();
158
+ const modalRef = React.useRef<GorhomBottomSheetModal>(null);
159
+
160
+ // Expose ref methods
161
+ React.useImperativeHandle(ref, () => ({
162
+ present: () => {
163
+ modalRef.current?.present();
164
+ },
165
+ dismiss: () => {
166
+ modalRef.current?.dismiss();
167
+ },
168
+ snapToIndex: (index: number) => {
169
+ modalRef.current?.snapToIndex(index);
170
+ },
171
+ snapToPosition: (position: string | number) => {
172
+ modalRef.current?.snapToPosition(position);
173
+ },
174
+ expand: () => {
175
+ modalRef.current?.expand();
176
+ },
177
+ collapse: () => {
178
+ modalRef.current?.collapse();
179
+ },
180
+ }));
181
+
182
+ // Get configuration from preset or custom
183
+ const config: BottomSheetConfig = useMemo(() => {
184
+ if (customSnapPoints) {
185
+ return BottomSheetUtils.createConfig({
186
+ snapPoints: customSnapPoints,
187
+ initialIndex,
188
+ enableBackdrop,
189
+ backdropAppearsOnIndex,
190
+ backdropDisappearsOnIndex,
191
+ keyboardBehavior,
192
+ enableHandleIndicator,
193
+ enablePanDownToClose,
194
+ enableDynamicSizing,
195
+ });
196
+ }
197
+ return BottomSheetUtils.getPreset(preset);
198
+ }, [
199
+ preset,
200
+ customSnapPoints,
201
+ initialIndex,
202
+ enableBackdrop,
203
+ backdropAppearsOnIndex,
204
+ backdropDisappearsOnIndex,
205
+ keyboardBehavior,
206
+ enableHandleIndicator,
207
+ enablePanDownToClose,
208
+ enableDynamicSizing,
209
+ ]);
210
+
211
+ // Render backdrop component
212
+ const renderBackdrop = useCallback(
213
+ (props: BottomSheetBackdropProps) =>
214
+ enableBackdrop ? (
215
+ <BottomSheetBackdrop
216
+ {...props}
217
+ appearsOnIndex={config.backdropAppearsOnIndex ?? 0}
218
+ disappearsOnIndex={config.backdropDisappearsOnIndex ?? -1}
219
+ opacity={0.5}
220
+ pressBehavior="close"
221
+ />
222
+ ) : null,
223
+ [enableBackdrop, config.backdropAppearsOnIndex, config.backdropDisappearsOnIndex]
224
+ );
225
+
226
+ // Handle sheet changes
227
+ const handleSheetChange = useCallback(
228
+ (index: number) => {
229
+ onChange?.(index);
230
+ if (index === -1) {
231
+ onDismiss?.();
232
+ }
233
+ },
234
+ [onChange, onDismiss]
235
+ );
236
+
237
+ return (
238
+ <GorhomBottomSheetModal
239
+ ref={modalRef}
240
+ index={config.initialIndex ?? 0}
241
+ snapPoints={config.snapPoints}
242
+ enableDynamicSizing={config.enableDynamicSizing}
243
+ backdropComponent={renderBackdrop}
244
+ keyboardBehavior={config.keyboardBehavior}
245
+ enableHandlePanningGesture={config.enableHandleIndicator}
246
+ enablePanDownToClose={config.enablePanDownToClose}
247
+ onChange={handleSheetChange}
248
+ onDismiss={onDismiss}
249
+ backgroundStyle={[
250
+ styles.background,
251
+ { backgroundColor: tokens.colors.surface },
252
+ ]}
253
+ handleIndicatorStyle={[
254
+ styles.handleIndicator,
255
+ { backgroundColor: tokens.colors.border },
256
+ ]}
257
+ >
258
+ <BottomSheetView style={styles.contentContainer}>
259
+ {children}
260
+ </BottomSheetView>
261
+ </GorhomBottomSheetModal>
262
+ );
263
+ }
264
+ );
265
+
266
+ BottomSheetModal.displayName = 'BottomSheetModal';
267
+
268
+ const styles = StyleSheet.create({
269
+ background: {
270
+ borderTopLeftRadius: 16,
271
+ borderTopRightRadius: 16,
272
+ },
273
+ handleIndicator: {
274
+ width: 40,
275
+ height: 4,
276
+ borderRadius: 2,
277
+ },
278
+ contentContainer: {
279
+ flex: 1,
280
+ },
281
+ });
282
+
@@ -0,0 +1,134 @@
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
+ import { useRef, useCallback } from 'react';
31
+ import type { BottomSheetModalRef } from '../components/BottomSheetModal';
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>;
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;
66
+
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
+ export const useBottomSheetModal = (): UseBottomSheetModalReturn => {
80
+ const modalRef = useRef<BottomSheetModalRef>(null);
81
+
82
+ /**
83
+ * Present bottom sheet modal
84
+ */
85
+ const present = useCallback(() => {
86
+ modalRef.current?.present();
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
+ }, []);
109
+
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
+ };
133
+ };
134
+