91 lines
2.2 KiB
TypeScript
91 lines
2.2 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useId, useRef } from "react";
|
|
import styles from "./ConfirmDialog.module.css";
|
|
|
|
export default function ConfirmDialog({
|
|
open,
|
|
title,
|
|
kicker = "Confirm",
|
|
message,
|
|
confirmLabel = "Confirm",
|
|
cancelLabel = "Cancel",
|
|
danger = false,
|
|
busy = false,
|
|
onConfirm,
|
|
onClose,
|
|
}: {
|
|
open: boolean;
|
|
title: string;
|
|
kicker?: string;
|
|
message: string;
|
|
confirmLabel?: string;
|
|
cancelLabel?: string;
|
|
danger?: boolean;
|
|
busy?: boolean;
|
|
onConfirm: () => void;
|
|
onClose: () => void;
|
|
}) {
|
|
const titleId = useId();
|
|
const cancelRef = useRef<HTMLButtonElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const previousOverflow = document.body.style.overflow;
|
|
document.body.style.overflow = "hidden";
|
|
cancelRef.current?.focus();
|
|
const onKey = (event: KeyboardEvent) => {
|
|
if (event.key === "Escape" && !busy) onClose();
|
|
};
|
|
window.addEventListener("keydown", onKey);
|
|
return () => {
|
|
document.body.style.overflow = previousOverflow;
|
|
window.removeEventListener("keydown", onKey);
|
|
};
|
|
}, [open, busy, onClose]);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<div
|
|
className={styles.overlay}
|
|
role="presentation"
|
|
onMouseDown={(event) => {
|
|
if (event.target === event.currentTarget && !busy) onClose();
|
|
}}
|
|
>
|
|
<div
|
|
className={styles.dialog}
|
|
role="alertdialog"
|
|
aria-modal="true"
|
|
aria-labelledby={titleId}
|
|
>
|
|
<p className={styles.kicker}>{kicker}</p>
|
|
<h2 id={titleId} className={styles.title}>
|
|
{title}
|
|
</h2>
|
|
<p className={styles.message}>{message}</p>
|
|
<div className={styles.actions}>
|
|
<button
|
|
ref={cancelRef}
|
|
type="button"
|
|
className={`outline-btn ${styles.cancelBtn}`}
|
|
disabled={busy}
|
|
onClick={onClose}
|
|
>
|
|
{cancelLabel}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`outline-btn ${styles.confirmBtn} ${danger ? styles.confirmDanger : ""}`}
|
|
disabled={busy}
|
|
onClick={onConfirm}
|
|
>
|
|
{busy ? "…" : confirmLabel}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|