Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 42x 42x 380x 380x 380x 103x 10x 7x 3x 2x 2x 103x 15x 2x 2x 2x | import { useState, type ReactNode } from 'react'
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { DiscardDialog } from './DiscardDialog'
import { Loader2 } from 'lucide-react'
export interface CrudModalProps {
open: boolean
title: string
isDirty: boolean
isPending?: boolean
saveLabel?: string
cancelLabel?: string
onClose: () => void
onSave?: () => void
children: ReactNode
}
export function CrudModal({
open,
title,
isDirty,
isPending = false,
saveLabel = 'Save',
cancelLabel = 'Cancel',
onClose,
onSave,
children,
}: CrudModalProps) {
const [showDiscard, setShowDiscard] = useState(false)
function handleCancel() {
if (isDirty && !isPending) {
setShowDiscard(true)
} else {
onClose()
}
}
function handleDiscard() {
setShowDiscard(false)
onClose()
}
function handleOpenChange(open: boolean) {
if (!open) {
handleCancel()
}
}
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent
showCloseButton={false}
onInteractOutside={(e) => e.preventDefault()}
onEscapeKeyDown={(e) => {
e.preventDefault()
handleCancel()
}}
>
<DialogHeader className="mb-3">
<DialogTitle className="text-lg font-semibold">{title}</DialogTitle>
</DialogHeader>
<div className="mb-3">{children}</div>
<DialogFooter className="flex justify-end gap-2">
<Button variant="outline" onClick={handleCancel} type="button" disabled={isPending}>
{cancelLabel}
</Button>
{onSave && (
<Button onClick={onSave} disabled={isPending} type="button">
{isPending && <Loader2 className="mr-1.5 inline-block size-4 animate-spin" />}
{isPending ? 'Saving...' : saveLabel}
</Button>
)}
</DialogFooter>
</DialogContent>
<DiscardDialog
open={showDiscard}
onDiscard={handleDiscard}
onKeepEditing={() => setShowDiscard(false)}
/>
</Dialog>
)
}
|