@umituz/react-native-design-system 2.6.101 → 2.6.103
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/exports/molecules.ts +9 -0
- package/src/molecules/action-footer/ActionFooter.tsx +91 -0
- package/src/molecules/action-footer/index.ts +3 -0
- package/src/molecules/action-footer/types.ts +13 -0
- package/src/molecules/filter-group/FilterGroup.tsx +4 -1
- package/src/molecules/filter-group/types.ts +2 -1
- package/src/molecules/hero-section/HeroSection.tsx +91 -0
- package/src/molecules/hero-section/index.ts +3 -0
- package/src/molecules/hero-section/types.ts +11 -0
- package/src/molecules/index.ts +3 -0
- package/src/molecules/info-card/InfoCard.tsx +44 -0
- package/src/molecules/info-card/index.ts +3 -0
- package/src/molecules/info-card/types.ts +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-design-system",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.103",
|
|
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",
|
package/src/exports/molecules.ts
CHANGED
|
@@ -172,5 +172,14 @@ export {
|
|
|
172
172
|
FilterGroup,
|
|
173
173
|
type FilterGroupProps,
|
|
174
174
|
type FilterGroupItem,
|
|
175
|
+
// Action Footer
|
|
176
|
+
ActionFooter,
|
|
177
|
+
type ActionFooterProps,
|
|
178
|
+
// Info Card
|
|
179
|
+
InfoCard,
|
|
180
|
+
type InfoCardProps,
|
|
181
|
+
// Hero Section
|
|
182
|
+
HeroSection,
|
|
183
|
+
type HeroSectionProps,
|
|
175
184
|
} from '../molecules';
|
|
176
185
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { View, StyleSheet, TouchableOpacity } from 'react-native';
|
|
4
|
+
import { AtomicText } from '../../atoms/AtomicText';
|
|
5
|
+
import { AtomicIcon } from '../../atoms/AtomicIcon';
|
|
6
|
+
import { useAppDesignTokens } from '../../theme';
|
|
7
|
+
import type { ActionFooterProps } from './types';
|
|
8
|
+
|
|
9
|
+
export const ActionFooter: React.FC<ActionFooterProps> = ({
|
|
10
|
+
onBack,
|
|
11
|
+
onAction,
|
|
12
|
+
actionLabel,
|
|
13
|
+
actionIcon = 'arrow-forward',
|
|
14
|
+
backIcon = 'chevron-back',
|
|
15
|
+
style,
|
|
16
|
+
loading = false,
|
|
17
|
+
}) => {
|
|
18
|
+
const tokens = useAppDesignTokens();
|
|
19
|
+
|
|
20
|
+
const styles = StyleSheet.create({
|
|
21
|
+
container: {
|
|
22
|
+
flexDirection: 'row',
|
|
23
|
+
alignItems: 'center',
|
|
24
|
+
paddingVertical: tokens.spacing.md,
|
|
25
|
+
gap: tokens.spacing.md,
|
|
26
|
+
},
|
|
27
|
+
backButton: {
|
|
28
|
+
width: 56,
|
|
29
|
+
height: 56,
|
|
30
|
+
borderRadius: tokens.borders.radius.lg,
|
|
31
|
+
backgroundColor: tokens.colors.surface,
|
|
32
|
+
justifyContent: 'center',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
borderWidth: 1,
|
|
35
|
+
borderColor: tokens.colors.outlineVariant,
|
|
36
|
+
},
|
|
37
|
+
actionButton: {
|
|
38
|
+
flex: 1,
|
|
39
|
+
height: 56,
|
|
40
|
+
borderRadius: tokens.borders.radius.lg,
|
|
41
|
+
overflow: 'hidden',
|
|
42
|
+
},
|
|
43
|
+
actionContent: {
|
|
44
|
+
flex: 1,
|
|
45
|
+
flexDirection: 'row',
|
|
46
|
+
alignItems: 'center',
|
|
47
|
+
justifyContent: 'center',
|
|
48
|
+
backgroundColor: tokens.colors.primary,
|
|
49
|
+
gap: tokens.spacing.sm,
|
|
50
|
+
},
|
|
51
|
+
actionText: {
|
|
52
|
+
color: tokens.colors.onPrimary,
|
|
53
|
+
fontWeight: '800',
|
|
54
|
+
fontSize: 18,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<View style={[styles.container, style]}>
|
|
60
|
+
<TouchableOpacity
|
|
61
|
+
style={styles.backButton}
|
|
62
|
+
onPress={onBack}
|
|
63
|
+
activeOpacity={0.7}
|
|
64
|
+
testID="action-footer-back"
|
|
65
|
+
>
|
|
66
|
+
<AtomicIcon
|
|
67
|
+
name={backIcon}
|
|
68
|
+
size="md"
|
|
69
|
+
color="textPrimary"
|
|
70
|
+
/>
|
|
71
|
+
</TouchableOpacity>
|
|
72
|
+
|
|
73
|
+
<TouchableOpacity
|
|
74
|
+
style={styles.actionButton}
|
|
75
|
+
onPress={onAction}
|
|
76
|
+
activeOpacity={0.9}
|
|
77
|
+
disabled={loading}
|
|
78
|
+
testID="action-footer-action"
|
|
79
|
+
>
|
|
80
|
+
<View style={styles.actionContent}>
|
|
81
|
+
<AtomicText style={styles.actionText}>{actionLabel}</AtomicText>
|
|
82
|
+
<AtomicIcon
|
|
83
|
+
name={actionIcon}
|
|
84
|
+
size="sm"
|
|
85
|
+
color="onPrimary"
|
|
86
|
+
/>
|
|
87
|
+
</View>
|
|
88
|
+
</TouchableOpacity>
|
|
89
|
+
</View>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ViewStyle, StyleProp } from 'react-native';
|
|
4
|
+
|
|
5
|
+
export interface ActionFooterProps {
|
|
6
|
+
onBack: () => void;
|
|
7
|
+
onAction: () => void;
|
|
8
|
+
actionLabel: string;
|
|
9
|
+
actionIcon?: string;
|
|
10
|
+
backIcon?: string;
|
|
11
|
+
style?: StyleProp<ViewStyle>;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
}
|
|
@@ -9,6 +9,7 @@ export function FilterGroup<T = string>({
|
|
|
9
9
|
items,
|
|
10
10
|
selectedValue,
|
|
11
11
|
onSelect,
|
|
12
|
+
multiSelect = false,
|
|
12
13
|
style,
|
|
13
14
|
contentContainerStyle,
|
|
14
15
|
itemStyle,
|
|
@@ -37,7 +38,9 @@ export function FilterGroup<T = string>({
|
|
|
37
38
|
contentContainerStyle={[styles.content, contentContainerStyle]}
|
|
38
39
|
>
|
|
39
40
|
{items.map((item) => {
|
|
40
|
-
const isSelected =
|
|
41
|
+
const isSelected = multiSelect
|
|
42
|
+
? Array.isArray(selectedValue) && selectedValue.includes(item.value)
|
|
43
|
+
: item.value === selectedValue;
|
|
41
44
|
return (
|
|
42
45
|
<AtomicChip
|
|
43
46
|
key={`${item.value}`}
|
|
@@ -7,8 +7,9 @@ export interface FilterGroupItem<T = string> {
|
|
|
7
7
|
|
|
8
8
|
export interface FilterGroupProps<T = string> {
|
|
9
9
|
items: FilterGroupItem<T>[];
|
|
10
|
-
selectedValue: T;
|
|
10
|
+
selectedValue: T | T[];
|
|
11
11
|
onSelect: (value: T) => void;
|
|
12
|
+
multiSelect?: boolean;
|
|
12
13
|
style?: any;
|
|
13
14
|
contentContainerStyle?: any;
|
|
14
15
|
itemStyle?: any;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { View, StyleSheet, Text, Image } from 'react-native';
|
|
4
|
+
import { useAppDesignTokens } from '../../theme';
|
|
5
|
+
import type { HeroSectionProps } from './types';
|
|
6
|
+
|
|
7
|
+
export const HeroSection: React.FC<HeroSectionProps> = ({
|
|
8
|
+
icon,
|
|
9
|
+
imageUrl,
|
|
10
|
+
imageSource,
|
|
11
|
+
height,
|
|
12
|
+
style,
|
|
13
|
+
}) => {
|
|
14
|
+
const tokens = useAppDesignTokens();
|
|
15
|
+
// Default height 50% of screen if not provided? No, components shouldn't guess screen height directly if possible, or use prop.
|
|
16
|
+
// We'll leave height control to the parent or style, but provide a sensible default if passed as prop.
|
|
17
|
+
|
|
18
|
+
const styles = StyleSheet.create({
|
|
19
|
+
container: {
|
|
20
|
+
width: '100%',
|
|
21
|
+
height: height || 400,
|
|
22
|
+
position: 'relative',
|
|
23
|
+
backgroundColor: tokens.colors.surface,
|
|
24
|
+
justifyContent: 'center',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
overflow: 'hidden',
|
|
27
|
+
},
|
|
28
|
+
background: {
|
|
29
|
+
...StyleSheet.absoluteFillObject,
|
|
30
|
+
backgroundColor: tokens.colors.surfaceVariant,
|
|
31
|
+
},
|
|
32
|
+
image: {
|
|
33
|
+
...StyleSheet.absoluteFillObject,
|
|
34
|
+
width: '100%',
|
|
35
|
+
height: '100%',
|
|
36
|
+
},
|
|
37
|
+
iconWrapper: {
|
|
38
|
+
width: 120,
|
|
39
|
+
height: 120,
|
|
40
|
+
borderRadius: 60,
|
|
41
|
+
backgroundColor: tokens.colors.surfaceVariant,
|
|
42
|
+
justifyContent: 'center',
|
|
43
|
+
alignItems: 'center',
|
|
44
|
+
borderWidth: 2,
|
|
45
|
+
borderColor: tokens.colors.outlineVariant,
|
|
46
|
+
zIndex: 10,
|
|
47
|
+
},
|
|
48
|
+
emoji: {
|
|
49
|
+
fontSize: 64,
|
|
50
|
+
textAlign: 'center',
|
|
51
|
+
includeFontPadding: false,
|
|
52
|
+
},
|
|
53
|
+
fadeOverlay: {
|
|
54
|
+
position: 'absolute',
|
|
55
|
+
bottom: -1,
|
|
56
|
+
left: 0,
|
|
57
|
+
right: 0,
|
|
58
|
+
height: 100,
|
|
59
|
+
// We can't use LinearGradient here per rules.
|
|
60
|
+
// Use a solid color with opacity or a series of views if really needed, but user said NO GRADIENT.
|
|
61
|
+
// The original code used backgroundColor: "rgba(15, 5, 10, 0.7)".
|
|
62
|
+
// We'll use a semi-transparent overlay at the bottom.
|
|
63
|
+
backgroundColor: 'rgba(0,0,0,0.5)', // Generic dark overlay
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const source = imageUrl ? { uri: imageUrl } : imageSource;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<View style={[styles.container, style]}>
|
|
71
|
+
{source ? (
|
|
72
|
+
<Image
|
|
73
|
+
source={source}
|
|
74
|
+
style={styles.image}
|
|
75
|
+
resizeMode="cover"
|
|
76
|
+
/>
|
|
77
|
+
) : (
|
|
78
|
+
<View style={styles.background} />
|
|
79
|
+
)}
|
|
80
|
+
|
|
81
|
+
{/* Show icon only if no image */}
|
|
82
|
+
{!source && icon && (
|
|
83
|
+
<View style={styles.iconWrapper}>
|
|
84
|
+
<Text style={styles.emoji}>{icon}</Text>
|
|
85
|
+
</View>
|
|
86
|
+
)}
|
|
87
|
+
|
|
88
|
+
<View style={styles.fadeOverlay} />
|
|
89
|
+
</View>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ViewStyle, StyleProp, ImageSourcePropType } from 'react-native';
|
|
4
|
+
|
|
5
|
+
export interface HeroSectionProps {
|
|
6
|
+
icon?: string;
|
|
7
|
+
imageUrl?: string;
|
|
8
|
+
imageSource?: ImageSourcePropType;
|
|
9
|
+
height?: number;
|
|
10
|
+
style?: StyleProp<ViewStyle>;
|
|
11
|
+
}
|
package/src/molecules/index.ts
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { View, StyleSheet } from 'react-native';
|
|
4
|
+
import { AtomicText } from '../../atoms/AtomicText';
|
|
5
|
+
import { useAppDesignTokens } from '../../theme';
|
|
6
|
+
import type { InfoCardProps } from './types';
|
|
7
|
+
|
|
8
|
+
export const InfoCard: React.FC<InfoCardProps> = ({
|
|
9
|
+
title,
|
|
10
|
+
content,
|
|
11
|
+
style,
|
|
12
|
+
}) => {
|
|
13
|
+
const tokens = useAppDesignTokens();
|
|
14
|
+
|
|
15
|
+
const styles = StyleSheet.create({
|
|
16
|
+
container: {
|
|
17
|
+
backgroundColor: tokens.colors.surface,
|
|
18
|
+
borderRadius: tokens.borders.radius.xl,
|
|
19
|
+
padding: tokens.spacing.lg,
|
|
20
|
+
borderWidth: 1,
|
|
21
|
+
borderColor: tokens.colors.outlineVariant,
|
|
22
|
+
},
|
|
23
|
+
title: {
|
|
24
|
+
...tokens.typography.labelLarge,
|
|
25
|
+
color: tokens.colors.primary,
|
|
26
|
+
fontWeight: '800', // Using string as expected by RN
|
|
27
|
+
textTransform: 'uppercase',
|
|
28
|
+
letterSpacing: 1,
|
|
29
|
+
marginBottom: tokens.spacing.xs,
|
|
30
|
+
},
|
|
31
|
+
content: {
|
|
32
|
+
...tokens.typography.bodyMedium,
|
|
33
|
+
color: tokens.colors.textPrimary,
|
|
34
|
+
lineHeight: 22,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<View style={[styles.container, style]}>
|
|
40
|
+
<AtomicText style={styles.title}>{title}</AtomicText>
|
|
41
|
+
<AtomicText style={styles.content}>{content}</AtomicText>
|
|
42
|
+
</View>
|
|
43
|
+
);
|
|
44
|
+
};
|