Files
gig-admin/src/components/confirm-dialog.tsx
2026-08-11 22:07:39 +08:00

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>
)
}