@umituz/react-native-design-system 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 (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/package.json +43 -0
  4. package/src/index.ts +345 -0
  5. package/src/presentation/atoms/AtomicAvatar.tsx +157 -0
  6. package/src/presentation/atoms/AtomicAvatarGroup.tsx +169 -0
  7. package/src/presentation/atoms/AtomicBadge.tsx +232 -0
  8. package/src/presentation/atoms/AtomicButton.tsx +124 -0
  9. package/src/presentation/atoms/AtomicCard.tsx +112 -0
  10. package/src/presentation/atoms/AtomicChip.tsx +223 -0
  11. package/src/presentation/atoms/AtomicDatePicker.tsx +347 -0
  12. package/src/presentation/atoms/AtomicDivider.tsx +114 -0
  13. package/src/presentation/atoms/AtomicFab.tsx +104 -0
  14. package/src/presentation/atoms/AtomicFilter.tsx +154 -0
  15. package/src/presentation/atoms/AtomicFormError.tsx +105 -0
  16. package/src/presentation/atoms/AtomicIcon.tsx +29 -0
  17. package/src/presentation/atoms/AtomicImage.tsx +149 -0
  18. package/src/presentation/atoms/AtomicInput.tsx +232 -0
  19. package/src/presentation/atoms/AtomicNumberInput.tsx +182 -0
  20. package/src/presentation/atoms/AtomicPicker.tsx +458 -0
  21. package/src/presentation/atoms/AtomicProgress.tsx +143 -0
  22. package/src/presentation/atoms/AtomicSearchBar.tsx +114 -0
  23. package/src/presentation/atoms/AtomicSkeleton.tsx +146 -0
  24. package/src/presentation/atoms/AtomicSort.tsx +145 -0
  25. package/src/presentation/atoms/AtomicSwitch.tsx +166 -0
  26. package/src/presentation/atoms/AtomicText.tsx +50 -0
  27. package/src/presentation/atoms/AtomicTextArea.tsx +198 -0
  28. package/src/presentation/atoms/AtomicTouchable.tsx +233 -0
  29. package/src/presentation/atoms/fab/styles/fabStyles.ts +69 -0
  30. package/src/presentation/atoms/fab/types/index.ts +88 -0
  31. package/src/presentation/atoms/filter/styles/filterStyles.ts +32 -0
  32. package/src/presentation/atoms/filter/types/index.ts +89 -0
  33. package/src/presentation/atoms/index.ts +378 -0
  34. package/src/presentation/atoms/input/hooks/useInputState.ts +15 -0
  35. package/src/presentation/atoms/input/styles/inputStyles.ts +66 -0
  36. package/src/presentation/atoms/input/types/index.ts +25 -0
  37. package/src/presentation/atoms/picker/styles/pickerStyles.ts +200 -0
  38. package/src/presentation/atoms/picker/types/index.ts +40 -0
  39. package/src/presentation/atoms/touchable/styles/touchableStyles.ts +71 -0
  40. package/src/presentation/atoms/touchable/types/index.ts +162 -0
  41. package/src/presentation/hooks/useAppDesignTokens.ts +78 -0
  42. package/src/presentation/hooks/useResponsive.ts +180 -0
  43. package/src/presentation/loading/index.ts +40 -0
  44. package/src/presentation/loading/presentation/components/LoadingSpinner.tsx +116 -0
  45. package/src/presentation/loading/presentation/components/LoadingState.tsx +200 -0
  46. package/src/presentation/loading/presentation/hooks/useLoading.ts +100 -0
  47. package/src/presentation/molecules/AtomicConfirmationModal.tsx +263 -0
  48. package/src/presentation/molecules/EmptyState.tsx +130 -0
  49. package/src/presentation/molecules/FormField.tsx +128 -0
  50. package/src/presentation/molecules/GridContainer.tsx +124 -0
  51. package/src/presentation/molecules/IconContainer.tsx +94 -0
  52. package/src/presentation/molecules/LanguageSwitcher.tsx +42 -0
  53. package/src/presentation/molecules/ListItem.tsx +36 -0
  54. package/src/presentation/molecules/ScreenHeader.tsx +140 -0
  55. package/src/presentation/molecules/SearchBar.tsx +85 -0
  56. package/src/presentation/molecules/SectionCard.tsx +74 -0
  57. package/src/presentation/molecules/SectionContainer.tsx +106 -0
  58. package/src/presentation/molecules/SectionHeader.tsx +125 -0
  59. package/src/presentation/molecules/confirmation-modal/styles/confirmationModalStyles.ts +133 -0
  60. package/src/presentation/molecules/confirmation-modal/types/index.ts +107 -0
  61. package/src/presentation/molecules/index.ts +42 -0
  62. package/src/presentation/molecules/languageswitcher/config/languageSwitcherConfig.ts +5 -0
  63. package/src/presentation/molecules/languageswitcher/hooks/useLanguageNavigation.ts +15 -0
  64. package/src/presentation/molecules/listitem/styles/listItemStyles.ts +19 -0
  65. package/src/presentation/molecules/listitem/types/index.ts +17 -0
  66. package/src/presentation/organisms/AppHeader.tsx +136 -0
  67. package/src/presentation/organisms/FormContainer.tsx +180 -0
  68. package/src/presentation/organisms/ScreenLayout.tsx +209 -0
  69. package/src/presentation/organisms/index.ts +25 -0
  70. package/src/presentation/tokens/AppDesignTokens.ts +57 -0
  71. package/src/presentation/tokens/commonStyles.ts +253 -0
  72. package/src/presentation/tokens/core/BaseTokens.ts +394 -0
  73. package/src/presentation/tokens/core/ColorPalette.ts +398 -0
  74. package/src/presentation/tokens/core/TokenFactory.ts +120 -0
  75. package/src/presentation/utils/platformConstants.ts +124 -0
  76. package/src/presentation/utils/responsive.ts +516 -0
  77. package/src/presentation/utils/variants/compound.ts +29 -0
  78. package/src/presentation/utils/variants/core.ts +39 -0
  79. package/src/presentation/utils/variants/helpers.ts +13 -0
  80. package/src/presentation/utils/variants.ts +3 -0
@@ -0,0 +1,233 @@
1
+ import React from 'react';
2
+ import { Pressable, View, ActivityIndicator, StyleSheet } from 'react-native';
3
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
4
+ import { AtomicTouchableProps, TouchableFeedback, FeedbackStrength } from './touchable/types';
5
+ import {
6
+ getOpacityValue,
7
+ getTouchableContainerStyle,
8
+ getDisabledStyle,
9
+ getLoadingContainerStyle,
10
+ normalizeHitSlop,
11
+ } from './touchable/styles/touchableStyles';
12
+
13
+ export type {
14
+ AtomicTouchableProps,
15
+ TouchableFeedback,
16
+ FeedbackStrength,
17
+ HitSlop,
18
+ } from './touchable/types';
19
+
20
+ export {
21
+ getOpacityValue,
22
+ getTouchableContainerStyle,
23
+ getDisabledStyle,
24
+ getLoadingContainerStyle,
25
+ normalizeHitSlop,
26
+ } from './touchable/styles/touchableStyles';
27
+
28
+ /**
29
+ * AtomicTouchable - Unified Touchable Component
30
+ *
31
+ * A modern, accessible touchable wrapper using React Native's Pressable API.
32
+ * Provides consistent behavior across iOS, Android, and Web.
33
+ *
34
+ * Features:
35
+ * - Multiple feedback variants (opacity, highlight, ripple, none)
36
+ * - Configurable feedback strength (subtle, normal, strong)
37
+ * - Loading state with indicator
38
+ * - Disabled state with visual feedback
39
+ * - Hit slop customization for small touch targets
40
+ * - Minimum 48x48 touch target (iOS HIG compliance)
41
+ * - Full accessibility support
42
+ * - Theme-aware ripple colors
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // Basic usage with opacity feedback
47
+ * <AtomicTouchable onPress={handlePress}>
48
+ * <AtomicText>Press Me</AtomicText>
49
+ * </AtomicTouchable>
50
+ *
51
+ * // With loading state
52
+ * <AtomicTouchable
53
+ * onPress={handleSubmit}
54
+ * loading={isSubmitting}
55
+ * feedback="highlight"
56
+ * >
57
+ * <AtomicText>Submit</AtomicText>
58
+ * </AtomicTouchable>
59
+ *
60
+ * // With custom hit slop (extends touch area)
61
+ * <AtomicTouchable
62
+ * onPress={handlePress}
63
+ * hitSlop={8}
64
+ * feedback="ripple"
65
+ * >
66
+ * <AtomicIcon name="X" size="sm" />
67
+ * </AtomicTouchable>
68
+ * ```
69
+ */
70
+ export const AtomicTouchable: React.FC<AtomicTouchableProps> = ({
71
+ children,
72
+ onPress,
73
+ onPressIn,
74
+ onPressOut,
75
+ onLongPress,
76
+ feedback = 'opacity',
77
+ strength = 'normal',
78
+ disabled = false,
79
+ loading = false,
80
+ hitSlop,
81
+ style,
82
+ pressedStyle,
83
+ disabledStyle,
84
+ accessibilityLabel,
85
+ accessibilityHint,
86
+ accessibilityRole = 'button',
87
+ testID,
88
+ delayLongPress = 500,
89
+ rippleColor,
90
+ rippleRadius = 0,
91
+ }) => {
92
+ const tokens = useAppDesignTokens();
93
+
94
+ // Determine if touchable should be disabled
95
+ const isDisabled = disabled || loading;
96
+
97
+ // Get opacity value based on strength
98
+ const opacityValue = getOpacityValue(strength);
99
+
100
+ // Normalize hit slop
101
+ const normalizedHitSlop = normalizeHitSlop(hitSlop);
102
+
103
+ // Default ripple color (theme primary with alpha)
104
+ const defaultRippleColor = tokens.colors.primary + '40'; // 40 = 25% opacity in hex
105
+
106
+ /**
107
+ * Get style based on pressed state
108
+ */
109
+ const getPressedStyle = ({ pressed }: { pressed: boolean }) => {
110
+ const baseStyle = [
111
+ getTouchableContainerStyle(),
112
+ style,
113
+ ];
114
+
115
+ if (isDisabled) {
116
+ return [...baseStyle, getDisabledStyle(), disabledStyle];
117
+ }
118
+
119
+ if (pressed) {
120
+ // Apply feedback based on variant
121
+ switch (feedback) {
122
+ case 'opacity':
123
+ return [...baseStyle, { opacity: opacityValue }, pressedStyle];
124
+ case 'highlight':
125
+ return [
126
+ ...baseStyle,
127
+ { backgroundColor: tokens.colors.surfaceVariant },
128
+ pressedStyle,
129
+ ];
130
+ case 'none':
131
+ return [...baseStyle, pressedStyle];
132
+ case 'ripple':
133
+ // Ripple is handled by android_ripple prop
134
+ return [...baseStyle, pressedStyle];
135
+ default:
136
+ return [...baseStyle, pressedStyle];
137
+ }
138
+ }
139
+
140
+ return baseStyle;
141
+ };
142
+
143
+ /**
144
+ * Android ripple configuration
145
+ * Used when feedback='ripple'
146
+ * Pressable automatically ignores this prop on iOS/Web
147
+ */
148
+ const androidRippleConfig = feedback === 'ripple'
149
+ ? {
150
+ color: rippleColor || defaultRippleColor,
151
+ borderless: false,
152
+ radius: rippleRadius,
153
+ }
154
+ : undefined;
155
+
156
+ return (
157
+ <Pressable
158
+ onPress={isDisabled ? undefined : onPress}
159
+ onPressIn={isDisabled ? undefined : onPressIn}
160
+ onPressOut={isDisabled ? undefined : onPressOut}
161
+ onLongPress={isDisabled ? undefined : onLongPress}
162
+ delayLongPress={delayLongPress}
163
+ disabled={isDisabled}
164
+ hitSlop={normalizedHitSlop}
165
+ style={getPressedStyle}
166
+ android_ripple={androidRippleConfig}
167
+ accessibilityLabel={accessibilityLabel}
168
+ accessibilityHint={accessibilityHint}
169
+ accessibilityRole={accessibilityRole}
170
+ accessibilityState={{
171
+ disabled: isDisabled,
172
+ busy: loading,
173
+ }}
174
+ testID={testID}
175
+ >
176
+ {loading ? (
177
+ <View style={getLoadingContainerStyle()}>
178
+ <ActivityIndicator
179
+ size="small"
180
+ color={tokens.colors.primary}
181
+ testID={`${testID}-loading`}
182
+ />
183
+ </View>
184
+ ) : (
185
+ children
186
+ )}
187
+ </Pressable>
188
+ );
189
+ };
190
+
191
+ /**
192
+ * Preset touchable configurations for common use cases
193
+ */
194
+ export const TouchablePresets = {
195
+ /**
196
+ * iOS-style opacity feedback (default)
197
+ */
198
+ ios: {
199
+ feedback: 'opacity' as TouchableFeedback,
200
+ strength: 'normal' as FeedbackStrength,
201
+ },
202
+
203
+ /**
204
+ * Android-style ripple feedback
205
+ */
206
+ android: {
207
+ feedback: 'ripple' as TouchableFeedback,
208
+ strength: 'normal' as FeedbackStrength,
209
+ },
210
+
211
+ /**
212
+ * Subtle feedback for secondary actions
213
+ */
214
+ subtle: {
215
+ feedback: 'opacity' as TouchableFeedback,
216
+ strength: 'subtle' as FeedbackStrength,
217
+ },
218
+
219
+ /**
220
+ * Strong feedback for primary actions
221
+ */
222
+ strong: {
223
+ feedback: 'opacity' as TouchableFeedback,
224
+ strength: 'strong' as FeedbackStrength,
225
+ },
226
+
227
+ /**
228
+ * No visual feedback (use sparingly)
229
+ */
230
+ none: {
231
+ feedback: 'none' as TouchableFeedback,
232
+ },
233
+ };
@@ -0,0 +1,69 @@
1
+ import { ViewStyle } from 'react-native';
2
+ import { FabVariant, FabSize, FabVariantConfig, FabSizeConfig } from '../types';
3
+ import type { DesignTokens } from '../../../tokens/AppDesignTokens';
4
+
5
+ /**
6
+ * Material Design 3 FAB size configurations
7
+ */
8
+ export const FAB_SIZES: Record<FabSize, FabSizeConfig> = {
9
+ sm: {
10
+ width: 40,
11
+ height: 40,
12
+ borderRadius: 20, // Perfect circle
13
+ },
14
+ md: {
15
+ width: 56,
16
+ height: 56,
17
+ borderRadius: 28, // Perfect circle (Material Design 3 standard)
18
+ },
19
+ lg: {
20
+ width: 72,
21
+ height: 72,
22
+ borderRadius: 36, // Perfect circle
23
+ },
24
+ };
25
+
26
+ /**
27
+ * Get FAB variant styles based on design tokens
28
+ * Note: Icon colors are handled via customColor in AtomicIcon
29
+ */
30
+ export const getFabVariants = (tokens: DesignTokens): Record<FabVariant, FabVariantConfig> => ({
31
+ primary: {
32
+ backgroundColor: tokens.colors.primary,
33
+ iconColor: tokens.colors.onPrimary,
34
+ },
35
+ secondary: {
36
+ backgroundColor: tokens.colors.secondary,
37
+ iconColor: tokens.colors.onSecondary,
38
+ },
39
+ surface: {
40
+ backgroundColor: tokens.colors.surface,
41
+ iconColor: tokens.colors.onSurface,
42
+ },
43
+ });
44
+
45
+ /**
46
+ * Get icon size based on FAB size
47
+ * Returns AtomicIconSize type ('sm', 'md', 'lg')
48
+ */
49
+ export const getFabIconSize = (size: FabSize): 'sm' | 'md' | 'lg' => {
50
+ switch (size) {
51
+ case 'sm':
52
+ return 'sm';
53
+ case 'md':
54
+ return 'md';
55
+ case 'lg':
56
+ return 'lg';
57
+ default:
58
+ return 'md';
59
+ }
60
+ };
61
+
62
+ /**
63
+ * Get FAB border for depth (shadows removed per CLAUDE.md)
64
+ * Subtle border provides visual elevation without shadow issues
65
+ */
66
+ export const getFabBorder = (tokens: DesignTokens): ViewStyle => ({
67
+ borderWidth: 1,
68
+ borderColor: tokens.colors.outline,
69
+ });
@@ -0,0 +1,88 @@
1
+ import { StyleProp, ViewStyle } from 'react-native';
2
+
3
+ /**
4
+ * FAB (Floating Action Button) size variants
5
+ * Based on Material Design 3 standards
6
+ */
7
+ export type FabSize = 'sm' | 'md' | 'lg';
8
+
9
+ /**
10
+ * FAB variant types
11
+ * - primary: Main action (uses primary color)
12
+ * - secondary: Secondary action (uses secondary color)
13
+ * - surface: Neutral action (uses surface color with border)
14
+ */
15
+ export type FabVariant = 'primary' | 'secondary' | 'surface';
16
+
17
+ /**
18
+ * FAB configuration for variant styling
19
+ */
20
+ export interface FabVariantConfig {
21
+ backgroundColor: string;
22
+ iconColor: string;
23
+ }
24
+
25
+ /**
26
+ * FAB configuration for size styling
27
+ */
28
+ export interface FabSizeConfig {
29
+ width: number;
30
+ height: number;
31
+ borderRadius: number;
32
+ }
33
+
34
+ /**
35
+ * AtomicFab component props
36
+ */
37
+ export interface AtomicFabProps {
38
+ /**
39
+ * Icon name to display (required)
40
+ * Any MaterialIcons name (see https://fonts.google.com/icons)
41
+ * Examples: 'add', 'edit', 'camera', etc.
42
+ */
43
+ icon: string;
44
+
45
+ /**
46
+ * Callback when FAB is pressed
47
+ */
48
+ onPress: () => void;
49
+
50
+ /**
51
+ * Visual variant of the FAB
52
+ * @default 'primary'
53
+ */
54
+ variant?: FabVariant;
55
+
56
+ /**
57
+ * Size of the FAB
58
+ * @default 'md'
59
+ */
60
+ size?: FabSize;
61
+
62
+ /**
63
+ * Whether the FAB is disabled
64
+ * @default false
65
+ */
66
+ disabled?: boolean;
67
+
68
+ /**
69
+ * Whether to show loading state
70
+ * @default false
71
+ */
72
+ loading?: boolean;
73
+
74
+ /**
75
+ * Custom style for the FAB container
76
+ */
77
+ style?: StyleProp<ViewStyle>;
78
+
79
+ /**
80
+ * Test ID for testing
81
+ */
82
+ testID?: string;
83
+
84
+ /**
85
+ * Accessibility label
86
+ */
87
+ accessibilityLabel?: string;
88
+ }
@@ -0,0 +1,32 @@
1
+ import { ViewStyle } from 'react-native';
2
+
3
+ /**
4
+ * Filter container styles
5
+ * Horizontal scrollable filter chip list
6
+ */
7
+ export const getFilterContainerStyle = (): ViewStyle => ({
8
+ flexDirection: 'row',
9
+ paddingHorizontal: 16,
10
+ paddingVertical: 12,
11
+ gap: 8, // Space between chips
12
+ });
13
+
14
+ /**
15
+ * Clear all button container styles
16
+ */
17
+ export const getClearAllContainerStyle = (): ViewStyle => ({
18
+ marginLeft: 8,
19
+ paddingHorizontal: 12,
20
+ paddingVertical: 6,
21
+ borderRadius: 16,
22
+ justifyContent: 'center',
23
+ alignItems: 'center',
24
+ });
25
+
26
+ /**
27
+ * ScrollView content container style
28
+ */
29
+ export const getScrollContentContainerStyle = (): ViewStyle => ({
30
+ alignItems: 'center',
31
+ gap: 8,
32
+ });
@@ -0,0 +1,89 @@
1
+ import { StyleProp, ViewStyle } from 'react-native';
2
+
3
+ /**
4
+ * Filter option interface
5
+ * Represents a single filterable option
6
+ */
7
+ export interface FilterOption {
8
+ /**
9
+ * Unique identifier for the filter option
10
+ */
11
+ id: string;
12
+
13
+ /**
14
+ * Display label for the filter
15
+ */
16
+ label: string;
17
+
18
+ /**
19
+ * Optional value associated with the filter
20
+ * Can be used for backend filtering
21
+ */
22
+ value?: unknown;
23
+
24
+ /**
25
+ * Optional icon name to display
26
+ */
27
+ icon?: string;
28
+ }
29
+
30
+ /**
31
+ * AtomicFilter component props
32
+ */
33
+ export interface AtomicFilterProps {
34
+ /**
35
+ * Array of filter options to display
36
+ */
37
+ options: FilterOption[];
38
+
39
+ /**
40
+ * Array of currently selected filter IDs
41
+ */
42
+ selectedIds: string[];
43
+
44
+ /**
45
+ * Callback when selection changes
46
+ * @param selectedIds - New array of selected IDs
47
+ */
48
+ onSelectionChange: (selectedIds: string[]) => void;
49
+
50
+ /**
51
+ * Enable multi-select mode
52
+ * @default true
53
+ */
54
+ multiSelect?: boolean;
55
+
56
+ /**
57
+ * Show "Clear All" button when filters are active
58
+ * @default true
59
+ */
60
+ showClearAll?: boolean;
61
+
62
+ /**
63
+ * Chip variant style
64
+ * @default 'outlined'
65
+ */
66
+ variant?: 'filled' | 'outlined' | 'soft';
67
+
68
+ /**
69
+ * Chip color theme
70
+ * @default 'primary'
71
+ */
72
+ color?: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info';
73
+
74
+ /**
75
+ * Chip size
76
+ * @default 'md'
77
+ */
78
+ size?: 'sm' | 'md' | 'lg';
79
+
80
+ /**
81
+ * Custom style for the container
82
+ */
83
+ style?: StyleProp<ViewStyle>;
84
+
85
+ /**
86
+ * Test ID for testing
87
+ */
88
+ testID?: string;
89
+ }