69 lines
1.9 KiB
TypeScript
69 lines
1.9 KiB
TypeScript
import { useState } from 'react'
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
AlertDialogTrigger,
|
|
} from '@/components/ui/alert-dialog'
|
|
|
|
interface ConfirmDialogProps {
|
|
trigger: React.ReactNode
|
|
title: string
|
|
description?: string
|
|
confirmLabel?: string
|
|
destructive?: boolean
|
|
onConfirm: () => Promise<void> | void
|
|
}
|
|
|
|
export function ConfirmDialog({
|
|
trigger,
|
|
title,
|
|
description,
|
|
confirmLabel = '确认',
|
|
destructive = false,
|
|
onConfirm,
|
|
}: ConfirmDialogProps) {
|
|
const [busy, setBusy] = useState(false)
|
|
|
|
return (
|
|
<AlertDialog>
|
|
<AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>{title}</AlertDialogTitle>
|
|
{description && <AlertDialogDescription>{description}</AlertDialogDescription>}
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>取消</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
disabled={busy}
|
|
variant={destructive ? 'destructive' : 'default'}
|
|
onClick={async (event) => {
|
|
event.preventDefault()
|
|
setBusy(true)
|
|
try {
|
|
await onConfirm()
|
|
// close via Escape-equivalent: AlertDialogAction default closes,
|
|
// but we prevented it — dispatch close by clicking cancel sibling.
|
|
;(event.target as HTMLElement)
|
|
.closest('[role="alertdialog"]')
|
|
?.querySelector<HTMLButtonElement>('[data-slot="alert-dialog-cancel"]')
|
|
?.click()
|
|
} finally {
|
|
setBusy(false)
|
|
}
|
|
}}
|
|
>
|
|
{busy ? '处理中…' : confirmLabel}
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
)
|
|
}
|