initial commit after split
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { useEffect, useId, type ReactNode } from "react";
|
||||
|
||||
export type DialogProps = {
|
||||
open: boolean;
|
||||
title: ReactNode;
|
||||
children: ReactNode;
|
||||
footer?: ReactNode;
|
||||
role?: "dialog" | "alertdialog";
|
||||
ariaDescribedBy?: string;
|
||||
closeLabel?: string;
|
||||
closeOnBackdrop?: boolean;
|
||||
showCloseButton?: boolean;
|
||||
closeDisabled?: boolean;
|
||||
onClose?: () => void;
|
||||
className?: string;
|
||||
backdropClassName?: string;
|
||||
headerClassName?: string;
|
||||
titleClassName?: string;
|
||||
bodyClassName?: string;
|
||||
footerClassName?: string;
|
||||
};
|
||||
|
||||
function joinClasses(...classes: Array<string | undefined | false>) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
export default function Dialog({
|
||||
open,
|
||||
title,
|
||||
children,
|
||||
footer,
|
||||
role = "dialog",
|
||||
ariaDescribedBy,
|
||||
closeLabel = "Close",
|
||||
closeOnBackdrop = true,
|
||||
showCloseButton = true,
|
||||
closeDisabled = false,
|
||||
onClose,
|
||||
className = "",
|
||||
backdropClassName = "",
|
||||
headerClassName = "",
|
||||
titleClassName = "",
|
||||
bodyClassName = "",
|
||||
footerClassName = ""
|
||||
}: DialogProps) {
|
||||
const titleId = useId();
|
||||
const canClose = Boolean(onClose) && !closeDisabled;
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !canClose) return undefined;
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") onClose?.();
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [canClose, onClose, open]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={joinClasses("dialog-backdrop", backdropClassName)}
|
||||
role="presentation"
|
||||
onMouseDown={(event) => {
|
||||
if (closeOnBackdrop && canClose && event.target === event.currentTarget) onClose?.();
|
||||
}}
|
||||
>
|
||||
<section
|
||||
className={joinClasses("dialog-panel", className)}
|
||||
role={role}
|
||||
aria-modal="true"
|
||||
aria-labelledby={titleId}
|
||||
aria-describedby={ariaDescribedBy}
|
||||
>
|
||||
<div className={joinClasses("dialog-header", headerClassName)}>
|
||||
<h2 id={titleId} className={joinClasses("dialog-title", titleClassName)}>{title}</h2>
|
||||
{showCloseButton && onClose && (
|
||||
<button
|
||||
type="button"
|
||||
className="dialog-close"
|
||||
onClick={onClose}
|
||||
aria-label={closeLabel}
|
||||
disabled={closeDisabled}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className={joinClasses("dialog-body", bodyClassName)}>{children}</div>
|
||||
{footer && <div className={joinClasses("dialog-footer", footerClassName)}>{footer}</div>}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user