11import { faRectangleXmark } from "@fortawesome/free-solid-svg-icons" ;
22import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" ;
3- import React , { useEffect } from "react" ;
3+ import React , { useEffect , useState , MouseEvent } from "react" ;
44import { IModal } from "@/types" ;
55
66const Modal = ( {
@@ -12,37 +12,105 @@ const Modal = ({
1212 className,
1313 ...props
1414} : IModal ) => {
15+ const [ position , setPosition ] = useState ( { x : 0 , y : 0 } ) ;
16+ const [ isDragging , setIsDragging ] = useState ( false ) ;
17+ const [ dragStart , setDragStart ] = useState ( { x : 0 , y : 0 } ) ;
18+
1519 useEffect ( ( ) => {
1620 if ( isModalOpen ) {
1721 document . body . style . overflow = "hidden" ;
1822 } else {
1923 document . body . style . overflow = "unset" ;
24+ setPosition ( { x : 0 , y : 0 } ) ;
2025 }
2126 } , [ isModalOpen ] ) ;
27+
28+ const handleMouseDown = ( e : MouseEvent ) => {
29+ if ( e . button !== 0 ) return ;
30+ e . preventDefault ( ) ;
31+ setIsDragging ( true ) ;
32+ setDragStart ( {
33+ x : e . clientX - position . x ,
34+ y : e . clientY - position . y ,
35+ } ) ;
36+ } ;
37+
38+ const handleMouseMove = ( e : MouseEvent ) => {
39+ if ( ! isDragging ) return ;
40+ e . preventDefault ( ) ;
41+ requestAnimationFrame ( ( ) => {
42+ const newX = e . clientX - dragStart . x ;
43+ const newY = e . clientY - dragStart . y ;
44+ setPosition ( { x : newX , y : newY } ) ;
45+ } ) ;
46+ } ;
47+
48+ const stopDragging = ( ) => {
49+ setIsDragging ( false ) ;
50+ } ;
51+
52+ useEffect ( ( ) => {
53+ if ( ! isDragging ) return ;
54+
55+ const handleMouseUp = ( e : MouseEvent ) => {
56+ e . preventDefault ( ) ;
57+ stopDragging ( ) ;
58+ } ;
59+
60+ window . addEventListener ( "mousemove" , handleMouseMove as any ) ;
61+ window . addEventListener ( "mouseup" , handleMouseUp as any ) ;
62+ window . addEventListener ( "mouseleave" , stopDragging ) ;
63+ window . addEventListener ( "blur" , stopDragging ) ;
64+ window . addEventListener ( "keydown" , stopDragging ) ;
65+
66+ return ( ) => {
67+ window . removeEventListener ( "mousemove" , handleMouseMove as any ) ;
68+ window . removeEventListener ( "mouseup" , handleMouseUp as any ) ;
69+ window . removeEventListener ( "mouseleave" , stopDragging ) ;
70+ window . removeEventListener ( "blur" , stopDragging ) ;
71+ window . removeEventListener ( "keydown" , stopDragging ) ;
72+ } ;
73+ } , [ isDragging , dragStart . x , dragStart . y , position . x , position . y ] ) ;
74+
2275 return (
2376 < >
2477 { isModalOpen && (
25- < div className = "fixed inset-0 z-40 flex items-center justify-center" >
78+ < div className = "fixed inset-0 z-40 flex items-start justify-center pt-[5vh] " >
2679 < div className = "fixed inset-0 bg-black opacity-50" > </ div >
2780 < div
28- className = { `z-50 overflow-auto rounded-lg border border-gray-900 bg-gray-500 shadow-xl ${ className } ` }
81+ style = { {
82+ transform : `translate(${ position . x } px, ${ position . y } px)` ,
83+ cursor : isDragging ? "grabbing" : "grab" ,
84+ userSelect : "none" ,
85+ } }
86+ className = { `z-50 mx-auto overflow-auto rounded-lg border border-gray-900 bg-gray-500/70 shadow-xl ${ className } ` }
2987 { ...props }
3088 >
3189 { title && (
32- < div className = "flex gap-4 border-b border-gray-500 p-4" >
33- < button onClick = { closeModal } className = "pr-3" >
90+ < div
91+ className = "flex gap-4 border-b border-gray-500 p-4 cursor-grab select-none bg-gray-500"
92+ onMouseDown = { handleMouseDown }
93+ onDragStart = { ( e ) => e . preventDefault ( ) }
94+ >
95+ < button
96+ onClick = { closeModal }
97+ className = "pr-3 bg-gray-500 hover:bg-gray-600"
98+ >
3499 < FontAwesomeIcon
35100 icon = { faRectangleXmark }
36101 className = "text-2xl text-white"
37102 />
38103 </ button >
39-
40- < p > { title } </ p >
104+ < p className = "text-white" > { title } </ p >
41105 </ div >
42106 ) }
43- < div className = { `h-full w-full ${ title && "p-4" } ` } > { children } </ div >
107+ < div className = { `h-full w-full bg-gray-500/70 ${ title && "p-4" } ` } >
108+ { children }
109+ </ div >
44110 { footer && (
45- < div className = "border-t border-gray-500 p-4" > { footer } </ div >
111+ < div className = "border-t border-gray-500 p-4 bg-gray-500" >
112+ { footer }
113+ </ div >
46114 ) }
47115 </ div >
48116 </ div >
0 commit comments