@umituz/react-native-bottom-sheet 1.3.4 → 1.4.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,12 +1,12 @@
1
1
  {
2
2
  "name": "@umituz/react-native-bottom-sheet",
3
- "version": "1.3.4",
4
- "description": "Modern, performant bottom sheets for React Native with preset configurations, keyboard handling, and smooth animations",
3
+ "version": "1.4.0",
4
+ "description": "Modern, performant bottom sheets for React Native with preset configurations, keyboard handling, smooth animations, and advanced filtering capabilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
7
7
  "scripts": {
8
- "typecheck": "echo 'TypeScript validation passed'",
9
- "lint": "echo 'Lint passed'",
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'"
@@ -16,7 +16,9 @@
16
16
  "bottom-sheet",
17
17
  "modal",
18
18
  "sheet",
19
- "overlay"
19
+ "overlay",
20
+ "filter",
21
+ "filtering"
20
22
  ],
21
23
  "author": "Ümit UZ <umit@umituz.com>",
22
24
  "license": "MIT",
@@ -26,20 +28,30 @@
26
28
  },
27
29
  "peerDependencies": {
28
30
  "@gorhom/bottom-sheet": ">=4.6.0",
29
- "@umituz/react-native-design-system": "latest",
31
+ "@umituz/react-native-design-system": "2.0.16",
32
+ "@umituz/react-native-localization": "latest",
30
33
  "react": ">=18.2.0",
31
34
  "react-native": ">=0.74.0",
32
35
  "react-native-gesture-handler": ">=2.0.0",
33
- "react-native-reanimated": ">=3.0.0"
36
+ "react-native-reanimated": ">=3.0.0",
37
+ "react-native-safe-area-context": ">=4.0.0"
34
38
  },
35
39
  "devDependencies": {
36
40
  "@gorhom/bottom-sheet": "^5.0.0",
37
- "@umituz/react-native-design-system": "latest",
38
41
  "@types/react": "~19.1.0",
42
+ "@typescript-eslint/eslint-plugin": "^8.50.1",
43
+ "@typescript-eslint/parser": "^8.50.1",
44
+ "@umituz/react-native-design-system": "2.0.16",
45
+ "@umituz/react-native-localization": "^3.5.25",
46
+ "@umituz/react-native-storage": "^2.4.5",
47
+ "eslint": "^9.39.2",
48
+ "eslint-plugin-react": "^7.37.5",
49
+ "eslint-plugin-react-native": "^5.0.0",
39
50
  "react": "19.1.0",
40
51
  "react-native": "0.81.5",
41
52
  "react-native-gesture-handler": "^2.0.0",
42
53
  "react-native-reanimated": "^3.0.0",
54
+ "react-native-safe-area-context": "^5.6.2",
43
55
  "typescript": "~5.9.2"
44
56
  },
45
57
  "publishConfig": {
@@ -1,9 +1,7 @@
1
- import { Dimensions } from 'react-native';
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) {
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Domain - Filter Entities
3
+ */
4
+
5
+ export interface FilterOption {
6
+ id: string;
7
+ label: string;
8
+ icon?: string;
9
+ type?: string;
10
+ count?: number;
11
+ }
12
+
13
+ export interface FilterCategory {
14
+ id: string;
15
+ title: string;
16
+ options: FilterOption[];
17
+ multiSelect?: boolean;
18
+ }
19
+
20
+ export class FilterUtils {
21
+ static hasActiveFilter(selectedIds: string[], defaultId: string = "all"): boolean {
22
+ if (selectedIds.length === 0) return false;
23
+ if (selectedIds.length === 1 && selectedIds[0] === defaultId) return false;
24
+ return true;
25
+ }
26
+
27
+ static toggleFilter(
28
+ selectedIds: string[],
29
+ filterId: string,
30
+ multiSelect: boolean = false,
31
+ defaultId: string = "all"
32
+ ): string[] {
33
+ if (filterId === defaultId) {
34
+ return [defaultId];
35
+ }
36
+
37
+ if (multiSelect) {
38
+ const newIds = selectedIds.filter((id) => id !== defaultId);
39
+ if (newIds.includes(filterId)) {
40
+ const filtered = newIds.filter((id) => id !== filterId);
41
+ return filtered.length === 0 ? [defaultId] : filtered;
42
+ }
43
+ return [...newIds, filterId];
44
+ }
45
+
46
+ return [filterId];
47
+ }
48
+ }
package/src/index.ts CHANGED
@@ -4,3 +4,9 @@ export * from './presentation/components/SafeBottomSheetModalProvider';
4
4
  export * from './presentation/hooks/useBottomSheet';
5
5
  export * from './presentation/hooks/useBottomSheetModal';
6
6
  export * from './domain/entities/BottomSheet';
7
+
8
+ // Filter exports
9
+ export * from './domain/entities/Filter';
10
+ export * from './presentation/components/filter/FilterSheet';
11
+ export * from './presentation/components/filter/FilterBottomSheet';
12
+ export * from './presentation/hooks/useListFilters';
@@ -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
- BottomSheetPreset,
43
- SnapPoint,
44
- KeyboardBehavior,
11
+ BottomSheetRef,
12
+ BottomSheetProps,
45
13
  } from '../../domain/entities/BottomSheet';
46
14
  import { BottomSheetUtils } from '../../domain/entities/BottomSheet';
47
15
 
48
- /**
49
- * Bottom sheet ref methods
50
- */
51
- export interface BottomSheetRef {
52
- snapToIndex: (index: number) => void;
53
- snapToPosition: (position: string | number) => void;
54
- expand: () => void;
55
- collapse: () => void;
56
- close: () => void;
57
- }
58
-
59
-
60
- /**
61
- * Bottom sheet component props
62
- */
63
- export interface BottomSheetProps {
64
- /**
65
- * Bottom sheet content
66
- */
67
- children: React.ReactNode;
68
-
69
- /**
70
- * Preset configuration (small, medium, large, full)
71
- */
72
- preset?: BottomSheetPreset;
73
-
74
- /**
75
- * Custom snap points (overrides preset)
76
- */
77
- snapPoints?: SnapPoint[];
78
-
79
- /**
80
- * Initial snap point index
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
- return (
238
- <GorhomBottomSheet
239
- ref={sheetRef}
240
- index={-1}
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
- backgroundStyle={[
249
- styles.background,
250
- { backgroundColor: tokens.colors.surface },
251
- ]}
252
- handleIndicatorStyle={[
253
- styles.handleIndicator,
254
- { backgroundColor: tokens.colors.border },
255
- ]}
256
- >
257
- <BottomSheetView style={styles.contentContainer}>
258
- {children}
259
- </BottomSheetView>
260
- </GorhomBottomSheet>
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
+
@@ -9,38 +9,11 @@ import {
9
9
  import { useAppDesignTokens } from '@umituz/react-native-design-system';
10
10
  import type {
11
11
  BottomSheetConfig,
12
- BottomSheetPreset,
13
- SnapPoint,
14
- KeyboardBehavior,
12
+ BottomSheetModalRef,
13
+ BottomSheetModalProps,
15
14
  } from '../../domain/entities/BottomSheet';
16
15
  import { BottomSheetUtils } from '../../domain/entities/BottomSheet';
17
16
 
18
- export interface BottomSheetModalRef {
19
- present: () => void;
20
- dismiss: () => void;
21
- snapToIndex: (index: number) => void;
22
- snapToPosition: (position: string | number) => void;
23
- expand: () => void;
24
- collapse: () => void;
25
- }
26
-
27
- export interface BottomSheetModalProps {
28
- children: React.ReactNode;
29
- preset?: BottomSheetPreset;
30
- snapPoints?: SnapPoint[];
31
- initialIndex?: number;
32
- enableBackdrop?: boolean;
33
- backdropAppearsOnIndex?: number;
34
- backdropDisappearsOnIndex?: number;
35
- keyboardBehavior?: KeyboardBehavior;
36
- enableHandleIndicator?: boolean;
37
- enablePanDownToClose?: boolean;
38
- enableDynamicSizing?: boolean;
39
- onChange?: (index: number) => void;
40
- onDismiss?: () => void;
41
- backgroundColor?: string;
42
- }
43
-
44
17
  export const BottomSheetModal = forwardRef<BottomSheetModalRef, BottomSheetModalProps>((props, ref) => {
45
18
  const {
46
19
  children,
@@ -148,3 +121,4 @@ const styles = StyleSheet.create({
148
121
  flex: 1,
149
122
  },
150
123
  });
124
+
@@ -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: React.ReactNode;
5
+ children: ReactNode;
24
6
  }
25
7
 
26
- /**
27
- * SafeBottomSheetModalProvider
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