@umituz/react-native-design-system 1.5.33 → 1.5.34
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.
- package/package.json +4 -1
- package/src/presentation/atoms/AtomicDatePicker.tsx +2 -1
- package/src/presentation/atoms/AtomicInput.tsx +19 -12
- package/src/index.js +0 -100
- package/src/presentation/atoms/AtomicAvatar.js +0 -84
- package/src/presentation/atoms/AtomicAvatarGroup.js +0 -82
- package/src/presentation/atoms/AtomicBadge.js +0 -167
- package/src/presentation/atoms/AtomicButton.js +0 -171
- package/src/presentation/atoms/AtomicCard.js +0 -69
- package/src/presentation/atoms/AtomicChip.js +0 -130
- package/src/presentation/atoms/AtomicDatePicker.js +0 -245
- package/src/presentation/atoms/AtomicDivider.js +0 -57
- package/src/presentation/atoms/AtomicFab.js +0 -67
- package/src/presentation/atoms/AtomicFilter.js +0 -103
- package/src/presentation/atoms/AtomicFormError.js +0 -63
- package/src/presentation/atoms/AtomicIcon.js +0 -29
- package/src/presentation/atoms/AtomicImage.js +0 -91
- package/src/presentation/atoms/AtomicInput.js +0 -201
- package/src/presentation/atoms/AtomicNumberInput.js +0 -124
- package/src/presentation/atoms/AtomicPicker.js +0 -298
- package/src/presentation/atoms/AtomicProgress.js +0 -79
- package/src/presentation/atoms/AtomicSearchBar.js +0 -45
- package/src/presentation/atoms/AtomicSort.js +0 -76
- package/src/presentation/atoms/AtomicSwitch.js +0 -103
- package/src/presentation/atoms/AtomicText.js +0 -22
- package/src/presentation/atoms/AtomicTextArea.js +0 -195
- package/src/presentation/atoms/AtomicTouchable.js +0 -137
- package/src/presentation/atoms/fab/styles/fabStyles.js +0 -62
- package/src/presentation/atoms/fab/types/index.js +0 -1
- package/src/presentation/atoms/filter/styles/filterStyles.js +0 -28
- package/src/presentation/atoms/filter/types/index.js +0 -1
- package/src/presentation/atoms/index.js +0 -145
- package/src/presentation/atoms/input/hooks/useInputState.js +0 -12
- package/src/presentation/atoms/input/styles/inputStyles.js +0 -58
- package/src/presentation/atoms/input/types/index.js +0 -1
- package/src/presentation/atoms/picker/styles/pickerStyles.js +0 -176
- package/src/presentation/atoms/picker/types/index.js +0 -1
- package/src/presentation/atoms/touchable/styles/touchableStyles.js +0 -53
- package/src/presentation/atoms/touchable/types/index.js +0 -1
- package/src/presentation/hooks/useResponsive.js +0 -81
- package/src/presentation/molecules/AtomicConfirmationModal.js +0 -153
- package/src/presentation/molecules/EmptyState.js +0 -67
- package/src/presentation/molecules/FormField.js +0 -75
- package/src/presentation/molecules/GridContainer.js +0 -76
- package/src/presentation/molecules/IconContainer.js +0 -59
- package/src/presentation/molecules/ListItem.js +0 -23
- package/src/presentation/molecules/ScreenHeader.js +0 -93
- package/src/presentation/molecules/SearchBar.js +0 -46
- package/src/presentation/molecules/SectionCard.js +0 -46
- package/src/presentation/molecules/SectionContainer.js +0 -63
- package/src/presentation/molecules/SectionHeader.js +0 -72
- package/src/presentation/molecules/confirmation-modal/styles/confirmationModalStyles.js +0 -114
- package/src/presentation/molecules/confirmation-modal/types/index.js +0 -6
- package/src/presentation/molecules/index.js +0 -16
- package/src/presentation/molecules/listitem/styles/listItemStyles.js +0 -14
- package/src/presentation/molecules/listitem/types/index.js +0 -1
- package/src/presentation/organisms/AppHeader.js +0 -77
- package/src/presentation/organisms/FormContainer.js +0 -126
- package/src/presentation/organisms/ScreenLayout.js +0 -68
- package/src/presentation/organisms/index.js +0 -13
- package/src/presentation/tokens/commonStyles.js +0 -219
- package/src/presentation/utils/platformConstants.js +0 -113
- package/src/presentation/utils/responsive.js +0 -451
- package/src/presentation/utils/variants/compound.js +0 -15
- package/src/presentation/utils/variants/core.js +0 -22
- package/src/presentation/utils/variants/helpers.js +0 -9
- 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
|
-
};
|