@umituz/react-native-design-system 1.14.0 → 2.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 (87) hide show
  1. package/package.json +26 -19
  2. package/src/atoms/AtomicAvatar.tsx +161 -0
  3. package/src/atoms/AtomicButton.tsx +241 -0
  4. package/src/atoms/AtomicChip.tsx +226 -0
  5. package/src/atoms/AtomicDatePicker.tsx +255 -0
  6. package/src/atoms/AtomicFab.tsx +99 -0
  7. package/src/atoms/AtomicIcon.tsx +149 -0
  8. package/src/atoms/AtomicInput.tsx +308 -0
  9. package/src/atoms/AtomicPicker.tsx +310 -0
  10. package/src/atoms/AtomicProgress.tsx +149 -0
  11. package/src/atoms/AtomicText.tsx +55 -0
  12. package/src/atoms/__tests__/AtomicButton.test.tsx +107 -0
  13. package/src/atoms/__tests__/AtomicIcon.test.tsx +110 -0
  14. package/src/atoms/__tests__/AtomicInput.test.tsx +195 -0
  15. package/src/atoms/datepicker/components/DatePickerButton.tsx +112 -0
  16. package/src/atoms/datepicker/components/DatePickerModal.tsx +143 -0
  17. package/src/atoms/fab/styles/fabStyles.ts +98 -0
  18. package/src/atoms/fab/types/index.ts +88 -0
  19. package/src/atoms/index.ts +70 -0
  20. package/src/atoms/input/hooks/useInputState.ts +63 -0
  21. package/src/atoms/input/styles/inputStylesHelper.ts +120 -0
  22. package/src/atoms/picker/components/PickerChips.tsx +57 -0
  23. package/src/atoms/picker/components/PickerModal.tsx +214 -0
  24. package/src/atoms/picker/styles/pickerStyles.ts +223 -0
  25. package/src/atoms/picker/types/index.ts +42 -0
  26. package/src/index.ts +148 -79
  27. package/src/molecules/ConfirmationModal.tsx +42 -0
  28. package/src/molecules/ConfirmationModalContent.tsx +87 -0
  29. package/src/molecules/ConfirmationModalMain.tsx +91 -0
  30. package/src/molecules/FormField.tsx +155 -0
  31. package/src/molecules/IconContainer.tsx +79 -0
  32. package/src/molecules/ListItem.tsx +35 -0
  33. package/src/molecules/ScreenHeader.tsx +171 -0
  34. package/src/molecules/SearchBar.tsx +198 -0
  35. package/src/molecules/confirmation-modal/components.tsx +94 -0
  36. package/src/molecules/confirmation-modal/index.ts +7 -0
  37. package/src/molecules/confirmation-modal/styles/confirmationModalStyles.ts +133 -0
  38. package/src/molecules/confirmation-modal/types/index.ts +41 -0
  39. package/src/molecules/confirmation-modal/useConfirmationModal.ts +50 -0
  40. package/src/molecules/index.ts +19 -0
  41. package/src/molecules/listitem/index.ts +6 -0
  42. package/src/molecules/listitem/styles/listItemStyles.ts +37 -0
  43. package/src/molecules/listitem/types/index.ts +21 -0
  44. package/src/organisms/AppHeader.tsx +136 -0
  45. package/src/organisms/FormContainer.tsx +169 -0
  46. package/src/organisms/ScreenLayout.tsx +183 -0
  47. package/src/organisms/index.ts +31 -0
  48. package/src/responsive/config.ts +139 -0
  49. package/src/responsive/deviceDetection.ts +155 -0
  50. package/src/responsive/gridUtils.ts +79 -0
  51. package/src/responsive/index.ts +52 -0
  52. package/src/responsive/platformConstants.ts +98 -0
  53. package/src/responsive/responsive.ts +61 -0
  54. package/src/responsive/responsiveLayout.ts +137 -0
  55. package/src/responsive/responsiveSizing.ts +134 -0
  56. package/src/responsive/useResponsive.ts +140 -0
  57. package/src/responsive/validation.ts +158 -0
  58. package/src/theme/core/BaseTokens.ts +42 -0
  59. package/src/theme/core/ColorPalette.ts +29 -0
  60. package/src/theme/core/CustomColors.ts +122 -0
  61. package/src/theme/core/NavigationTheme.ts +72 -0
  62. package/src/theme/core/TokenFactory.ts +103 -0
  63. package/src/theme/core/colors/ColorUtils.ts +53 -0
  64. package/src/theme/core/colors/DarkColors.ts +146 -0
  65. package/src/theme/core/colors/LightColors.ts +146 -0
  66. package/src/theme/core/constants/DesignConstants.ts +31 -0
  67. package/src/theme/core/themes.ts +118 -0
  68. package/src/theme/core/tokens/BaseTokens.ts +144 -0
  69. package/src/theme/core/tokens/Borders.ts +43 -0
  70. package/src/theme/core/tokens/Sizes.ts +51 -0
  71. package/src/theme/core/tokens/Spacing.ts +38 -0
  72. package/src/theme/core/tokens/Typography.ts +143 -0
  73. package/src/theme/hooks/useAppDesignTokens.ts +45 -0
  74. package/src/theme/hooks/useCommonStyles.ts +248 -0
  75. package/src/theme/hooks/useThemedStyles.ts +68 -0
  76. package/src/theme/index.ts +94 -0
  77. package/src/theme/infrastructure/globalThemeStore.ts +69 -0
  78. package/src/theme/infrastructure/storage/ThemeStorage.ts +93 -0
  79. package/src/theme/infrastructure/stores/themeStore.ts +109 -0
  80. package/src/typography/__tests__/colorValidationUtils.test.ts +180 -0
  81. package/src/typography/__tests__/textColorUtils.test.ts +185 -0
  82. package/src/typography/__tests__/textStyleUtils.test.ts +168 -0
  83. package/src/typography/domain/entities/TypographyTypes.ts +88 -0
  84. package/src/typography/index.ts +53 -0
  85. package/src/typography/presentation/utils/colorValidationUtils.ts +133 -0
  86. package/src/typography/presentation/utils/textColorUtils.ts +205 -0
  87. package/src/typography/presentation/utils/textStyleUtils.ts +159 -0
@@ -0,0 +1,149 @@
1
+ /**
2
+ * AtomicIcon - Theme-aware Icon Component
3
+ *
4
+ * Uses @umituz/react-native-icons internally
5
+ * Adds theme-aware semantic colors and background support
6
+ */
7
+
8
+ import React from "react";
9
+ import { View, StyleSheet, StyleProp, ViewStyle } from "react-native";
10
+ import { Icon, type IconSize as BaseIconSize, ICON_SIZES } from "@umituz/react-native-icons";
11
+ import { useAppDesignTokens } from '../theme';
12
+
13
+ // Re-export IconSize from icons package for convenience
14
+ export type IconSize = BaseIconSize;
15
+
16
+ // Semantic color names that map to theme tokens
17
+ export type IconColor =
18
+ | "primary"
19
+ | "secondary"
20
+ | "success"
21
+ | "warning"
22
+ | "error"
23
+ | "info"
24
+ | "onSurface"
25
+ | "surfaceVariant"
26
+ | "onPrimary"
27
+ | "onSecondary"
28
+ | "textInverse";
29
+
30
+ // Accept any string for flexibility
31
+ export type IconName = string;
32
+
33
+ export interface AtomicIconProps {
34
+ /** Icon name (Ionicons) */
35
+ name: IconName;
36
+ /** Semantic size preset */
37
+ size?: IconSize;
38
+ /** Custom size in pixels (overrides size) */
39
+ customSize?: number;
40
+ /** Semantic color from theme */
41
+ color?: IconColor;
42
+ /** Custom color (overrides color) */
43
+ customColor?: string;
44
+ /** Add circular background */
45
+ withBackground?: boolean;
46
+ /** Background color */
47
+ backgroundColor?: string;
48
+ /** Accessibility label */
49
+ accessibilityLabel?: string;
50
+ /** Test ID */
51
+ testID?: string;
52
+ /** Additional styles */
53
+ style?: StyleProp<ViewStyle>;
54
+ }
55
+
56
+ const getSemanticColor = (
57
+ color: IconColor,
58
+ tokens: ReturnType<typeof useAppDesignTokens>
59
+ ): string => {
60
+ const colorMap: Record<IconColor, string> = {
61
+ primary: tokens.colors.primary,
62
+ secondary: tokens.colors.secondary,
63
+ success: tokens.colors.success,
64
+ warning: tokens.colors.warning,
65
+ error: tokens.colors.error,
66
+ info: tokens.colors.info,
67
+ onSurface: tokens.colors.onSurface,
68
+ surfaceVariant: tokens.colors.surfaceVariant,
69
+ onPrimary: tokens.colors.onPrimary,
70
+ onSecondary: tokens.colors.onSecondary,
71
+ textInverse: tokens.colors.textInverse,
72
+ };
73
+ return colorMap[color];
74
+ };
75
+
76
+ /**
77
+ * Theme-aware icon component
78
+ *
79
+ * @example
80
+ * <AtomicIcon name="heart-outline" size="md" color="primary" />
81
+ * <AtomicIcon name="star" customSize={32} customColor="#FFD700" />
82
+ * <AtomicIcon name="settings" size="lg" withBackground />
83
+ */
84
+ export const AtomicIcon: React.FC<AtomicIconProps> = React.memo(({
85
+ name,
86
+ size = "md",
87
+ customSize,
88
+ color,
89
+ customColor,
90
+ withBackground = false,
91
+ backgroundColor,
92
+ accessibilityLabel,
93
+ testID,
94
+ style,
95
+ }) => {
96
+ const tokens = useAppDesignTokens();
97
+
98
+ // Calculate size
99
+ const iconSize = customSize ?? (typeof size === "number" ? size : ICON_SIZES[size]);
100
+
101
+ // Calculate color
102
+ const iconColor = customColor
103
+ ? customColor
104
+ : color
105
+ ? getSemanticColor(color, tokens)
106
+ : tokens.colors.textPrimary;
107
+
108
+ if (withBackground) {
109
+ const bgColor = backgroundColor || tokens.colors.surfaceVariant;
110
+ const containerSize = iconSize + 16;
111
+
112
+ return (
113
+ <View
114
+ style={[
115
+ styles.backgroundContainer,
116
+ {
117
+ width: containerSize,
118
+ height: containerSize,
119
+ borderRadius: containerSize / 2,
120
+ backgroundColor: bgColor,
121
+ },
122
+ style,
123
+ ]}
124
+ testID={testID}
125
+ accessibilityLabel={accessibilityLabel}
126
+ >
127
+ <Icon name={name} size={iconSize} color={iconColor} />
128
+ </View>
129
+ );
130
+ }
131
+
132
+ return (
133
+ <View accessibilityLabel={accessibilityLabel} testID={testID} style={style}>
134
+ <Icon name={name} size={iconSize} color={iconColor} />
135
+ </View>
136
+ );
137
+ });
138
+
139
+ AtomicIcon.displayName = "AtomicIcon";
140
+
141
+ const styles = StyleSheet.create({
142
+ backgroundContainer: {
143
+ justifyContent: "center",
144
+ alignItems: "center",
145
+ },
146
+ });
147
+
148
+ // Legacy type alias for backward compatibility
149
+ export type IconProps = AtomicIconProps;
@@ -0,0 +1,308 @@
1
+ import React, { useMemo } from 'react';
2
+ import { View, TextInput, Pressable, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
3
+ import { useAppDesignTokens } from '../theme';
4
+ import { AtomicIcon } from './AtomicIcon';
5
+ import { AtomicText } from './AtomicText';
6
+ import { useInputState } from './input/hooks/useInputState';
7
+ import { getSizeConfig, getVariantStyle, getTextColor } from './input/styles/inputStylesHelper';
8
+ import type { IconName } from './AtomicIcon';
9
+
10
+ export type AtomicInputVariant = 'outlined' | 'filled' | 'flat';
11
+ export type AtomicInputState = 'default' | 'error' | 'success' | 'disabled';
12
+ export type AtomicInputSize = 'sm' | 'md' | 'lg';
13
+
14
+ export interface AtomicInputProps {
15
+ /** Input label */
16
+ label?: string;
17
+ /** Current input value */
18
+ value?: string;
19
+ /** Value change callback */
20
+ onChangeText?: (text: string) => void;
21
+ /** Input variant (outlined, filled, flat) */
22
+ variant?: AtomicInputVariant;
23
+ /** Input state (default, error, success, disabled) */
24
+ state?: AtomicInputState;
25
+ /** Input size (sm, md, lg) */
26
+ size?: AtomicInputSize;
27
+ /** Placeholder text */
28
+ placeholder?: string;
29
+ /** Helper text below input */
30
+ helperText?: string;
31
+ /** Leading icon (Lucide icon name) */
32
+ leadingIcon?: IconName;
33
+ /** Trailing icon (Lucide icon name) */
34
+ trailingIcon?: IconName;
35
+ /** Callback when trailing icon is pressed */
36
+ onTrailingIconPress?: () => void;
37
+ /** Show password toggle for secure inputs */
38
+ showPasswordToggle?: boolean;
39
+ /** Secure text entry (password field) */
40
+ secureTextEntry?: boolean;
41
+ /** Maximum character length */
42
+ maxLength?: number;
43
+ /** Show character counter */
44
+ showCharacterCount?: boolean;
45
+ /** Keyboard type */
46
+ keyboardType?: 'default' | 'email-address' | 'numeric' | 'phone-pad' | 'url' | 'number-pad' | 'decimal-pad';
47
+ /** Auto-capitalize */
48
+ autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
49
+ /** Auto-correct */
50
+ autoCorrect?: boolean;
51
+ /** Disabled state */
52
+ disabled?: boolean;
53
+ /** Container style */
54
+ style?: StyleProp<ViewStyle>;
55
+ /** Input text style */
56
+ inputStyle?: StyleProp<TextStyle>;
57
+ /** Test ID for E2E testing */
58
+ testID?: string;
59
+ /** Blur callback */
60
+ onBlur?: () => void;
61
+ /** Focus callback */
62
+ onFocus?: () => void;
63
+ }
64
+
65
+ /**
66
+ * AtomicInput - Pure React Native Text Input
67
+ *
68
+ * Features:
69
+ * - Pure React Native implementation (no Paper dependency)
70
+ * - Lucide icons for password toggle and custom icons
71
+ * - Outlined/filled/flat variants
72
+ * - Error, success, disabled states
73
+ * - Character counter
74
+ * - Responsive sizing
75
+ * - Full accessibility support
76
+ */
77
+ export const AtomicInput: React.FC<AtomicInputProps> = ({
78
+ variant = 'outlined',
79
+ state = 'default',
80
+ size = 'md',
81
+ label,
82
+ value = '',
83
+ onChangeText,
84
+ placeholder,
85
+ helperText,
86
+ leadingIcon,
87
+ trailingIcon,
88
+ onTrailingIconPress,
89
+ showPasswordToggle = false,
90
+ secureTextEntry = false,
91
+ maxLength,
92
+ showCharacterCount = false,
93
+ keyboardType = 'default',
94
+ autoCapitalize = 'sentences',
95
+ autoCorrect = true,
96
+ disabled = false,
97
+ style,
98
+ inputStyle,
99
+ testID,
100
+ onBlur,
101
+ onFocus,
102
+ }) => {
103
+ const tokens = useAppDesignTokens();
104
+
105
+ const {
106
+ localValue,
107
+ isFocused,
108
+ isPasswordVisible,
109
+ characterCount,
110
+ isAtMaxLength,
111
+ setIsFocused,
112
+ handleTextChange,
113
+ togglePasswordVisibility,
114
+ } = useInputState({
115
+ value,
116
+ onChangeText,
117
+ secureTextEntry,
118
+ showPasswordToggle,
119
+ maxLength,
120
+ showCharacterCount,
121
+ });
122
+
123
+ const isDisabled = state === 'disabled' || disabled;
124
+ const hasError = state === 'error';
125
+ const hasSuccess = state === 'success';
126
+
127
+ const sizeConfig = getSizeConfig({ size, tokens });
128
+ const variantStyle = getVariantStyle({
129
+ variant,
130
+ isFocused,
131
+ hasError,
132
+ hasSuccess,
133
+ isDisabled,
134
+ tokens,
135
+ });
136
+ const textColor = getTextColor({
137
+ isDisabled,
138
+ hasError,
139
+ hasSuccess,
140
+ tokens,
141
+ });
142
+
143
+ const iconColor = isDisabled ? tokens.colors.textDisabled : tokens.colors.textSecondary;
144
+
145
+ const containerStyle: StyleProp<ViewStyle> = [
146
+ styles.container,
147
+ variantStyle,
148
+ {
149
+ paddingTop: sizeConfig.paddingVertical,
150
+ paddingBottom: sizeConfig.paddingVertical,
151
+ paddingHorizontal: sizeConfig.paddingHorizontal,
152
+ minHeight: sizeConfig.minHeight,
153
+ justifyContent: 'center',
154
+ opacity: isDisabled ? 0.5 : 1,
155
+ },
156
+ style,
157
+ ];
158
+
159
+ const textInputStyle: StyleProp<TextStyle> = [
160
+ styles.input,
161
+ {
162
+ fontSize: sizeConfig.fontSize,
163
+ lineHeight: (sizeConfig.fontSize || 16) * 1.2,
164
+ color: textColor,
165
+ paddingVertical: 0,
166
+ },
167
+ leadingIcon ? { paddingLeft: sizeConfig.iconSize + 8 } : undefined,
168
+ (trailingIcon || showPasswordToggle) ? { paddingRight: sizeConfig.iconSize + 8 } : undefined,
169
+ inputStyle,
170
+ ];
171
+
172
+ return (
173
+ <View testID={testID}>
174
+ {label && (
175
+ <AtomicText
176
+ type="labelMedium"
177
+ color={hasError ? 'error' : hasSuccess ? 'success' : 'secondary'}
178
+ style={styles.label}
179
+ >
180
+ {label}
181
+ </AtomicText>
182
+ )}
183
+
184
+ <View style={containerStyle}>
185
+ {leadingIcon && (
186
+ <View style={styles.leadingIcon}>
187
+ <AtomicIcon
188
+ name={leadingIcon}
189
+ customSize={sizeConfig.iconSize}
190
+ customColor={iconColor}
191
+ />
192
+ </View>
193
+ )}
194
+
195
+ <TextInput
196
+ value={localValue}
197
+ onChangeText={handleTextChange}
198
+ placeholder={placeholder}
199
+ placeholderTextColor={tokens.colors.textSecondary}
200
+ secureTextEntry={secureTextEntry && !isPasswordVisible}
201
+ maxLength={maxLength}
202
+ keyboardType={keyboardType}
203
+ autoCapitalize={autoCapitalize}
204
+ autoCorrect={autoCorrect}
205
+ editable={!isDisabled}
206
+ style={textInputStyle}
207
+ onBlur={() => {
208
+ setIsFocused(false);
209
+ onBlur?.();
210
+ }}
211
+ onFocus={() => {
212
+ setIsFocused(true);
213
+ onFocus?.();
214
+ }}
215
+ testID={testID ? `${testID}-input` : undefined}
216
+ />
217
+
218
+ {(showPasswordToggle && secureTextEntry) && (
219
+ <Pressable
220
+ onPress={() => togglePasswordVisibility()}
221
+ style={styles.trailingIcon}
222
+ >
223
+ <AtomicIcon
224
+ name={isPasswordVisible ? "EyeOff" : "Eye"}
225
+ customSize={sizeConfig.iconSize}
226
+ customColor={iconColor}
227
+ />
228
+ </Pressable>
229
+ )}
230
+
231
+ {trailingIcon && !showPasswordToggle && (
232
+ <Pressable
233
+ onPress={onTrailingIconPress}
234
+ style={styles.trailingIcon}
235
+ disabled={!onTrailingIconPress}
236
+ >
237
+ <AtomicIcon
238
+ name={trailingIcon}
239
+ customSize={sizeConfig.iconSize}
240
+ customColor={iconColor}
241
+ />
242
+ </Pressable>
243
+ )}
244
+ </View>
245
+
246
+ {(helperText || showCharacterCount) && (
247
+ <View style={styles.helperRow}>
248
+ {helperText && (
249
+ <AtomicText
250
+ style={styles.helperText}
251
+ color={hasError ? 'error' : 'secondary'}
252
+ testID={testID ? `${testID}-helper` : undefined}
253
+ >
254
+ {helperText}
255
+ </AtomicText>
256
+ )}
257
+ {showCharacterCount && maxLength && (
258
+ <AtomicText
259
+ style={[styles.helperText, styles.characterCount]}
260
+ color="secondary"
261
+ testID={testID ? `${testID}-count` : undefined}
262
+ >
263
+ {characterCount}/{maxLength}
264
+ </AtomicText>
265
+ )}
266
+ </View>
267
+ )}
268
+ </View>
269
+ );
270
+ };
271
+
272
+ const styles = StyleSheet.create({
273
+ container: {
274
+ flexDirection: 'row',
275
+ alignItems: 'center',
276
+ },
277
+ input: {
278
+ flex: 1,
279
+ margin: 0,
280
+ padding: 0,
281
+ },
282
+ label: {
283
+ marginBottom: 4,
284
+ },
285
+ leadingIcon: {
286
+ position: 'absolute',
287
+ left: 12,
288
+ zIndex: 1,
289
+ },
290
+ trailingIcon: {
291
+ position: 'absolute',
292
+ right: 12,
293
+ zIndex: 1,
294
+ },
295
+ helperRow: {
296
+ flexDirection: 'row',
297
+ justifyContent: 'space-between',
298
+ marginTop: 4,
299
+ },
300
+ helperText: {
301
+ flex: 1,
302
+ },
303
+ characterCount: {
304
+ marginLeft: 8,
305
+ },
306
+ });
307
+
308
+ export type { AtomicInputProps as InputProps };