2025-05-28 15:36:51 -07:00

117 lines
3.5 KiB
TypeScript

import clsx from "clsx";
import { FC, useEffect } from "react";
import CloseIcon from "@dndbeyond/fontawesome-cache/svgs/solid/x.svg";
import { Dialog, DialogProps } from "@dndbeyond/ttui/components/Dialog";
import { Button, ButtonProps } from "../Button";
import styles from "./styles.module.css";
/**
* @heading Text for the title of the modal - defaults to "Confirm"
* @onClose Function to call when the Close button is clicked
* @onConfirm Function to call when the Confirm button is clicked
* @confirmButtonText Provide text to the Confirm button - defaults to "Confirm"
* @closeButtonText Provide text to the Close button - defaults to "Cancel"
* @variant "default" | "remove" | "confirm-only"
* --default: default modal with both Confirm and Close buttons using the "success" button color
* --remove: modal with both Confirm and Close buttons using the "secondary" color for the header and Confirm button
* --confirm-only: modal with only the Confirm button using the "success" button color
* @size "default" | "fit-content"
* --default: default modal size
* --fit-content: modal size adjusts to the content
* @color Passes a ButtonProps color to the confirm button
* @useMobileFullScreen Boolean to set the modal to full screen on mobile breakpoint
*/
export interface ConfirmModalProps extends DialogProps {
heading?: string;
onClose: () => void;
onConfirm: () => void;
confirmButtonText?: string;
closeButtonText?: string;
variant?: "default" | "remove" | "confirm-only";
size?: "default" | "fit-content";
color?: ButtonProps["color"];
useMobileFullScreen?: boolean;
}
export const ConfirmModal: FC<ConfirmModalProps> = ({
children,
className,
heading = "Confirm",
onClose,
onConfirm,
confirmButtonText = "Confirm",
closeButtonText = "Cancel",
open,
variant = "default",
size = "default",
color = "success",
useMobileFullScreen,
...props
}) => {
const isConfirmOnly = variant === "confirm-only";
useEffect(() => {
const body = document.querySelector("body");
if (!body) return;
// Prevent scrolling when modal is visible
if (open) {
body.style.overflow = "hidden";
} else {
body.style.overflow = "";
}
}, [open]);
return (
<Dialog
className={clsx([
styles.confirmModal,
useMobileFullScreen && styles.fullScreen,
styles[variant],
styles[size],
className,
])}
onClose={onClose}
modal
open={open}
{...props}
>
<header className={styles.header}>
<h2>{heading}</h2>
<Button
className={styles.closeButton}
size="x-small"
variant="text"
onClick={onClose}
aria-label={`Close ${heading} Modal`}
forceThemeMode="light"
>
<CloseIcon className={styles.closeIcon} />
</Button>
</header>
<div className={styles.content}>{children}</div>
<footer className={styles.footer}>
{!isConfirmOnly && (
<Button className={styles.footerButton} onClick={onClose}>
{closeButtonText}
</Button>
)}
<Button
className={clsx([
styles.footerButton,
isConfirmOnly && styles.confirmOnly,
])}
color={variant === "remove" ? "secondary" : color}
onClick={onConfirm}
forceThemeMode="light"
size={isConfirmOnly ? "x-small" : undefined}
>
{confirmButtonText}
</Button>
</footer>
</Dialog>
);
};