import React from 'react'; import Box from '@mui/material/Box'; import type { BoxProps } from '@mui/material/Box'; import type { Theme } from '@mui/material/styles'; // ─── Types ─────────────────────────────────────────────────────────────────── /** Colour intent for the badge */ export type BadgeColor = 'default' | 'brand' | 'success' | 'warning' | 'error' | 'info'; /** Props for the FA Badge component */ export interface BadgeProps extends Omit { /** Colour intent */ color?: BadgeColor; /** Visual style: "filled" (solid background) or "soft" (tonal/subtle background) */ variant?: 'filled' | 'soft'; /** Size preset */ size?: 'small' | 'medium' | 'large'; /** Optional leading icon */ icon?: React.ReactNode; /** Label text */ children: React.ReactNode; } // ─── Colour maps ───────────────────────────────────────────────────────────── const filledColors: Record = { default: { bg: 'var(--fa-color-neutral-700)', text: 'var(--fa-color-white)' }, brand: { bg: 'var(--fa-color-interactive-default)', text: 'var(--fa-color-white)' }, success: { bg: 'var(--fa-color-feedback-success)', text: 'var(--fa-color-white)' }, warning: { bg: 'var(--fa-color-feedback-warning)', text: 'var(--fa-color-white)' }, error: { bg: 'var(--fa-color-feedback-error)', text: 'var(--fa-color-white)' }, info: { bg: 'var(--fa-color-feedback-info)', text: 'var(--fa-color-white)' }, }; const softColors: Record = { default: { bg: 'var(--fa-color-neutral-200)', text: 'var(--fa-color-neutral-700)' }, brand: { bg: 'var(--fa-color-brand-200)', text: 'var(--fa-color-brand-700)' }, success: { bg: 'var(--fa-color-feedback-success-subtle)', text: 'var(--fa-color-feedback-success)', }, warning: { bg: 'var(--fa-color-feedback-warning-subtle)', text: 'var(--fa-color-text-warning)' }, error: { bg: 'var(--fa-color-feedback-error-subtle)', text: 'var(--fa-color-feedback-error)' }, info: { bg: 'var(--fa-color-feedback-info-subtle)', text: 'var(--fa-color-feedback-info)' }, }; // ─── Component ─────────────────────────────────────────────────────────────── /** * Status indicator label for the FA design system. * * Pill-shaped, display-only badge for communicating status, category, * or emphasis. Used in PriceCard ("Popular"), ServiceOption ("Included"), * and other contexts. * * Colour options: * - `default` — neutral grey (general labels) * - `brand` — warm gold/copper (promoted, featured) * - `success` — green (confirmed, included, available) * - `warning` — amber (limited, expiring, attention) * - `error` — red (sold out, unavailable, urgent) * - `info` — blue (new, updated, informational) * * Variant options: * - `soft` (default) — tonal background, coloured text. Calmer, preferred for FA. * - `filled` — solid background, white text. For high-priority emphasis. * * **Accessibility**: If a Badge contains only an icon (no text children), * provide an `aria-label` prop so screen readers can announce the status. */ export const Badge = React.forwardRef( ({ color = 'default', variant = 'soft', size = 'medium', icon, children, sx, ...props }, ref) => { const sizeMap = { small: { height: 'var(--fa-badge-height-sm)', px: 'var(--fa-badge-padding-x-sm)', fontSize: 'var(--fa-badge-font-size-sm)', iconSize: 'var(--fa-badge-icon-size-sm)', }, medium: { height: 'var(--fa-badge-height-md)', px: 'var(--fa-badge-padding-x-md)', fontSize: 'var(--fa-badge-font-size-md)', iconSize: 'var(--fa-badge-icon-size-md)', }, large: { height: 'var(--fa-badge-height-lg)', px: 'var(--fa-badge-padding-x-lg)', fontSize: 'var(--fa-badge-font-size-lg)', iconSize: 'var(--fa-badge-icon-size-lg)', }, } as const; const s = sizeMap[size]; return ( { const colors = variant === 'filled' ? filledColors[color] : softColors[color]; return { display: 'inline-flex', alignItems: 'center', gap: 'var(--fa-badge-icon-gap-default)', minHeight: s.height, px: s.px, borderRadius: 'var(--fa-badge-border-radius-default)', backgroundColor: colors.bg, color: colors.text, fontSize: s.fontSize, fontWeight: 600, fontFamily: theme.typography.fontFamily, lineHeight: 1, letterSpacing: '0.02em', whiteSpace: 'nowrap', userSelect: 'none', // Icon sizing '& > .MuiSvgIcon-root, & > svg': { fontSize: s.iconSize, flexShrink: 0, }, }; }, ...(Array.isArray(sx) ? sx : [sx]), ]} {...props} > {icon} {children} ); }, ); Badge.displayName = 'Badge'; export default Badge;