187 lines
5.2 KiB
TypeScript
187 lines
5.2 KiB
TypeScript
"use client"
|
|
|
|
import { useState, createContext, useContext, ReactNode } from "react"
|
|
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"
|
|
|
|
type DialogType = "alert" | "confirm" | "prompt"
|
|
|
|
interface DialogConfig {
|
|
type: DialogType
|
|
title?: string
|
|
message: string
|
|
defaultValue?: string
|
|
confirmText?: string
|
|
cancelText?: string
|
|
variant?: "default" | "destructive"
|
|
onConfirm?: (value?: string) => void
|
|
onCancel?: () => void
|
|
}
|
|
|
|
interface DialogContextType {
|
|
showAlert: (message: string, title?: string) => Promise<void>
|
|
showConfirm: (message: string, title?: string, variant?: "default" | "destructive") => Promise<boolean>
|
|
showPrompt: (message: string, defaultValue?: string, title?: string) => Promise<string | null>
|
|
}
|
|
|
|
const DialogContext = createContext<DialogContextType | undefined>(undefined)
|
|
|
|
export function DialogProvider({ children }: { children: ReactNode }) {
|
|
const [open, setOpen] = useState(false)
|
|
const [config, setConfig] = useState<DialogConfig | null>(null)
|
|
const [inputValue, setInputValue] = useState("")
|
|
|
|
const showAlert = (message: string, title?: string): Promise<void> => {
|
|
return new Promise((resolve) => {
|
|
setConfig({
|
|
type: "alert",
|
|
title: title || "提示",
|
|
message,
|
|
confirmText: "确定",
|
|
onConfirm: () => {
|
|
setOpen(false)
|
|
// 等待模态框动画完成后再 resolve
|
|
setTimeout(() => resolve(), 150)
|
|
},
|
|
})
|
|
setOpen(true)
|
|
})
|
|
}
|
|
|
|
const showConfirm = (
|
|
message: string,
|
|
title?: string,
|
|
variant: "default" | "destructive" = "default"
|
|
): Promise<boolean> => {
|
|
return new Promise((resolve) => {
|
|
setConfig({
|
|
type: "confirm",
|
|
title: title || "确认",
|
|
message,
|
|
confirmText: "确定",
|
|
cancelText: "取消",
|
|
variant,
|
|
onConfirm: () => {
|
|
setOpen(false)
|
|
// 等待模态框动画完成后再 resolve
|
|
setTimeout(() => resolve(true), 150)
|
|
},
|
|
onCancel: () => {
|
|
setOpen(false)
|
|
// 等待模态框动画完成后再 resolve
|
|
setTimeout(() => resolve(false), 150)
|
|
},
|
|
})
|
|
setOpen(true)
|
|
})
|
|
}
|
|
|
|
const showPrompt = (
|
|
message: string,
|
|
defaultValue: string = "",
|
|
title?: string
|
|
): Promise<string | null> => {
|
|
return new Promise((resolve) => {
|
|
setInputValue(defaultValue)
|
|
setConfig({
|
|
type: "prompt",
|
|
title: title || "输入",
|
|
message,
|
|
defaultValue,
|
|
confirmText: "确定",
|
|
cancelText: "取消",
|
|
onConfirm: (value) => {
|
|
setOpen(false)
|
|
// 等待模态框动画完成后再 resolve
|
|
setTimeout(() => resolve(value || null), 150)
|
|
},
|
|
onCancel: () => {
|
|
setOpen(false)
|
|
// 等待模态框动画完成后再 resolve
|
|
setTimeout(() => resolve(null), 150)
|
|
},
|
|
})
|
|
setOpen(true)
|
|
})
|
|
}
|
|
|
|
const handleConfirm = () => {
|
|
if (config?.type === "prompt") {
|
|
config.onConfirm?.(inputValue)
|
|
} else {
|
|
config?.onConfirm?.()
|
|
}
|
|
}
|
|
|
|
const handleCancel = () => {
|
|
config?.onCancel?.()
|
|
}
|
|
|
|
return (
|
|
<DialogContext.Provider value={{ showAlert, showConfirm, showPrompt }}>
|
|
{children}
|
|
<AlertDialog open={open} onOpenChange={setOpen}>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>{config?.title}</AlertDialogTitle>
|
|
<AlertDialogDescription className="whitespace-pre-wrap">
|
|
{config?.message}
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
|
|
{config?.type === "prompt" && (
|
|
<div className="py-4">
|
|
<Label htmlFor="prompt-input" className="sr-only">
|
|
输入值
|
|
</Label>
|
|
<Input
|
|
id="prompt-input"
|
|
value={inputValue}
|
|
onChange={(e) => setInputValue(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") {
|
|
handleConfirm()
|
|
}
|
|
}}
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<AlertDialogFooter>
|
|
{config?.type !== "alert" && (
|
|
<AlertDialogCancel onClick={handleCancel}>
|
|
{config?.cancelText || "取消"}
|
|
</AlertDialogCancel>
|
|
)}
|
|
<AlertDialogAction
|
|
onClick={handleConfirm}
|
|
className={config?.variant === "destructive" ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : ""}
|
|
>
|
|
{config?.confirmText || "确定"}
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</DialogContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useDialog() {
|
|
const context = useContext(DialogContext)
|
|
if (!context) {
|
|
throw new Error("useDialog must be used within a DialogProvider")
|
|
}
|
|
return context
|
|
}
|