This repository has been archived on 2026-08-11. You can view files and clone it, but cannot push or open issues or pull requests.
Gishen-Mob/components/ui/Chip.tsx
kirukib 7310a349c5 Add Gishen mobile app with complete demo shopping and care flows.
Ship Expo Router screens, seeded stores, and polished UX so auth, shop, checkout, prescriptions, loyalty, family, and account processes work end-to-end offline.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 01:21:32 +03:00

139 lines
3.3 KiB
TypeScript

import type { ReactNode } from "react";
import { Ionicons } from "@expo/vector-icons";
import { Pressable, ScrollView, View } from "react-native";
import { Text } from "@/components/ui/Text";
import { colors, radii, space } from "@/lib/theme";
export function Chip({
label,
selected,
onPress,
onRemove,
icon,
tone = "default",
size = "md",
}: {
label: string;
selected?: boolean;
onPress?: () => void;
onRemove?: () => void;
icon?: keyof typeof Ionicons.glyphMap;
tone?: "default" | "outline";
size?: "sm" | "md";
}) {
const bg = selected ? colors.brand : tone === "outline" ? "transparent" : colors.surface;
const fg = selected ? colors.ink : colors.muted;
const border = selected ? colors.brand : colors.line;
return (
<Pressable
onPress={onPress}
style={({ pressed }) => ({
flexDirection: "row",
alignItems: "center",
gap: 6,
paddingVertical: size === "sm" ? 6 : 9,
paddingHorizontal: size === "sm" ? 10 : 14,
borderRadius: radii.pill,
backgroundColor: bg,
borderWidth: 1,
borderColor: border,
opacity: pressed ? 0.8 : 1,
})}
>
{icon ? <Ionicons name={icon} size={13} color={fg} /> : null}
<Text variant={size === "sm" ? "caption" : "label"} color={fg}>
{label}
</Text>
{onRemove ? (
<Pressable onPress={onRemove} hitSlop={8}>
<Ionicons name="close" size={14} color={fg} />
</Pressable>
) : null}
</Pressable>
);
}
/** Horizontally scrolling chip strip that bleeds to the screen edge. */
export function ChipRow({
children,
gutters = 20,
}: {
children: ReactNode;
gutters?: number;
}) {
return (
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
gap: space.sm,
paddingHorizontal: gutters,
}}
style={{ marginHorizontal: -gutters }}
>
{children}
</ScrollView>
);
}
/** Read-only meta tag, e.g. "Tablet" or "Qty: 100". */
export function MetaTag({
label,
icon,
}: {
label: string;
icon?: keyof typeof Ionicons.glyphMap;
}) {
return (
<View style={{ flexDirection: "row", alignItems: "center", gap: 4 }}>
{icon ? <Ionicons name={icon} size={11} color={colors.subtle} /> : null}
<Text variant="micro" color={colors.subtle}>
{label}
</Text>
</View>
);
}
export function Segmented<T extends string>({
options,
value,
onChange,
}: {
options: { value: T; label: string }[];
value: T;
onChange: (value: T) => void;
}) {
return (
<View
style={{
flexDirection: "row",
backgroundColor: colors.surfaceAlt,
borderRadius: radii.pill,
padding: 4,
}}
>
{options.map((option) => {
const active = option.value === value;
return (
<Pressable
key={option.value}
onPress={() => onChange(option.value)}
style={{
flex: 1,
alignItems: "center",
paddingVertical: 10,
borderRadius: radii.pill,
backgroundColor: active ? colors.surface : "transparent",
}}
>
<Text variant="h3" color={active ? colors.ink : colors.subtle}>
{option.label}
</Text>
</Pressable>
);
})}
</View>
);
}