@@ -2,14 +2,6 @@ import * as Dialog from '@radix-ui/react-dialog'
22import { motion , AnimatePresence } from 'framer-motion'
33import { X } from 'lucide-react'
44
5- /**
6- * Accessible modal built on Radix Dialog with glass-style card.
7- *
8- * Usage:
9- * <Modal open={open} onOpenChange={setOpen} title="My Modal">
10- * <p>Content here</p>
11- * </Modal>
12- */
135export default function Modal ( { open, onOpenChange, title, description, children, size = 'md' } ) {
146 const widths = { sm : 'max-w-sm' , md : 'max-w-lg' , lg : 'max-w-2xl' , xl : 'max-w-4xl' }
157
@@ -18,69 +10,60 @@ export default function Modal({ open, onOpenChange, title, description, children
1810 < AnimatePresence >
1911 { open && (
2012 < Dialog . Portal forceMount >
13+ { /* Overlay doubles as the scroll container */ }
2114 < Dialog . Overlay asChild >
2215 < motion . div
2316 initial = { { opacity : 0 } }
2417 animate = { { opacity : 1 } }
2518 exit = { { opacity : 0 } }
26- className = "fixed inset-0 z-50 bg-black/40 backdrop-blur-sm"
27- />
28- </ Dialog . Overlay >
29-
30- < Dialog . Content asChild >
31- < motion . div
32- initial = { { opacity : 0 , scale : 0.96 , y : 12 } }
33- animate = { { opacity : 1 , scale : 1 , y : 0 } }
34- exit = { { opacity : 0 , scale : 0.96 , y : 12 } }
35- transition = { { duration : 0.2 , ease : 'easeOut' } }
36- className = { `fixed z-50 left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2
37- w-full ${ widths [ size ] ?? widths . md } mx-4
38- glass-card outline-none flex flex-col` }
39- style = { { maxHeight : '90vh' } }
19+ style = { { overflowY : 'auto' } }
20+ className = "fixed inset-0 z-50 bg-black/40 backdrop-blur-sm flex items-start justify-center p-4 py-8"
4021 >
41- { /* Header */ }
42- < div className = "flex-shrink-0 px-6 pt-6 pb-0" >
43- { ( title || description ) && (
44- < div className = "mb-5 pr-8" >
45- { title && (
46- < Dialog . Title className = "text-lg font-bold text-[var(--color-text)] leading-tight" >
47- { title }
48- </ Dialog . Title >
49- ) }
50- { description && (
51- < Dialog . Description className = "text-sm text-[var(--color-text-muted)] mt-1" >
52- { description }
53- </ Dialog . Description >
54- ) }
55- </ div >
56- ) }
57- </ div >
22+ < Dialog . Content asChild >
23+ < motion . div
24+ initial = { { opacity : 0 , scale : 0.96 , y : 12 } }
25+ animate = { { opacity : 1 , scale : 1 , y : 0 } }
26+ exit = { { opacity : 0 , scale : 0.96 , y : 12 } }
27+ transition = { { duration : 0.2 , ease : 'easeOut' } }
28+ className = { `relative w-full ${ widths [ size ] ?? widths . md } glass-card p-6 outline-none my-auto` }
29+ >
30+ { /* Header */ }
31+ { ( title || description ) && (
32+ < div className = "mb-5 pr-8" >
33+ { title && (
34+ < Dialog . Title className = "text-lg font-bold text-[var(--color-text)] leading-tight" >
35+ { title }
36+ </ Dialog . Title >
37+ ) }
38+ { description && (
39+ < Dialog . Description className = "text-sm text-[var(--color-text-muted)] mt-1" >
40+ { description }
41+ </ Dialog . Description >
42+ ) }
43+ </ div >
44+ ) }
5845
59- { /* Close */ }
60- < Dialog . Close asChild >
61- < button className = "absolute top-4 right-4 w-7 h-7 flex items-center justify-center
62- rounded-lg text-[var(--color-text-muted)] hover:text-[var(--color-text)]
63- hover:bg-[var(--color-surface-2)] transition-colors" >
64- < X size = { 16 } />
65- </ button >
66- </ Dialog . Close >
46+ { /* Close */ }
47+ < Dialog . Close asChild >
48+ < button className = "absolute top-4 right-4 w-7 h-7 flex items-center justify-center
49+ rounded-lg text-[var(--color-text-muted)] hover:text-[var(--color-text)]
50+ hover:bg-[var(--color-surface-2)] transition-colors" >
51+ < X size = { 16 } />
52+ </ button >
53+ </ Dialog . Close >
6754
68- { /* Scrollable content */ }
69- < div className = "flex-1 overflow-y-auto px-6 pb-6" >
70- { children }
71- </ div >
55+ { children }
56+ </ motion . div >
57+ </ Dialog . Content >
7258 </ motion . div >
73- </ Dialog . Content >
59+ </ Dialog . Overlay >
7460 </ Dialog . Portal >
7561 ) }
7662 </ AnimatePresence >
7763 </ Dialog . Root >
7864 )
7965}
8066
81- /**
82- * Convenience component for modal footer with action buttons.
83- */
8467export function ModalFooter ( { children } ) {
8568 return (
8669 < div className = "flex justify-end gap-3 mt-6 pt-4 border-t border-[var(--color-border)]" >
0 commit comments