React 18 doesn't pass ref as a regular prop to function components — it needs forwardRef. Without it, the <dialog> element never received the ref, so showModal() had no target. Click icon, nothing happens. This was the real bug behind 'click icon, nothing happens' — Gallery was already using ConfirmDialog but the ref was silently null.
80 lines
2.5 KiB
TypeScript
80 lines
2.5 KiB
TypeScript
import { useEffect, forwardRef } from 'react'
|
|
|
|
interface Props {
|
|
/** Title shown in the dialog header. */
|
|
title: string
|
|
/** Body text explaining what will happen. */
|
|
message: string
|
|
/** Label for the confirm button. Defaults to "Confirm". */
|
|
confirmLabel?: string
|
|
/** Called when the user confirms. */
|
|
onConfirm: () => void
|
|
/** Optional: called when the user cancels. */
|
|
onCancel?: () => void
|
|
/** Danger style — makes confirm button red. Default true. */
|
|
danger?: boolean
|
|
}
|
|
|
|
const ConfirmDialog = forwardRef<HTMLDialogElement, Props>(function ConfirmDialog({
|
|
title,
|
|
message,
|
|
confirmLabel = 'Confirm',
|
|
onConfirm,
|
|
onCancel,
|
|
danger = true,
|
|
}, ref) {
|
|
useEffect(() => {
|
|
if (!ref || !('current' in ref) || !ref.current) return
|
|
const dialog = ref.current
|
|
|
|
const handleClose = () => onCancel?.()
|
|
dialog.addEventListener('close', handleClose)
|
|
return () => dialog.removeEventListener('close', handleClose)
|
|
}, [ref, onCancel])
|
|
|
|
const handleConfirm = () => {
|
|
if (ref && 'current' in ref && ref.current) ref.current.close('confirm')
|
|
onConfirm()
|
|
}
|
|
|
|
const handleCancel = () => {
|
|
if (ref && 'current' in ref && ref.current) ref.current.close('cancel')
|
|
}
|
|
|
|
return (
|
|
<dialog
|
|
ref={ref}
|
|
className="bg-kithara-panel border border-white/10 rounded-lg text-white backdrop:bg-black/60 backdrop:backdrop-blur-sm max-w-sm w-full p-0"
|
|
onClick={(e) => {
|
|
if (ref && 'current' in ref && e.target === ref.current) handleCancel()
|
|
}}
|
|
>
|
|
<form method="dialog" className="p-5">
|
|
<h3 className="text-base font-semibold mb-2">{title}</h3>
|
|
<p className="text-sm text-kithara-muted mb-5">{message}</p>
|
|
<div className="flex justify-end gap-2">
|
|
<button
|
|
type="button"
|
|
onClick={handleCancel}
|
|
className="px-3 py-1.5 text-sm rounded bg-white/5 border border-white/10 text-kithara-muted hover:text-white hover:bg-white/10 transition-colors"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleConfirm}
|
|
className={`px-3 py-1.5 text-sm rounded font-medium transition-colors ${
|
|
danger
|
|
? 'bg-red-500/80 hover:bg-red-500 text-white'
|
|
: 'bg-kithara-accent hover:bg-kithara-accent/80 text-white'
|
|
}`}
|
|
>
|
|
{confirmLabel}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</dialog>
|
|
)
|
|
})
|
|
|
|
export default ConfirmDialog |