@umituz/react-native-design-system 1.5.33 → 1.5.35

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 (67) hide show
  1. package/package.json +4 -1
  2. package/src/presentation/atoms/AtomicDatePicker.tsx +7 -3
  3. package/src/presentation/atoms/AtomicInput.tsx +19 -12
  4. package/src/index.js +0 -100
  5. package/src/presentation/atoms/AtomicAvatar.js +0 -84
  6. package/src/presentation/atoms/AtomicAvatarGroup.js +0 -82
  7. package/src/presentation/atoms/AtomicBadge.js +0 -167
  8. package/src/presentation/atoms/AtomicButton.js +0 -171
  9. package/src/presentation/atoms/AtomicCard.js +0 -69
  10. package/src/presentation/atoms/AtomicChip.js +0 -130
  11. package/src/presentation/atoms/AtomicDatePicker.js +0 -245
  12. package/src/presentation/atoms/AtomicDivider.js +0 -57
  13. package/src/presentation/atoms/AtomicFab.js +0 -67
  14. package/src/presentation/atoms/AtomicFilter.js +0 -103
  15. package/src/presentation/atoms/AtomicFormError.js +0 -63
  16. package/src/presentation/atoms/AtomicIcon.js +0 -29
  17. package/src/presentation/atoms/AtomicImage.js +0 -91
  18. package/src/presentation/atoms/AtomicInput.js +0 -201
  19. package/src/presentation/atoms/AtomicNumberInput.js +0 -124
  20. package/src/presentation/atoms/AtomicPicker.js +0 -298
  21. package/src/presentation/atoms/AtomicProgress.js +0 -79
  22. package/src/presentation/atoms/AtomicSearchBar.js +0 -45
  23. package/src/presentation/atoms/AtomicSort.js +0 -76
  24. package/src/presentation/atoms/AtomicSwitch.js +0 -103
  25. package/src/presentation/atoms/AtomicText.js +0 -22
  26. package/src/presentation/atoms/AtomicTextArea.js +0 -195
  27. package/src/presentation/atoms/AtomicTouchable.js +0 -137
  28. package/src/presentation/atoms/fab/styles/fabStyles.js +0 -62
  29. package/src/presentation/atoms/fab/types/index.js +0 -1
  30. package/src/presentation/atoms/filter/styles/filterStyles.js +0 -28
  31. package/src/presentation/atoms/filter/types/index.js +0 -1
  32. package/src/presentation/atoms/index.js +0 -145
  33. package/src/presentation/atoms/input/hooks/useInputState.js +0 -12
  34. package/src/presentation/atoms/input/styles/inputStyles.js +0 -58
  35. package/src/presentation/atoms/input/types/index.js +0 -1
  36. package/src/presentation/atoms/picker/styles/pickerStyles.js +0 -176
  37. package/src/presentation/atoms/picker/types/index.js +0 -1
  38. package/src/presentation/atoms/touchable/styles/touchableStyles.js +0 -53
  39. package/src/presentation/atoms/touchable/types/index.js +0 -1
  40. package/src/presentation/hooks/useResponsive.js +0 -81
  41. package/src/presentation/molecules/AtomicConfirmationModal.js +0 -153
  42. package/src/presentation/molecules/EmptyState.js +0 -67
  43. package/src/presentation/molecules/FormField.js +0 -75
  44. package/src/presentation/molecules/GridContainer.js +0 -76
  45. package/src/presentation/molecules/IconContainer.js +0 -59
  46. package/src/presentation/molecules/ListItem.js +0 -23
  47. package/src/presentation/molecules/ScreenHeader.js +0 -93
  48. package/src/presentation/molecules/SearchBar.js +0 -46
  49. package/src/presentation/molecules/SectionCard.js +0 -46
  50. package/src/presentation/molecules/SectionContainer.js +0 -63
  51. package/src/presentation/molecules/SectionHeader.js +0 -72
  52. package/src/presentation/molecules/confirmation-modal/styles/confirmationModalStyles.js +0 -114
  53. package/src/presentation/molecules/confirmation-modal/types/index.js +0 -6
  54. package/src/presentation/molecules/index.js +0 -16
  55. package/src/presentation/molecules/listitem/styles/listItemStyles.js +0 -14
  56. package/src/presentation/molecules/listitem/types/index.js +0 -1
  57. package/src/presentation/organisms/AppHeader.js +0 -77
  58. package/src/presentation/organisms/FormContainer.js +0 -126
  59. package/src/presentation/organisms/ScreenLayout.js +0 -68
  60. package/src/presentation/organisms/index.js +0 -13
  61. package/src/presentation/tokens/commonStyles.js +0 -219
  62. package/src/presentation/utils/platformConstants.js +0 -113
  63. package/src/presentation/utils/responsive.js +0 -451
  64. package/src/presentation/utils/variants/compound.js +0 -15
  65. package/src/presentation/utils/variants/core.js +0 -22
  66. package/src/presentation/utils/variants/helpers.js +0 -9
  67. package/src/presentation/utils/variants.js +0 -3
@@ -1,298 +0,0 @@
1
- /**
2
- * AtomicPicker Component
3
- *
4
- * A reusable option picker/dropdown component for selecting from a list of options.
5
- *
6
- * Features:
7
- * - Single and multi-select support
8
- * - Modal display mode (full-screen on mobile)
9
- * - Optional search/filter capability
10
- * - Error and disabled states
11
- * - Theme-aware styling
12
- * - Icons for options
13
- * - Clearable selection
14
- * - react-hook-form integration ready
15
- *
16
- * Architecture:
17
- * - Follows AtomicButton pattern with separated types and styles
18
- * - Uses helper functions from picker/styles/pickerStyles.ts
19
- * - Types defined in picker/types/index.ts
20
- * - Zero inline StyleSheet.create()
21
- *
22
- * Usage:
23
- * ```tsx
24
- * const [partyType, setPartyType] = useState('birthday');
25
- *
26
- * <AtomicPicker
27
- * value={partyType}
28
- * onChange={setPartyType}
29
- * options={[
30
- * { label: 'Birthday Party', value: 'birthday', icon: 'cake' },
31
- * { label: 'Wedding', value: 'wedding', icon: 'heart' },
32
- * { label: 'Corporate Event', value: 'corporate', icon: 'briefcase' },
33
- * ]}
34
- * label="Party Type"
35
- * placeholder="Select party type"
36
- * searchable
37
- * />
38
- * ```
39
- *
40
- * @module AtomicPicker
41
- */
42
- import React, { useState, useMemo } from 'react';
43
- import { View, TouchableOpacity, Modal, FlatList, TextInput, useWindowDimensions, StyleSheet, } from 'react-native';
44
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
45
- import { useAppDesignTokens } from '@umituz/react-native-theme';
46
- import { AtomicIcon } from './AtomicIcon';
47
- import { AtomicText } from './AtomicText';
48
- import { getPickerContainerStyles, getPickerLabelStyles, getPickerPlaceholderStyles, getPickerValueStyles, getPickerErrorStyles, getModalOverlayStyles, getModalContainerStyles, getModalHeaderStyles, getModalTitleStyles, getSearchContainerStyles, getSearchInputStyles, getOptionContainerStyles, getOptionTextStyles, getOptionDescriptionStyles, getEmptyStateStyles, getEmptyStateTextStyles, getChipContainerStyles, getChipStyles, getChipTextStyles, } from './picker/styles/pickerStyles';
49
- /**
50
- * AtomicPicker - Universal option picker component
51
- *
52
- * Displays a button that opens a modal for selection.
53
- * Supports single/multi-select, search, and custom rendering.
54
- */
55
- export const AtomicPicker = ({ value, onChange, options, label, placeholder = 'Select...', error, disabled = false, multiple = false, searchable = false, clearable = false, autoClose = true, color = 'primary', size = 'md', modalTitle, emptyMessage = 'No options available', style, labelStyle, testID, }) => {
56
- const tokens = useAppDesignTokens();
57
- const { height } = useWindowDimensions();
58
- const insets = useSafeAreaInsets();
59
- const [modalVisible, setModalVisible] = useState(false);
60
- const [searchQuery, setSearchQuery] = useState('');
61
- // Get style helpers with design tokens
62
- const containerStyles = getPickerContainerStyles(tokens);
63
- const labelStyles = getPickerLabelStyles(tokens);
64
- const placeholderStyles = getPickerPlaceholderStyles(tokens);
65
- const valueStyles = getPickerValueStyles(tokens);
66
- const errorStyles = getPickerErrorStyles(tokens);
67
- const modalOverlayStyles = getModalOverlayStyles(tokens);
68
- const modalContainerStyles = getModalContainerStyles(tokens, height * 0.85);
69
- const modalHeaderStyles = getModalHeaderStyles(tokens);
70
- const modalTitleStyles = getModalTitleStyles(tokens);
71
- const searchContainerStyles = getSearchContainerStyles(tokens);
72
- const searchInputStyles = getSearchInputStyles(tokens);
73
- const emptyStateStyles = getEmptyStateStyles(tokens);
74
- const emptyStateTextStyles = getEmptyStateTextStyles(tokens);
75
- const chipContainerStyles = getChipContainerStyles(tokens);
76
- const chipStyles = getChipStyles(tokens);
77
- const chipTextStyles = getChipTextStyles(tokens);
78
- /**
79
- * Normalize value to array for consistent handling
80
- */
81
- const selectedValues = useMemo(() => {
82
- if (multiple) {
83
- return Array.isArray(value) ? value : [];
84
- }
85
- return value ? [value] : [];
86
- }, [value, multiple]);
87
- /**
88
- * Get selected option objects
89
- */
90
- const selectedOptions = useMemo(() => {
91
- return options.filter((opt) => selectedValues.includes(opt.value));
92
- }, [options, selectedValues]);
93
- /**
94
- * Filter options based on search query
95
- */
96
- const filteredOptions = useMemo(() => {
97
- if (!searchQuery.trim())
98
- return options;
99
- const query = searchQuery.toLowerCase();
100
- return options.filter((opt) => opt.label.toLowerCase().includes(query) ||
101
- opt.description?.toLowerCase().includes(query));
102
- }, [options, searchQuery]);
103
- /**
104
- * Format display text for selected value(s)
105
- */
106
- const displayText = useMemo(() => {
107
- if (selectedOptions.length === 0) {
108
- return placeholder;
109
- }
110
- if (multiple) {
111
- return selectedOptions.length === 1
112
- ? selectedOptions[0].label
113
- : `${selectedOptions.length} selected`;
114
- }
115
- return selectedOptions[0]?.label || placeholder;
116
- }, [selectedOptions, placeholder, multiple]);
117
- /**
118
- * Handle modal open
119
- */
120
- const openModal = () => {
121
- if (disabled)
122
- return;
123
- setModalVisible(true);
124
- setSearchQuery('');
125
- };
126
- /**
127
- * Handle modal close
128
- */
129
- const closeModal = () => {
130
- setModalVisible(false);
131
- setSearchQuery('');
132
- };
133
- /**
134
- * Handle option selection
135
- */
136
- const handleSelect = (optionValue) => {
137
- if (multiple) {
138
- const newValues = selectedValues.includes(optionValue)
139
- ? selectedValues.filter((v) => v !== optionValue)
140
- : [...selectedValues, optionValue];
141
- onChange(newValues);
142
- }
143
- else {
144
- onChange(optionValue);
145
- if (autoClose) {
146
- closeModal();
147
- }
148
- }
149
- };
150
- /**
151
- * Handle clear selection
152
- */
153
- const handleClear = () => {
154
- onChange(multiple ? [] : '');
155
- };
156
- /**
157
- * Handle search query change
158
- */
159
- const handleSearch = (query) => {
160
- setSearchQuery(query);
161
- };
162
- /**
163
- * Check if option is selected
164
- */
165
- const isSelected = (optionValue) => {
166
- return selectedValues.includes(optionValue);
167
- };
168
- /**
169
- * Render single option
170
- */
171
- const renderOption = ({ item }) => {
172
- const selected = isSelected(item.value);
173
- const itemDisabled = item.disabled || false;
174
- const optionContainerStyle = getOptionContainerStyles(tokens, selected, itemDisabled);
175
- const optionTextStyle = getOptionTextStyles(tokens, selected);
176
- const optionDescriptionStyle = getOptionDescriptionStyles(tokens);
177
- return (<TouchableOpacity onPress={() => !itemDisabled && handleSelect(item.value)} disabled={itemDisabled} testID={item.testID || `${testID}-option-${item.value}`} style={optionContainerStyle}>
178
- {/* Option Icon */}
179
- {item.icon && (<AtomicIcon name={item.icon} size="md" color={selected ? 'primary' : 'secondary'}/>)}
180
-
181
- {/* Option Content */}
182
- <View style={{ flex: 1 }}>
183
- <AtomicText style={optionTextStyle}>{item.label}</AtomicText>
184
- {item.description && (<AtomicText style={optionDescriptionStyle}>
185
- {item.description}
186
- </AtomicText>)}
187
- </View>
188
-
189
- {/* Selected Indicator */}
190
- {selected && (<AtomicIcon name="CircleCheck" size="md" color="primary"/>)}
191
- </TouchableOpacity>);
192
- };
193
- /**
194
- * Render selected chips for multi-select
195
- */
196
- const renderSelectedChips = () => {
197
- if (!multiple || selectedOptions.length === 0)
198
- return null;
199
- return (<View style={chipContainerStyles}>
200
- {selectedOptions.map((opt) => (<View key={opt.value} style={chipStyles}>
201
- <AtomicText style={chipTextStyles}>{opt.label}</AtomicText>
202
- <TouchableOpacity onPress={(e) => {
203
- e.stopPropagation();
204
- handleSelect(opt.value);
205
- }} hitSlop={{ top: 4, bottom: 4, left: 4, right: 4 }}>
206
- <AtomicIcon name="X" size="sm" color="primary"/>
207
- </TouchableOpacity>
208
- </View>))}
209
- </View>);
210
- };
211
- const pickerContainerStyle = StyleSheet.flatten([
212
- containerStyles.base,
213
- containerStyles.size[size],
214
- error ? containerStyles.state.error : undefined,
215
- disabled ? containerStyles.state.disabled : undefined,
216
- style,
217
- ]);
218
- const pickerLabelStyle = StyleSheet.flatten([
219
- labelStyles.base,
220
- labelStyles.size[size],
221
- labelStyle,
222
- ]);
223
- const pickerValueStyle = StyleSheet.flatten([
224
- selectedOptions.length > 0 ? valueStyles.base : placeholderStyles.base,
225
- selectedOptions.length > 0
226
- ? valueStyles.size[size]
227
- : placeholderStyles.size[size],
228
- ]);
229
- return (<View>
230
- {/* Label */}
231
- {label && <AtomicText style={pickerLabelStyle}>{label}</AtomicText>}
232
-
233
- {/* Picker Button */}
234
- <TouchableOpacity onPress={openModal} disabled={disabled} accessibilityRole="button" accessibilityLabel={label || placeholder} accessibilityState={{ disabled }} testID={testID} style={pickerContainerStyle}>
235
- {/* Display Text */}
236
- <AtomicText style={pickerValueStyle} numberOfLines={1}>
237
- {displayText}
238
- </AtomicText>
239
-
240
- {/* Icons */}
241
- <View style={{ flexDirection: 'row', alignItems: 'center', gap: tokens.spacing.xs }}>
242
- {/* Clear Button */}
243
- {clearable && selectedOptions.length > 0 && !disabled && (<TouchableOpacity onPress={handleClear} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} accessibilityRole="button" accessibilityLabel="Clear selection" testID={`${testID}-clear`}>
244
- <AtomicIcon name="X" size="sm" color="secondary"/>
245
- </TouchableOpacity>)}
246
-
247
- {/* Dropdown Icon */}
248
- <AtomicIcon name={modalVisible ? 'ChevronUp' : 'ChevronDown'} size="sm" color={disabled ? 'surfaceVariant' : 'secondary'}/>
249
- </View>
250
- </TouchableOpacity>
251
-
252
- {/* Selected Chips (Multi-select) */}
253
- {renderSelectedChips()}
254
-
255
- {/* Error Message */}
256
- {error && <AtomicText style={errorStyles}>{error}</AtomicText>}
257
-
258
- {/* Selection Modal */}
259
- <Modal visible={modalVisible} animationType="slide" transparent onRequestClose={closeModal} testID={`${testID}-modal`}>
260
- <View style={modalOverlayStyles}>
261
- <View style={[
262
- modalContainerStyles,
263
- { paddingBottom: insets.bottom + tokens.spacing.md },
264
- ]}>
265
- {/* Modal Header */}
266
- <View style={modalHeaderStyles}>
267
- {/* Title */}
268
- <AtomicText style={modalTitleStyles}>
269
- {modalTitle || label || 'Select'}
270
- </AtomicText>
271
-
272
- {/* Close Button */}
273
- <TouchableOpacity onPress={closeModal} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }} accessibilityRole="button" accessibilityLabel="Close picker" testID={`${testID}-close`}>
274
- <AtomicIcon name="X" size="md" color="primary"/>
275
- </TouchableOpacity>
276
- </View>
277
-
278
- {/* Search Bar */}
279
- {searchable && (<View style={searchContainerStyles}>
280
- <AtomicIcon name="Search" size="sm" color="secondary"/>
281
- <TextInput value={searchQuery} onChangeText={handleSearch} placeholder="Search..." placeholderTextColor={tokens.colors.textSecondary} style={searchInputStyles} testID={`${testID}-search`}/>
282
- {searchQuery.length > 0 && (<TouchableOpacity onPress={() => handleSearch('')}>
283
- <AtomicIcon name="X" size="sm" color="secondary"/>
284
- </TouchableOpacity>)}
285
- </View>)}
286
-
287
- {/* Options List */}
288
- {filteredOptions.length > 0 ? (<FlatList data={filteredOptions} keyExtractor={(item) => item.value} renderItem={renderOption} showsVerticalScrollIndicator testID={`${testID}-list`}/>) : (<View style={emptyStateStyles}>
289
- <AtomicIcon name="Info" size="xl" color="secondary"/>
290
- <AtomicText style={emptyStateTextStyles}>
291
- {emptyMessage}
292
- </AtomicText>
293
- </View>)}
294
- </View>
295
- </View>
296
- </Modal>
297
- </View>);
298
- };
@@ -1,79 +0,0 @@
1
- /**
2
- * AtomicProgress - Universal Progress Bar Component
3
- *
4
- * Displays progress bars for completion tracking
5
- * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
- *
7
- * Atomic Design Level: ATOM
8
- * Purpose: Progress indication and completion tracking
9
- *
10
- * Usage:
11
- * - File upload progress
12
- * - Task completion progress
13
- * - Achievement progress
14
- * - Form completion
15
- */
16
- import React from 'react';
17
- import { View, StyleSheet, Text } from 'react-native';
18
- import { useAppDesignTokens } from '@umituz/react-native-theme';
19
- // =============================================================================
20
- // COMPONENT IMPLEMENTATION
21
- // =============================================================================
22
- export const AtomicProgress = ({ value, height = 8, width = '100%', color, backgroundColor, shape = 'rounded', showPercentage = false, showValue = false, textColor, style, testID, }) => {
23
- const tokens = useAppDesignTokens();
24
- // Clamp value between 0 and 100
25
- const clampedValue = Math.max(0, Math.min(100, value));
26
- // Default colors
27
- const progressColor = color || tokens.colors.primary;
28
- const progressBackground = backgroundColor || tokens.colors.surfaceVariant;
29
- const progressTextColor = textColor || tokens.colors.textPrimary;
30
- // Calculate progress width
31
- const progressWidth = `${clampedValue}%`;
32
- // Border radius based on shape
33
- const borderRadius = shape === 'rounded' ? height / 2 : 0;
34
- const containerStyle = {
35
- width: width,
36
- height,
37
- backgroundColor: progressBackground,
38
- borderRadius,
39
- overflow: 'hidden',
40
- };
41
- const progressStyle = {
42
- width: progressWidth,
43
- height: '100%',
44
- backgroundColor: progressColor,
45
- borderRadius,
46
- };
47
- const textStyle = {
48
- fontSize: tokens.typography.bodySmall.fontSize,
49
- fontWeight: tokens.typography.labelMedium.fontWeight,
50
- color: progressTextColor,
51
- textAlign: 'center',
52
- };
53
- return (<View style={[containerStyle, style]} testID={testID}>
54
- <View style={progressStyle}/>
55
- {(showPercentage || showValue) && (<View style={styles.textContainer}>
56
- <Text style={textStyle}>
57
- {showPercentage ? `${Math.round(clampedValue)}%` : `${Math.round(clampedValue)}`}
58
- </Text>
59
- </View>)}
60
- </View>);
61
- };
62
- // =============================================================================
63
- // STYLES
64
- // =============================================================================
65
- const styles = StyleSheet.create({
66
- textContainer: {
67
- position: 'absolute',
68
- top: 0,
69
- left: 0,
70
- right: 0,
71
- bottom: 0,
72
- justifyContent: 'center',
73
- alignItems: 'center',
74
- },
75
- });
76
- // =============================================================================
77
- // EXPORTS
78
- // =============================================================================
79
- export default AtomicProgress;
@@ -1,45 +0,0 @@
1
- import React from 'react';
2
- import { View, TextInput, TouchableOpacity } from 'react-native';
3
- import { useAppDesignTokens } from '@umituz/react-native-theme';
4
- import { AtomicIcon } from './AtomicIcon';
5
- export const AtomicSearchBar = ({ value, onChangeText, placeholder = 'Search...', autoFocus = false, editable = true, onClear, onFocus, onBlur, onSubmitEditing, style, inputStyle, accessibilityLabel = 'Search input', accessibilityHint, }) => {
6
- const tokens = useAppDesignTokens();
7
- const handleClear = () => {
8
- onChangeText('');
9
- onClear?.();
10
- };
11
- return (<View style={[
12
- {
13
- flexDirection: 'row',
14
- alignItems: 'center',
15
- backgroundColor: tokens.colors.surfaceSecondary,
16
- borderRadius: tokens.borders.radius.lg,
17
- borderWidth: 1,
18
- borderColor: tokens.colors.border,
19
- paddingHorizontal: tokens.spacing.md,
20
- paddingVertical: tokens.spacing.sm,
21
- gap: tokens.spacing.sm,
22
- minHeight: 48,
23
- },
24
- style,
25
- ]} accessibilityRole="search">
26
- <AtomicIcon name="Search" size="sm" color="secondary"/>
27
-
28
- <TextInput value={value} onChangeText={onChangeText} placeholder={placeholder} placeholderTextColor={tokens.colors.textSecondary} autoFocus={autoFocus} editable={editable} onFocus={onFocus} onBlur={onBlur} onSubmitEditing={onSubmitEditing} style={[
29
- {
30
- flex: 1,
31
- ...tokens.typography.bodyMedium,
32
- color: tokens.colors.textPrimary,
33
- padding: 0,
34
- margin: 0,
35
- },
36
- inputStyle,
37
- ]} accessibilityLabel={accessibilityLabel} accessibilityHint={accessibilityHint} returnKeyType="search" underlineColorAndroid="transparent"/>
38
-
39
- {value.length > 0 && (<TouchableOpacity onPress={handleClear} style={{
40
- padding: tokens.spacing.xs,
41
- }} accessibilityLabel="Clear search" accessibilityRole="button">
42
- <AtomicIcon name="X" size="sm" color="secondary"/>
43
- </TouchableOpacity>)}
44
- </View>);
45
- };
@@ -1,76 +0,0 @@
1
- import React from 'react';
2
- import { ScrollView, View } from 'react-native';
3
- import { useAppDesignTokens } from '@umituz/react-native-theme';
4
- import { AtomicChip } from './AtomicChip';
5
- /**
6
- * AtomicSort - Horizontal Sort Chip Component
7
- *
8
- * A Material Design 3 compliant sort component using chip selection.
9
- * Supports single selection with ascending/descending direction toggle.
10
- *
11
- * @example
12
- * ```tsx
13
- * const [sortBy, setSortBy] = useState<string | null>('name');
14
- * const [sortDir, setSortDir] = useState<SortDirection>('asc');
15
- *
16
- * <AtomicSort
17
- * options={[
18
- * { id: 'name', label: 'Name', icon: 'sort-alpha' },
19
- * { id: 'date', label: 'Date', icon: 'schedule' },
20
- * { id: 'priority', label: 'Priority', icon: 'flag' },
21
- * ]}
22
- * selectedId={sortBy}
23
- * sortDirection={sortDir}
24
- * onSortChange={(id, dir) => {
25
- * setSortBy(id);
26
- * setSortDir(dir);
27
- * }}
28
- * showDirectionToggle={true}
29
- * />
30
- * ```
31
- *
32
- * Features:
33
- * - Horizontal scrollable sort chips
34
- * - Single selection (one active sort at a time)
35
- * - Direction toggle (click active chip to switch asc/desc)
36
- * - Visual arrow indicators (↑ asc, ↓ desc)
37
- * - Theme-aware colors from design tokens
38
- * - Icon support per sort option
39
- * - Fully controlled component
40
- *
41
- * Behavior:
42
- * - Click inactive chip → Selects it with ascending direction
43
- * - Click active chip → Toggles direction (asc ↔ desc)
44
- * - Visual feedback via filled variant for active sort
45
- */
46
- export const AtomicSort = ({ options, selectedId, sortDirection, onSortChange, showDirectionToggle = true, variant = 'outlined', color = 'primary', size = 'md', style, testID, }) => {
47
- const tokens = useAppDesignTokens();
48
- /**
49
- * Handle sort chip press
50
- * - If clicking active chip: Toggle direction
51
- * - If clicking inactive chip: Select it with 'asc' direction
52
- */
53
- const handleSortPress = (optionId) => {
54
- if (selectedId === optionId) {
55
- const newDirection = sortDirection === 'asc' ? 'desc' : 'asc';
56
- onSortChange(optionId, newDirection);
57
- }
58
- else {
59
- onSortChange(optionId, 'asc');
60
- }
61
- };
62
- const directionIcon = sortDirection === 'asc' ? 'arrow-upward' : 'arrow-downward';
63
- return (<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={{
64
- paddingHorizontal: tokens.spacing.sm,
65
- gap: tokens.spacing.sm,
66
- }} style={[style]} testID={testID}>
67
- <View style={{ flexDirection: 'row', gap: tokens.spacing.sm }}>
68
- {options.map((option) => {
69
- const isSelected = selectedId === option.id;
70
- return (<AtomicChip key={option.id} variant={isSelected ? 'filled' : variant} color={color} size={size} leadingIcon={option.icon} trailingIcon={isSelected && showDirectionToggle ? directionIcon : undefined} selected={isSelected} clickable={true} onPress={() => handleSortPress(option.id)} testID={`sort-chip-${option.id}`}>
71
- {option.label}
72
- </AtomicChip>);
73
- })}
74
- </View>
75
- </ScrollView>);
76
- };
@@ -1,103 +0,0 @@
1
- /**
2
- * AtomicSwitch - Universal Switch Component
3
- *
4
- * Provides consistent switch/toggle functionality with theme integration
5
- * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
- *
7
- * Atomic Design Level: ATOM
8
- * Purpose: Basic switch/toggle input
9
- *
10
- * Usage:
11
- * - Settings toggles
12
- * - Feature enable/disable
13
- * - Boolean preferences
14
- * - Form inputs
15
- */
16
- import React from 'react';
17
- import { Switch, StyleSheet } from 'react-native';
18
- import { useAppDesignTokens } from '@umituz/react-native-theme';
19
- // =============================================================================
20
- // SIZE CONFIGURATION
21
- // =============================================================================
22
- const SIZE_CONFIG = {
23
- sm: { scaleX: 0.8, scaleY: 0.8 },
24
- md: { scaleX: 1, scaleY: 1 },
25
- lg: { scaleX: 1.2, scaleY: 1.2 },
26
- };
27
- // =============================================================================
28
- // COMPONENT IMPLEMENTATION
29
- // =============================================================================
30
- export const AtomicSwitch = ({ value, onValueChange, size = 'md', variant = 'primary', disabled = false, style, trackColor, thumbColor, ios_backgroundColor, ...props }) => {
31
- const tokens = useAppDesignTokens();
32
- const styles = getStyles(tokens);
33
- const sizeConfig = SIZE_CONFIG[size];
34
- const colors = getVariantColors(tokens, variant);
35
- const defaultTrackColor = trackColor || {
36
- false: colors.trackFalse,
37
- true: colors.trackTrue,
38
- };
39
- const defaultThumbColor = thumbColor || colors.thumb;
40
- const defaultIosBackgroundColor = ios_backgroundColor || colors.trackFalse;
41
- return (<Switch value={value} onValueChange={onValueChange} disabled={disabled} trackColor={defaultTrackColor} thumbColor={defaultThumbColor} ios_backgroundColor={defaultIosBackgroundColor} style={[
42
- styles.switch,
43
- {
44
- transform: [{ scaleX: sizeConfig.scaleX }, { scaleY: sizeConfig.scaleY }],
45
- },
46
- style,
47
- ]} {...props}/>);
48
- };
49
- // =============================================================================
50
- // HELPER FUNCTIONS
51
- // =============================================================================
52
- const getVariantColors = (tokens, variant) => {
53
- switch (variant) {
54
- case 'primary':
55
- return {
56
- trackFalse: tokens.colors.surfaceSecondary,
57
- trackTrue: tokens.colors.primary,
58
- thumb: tokens.colors.surface,
59
- };
60
- case 'secondary':
61
- return {
62
- trackFalse: tokens.colors.surfaceSecondary,
63
- trackTrue: tokens.colors.secondary,
64
- thumb: tokens.colors.surface,
65
- };
66
- case 'success':
67
- return {
68
- trackFalse: tokens.colors.surfaceSecondary,
69
- trackTrue: tokens.colors.success,
70
- thumb: tokens.colors.surface,
71
- };
72
- case 'warning':
73
- return {
74
- trackFalse: tokens.colors.surfaceSecondary,
75
- trackTrue: tokens.colors.warning,
76
- thumb: tokens.colors.surface,
77
- };
78
- case 'error':
79
- return {
80
- trackFalse: tokens.colors.surfaceSecondary,
81
- trackTrue: tokens.colors.error,
82
- thumb: tokens.colors.surface,
83
- };
84
- default:
85
- return {
86
- trackFalse: tokens.colors.surfaceSecondary,
87
- trackTrue: tokens.colors.primary,
88
- thumb: tokens.colors.surface,
89
- };
90
- }
91
- };
92
- // =============================================================================
93
- // STYLES
94
- // =============================================================================
95
- const getStyles = (tokens) => StyleSheet.create({
96
- switch: {
97
- // Default switch styling is handled by platform
98
- },
99
- });
100
- // =============================================================================
101
- // EXPORTS
102
- // =============================================================================
103
- export default AtomicSwitch;
@@ -1,22 +0,0 @@
1
- import React from 'react';
2
- import { Text } from 'react-native';
3
- import { useAppDesignTokens } from '@umituz/react-native-theme';
4
- import { getTextColor } from '@umituz/react-native-typography';
5
- export const AtomicText = ({ children, type = 'bodyMedium', color, numberOfLines, ellipsizeMode, textAlign, style, testID, }) => {
6
- const tokens = useAppDesignTokens();
7
- // Get typography style from tokens
8
- const typographyStyle = tokens.typography[type];
9
- // Get color from tokens or use custom color using utility function
10
- const resolvedColor = getTextColor(color, tokens);
11
- const textStyle = [
12
- typographyStyle,
13
- {
14
- color: resolvedColor,
15
- ...(textAlign && { textAlign }),
16
- },
17
- style,
18
- ];
19
- return (<Text numberOfLines={numberOfLines} ellipsizeMode={ellipsizeMode} style={textStyle} testID={testID}>
20
- {children}
21
- </Text>);
22
- };