@umituz/react-native-design-system 1.0.0

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 (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/package.json +43 -0
  4. package/src/index.ts +345 -0
  5. package/src/presentation/atoms/AtomicAvatar.tsx +157 -0
  6. package/src/presentation/atoms/AtomicAvatarGroup.tsx +169 -0
  7. package/src/presentation/atoms/AtomicBadge.tsx +232 -0
  8. package/src/presentation/atoms/AtomicButton.tsx +124 -0
  9. package/src/presentation/atoms/AtomicCard.tsx +112 -0
  10. package/src/presentation/atoms/AtomicChip.tsx +223 -0
  11. package/src/presentation/atoms/AtomicDatePicker.tsx +347 -0
  12. package/src/presentation/atoms/AtomicDivider.tsx +114 -0
  13. package/src/presentation/atoms/AtomicFab.tsx +104 -0
  14. package/src/presentation/atoms/AtomicFilter.tsx +154 -0
  15. package/src/presentation/atoms/AtomicFormError.tsx +105 -0
  16. package/src/presentation/atoms/AtomicIcon.tsx +29 -0
  17. package/src/presentation/atoms/AtomicImage.tsx +149 -0
  18. package/src/presentation/atoms/AtomicInput.tsx +232 -0
  19. package/src/presentation/atoms/AtomicNumberInput.tsx +182 -0
  20. package/src/presentation/atoms/AtomicPicker.tsx +458 -0
  21. package/src/presentation/atoms/AtomicProgress.tsx +143 -0
  22. package/src/presentation/atoms/AtomicSearchBar.tsx +114 -0
  23. package/src/presentation/atoms/AtomicSkeleton.tsx +146 -0
  24. package/src/presentation/atoms/AtomicSort.tsx +145 -0
  25. package/src/presentation/atoms/AtomicSwitch.tsx +166 -0
  26. package/src/presentation/atoms/AtomicText.tsx +50 -0
  27. package/src/presentation/atoms/AtomicTextArea.tsx +198 -0
  28. package/src/presentation/atoms/AtomicTouchable.tsx +233 -0
  29. package/src/presentation/atoms/fab/styles/fabStyles.ts +69 -0
  30. package/src/presentation/atoms/fab/types/index.ts +88 -0
  31. package/src/presentation/atoms/filter/styles/filterStyles.ts +32 -0
  32. package/src/presentation/atoms/filter/types/index.ts +89 -0
  33. package/src/presentation/atoms/index.ts +378 -0
  34. package/src/presentation/atoms/input/hooks/useInputState.ts +15 -0
  35. package/src/presentation/atoms/input/styles/inputStyles.ts +66 -0
  36. package/src/presentation/atoms/input/types/index.ts +25 -0
  37. package/src/presentation/atoms/picker/styles/pickerStyles.ts +200 -0
  38. package/src/presentation/atoms/picker/types/index.ts +40 -0
  39. package/src/presentation/atoms/touchable/styles/touchableStyles.ts +71 -0
  40. package/src/presentation/atoms/touchable/types/index.ts +162 -0
  41. package/src/presentation/hooks/useAppDesignTokens.ts +78 -0
  42. package/src/presentation/hooks/useResponsive.ts +180 -0
  43. package/src/presentation/loading/index.ts +40 -0
  44. package/src/presentation/loading/presentation/components/LoadingSpinner.tsx +116 -0
  45. package/src/presentation/loading/presentation/components/LoadingState.tsx +200 -0
  46. package/src/presentation/loading/presentation/hooks/useLoading.ts +100 -0
  47. package/src/presentation/molecules/AtomicConfirmationModal.tsx +263 -0
  48. package/src/presentation/molecules/EmptyState.tsx +130 -0
  49. package/src/presentation/molecules/FormField.tsx +128 -0
  50. package/src/presentation/molecules/GridContainer.tsx +124 -0
  51. package/src/presentation/molecules/IconContainer.tsx +94 -0
  52. package/src/presentation/molecules/LanguageSwitcher.tsx +42 -0
  53. package/src/presentation/molecules/ListItem.tsx +36 -0
  54. package/src/presentation/molecules/ScreenHeader.tsx +140 -0
  55. package/src/presentation/molecules/SearchBar.tsx +85 -0
  56. package/src/presentation/molecules/SectionCard.tsx +74 -0
  57. package/src/presentation/molecules/SectionContainer.tsx +106 -0
  58. package/src/presentation/molecules/SectionHeader.tsx +125 -0
  59. package/src/presentation/molecules/confirmation-modal/styles/confirmationModalStyles.ts +133 -0
  60. package/src/presentation/molecules/confirmation-modal/types/index.ts +107 -0
  61. package/src/presentation/molecules/index.ts +42 -0
  62. package/src/presentation/molecules/languageswitcher/config/languageSwitcherConfig.ts +5 -0
  63. package/src/presentation/molecules/languageswitcher/hooks/useLanguageNavigation.ts +15 -0
  64. package/src/presentation/molecules/listitem/styles/listItemStyles.ts +19 -0
  65. package/src/presentation/molecules/listitem/types/index.ts +17 -0
  66. package/src/presentation/organisms/AppHeader.tsx +136 -0
  67. package/src/presentation/organisms/FormContainer.tsx +180 -0
  68. package/src/presentation/organisms/ScreenLayout.tsx +209 -0
  69. package/src/presentation/organisms/index.ts +25 -0
  70. package/src/presentation/tokens/AppDesignTokens.ts +57 -0
  71. package/src/presentation/tokens/commonStyles.ts +253 -0
  72. package/src/presentation/tokens/core/BaseTokens.ts +394 -0
  73. package/src/presentation/tokens/core/ColorPalette.ts +398 -0
  74. package/src/presentation/tokens/core/TokenFactory.ts +120 -0
  75. package/src/presentation/utils/platformConstants.ts +124 -0
  76. package/src/presentation/utils/responsive.ts +516 -0
  77. package/src/presentation/utils/variants/compound.ts +29 -0
  78. package/src/presentation/utils/variants/core.ts +39 -0
  79. package/src/presentation/utils/variants/helpers.ts +13 -0
  80. package/src/presentation/utils/variants.ts +3 -0
@@ -0,0 +1,114 @@
1
+ /**
2
+ * AtomicDivider - Universal Divider Component
3
+ *
4
+ * Displays horizontal or vertical dividers for content separation
5
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
+ *
7
+ * Atomic Design Level: ATOM
8
+ * Purpose: Content separation and visual hierarchy
9
+ *
10
+ * Usage:
11
+ * - Section separators
12
+ * - List item dividers
13
+ * - Card separators
14
+ * - Menu dividers
15
+ * - Form field separators
16
+ */
17
+
18
+ import React from 'react';
19
+ import { View, StyleSheet, ViewStyle } from 'react-native';
20
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
21
+
22
+ // =============================================================================
23
+ // TYPE DEFINITIONS
24
+ // =============================================================================
25
+
26
+ export interface AtomicDividerProps {
27
+ /** Divider orientation */
28
+ orientation?: 'horizontal' | 'vertical';
29
+ /** Divider thickness */
30
+ thickness?: 'thin' | 'medium' | 'thick';
31
+ /** Divider color */
32
+ color?: string;
33
+ /** Divider length (for horizontal: width, for vertical: height) */
34
+ length?: number | string;
35
+ /** Margin around the divider */
36
+ margin?: number;
37
+ /** Margin top */
38
+ marginTop?: number;
39
+ /** Margin bottom */
40
+ marginBottom?: number;
41
+ /** Margin left */
42
+ marginLeft?: number;
43
+ /** Margin right */
44
+ marginRight?: number;
45
+ /** Style overrides */
46
+ style?: ViewStyle;
47
+ /** Test ID for testing */
48
+ testID?: string;
49
+ }
50
+
51
+ // =============================================================================
52
+ // COMPONENT IMPLEMENTATION
53
+ // =============================================================================
54
+
55
+ export const AtomicDivider: React.FC<AtomicDividerProps> = ({
56
+ orientation = 'horizontal',
57
+ thickness = 'thin',
58
+ color,
59
+ length,
60
+ margin,
61
+ marginTop,
62
+ marginBottom,
63
+ marginLeft,
64
+ marginRight,
65
+ style,
66
+ testID,
67
+ }) => {
68
+ const tokens = useAppDesignTokens();
69
+
70
+ // Thickness mapping
71
+ const thicknessMap = {
72
+ thin: 1,
73
+ medium: 2,
74
+ thick: 4,
75
+ };
76
+
77
+ const dividerThickness = thicknessMap[thickness];
78
+ const dividerColor = color || tokens.colors.border;
79
+
80
+ // Compute final length values with proper type handling
81
+ const finalLength = length !== undefined ? length : (orientation === 'horizontal' ? '100%' : 20);
82
+
83
+ // Base styles for all dividers
84
+ const baseStyle: ViewStyle = {
85
+ backgroundColor: dividerColor,
86
+ margin: margin,
87
+ marginTop: marginTop,
88
+ marginBottom: marginBottom,
89
+ marginLeft: marginLeft,
90
+ marginRight: marginRight,
91
+ };
92
+
93
+ // Orientation-specific styles with explicit type casting
94
+ const orientationStyle: ViewStyle = (orientation === 'horizontal' ? {
95
+ width: finalLength as ViewStyle['width'],
96
+ height: dividerThickness,
97
+ } : {
98
+ width: dividerThickness,
99
+ height: finalLength as ViewStyle['height'],
100
+ }) as ViewStyle;
101
+
102
+ return (
103
+ <View
104
+ style={[baseStyle, orientationStyle, style]}
105
+ testID={testID}
106
+ />
107
+ );
108
+ };
109
+
110
+ // =============================================================================
111
+ // EXPORTS
112
+ // =============================================================================
113
+
114
+ export default AtomicDivider;
@@ -0,0 +1,104 @@
1
+ import React from 'react';
2
+ import { TouchableOpacity, ActivityIndicator, StyleSheet } from 'react-native';
3
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
4
+ import { useResponsive } from '../hooks/useResponsive';
5
+ import { AtomicIcon } from './AtomicIcon';
6
+ import { AtomicFabProps } from './fab/types';
7
+ import {
8
+ FAB_SIZES,
9
+ getFabVariants,
10
+ getFabIconSize,
11
+ getFabBorder,
12
+ } from './fab/styles/fabStyles';
13
+
14
+ export type { FabSize, FabVariant, FabVariantConfig, FabSizeConfig, AtomicFabProps } from './fab/types';
15
+ export { FAB_SIZES, getFabVariants, getFabIconSize, getFabBorder };
16
+
17
+ /**
18
+ * AtomicFab - Floating Action Button Component
19
+ *
20
+ * A Material Design 3 compliant FAB component for primary actions.
21
+ * Follows CLAUDE.md standards for responsive positioning.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * // IMPORTANT: FAB must be used at screen level, NOT inside ScrollView
26
+ * <ScreenLayout>
27
+ * <ScrollView>
28
+ * {/* Your content *\/}
29
+ * </ScrollView>
30
+ * <AtomicFab
31
+ * icon="add"
32
+ * onPress={handleAddItem}
33
+ * variant="primary"
34
+ * size="md"
35
+ * />
36
+ * </ScreenLayout>
37
+ * ```
38
+ *
39
+ * Features:
40
+ * - Material Design 3 sizes (sm: 40px, md: 56px, lg: 72px)
41
+ * - Three variants: primary, secondary, surface
42
+ * - Responsive positioning (above tab bar, safe area aware)
43
+ * - Loading state with spinner
44
+ * - Disabled state with opacity
45
+ * - Theme-aware colors from design tokens
46
+ * - Border for depth (no shadows per CLAUDE.md)
47
+ */
48
+ export const AtomicFab: React.FC<AtomicFabProps> = ({
49
+ icon,
50
+ onPress,
51
+ variant = 'primary',
52
+ size = 'md',
53
+ disabled = false,
54
+ loading = false,
55
+ style,
56
+ testID,
57
+ accessibilityLabel,
58
+ }) => {
59
+ const tokens = useAppDesignTokens();
60
+ const responsive = useResponsive();
61
+ const isDisabled = disabled || loading;
62
+
63
+ // Get configurations
64
+ const sizeConfig = FAB_SIZES[size];
65
+ const variants = getFabVariants(tokens);
66
+ const variantConfig = variants[variant];
67
+ const iconSize = getFabIconSize(size);
68
+
69
+ // Combine styles
70
+ const fabStyle = StyleSheet.flatten([
71
+ {
72
+ position: 'absolute' as const,
73
+ bottom: responsive.fabPosition.bottom,
74
+ right: responsive.fabPosition.right,
75
+ width: sizeConfig.width,
76
+ height: sizeConfig.height,
77
+ borderRadius: sizeConfig.borderRadius,
78
+ backgroundColor: variantConfig.backgroundColor,
79
+ alignItems: 'center' as const,
80
+ justifyContent: 'center' as const,
81
+ },
82
+ getFabBorder(tokens),
83
+ isDisabled ? { opacity: tokens.opacity.disabled } : undefined,
84
+ style, // Custom style override
85
+ ]);
86
+
87
+ return (
88
+ <TouchableOpacity
89
+ style={fabStyle}
90
+ onPress={onPress}
91
+ disabled={isDisabled}
92
+ activeOpacity={0.7}
93
+ testID={testID}
94
+ accessibilityLabel={accessibilityLabel || `${icon} button`}
95
+ accessibilityRole="button"
96
+ >
97
+ {loading ? (
98
+ <ActivityIndicator size="small" color={variantConfig.iconColor} />
99
+ ) : (
100
+ <AtomicIcon name={icon} size={iconSize} customColor={variantConfig.iconColor} />
101
+ )}
102
+ </TouchableOpacity>
103
+ );
104
+ };
@@ -0,0 +1,154 @@
1
+ import React from 'react';
2
+ import { ScrollView, View, TouchableOpacity, ViewStyle } from 'react-native';
3
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
4
+ import { AtomicChip } from './AtomicChip';
5
+ import { AtomicText } from './AtomicText';
6
+ import { AtomicIcon } from './AtomicIcon';
7
+ import { AtomicFilterProps, FilterOption } from './filter/types';
8
+ import {
9
+ getFilterContainerStyle,
10
+ getClearAllContainerStyle,
11
+ getScrollContentContainerStyle,
12
+ } from './filter/styles/filterStyles';
13
+
14
+ export type { FilterOption, AtomicFilterProps } from './filter/types';
15
+ export {
16
+ getFilterContainerStyle,
17
+ getClearAllContainerStyle,
18
+ getScrollContentContainerStyle,
19
+ } from './filter/styles/filterStyles';
20
+
21
+ /**
22
+ * AtomicFilter - Horizontal Filter Chip Component
23
+ *
24
+ * A Material Design 3 compliant filter component using chip selection.
25
+ * Supports single and multi-select modes with "Clear All" functionality.
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * const [selectedFilters, setSelectedFilters] = useState<string[]>([]);
30
+ *
31
+ * <AtomicFilter
32
+ * options={[
33
+ * { id: 'active', label: 'Active', icon: 'check-circle' },
34
+ * { id: 'completed', label: 'Completed', icon: 'check' },
35
+ * { id: 'pending', label: 'Pending', icon: 'clock' },
36
+ * ]}
37
+ * selectedIds={selectedFilters}
38
+ * onSelectionChange={setSelectedFilters}
39
+ * multiSelect={true}
40
+ * showClearAll={true}
41
+ * />
42
+ * ```
43
+ *
44
+ * Features:
45
+ * - Horizontal scrollable filter chips
46
+ * - Single/Multi-select modes
47
+ * - Clear all button (when filters active)
48
+ * - Theme-aware colors from design tokens
49
+ * - Icon support per filter option
50
+ * - Fully controlled component
51
+ */
52
+ export const AtomicFilter: React.FC<AtomicFilterProps> = ({
53
+ options,
54
+ selectedIds,
55
+ onSelectionChange,
56
+ multiSelect = true,
57
+ showClearAll = true,
58
+ variant = 'outlined',
59
+ color = 'primary',
60
+ size = 'md',
61
+ style,
62
+ testID,
63
+ }) => {
64
+ const tokens = useAppDesignTokens();
65
+
66
+ /**
67
+ * Handle filter chip press
68
+ */
69
+ const handleFilterPress = (optionId: string) => {
70
+ if (multiSelect) {
71
+ // Multi-select mode: Toggle selection
72
+ if (selectedIds.includes(optionId)) {
73
+ // Deselect
74
+ onSelectionChange(selectedIds.filter(id => id !== optionId));
75
+ } else {
76
+ // Select
77
+ onSelectionChange([...selectedIds, optionId]);
78
+ }
79
+ } else {
80
+ // Single-select mode: Replace selection
81
+ if (selectedIds.includes(optionId)) {
82
+ // Deselect (clear selection)
83
+ onSelectionChange([]);
84
+ } else {
85
+ // Select (only this one)
86
+ onSelectionChange([optionId]);
87
+ }
88
+ }
89
+ };
90
+
91
+ /**
92
+ * Handle clear all button press
93
+ */
94
+ const handleClearAll = () => {
95
+ onSelectionChange([]);
96
+ };
97
+
98
+ const hasActiveFilters = selectedIds.length > 0;
99
+
100
+ return (
101
+ <ScrollView
102
+ horizontal
103
+ showsHorizontalScrollIndicator={false}
104
+ contentContainerStyle={getScrollContentContainerStyle()}
105
+ style={[style]}
106
+ testID={testID}
107
+ >
108
+ <View style={getFilterContainerStyle()}>
109
+ {options.map((option) => {
110
+ const isSelected = selectedIds.includes(option.id);
111
+
112
+ return (
113
+ <AtomicChip
114
+ key={option.id}
115
+ variant={isSelected ? 'filled' : variant}
116
+ color={color}
117
+ size={size}
118
+ leadingIcon={option.icon}
119
+ selected={isSelected}
120
+ clickable={true}
121
+ onPress={() => handleFilterPress(option.id)}
122
+ testID={`filter-chip-${option.id}`}
123
+ >
124
+ {option.label}
125
+ </AtomicChip>
126
+ );
127
+ })}
128
+
129
+ {/* Clear All Button */}
130
+ {showClearAll && hasActiveFilters && (
131
+ <TouchableOpacity
132
+ onPress={handleClearAll}
133
+ style={[
134
+ getClearAllContainerStyle(),
135
+ {
136
+ backgroundColor: tokens.colors.surfaceVariant,
137
+ borderWidth: 1,
138
+ borderColor: tokens.colors.outline,
139
+ }
140
+ ]}
141
+ testID="clear-all-button"
142
+ >
143
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: tokens.spacing.xs }}>
144
+ <AtomicIcon name="X" size="xs" color="surfaceVariant" />
145
+ <AtomicText type="labelSmall" style={{ color: tokens.colors.textSecondary }}>
146
+ Clear All
147
+ </AtomicText>
148
+ </View>
149
+ </TouchableOpacity>
150
+ )}
151
+ </View>
152
+ </ScrollView>
153
+ );
154
+ };
@@ -0,0 +1,105 @@
1
+ /**
2
+ * AtomicFormError - Universal Form Error Component
3
+ *
4
+ * Provides consistent error message display for forms
5
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
+ *
7
+ * Atomic Design Level: ATOM
8
+ * Purpose: Display validation error messages
9
+ *
10
+ * Usage:
11
+ * - Form field validation errors
12
+ * - Global form error messages
13
+ * - API error display
14
+ * - Input validation feedback
15
+ */
16
+
17
+ import React from 'react';
18
+ import { View, StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
19
+ import { AtomicText } from './AtomicText';
20
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
21
+ import { withAlpha } from '../tokens/AppDesignTokens';
22
+
23
+ // =============================================================================
24
+ // TYPE DEFINITIONS
25
+ // =============================================================================
26
+
27
+ export interface AtomicFormErrorProps {
28
+ /** Error message to display */
29
+ message: string | null | undefined;
30
+ /** Error display variant */
31
+ variant?: 'global' | 'field';
32
+ /** Custom container style */
33
+ style?: StyleProp<ViewStyle>;
34
+ /** Custom text style */
35
+ textStyle?: StyleProp<TextStyle>;
36
+ }
37
+
38
+ // =============================================================================
39
+ // COMPONENT IMPLEMENTATION
40
+ // =============================================================================
41
+
42
+ export const AtomicFormError: React.FC<AtomicFormErrorProps> = ({
43
+ message,
44
+ variant = 'field',
45
+ style,
46
+ textStyle,
47
+ }) => {
48
+ const tokens = useAppDesignTokens();
49
+
50
+ if (!message) {
51
+ return null;
52
+ }
53
+
54
+ if (variant === 'global') {
55
+ return (
56
+ <View
57
+ style={[
58
+ {
59
+ padding: tokens.spacing.md,
60
+ borderRadius: tokens.borders.radius.md,
61
+ marginBottom: tokens.spacing.sm,
62
+ backgroundColor: withAlpha(tokens.colors.error, 0.15),
63
+ },
64
+ style,
65
+ ]}
66
+ >
67
+ <AtomicText
68
+ type="bodySmall"
69
+ color="error"
70
+ style={StyleSheet.flatten([
71
+ {
72
+ textAlign: 'center',
73
+ fontWeight: tokens.typography.medium,
74
+ },
75
+ textStyle,
76
+ ])}
77
+ >
78
+ {message}
79
+ </AtomicText>
80
+ </View>
81
+ );
82
+ }
83
+
84
+ return (
85
+ <AtomicText
86
+ type="bodySmall"
87
+ color="error"
88
+ style={StyleSheet.flatten([
89
+ {
90
+ marginTop: tokens.spacing.xs,
91
+ marginLeft: tokens.spacing.xs,
92
+ },
93
+ textStyle,
94
+ ])}
95
+ >
96
+ {message}
97
+ </AtomicText>
98
+ );
99
+ };
100
+
101
+ // =============================================================================
102
+ // EXPORTS
103
+ // =============================================================================
104
+
105
+ export default AtomicFormError;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * AtomicIcon - Design System Icon Component
3
+ *
4
+ * Simple wrapper around Icon domain for backward compatibility.
5
+ * All icon functionality is provided by @domains/icons.
6
+ *
7
+ * @deprecated Use Icon from @domains/icons directly in new code
8
+ */
9
+
10
+ import React from 'react';
11
+ import { Icon, type IconProps } from '@domains/icons';
12
+
13
+ /**
14
+ * AtomicIcon Component (Wrapper)
15
+ *
16
+ * This is a simple wrapper around Icon from @domains/icons.
17
+ * It exists for backward compatibility with existing code.
18
+ *
19
+ * For new code, import Icon directly from @domains/icons
20
+ */
21
+ export const AtomicIcon: React.FC<IconProps> = (props) => {
22
+ return <Icon {...props} />;
23
+ };
24
+
25
+ // Re-export types for backward compatibility
26
+ export type { IconProps as AtomicIconProps } from '@domains/icons';
27
+ export type { IconSize as AtomicIconSize } from '@domains/icons';
28
+ export type { IconColor as AtomicIconColor } from '@domains/icons';
29
+ export type { IconName as AtomicIconName } from '@domains/icons';
@@ -0,0 +1,149 @@
1
+ /**
2
+ * AtomicImage - Universal Image Component
3
+ *
4
+ * Provides consistent image handling across the app with theme integration
5
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
+ *
7
+ * Atomic Design Level: ATOM
8
+ * Purpose: Basic image display with consistent styling
9
+ *
10
+ * Usage:
11
+ * - Profile pictures
12
+ * - Product images
13
+ * - Icons and illustrations
14
+ * - Background images
15
+ */
16
+
17
+ import React from 'react';
18
+ import { Image, ImageProps, StyleSheet, ViewStyle } from 'react-native';
19
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
20
+
21
+ // =============================================================================
22
+ // TYPE DEFINITIONS
23
+ // =============================================================================
24
+
25
+ export interface AtomicImageProps extends Omit<ImageProps, 'style'> {
26
+ /** Image source */
27
+ source: ImageProps['source'];
28
+ /** Size variant */
29
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
30
+ /** Shape variant */
31
+ shape?: 'square' | 'circle' | 'rounded';
32
+ /** Border radius override */
33
+ borderRadius?: number;
34
+ /** Container style override */
35
+ style?: ViewStyle | ViewStyle[];
36
+ /** Image style override */
37
+ imageStyle?: ImageProps['style'];
38
+ /** Background color */
39
+ backgroundColor?: string;
40
+ /** Border color */
41
+ borderColor?: string;
42
+ /** Border width */
43
+ borderWidth?: number;
44
+ }
45
+
46
+ // =============================================================================
47
+ // SIZE CONFIGURATION
48
+ // =============================================================================
49
+
50
+ const SIZE_CONFIG = {
51
+ xs: 24,
52
+ sm: 32,
53
+ md: 48,
54
+ lg: 64,
55
+ xl: 96,
56
+ xxl: 128,
57
+ } as const;
58
+
59
+ // =============================================================================
60
+ // COMPONENT IMPLEMENTATION
61
+ // =============================================================================
62
+
63
+ export const AtomicImage: React.FC<AtomicImageProps> = ({
64
+ source,
65
+ size = 'md',
66
+ shape = 'rounded',
67
+ borderRadius,
68
+ style,
69
+ imageStyle,
70
+ backgroundColor,
71
+ borderColor,
72
+ borderWidth = 0,
73
+ ...props
74
+ }) => {
75
+ const tokens = useAppDesignTokens();
76
+ const styles = getStyles(tokens);
77
+
78
+ const imageSize = SIZE_CONFIG[size];
79
+ const calculatedBorderRadius = borderRadius ?? getBorderRadius(shape, imageSize, tokens);
80
+
81
+ const containerStyle = [
82
+ styles.container,
83
+ {
84
+ width: imageSize,
85
+ height: imageSize,
86
+ borderRadius: calculatedBorderRadius,
87
+ backgroundColor: backgroundColor || tokens.colors.surface,
88
+ borderColor: borderColor || tokens.colors.border,
89
+ borderWidth,
90
+ },
91
+ style,
92
+ ];
93
+
94
+ const finalImageStyle = [
95
+ styles.image,
96
+ {
97
+ borderRadius: calculatedBorderRadius,
98
+ },
99
+ imageStyle,
100
+ ];
101
+
102
+ return (
103
+ <Image
104
+ source={source}
105
+ style={finalImageStyle}
106
+ {...props}
107
+ />
108
+ );
109
+ };
110
+
111
+ // =============================================================================
112
+ // HELPER FUNCTIONS
113
+ // =============================================================================
114
+
115
+ const getBorderRadius = (shape: AtomicImageProps['shape'], size: number, tokens: ReturnType<typeof useAppDesignTokens>): number => {
116
+ switch (shape) {
117
+ case 'circle':
118
+ return size / 2;
119
+ case 'square':
120
+ return 0;
121
+ case 'rounded':
122
+ default:
123
+ return tokens.borders.radius.md;
124
+ }
125
+ };
126
+
127
+ // =============================================================================
128
+ // STYLES
129
+ // =============================================================================
130
+
131
+ const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
132
+ StyleSheet.create({
133
+ container: {
134
+ overflow: 'hidden',
135
+ justifyContent: 'center',
136
+ alignItems: 'center',
137
+ },
138
+ image: {
139
+ width: '100%',
140
+ height: '100%',
141
+ resizeMode: 'cover',
142
+ },
143
+ });
144
+
145
+ // =============================================================================
146
+ // EXPORTS
147
+ // =============================================================================
148
+
149
+ export default AtomicImage;