import { Ionicons } from "@expo/vector-icons"; import { View, type StyleProp, type ViewStyle } from "react-native"; import { Text } from "@/components/ui/Text"; import { colors, radii } from "@/lib/theme"; type Tone = "brand" | "danger" | "warn" | "info" | "neutral" | "dark"; const tones: Record = { brand: { bg: colors.brandSoft, fg: colors.brandText }, danger: { bg: colors.dangerSoft, fg: colors.danger }, warn: { bg: colors.warnSoft, fg: colors.warn }, info: { bg: colors.infoSoft, fg: colors.info }, neutral: { bg: colors.surfaceAlt, fg: colors.muted }, dark: { bg: colors.ink, fg: colors.surface }, }; export function Badge({ label, tone = "brand", icon, solid, style, }: { label: string; tone?: Tone; icon?: keyof typeof Ionicons.glyphMap; /** Filled lime treatment used for discount flags on imagery. */ solid?: boolean; style?: StyleProp; }) { const skin = tones[tone]; const bg = solid ? colors.brand : skin.bg; const fg = solid ? colors.ink : skin.fg; return ( {icon ? : null} {label} ); } export function StockLabel({ inStock, count, }: { inStock: boolean; count?: number; }) { return ( {inStock ? `In stock${count ? ` (${count})` : ""}` : "Out of stock"} ); } export function Rating({ value, count, showStars = true, size = 13, }: { value: number; count?: number; showStars?: boolean; size?: number; }) { return ( {showStars ? ( {[1, 2, 3, 4, 5].map((i) => ( = i ? "star" : value >= i - 0.5 ? "star-half" : "star-outline" } size={size} color={value >= i - 0.5 ? colors.star : colors.disabled} /> ))} ) : ( )} {value.toFixed(1)} {count !== undefined ? ` (${count})` : ""} ); } /** Compact price with optional struck-through original. */ export function Price({ value, compareAt, size = "md", format, }: { value: number; compareAt?: number; size?: "sm" | "md" | "lg"; format: (n: number) => string; }) { return ( {format(value)} {compareAt && compareAt > value ? ( {format(compareAt)} ) : null} ); }