@umituz/react-native-design-system 1.5.17 → 1.5.18

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 (65) hide show
  1. package/package.json +9 -8
  2. package/src/index.js +100 -0
  3. package/src/presentation/atoms/AtomicAvatar.js +84 -0
  4. package/src/presentation/atoms/AtomicAvatarGroup.js +82 -0
  5. package/src/presentation/atoms/AtomicBadge.js +167 -0
  6. package/src/presentation/atoms/AtomicButton.js +171 -0
  7. package/src/presentation/atoms/AtomicCard.js +69 -0
  8. package/src/presentation/atoms/AtomicChip.js +130 -0
  9. package/src/presentation/atoms/AtomicDatePicker.js +245 -0
  10. package/src/presentation/atoms/AtomicDivider.js +57 -0
  11. package/src/presentation/atoms/AtomicFab.js +67 -0
  12. package/src/presentation/atoms/AtomicFilter.js +103 -0
  13. package/src/presentation/atoms/AtomicFormError.js +63 -0
  14. package/src/presentation/atoms/AtomicIcon.js +29 -0
  15. package/src/presentation/atoms/AtomicImage.js +91 -0
  16. package/src/presentation/atoms/AtomicInput.js +201 -0
  17. package/src/presentation/atoms/AtomicNumberInput.js +124 -0
  18. package/src/presentation/atoms/AtomicPicker.js +298 -0
  19. package/src/presentation/atoms/AtomicProgress.js +79 -0
  20. package/src/presentation/atoms/AtomicSearchBar.js +45 -0
  21. package/src/presentation/atoms/AtomicSort.js +76 -0
  22. package/src/presentation/atoms/AtomicSwitch.js +103 -0
  23. package/src/presentation/atoms/AtomicText.js +58 -0
  24. package/src/presentation/atoms/AtomicTextArea.js +195 -0
  25. package/src/presentation/atoms/AtomicTouchable.js +137 -0
  26. package/src/presentation/atoms/fab/styles/fabStyles.js +62 -0
  27. package/src/presentation/atoms/fab/types/index.js +1 -0
  28. package/src/presentation/atoms/filter/styles/filterStyles.js +28 -0
  29. package/src/presentation/atoms/filter/types/index.js +1 -0
  30. package/src/presentation/atoms/index.js +145 -0
  31. package/src/presentation/atoms/input/hooks/useInputState.js +12 -0
  32. package/src/presentation/atoms/input/styles/inputStyles.js +58 -0
  33. package/src/presentation/atoms/input/types/index.js +1 -0
  34. package/src/presentation/atoms/picker/styles/pickerStyles.js +176 -0
  35. package/src/presentation/atoms/picker/types/index.js +1 -0
  36. package/src/presentation/atoms/touchable/styles/touchableStyles.js +53 -0
  37. package/src/presentation/atoms/touchable/types/index.js +1 -0
  38. package/src/presentation/hooks/useResponsive.js +81 -0
  39. package/src/presentation/molecules/AtomicConfirmationModal.js +153 -0
  40. package/src/presentation/molecules/EmptyState.js +67 -0
  41. package/src/presentation/molecules/FormField.js +75 -0
  42. package/src/presentation/molecules/GridContainer.js +76 -0
  43. package/src/presentation/molecules/IconContainer.js +59 -0
  44. package/src/presentation/molecules/ListItem.js +23 -0
  45. package/src/presentation/molecules/ScreenHeader.js +93 -0
  46. package/src/presentation/molecules/SearchBar.js +46 -0
  47. package/src/presentation/molecules/SectionCard.js +46 -0
  48. package/src/presentation/molecules/SectionContainer.js +63 -0
  49. package/src/presentation/molecules/SectionHeader.js +72 -0
  50. package/src/presentation/molecules/confirmation-modal/styles/confirmationModalStyles.js +114 -0
  51. package/src/presentation/molecules/confirmation-modal/types/index.js +6 -0
  52. package/src/presentation/molecules/index.js +16 -0
  53. package/src/presentation/molecules/listitem/styles/listItemStyles.js +14 -0
  54. package/src/presentation/molecules/listitem/types/index.js +1 -0
  55. package/src/presentation/organisms/AppHeader.js +77 -0
  56. package/src/presentation/organisms/FormContainer.js +126 -0
  57. package/src/presentation/organisms/ScreenLayout.js +68 -0
  58. package/src/presentation/organisms/index.js +13 -0
  59. package/src/presentation/tokens/commonStyles.js +219 -0
  60. package/src/presentation/utils/platformConstants.js +113 -0
  61. package/src/presentation/utils/responsive.js +451 -0
  62. package/src/presentation/utils/variants/compound.js +15 -0
  63. package/src/presentation/utils/variants/core.js +22 -0
  64. package/src/presentation/utils/variants/helpers.js +9 -0
  65. package/src/presentation/utils/variants.js +3 -0
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@umituz/react-native-design-system",
3
- "version": "1.5.17",
3
+ "version": "1.5.18",
4
4
  "description": "Universal design system for React Native apps - Domain-Driven Design architecture with Material Design 3 components",
5
- "main": "./src/index.ts",
6
- "types": "./src/index.ts",
5
+ "main": "./lib/index.js",
6
+ "types": "./lib/index.d.ts",
7
7
  "exports": {
8
8
  ".": {
9
- "types": "./src/index.ts",
10
- "import": "./src/index.ts",
11
- "require": "./src/index.ts"
9
+ "types": "./lib/index.d.ts",
10
+ "import": "./lib/index.js",
11
+ "require": "./lib/index.js"
12
12
  }
13
13
  },
14
14
  "scripts": {
15
- "typecheck": "tsc --noEmit",
16
- "lint": "tsc --noEmit",
15
+ "build": "tsc",
16
+ "prepublishOnly": "npm run build",
17
17
  "version:patch": "npm version patch -m 'chore: release v%s'",
18
18
  "version:minor": "npm version minor -m 'chore: release v%s'",
19
19
  "version:major": "npm version major -m 'chore: release v%s'"
@@ -78,6 +78,7 @@
78
78
  "access": "public"
79
79
  },
80
80
  "files": [
81
+ "lib",
81
82
  "src",
82
83
  "README.md",
83
84
  "LICENSE"
package/src/index.js ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @umituz/react-native-design-system - Public API
3
+ *
4
+ * Universal UI component library for React Native apps
5
+ * Domain-Driven Design (DDD) Architecture
6
+ *
7
+ * This is the SINGLE SOURCE OF TRUTH for all UI components.
8
+ * ALL imports from the design system MUST go through this file.
9
+ *
10
+ * Architecture:
11
+ * - presentation/atoms: Primitive UI components (AtomicButton, AtomicText, etc.)
12
+ * - presentation/molecules: Composite components (SearchBar, ListItem, etc.)
13
+ * - presentation/organisms: Complex patterns (ScreenLayout, AppHeader, FormContainer)
14
+ * - presentation/tokens: Design tokens (colors, typography, spacing, etc.)
15
+ * - presentation/utils: Utility functions and helpers
16
+ *
17
+ * Usage:
18
+ * import { AtomicButton, AtomicFilter, AtomicTouchable, SearchBar, STATIC_TOKENS } from '@umituz/react-native-design-system';
19
+ */
20
+ // =============================================================================
21
+ // ATOMS - Primitive UI Components
22
+ // =============================================================================
23
+ export { AtomicButton, } from './presentation/atoms/AtomicButton';
24
+ export { AtomicText, } from './presentation/atoms/AtomicText';
25
+ export { AtomicCard, } from './presentation/atoms/AtomicCard';
26
+ export { AtomicInput, } from './presentation/atoms/AtomicInput';
27
+ export { AtomicNumberInput, } from './presentation/atoms/AtomicNumberInput';
28
+ export { AtomicSwitch, } from './presentation/atoms/AtomicSwitch';
29
+ export { AtomicIcon, } from './presentation/atoms/AtomicIcon';
30
+ export { AtomicFormError, } from './presentation/atoms/AtomicFormError';
31
+ export { AtomicDatePicker, } from './presentation/atoms/AtomicDatePicker';
32
+ export { AtomicPicker, } from './presentation/atoms/AtomicPicker';
33
+ export { AtomicTextArea, } from './presentation/atoms/AtomicTextArea';
34
+ export { AtomicBadge, } from './presentation/atoms/AtomicBadge';
35
+ export { AtomicProgress, } from './presentation/atoms/AtomicProgress';
36
+ export { AtomicDivider, } from './presentation/atoms/AtomicDivider';
37
+ export { AtomicFab, getFabVariants, } from './presentation/atoms/AtomicFab';
38
+ export { AtomicFilter, getFilterContainerStyle, getClearAllContainerStyle, getScrollContentContainerStyle, } from './presentation/atoms/AtomicFilter';
39
+ export { AtomicTouchable, TouchablePresets, getOpacityValue, normalizeHitSlop, } from './presentation/atoms/AtomicTouchable';
40
+ export { AtomicSearchBar, } from './presentation/atoms/AtomicSearchBar';
41
+ export { AtomicSort, } from './presentation/atoms/AtomicSort';
42
+ // =============================================================================
43
+ // MOLECULES - Composite Components
44
+ // =============================================================================
45
+ export { FormField, } from './presentation/molecules/FormField';
46
+ export { ListItem, } from './presentation/molecules/ListItem';
47
+ export { SearchBar, } from './presentation/molecules/SearchBar';
48
+ // SettingItem moved to @domains/settings/presentation/components/SettingItem
49
+ // LanguageSwitcher moved to @umituz/react-native-localization
50
+ // Import directly: import { SettingItem } from '@domains/settings/presentation/components/SettingItem';
51
+ export { SectionCard, } from './presentation/molecules/SectionCard';
52
+ export { IconContainer, } from './presentation/molecules/IconContainer';
53
+ export { SectionHeader, } from './presentation/molecules/SectionHeader';
54
+ export { EmptyState, } from './presentation/molecules/EmptyState';
55
+ export { GridContainer, } from './presentation/molecules/GridContainer';
56
+ export { SectionContainer, } from './presentation/molecules/SectionContainer';
57
+ export { ScreenHeader, } from './presentation/molecules/ScreenHeader';
58
+ export { AtomicConfirmationModal, useConfirmationModal, } from './presentation/molecules/AtomicConfirmationModal';
59
+ // =============================================================================
60
+ // ORGANISMS - Complex Patterns
61
+ // =============================================================================
62
+ export { ScreenLayout, } from './presentation/organisms/ScreenLayout';
63
+ export { AppHeader, } from './presentation/organisms/AppHeader';
64
+ export { FormContainer, } from './presentation/organisms/FormContainer';
65
+ // Note: FeedbackModal moved to @domains/feedback
66
+ // Import from feedback domain: import { FeedbackModal } from '@domains/feedback';
67
+ // =============================================================================
68
+ // THEME-RELATED EXPORTS - Re-exported from @umituz/react-native-theme
69
+ // =============================================================================
70
+ // All tokens, colors, and theme utilities come from @umituz/react-native-theme
71
+ // Design system does NOT define any tokens - it only uses them from theme package
72
+ export {
73
+ // Token factory
74
+ createDesignTokens, STATIC_DESIGN_TOKENS, BASE_TOKENS, STATIC_TOKENS,
75
+ // Color utilities
76
+ withAlpha, lightColors, darkColors, getColorPalette, } from '@umituz/react-native-theme';
77
+ // Hook for dynamic theme-aware tokens (re-exported from theme package)
78
+ export { useAppDesignTokens } from '@umituz/react-native-theme';
79
+ export { useCommonStyles, } from './presentation/tokens/commonStyles';
80
+ // =============================================================================
81
+ // UTILITIES - Helper Functions
82
+ // =============================================================================
83
+ export { IOS_HIG, ANDROID_MATERIAL, PLATFORM_CONSTANTS, isValidTouchTarget, getMinTouchTarget, } from './presentation/utils/platformConstants';
84
+ // =============================================================================
85
+ // RESPONSIVE UTILITIES - Centralized Responsive Management
86
+ // =============================================================================
87
+ export { useResponsive, useResponsiveSizes, useDeviceType, } from './presentation/hooks/useResponsive';
88
+ export { getScreenDimensions, isSmallPhone, isTablet, getResponsiveLogoSize, getResponsiveInputHeight, getResponsiveHorizontalPadding, getResponsiveBottomPosition, getResponsiveFABPosition, getResponsiveModalMaxHeight, getResponsiveMinModalHeight, getResponsiveIconContainerSize, getResponsiveGridColumns, getResponsiveMaxWidth, getResponsiveFontSize, isLandscape, getKeyboardBehavior, getDeviceType, getMinTouchTargetSize, getSpacingMultiplier, getOnboardingIconMarginTop, getOnboardingIconMarginBottom, getOnboardingTitleMarginBottom, getOnboardingTextPadding, getOnboardingDescriptionMarginTop, DeviceType, } from './presentation/utils/responsive';
89
+ // =============================================================================
90
+ // THEME MANAGEMENT - Global Theme Store
91
+ // =============================================================================
92
+ // Theme management moved to @umituz/react-native-theme
93
+ export { useDesignSystemTheme, } from '@umituz/react-native-theme';
94
+ // =============================================================================
95
+ // ICONS DOMAIN - Universal Icon System
96
+ // =============================================================================
97
+ // Icon components have been moved to @umituz/react-native-icon package.
98
+ // Import directly: import { Icon, IconName, IconSize, IconColor } from '@umituz/react-native-icon';
99
+ // Re-export for backward compatibility (deprecated - use @umituz/react-native-icon directly)
100
+ export { Icon, } from '@umituz/react-native-icon';
@@ -0,0 +1,84 @@
1
+ /**
2
+ * AtomicAvatar - Universal Avatar Component
3
+ *
4
+ * Displays user profile images with fallback to initials
5
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
+ *
7
+ * Atomic Design Level: ATOM
8
+ * Purpose: User profile image display
9
+ *
10
+ * Usage:
11
+ * - User profile pictures
12
+ * - Contact avatars
13
+ * - Group member avatars
14
+ * - Default user placeholders
15
+ */
16
+ import React from 'react';
17
+ import { View, StyleSheet } from 'react-native';
18
+ import { AtomicImage } from './AtomicImage';
19
+ import { AtomicText } from './AtomicText';
20
+ import { useAppDesignTokens } from '@umituz/react-native-theme';
21
+ // =============================================================================
22
+ // COMPONENT IMPLEMENTATION
23
+ // =============================================================================
24
+ export const AtomicAvatar = ({ source, name, size = 'md', customSize, backgroundColor, textColor, borderRadius, borderWidth = 0, borderColor, style, imageStyle, testID, }) => {
25
+ const tokens = useAppDesignTokens();
26
+ const avatarSize = customSize || tokens.avatarSizes[size];
27
+ const avatarRadius = borderRadius ?? avatarSize / 2;
28
+ // Generate initials from name
29
+ const getInitials = (name) => {
30
+ return name
31
+ .split(' ')
32
+ .map(word => word.charAt(0))
33
+ .join('')
34
+ .toUpperCase()
35
+ .slice(0, 2);
36
+ };
37
+ // Default colors
38
+ const defaultBackgroundColor = backgroundColor || tokens.colors.primary;
39
+ const defaultTextColor = textColor || tokens.colors.onPrimary;
40
+ const defaultBorderColor = borderColor || tokens.colors.border;
41
+ const avatarStyle = {
42
+ width: avatarSize,
43
+ height: avatarSize,
44
+ borderRadius: avatarRadius,
45
+ backgroundColor: defaultBackgroundColor,
46
+ borderWidth,
47
+ borderColor: defaultBorderColor,
48
+ alignItems: 'center',
49
+ justifyContent: 'center',
50
+ overflow: 'hidden',
51
+ };
52
+ const imageStyleFinal = {
53
+ width: avatarSize,
54
+ height: avatarSize,
55
+ borderRadius: avatarRadius,
56
+ };
57
+ // Font size based on avatar size
58
+ const getFontSize = (size) => {
59
+ if (size <= 32)
60
+ return 12;
61
+ if (size <= 48)
62
+ return 16;
63
+ if (size <= 64)
64
+ return 20;
65
+ return 24;
66
+ };
67
+ return (<View style={[avatarStyle, style]} testID={testID}>
68
+ {source ? (<AtomicImage source={source} style={StyleSheet.flatten([imageStyleFinal, imageStyle])} resizeMode="cover"/>) : name ? (<AtomicText type="labelLarge" color={defaultTextColor} style={{
69
+ fontSize: getFontSize(avatarSize),
70
+ fontWeight: tokens.typography.semibold,
71
+ }}>
72
+ {getInitials(name)}
73
+ </AtomicText>) : (<AtomicText type="labelLarge" color={defaultTextColor} style={{
74
+ fontSize: getFontSize(avatarSize),
75
+ fontWeight: tokens.typography.semibold,
76
+ }}>
77
+ ?
78
+ </AtomicText>)}
79
+ </View>);
80
+ };
81
+ // =============================================================================
82
+ // EXPORTS
83
+ // =============================================================================
84
+ export default AtomicAvatar;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * AtomicAvatarGroup - Universal Avatar Group Component
3
+ *
4
+ * Displays multiple avatars in a group with overlap and overflow handling
5
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
+ *
7
+ * Atomic Design Level: ATOM
8
+ * Purpose: Multiple avatar display with group behavior
9
+ *
10
+ * Usage:
11
+ * - Team member avatars
12
+ * - Group chat participants
13
+ * - Project collaborators
14
+ * - Event attendees
15
+ * - Social connections
16
+ */
17
+ import React from 'react';
18
+ import { View } from 'react-native';
19
+ import { AtomicAvatar } from './AtomicAvatar';
20
+ import { AtomicText } from './AtomicText';
21
+ import { useAppDesignTokens } from '@umituz/react-native-theme';
22
+ // =============================================================================
23
+ // COMPONENT IMPLEMENTATION
24
+ // =============================================================================
25
+ export const AtomicAvatarGroup = ({ avatars, maxVisible = 3, size = 'md', customSize, spacing = -8, showOverflow = true, overflowBackgroundColor, overflowTextColor, borderWidth = 2, borderColor, style, testID, }) => {
26
+ const tokens = useAppDesignTokens();
27
+ // Calculate visible avatars and overflow count
28
+ const visibleAvatars = avatars.slice(0, maxVisible);
29
+ const overflowCount = avatars.length - maxVisible;
30
+ // Size mapping for overflow text
31
+ const sizeMap = {
32
+ xs: 10,
33
+ sm: 12,
34
+ md: 14,
35
+ lg: 16,
36
+ xl: 18,
37
+ xxl: 20,
38
+ };
39
+ const textSize = sizeMap[size];
40
+ // Default colors for overflow
41
+ const defaultOverflowBackground = overflowBackgroundColor || tokens.colors.surfaceVariant;
42
+ const defaultOverflowText = overflowTextColor || tokens.colors.textPrimary;
43
+ const containerStyle = {
44
+ flexDirection: 'row',
45
+ alignItems: 'center',
46
+ };
47
+ const avatarStyle = {
48
+ marginLeft: spacing,
49
+ borderWidth,
50
+ borderColor: borderColor || tokens.colors.surface,
51
+ };
52
+ return (<View style={[containerStyle, style]} testID={testID}>
53
+ {visibleAvatars.map((avatar, index) => (<AtomicAvatar key={avatar.id} source={avatar.source} name={avatar.name} size={size} customSize={customSize} backgroundColor={avatar.backgroundColor} textColor={avatar.textColor} borderWidth={borderWidth} borderColor={borderColor} style={[
54
+ avatarStyle,
55
+ ...(index === 0 ? [{ marginLeft: 0 }] : []), // First avatar has no left margin
56
+ ]}/>))}
57
+ {showOverflow && overflowCount > 0 && (<View style={[
58
+ {
59
+ alignItems: 'center',
60
+ justifyContent: 'center',
61
+ width: customSize || 40,
62
+ height: customSize || 40,
63
+ borderRadius: (customSize || 40) / 2,
64
+ backgroundColor: defaultOverflowBackground,
65
+ borderWidth,
66
+ borderColor: borderColor || tokens.colors.surface,
67
+ marginLeft: spacing,
68
+ },
69
+ ]}>
70
+ <AtomicText type="labelMedium" color={defaultOverflowText} style={{
71
+ fontWeight: tokens.typography.semibold,
72
+ fontSize: textSize,
73
+ }}>
74
+ +{overflowCount}
75
+ </AtomicText>
76
+ </View>)}
77
+ </View>);
78
+ };
79
+ // =============================================================================
80
+ // EXPORTS
81
+ // =============================================================================
82
+ export default AtomicAvatarGroup;
@@ -0,0 +1,167 @@
1
+ /**
2
+ * AtomicBadge - Universal Badge Component
3
+ *
4
+ * Provides consistent badge/notification count display
5
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
+ *
7
+ * Atomic Design Level: ATOM
8
+ * Purpose: Display counts, notifications, status indicators
9
+ *
10
+ * Usage:
11
+ * - Notification counts
12
+ * - Cart item counts
13
+ * - Status indicators
14
+ * - Achievement badges
15
+ */
16
+ import React from 'react';
17
+ import { View, StyleSheet } from 'react-native';
18
+ import { AtomicText } from './AtomicText';
19
+ import { useAppDesignTokens } from '@umituz/react-native-theme';
20
+ // =============================================================================
21
+ // SIZE CONFIGURATION
22
+ // =============================================================================
23
+ const getSizeConfig = (tokens) => ({
24
+ xs: {
25
+ minHeight: tokens.spacing.sm,
26
+ paddingHorizontal: tokens.spacing.xs,
27
+ fontSize: tokens.typography.labelSmall.fontSize,
28
+ borderRadius: tokens.borders.radius.sm,
29
+ },
30
+ sm: {
31
+ minHeight: tokens.spacing.md,
32
+ paddingHorizontal: tokens.spacing.sm,
33
+ fontSize: tokens.typography.bodySmall.fontSize,
34
+ borderRadius: tokens.borders.radius.md,
35
+ },
36
+ md: {
37
+ minHeight: tokens.spacing.lg,
38
+ paddingHorizontal: tokens.spacing.sm,
39
+ fontSize: tokens.typography.bodyMedium.fontSize,
40
+ borderRadius: tokens.borders.radius.md,
41
+ },
42
+ lg: {
43
+ minHeight: tokens.spacing.xl,
44
+ paddingHorizontal: tokens.spacing.md,
45
+ fontSize: tokens.typography.bodyLarge.fontSize,
46
+ borderRadius: tokens.borders.radius.lg,
47
+ },
48
+ });
49
+ // =============================================================================
50
+ // COMPONENT IMPLEMENTATION
51
+ // =============================================================================
52
+ export const AtomicBadge = ({ children, size = 'md', variant = 'primary', shape = 'circle', max, showZero = false, style, textStyle, minWidth, maxWidth, }) => {
53
+ const tokens = useAppDesignTokens();
54
+ const styles = getStyles(tokens);
55
+ const sizeConfig = getSizeConfig(tokens)[size];
56
+ const colors = getVariantColors(tokens, variant);
57
+ const borderRadius = getBorderRadius(shape, sizeConfig.borderRadius, tokens);
58
+ // Handle max value display
59
+ const displayValue = React.useMemo(() => {
60
+ if (typeof children === 'number') {
61
+ if (max && children > max) {
62
+ return `${max}+`;
63
+ }
64
+ return children.toString();
65
+ }
66
+ return children;
67
+ }, [children, max]);
68
+ // Don't render if count is 0 and showZero is false
69
+ if (typeof children === 'number' && children === 0 && !showZero) {
70
+ return null;
71
+ }
72
+ const containerStyle = [
73
+ styles.container,
74
+ {
75
+ minHeight: sizeConfig.minHeight,
76
+ paddingHorizontal: sizeConfig.paddingHorizontal,
77
+ borderRadius,
78
+ backgroundColor: colors.background,
79
+ minWidth: minWidth || sizeConfig.minHeight,
80
+ maxWidth,
81
+ },
82
+ style,
83
+ ];
84
+ const textStyleFinal = StyleSheet.flatten([
85
+ styles.text,
86
+ {
87
+ fontSize: sizeConfig.fontSize,
88
+ },
89
+ textStyle,
90
+ ]);
91
+ return (<View style={containerStyle}>
92
+ <AtomicText type="bodySmall" color={colors.text} style={textStyleFinal} numberOfLines={1}>
93
+ {displayValue}
94
+ </AtomicText>
95
+ </View>);
96
+ };
97
+ // =============================================================================
98
+ // HELPER FUNCTIONS
99
+ // =============================================================================
100
+ const getVariantColors = (tokens, variant) => {
101
+ switch (variant) {
102
+ case 'primary':
103
+ return {
104
+ background: tokens.colors.primary,
105
+ text: tokens.colors.textInverse,
106
+ };
107
+ case 'secondary':
108
+ return {
109
+ background: tokens.colors.secondary,
110
+ text: tokens.colors.textInverse,
111
+ };
112
+ case 'success':
113
+ return {
114
+ background: tokens.colors.success,
115
+ text: tokens.colors.textInverse,
116
+ };
117
+ case 'warning':
118
+ return {
119
+ background: tokens.colors.warning,
120
+ text: tokens.colors.textInverse,
121
+ };
122
+ case 'error':
123
+ return {
124
+ background: tokens.colors.error,
125
+ text: tokens.colors.textInverse,
126
+ };
127
+ case 'info':
128
+ return {
129
+ background: tokens.colors.info,
130
+ text: tokens.colors.textInverse,
131
+ };
132
+ default:
133
+ return {
134
+ background: tokens.colors.primary,
135
+ text: tokens.colors.textInverse,
136
+ };
137
+ }
138
+ };
139
+ const getBorderRadius = (shape, defaultRadius, tokens) => {
140
+ switch (shape) {
141
+ case 'circle':
142
+ return tokens.borders.radius.full; // Very large radius for circle
143
+ case 'square':
144
+ return tokens.borders.radius.sm;
145
+ case 'rounded':
146
+ default:
147
+ return defaultRadius;
148
+ }
149
+ };
150
+ // =============================================================================
151
+ // STYLES
152
+ // =============================================================================
153
+ const getStyles = (tokens) => StyleSheet.create({
154
+ container: {
155
+ justifyContent: 'center',
156
+ alignItems: 'center',
157
+ alignSelf: 'flex-start',
158
+ },
159
+ text: {
160
+ fontWeight: tokens.typography.semibold,
161
+ textAlign: 'center',
162
+ },
163
+ });
164
+ // =============================================================================
165
+ // EXPORTS
166
+ // =============================================================================
167
+ export default AtomicBadge;
@@ -0,0 +1,171 @@
1
+ import React from 'react';
2
+ import { StyleSheet, TouchableOpacity, View } from 'react-native';
3
+ import { AtomicText } from './AtomicText';
4
+ import { Icon } from '@umituz/react-native-icon';
5
+ import { useAppDesignTokens } from '@umituz/react-native-theme';
6
+ export const AtomicButton = ({ title, children, onPress, variant = 'primary', size = 'md', disabled = false, icon, fullWidth = false, style, textStyle, testID, }) => {
7
+ const tokens = useAppDesignTokens();
8
+ const handlePress = () => {
9
+ if (!disabled) {
10
+ onPress();
11
+ }
12
+ };
13
+ // Size configurations
14
+ const sizeConfig = {
15
+ sm: {
16
+ paddingVertical: tokens.spacing.xs,
17
+ paddingHorizontal: tokens.spacing.sm,
18
+ fontSize: tokens.typography.bodySmall.fontSize,
19
+ iconSize: 16,
20
+ minHeight: 32,
21
+ },
22
+ md: {
23
+ paddingVertical: tokens.spacing.sm,
24
+ paddingHorizontal: tokens.spacing.md,
25
+ fontSize: tokens.typography.bodyMedium.fontSize,
26
+ iconSize: 20,
27
+ minHeight: 44,
28
+ },
29
+ lg: {
30
+ paddingVertical: tokens.spacing.md,
31
+ paddingHorizontal: tokens.spacing.lg,
32
+ fontSize: tokens.typography.bodyLarge.fontSize,
33
+ iconSize: 24,
34
+ minHeight: 52,
35
+ },
36
+ };
37
+ const config = sizeConfig[size];
38
+ // Variant styles
39
+ const getVariantStyles = () => {
40
+ const baseStyle = {
41
+ backgroundColor: tokens.colors.primary,
42
+ borderWidth: 0,
43
+ };
44
+ const baseTextStyle = {
45
+ color: tokens.colors.textInverse,
46
+ };
47
+ switch (variant) {
48
+ case 'primary':
49
+ return {
50
+ container: {
51
+ ...baseStyle,
52
+ backgroundColor: tokens.colors.primary,
53
+ },
54
+ text: {
55
+ ...baseTextStyle,
56
+ color: tokens.colors.textInverse,
57
+ },
58
+ };
59
+ case 'secondary':
60
+ return {
61
+ container: {
62
+ ...baseStyle,
63
+ backgroundColor: tokens.colors.surfaceSecondary,
64
+ },
65
+ text: {
66
+ ...baseTextStyle,
67
+ color: tokens.colors.textPrimary,
68
+ },
69
+ };
70
+ case 'outline':
71
+ return {
72
+ container: {
73
+ ...baseStyle,
74
+ backgroundColor: 'transparent',
75
+ borderWidth: 1,
76
+ borderColor: tokens.colors.border,
77
+ },
78
+ text: {
79
+ ...baseTextStyle,
80
+ color: tokens.colors.textPrimary,
81
+ },
82
+ };
83
+ case 'text':
84
+ return {
85
+ container: {
86
+ ...baseStyle,
87
+ backgroundColor: 'transparent',
88
+ },
89
+ text: {
90
+ ...baseTextStyle,
91
+ color: tokens.colors.primary,
92
+ },
93
+ };
94
+ case 'danger':
95
+ return {
96
+ container: {
97
+ ...baseStyle,
98
+ backgroundColor: tokens.colors.error,
99
+ },
100
+ text: {
101
+ ...baseTextStyle,
102
+ color: tokens.colors.textInverse,
103
+ },
104
+ };
105
+ default:
106
+ return {
107
+ container: baseStyle,
108
+ text: baseTextStyle,
109
+ };
110
+ }
111
+ };
112
+ const variantStyles = getVariantStyles();
113
+ const containerStyle = [
114
+ styles.button,
115
+ {
116
+ paddingVertical: config.paddingVertical,
117
+ paddingHorizontal: config.paddingHorizontal,
118
+ minHeight: config.minHeight,
119
+ borderRadius: tokens.borders.radius.md,
120
+ },
121
+ variantStyles.container,
122
+ fullWidth ? styles.fullWidth : undefined,
123
+ disabled ? styles.disabled : undefined,
124
+ style,
125
+ ];
126
+ const buttonTextStyle = [
127
+ {
128
+ fontSize: config.fontSize,
129
+ fontWeight: '600',
130
+ },
131
+ variantStyles.text,
132
+ disabled ? styles.disabledText : undefined,
133
+ textStyle,
134
+ ];
135
+ const buttonText = title || children;
136
+ const showIcon = icon;
137
+ const iconColor = variantStyles.text.color;
138
+ return (<TouchableOpacity style={containerStyle} onPress={handlePress} activeOpacity={0.8} disabled={disabled} testID={testID}>
139
+ <View style={styles.content}>
140
+ {showIcon ? (<Icon name={icon} customSize={config.iconSize} customColor={typeof iconColor === 'string' ? iconColor : undefined} style={styles.icon}/>) : null}
141
+
142
+ <AtomicText style={buttonTextStyle}>
143
+ {buttonText}
144
+ </AtomicText>
145
+ </View>
146
+ </TouchableOpacity>);
147
+ };
148
+ const styles = StyleSheet.create({
149
+ button: {
150
+ alignItems: 'center',
151
+ justifyContent: 'center',
152
+ flexDirection: 'row',
153
+ },
154
+ content: {
155
+ flexDirection: 'row',
156
+ alignItems: 'center',
157
+ justifyContent: 'center',
158
+ },
159
+ fullWidth: {
160
+ width: '100%',
161
+ },
162
+ disabled: {
163
+ opacity: 0.5,
164
+ },
165
+ disabledText: {
166
+ opacity: 0.7,
167
+ },
168
+ icon: {
169
+ marginRight: 8,
170
+ },
171
+ });