@umituz/react-native-design-system 2.5.14 → 2.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/atoms/AtomicSwitch.tsx +83 -0
- package/src/atoms/AtomicTextArea.tsx +105 -0
- package/src/atoms/AtomicTouchable.tsx +42 -0
- package/src/atoms/EmptyState.tsx +121 -0
- package/src/atoms/index.ts +13 -0
- package/src/index.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.16",
|
|
4
4
|
"description": "Universal design system for React Native apps - Consolidated package with atoms, molecules, organisms, theme, typography, responsive and safe area utilities",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AtomicSwitch - Toggle Switch Component
|
|
3
|
+
*
|
|
4
|
+
* Atomic Design Level: ATOM
|
|
5
|
+
* Purpose: Boolean toggle across all apps
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { View, Switch, StyleSheet, ViewStyle } from 'react-native';
|
|
10
|
+
import { useAppDesignTokens } from '../theme';
|
|
11
|
+
import { AtomicText } from './AtomicText';
|
|
12
|
+
|
|
13
|
+
export interface AtomicSwitchProps {
|
|
14
|
+
label?: string;
|
|
15
|
+
value: boolean;
|
|
16
|
+
onValueChange: (value: boolean) => void;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
description?: string;
|
|
19
|
+
style?: ViewStyle;
|
|
20
|
+
testID?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const AtomicSwitch: React.FC<AtomicSwitchProps> = ({
|
|
24
|
+
label,
|
|
25
|
+
value,
|
|
26
|
+
onValueChange,
|
|
27
|
+
disabled = false,
|
|
28
|
+
description,
|
|
29
|
+
style,
|
|
30
|
+
testID,
|
|
31
|
+
}) => {
|
|
32
|
+
const tokens = useAppDesignTokens();
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<View style={[styles.container, style]} testID={testID}>
|
|
36
|
+
<View style={styles.row}>
|
|
37
|
+
{label && (
|
|
38
|
+
<View style={styles.labelContainer}>
|
|
39
|
+
<AtomicText
|
|
40
|
+
type="bodyMedium"
|
|
41
|
+
style={{ color: tokens.colors.textPrimary }}
|
|
42
|
+
>
|
|
43
|
+
{label}
|
|
44
|
+
</AtomicText>
|
|
45
|
+
{description && (
|
|
46
|
+
<AtomicText
|
|
47
|
+
type="bodySmall"
|
|
48
|
+
style={{ color: tokens.colors.textSecondary, marginTop: 2 }}
|
|
49
|
+
>
|
|
50
|
+
{description}
|
|
51
|
+
</AtomicText>
|
|
52
|
+
)}
|
|
53
|
+
</View>
|
|
54
|
+
)}
|
|
55
|
+
<Switch
|
|
56
|
+
value={value}
|
|
57
|
+
onValueChange={onValueChange}
|
|
58
|
+
disabled={disabled}
|
|
59
|
+
trackColor={{
|
|
60
|
+
false: tokens.colors.border,
|
|
61
|
+
true: tokens.colors.primary,
|
|
62
|
+
}}
|
|
63
|
+
thumbColor={value ? tokens.colors.onPrimary : tokens.colors.surface}
|
|
64
|
+
/>
|
|
65
|
+
</View>
|
|
66
|
+
</View>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const styles = StyleSheet.create({
|
|
71
|
+
container: {
|
|
72
|
+
marginBottom: 16,
|
|
73
|
+
},
|
|
74
|
+
row: {
|
|
75
|
+
flexDirection: 'row',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
justifyContent: 'space-between',
|
|
78
|
+
},
|
|
79
|
+
labelContainer: {
|
|
80
|
+
flex: 1,
|
|
81
|
+
marginRight: 16,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AtomicTextArea - Multiline Text Input Component
|
|
3
|
+
*
|
|
4
|
+
* Atomic Design Level: ATOM
|
|
5
|
+
* Purpose: Multiline text input across all apps
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { View, TextInput, StyleSheet, ViewStyle } from 'react-native';
|
|
10
|
+
import { useAppDesignTokens } from '../theme';
|
|
11
|
+
import { AtomicText } from './AtomicText';
|
|
12
|
+
|
|
13
|
+
export interface AtomicTextAreaProps {
|
|
14
|
+
label?: string;
|
|
15
|
+
value?: string;
|
|
16
|
+
onChangeText?: (text: string) => void;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
helperText?: string;
|
|
19
|
+
errorText?: string;
|
|
20
|
+
maxLength?: number;
|
|
21
|
+
numberOfLines?: number;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
style?: ViewStyle;
|
|
24
|
+
testID?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const AtomicTextArea: React.FC<AtomicTextAreaProps> = ({
|
|
28
|
+
label,
|
|
29
|
+
value,
|
|
30
|
+
onChangeText,
|
|
31
|
+
placeholder,
|
|
32
|
+
helperText,
|
|
33
|
+
errorText,
|
|
34
|
+
maxLength,
|
|
35
|
+
numberOfLines = 4,
|
|
36
|
+
disabled = false,
|
|
37
|
+
style,
|
|
38
|
+
testID,
|
|
39
|
+
}) => {
|
|
40
|
+
const tokens = useAppDesignTokens();
|
|
41
|
+
const hasError = !!errorText;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<View style={[styles.container, style]} testID={testID}>
|
|
45
|
+
{label && (
|
|
46
|
+
<AtomicText
|
|
47
|
+
type="labelMedium"
|
|
48
|
+
style={[styles.label, { color: tokens.colors.textSecondary }]}
|
|
49
|
+
>
|
|
50
|
+
{label}
|
|
51
|
+
</AtomicText>
|
|
52
|
+
)}
|
|
53
|
+
<TextInput
|
|
54
|
+
value={value}
|
|
55
|
+
onChangeText={onChangeText}
|
|
56
|
+
placeholder={placeholder}
|
|
57
|
+
placeholderTextColor={tokens.colors.textTertiary}
|
|
58
|
+
maxLength={maxLength}
|
|
59
|
+
numberOfLines={numberOfLines}
|
|
60
|
+
multiline
|
|
61
|
+
editable={!disabled}
|
|
62
|
+
textAlignVertical="top"
|
|
63
|
+
style={[
|
|
64
|
+
styles.input,
|
|
65
|
+
{
|
|
66
|
+
backgroundColor: tokens.colors.surface,
|
|
67
|
+
borderColor: hasError ? tokens.colors.error : tokens.colors.border,
|
|
68
|
+
color: tokens.colors.textPrimary,
|
|
69
|
+
minHeight: numberOfLines * 24,
|
|
70
|
+
},
|
|
71
|
+
disabled && { opacity: 0.5 },
|
|
72
|
+
]}
|
|
73
|
+
/>
|
|
74
|
+
{(helperText || errorText) && (
|
|
75
|
+
<AtomicText
|
|
76
|
+
type="bodySmall"
|
|
77
|
+
style={[
|
|
78
|
+
styles.helperText,
|
|
79
|
+
{ color: hasError ? tokens.colors.error : tokens.colors.textSecondary },
|
|
80
|
+
]}
|
|
81
|
+
>
|
|
82
|
+
{errorText || helperText}
|
|
83
|
+
</AtomicText>
|
|
84
|
+
)}
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const styles = StyleSheet.create({
|
|
90
|
+
container: {
|
|
91
|
+
marginBottom: 16,
|
|
92
|
+
},
|
|
93
|
+
label: {
|
|
94
|
+
marginBottom: 8,
|
|
95
|
+
},
|
|
96
|
+
input: {
|
|
97
|
+
borderWidth: 1,
|
|
98
|
+
borderRadius: 12,
|
|
99
|
+
padding: 12,
|
|
100
|
+
fontSize: 16,
|
|
101
|
+
},
|
|
102
|
+
helperText: {
|
|
103
|
+
marginTop: 4,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AtomicTouchable - Touchable Component
|
|
3
|
+
*
|
|
4
|
+
* Atomic Design Level: ATOM
|
|
5
|
+
* Purpose: Touchable wrapper across all apps
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { TouchableOpacity, ViewStyle, StyleProp } from 'react-native';
|
|
10
|
+
|
|
11
|
+
export interface AtomicTouchableProps {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
onPress?: () => void;
|
|
14
|
+
onLongPress?: () => void;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
activeOpacity?: number;
|
|
17
|
+
style?: StyleProp<ViewStyle>;
|
|
18
|
+
testID?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const AtomicTouchable: React.FC<AtomicTouchableProps> = ({
|
|
22
|
+
children,
|
|
23
|
+
onPress,
|
|
24
|
+
onLongPress,
|
|
25
|
+
disabled = false,
|
|
26
|
+
activeOpacity = 0.7,
|
|
27
|
+
style,
|
|
28
|
+
testID,
|
|
29
|
+
}) => {
|
|
30
|
+
return (
|
|
31
|
+
<TouchableOpacity
|
|
32
|
+
onPress={onPress}
|
|
33
|
+
onLongPress={onLongPress}
|
|
34
|
+
disabled={disabled}
|
|
35
|
+
activeOpacity={activeOpacity}
|
|
36
|
+
style={style}
|
|
37
|
+
testID={testID}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</TouchableOpacity>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmptyState - Universal Empty State Component
|
|
3
|
+
*
|
|
4
|
+
* Displays when no data is available
|
|
5
|
+
*
|
|
6
|
+
* Atomic Design Level: ATOM
|
|
7
|
+
* Purpose: Empty state indication across all apps
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { View, StyleSheet, TouchableOpacity, ViewStyle } from 'react-native';
|
|
12
|
+
import { AtomicIcon } from './AtomicIcon';
|
|
13
|
+
import { AtomicText } from './AtomicText';
|
|
14
|
+
import { useAppDesignTokens, STATIC_TOKENS } from '../theme';
|
|
15
|
+
|
|
16
|
+
export interface EmptyStateProps {
|
|
17
|
+
icon?: string;
|
|
18
|
+
title: string;
|
|
19
|
+
subtitle?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
actionLabel?: string;
|
|
22
|
+
onAction?: () => void;
|
|
23
|
+
illustration?: React.ReactNode;
|
|
24
|
+
style?: ViewStyle;
|
|
25
|
+
testID?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const EmptyState: React.FC<EmptyStateProps> = ({
|
|
29
|
+
icon = 'inbox',
|
|
30
|
+
title,
|
|
31
|
+
subtitle,
|
|
32
|
+
description,
|
|
33
|
+
actionLabel,
|
|
34
|
+
onAction,
|
|
35
|
+
illustration,
|
|
36
|
+
style,
|
|
37
|
+
testID,
|
|
38
|
+
}) => {
|
|
39
|
+
const tokens = useAppDesignTokens();
|
|
40
|
+
const displayDescription = description || subtitle;
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<View style={[styles.container, style]} testID={testID}>
|
|
44
|
+
{illustration ? (
|
|
45
|
+
illustration
|
|
46
|
+
) : (
|
|
47
|
+
<View
|
|
48
|
+
style={[
|
|
49
|
+
styles.iconContainer,
|
|
50
|
+
{ backgroundColor: tokens.colors.surface },
|
|
51
|
+
]}
|
|
52
|
+
>
|
|
53
|
+
<AtomicIcon name={icon} size="xxl" color="secondary" />
|
|
54
|
+
</View>
|
|
55
|
+
)}
|
|
56
|
+
|
|
57
|
+
<AtomicText
|
|
58
|
+
type="headlineSmall"
|
|
59
|
+
color="primary"
|
|
60
|
+
style={[styles.title, { textAlign: 'center' }]}
|
|
61
|
+
>
|
|
62
|
+
{title}
|
|
63
|
+
</AtomicText>
|
|
64
|
+
|
|
65
|
+
{displayDescription && (
|
|
66
|
+
<AtomicText
|
|
67
|
+
type="bodyMedium"
|
|
68
|
+
color="secondary"
|
|
69
|
+
style={[styles.description, { textAlign: 'center' }]}
|
|
70
|
+
>
|
|
71
|
+
{displayDescription}
|
|
72
|
+
</AtomicText>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
{actionLabel && onAction && (
|
|
76
|
+
<TouchableOpacity
|
|
77
|
+
style={[
|
|
78
|
+
styles.actionButton,
|
|
79
|
+
{ backgroundColor: tokens.colors.primary },
|
|
80
|
+
]}
|
|
81
|
+
onPress={onAction}
|
|
82
|
+
activeOpacity={0.8}
|
|
83
|
+
>
|
|
84
|
+
<AtomicText type="labelLarge" color="onPrimary">
|
|
85
|
+
{actionLabel}
|
|
86
|
+
</AtomicText>
|
|
87
|
+
</TouchableOpacity>
|
|
88
|
+
)}
|
|
89
|
+
</View>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const styles = StyleSheet.create({
|
|
94
|
+
container: {
|
|
95
|
+
flex: 1,
|
|
96
|
+
alignItems: 'center',
|
|
97
|
+
justifyContent: 'center',
|
|
98
|
+
padding: STATIC_TOKENS.spacing.xl,
|
|
99
|
+
},
|
|
100
|
+
iconContainer: {
|
|
101
|
+
width: 120,
|
|
102
|
+
height: 120,
|
|
103
|
+
borderRadius: 60,
|
|
104
|
+
alignItems: 'center',
|
|
105
|
+
justifyContent: 'center',
|
|
106
|
+
marginBottom: STATIC_TOKENS.spacing.lg,
|
|
107
|
+
},
|
|
108
|
+
title: {
|
|
109
|
+
marginBottom: STATIC_TOKENS.spacing.sm,
|
|
110
|
+
},
|
|
111
|
+
description: {
|
|
112
|
+
marginBottom: STATIC_TOKENS.spacing.lg,
|
|
113
|
+
maxWidth: 280,
|
|
114
|
+
},
|
|
115
|
+
actionButton: {
|
|
116
|
+
paddingHorizontal: STATIC_TOKENS.spacing.lg,
|
|
117
|
+
paddingVertical: STATIC_TOKENS.spacing.md,
|
|
118
|
+
borderRadius: STATIC_TOKENS.borders.radius.md,
|
|
119
|
+
marginTop: STATIC_TOKENS.spacing.sm,
|
|
120
|
+
},
|
|
121
|
+
});
|
package/src/atoms/index.ts
CHANGED
|
@@ -91,7 +91,20 @@ export {
|
|
|
91
91
|
// Spinner
|
|
92
92
|
export {
|
|
93
93
|
AtomicSpinner,
|
|
94
|
+
AtomicSpinner as LoadingSpinner,
|
|
94
95
|
type AtomicSpinnerProps,
|
|
95
96
|
type SpinnerSize,
|
|
96
97
|
type SpinnerColor,
|
|
97
98
|
} from './AtomicSpinner';
|
|
99
|
+
|
|
100
|
+
// Empty State
|
|
101
|
+
export { EmptyState, type EmptyStateProps } from './EmptyState';
|
|
102
|
+
|
|
103
|
+
// TextArea
|
|
104
|
+
export { AtomicTextArea, type AtomicTextAreaProps } from './AtomicTextArea';
|
|
105
|
+
|
|
106
|
+
// Switch
|
|
107
|
+
export { AtomicSwitch, type AtomicSwitchProps } from './AtomicSwitch';
|
|
108
|
+
|
|
109
|
+
// Touchable
|
|
110
|
+
export { AtomicTouchable, type AtomicTouchableProps } from './AtomicTouchable';
|
package/src/index.ts
CHANGED
|
@@ -189,6 +189,8 @@ export {
|
|
|
189
189
|
AtomicSkeleton,
|
|
190
190
|
AtomicBadge,
|
|
191
191
|
AtomicSpinner,
|
|
192
|
+
LoadingSpinner,
|
|
193
|
+
EmptyState,
|
|
192
194
|
type IconName,
|
|
193
195
|
type IconSize,
|
|
194
196
|
type IconColor,
|
|
@@ -224,6 +226,13 @@ export {
|
|
|
224
226
|
type AtomicSpinnerProps,
|
|
225
227
|
type SpinnerSize,
|
|
226
228
|
type SpinnerColor,
|
|
229
|
+
type EmptyStateProps,
|
|
230
|
+
AtomicTextArea,
|
|
231
|
+
type AtomicTextAreaProps,
|
|
232
|
+
AtomicSwitch,
|
|
233
|
+
type AtomicSwitchProps,
|
|
234
|
+
AtomicTouchable,
|
|
235
|
+
type AtomicTouchableProps,
|
|
227
236
|
} from './atoms';
|
|
228
237
|
|
|
229
238
|
// =============================================================================
|