mirror of https://github.com/jetkvm/kvm.git
add confirm dialog component
This commit is contained in:
parent
8a056960bf
commit
5fcc1f4079
|
@ -0,0 +1,106 @@
|
||||||
|
import { ExclamationTriangleIcon, CheckCircleIcon, InformationCircleIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { cx } from "@/cva.config";
|
||||||
|
import { Button } from "@/components/Button";
|
||||||
|
import Modal from "@/components/Modal";
|
||||||
|
|
||||||
|
type Variant = "danger" | "success" | "warning" | "info";
|
||||||
|
|
||||||
|
interface ConfirmDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
variant?: Variant;
|
||||||
|
confirmText?: string;
|
||||||
|
cancelText?: string | null;
|
||||||
|
onConfirm: () => void;
|
||||||
|
isConfirming?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantConfig = {
|
||||||
|
danger: {
|
||||||
|
icon: ExclamationTriangleIcon,
|
||||||
|
iconClass: "text-red-600",
|
||||||
|
iconBgClass: "bg-red-100",
|
||||||
|
buttonTheme: "danger",
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
icon: CheckCircleIcon,
|
||||||
|
iconClass: "text-green-600",
|
||||||
|
iconBgClass: "bg-green-100",
|
||||||
|
buttonTheme: "primary",
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
icon: ExclamationTriangleIcon,
|
||||||
|
iconClass: "text-yellow-600",
|
||||||
|
iconBgClass: "bg-yellow-100",
|
||||||
|
buttonTheme: "lightDanger",
|
||||||
|
},
|
||||||
|
info: {
|
||||||
|
icon: InformationCircleIcon,
|
||||||
|
iconClass: "text-blue-600",
|
||||||
|
iconBgClass: "bg-blue-100",
|
||||||
|
buttonTheme: "primary",
|
||||||
|
},
|
||||||
|
} as Record<Variant, {
|
||||||
|
icon: React.ElementType;
|
||||||
|
iconClass: string;
|
||||||
|
iconBgClass: string;
|
||||||
|
buttonTheme: "danger" | "primary" | "blank" | "light" | "lightDanger";
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export function ConfirmDialog({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
variant = "info",
|
||||||
|
confirmText = "Confirm",
|
||||||
|
cancelText = "Cancel",
|
||||||
|
onConfirm,
|
||||||
|
isConfirming = false,
|
||||||
|
}: ConfirmDialogProps) {
|
||||||
|
const { icon: Icon, iconClass, iconBgClass, buttonTheme } = variantConfig[variant];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal open={open} onClose={onClose}>
|
||||||
|
<div className="mx-auto max-w-xl px-4 transition-all duration-300 ease-in-out">
|
||||||
|
<div className="relative w-full overflow-hidden rounded-lg bg-white p-6 text-left align-middle shadow-xl transition-all dark:bg-slate-800 pointer-events-auto">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="sm:flex sm:items-start">
|
||||||
|
<div className={cx("mx-auto flex size-12 shrink-0 items-center justify-center rounded-full sm:mx-0 sm:size-10", iconBgClass)}>
|
||||||
|
<Icon aria-hidden="true" className={cx("size-6", iconClass)} />
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||||
|
<h2 className="text-lg font-bold leading-tight text-black dark:text-white">
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
<div className="mt-2 text-sm leading-snug text-slate-600 dark:text-slate-400">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end gap-x-2">
|
||||||
|
{cancelText && (
|
||||||
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme="blank"
|
||||||
|
text={cancelText}
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme={buttonTheme}
|
||||||
|
text={isConfirming ? `${confirmText}...` : confirmText}
|
||||||
|
onClick={onConfirm}
|
||||||
|
disabled={isConfirming}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import { LuPlus, LuInfo } from "react-icons/lu";
|
import { LuPlus } from "react-icons/lu";
|
||||||
|
|
||||||
import { KeySequence } from "@/hooks/stores";
|
import { KeySequence } from "@/hooks/stores";
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { InputFieldWithLabel, FieldError } from "@/components/InputField";
|
import { InputFieldWithLabel, FieldError } from "@/components/InputField";
|
||||||
import Fieldset from "@/components/Fieldset";
|
import Fieldset from "@/components/Fieldset";
|
||||||
import { MacroStepCard } from "@/components/MacroStepCard";
|
import { MacroStepCard } from "@/components/MacroStepCard";
|
||||||
import Modal from "@/components/Modal";
|
|
||||||
import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP } from "@/constants/macros";
|
import { DEFAULT_DELAY, MAX_STEPS_PER_MACRO, MAX_KEYS_PER_STEP } from "@/constants/macros";
|
||||||
import FieldLabel from "@/components/FieldLabel";
|
import FieldLabel from "@/components/FieldLabel";
|
||||||
|
import { ConfirmDialog } from "@/components/ConfirmDialog";
|
||||||
|
|
||||||
interface ValidationErrors {
|
interface ValidationErrors {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -312,44 +312,19 @@ export function MacroForm({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Modal
|
<ConfirmDialog
|
||||||
open={showDeleteConfirm}
|
open={showDeleteConfirm}
|
||||||
onClose={() => setShowDeleteConfirm(false)}
|
onClose={() => setShowDeleteConfirm(false)}
|
||||||
>
|
title="Delete Macro"
|
||||||
<div className="mx-auto max-w-xl px-4 transition-all duration-300 ease-in-out">
|
description="Are you sure you want to delete this macro? This action cannot be undone."
|
||||||
<div className="relative w-full overflow-hidden rounded-lg bg-white p-6 text-left align-middle shadow-xl transition-all dark:bg-slate-800 pointer-events-auto">
|
variant="danger"
|
||||||
<div className="space-y-4">
|
confirmText={isDeleting ? "Deleting" : "Delete"}
|
||||||
<div className="space-y-0">
|
onConfirm={() => {
|
||||||
<h2 className="text-lg font-bold leading-tight text-black dark:text-white">
|
onDelete?.();
|
||||||
Delete Macro
|
setShowDeleteConfirm(false);
|
||||||
</h2>
|
}}
|
||||||
<div className="text-sm leading-snug text-slate-600 dark:text-slate-400">
|
isConfirming={isDeleting}
|
||||||
Are you sure you want to delete this macro? This action cannot be undone.
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-end gap-x-2">
|
|
||||||
<Button
|
|
||||||
size="SM"
|
|
||||||
theme="blank"
|
|
||||||
text="Cancel"
|
|
||||||
onClick={() => setShowDeleteConfirm(false)}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
size="SM"
|
|
||||||
theme="danger"
|
|
||||||
text={isDeleting ? "Deleting..." : "Delete"}
|
|
||||||
onClick={() => {
|
|
||||||
onDelete?.();
|
|
||||||
setShowDeleteConfirm(false);
|
|
||||||
}}
|
|
||||||
disabled={isDeleting}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import { LuArrowUp, LuArrowDown, LuX, LuInfo } from "react-icons/lu";
|
import { LuArrowUp, LuArrowDown, LuX } from "react-icons/lu";
|
||||||
|
|
||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { Combobox } from "@/components/Combobox";
|
import { Combobox } from "@/components/Combobox";
|
||||||
|
|
Loading…
Reference in New Issue