eletrotupi / tcc/ commit / 4101720

frontend: introduce category chips

It'll be used for antoerh patch down the line with the triggers
Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago 4101720fb8e44a37a975d664d9e92b2e444b98ec Browse files
Parents 76614c0
Changed files
1
Additions
+96
Deletions
0
/dev/null → frontend/components/ui/CategoryChips.tsx
1 +
import {
2 +
  TouchableOpacity,
3 +
  Text,
4 +
  View,
5 +
  StyleSheet,
6 +
} from 'react-native';
7 +
import { Colors } from '@/constants/theme';
8 +

9 +
export type CategoryOption<T extends string = string> = {
10 +
  label: string;
11 +
  value: T;
12 +
  icon: React.ReactNode;
13 +
};
14 +

15 +
type Props<T extends string = string> = {
16 +
  options: CategoryOption<T>[];
17 +
  active: T | null;
18 +
  onChange: (value: T) => void;
19 +
  columns?: number;
20 +
};
21 +

22 +
export function CategoryChips<T extends string = string>({
23 +
  options,
24 +
  active,
25 +
  onChange,
26 +
  columns = 2,
27 +
}: Props<T>) {
28 +
  return (
29 +
    <View style={styles.grid}>
30 +
      {options.map((option) => {
31 +
        const isActive = option.value === active;
32 +
        return (
33 +
          <TouchableOpacity
34 +
            key={option.value}
35 +
            onPress={() => onChange(option.value)}
36 +
            activeOpacity={0.7}
37 +
            style={[
38 +
              styles.chip,
39 +
              { width: `${100 / columns - 2}%` as any },
40 +
              isActive && styles.chipActive,
41 +
            ]}
42 +
          >
43 +
            <View style={styles.chipInner}>
44 +
              <View style={styles.iconWrapper}>{option.icon}</View>
45 +
              <Text style={[styles.label, isActive && styles.labelActive]}>
46 +
                {option.label}
47 +
              </Text>
48 +
            </View>
49 +
          </TouchableOpacity>
50 +
        );
51 +
      })}
52 +
    </View>
53 +
  );
54 +
}
55 +

56 +
const styles = StyleSheet.create({
57 +
  grid: {
58 +
    flexDirection: 'row',
59 +
    flexWrap: 'wrap',
60 +
    gap: 10,
61 +
    paddingHorizontal: 16,
62 +
    paddingVertical: 8,
63 +
  },
64 +
  chip: {
65 +
    paddingHorizontal: 16,
66 +
    paddingVertical: 12,
67 +
    borderRadius: 999,
68 +
    backgroundColor: '#fff',
69 +
    borderWidth: 1.5,
70 +
    borderColor: 'transparent',
71 +
  },
72 +
  chipActive: {
73 +
    backgroundColor: Colors.light.tint + '1A', // ~10% opacity tint background
74 +
    borderColor: Colors.light.tint,
75 +
  },
76 +
  chipInner: {
77 +
    flexDirection: 'row',
78 +
    alignItems: 'center',
79 +
    gap: 8,
80 +
  },
81 +
  iconWrapper: {
82 +
    width: 20,
83 +
    height: 20,
84 +
    alignItems: 'center',
85 +
    justifyContent: 'center',
86 +
  },
87 +
  label: {
88 +
    fontSize: 14,
89 +
    fontWeight: '500',
90 +
    color: '#3C3C43',
91 +
    letterSpacing: -0.1,
92 +
  },
93 +
  labelActive: {
94 +
    fontWeight: '600',
95 +
  },
96 +
});