117 lines
4.1 KiB
TypeScript
117 lines
4.1 KiB
TypeScript
import { Fragment, forwardRef, useEffect, useImperativeHandle, useState, Ref } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from "~/components/ui/alert-dialog";
|
|
import { Input } from "~/components/ui/input";
|
|
import { Label } from "~/components/ui/label";
|
|
|
|
export interface RefConfirmDialog {
|
|
show: (options: {
|
|
title?: string;
|
|
description?: string;
|
|
confirmText?: string;
|
|
cancelText?: string;
|
|
destructive?: boolean;
|
|
typeToConfirm?: string;
|
|
onConfirm?: () => void;
|
|
}) => void;
|
|
}
|
|
|
|
interface Props {
|
|
destructive?: boolean;
|
|
}
|
|
|
|
const ConfirmDialog = (props: Props, ref: Ref<RefConfirmDialog>) => {
|
|
const { t } = useTranslation();
|
|
const [open, setOpen] = useState(false);
|
|
const [title, setTitle] = useState("");
|
|
const [description, setDescription] = useState<string | undefined>();
|
|
const [confirmText, setConfirmText] = useState(t("shared.confirm"));
|
|
const [cancelText, setCancelText] = useState(t("shared.cancel"));
|
|
const [destructive, setDestructive] = useState(props.destructive || false);
|
|
const [typeToConfirm, setTypeToConfirm] = useState<string | undefined>();
|
|
const [typedConfirmation, setTypedConfirmation] = useState("");
|
|
const [onConfirmCallback, setOnConfirmCallback] = useState<(() => void) | undefined>();
|
|
|
|
const isConfirmEnabled = !typeToConfirm || typedConfirmation.trim().toLowerCase() === typeToConfirm.toLowerCase();
|
|
|
|
const handleCancel = () => {
|
|
setOpen(false);
|
|
setTypedConfirmation("");
|
|
};
|
|
|
|
const handleConfirm = () => {
|
|
if (isConfirmEnabled) {
|
|
setOpen(false);
|
|
setTypedConfirmation("");
|
|
if (onConfirmCallback) {
|
|
onConfirmCallback();
|
|
}
|
|
}
|
|
};
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
show: ({ title, description, confirmText, cancelText, destructive, typeToConfirm, onConfirm }) => {
|
|
setTitle(title || t("shared.confirm"));
|
|
setDescription(description || t("shared.warningCannotUndo"));
|
|
setConfirmText(confirmText || t("shared.confirm"));
|
|
setCancelText(cancelText || t("shared.cancel"));
|
|
setDestructive(destructive ?? props.destructive ?? false);
|
|
setTypeToConfirm(typeToConfirm);
|
|
setTypedConfirmation("");
|
|
setOnConfirmCallback(() => onConfirm);
|
|
setOpen(true);
|
|
},
|
|
}));
|
|
|
|
return (
|
|
<AlertDialog open={open} onOpenChange={setOpen}>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>{title}</AlertDialogTitle>
|
|
{description && <AlertDialogDescription>{description}</AlertDialogDescription>}
|
|
</AlertDialogHeader>
|
|
|
|
{typeToConfirm && (
|
|
// Subframe ConfirmDialog spacing handled by AlertDialogContent gap-6 grid; input block uses its own gap-2.
|
|
<div className="flex flex-col gap-2">
|
|
<Label htmlFor="confirm-text" className="block">
|
|
{t("components.confirmDialog.typePrefix")} <span className="font-bold">{typeToConfirm}</span> {t("components.confirmDialog.typeSuffix")}
|
|
</Label>
|
|
<Input
|
|
id="confirm-text"
|
|
autoComplete="off"
|
|
value={typedConfirmation}
|
|
onChange={(e) => setTypedConfirmation(e.target.value)}
|
|
placeholder={typeToConfirm}
|
|
className={destructive ? "border-border-negative-solid focus-visible:shadow-focus" : ""}
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel onClick={handleCancel}>{cancelText}</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
onClick={handleConfirm}
|
|
disabled={!isConfirmEnabled}
|
|
className={destructive ? "bg-surface-feedback-solid-negative-default hover:bg-surface-feedback-solid-negative-hover text-text-neutral-on-solid disabled:bg-surface-neutral-disabled disabled:text-text-neutral-disabled" : undefined}
|
|
>
|
|
{confirmText}
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
);
|
|
};
|
|
|
|
export default forwardRef(ConfirmDialog);
|