Files
HiveGrid-Wireframes/hivegrid-cloud/apps/web/app/components/ui/modals/ConfirmModal.tsx
T

190 lines
6.6 KiB
TypeScript

import { forwardRef, Ref, useEffect, useImperativeHandle, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "~/components/ui/alert-dialog";
import { cn } from "~/lib/utils";
import { MailIcon } from "~/components/ui/icons/vendor";
export interface RefConfirmModal {
setDestructive: (destructive: boolean) => void;
setValue: (value: any) => void;
show: (_question: string, _yesTitle?: string, _noTitle?: string, _description?: string, _inputString?: string) => void;
}
interface Props {
destructive?: boolean;
inputType?: string;
onYes?: (value: any) => void;
onNo?: () => void;
placeholder?: string;
contentUiId?: string;
}
const ConfirmModal = (props: Props, ref: Ref<RefConfirmModal>) => {
const { t } = useTranslation();
const [open, setOpen] = useState(false);
const [title, setTitle] = useState<string>();
const [value, setValue] = useState<string>();
const [description, setDescription] = useState<string>();
const [inputString, setInputString] = useState<string | undefined>("");
const [yesTitle, setYesTitle] = useState<string>("");
const [noTitle, setNoTitle] = useState<string>("");
const [isDestructive, setDestructive] = useState<boolean>(props.destructive || false);
const inputValue = useRef<HTMLInputElement>(null);
useEffect(() => {
setTitle(t("shared.confirm").toString());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function no() {
setOpen(false);
if (props.onNo) {
props.onNo();
}
}
function yes() {
setOpen(false);
if (props.onYes) {
props.onYes(value ?? inputString);
}
}
useImperativeHandle(ref, () => ({ show, setValue, setDestructive }));
function show(
_question: string,
_yesTitle: string = t("shared.confirm").toString(),
_noTitle: string = t("shared.back").toString(),
_description?: string,
_inputString?: string
) {
setTitle(_question.toString());
if (_yesTitle) {
setYesTitle(_yesTitle);
}
if (_noTitle) {
setNoTitle(_noTitle);
}
if (_description) {
setDescription(_description);
}
setTimeout(() => {
if ((props.inputType === "email" || props.inputType === "string" || props.inputType === "slug" || props.inputType === "password") && inputValue.current) {
inputValue.current.focus();
inputValue.current.select();
}
}, 0);
setOpen(true);
setInputString(_inputString);
}
return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialogContent data-ui-id={props.contentUiId}>
<AlertDialogHeader>
<AlertDialogTitle>{title}</AlertDialogTitle>
{description && <AlertDialogDescription>{description}</AlertDialogDescription>}
</AlertDialogHeader>
<div>
{/* Subframe ConfirmDialog spacing handled by AlertDialogContent gap-6 grid; per-input top margins removed. */}
{props.inputType === "email" && (
<div>
<div className="relative rounded-md shadow-2xs">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<MailIcon className="text-text-neutral-tertiary h-5 w-5" aria-hidden="true" />
</div>
<input
value={inputString}
onChange={(e) => setInputString(e.target.value)}
ref={inputValue}
type="email"
name="email"
id="email"
aria-label={title}
className="border-border-default focus:border-brand-brand-border-solid-default block w-full rounded-md pl-10 focus:shadow-focus sm:text-sm"
placeholder={props.placeholder ?? t("account.shared.emailPlaceholder")}
/>
</div>
</div>
)}
{props.inputType === "string" && (
<div>
<div className="relative rounded-md shadow-2xs">
<input
value={inputString}
onChange={(e) => setInputString(e.target.value)}
ref={inputValue}
type="text"
name="value"
id="value"
aria-label={title}
className="border-border-default focus:border-brand-brand-border-solid-default block w-full rounded-md focus:shadow-focus sm:text-sm"
placeholder={props.placeholder}
/>
</div>
</div>
)}
{props.inputType === "slug" && (
<div>
<div className="relative rounded-md shadow-2xs">
<input
value={inputString}
onChange={(e) => setInputString(e.target.value.toLowerCase())}
ref={inputValue}
type="text"
name="value"
id="value"
aria-label={title}
className="border-border-default focus:border-brand-brand-border-solid-default block w-full rounded-md focus:shadow-focus sm:text-sm"
placeholder={props.placeholder}
/>
</div>
</div>
)}
{props.inputType === "password" && (
<div>
<div className="relative rounded-md shadow-2xs">
<input
value={inputString}
onChange={(e) => setInputString(e.target.value)}
ref={inputValue}
type="password"
name="passwordCurrent"
id="passwordCurrent"
aria-label={title}
className="border-border-default focus:border-brand-brand-border-solid-default block w-full rounded-md focus:shadow-focus sm:text-sm"
placeholder={props.placeholder}
/>
</div>
</div>
)}
</div>
<AlertDialogFooter>
<AlertDialogCancel onClick={no} className={cn("sm:col-start-1")} data-ui-id="ui.confirm.cancel">
{noTitle}
</AlertDialogCancel>
<AlertDialogAction onClick={yes} className={cn(isDestructive ? "bg-surface-feedback-solid-negative-default hover:bg-surface-feedback-solid-negative-hover" : undefined)} data-ui-id="ui.confirm.action">
{yesTitle}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};
export default forwardRef(ConfirmModal);