@umituz/react-native-design-system 2.6.62 → 2.6.65

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 (49) hide show
  1. package/package.json +1 -1
  2. package/src/atoms/AtomicIcon.tsx +2 -6
  3. package/src/atoms/AtomicIcon.types.ts +5 -0
  4. package/src/atoms/AtomicInput.tsx +34 -154
  5. package/src/atoms/AtomicPicker.tsx +31 -123
  6. package/src/atoms/index.ts +6 -4
  7. package/src/atoms/input/components/InputHelper.tsx +49 -0
  8. package/src/atoms/input/components/InputIcon.tsx +44 -0
  9. package/src/atoms/input/components/InputLabel.tsx +20 -0
  10. package/src/atoms/input/styles/inputStylesHelper.ts +1 -1
  11. package/src/atoms/input/types.ts +72 -0
  12. package/src/atoms/picker/hooks/usePickerState.ts +139 -0
  13. package/src/exports/atoms.ts +69 -0
  14. package/src/exports/device.ts +58 -0
  15. package/src/exports/layouts.ts +19 -0
  16. package/src/exports/molecules.ts +166 -0
  17. package/src/exports/organisms.ts +9 -0
  18. package/src/exports/responsive.ts +36 -0
  19. package/src/exports/safe-area.ts +6 -0
  20. package/src/exports/theme.ts +47 -0
  21. package/src/exports/typography.ts +22 -0
  22. package/src/exports/utilities.ts +6 -0
  23. package/src/exports/variants.ts +22 -0
  24. package/src/index.ts +11 -417
  25. package/src/molecules/avatar/Avatar.constants.ts +103 -0
  26. package/src/molecules/avatar/Avatar.types.ts +64 -0
  27. package/src/molecules/avatar/Avatar.utils.ts +8 -160
  28. package/src/molecules/calendar/index.ts +4 -9
  29. package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts +101 -301
  30. package/src/molecules/calendar/infrastructure/storage/CalendarStore.ts.bak +116 -0
  31. package/src/molecules/calendar/infrastructure/storage/CalendarStore.types.ts +64 -0
  32. package/src/molecules/calendar/infrastructure/storage/CalendarStore.utils.ts +56 -0
  33. package/src/molecules/calendar/infrastructure/storage/EventActions.ts +140 -0
  34. package/src/molecules/calendar/infrastructure/storage/NavigationActions.ts +118 -0
  35. package/src/molecules/calendar/infrastructure/stores/storageAdapter.ts +34 -0
  36. package/src/molecules/calendar/infrastructure/stores/useCalendarEvents.ts +168 -0
  37. package/src/molecules/calendar/infrastructure/stores/useCalendarNavigation.ts +47 -0
  38. package/src/molecules/calendar/infrastructure/stores/useCalendarView.ts +24 -0
  39. package/src/molecules/calendar/presentation/hooks/useCalendar.ts +7 -11
  40. package/src/responsive/compute/computeDeviceInfo.ts +22 -0
  41. package/src/responsive/compute/computeResponsivePositioning.ts +42 -0
  42. package/src/responsive/compute/computeResponsiveSizes.ts +48 -0
  43. package/src/responsive/padding/paddingUtils.ts +65 -0
  44. package/src/responsive/positioning/positioningUtils.ts +61 -0
  45. package/src/responsive/responsiveLayout.ts +11 -264
  46. package/src/responsive/screen/screenLayoutConfig.ts +38 -0
  47. package/src/responsive/tabbar/tabBarConfig.ts +88 -0
  48. package/src/responsive/types/responsiveTypes.ts +69 -0
  49. package/src/responsive/useResponsive.ts +69 -158
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "2.6.62",
3
+ "version": "2.6.65",
4
4
  "description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -12,11 +12,12 @@ import Svg, { Path } from "react-native-svg";
12
12
  import { useAppDesignTokens } from '../theme';
13
13
  import {
14
14
  type IconSize as BaseIconSize,
15
- type IoniconsName
15
+ type IconName,
16
16
  } from "./AtomicIcon.types";
17
17
 
18
18
  // Re-export IconSize for convenience
19
19
  export type IconSize = BaseIconSize;
20
+ export type { IconName };
20
21
 
21
22
  const FALLBACK_ICON = "help-circle-outline";
22
23
 
@@ -38,11 +39,6 @@ export type IconColor =
38
39
  | "textTertiary"
39
40
  | "onSurfaceVariant";
40
41
 
41
- /**
42
- * IconName can be a valid Ionicons name or any string (for custom SVGs)
43
- */
44
- export type IconName = IoniconsName | string;
45
-
46
42
  export interface AtomicIconProps {
47
43
  /** Icon name (Ionicons) */
48
44
  name?: IconName;
@@ -10,6 +10,11 @@ import type { Ionicons } from "@expo/vector-icons";
10
10
  */
11
11
  export type IoniconsName = keyof typeof Ionicons.glyphMap;
12
12
 
13
+ /**
14
+ * Icon name - either Ionicons or custom string
15
+ */
16
+ export type IconName = IoniconsName | string;
17
+
13
18
  /**
14
19
  * Semantic icon size presets
15
20
  */
@@ -1,78 +1,12 @@
1
1
  import React from 'react';
2
- import { View, TextInput, Pressable, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
2
+ import { View, TextInput, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
3
3
  import { useAppDesignTokens } from '../theme';
4
- import { AtomicIcon } from './AtomicIcon';
5
- import { AtomicText } from './AtomicText';
6
4
  import { useInputState } from './input/hooks/useInputState';
7
5
  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 (Ionicons name) */
32
- leadingIcon?: IconName;
33
- /** Trailing icon (Ionicons 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' | 'web-search' | 'twitter' | 'numeric' | 'visible-password';
47
- /** Return key type */
48
- returnKeyType?: 'done' | 'go' | 'next' | 'search' | 'send';
49
- /** Callback when submit button is pressed */
50
- onSubmitEditing?: () => void;
51
- /** Blur on submit */
52
- blurOnSubmit?: boolean;
53
- /** Auto focus */
54
- autoFocus?: boolean;
55
- /** Auto-capitalize */
56
- autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
57
- /** Auto-correct */
58
- autoCorrect?: boolean;
59
- /** Disabled state */
60
- disabled?: boolean;
61
- /** Container style */
62
- style?: StyleProp<ViewStyle>;
63
- /** Input text style */
64
- inputStyle?: StyleProp<TextStyle>;
65
- /** Test ID for E2E testing */
66
- testID?: string;
67
- /** Blur callback */
68
- onBlur?: () => void;
69
- /** Focus callback */
70
- onFocus?: () => void;
71
- /** Multiline input support */
72
- multiline?: boolean;
73
- /** Number of lines for multiline input */
74
- numberOfLines?: number;
75
- }
6
+ import type { AtomicInputProps } from './input/types';
7
+ import { InputLabel } from './input/components/InputLabel';
8
+ import { InputIcon } from './input/components/InputIcon';
9
+ import { InputHelper } from './input/components/InputHelper';
76
10
 
77
11
  /**
78
12
  * AtomicInput - Pure React Native Text Input
@@ -188,25 +122,16 @@ export const AtomicInput = React.forwardRef<TextInput, AtomicInputProps>(({
188
122
 
189
123
  return (
190
124
  <View testID={testID}>
191
- {label && (
192
- <AtomicText
193
- type="labelMedium"
194
- color={hasError ? 'error' : hasSuccess ? 'success' : 'secondary'}
195
- style={styles.label}
196
- >
197
- {label}
198
- </AtomicText>
199
- )}
125
+ <InputLabel label={label} state={state} />
200
126
 
201
127
  <View style={containerStyle}>
202
128
  {leadingIcon && (
203
- <View style={styles.leadingIcon}>
204
- <AtomicIcon
205
- name={leadingIcon}
206
- customSize={sizeConfig.iconSize}
207
- customColor={iconColor}
208
- />
209
- </View>
129
+ <InputIcon
130
+ name={leadingIcon}
131
+ size={sizeConfig.iconSize}
132
+ color={iconColor}
133
+ position="leading"
134
+ />
210
135
  )}
211
136
 
212
137
  <TextInput
@@ -240,55 +165,36 @@ export const AtomicInput = React.forwardRef<TextInput, AtomicInputProps>(({
240
165
  />
241
166
 
242
167
  {(showPasswordToggle && secureTextEntry) && (
243
- <Pressable
168
+ <InputIcon
169
+ name={isPasswordVisible ? "eye-off-outline" : "eye-outline"}
170
+ size={sizeConfig.iconSize}
171
+ color={iconColor}
172
+ position="trailing"
244
173
  onPress={() => togglePasswordVisibility()}
245
- style={styles.trailingIcon}
246
- >
247
- <AtomicIcon
248
- name={isPasswordVisible ? "eye-off-outline" : "eye-outline"}
249
- customSize={sizeConfig.iconSize}
250
- customColor={iconColor}
251
- />
252
- </Pressable>
174
+ testID={testID ? `${testID}-toggle-password` : undefined}
175
+ />
253
176
  )}
254
177
 
255
178
  {trailingIcon && !showPasswordToggle && (
256
- <Pressable
179
+ <InputIcon
180
+ name={trailingIcon}
181
+ size={sizeConfig.iconSize}
182
+ color={iconColor}
183
+ position="trailing"
257
184
  onPress={onTrailingIconPress}
258
- style={styles.trailingIcon}
259
- disabled={!onTrailingIconPress}
260
- >
261
- <AtomicIcon
262
- name={trailingIcon}
263
- customSize={sizeConfig.iconSize}
264
- customColor={iconColor}
265
- />
266
- </Pressable>
185
+ testID={testID ? `${testID}-trailing-icon` : undefined}
186
+ />
267
187
  )}
268
188
  </View>
269
189
 
270
- {(helperText || showCharacterCount) && (
271
- <View style={styles.helperRow}>
272
- {helperText && (
273
- <AtomicText
274
- style={styles.helperText}
275
- color={hasError ? 'error' : 'secondary'}
276
- testID={testID ? `${testID}-helper` : undefined}
277
- >
278
- {helperText}
279
- </AtomicText>
280
- )}
281
- {showCharacterCount && maxLength && (
282
- <AtomicText
283
- style={[styles.helperText, styles.characterCount]}
284
- color="secondary"
285
- testID={testID ? `${testID}-count` : undefined}
286
- >
287
- {characterCount}/{maxLength}
288
- </AtomicText>
289
- )}
290
- </View>
291
- )}
190
+ <InputHelper
191
+ helperText={helperText}
192
+ showCharacterCount={showCharacterCount}
193
+ characterCount={characterCount}
194
+ maxLength={maxLength}
195
+ state={state}
196
+ testID={testID}
197
+ />
292
198
  </View>
293
199
  );
294
200
  });
@@ -303,30 +209,4 @@ const styles = StyleSheet.create({
303
209
  margin: 0,
304
210
  padding: 0,
305
211
  },
306
- label: {
307
- marginBottom: 4,
308
- },
309
- leadingIcon: {
310
- position: 'absolute',
311
- left: 12,
312
- zIndex: 1,
313
- },
314
- trailingIcon: {
315
- position: 'absolute',
316
- right: 12,
317
- zIndex: 1,
318
- },
319
- helperRow: {
320
- flexDirection: 'row',
321
- justifyContent: 'space-between',
322
- marginTop: 4,
323
- },
324
- helperText: {
325
- flex: 1,
326
- },
327
- characterCount: {
328
- marginLeft: 8,
329
- },
330
212
  });
331
-
332
- export type { AtomicInputProps as InputProps };
@@ -40,7 +40,7 @@
40
40
  * @module AtomicPicker
41
41
  */
42
42
 
43
- import React, { useState, useMemo } from 'react';
43
+ import React from 'react';
44
44
  import {
45
45
  View,
46
46
  TouchableOpacity,
@@ -59,6 +59,7 @@ import {
59
59
  getPickerValueStyles,
60
60
  getPickerErrorStyles,
61
61
  } from './picker/styles/pickerStyles';
62
+ import { usePickerState } from './picker/hooks/usePickerState';
62
63
 
63
64
  export type { AtomicPickerProps, PickerOption, PickerSize } from './picker/types';
64
65
 
@@ -92,8 +93,14 @@ export const AtomicPicker: React.FC<AtomicPickerProps> = ({
92
93
  }) => {
93
94
  const tokens = useAppDesignTokens();
94
95
 
95
- const [modalVisible, setModalVisible] = useState(false);
96
- const [searchQuery, setSearchQuery] = useState('');
96
+ const pickerState = usePickerState({
97
+ value,
98
+ multiple,
99
+ options,
100
+ placeholder,
101
+ autoClose,
102
+ onChange,
103
+ });
97
104
 
98
105
  // Get style helpers with design tokens
99
106
  const containerStyles = getPickerContainerStyles(tokens);
@@ -102,110 +109,6 @@ export const AtomicPicker: React.FC<AtomicPickerProps> = ({
102
109
  const valueStyles = getPickerValueStyles(tokens);
103
110
  const errorStyles = getPickerErrorStyles(tokens);
104
111
 
105
- /**
106
- * Normalize value to array for consistent handling
107
- */
108
- const selectedValues = useMemo(() => {
109
- if (multiple) {
110
- return Array.isArray(value) ? value : [];
111
- }
112
- return value ? [value as string] : [];
113
- }, [value, multiple]);
114
-
115
- /**
116
- * Get selected option objects
117
- */
118
- const selectedOptions = useMemo(() => {
119
- return options.filter((opt) => selectedValues.includes(opt.value));
120
- }, [options, selectedValues]);
121
-
122
- /**
123
- * Filter options based on search query
124
- */
125
- const filteredOptions = useMemo(() => {
126
- if (!searchQuery.trim()) return options;
127
-
128
- const query = searchQuery.toLowerCase();
129
- return options.filter(
130
- (opt) =>
131
- opt.label.toLowerCase().includes(query) ||
132
- opt.description?.toLowerCase().includes(query)
133
- );
134
- }, [options, searchQuery]);
135
-
136
- /**
137
- * Format display text for selected value(s)
138
- */
139
- const displayText = useMemo(() => {
140
- if (selectedOptions.length === 0) {
141
- return placeholder;
142
- }
143
-
144
- if (multiple) {
145
- return selectedOptions.length === 1
146
- ? selectedOptions[0]?.label || placeholder
147
- : `${selectedOptions.length} selected`;
148
- }
149
- return selectedOptions[0]?.label || placeholder;
150
- }, [selectedOptions, placeholder, multiple]);
151
-
152
- /**
153
- * Handle modal open
154
- */
155
- const openModal = () => {
156
- if (disabled) return;
157
- setModalVisible(true);
158
- setSearchQuery('');
159
- };
160
-
161
- /**
162
- * Handle modal close
163
- */
164
- const closeModal = () => {
165
- setModalVisible(false);
166
- setSearchQuery('');
167
- };
168
-
169
- /**
170
- * Handle option selection
171
- */
172
- const handleSelect = (optionValue: string) => {
173
- if (multiple) {
174
- const newValues = selectedValues.includes(optionValue)
175
- ? selectedValues.filter((v) => v !== optionValue)
176
- : [...selectedValues, optionValue];
177
- onChange(newValues);
178
- } else {
179
- onChange(optionValue);
180
- if (autoClose) {
181
- closeModal();
182
- }
183
- }
184
- };
185
-
186
- /**
187
- * Handle clear selection
188
- */
189
- const handleClear = () => {
190
- onChange(multiple ? [] : '');
191
- };
192
-
193
- /**
194
- * Handle search query change
195
- */
196
- const handleSearch = (query: string) => {
197
- setSearchQuery(query);
198
- };
199
-
200
-
201
-
202
- /**
203
- * Handle chip removal
204
- */
205
- const handleChipRemove = (value: string) => {
206
- handleSelect(value);
207
- };
208
-
209
112
  const pickerContainerStyle = StyleSheet.flatten([
210
113
  containerStyles.base,
211
114
  containerStyles.size[size],
@@ -221,12 +124,17 @@ export const AtomicPicker: React.FC<AtomicPickerProps> = ({
221
124
  ]);
222
125
 
223
126
  const pickerValueStyle = StyleSheet.flatten([
224
- selectedOptions.length > 0 ? valueStyles.base : placeholderStyles.base,
225
- selectedOptions.length > 0
127
+ pickerState.selectedOptions.length > 0 ? valueStyles.base : placeholderStyles.base,
128
+ pickerState.selectedOptions.length > 0
226
129
  ? valueStyles.size[size]
227
130
  : placeholderStyles.size[size],
228
131
  ]);
229
132
 
133
+ const handleOpenModal = () => {
134
+ if (disabled) return;
135
+ pickerState.openModal();
136
+ };
137
+
230
138
  return (
231
139
  <View>
232
140
  {/* Label */}
@@ -234,7 +142,7 @@ export const AtomicPicker: React.FC<AtomicPickerProps> = ({
234
142
 
235
143
  {/* Picker Button */}
236
144
  <TouchableOpacity
237
- onPress={openModal}
145
+ onPress={handleOpenModal}
238
146
  disabled={disabled}
239
147
  accessibilityRole="button"
240
148
  accessibilityLabel={label || placeholder}
@@ -244,15 +152,15 @@ export const AtomicPicker: React.FC<AtomicPickerProps> = ({
244
152
  >
245
153
  {/* Display Text */}
246
154
  <AtomicText style={pickerValueStyle} numberOfLines={1}>
247
- {displayText}
155
+ {pickerState.displayText}
248
156
  </AtomicText>
249
157
 
250
158
  {/* Icons */}
251
159
  <View style={{ flexDirection: 'row', alignItems: 'center', gap: tokens.spacing.xs }}>
252
160
  {/* Clear Button */}
253
- {clearable && selectedOptions.length > 0 && !disabled && (
161
+ {clearable && pickerState.selectedOptions.length > 0 && !disabled && (
254
162
  <TouchableOpacity
255
- onPress={handleClear}
163
+ onPress={pickerState.handleClear}
256
164
  hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
257
165
  accessibilityRole="button"
258
166
  accessibilityLabel={clearAccessibilityLabel}
@@ -264,7 +172,7 @@ export const AtomicPicker: React.FC<AtomicPickerProps> = ({
264
172
 
265
173
  {/* Dropdown Icon */}
266
174
  <AtomicIcon
267
- name={modalVisible ? 'ChevronUp' : 'ChevronDown'}
175
+ name={pickerState.modalVisible ? 'ChevronUp' : 'ChevronDown'}
268
176
  size="sm"
269
177
  color={disabled ? 'surfaceVariant' : 'secondary'}
270
178
  />
@@ -273,8 +181,8 @@ export const AtomicPicker: React.FC<AtomicPickerProps> = ({
273
181
 
274
182
  {/* Selected Chips (Multi-select) */}
275
183
  <PickerChips
276
- selectedOptions={selectedOptions}
277
- onRemoveChip={handleChipRemove}
184
+ selectedOptions={pickerState.selectedOptions}
185
+ onRemoveChip={pickerState.handleChipRemove}
278
186
  testID={testID}
279
187
  />
280
188
 
@@ -283,16 +191,16 @@ export const AtomicPicker: React.FC<AtomicPickerProps> = ({
283
191
 
284
192
  {/* Selection Modal */}
285
193
  <PickerModal
286
- visible={modalVisible}
287
- onClose={closeModal}
194
+ visible={pickerState.modalVisible}
195
+ onClose={pickerState.closeModal}
288
196
  options={options}
289
- selectedValues={selectedValues}
290
- onSelect={handleSelect}
197
+ selectedValues={pickerState.selectedValues}
198
+ onSelect={pickerState.handleSelect}
291
199
  title={modalTitle || label}
292
200
  searchable={searchable}
293
- searchQuery={searchQuery}
294
- onSearchChange={handleSearch}
295
- filteredOptions={filteredOptions}
201
+ searchQuery={pickerState.searchQuery}
202
+ onSearchChange={pickerState.handleSearch}
203
+ filteredOptions={pickerState.filteredOptions}
296
204
  multiple={multiple}
297
205
  emptyMessage={emptyMessage}
298
206
  searchPlaceholder={searchPlaceholder}
@@ -25,11 +25,13 @@ export {
25
25
  // Input
26
26
  export {
27
27
  AtomicInput,
28
- type AtomicInputProps,
29
- type AtomicInputVariant,
30
- type AtomicInputState,
31
- type AtomicInputSize,
32
28
  } from './AtomicInput';
29
+ export type {
30
+ AtomicInputProps,
31
+ AtomicInputVariant,
32
+ AtomicInputState,
33
+ AtomicInputSize,
34
+ } from './input/types';
33
35
 
34
36
  // Icon
35
37
  export {
@@ -0,0 +1,49 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { AtomicText } from '../../AtomicText';
4
+ import type { AtomicInputState } from '../../input/types';
5
+
6
+ interface InputHelperProps {
7
+ helperText?: string;
8
+ showCharacterCount?: boolean;
9
+ characterCount?: number;
10
+ maxLength?: number;
11
+ state?: AtomicInputState;
12
+ testID?: string;
13
+ }
14
+
15
+ export const InputHelper: React.FC<InputHelperProps> = ({
16
+ helperText,
17
+ showCharacterCount,
18
+ characterCount = 0,
19
+ maxLength,
20
+ state,
21
+ testID,
22
+ }) => {
23
+ if (!helperText && !showCharacterCount) return null;
24
+
25
+ const color = state === 'error' ? 'error' : 'secondary';
26
+
27
+ return (
28
+ <View style={{ flexDirection: 'row', justifyContent: 'space-between', marginTop: 4 }}>
29
+ {helperText && (
30
+ <AtomicText
31
+ style={{ flex: 1 }}
32
+ color={color}
33
+ testID={testID ? `${testID}-helper` : undefined}
34
+ >
35
+ {helperText}
36
+ </AtomicText>
37
+ )}
38
+ {showCharacterCount && maxLength && (
39
+ <AtomicText
40
+ style={{ marginLeft: 8 }}
41
+ color="secondary"
42
+ testID={testID ? `${testID}-count` : undefined}
43
+ >
44
+ {characterCount}/{maxLength}
45
+ </AtomicText>
46
+ )}
47
+ </View>
48
+ );
49
+ };
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import { View, Pressable } from 'react-native';
3
+ import { AtomicIcon } from '../../AtomicIcon';
4
+
5
+ interface InputIconProps {
6
+ name: string;
7
+ size: number;
8
+ color: string;
9
+ position: 'leading' | 'trailing';
10
+ onPress?: () => void;
11
+ testID?: string;
12
+ }
13
+
14
+ export const InputIcon: React.FC<InputIconProps> = ({
15
+ name,
16
+ size,
17
+ color,
18
+ position,
19
+ onPress,
20
+ testID,
21
+ }) => {
22
+ const positionStyle = position === 'leading' ? styles.leadingIcon : styles.trailingIcon;
23
+ const Wrapper = onPress ? Pressable : View;
24
+ const wrapperProps = onPress ? { onPress, testID } : {};
25
+
26
+ return (
27
+ <Wrapper style={positionStyle} {...wrapperProps}>
28
+ <AtomicIcon name={name} customSize={size} customColor={color} />
29
+ </Wrapper>
30
+ );
31
+ };
32
+
33
+ const styles = {
34
+ leadingIcon: {
35
+ position: 'absolute' as const,
36
+ left: 12,
37
+ zIndex: 1,
38
+ },
39
+ trailingIcon: {
40
+ position: 'absolute' as const,
41
+ right: 12,
42
+ zIndex: 1,
43
+ },
44
+ };
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { AtomicText } from '../../AtomicText';
3
+ import type { AtomicInputState } from '../../input/types';
4
+
5
+ interface InputLabelProps {
6
+ label?: string;
7
+ state?: AtomicInputState;
8
+ }
9
+
10
+ export const InputLabel: React.FC<InputLabelProps> = ({ label, state }) => {
11
+ if (!label) return null;
12
+
13
+ const color = state === 'error' ? 'error' : state === 'success' ? 'success' : 'secondary';
14
+
15
+ return (
16
+ <AtomicText type="labelMedium" color={color} style={{ marginBottom: 4 }}>
17
+ {label}
18
+ </AtomicText>
19
+ );
20
+ };
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { ViewStyle } from 'react-native';
9
9
  import { useAppDesignTokens } from '../../../theme';
10
- import type { AtomicInputVariant, AtomicInputSize } from '../../AtomicInput';
10
+ import type { AtomicInputVariant, AtomicInputSize } from '../../input/types';
11
11
 
12
12
  interface GetVariantStyleParams {
13
13
  variant: AtomicInputVariant;