@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,200 @@
1
+ /**
2
+ * LoadingState - Dynamic Icon-Based Loading Component
3
+ *
4
+ * Universal loading component with configurable emoji/icon support
5
+ * Inspired by meditation_timer's breathing animation pattern
6
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
7
+ *
8
+ * Features:
9
+ * - ✅ Dynamic emoji/icon per screen (🏠 Home, ⚙️ Settings, 💪 Workout, etc.)
10
+ * - ✅ Breathing animation effect (scale 1 → 1.15 → 1)
11
+ * - ✅ Size variants (small, medium, large)
12
+ * - ✅ Full screen or inline modes
13
+ * - ✅ Optional loading message
14
+ * - ✅ Theme-aware styling
15
+ */
16
+
17
+ import React, { useRef, useEffect, useMemo } from 'react';
18
+ import {
19
+ View,
20
+ StyleSheet,
21
+ Animated,
22
+ Easing,
23
+ } from 'react-native';
24
+ import { useAppDesignTokens } from '../../../hooks/useAppDesignTokens';
25
+ import { STATIC_TOKENS } from '../../../tokens/AppDesignTokens';
26
+ import { AtomicText } from '../../../atoms/AtomicText';
27
+
28
+ // =============================================================================
29
+ // TYPE DEFINITIONS
30
+ // =============================================================================
31
+
32
+ export type LoadingStateSize = 'small' | 'medium' | 'large';
33
+
34
+ export interface LoadingStateProps {
35
+ /**
36
+ * Emoji/icon to display (changes per screen context)
37
+ * Examples: 🏠 Home, ⚙️ Settings, 💪 Workout, 🧘 Meditation, 📊 Analytics
38
+ */
39
+ icon?: string;
40
+
41
+ /**
42
+ * Optional loading message
43
+ */
44
+ message?: string;
45
+
46
+ /**
47
+ * Size variant
48
+ */
49
+ size?: LoadingStateSize;
50
+
51
+ /**
52
+ * Full screen overlay mode
53
+ */
54
+ fullScreen?: boolean;
55
+ }
56
+
57
+ // =============================================================================
58
+ // SIZE CONFIGURATION
59
+ // =============================================================================
60
+
61
+ interface SizeConfig {
62
+ iconSize: number;
63
+ showMessage: boolean;
64
+ containerPadding: number;
65
+ }
66
+
67
+ const SIZE_CONFIG: Record<LoadingStateSize, SizeConfig> = {
68
+ small: {
69
+ iconSize: 32,
70
+ showMessage: false,
71
+ containerPadding: 16,
72
+ },
73
+ medium: {
74
+ iconSize: 48,
75
+ showMessage: true,
76
+ containerPadding: 24,
77
+ },
78
+ large: {
79
+ iconSize: 64,
80
+ showMessage: true,
81
+ containerPadding: 32,
82
+ },
83
+ };
84
+
85
+ // =============================================================================
86
+ // COMPONENT IMPLEMENTATION
87
+ // =============================================================================
88
+
89
+ export const LoadingState: React.FC<LoadingStateProps> = ({
90
+ icon = '⏳', // Default hourglass icon
91
+ message,
92
+ size = 'large',
93
+ fullScreen = false,
94
+ }) => {
95
+ // ✅ Dynamic theme tokens
96
+ const tokens = useAppDesignTokens();
97
+
98
+ // Animation ref for breathing effect
99
+ const scaleAnim = useRef(new Animated.Value(1)).current;
100
+
101
+ // Size configuration
102
+ const config = SIZE_CONFIG[size];
103
+
104
+ /**
105
+ * Breathing Animation Effect
106
+ * Smoothly scales icon from 1 → 1.15 → 1 in continuous loop
107
+ * Creates calming, natural breathing sensation
108
+ */
109
+ useEffect(() => {
110
+ const breathingAnimation = Animated.loop(
111
+ Animated.sequence([
112
+ // Expand (inhale)
113
+ Animated.timing(scaleAnim, {
114
+ toValue: 1.15,
115
+ duration: tokens.animations.slowest,
116
+ easing: Easing.inOut(Easing.ease),
117
+ useNativeDriver: true,
118
+ }),
119
+ // Contract (exhale)
120
+ Animated.timing(scaleAnim, {
121
+ toValue: 1,
122
+ duration: tokens.animations.slowest,
123
+ easing: Easing.inOut(Easing.ease),
124
+ useNativeDriver: true,
125
+ }),
126
+ ])
127
+ );
128
+
129
+ breathingAnimation.start();
130
+
131
+ return () => {
132
+ breathingAnimation.stop();
133
+ };
134
+ }, [scaleAnim]);
135
+
136
+ // Dynamic styles based on theme
137
+ const styles = useMemo(() => getStyles(tokens, config, fullScreen), [tokens, config, fullScreen]);
138
+
139
+ return (
140
+ <View style={styles.container}>
141
+ {/* Animated Icon/Emoji */}
142
+ <Animated.Text
143
+ style={[
144
+ styles.icon,
145
+ {
146
+ fontSize: config.iconSize,
147
+ transform: [{ scale: scaleAnim }],
148
+ },
149
+ ]}
150
+ >
151
+ {icon}
152
+ </Animated.Text>
153
+
154
+ {/* Optional Loading Message */}
155
+ {config.showMessage && message && (
156
+ <AtomicText
157
+ type="bodyMedium"
158
+ style={styles.message}
159
+ >
160
+ {message}
161
+ </AtomicText>
162
+ )}
163
+ </View>
164
+ );
165
+ };
166
+
167
+ // =============================================================================
168
+ // STYLES
169
+ // =============================================================================
170
+
171
+ const getStyles = (
172
+ tokens: ReturnType<typeof useAppDesignTokens>,
173
+ config: SizeConfig,
174
+ fullScreen: boolean
175
+ ) => StyleSheet.create({
176
+ container: {
177
+ ...(fullScreen ? {
178
+ flex: 1,
179
+ justifyContent: 'center',
180
+ alignItems: 'center',
181
+ backgroundColor: tokens.colors.backgroundPrimary,
182
+ } : {
183
+ justifyContent: 'center',
184
+ alignItems: 'center',
185
+ padding: config.containerPadding,
186
+ }),
187
+ },
188
+ icon: {
189
+ textAlign: 'center',
190
+ marginBottom: tokens.spacing.md,
191
+ },
192
+ message: {
193
+ color: tokens.colors.textSecondary,
194
+ textAlign: 'center',
195
+ marginTop: tokens.spacing.sm,
196
+ maxWidth: 300,
197
+ },
198
+ });
199
+
200
+ export default LoadingState;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * useLoading - Loading State Management Hook
3
+ *
4
+ * Centralized hook for managing loading states across the application
5
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
+ *
7
+ * Features:
8
+ * - ✅ Simple boolean loading state
9
+ * - ✅ Message management
10
+ * - ✅ Icon configuration per context
11
+ * - ✅ Type-safe loading control
12
+ */
13
+
14
+ import { useState, useCallback } from 'react';
15
+
16
+ // =============================================================================
17
+ // TYPE DEFINITIONS
18
+ // =============================================================================
19
+
20
+ export interface LoadingConfig {
21
+ isLoading: boolean;
22
+ message?: string;
23
+ icon?: string;
24
+ }
25
+
26
+ export interface UseLoadingReturn {
27
+ /**
28
+ * Current loading state
29
+ */
30
+ isLoading: boolean;
31
+
32
+ /**
33
+ * Current loading message
34
+ */
35
+ message: string | undefined;
36
+
37
+ /**
38
+ * Current loading icon
39
+ */
40
+ icon: string | undefined;
41
+
42
+ /**
43
+ * Start loading with optional message and icon
44
+ */
45
+ startLoading: (message?: string, icon?: string) => void;
46
+
47
+ /**
48
+ * Stop loading and clear message
49
+ */
50
+ stopLoading: () => void;
51
+
52
+ /**
53
+ * Update loading message without affecting state
54
+ */
55
+ setMessage: (message: string | undefined) => void;
56
+
57
+ /**
58
+ * Update loading icon without affecting state
59
+ */
60
+ setIcon: (icon: string | undefined) => void;
61
+ }
62
+
63
+ // =============================================================================
64
+ // HOOK IMPLEMENTATION
65
+ // =============================================================================
66
+
67
+ export const useLoading = (initialConfig?: LoadingConfig): UseLoadingReturn => {
68
+ const [isLoading, setIsLoading] = useState(initialConfig?.isLoading ?? false);
69
+ const [message, setMessage] = useState<string | undefined>(initialConfig?.message);
70
+ const [icon, setIcon] = useState<string | undefined>(initialConfig?.icon);
71
+
72
+ /**
73
+ * Start loading with optional message and icon
74
+ */
75
+ const startLoading = useCallback((msg?: string, ico?: string) => {
76
+ setIsLoading(true);
77
+ if (msg !== undefined) setMessage(msg);
78
+ if (ico !== undefined) setIcon(ico);
79
+ }, []);
80
+
81
+ /**
82
+ * Stop loading and clear message
83
+ */
84
+ const stopLoading = useCallback(() => {
85
+ setIsLoading(false);
86
+ setMessage(undefined);
87
+ }, []);
88
+
89
+ return {
90
+ isLoading,
91
+ message,
92
+ icon,
93
+ startLoading,
94
+ stopLoading,
95
+ setMessage,
96
+ setIcon,
97
+ };
98
+ };
99
+
100
+ export default useLoading;
@@ -0,0 +1,263 @@
1
+ /**
2
+ * AtomicConfirmationModal - Universal Confirmation Dialog
3
+ *
4
+ * A reusable confirmation modal for destructive and important actions.
5
+ * Follows Material Design 3 dialog patterns and accessibility guidelines.
6
+ *
7
+ * Features:
8
+ * - Multiple variants (default, destructive, warning, success)
9
+ * - Configurable text and icons
10
+ * - Backdrop dismissal
11
+ * - Full keyboard and screen reader support
12
+ * - Theme-aware styling
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * // Destructive confirmation (delete)
17
+ * <AtomicConfirmationModal
18
+ * visible={showDeleteModal}
19
+ * variant="destructive"
20
+ * title="Delete Item?"
21
+ * message="This action cannot be undone. All data will be permanently deleted."
22
+ * confirmText="Delete"
23
+ * cancelText="Cancel"
24
+ * onConfirm={handleDelete}
25
+ * onCancel={() => setShowDeleteModal(false)}
26
+ * />
27
+ *
28
+ * // Generic confirmation
29
+ * <AtomicConfirmationModal
30
+ * visible={showConfirmModal}
31
+ * variant="default"
32
+ * title="Confirm Action"
33
+ * message="Are you sure you want to proceed?"
34
+ * onConfirm={handleConfirm}
35
+ * onCancel={() => setShowConfirmModal(false)}
36
+ * />
37
+ * ```
38
+ */
39
+
40
+ import React from 'react';
41
+ import { View, Modal, TouchableOpacity } from 'react-native';
42
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
43
+ import { useLocalization } from '@domains/localization';
44
+ import { AtomicText } from '../atoms/AtomicText';
45
+ import { AtomicButton } from '../atoms/AtomicButton';
46
+ import { AtomicIcon } from '../atoms/AtomicIcon';
47
+ import {
48
+ AtomicConfirmationModalProps,
49
+ ConfirmationModalVariant,
50
+ } from './confirmation-modal/types';
51
+ import {
52
+ getVariantConfig,
53
+ getModalOverlayStyle,
54
+ getBackdropStyle,
55
+ getModalContainerStyle,
56
+ getIconContainerStyle,
57
+ getTitleContainerStyle,
58
+ getMessageContainerStyle,
59
+ getButtonContainerStyle,
60
+ getButtonStyle,
61
+ } from './confirmation-modal/styles/confirmationModalStyles';
62
+
63
+ export type { AtomicConfirmationModalProps };
64
+ export type { ConfirmationModalVariant };
65
+
66
+ export const AtomicConfirmationModal: React.FC<AtomicConfirmationModalProps> = ({
67
+ visible,
68
+ title,
69
+ message,
70
+ variant = 'default',
71
+ confirmText,
72
+ cancelText,
73
+ icon,
74
+ onConfirm,
75
+ onCancel,
76
+ showBackdrop = true,
77
+ backdropDismissible = true,
78
+ style,
79
+ testID = 'atomic-confirmation-modal',
80
+ }) => {
81
+ const tokens = useAppDesignTokens();
82
+ const { t } = useLocalization();
83
+
84
+ // Get variant-specific configuration (icon and color only)
85
+ const variantConfig = getVariantConfig(variant, tokens);
86
+
87
+ // Get locale-aware default text based on variant
88
+ const getDefaultConfirmText = (): string => {
89
+ switch (variant) {
90
+ case 'destructive':
91
+ return t('general.delete');
92
+ case 'warning':
93
+ return t('general.continue');
94
+ case 'success':
95
+ case 'default':
96
+ default:
97
+ return t('general.confirm');
98
+ }
99
+ };
100
+
101
+ // Determine final text values
102
+ const finalConfirmText = confirmText || getDefaultConfirmText();
103
+ const finalCancelText = cancelText || t('general.cancel');
104
+
105
+ // Determine final icon
106
+ const finalIcon = icon || variantConfig.icon;
107
+
108
+ // Handle backdrop press
109
+ const handleBackdropPress = () => {
110
+ if (backdropDismissible) {
111
+ onCancel();
112
+ }
113
+ };
114
+
115
+ return (
116
+ <Modal
117
+ visible={visible}
118
+ transparent
119
+ animationType="fade"
120
+ onRequestClose={onCancel}
121
+ statusBarTranslucent
122
+ testID={testID}
123
+ >
124
+ <View style={getModalOverlayStyle(tokens)}>
125
+ {/* Backdrop - Tap to dismiss if enabled */}
126
+ {showBackdrop && (
127
+ <TouchableOpacity
128
+ style={getBackdropStyle()}
129
+ activeOpacity={1}
130
+ onPress={handleBackdropPress}
131
+ testID={`${testID}-backdrop`}
132
+ />
133
+ )}
134
+
135
+ {/* Modal Container */}
136
+ <View style={[getModalContainerStyle(tokens), style]}>
137
+ {/* Icon */}
138
+ <View style={getIconContainerStyle(tokens)}>
139
+ <AtomicIcon
140
+ name={finalIcon}
141
+ size="xl"
142
+ color={variantConfig.iconColor}
143
+ testID={`${testID}-icon`}
144
+ />
145
+ </View>
146
+
147
+ {/* Title */}
148
+ <View style={getTitleContainerStyle(tokens)}>
149
+ <AtomicText
150
+ type="titleLarge"
151
+ style={{
152
+ color: tokens.colors.textPrimary,
153
+ textAlign: 'center',
154
+ fontWeight: tokens.typography.bold,
155
+ }}
156
+ testID={`${testID}-title`}
157
+ >
158
+ {title}
159
+ </AtomicText>
160
+ </View>
161
+
162
+ {/* Message */}
163
+ <View style={getMessageContainerStyle(tokens)}>
164
+ <AtomicText
165
+ type="bodyMedium"
166
+ style={{
167
+ color: tokens.colors.textSecondary,
168
+ textAlign: 'center',
169
+ lineHeight: tokens.typography.bodyMedium.lineHeight,
170
+ }}
171
+ testID={`${testID}-message`}
172
+ >
173
+ {message}
174
+ </AtomicText>
175
+ </View>
176
+
177
+ {/* Action Buttons */}
178
+ <View style={getButtonContainerStyle(tokens)}>
179
+ {/* Cancel Button */}
180
+ <AtomicButton
181
+ variant="outline"
182
+ size="md"
183
+ onPress={onCancel}
184
+ style={getButtonStyle()}
185
+ testID={`${testID}-cancel-button`}
186
+ >
187
+ {finalCancelText}
188
+ </AtomicButton>
189
+
190
+ {/* Confirm Button */}
191
+ <AtomicButton
192
+ variant={variant === 'destructive' ? 'primary' : 'primary'}
193
+ size="md"
194
+ onPress={onConfirm}
195
+ style={[
196
+ getButtonStyle(),
197
+ ...(variant === 'destructive' ? [{ backgroundColor: tokens.colors.error }] : []),
198
+ ...(variant === 'warning' ? [{ backgroundColor: tokens.colors.warning }] : []),
199
+ ...(variant === 'success' ? [{ backgroundColor: tokens.colors.success }] : []),
200
+ ]}
201
+ testID={`${testID}-confirm-button`}
202
+ >
203
+ {finalConfirmText}
204
+ </AtomicButton>
205
+ </View>
206
+ </View>
207
+ </View>
208
+ </Modal>
209
+ );
210
+ };
211
+
212
+ /**
213
+ * Hook for managing confirmation modal state
214
+ *
215
+ * @example
216
+ * ```tsx
217
+ * const { showConfirmation, confirmationProps } = useConfirmationModal({
218
+ * title: 'Delete Item?',
219
+ * message: 'This cannot be undone',
220
+ * variant: 'destructive',
221
+ * onConfirm: handleDelete,
222
+ * });
223
+ *
224
+ * // In JSX
225
+ * <AtomicConfirmationModal {...confirmationProps} />
226
+ * <Button onPress={showConfirmation}>Delete</Button>
227
+ * ```
228
+ */
229
+ export const useConfirmationModal = (config: {
230
+ title: string;
231
+ message: string;
232
+ variant?: ConfirmationModalVariant;
233
+ confirmText?: string;
234
+ cancelText?: string;
235
+ onConfirm: () => void;
236
+ }) => {
237
+ const [visible, setVisible] = React.useState(false);
238
+
239
+ const showConfirmation = () => setVisible(true);
240
+ const hideConfirmation = () => setVisible(false);
241
+
242
+ const handleConfirm = () => {
243
+ config.onConfirm();
244
+ hideConfirmation();
245
+ };
246
+
247
+ const confirmationProps: AtomicConfirmationModalProps = {
248
+ visible,
249
+ title: config.title,
250
+ message: config.message,
251
+ variant: config.variant || 'default',
252
+ confirmText: config.confirmText,
253
+ cancelText: config.cancelText,
254
+ onConfirm: handleConfirm,
255
+ onCancel: hideConfirmation,
256
+ };
257
+
258
+ return {
259
+ showConfirmation,
260
+ hideConfirmation,
261
+ confirmationProps,
262
+ };
263
+ };
@@ -0,0 +1,130 @@
1
+ /**
2
+ * EmptyState Molecule - Universal Empty State Display
3
+ *
4
+ * Displays icon, title, and subtitle for empty data scenarios
5
+ * Theme: {{THEME_NAME}} ({{CATEGORY}} category)
6
+ *
7
+ * Atomic Design Level: MOLECULE
8
+ * Composition: Icon + AtomicText + Layout
9
+ *
10
+ * Usage:
11
+ * - Empty lists
12
+ * - Empty grids
13
+ * - No search results
14
+ * - No data states
15
+ */
16
+
17
+ import React from 'react';
18
+ import { View, StyleSheet, ViewStyle, TextStyle } from 'react-native';
19
+ import { AtomicText, AtomicIcon } from '../atoms';
20
+ import { useAppDesignTokens } from '../hooks/useAppDesignTokens';
21
+
22
+ // =============================================================================
23
+ // TYPE DEFINITIONS
24
+ // =============================================================================
25
+
26
+ export interface EmptyStateProps {
27
+ /** Material icon name */
28
+ icon: string;
29
+ /** Icon size (default: xl) */
30
+ iconSize?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
31
+ /** Main heading text */
32
+ title: string;
33
+ /** Descriptive subtitle text */
34
+ subtitle?: string;
35
+ /** Custom icon color (default: textTertiary) */
36
+ iconColor?: string;
37
+ /** Custom title color (default: textPrimary) */
38
+ titleColor?: string;
39
+ /** Custom subtitle color (default: textSecondary) */
40
+ subtitleColor?: string;
41
+ /** Container style override */
42
+ style?: ViewStyle;
43
+ /** Title style override */
44
+ titleStyle?: TextStyle;
45
+ /** Subtitle style override */
46
+ subtitleStyle?: TextStyle;
47
+ }
48
+
49
+ // =============================================================================
50
+ // COMPONENT IMPLEMENTATION
51
+ // =============================================================================
52
+
53
+ export const EmptyState: React.FC<EmptyStateProps> = ({
54
+ icon,
55
+ iconSize = 'xl',
56
+ title,
57
+ subtitle,
58
+ iconColor,
59
+ titleColor,
60
+ subtitleColor,
61
+ style,
62
+ titleStyle,
63
+ subtitleStyle,
64
+ }) => {
65
+ const tokens = useAppDesignTokens();
66
+ const styles = getStyles(tokens);
67
+
68
+ return (
69
+ <View style={[styles.container, style]}>
70
+ <AtomicIcon
71
+ name={icon}
72
+ size={iconSize}
73
+ customColor={iconColor || tokens.colors.textSecondary}
74
+ />
75
+ <AtomicText
76
+ type="headlineMedium"
77
+ color={titleColor || tokens.colors.textPrimary}
78
+ style={StyleSheet.flatten([
79
+ styles.title,
80
+ titleStyle,
81
+ ])}
82
+ >
83
+ {title}
84
+ </AtomicText>
85
+ {subtitle && (
86
+ <AtomicText
87
+ type="bodyMedium"
88
+ color={subtitleColor || tokens.colors.textSecondary}
89
+ style={StyleSheet.flatten([
90
+ styles.subtitle,
91
+ subtitleStyle,
92
+ ])}
93
+ >
94
+ {subtitle}
95
+ </AtomicText>
96
+ )}
97
+ </View>
98
+ );
99
+ };
100
+
101
+ // =============================================================================
102
+ // STYLES
103
+ // =============================================================================
104
+
105
+ const getStyles = (tokens: ReturnType<typeof useAppDesignTokens>) =>
106
+ StyleSheet.create({
107
+ container: {
108
+ flex: 1,
109
+ justifyContent: 'center',
110
+ alignItems: 'center',
111
+ paddingVertical: tokens.spacing.xl,
112
+ paddingHorizontal: tokens.spacing.lg,
113
+ },
114
+ title: {
115
+ fontSize: tokens.typography.headingMedium.fontSize,
116
+ fontWeight: tokens.typography.headingMedium.fontWeight,
117
+ marginTop: tokens.spacing.md,
118
+ textAlign: 'center',
119
+ },
120
+ subtitle: {
121
+ fontSize: tokens.typography.bodySmall.fontSize,
122
+ marginTop: tokens.spacing.xs,
123
+ textAlign: 'center',
124
+ },
125
+ });
126
+
127
+ // =============================================================================
128
+ // EXPORTS
129
+ // =============================================================================
130
+