| import type { ButtonHTMLAttributes, InputHTMLAttributes, ReactNode } from 'react' |
| import { statusTone, statusKey } from '../payments/status' |
| import { useI18n } from '../i18n' |
| import type { PaymentStatus } from '../types' |
|
|
| export function Button({ children, ...rest }: ButtonHTMLAttributes<HTMLButtonElement> & { children: ReactNode }) { |
| return ( |
| <button className="btn" {...rest}> |
| {children} |
| </button> |
| ) |
| } |
|
|
| interface FieldProps extends InputHTMLAttributes<HTMLInputElement> { |
| label: string |
| id: string |
| error?: string |
| } |
| export function Field({ label, id, error, ...rest }: FieldProps) { |
| const errId = error ? `${id}-error` : undefined |
| return ( |
| <div className="field"> |
| <label htmlFor={id}>{label}</label> |
| <input id={id} aria-invalid={!!error} aria-describedby={errId} {...rest} /> |
| {error && ( |
| <div id={errId} className="field-error" role="alert"> |
| {error} |
| </div> |
| )} |
| </div> |
| ) |
| } |
|
|
| export function StatusBadge({ status }: { status: PaymentStatus }) { |
| const { t } = useI18n() |
| return ( |
| <span className={`badge tone-${statusTone(status)}`} role="status" aria-live="polite"> |
| {t(statusKey(status) as never)} |
| </span> |
| ) |
| } |
|
|
| export function Callout({ tone = 'info', children }: { tone?: 'info' | 'ok' | 'bad' | 'warn'; children: ReactNode }) { |
| return ( |
| <div className={`callout tone-${tone}`} role="status"> |
| {children} |
| </div> |
| ) |
| } |
|
|
| export function Spinner({ label }: { label: string }) { |
| return ( |
| <span className="spinner" role="status" aria-live="polite"> |
| <span className="spin" aria-hidden="true" /> {label} |
| </span> |
| ) |
| } |
|
|