frontend/components: add emoji selectors
Pedro Lucas Porcellis porcellis@eletrotupi.com · 5 months ago
6f7c341bccb04f7d79cf57fb8c20a5dbe7756f3f Browse filesParents
ddfbd4eChanged files
1
Additions
+124
Deletions
0
/dev/null → frontend/components/ui/EmojiSelectors.tsx
1 +
import React from 'react';
2 +
import {
3 +
View,
4 +
Pressable,
5 +
StyleSheet,
6 +
Image,
7 +
Text,
8 +
ImageSourcePropType,
9 +
ViewStyle,
10 +
} from 'react-native';
11 +
import { Spacing, Typography, BorderRadius } from '@/constants/theme';
12 +
import { useThemeColor } from '@/hooks/use-theme-color';
13 +
14 +
type IconSource = string | ImageSourcePropType; // either string or a svg
15 +
16 +
// Specialized variant with labels below
17 +
interface MoodSelectorProps {
18 +
items: Array<{
19 +
id: string;
20 +
icon: IconSource;
21 +
label: string;
22 +
}>;
23 +
value?: string;
24 +
onSelect: (id: string) => void;
25 +
disabled?: boolean;
26 +
style?: ViewStyle;
27 +
}
28 +
29 +
export const MoodSelector: React.FC<MoodSelectorProps> = ({
30 +
items,
31 +
value,
32 +
onSelect,
33 +
disabled = false,
34 +
style,
35 +
}) => {
36 +
const accentBlueColor = useThemeColor({}, 'accentBlue');
37 +
const tintColor = useThemeColor({}, 'tint');
38 +
const surfaceColor = useThemeColor({}, 'surface');
39 +
const dividerColor = useThemeColor({}, 'divider');
40 +
const textSecondaryColor = useThemeColor({}, 'textSecondary');
41 +
42 +
const styles = StyleSheet.create({
43 +
container: {
44 +
flexDirection: 'row',
45 +
gap: Spacing.inlineGapSm,
46 +
justifyContent: 'space-around',
47 +
alignItems: 'flex-start',
48 +
flexWrap: 'wrap',
49 +
},
50 +
itemWrapper: {
51 +
alignItems: 'center',
52 +
gap: 8,
53 +
flex: 1,
54 +
minWidth: '8%',
55 +
},
56 +
itemButton: {
57 +
width: 50,
58 +
height: 50,
59 +
borderRadius: BorderRadius.lg,
60 +
justifyContent: 'center',
61 +
alignItems: 'center',
62 +
backgroundColor: surfaceColor,
63 +
borderWidth: 1,
64 +
borderColor: dividerColor,
65 +
},
66 +
itemButtonActive: {
67 +
backgroundColor: accentBlueColor,
68 +
borderColor: 'transparent',
69 +
},
70 +
icon: {
71 +
fontSize: 48,
72 +
lineHeight: 48,
73 +
},
74 +
emojiText: {
75 +
fontSize: 36,
76 +
lineHeight: 40,
77 +
},
78 +
label: {
79 +
fontSize: Typography.labelSm.fontSize,
80 +
fontWeight: '500',
81 +
color: textSecondaryColor,
82 +
textAlign: 'center',
83 +
},
84 +
});
85 +
86 +
const isEmoji = (icon: IconSource): icon is string => typeof icon === 'string';
87 +
88 +
return (
89 +
<View style={[styles.container, style]}>
90 +
{items.map((item) => {
91 +
const isSelected = value === item.id;
92 +
93 +
return (
94 +
<View key={item.id} style={styles.itemWrapper}>
95 +
<Pressable
96 +
onPress={() => !disabled && onSelect(item.id)}
97 +
disabled={disabled}
98 +
style={({ pressed }) => [
99 +
styles.itemButton,
100 +
isSelected && styles.itemButtonActive,
101 +
pressed && !disabled && { opacity: 0.7 },
102 +
disabled && { opacity: 0.5 },
103 +
]}
104 +
>
105 +
{isEmoji(item.icon) ? (
106 +
<Text style={styles.emojiText}>{item.icon}</Text>
107 +
) : (
108 +
<Image
109 +
source={item.icon}
110 +
style={{
111 +
width: 48,
112 +
height: 48,
113 +
}}
114 +
resizeMode="contain"
115 +
/>
116 +
)}
117 +
</Pressable>
118 +
<Text style={styles.label}>{item.label}</Text>
119 +
</View>
120 +
);
121 +
})}
122 +
</View>
123 +
);
124 +
};