@@ -15,31 +15,11 @@ type ModalProps = {
1515}
1616
1717const sizeClasses = {
18- sm : {
19- w : 'w-[calc(100vw-2rem)] max-w-sm' ,
20- h : 'max-h-[calc(100vh-4rem)] md:max-h-[560px]' ,
21- minH : 'min-h-[180px]' ,
22- } ,
23- md : {
24- w : 'w-[calc(100vw-2rem)] max-w-md' ,
25- h : 'max-h-[calc(100vh-4rem)] md:max-h-[640px]' ,
26- minH : 'min-h-[200px]' ,
27- } ,
28- lg : {
29- w : 'w-[calc(100vw-2rem)] max-w-lg' ,
30- h : 'max-h-[calc(100vh-4rem)] md:max-h-[720px]' ,
31- minH : 'min-h-[240px]' ,
32- } ,
33- xl : {
34- w : 'w-[calc(100vw-2rem)] max-w-4xl' ,
35- h : 'max-h-[calc(100vh-4rem)] md:max-h-[800px]' ,
36- minH : 'min-h-[280px]' ,
37- } ,
38- full : {
39- w : 'w-[calc(100vw-1rem)] max-w-5xl' ,
40- h : 'h-[calc(100vh-4rem)] md:h-[calc(100vh-6rem)]' ,
41- minH : 'min-h-[calc(100vh-4rem)]' ,
42- } ,
18+ sm : 'max-w-sm max-h-[80vh]' ,
19+ md : 'max-w-md max-h-[80vh]' ,
20+ lg : 'max-w-lg max-h-[80vh]' ,
21+ xl : 'max-w-4xl max-h-[80vh]' ,
22+ full : 'max-w-[95vw] max-h-[95vh]' ,
4323} as const
4424
4525export default function Modal ( {
@@ -53,95 +33,81 @@ export default function Modal({
5333 showCloseButton = true ,
5434 className = '' ,
5535} : ModalProps ) {
56- const modalRef = useRef < HTMLDivElement > ( null )
57- const sizeValue = sizeClasses [ size ] || sizeClasses . md
36+ const dialogRef = useRef < HTMLDialogElement > ( null )
5837
59- // Lock body scroll when modal is open
6038 useEffect ( ( ) => {
6139 if ( isOpen ) {
62- document . documentElement . classList . add ( 'modal-isActive' )
63- document . body . style . overflow = 'hidden'
40+ dialogRef . current ?. showModal ( )
6441 } else {
65- document . documentElement . classList . remove ( 'modal-isActive' )
66- document . body . style . overflow = ''
67- }
68- return ( ) => {
69- document . documentElement . classList . remove ( 'modal-isActive' )
70- document . body . style . overflow = ''
42+ dialogRef . current ?. close ( )
7143 }
7244 } , [ isOpen ] )
7345
74- // Handle keyboard events
75- useEffect ( ( ) => {
76- if ( ! isOpen ) return
77-
78- const handleKeyDown = ( e : KeyboardEvent ) => {
79- if ( e . key === 'Escape' ) {
80- e . preventDefault ( )
81- onClose ( )
82- }
46+ const handleBackdropClick = ( e : React . MouseEvent < HTMLDialogElement > ) => {
47+ if ( ! closeOnBackdropClick ) return
48+ const rect = dialogRef . current ?. getBoundingClientRect ( )
49+ if ( ! rect ) return
50+ const isClickOutside =
51+ e . clientX < rect . left ||
52+ e . clientX > rect . right ||
53+ e . clientY < rect . top ||
54+ e . clientY > rect . bottom
55+ if ( isClickOutside ) {
56+ onClose ( )
8357 }
58+ }
8459
85- document . addEventListener ( 'keydown' , handleKeyDown )
86- return ( ) => document . removeEventListener ( 'keydown' , handleKeyDown )
87- } , [ isOpen , onClose ] )
60+ const handleCancel = ( e : React . SyntheticEvent < HTMLDialogElement > ) => {
61+ e . preventDefault ( )
62+ onClose ( )
63+ }
8864
89- // Focus management
90- useEffect ( ( ) => {
91- if ( isOpen && modalRef . current ) {
92- const focusableElements = modalRef . current . querySelectorAll (
93- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
94- )
95- const firstElement = focusableElements [ 0 ] as HTMLElement
96- firstElement ?. focus ( )
97- }
98- } , [ isOpen ] )
99-
100- const modalBoxClasses = `
101- modal-box overflow-hidden rounded-xl md:rounded-2xl p-3 md:p-4 shadow-xl transition-all duration-200
102- ${ sizeValue . w } ${ sizeValue . minH } ${ size === 'full' ? sizeValue . h : '' } ${ className }
103- `
104-
105- if ( ! isOpen ) return null
65+ const sizeClass = sizeClasses [ size ] || sizeClasses . md
10666
10767 return createPortal (
10868 < dialog
109- open = { isOpen }
69+ ref = { dialogRef }
11070 dir = { direction }
111- aria-labelledby = { typeof title === 'string' ? title : 'modal-title' }
112- aria-modal = "true"
113- onClick = { ( ) => closeOnBackdropClick && onClose ( ) }
114- className = "flex items-center justify-center p-2 transition-opacity duration-200 opacity-100 modal modal-middle md:p-4"
71+ onCancel = { handleCancel }
72+ onClick = { handleBackdropClick }
73+ className = "modal"
11574 >
11675 < div
117- ref = { modalRef }
118- onClick = { ( e ) => e . stopPropagation ( ) }
119- className = { `${ modalBoxClasses } animate-modal-in` }
76+ className = { `
77+ modal-box
78+ rounded-xl md:rounded-2xl
79+ p-3 md:p-4
80+ shadow-xl
81+ overflow-hidden
82+ ${ sizeClass }
83+ ${ className }
84+ ` }
12085 >
12186 { ( title || showCloseButton ) && (
12287 < div className = "flex items-center justify-between gap-2 mb-2 md:mb-3 md:gap-4" >
12388 { title && (
124- < h3
125- id = "modal-title"
126- className = "text-base font-semibold md:text-lg"
127- >
89+ < h3 className = "text-base font-semibold md:text-lg" >
12890 { title }
12991 </ h3 >
13092 ) }
13193 { showCloseButton && (
13294 < button
13395 type = "button"
13496 onClick = { onClose }
135- className = "flex items-center justify-center transition-all rounded-full cursor-pointer w-7 h-7 md:w-8 md:h-8 bg -base-300 text-muted hover:bg-base-content/10 hover:scale-105 active:scale-95 shrink-0 outline-0! border-0! "
97+ className = "min-h-0 p-0 rounded-full btn btn-sm btn-ghost w-7 h-7 md:w-8 md:h-8 text -base-content/60 hover: text-base-content hover:bg-base-content/10"
13698 aria-label = "Close modal"
13799 >
138- < IoClose />
100+ < IoClose className = "w-5 h-5" />
139101 </ button >
140102 ) }
141103 </ div >
142104 ) }
143105 < div
144- className = { `overflow-y-auto overflow-x-hidden ${ size !== 'full' ? sizeValue . h : 'h-full' } pr-0.5 md:pr-1` }
106+ className = { `
107+ overflow-y-auto overflow-x-hidden
108+ pr-0.5 md:pr-1
109+ ${ size !== 'full' ? 'max-h-[70vh]' : 'max-h-[85vh]' }
110+ ` }
145111 >
146112 { children }
147113 </ div >
0 commit comments