eletrotupi / tcc/ commit / 6f7c341

frontend/components: add emoji selectors

Pedro Lucas Porcellis porcellis@eletrotupi.com · 5 months ago 6f7c341bccb04f7d79cf57fb8c20a5dbe7756f3f Browse files
Parents ddfbd4e
Changed 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 +
};