@umituz/react-native-design-system 1.5.29 → 1.5.31

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-design-system",
3
- "version": "1.5.29",
3
+ "version": "1.5.31",
4
4
  "description": "Universal design system for React Native apps - Domain-Driven Design architecture with Material Design 3 components",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./dist/index.d.ts",
@@ -37,13 +37,16 @@
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@expo/vector-icons": ">=14.0.0",
40
+ "@gorhom/bottom-sheet": ">=5.0.0",
40
41
  "@react-native-community/datetimepicker": "^8.5.0",
41
42
  "@react-navigation/native": "^6.0.0",
43
+ "@umituz/react-native-bottom-sheet": "*",
42
44
  "@umituz/react-native-icon": "*",
43
45
  "expo-linear-gradient": "^15.0.7",
44
46
  "lucide-react-native": ">=0.468.0",
45
47
  "react": ">=18.2.0 || ^19.0.0",
46
48
  "react-native": ">=0.74.0",
49
+ "react-native-gesture-handler": ">=2.16.0",
47
50
  "react-native-reanimated": "~3.10.1",
48
51
  "react-native-svg": ">=13.0.0",
49
52
  "zustand": "^5.0.2"
@@ -93,6 +96,7 @@
93
96
  ],
94
97
  "dependencies": {
95
98
  "@react-native-async-storage/async-storage": "^2.2.0",
96
- "@umituz/react-native-theme": "^1.2.5"
99
+ "@umituz/react-native-theme": "^1.2.5",
100
+ "@umituz/react-native-typography": "^1.1.0"
97
101
  }
98
102
  }
package/src/index.ts CHANGED
@@ -34,6 +34,12 @@ export {
34
34
  type AtomicTextProps,
35
35
  } from './presentation/atoms/AtomicText';
36
36
 
37
+ // Re-export typography types for convenience (from @umituz/react-native-typography)
38
+ export type {
39
+ TextStyleVariant,
40
+ ColorVariant,
41
+ } from '@umituz/react-native-typography';
42
+
37
43
  export {
38
44
  AtomicCard,
39
45
  type AtomicCardProps,
@@ -30,26 +30,27 @@
30
30
  * ```
31
31
  *
32
32
  * Platform Behavior:
33
- * - iOS: Opens modal with spinner wheel, requires "Done" button
34
- * - Android: Opens native dialog, auto-closes on selection
33
+ * - Opens bottom sheet from bottom with spinner wheel
34
+ * - Requires "Done" button to confirm selection
35
+ * - Can be dismissed by swiping down or tapping backdrop
35
36
  *
36
37
  * @module AtomicDatePicker
37
38
  */
38
39
 
39
- import React, { useState } from 'react';
40
+ import React, { useState, useEffect } from 'react';
40
41
  import {
41
42
  View,
42
43
  Text,
43
44
  TouchableOpacity,
44
45
  StyleSheet,
45
- Modal,
46
46
  useWindowDimensions,
47
47
  } from 'react-native';
48
48
  import DateTimePicker, { DateTimePickerEvent } from '@react-native-community/datetimepicker';
49
49
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
50
50
  import { useAppDesignTokens } from '@umituz/react-native-theme';
51
- import { useResponsive } from '../hooks/useResponsive';
52
51
  import { AtomicIcon, type AtomicIconColor } from './AtomicIcon';
52
+ import { BottomSheet, useBottomSheet } from '@umituz/react-native-bottom-sheet';
53
+ import { AtomicButton } from './AtomicButton';
53
54
 
54
55
  /**
55
56
  * Props for AtomicDatePicker component
@@ -102,28 +103,42 @@ export const AtomicDatePicker: React.FC<AtomicDatePickerProps> = ({
102
103
  const tokens = useAppDesignTokens();
103
104
  const { height } = useWindowDimensions();
104
105
  const insets = useSafeAreaInsets();
105
- const { isTabletDevice } = useResponsive();
106
- const [show, setShow] = useState(false);
106
+ const { sheetRef, open, close } = useBottomSheet();
107
+ const [tempDate, setTempDate] = useState<Date>(value || new Date());
108
+
109
+ // Update tempDate when value prop changes
110
+ useEffect(() => {
111
+ if (value) {
112
+ setTempDate(value);
113
+ }
114
+ }, [value]);
107
115
 
108
116
  /**
109
- * Handle date/time change
110
- * Universal handler that works across all platforms
111
- * Note: event.type can be 'set', 'dismissed', or 'neutralButtonPressed'
117
+ * Handle date/time change in picker
118
+ * Updates temporary date state (not final until Done is pressed)
112
119
  */
113
120
  const handleChange = (event: DateTimePickerEvent, selectedDate?: Date) => {
114
- // Close picker when user confirms or dismisses
115
- // iOS: Stays open until "Done" button (handled separately)
116
- // Android/Web: Auto-closes on selection
117
- if (event.type === 'set' || event.type === 'dismissed') {
118
- setShow(false);
119
- }
120
-
121
- // Update value only if date was selected (not dismissed)
122
121
  if (event.type === 'set' && selectedDate) {
123
- onChange(selectedDate);
122
+ setTempDate(selectedDate);
124
123
  }
125
124
  };
126
125
 
126
+ /**
127
+ * Handle Done button - apply selected date and close sheet
128
+ */
129
+ const handleDone = () => {
130
+ onChange(tempDate);
131
+ close();
132
+ };
133
+
134
+ /**
135
+ * Handle open - reset temp date to current value
136
+ */
137
+ const handleOpen = () => {
138
+ setTempDate(value || new Date());
139
+ open();
140
+ };
141
+
127
142
  /**
128
143
  * Format date based on mode
129
144
  * Uses native Date formatting (locale-aware)
@@ -182,7 +197,7 @@ export const AtomicDatePicker: React.FC<AtomicDatePickerProps> = ({
182
197
  error ? styles.buttonError : undefined,
183
198
  disabled ? styles.buttonDisabled : undefined,
184
199
  ]}
185
- onPress={() => !disabled && setShow(true)}
200
+ onPress={handleOpen}
186
201
  disabled={disabled}
187
202
  testID={testID ? `${testID}-button` : undefined}
188
203
  accessibilityLabel={label || placeholder}
@@ -211,49 +226,39 @@ export const AtomicDatePicker: React.FC<AtomicDatePickerProps> = ({
211
226
  </Text>
212
227
  )}
213
228
 
214
- {/* Universal DatePicker - Works across iOS, Android, Web */}
215
- {show && (
216
- <Modal
217
- transparent
218
- animationType={isTabletDevice ? 'fade' : 'slide'}
219
- visible={show}
220
- onRequestClose={() => setShow(false)}
221
- >
222
- <TouchableOpacity
223
- style={styles.modalOverlay}
224
- activeOpacity={1}
225
- onPress={() => setShow(false)}
226
- accessibilityLabel="Close date picker"
227
- accessibilityRole="button"
228
- >
229
- <View
230
- style={styles.pickerContainer}
231
- onStartShouldSetResponder={() => true}
232
- >
233
- <DateTimePicker
234
- value={value || new Date()}
235
- mode={mode}
236
- display="spinner"
237
- onChange={handleChange}
238
- minimumDate={minimumDate}
239
- maximumDate={maximumDate}
240
- testID={testID ? `${testID}-picker` : undefined}
241
- />
242
- <View style={styles.buttonContainer}>
243
- <TouchableOpacity
244
- style={styles.doneButton}
245
- onPress={() => setShow(false)}
246
- testID={testID ? `${testID}-done` : undefined}
247
- accessibilityLabel="Done"
248
- accessibilityRole="button"
249
- >
250
- <Text style={styles.doneText}>Done</Text>
251
- </TouchableOpacity>
252
- </View>
253
- </View>
254
- </TouchableOpacity>
255
- </Modal>
256
- )}
229
+ {/* Bottom Sheet DatePicker */}
230
+ <BottomSheet
231
+ ref={sheetRef}
232
+ preset="medium"
233
+ enableBackdrop
234
+ enablePanDownToClose
235
+ enableHandleIndicator
236
+ onClose={() => {
237
+ // Reset temp date when closed without saving
238
+ setTempDate(value || new Date());
239
+ }}
240
+ >
241
+ <View style={styles.bottomSheetContent}>
242
+ <DateTimePicker
243
+ value={tempDate}
244
+ mode={mode}
245
+ display="spinner"
246
+ onChange={handleChange}
247
+ minimumDate={minimumDate}
248
+ maximumDate={maximumDate}
249
+ testID={testID ? `${testID}-picker` : undefined}
250
+ />
251
+ <View style={styles.buttonContainer}>
252
+ <AtomicButton
253
+ label="Done"
254
+ onPress={handleDone}
255
+ variant="primary"
256
+ style={styles.doneButton}
257
+ testID={testID ? `${testID}-done` : undefined}
258
+ />
259
+ </View>
260
+ </View>
261
+ </BottomSheet>
257
262
  </View>
258
263
  );
259
264
  };
@@ -316,16 +321,9 @@ const getStyles = (
316
321
  marginTop: tokens.spacing.xs,
317
322
  marginLeft: tokens.spacing.xs,
318
323
  },
319
- modalOverlay: {
320
- flex: 1,
321
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
322
- justifyContent: 'flex-start',
323
- },
324
- pickerContainer: {
325
- backgroundColor: tokens.colors.surface,
326
- borderTopLeftRadius: tokens.borders.radius.xl,
327
- borderTopRightRadius: tokens.borders.radius.xl,
328
- paddingTop: tokens.spacing.lg,
324
+ bottomSheetContent: {
325
+ paddingHorizontal: tokens.spacing.lg,
326
+ paddingTop: tokens.spacing.md,
329
327
  paddingBottom: Math.max(
330
328
  insets.bottom + tokens.spacing.md,
331
329
  tokens.spacing.xl,
@@ -333,22 +331,11 @@ const getStyles = (
333
331
  },
334
332
  buttonContainer: {
335
333
  alignItems: 'center',
336
- marginTop: tokens.spacing.md,
337
- paddingHorizontal: tokens.spacing.lg,
334
+ marginTop: tokens.spacing.lg,
335
+ paddingHorizontal: tokens.spacing.md,
338
336
  },
339
337
  doneButton: {
340
- backgroundColor: tokens.colors.primary,
341
- paddingHorizontal: tokens.spacing.xl,
342
- paddingVertical: tokens.spacing.sm,
343
- borderRadius: tokens.borders.radius.lg,
344
338
  minWidth: buttonMinWidth,
345
- alignItems: 'center',
346
- minHeight: 44, // Apple HIG minimum touch target
347
- },
348
- doneText: {
349
- color: tokens.colors.onPrimary,
350
- fontSize: tokens.typography.bodyLarge.fontSize,
351
- fontWeight: tokens.typography.semibold,
352
339
  },
353
340
  });
354
341
  };
@@ -1,53 +1,17 @@
1
1
  import React from 'react';
2
2
  import { Text } from 'react-native';
3
3
  import { useAppDesignTokens } from '@umituz/react-native-theme';
4
+ import { getTextColor } from '@umituz/react-native-typography';
4
5
  export const AtomicText = ({ children, type = 'bodyMedium', color, numberOfLines, ellipsizeMode, textAlign, style, testID, }) => {
5
6
  const tokens = useAppDesignTokens();
6
7
  // Get typography style from tokens
7
8
  const typographyStyle = tokens.typography[type];
8
- // Get color from tokens or use custom color
9
- const getTextColor = () => {
10
- if (!color) {
11
- return tokens.colors.textPrimary;
12
- }
13
- // Material Design 3 text color mapping
14
- const colorMap = {
15
- // General text colors (Material Design 3)
16
- textPrimary: tokens.colors.textPrimary,
17
- textSecondary: tokens.colors.textSecondary,
18
- textTertiary: tokens.colors.textTertiary,
19
- textDisabled: tokens.colors.textDisabled,
20
- textInverse: tokens.colors.textInverse,
21
- // Text on surfaces (Material Design 3)
22
- onSurface: tokens.colors.onSurface,
23
- onBackground: tokens.colors.onBackground,
24
- // Text on colored backgrounds (Material Design 3)
25
- onPrimary: tokens.colors.onPrimary,
26
- onSecondary: tokens.colors.onSecondary,
27
- onSuccess: tokens.colors.onSuccess,
28
- onError: tokens.colors.onError,
29
- onWarning: tokens.colors.onWarning,
30
- onInfo: tokens.colors.onInfo,
31
- // Semantic colors (can be used as text)
32
- success: tokens.colors.success,
33
- error: tokens.colors.error,
34
- warning: tokens.colors.warning,
35
- info: tokens.colors.info,
36
- // Legacy support (deprecated - maps to new names)
37
- primary: tokens.colors.textPrimary, // Legacy: use textPrimary or onPrimary
38
- secondary: tokens.colors.textSecondary, // Legacy: use textSecondary or onSecondary
39
- tertiary: tokens.colors.textTertiary, // Legacy: use textTertiary
40
- disabled: tokens.colors.textDisabled, // Legacy: use textDisabled
41
- inverse: tokens.colors.textInverse, // Legacy: use textInverse
42
- // Legacy: surfaceVariant is a background color, but used as text - map to textSecondary
43
- surfaceVariant: tokens.colors.textSecondary, // Legacy: use textSecondary instead
44
- };
45
- return colorMap[color] || color;
46
- };
9
+ // Get color from tokens or use custom color using utility function
10
+ const resolvedColor = getTextColor(color, tokens);
47
11
  const textStyle = [
48
12
  typographyStyle,
49
13
  {
50
- color: getTextColor(),
14
+ color: resolvedColor,
51
15
  ...(textAlign && { textAlign }),
52
16
  },
53
17
  style,
@@ -1,62 +1,8 @@
1
1
  import React from 'react';
2
2
  import { Text, StyleProp, TextStyle } from 'react-native';
3
3
  import { useAppDesignTokens } from '@umituz/react-native-theme';
4
-
5
- export type TextStyleVariant =
6
- | 'displayLarge' | 'displayMedium' | 'displaySmall'
7
- | 'headlineLarge' | 'headlineMedium' | 'headlineSmall'
8
- | 'titleLarge' | 'titleMedium' | 'titleSmall'
9
- | 'bodyLarge' | 'bodyMedium' | 'bodySmall'
10
- | 'labelLarge' | 'labelMedium' | 'labelSmall';
11
-
12
- /**
13
- * Material Design 3 Text Color Variants
14
- *
15
- * TEXT COLORS (for text on surfaces):
16
- * - textPrimary, textSecondary, textTertiary: General text colors
17
- * - onSurface, onBackground: Text on surface/background
18
- *
19
- * ON COLORS (for text on colored backgrounds):
20
- * - onPrimary, onSecondary: Text on primary/secondary colored backgrounds
21
- * - onSuccess, onError, onWarning, onInfo: Text on semantic colored backgrounds
22
- *
23
- * SEMANTIC COLORS (can be used as text colors):
24
- * - success, error, warning, info: Semantic colors (can be text or background)
25
- *
26
- * NOTE: 'primary' and 'secondary' are BACKGROUND colors, not text colors.
27
- * Use 'onPrimary'/'onSecondary' for text on colored backgrounds, or
28
- * 'textPrimary'/'textSecondary' for general text.
29
- */
30
- export type ColorVariant =
31
- // General text colors (Material Design 3)
32
- | 'textPrimary'
33
- | 'textSecondary'
34
- | 'textTertiary'
35
- | 'textDisabled'
36
- | 'textInverse'
37
- // Text on surfaces (Material Design 3)
38
- | 'onSurface'
39
- | 'onBackground'
40
- // Text on colored backgrounds (Material Design 3)
41
- | 'onPrimary'
42
- | 'onSecondary'
43
- | 'onSuccess'
44
- | 'onError'
45
- | 'onWarning'
46
- | 'onInfo'
47
- // Semantic colors (can be used as text)
48
- | 'success'
49
- | 'error'
50
- | 'warning'
51
- | 'info'
52
- // Legacy support (deprecated - use textPrimary/textSecondary instead)
53
- | 'primary'
54
- | 'secondary'
55
- | 'tertiary'
56
- | 'disabled'
57
- | 'inverse'
58
- // Legacy: surfaceVariant is a background color, maps to textSecondary
59
- | 'surfaceVariant';
4
+ import type { TextStyleVariant, ColorVariant } from '@umituz/react-native-typography';
5
+ import { getTextColor } from '@umituz/react-native-typography';
60
6
 
61
7
  export interface AtomicTextProps {
62
8
  children: React.ReactNode;
@@ -84,56 +30,13 @@ export const AtomicText: React.FC<AtomicTextProps> = ({
84
30
  // Get typography style from tokens
85
31
  const typographyStyle = tokens.typography[type];
86
32
 
87
- // Get color from tokens or use custom color
88
- const getTextColor = (): string => {
89
- if (!color) {
90
- return tokens.colors.textPrimary;
91
- }
92
-
93
- // Material Design 3 text color mapping
94
- const colorMap: Partial<Record<ColorVariant, string>> = {
95
- // General text colors (Material Design 3)
96
- textPrimary: tokens.colors.textPrimary,
97
- textSecondary: tokens.colors.textSecondary,
98
- textTertiary: tokens.colors.textTertiary,
99
- textDisabled: tokens.colors.textDisabled,
100
- textInverse: tokens.colors.textInverse,
101
-
102
- // Text on surfaces (Material Design 3)
103
- onSurface: tokens.colors.onSurface,
104
- onBackground: tokens.colors.onBackground,
105
-
106
- // Text on colored backgrounds (Material Design 3)
107
- onPrimary: tokens.colors.onPrimary,
108
- onSecondary: tokens.colors.onSecondary,
109
- onSuccess: tokens.colors.onSuccess,
110
- onError: tokens.colors.onError,
111
- onWarning: tokens.colors.onWarning,
112
- onInfo: tokens.colors.onInfo,
113
-
114
- // Semantic colors (can be used as text)
115
- success: tokens.colors.success,
116
- error: tokens.colors.error,
117
- warning: tokens.colors.warning,
118
- info: tokens.colors.info,
119
-
120
- // Legacy support (deprecated - maps to new names)
121
- primary: tokens.colors.textPrimary, // Legacy: use textPrimary or onPrimary
122
- secondary: tokens.colors.textSecondary, // Legacy: use textSecondary or onSecondary
123
- tertiary: tokens.colors.textTertiary, // Legacy: use textTertiary
124
- disabled: tokens.colors.textDisabled, // Legacy: use textDisabled
125
- inverse: tokens.colors.textInverse, // Legacy: use textInverse
126
- // Legacy: surfaceVariant is a background color, but used as text - map to textSecondary
127
- surfaceVariant: tokens.colors.textSecondary, // Legacy: use textSecondary instead
128
- };
129
-
130
- return colorMap[color as ColorVariant] || color;
131
- };
33
+ // Get color from tokens or use custom color using utility function
34
+ const resolvedColor = getTextColor(color, tokens);
132
35
 
133
36
  const textStyle: StyleProp<TextStyle> = [
134
37
  typographyStyle,
135
38
  {
136
- color: getTextColor(),
39
+ color: resolvedColor,
137
40
  ...(textAlign && { textAlign }),
138
41
  },
139
42
  style,