import { useEffect } from 'react'; import type { HoopConfig } from '../data.ts'; import { HOOPS } from '../data.ts'; import { Dialog, DialogContent, DialogClose } from '@/components/ui/dialog.tsx'; import { buttonVariants } from '@/components/ui/button.tsx'; import { cn } from '@/utils.ts'; // Hoop shape icon — used in both the dialog and the header button export function HoopIcon({ hoop, size = 20 }: { hoop: HoopConfig; size?: number }) { const pad = size * 0.12; const hw = size / 2; const aspect = hoop.widthMM / hoop.heightMM; const maxW = size - pad * 2; const maxH = size - pad * 2; let shapeW: number, shapeH: number; if (aspect >= 1) { shapeW = maxW; shapeH = maxW / aspect; } else { shapeH = maxH; shapeW = maxH * aspect; } const rx = shapeW / 2; const ry = shapeH / 2; return ( {hoop.shape === 'circle' && ( )} {hoop.shape === 'oval' && ( )} {hoop.shape === 'rectangle' && ( )} ); } interface Props { open: boolean; current: HoopConfig; onSelect: (hoop: HoopConfig) => void; onClose: () => void; /** When true, the `hoop` language directive is active — show a "set by code" banner. */ isSetByCode?: boolean; } export default function HoopDialog({ open, current, onSelect, onClose, isSetByCode }: Props) { // Keep keyboard shortcut for Escape (also handled natively by base-ui Dialog, // but we keep this for belt-and-suspenders consistency) useEffect(() => { if (!open) return; const handle = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; document.addEventListener('keydown', handle); return () => document.removeEventListener('keydown', handle); }, [open, onClose]); return ( { if (!isOpen) onClose(); }} > {/* Header */} Hoop size & shape ✕ {/* "set by code" banner */} {isSetByCode && ( hoop set by{' '} hoop{' '} directive in the source — selection below is for the visual fallback only. )} {/* Grid of hoop options */} {HOOPS.map((hoop) => ( { onSelect(hoop); onClose(); }} aria-pressed={hoop.id === current.id} > {hoop.label} ))} ); }
hoop