@umituz/react-native-design-system 1.5.36 → 1.5.38

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 (62) hide show
  1. package/README.md +2 -2
  2. package/package.json +7 -5
  3. package/src/index.ts +29 -221
  4. package/src/presentation/organisms/AppHeader.tsx +3 -5
  5. package/src/presentation/tokens/commonStyles.ts +1 -1
  6. package/src/presentation/atoms/AtomicAvatar.tsx +0 -157
  7. package/src/presentation/atoms/AtomicAvatarGroup.tsx +0 -169
  8. package/src/presentation/atoms/AtomicBadge.tsx +0 -232
  9. package/src/presentation/atoms/AtomicButton.tsx +0 -236
  10. package/src/presentation/atoms/AtomicCard.tsx +0 -107
  11. package/src/presentation/atoms/AtomicChip.tsx +0 -223
  12. package/src/presentation/atoms/AtomicDatePicker.tsx +0 -347
  13. package/src/presentation/atoms/AtomicDivider.tsx +0 -114
  14. package/src/presentation/atoms/AtomicFab.tsx +0 -98
  15. package/src/presentation/atoms/AtomicFilter.tsx +0 -154
  16. package/src/presentation/atoms/AtomicFormError.tsx +0 -105
  17. package/src/presentation/atoms/AtomicIcon.tsx +0 -40
  18. package/src/presentation/atoms/AtomicImage.tsx +0 -149
  19. package/src/presentation/atoms/AtomicInput.tsx +0 -363
  20. package/src/presentation/atoms/AtomicNumberInput.tsx +0 -182
  21. package/src/presentation/atoms/AtomicPicker.tsx +0 -458
  22. package/src/presentation/atoms/AtomicProgress.tsx +0 -139
  23. package/src/presentation/atoms/AtomicSearchBar.tsx +0 -114
  24. package/src/presentation/atoms/AtomicSort.tsx +0 -145
  25. package/src/presentation/atoms/AtomicSwitch.tsx +0 -166
  26. package/src/presentation/atoms/AtomicText.tsx +0 -55
  27. package/src/presentation/atoms/AtomicTextArea.tsx +0 -313
  28. package/src/presentation/atoms/AtomicTouchable.tsx +0 -209
  29. package/src/presentation/atoms/fab/styles/fabStyles.ts +0 -69
  30. package/src/presentation/atoms/fab/types/index.ts +0 -82
  31. package/src/presentation/atoms/filter/styles/filterStyles.ts +0 -32
  32. package/src/presentation/atoms/filter/types/index.ts +0 -89
  33. package/src/presentation/atoms/index.ts +0 -366
  34. package/src/presentation/atoms/input/hooks/useInputState.ts +0 -15
  35. package/src/presentation/atoms/input/styles/inputStyles.ts +0 -66
  36. package/src/presentation/atoms/input/types/index.ts +0 -25
  37. package/src/presentation/atoms/picker/styles/pickerStyles.ts +0 -207
  38. package/src/presentation/atoms/picker/types/index.ts +0 -40
  39. package/src/presentation/atoms/touchable/styles/touchableStyles.ts +0 -62
  40. package/src/presentation/atoms/touchable/types/index.ts +0 -155
  41. package/src/presentation/hooks/useResponsive.ts +0 -180
  42. package/src/presentation/molecules/AtomicConfirmationModal.tsx +0 -243
  43. package/src/presentation/molecules/EmptyState.tsx +0 -130
  44. package/src/presentation/molecules/FormField.tsx +0 -128
  45. package/src/presentation/molecules/GridContainer.tsx +0 -124
  46. package/src/presentation/molecules/IconContainer.tsx +0 -94
  47. package/src/presentation/molecules/ListItem.tsx +0 -36
  48. package/src/presentation/molecules/ScreenHeader.tsx +0 -140
  49. package/src/presentation/molecules/SearchBar.tsx +0 -85
  50. package/src/presentation/molecules/SectionCard.tsx +0 -74
  51. package/src/presentation/molecules/SectionContainer.tsx +0 -106
  52. package/src/presentation/molecules/SectionHeader.tsx +0 -125
  53. package/src/presentation/molecules/confirmation-modal/styles/confirmationModalStyles.ts +0 -133
  54. package/src/presentation/molecules/confirmation-modal/types/index.ts +0 -105
  55. package/src/presentation/molecules/index.ts +0 -41
  56. package/src/presentation/molecules/listitem/styles/listItemStyles.ts +0 -19
  57. package/src/presentation/molecules/listitem/types/index.ts +0 -17
  58. package/src/presentation/organisms/FormContainer.tsx +0 -180
  59. package/src/presentation/organisms/ScreenLayout.tsx +0 -171
  60. package/src/presentation/organisms/index.ts +0 -25
  61. package/src/presentation/utils/platformConstants.ts +0 -124
  62. package/src/presentation/utils/responsive.ts +0 -516
@@ -1,363 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { View, TextInput, Pressable, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
3
- import { useAppDesignTokens } from '@umituz/react-native-theme';
4
- import { AtomicIcon } from './AtomicIcon';
5
- import { AtomicText } from './AtomicText';
6
- import type { AtomicIconName, AtomicIconSize } from './AtomicIcon';
7
-
8
- export type AtomicInputVariant = 'outlined' | 'filled' | 'flat';
9
- export type AtomicInputState = 'default' | 'error' | 'success' | 'disabled';
10
- export type AtomicInputSize = 'sm' | 'md' | 'lg';
11
-
12
- export interface AtomicInputProps {
13
- /** Input label */
14
- label?: string;
15
- /** Current input value */
16
- value?: string;
17
- /** Value change callback */
18
- onChangeText?: (text: string) => void;
19
- /** Input variant (outlined, filled, flat) */
20
- variant?: AtomicInputVariant;
21
- /** Input state (default, error, success, disabled) */
22
- state?: AtomicInputState;
23
- /** Input size (sm, md, lg) */
24
- size?: AtomicInputSize;
25
- /** Placeholder text */
26
- placeholder?: string;
27
- /** Helper text below input */
28
- helperText?: string;
29
- /** Leading icon (Lucide icon name) */
30
- leadingIcon?: AtomicIconName;
31
- /** Trailing icon (Lucide icon name) */
32
- trailingIcon?: AtomicIconName;
33
- /** Callback when trailing icon is pressed */
34
- onTrailingIconPress?: () => void;
35
- /** Show password toggle for secure inputs */
36
- showPasswordToggle?: boolean;
37
- /** Secure text entry (password field) */
38
- secureTextEntry?: boolean;
39
- /** Maximum character length */
40
- maxLength?: number;
41
- /** Show character counter */
42
- showCharacterCount?: boolean;
43
- /** Keyboard type */
44
- keyboardType?: 'default' | 'email-address' | 'numeric' | 'phone-pad' | 'url' | 'number-pad' | 'decimal-pad';
45
- /** Auto-capitalize */
46
- autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
47
- /** Auto-correct */
48
- autoCorrect?: boolean;
49
- /** Disabled state */
50
- disabled?: boolean;
51
- /** Container style */
52
- style?: StyleProp<ViewStyle>;
53
- /** Input text style */
54
- inputStyle?: StyleProp<TextStyle>;
55
- /** Test ID for E2E testing */
56
- testID?: string;
57
- /** Blur callback */
58
- onBlur?: () => void;
59
- /** Focus callback */
60
- onFocus?: () => void;
61
- }
62
-
63
- /**
64
- * AtomicInput - Pure React Native Text Input
65
- *
66
- * Features:
67
- * - Pure React Native implementation (no Paper dependency)
68
- * - Lucide icons for password toggle and custom icons
69
- * - Outlined/filled/flat variants
70
- * - Error, success, disabled states
71
- * - Character counter
72
- * - Responsive sizing
73
- * - Full accessibility support
74
- */
75
- export const AtomicInput: React.FC<AtomicInputProps> = ({
76
- variant = 'outlined',
77
- state = 'default',
78
- size = 'md',
79
- label,
80
- value = '',
81
- onChangeText,
82
- placeholder,
83
- helperText,
84
- leadingIcon,
85
- trailingIcon,
86
- onTrailingIconPress,
87
- showPasswordToggle = false,
88
- secureTextEntry = false,
89
- maxLength,
90
- showCharacterCount = false,
91
- keyboardType = 'default',
92
- autoCapitalize = 'sentences',
93
- autoCorrect = true,
94
- disabled = false,
95
- style,
96
- inputStyle,
97
- testID,
98
- onBlur,
99
- onFocus,
100
- }) => {
101
- const tokens = useAppDesignTokens();
102
- const [isPasswordVisible, setIsPasswordVisible] = useState(false);
103
- const [isFocused, setIsFocused] = useState(false);
104
- const isDisabled = state === 'disabled' || disabled;
105
- const characterCount = value?.toString().length || 0;
106
- const hasError = state === 'error';
107
- const hasSuccess = state === 'success';
108
-
109
- // Size configuration
110
- const sizeConfig = {
111
- sm: {
112
- paddingVertical: tokens.spacing.xs,
113
- paddingHorizontal: tokens.spacing.sm,
114
- fontSize: tokens.typography.bodySmall.fontSize,
115
- iconSize: 16,
116
- minHeight: 40,
117
- },
118
- md: {
119
- paddingVertical: tokens.spacing.sm,
120
- paddingHorizontal: tokens.spacing.md,
121
- fontSize: tokens.typography.bodyMedium.fontSize,
122
- iconSize: 20,
123
- minHeight: 48,
124
- },
125
- lg: {
126
- paddingVertical: tokens.spacing.md,
127
- paddingHorizontal: tokens.spacing.lg,
128
- fontSize: tokens.typography.bodyLarge.fontSize,
129
- iconSize: 24,
130
- minHeight: 56,
131
- },
132
- };
133
-
134
- const config = sizeConfig[size] ?? sizeConfig.md;
135
-
136
- // Ensure config is always defined with safe fallbacks
137
- const fontSize = config?.fontSize ?? tokens.typography.bodyMedium?.fontSize ?? 16;
138
- const iconSize = config?.iconSize ?? 20;
139
- const paddingVertical = config?.paddingVertical ?? tokens.spacing?.sm ?? 8;
140
- const paddingHorizontal = config?.paddingHorizontal ?? tokens.spacing?.md ?? 12;
141
- const minHeight = config?.minHeight ?? 48;
142
-
143
- // Get variant styles
144
- const getVariantStyle = (): ViewStyle => {
145
- const baseStyle: ViewStyle = {
146
- backgroundColor: tokens.colors.surface,
147
- borderRadius: tokens.borders.radius.md,
148
- };
149
-
150
- let borderColor = tokens.colors.border;
151
- if (isFocused) borderColor = tokens.colors.primary;
152
- if (hasError) borderColor = tokens.colors.error;
153
- if (hasSuccess) borderColor = tokens.colors.success;
154
- if (isDisabled) borderColor = tokens.colors.borderDisabled;
155
-
156
- switch (variant) {
157
- case 'outlined':
158
- return {
159
- ...baseStyle,
160
- borderWidth: isFocused ? 2 : 1,
161
- borderColor,
162
- };
163
-
164
- case 'filled':
165
- return {
166
- ...baseStyle,
167
- backgroundColor: tokens.colors.surfaceSecondary,
168
- borderWidth: 0,
169
- borderBottomWidth: isFocused ? 2 : 1,
170
- borderBottomColor: borderColor,
171
- };
172
-
173
- case 'flat':
174
- return {
175
- ...baseStyle,
176
- backgroundColor: 'transparent',
177
- borderWidth: 0,
178
- borderBottomWidth: 1,
179
- borderBottomColor: borderColor,
180
- borderRadius: 0,
181
- };
182
-
183
- default:
184
- return baseStyle;
185
- }
186
- };
187
-
188
- // Get text color based on state
189
- const getTextColor = () => {
190
- if (isDisabled) return tokens.colors.textDisabled;
191
- if (hasError) return tokens.colors.error;
192
- if (hasSuccess) return tokens.colors.success;
193
- return tokens.colors.textPrimary;
194
- };
195
-
196
- const iconColor = isDisabled ? tokens.colors.textDisabled : tokens.colors.textSecondary;
197
-
198
- const containerStyle: StyleProp<ViewStyle> = [
199
- styles.container,
200
- getVariantStyle(),
201
- {
202
- paddingTop: paddingVertical,
203
- paddingBottom: paddingVertical,
204
- paddingHorizontal: paddingHorizontal,
205
- minHeight: minHeight,
206
- justifyContent: 'center',
207
- opacity: isDisabled ? 0.5 : 1,
208
- },
209
- style,
210
- ];
211
-
212
- const textInputStyle: StyleProp<TextStyle> = [
213
- styles.input,
214
- {
215
- fontSize: fontSize,
216
- lineHeight: fontSize * 1.2, // Tighter lineHeight to prevent text clipping
217
- color: getTextColor(),
218
- paddingVertical: 0, // Remove vertical padding to prevent clipping
219
- },
220
- leadingIcon ? { paddingLeft: iconSize + 8 } : undefined,
221
- (trailingIcon || showPasswordToggle) ? { paddingRight: iconSize + 8 } : undefined,
222
- inputStyle,
223
- ];
224
-
225
- return (
226
- <View testID={testID}>
227
- {label && (
228
- <AtomicText
229
- type="labelMedium"
230
- color={hasError ? 'error' : hasSuccess ? 'success' : 'secondary'}
231
- style={styles.label}
232
- >
233
- {label}
234
- </AtomicText>
235
- )}
236
-
237
- <View style={containerStyle}>
238
- {leadingIcon && (
239
- <View style={styles.leadingIcon}>
240
- <AtomicIcon
241
- name={leadingIcon}
242
- customSize={iconSize}
243
- customColor={iconColor}
244
- />
245
- </View>
246
- )}
247
-
248
- <TextInput
249
- value={value}
250
- onChangeText={onChangeText}
251
- placeholder={placeholder}
252
- placeholderTextColor={tokens.colors.textSecondary}
253
- secureTextEntry={secureTextEntry && !isPasswordVisible}
254
- maxLength={maxLength}
255
- keyboardType={keyboardType}
256
- autoCapitalize={autoCapitalize}
257
- autoCorrect={autoCorrect}
258
- editable={!isDisabled}
259
- style={textInputStyle}
260
- onBlur={() => {
261
- setIsFocused(false);
262
- onBlur?.();
263
- }}
264
- onFocus={() => {
265
- setIsFocused(true);
266
- onFocus?.();
267
- }}
268
- testID={testID ? `${testID}-input` : undefined}
269
- />
270
-
271
- {(showPasswordToggle && secureTextEntry) && (
272
- <Pressable
273
- onPress={() => setIsPasswordVisible(!isPasswordVisible)}
274
- style={styles.trailingIcon}
275
- >
276
- <AtomicIcon
277
- name={isPasswordVisible ? "EyeOff" : "Eye"}
278
- customSize={iconSize}
279
- customColor={iconColor}
280
- />
281
- </Pressable>
282
- )}
283
-
284
- {trailingIcon && !showPasswordToggle && (
285
- <Pressable
286
- onPress={onTrailingIconPress}
287
- style={styles.trailingIcon}
288
- disabled={!onTrailingIconPress}
289
- >
290
- <AtomicIcon
291
- name={trailingIcon}
292
- customSize={iconSize}
293
- customColor={iconColor}
294
- />
295
- </Pressable>
296
- )}
297
- </View>
298
-
299
- {(helperText || showCharacterCount) && (
300
- <View style={styles.helperRow}>
301
- {helperText && (
302
- <AtomicText
303
- type="bodySmall"
304
- color={hasError ? 'error' : 'secondary'}
305
- style={styles.helperText}
306
- testID={testID ? `${testID}-helper` : undefined}
307
- >
308
- {helperText}
309
- </AtomicText>
310
- )}
311
- {showCharacterCount && maxLength && (
312
- <AtomicText
313
- type="bodySmall"
314
- color="secondary"
315
- style={styles.characterCount}
316
- testID={testID ? `${testID}-count` : undefined}
317
- >
318
- {characterCount}/{maxLength}
319
- </AtomicText>
320
- )}
321
- </View>
322
- )}
323
- </View>
324
- );
325
- };
326
-
327
- const styles = StyleSheet.create({
328
- container: {
329
- flexDirection: 'row',
330
- alignItems: 'center',
331
- },
332
- input: {
333
- flex: 1,
334
- margin: 0,
335
- padding: 0,
336
- },
337
- label: {
338
- marginBottom: 4,
339
- },
340
- leadingIcon: {
341
- position: 'absolute',
342
- left: 12,
343
- zIndex: 1,
344
- },
345
- trailingIcon: {
346
- position: 'absolute',
347
- right: 12,
348
- zIndex: 1,
349
- },
350
- helperRow: {
351
- flexDirection: 'row',
352
- justifyContent: 'space-between',
353
- marginTop: 4,
354
- },
355
- helperText: {
356
- flex: 1,
357
- },
358
- characterCount: {
359
- marginLeft: 8,
360
- },
361
- });
362
-
363
- export type { AtomicInputProps as InputProps };
@@ -1,182 +0,0 @@
1
- /**
2
- * AtomicNumberInput Component
3
- *
4
- * A specialized number input component that wraps AtomicInput with
5
- * number-specific validation and keyboard handling.
6
- *
7
- * Features:
8
- * - Numeric keyboard (integer or decimal)
9
- * - Min/max validation
10
- * - Step increment support
11
- * - Automatic error states for invalid numbers
12
- * - Parsed number callback (onValueChange)
13
- * - Consistent styling with AtomicInput
14
- * - All AtomicInput features (variants, states, sizes)
15
- *
16
- * Usage:
17
- * ```tsx
18
- * const [age, setAge] = useState<number | null>(null);
19
- *
20
- * <AtomicNumberInput
21
- * value={age?.toString() || ''}
22
- * onValueChange={setAge}
23
- * label="Age"
24
- * min={0}
25
- * max={150}
26
- * helperText="Enter your age"
27
- * />
28
- * ```
29
- *
30
- * Why This Component:
31
- * - Separation of concerns (text vs number input)
32
- * - Built-in number validation
33
- * - Type-safe number callbacks
34
- * - Prevents non-numeric input via keyboard
35
- * - Consistent with AtomicInput styling
36
- *
37
- * @module AtomicNumberInput
38
- */
39
-
40
- import React, { useState, useEffect } from 'react';
41
- import { AtomicInput, AtomicInputProps } from './AtomicInput';
42
-
43
- /**
44
- * Props for AtomicNumberInput component
45
- * Extends AtomicInput but removes text-specific props
46
- */
47
- export interface AtomicNumberInputProps
48
- extends Omit<
49
- AtomicInputProps,
50
- 'keyboardType' | 'secureTextEntry' | 'showPasswordToggle' | 'onChangeText'
51
- > {
52
- /** Minimum allowed value */
53
- min?: number;
54
- /** Maximum allowed value */
55
- max?: number;
56
- /** Step increment (for spinners, future feature) */
57
- step?: number;
58
- /** Allow decimal numbers (default: false for integers only) */
59
- allowDecimal?: boolean;
60
- /** Callback when valid number is entered (null if invalid/empty) */
61
- onValueChange?: (value: number | null) => void;
62
- /** Callback when raw text changes (optional) */
63
- onTextChange?: (text: string) => void;
64
- }
65
-
66
- /**
67
- * AtomicNumberInput - Specialized numeric input component
68
- *
69
- * Wraps AtomicInput with:
70
- * - Numeric keyboard
71
- * - Number validation (min, max, format)
72
- * - Parsed number callbacks
73
- * - Automatic error states
74
- */
75
- export const AtomicNumberInput: React.FC<AtomicNumberInputProps> = ({
76
- min,
77
- max,
78
- step = 1,
79
- allowDecimal = false,
80
- onValueChange,
81
- onTextChange,
82
- value = '',
83
- state: externalState,
84
- helperText: externalHelperText,
85
- ...props
86
- }) => {
87
- const [internalError, setInternalError] = useState<string | undefined>(undefined);
88
-
89
- /**
90
- * Validate number and return error message if invalid
91
- */
92
- const validateNumber = (text: string): string | undefined => {
93
- // Empty is valid (null value)
94
- if (!text || text === '' || text === '-' || text === '.') {
95
- return undefined;
96
- }
97
-
98
- // Parse number
99
- const num = parseFloat(text);
100
-
101
- // Check if valid number
102
- if (isNaN(num)) {
103
- return 'Invalid number';
104
- }
105
-
106
- // Check min constraint
107
- if (min !== undefined && num < min) {
108
- return `Minimum value is ${min}`;
109
- }
110
-
111
- // Check max constraint
112
- if (max !== undefined && num > max) {
113
- return `Maximum value is ${max}`;
114
- }
115
-
116
- return undefined;
117
- };
118
-
119
- /**
120
- * Handle text change with validation
121
- */
122
- const handleChangeText = (text: string) => {
123
- // Allow empty, minus sign, and decimal point during typing
124
- if (text === '' || text === '-' || (allowDecimal && text === '.')) {
125
- setInternalError(undefined);
126
- onTextChange?.(text);
127
- onValueChange?.(null);
128
- return;
129
- }
130
-
131
- // Validate format
132
- const decimalRegex = allowDecimal ? /^-?\d*\.?\d*$/ : /^-?\d*$/;
133
- if (!decimalRegex.test(text)) {
134
- // Invalid format, don't update
135
- return;
136
- }
137
-
138
- // Validate number
139
- const error = validateNumber(text);
140
- setInternalError(error);
141
-
142
- // Call text callback
143
- onTextChange?.(text);
144
-
145
- // Call value callback with parsed number
146
- if (!error && text !== '' && text !== '-' && text !== '.') {
147
- const num = parseFloat(text);
148
- onValueChange?.(isNaN(num) ? null : num);
149
- } else {
150
- onValueChange?.(null);
151
- }
152
- };
153
-
154
- /**
155
- * Validate on mount and when value/constraints change
156
- */
157
- useEffect(() => {
158
- if (value) {
159
- const error = validateNumber(value.toString());
160
- setInternalError(error);
161
- } else {
162
- setInternalError(undefined);
163
- }
164
- }, [value, min, max]);
165
-
166
- // Determine final state (external state overrides internal error)
167
- const finalState = externalState || (internalError ? 'error' : 'default');
168
-
169
- // Determine final helper text (internal error overrides external helper)
170
- const finalHelperText = internalError || externalHelperText;
171
-
172
- return (
173
- <AtomicInput
174
- {...props}
175
- value={value}
176
- onChangeText={handleChangeText}
177
- keyboardType={allowDecimal ? 'decimal-pad' : 'numeric'}
178
- state={finalState}
179
- helperText={finalHelperText}
180
- />
181
- );
182
- };