Spaces:
Running
Running
| import { useEffect, useRef } from "react"; | |
| import { Button } from "./Button"; | |
| export function ConfirmDialog({ | |
| open, | |
| title, | |
| body, | |
| confirmLabel = "Confirm", | |
| danger = false, | |
| busy = false, | |
| onConfirm, | |
| onCancel, | |
| }: { | |
| open: boolean; | |
| title: string; | |
| body: string; | |
| confirmLabel?: string; | |
| danger?: boolean; | |
| busy?: boolean; | |
| onConfirm: () => void; | |
| onCancel: () => void; | |
| }) { | |
| const cancelRef = useRef<HTMLButtonElement>(null); | |
| useEffect(() => { | |
| if (!open) return; | |
| cancelRef.current?.focus(); | |
| const onKey = (e: KeyboardEvent) => { | |
| if (e.key === "Escape") onCancel(); | |
| }; | |
| window.addEventListener("keydown", onKey); | |
| return () => window.removeEventListener("keydown", onKey); | |
| }, [open, onCancel]); | |
| if (!open) return null; | |
| return ( | |
| <div | |
| className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" | |
| role="dialog" | |
| aria-modal="true" | |
| aria-labelledby="confirm-title" | |
| onMouseDown={(e) => { | |
| if (e.target === e.currentTarget) onCancel(); | |
| }} | |
| > | |
| <div className="w-full max-w-md rounded-md border border-line bg-surface-raised p-5 shadow-lg"> | |
| <h2 id="confirm-title" className="text-base font-semibold text-ink"> | |
| {title} | |
| </h2> | |
| <p className="mt-2 text-sm leading-relaxed text-ink-secondary">{body}</p> | |
| <div className="mt-5 flex justify-end gap-2"> | |
| <button | |
| ref={cancelRef} | |
| type="button" | |
| onClick={onCancel} | |
| disabled={busy} | |
| className="inline-flex h-9 items-center justify-center rounded-md border border-line bg-surface-raised px-3.5 text-sm font-medium text-ink hover:bg-surface-sunken focus-ring disabled:opacity-50" | |
| > | |
| Cancel | |
| </button> | |
| <Button variant={danger ? "danger" : "primary"} onClick={onConfirm} disabled={busy}> | |
| {busy ? "Working…" : confirmLabel} | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |