@umituz/react-native-design-system 1.1.3 → 1.3.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.
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
- import { Button as PaperButton } from 'react-native-paper';
3
- import { StyleSheet, StyleProp, ViewStyle, TextStyle } from 'react-native';
2
+ import { StyleSheet, StyleProp, ViewStyle, TextStyle, TouchableOpacity, ActivityIndicator, View } from 'react-native';
4
3
  import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
5
- import { Pressable } from 'react-native';
4
+ import { AtomicText } from './AtomicText';
5
+ import { Icon } from '../../domains/icons/presentation/components/Icon';
6
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
7
+ import type { IconName } from '../../domains/icons/domain/interfaces/IIconAdapter';
6
8
 
7
9
  export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'text' | 'danger';
8
10
  export type ButtonSize = 'sm' | 'md' | 'lg';
@@ -15,14 +17,14 @@ export interface AtomicButtonProps {
15
17
  size?: ButtonSize;
16
18
  disabled?: boolean;
17
19
  loading?: boolean;
18
- icon?: string;
20
+ icon?: IconName;
19
21
  fullWidth?: boolean;
20
22
  style?: StyleProp<ViewStyle>;
21
23
  textStyle?: StyleProp<TextStyle>;
22
24
  testID?: string;
23
25
  }
24
26
 
25
- const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
27
+ const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
26
28
 
27
29
  export const AtomicButton: React.FC<AtomicButtonProps> = ({
28
30
  title,
@@ -35,8 +37,11 @@ export const AtomicButton: React.FC<AtomicButtonProps> = ({
35
37
  icon,
36
38
  fullWidth = false,
37
39
  style,
40
+ textStyle,
38
41
  testID,
39
42
  }) => {
43
+ const tokens = useAppDesignTokens();
44
+
40
45
  // Animation
41
46
  const scale = useSharedValue(1);
42
47
 
@@ -63,62 +68,205 @@ export const AtomicButton: React.FC<AtomicButtonProps> = ({
63
68
  }
64
69
  };
65
70
 
66
- // Map variants to Paper modes
67
- const getPaperMode = (): 'contained' | 'outlined' | 'text' | 'contained-tonal' | 'elevated' => {
71
+ // Size configurations
72
+ const sizeConfig = {
73
+ sm: {
74
+ paddingVertical: tokens.spacing.xs,
75
+ paddingHorizontal: tokens.spacing.sm,
76
+ fontSize: tokens.typography.bodySmall.fontSize,
77
+ iconSize: 16,
78
+ minHeight: 32,
79
+ },
80
+ md: {
81
+ paddingVertical: tokens.spacing.sm,
82
+ paddingHorizontal: tokens.spacing.md,
83
+ fontSize: tokens.typography.bodyMedium.fontSize,
84
+ iconSize: 20,
85
+ minHeight: 44,
86
+ },
87
+ lg: {
88
+ paddingVertical: tokens.spacing.md,
89
+ paddingHorizontal: tokens.spacing.lg,
90
+ fontSize: tokens.typography.bodyLarge.fontSize,
91
+ iconSize: 24,
92
+ minHeight: 52,
93
+ },
94
+ };
95
+
96
+ const config = sizeConfig[size];
97
+
98
+ // Variant styles
99
+ const getVariantStyles = () => {
100
+ const baseStyle: ViewStyle = {
101
+ backgroundColor: tokens.colors.primary,
102
+ borderWidth: 0,
103
+ };
104
+
105
+ const baseTextStyle: TextStyle = {
106
+ color: tokens.colors.textInverse,
107
+ };
108
+
68
109
  switch (variant) {
69
110
  case 'primary':
70
- return 'contained';
111
+ return {
112
+ container: {
113
+ ...baseStyle,
114
+ backgroundColor: tokens.colors.primary,
115
+ },
116
+ text: {
117
+ ...baseTextStyle,
118
+ color: tokens.colors.textInverse,
119
+ },
120
+ };
121
+
71
122
  case 'secondary':
72
- return 'contained-tonal';
123
+ return {
124
+ container: {
125
+ ...baseStyle,
126
+ backgroundColor: tokens.colors.surfaceSecondary,
127
+ },
128
+ text: {
129
+ ...baseTextStyle,
130
+ color: tokens.colors.textPrimary,
131
+ },
132
+ };
133
+
73
134
  case 'outline':
74
- return 'outlined';
135
+ return {
136
+ container: {
137
+ ...baseStyle,
138
+ backgroundColor: 'transparent',
139
+ borderWidth: 1,
140
+ borderColor: tokens.colors.border,
141
+ },
142
+ text: {
143
+ ...baseTextStyle,
144
+ color: tokens.colors.textPrimary,
145
+ },
146
+ };
147
+
75
148
  case 'text':
76
- return 'text';
149
+ return {
150
+ container: {
151
+ ...baseStyle,
152
+ backgroundColor: 'transparent',
153
+ },
154
+ text: {
155
+ ...baseTextStyle,
156
+ color: tokens.colors.primary,
157
+ },
158
+ };
159
+
77
160
  case 'danger':
78
- return 'contained';
161
+ return {
162
+ container: {
163
+ ...baseStyle,
164
+ backgroundColor: tokens.colors.error,
165
+ },
166
+ text: {
167
+ ...baseTextStyle,
168
+ color: tokens.colors.textInverse,
169
+ },
170
+ };
171
+
79
172
  default:
80
- return 'contained';
173
+ return {
174
+ container: baseStyle,
175
+ text: baseTextStyle,
176
+ };
81
177
  }
82
178
  };
83
179
 
84
- // Map size to padding
85
- const getContentStyle = () => {
86
- const paddingMap = {
87
- sm: { paddingVertical: 4, paddingHorizontal: 12 },
88
- md: { paddingVertical: 8, paddingHorizontal: 16 },
89
- lg: { paddingVertical: 12, paddingHorizontal: 20 },
90
- };
91
- return paddingMap[size];
92
- };
180
+ const variantStyles = getVariantStyles();
93
181
 
94
- const buttonStyle: StyleProp<ViewStyle> = StyleSheet.flatten([
95
- fullWidth ? { width: '100%' } : undefined,
182
+ const containerStyle: StyleProp<ViewStyle> = [
183
+ styles.button,
184
+ {
185
+ paddingVertical: config.paddingVertical,
186
+ paddingHorizontal: config.paddingHorizontal,
187
+ minHeight: config.minHeight,
188
+ borderRadius: tokens.borders.radius.md,
189
+ },
190
+ variantStyles.container,
191
+ fullWidth ? styles.fullWidth : undefined,
192
+ disabled ? styles.disabled : undefined,
96
193
  style,
97
- ]);
194
+ ];
195
+
196
+ const buttonTextStyle: StyleProp<TextStyle> = [
197
+ {
198
+ fontSize: config.fontSize,
199
+ fontWeight: '600',
200
+ },
201
+ variantStyles.text,
202
+ disabled ? styles.disabledText : undefined,
203
+ textStyle,
204
+ ];
98
205
 
99
206
  const buttonText = title || children;
207
+ const showIcon = icon && !loading;
208
+ const iconColor = variantStyles.text.color;
100
209
 
101
210
  return (
102
- <AnimatedPressable
103
- style={animatedStyle}
211
+ <AnimatedTouchable
212
+ style={[animatedStyle, containerStyle]}
104
213
  onPressIn={handlePressIn}
105
214
  onPressOut={handlePressOut}
106
215
  onPress={handlePress}
216
+ activeOpacity={0.8}
217
+ disabled={disabled || loading}
218
+ testID={testID}
107
219
  >
108
- <PaperButton
109
- mode={getPaperMode()}
110
- disabled={disabled}
111
- loading={loading}
112
- icon={icon}
113
- style={buttonStyle}
114
- contentStyle={getContentStyle()}
115
- testID={testID}
116
- buttonColor={variant === 'danger' ? '#DC2626' : undefined}
117
- >
118
- {buttonText}
119
- </PaperButton>
120
- </AnimatedPressable>
220
+ <View style={styles.content}>
221
+ {loading ? (
222
+ <ActivityIndicator
223
+ size="small"
224
+ color={variantStyles.text.color}
225
+ style={styles.loader}
226
+ />
227
+ ) : showIcon ? (
228
+ <Icon
229
+ name={icon}
230
+ customSize={config.iconSize}
231
+ customColor={iconColor}
232
+ style={styles.icon}
233
+ />
234
+ ) : null}
235
+
236
+ <AtomicText style={buttonTextStyle}>
237
+ {buttonText}
238
+ </AtomicText>
239
+ </View>
240
+ </AnimatedTouchable>
121
241
  );
122
242
  };
123
243
 
244
+ const styles = StyleSheet.create({
245
+ button: {
246
+ alignItems: 'center',
247
+ justifyContent: 'center',
248
+ flexDirection: 'row',
249
+ },
250
+ content: {
251
+ flexDirection: 'row',
252
+ alignItems: 'center',
253
+ justifyContent: 'center',
254
+ },
255
+ fullWidth: {
256
+ width: '100%',
257
+ },
258
+ disabled: {
259
+ opacity: 0.5,
260
+ },
261
+ disabledText: {
262
+ opacity: 0.7,
263
+ },
264
+ icon: {
265
+ marginRight: 8,
266
+ },
267
+ loader: {
268
+ marginRight: 8,
269
+ },
270
+ });
271
+
124
272
  export type { AtomicButtonProps as ButtonProps };
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { Card as PaperCard } from 'react-native-paper';
3
- import { StyleProp, ViewStyle } from 'react-native';
2
+ import { View, StyleProp, ViewStyle } from 'react-native';
4
3
  import Animated, { useSharedValue, useAnimatedStyle, withSpring } from 'react-native-reanimated';
5
4
  import { Pressable } from 'react-native';
5
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
6
6
 
7
7
  export type AtomicCardVariant = 'flat' | 'elevated' | 'outlined';
8
8
  export type AtomicCardPadding = 'none' | 'sm' | 'md' | 'lg' | 'xl';
@@ -28,6 +28,8 @@ export const AtomicCard: React.FC<AtomicCardProps> = ({
28
28
  children,
29
29
  testID,
30
30
  }) => {
31
+ const tokens = useAppDesignTokens();
32
+
31
33
  // Animation for tap feedback
32
34
  const scale = useSharedValue(1);
33
35
 
@@ -54,43 +56,64 @@ export const AtomicCard: React.FC<AtomicCardProps> = ({
54
56
  }
55
57
  };
56
58
 
57
- // Map variants to Paper modes
58
- const getPaperMode = (): 'elevated' | 'outlined' | 'contained' => {
59
+ // Map padding to token values
60
+ const getPaddingValue = (): number => {
61
+ const paddingMap = {
62
+ none: 0,
63
+ sm: tokens.spacing.sm,
64
+ md: tokens.spacing.md,
65
+ lg: tokens.spacing.lg,
66
+ xl: tokens.spacing.xl,
67
+ };
68
+ return paddingMap[padding];
69
+ };
70
+
71
+ // Get variant styles
72
+ const getVariantStyle = (): ViewStyle => {
73
+ const baseStyle: ViewStyle = {
74
+ backgroundColor: tokens.colors.surface,
75
+ borderRadius: tokens.borders.radius.md,
76
+ };
77
+
59
78
  switch (variant) {
60
79
  case 'elevated':
61
- return 'elevated';
80
+ return {
81
+ ...baseStyle,
82
+ borderWidth: 1,
83
+ borderColor: tokens.colors.border,
84
+ };
85
+
62
86
  case 'outlined':
63
- return 'outlined';
87
+ return {
88
+ ...baseStyle,
89
+ borderWidth: 1,
90
+ borderColor: tokens.colors.border,
91
+ };
92
+
64
93
  case 'flat':
65
- return 'contained';
94
+ return {
95
+ ...baseStyle,
96
+ borderWidth: 0,
97
+ };
98
+
66
99
  default:
67
- return 'elevated';
100
+ return baseStyle;
68
101
  }
69
102
  };
70
103
 
71
- // Map padding to actual values
72
- const getContentStyle = () => {
73
- const paddingMap = {
74
- none: 0,
75
- sm: 8,
76
- md: 16,
77
- lg: 24,
78
- xl: 32,
79
- };
80
- const paddingValue = paddingMap[padding];
81
- return { padding: paddingValue };
82
- };
104
+ const cardStyle: StyleProp<ViewStyle> = [
105
+ getVariantStyle(),
106
+ {
107
+ padding: getPaddingValue(),
108
+ opacity: disabled ? 0.5 : 1,
109
+ },
110
+ style,
111
+ ];
83
112
 
84
113
  const cardContent = (
85
- <PaperCard
86
- mode={getPaperMode()}
87
- style={style}
88
- testID={testID}
89
- >
90
- <PaperCard.Content style={getContentStyle()}>
91
- {children}
92
- </PaperCard.Content>
93
- </PaperCard>
114
+ <View style={cardStyle} testID={testID}>
115
+ {children}
116
+ </View>
94
117
  );
95
118
 
96
119
  // If onPress provided, wrap with animated pressable
@@ -1,132 +1,41 @@
1
1
  /**
2
- * AtomicIcon - Design System Icon Component
2
+ * AtomicIcon - Atomic Design System Icon Component
3
3
  *
4
- * Universal icon component using Lucide icons.
5
- * Provides semantic color mappings and size presets.
4
+ * Wrapper for the universal Icon component from @domains/icons
5
+ * Provides backward compatibility with AtomicIcon naming convention
6
+ * while leveraging the full power of the icons domain architecture.
6
7
  */
7
8
 
8
9
  import React from 'react';
9
- import type { ViewStyle } from 'react-native';
10
- import * as LucideIcons from 'lucide-react-native';
11
- import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
12
-
13
- /**
14
- * Icon size presets
15
- */
16
- export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
17
-
18
- /**
19
- * Semantic color tokens
20
- */
21
- export type IconColor =
22
- | 'primary'
23
- | 'secondary'
24
- | 'error'
25
- | 'warning'
26
- | 'success'
27
- | 'info'
28
- | 'onPrimary'
29
- | 'onSurface'
30
- | 'textPrimary'
31
- | 'textSecondary'
32
- | 'textDisabled';
33
-
34
- /**
35
- * Icon component props
36
- */
37
- export interface IconProps {
38
- /** Icon name from Lucide icon set (PascalCase) */
39
- name: string;
40
- /** Semantic color token */
41
- color?: IconColor;
42
- /** Size preset */
43
- size?: IconSize;
44
- /** Custom size override (pixels) */
45
- customSize?: number;
46
- /** Custom color override (hex) */
47
- customColor?: string;
48
- /** Optional style */
49
- style?: ViewStyle;
50
- /** Optional test ID */
51
- testID?: string;
52
- /** Legacy prop support: icon (alias for name) */
53
- icon?: string;
54
- }
55
-
56
- /**
57
- * Icon name type (all Lucide icon names as string)
58
- */
59
- export type IconName = string;
60
-
61
- /**
62
- * Size mapping (pixels)
63
- */
64
- const sizeMap: Record<IconSize, number> = {
65
- xs: 16,
66
- sm: 20,
67
- md: 24,
68
- lg: 28,
69
- xl: 32,
70
- xxl: 48,
71
- };
10
+ import { Icon } from '../../domains/icons/presentation/components/Icon';
11
+ import type { IconProps, IconSize, IconColor } from '../../domains/icons/domain/interfaces/IIconAdapter';
12
+ import type { LucideIconName } from '../../domains/icons/infrastructure/adapters/LucideAdapter';
72
13
 
73
14
  /**
74
15
  * AtomicIcon Component
75
16
  *
76
- * Universal icon component with theme integration
17
+ * @example
18
+ * ```tsx
19
+ * import { AtomicIcon } from '@umituz/react-native-design-system';
20
+ *
21
+ * // Basic usage
22
+ * <AtomicIcon name="Settings" size="md" color="primary" />
23
+ *
24
+ * // Custom size and color
25
+ * <AtomicIcon name="Heart" customSize={32} customColor="#FF0000" />
26
+ *
27
+ * // With background
28
+ * <AtomicIcon name="Info" size="lg" withBackground backgroundColor="#667eea" />
29
+ * ```
77
30
  */
78
- export const AtomicIcon: React.FC<IconProps> = ({
79
- name,
80
- icon, // Legacy support
81
- color = 'textPrimary',
82
- size = 'md',
83
- customSize,
84
- customColor,
85
- style,
86
- testID,
87
- }) => {
88
- const tokens = useAppDesignTokens();
89
-
90
- // Support legacy 'icon' prop (fallback to 'name')
91
- const iconName = name || icon || '';
92
-
93
- // Get icon component from Lucide
94
- const IconComponent = (LucideIcons as any)[iconName];
95
-
96
- if (!IconComponent) {
97
- console.warn(`Icon "${iconName}" not found in Lucide icon set`);
98
- return null;
99
- }
100
-
101
- // Resolve color from semantic token
102
- const resolveColor = (): string => {
103
- if (customColor) return customColor;
104
-
105
- const colorMap: Record<IconColor, string> = {
106
- primary: tokens.colors.primary,
107
- secondary: tokens.colors.secondary,
108
- error: tokens.colors.error,
109
- warning: tokens.colors.warning,
110
- success: tokens.colors.success,
111
- info: tokens.colors.info,
112
- onPrimary: tokens.colors.onPrimary,
113
- onSurface: tokens.colors.onSurface,
114
- textPrimary: tokens.colors.textPrimary,
115
- textSecondary: tokens.colors.textSecondary,
116
- textDisabled: tokens.colors.textDisabled,
117
- };
118
-
119
- return colorMap[color];
120
- };
121
-
122
- // Resolve size
123
- const iconSize = customSize || sizeMap[size];
124
-
125
- return <IconComponent color={resolveColor()} size={iconSize} style={style} testID={testID} />;
31
+ export const AtomicIcon: React.FC<IconProps> = (props) => {
32
+ return <Icon {...props} />;
126
33
  };
127
34
 
128
- // Re-export types for backward compatibility
35
+ /**
36
+ * Re-export types with Atomic naming convention
37
+ */
129
38
  export type AtomicIconProps = IconProps;
130
39
  export type AtomicIconSize = IconSize;
131
40
  export type AtomicIconColor = IconColor;
132
- export type AtomicIconName = IconName;
41
+ export type AtomicIconName = LucideIconName;