1import { GlassView } from "expo-glass-effect";
2import { Pressable, View } from "react-native";
3import { withUniwind } from "uniwind";
4
5import { cn } from "../../lib/cn";
6import { NATIVE_LIQUID_GLASS_SUPPORTED } from "../../native/native-glass";
7import { AppText } from "./AppText";
8import { SymbolView, type AppSymbolName } from "./AppSymbol";
9
10const ChipGlass = withUniwind(GlassView, {
11 style: { fromClassName: "className" },
12 tintColor: { fromClassName: "tintColorClassName", styleProperty: "accentColor" },
13});
14
15export function Chip(props: {
16 readonly label: string;
17 readonly selected?: boolean;
18 readonly icon?: AppSymbolName;
19 readonly trailingIcon?: AppSymbolName;
20 readonly size?: "small" | "default";
21 readonly glass?: boolean;
22 readonly colorScheme?: "light" | "dark";
23 readonly onPress?: () => void;
24 readonly accessibilityLabel?: string;
25 readonly disabled?: boolean;
26}) {
27 const small = props.size === "small";
28 const layout = cn(
29 "flex-row items-center gap-1.5 rounded-2xl px-3",
30 small ? "py-1" : "min-h-8 py-1.5",
31 );
32 const iconColor = props.selected ? "accent-primary-foreground" : "accent-icon-muted";
33 const content = (
34 <>
35 {props.icon ? (
36 <SymbolView name={props.icon} size={15} tintColorClassName={iconColor} />
37 ) : null}
38 <AppText
39 textRole={small ? "caption" : "footnote"}
40 numberOfLines={small ? undefined : 1}
41 className={cn(
42 "shrink",
43 !small && "max-w-[180px]",
44 !small && "font-t3-medium",
45 props.selected ? "text-primary-foreground" : "text-foreground-muted",
46 )}
47 >
48 {props.label}
49 </AppText>
50 {props.trailingIcon ? (
51 <SymbolView name={props.trailingIcon} size={11} tintColorClassName={iconColor} />
52 ) : null}
53 </>
54 );
55 const body =
56 props.glass && NATIVE_LIQUID_GLASS_SUPPORTED ? (
57 <ChipGlass
58 isInteractive
59 colorScheme={props.colorScheme}
60 tintColorClassName={props.selected ? "accent-primary" : undefined}
61 className={layout}
62 >
63 {content}
64 </ChipGlass>
65 ) : (
66 <View
67 className={cn(
68 layout,
69 props.selected ? "bg-primary" : small ? "bg-grouped-card" : "bg-subtle",
70 )}
71 >
72 {content}
73 </View>
74 );
75 return props.onPress ? (
76 <Pressable
77 accessibilityRole="button"
78 accessibilityLabel={props.accessibilityLabel ?? props.label}
79 accessibilityState={{ selected: props.selected, disabled: Boolean(props.disabled) }}
80 disabled={props.disabled}
81 onPress={props.onPress}
82 hitSlop={6}
83 className={props.disabled ? "opacity-45" : "active:opacity-70"}
84 >
85 {body}
86 </Pressable>
87 ) : (
88 body
89 );
90}