@@ -21,6 +21,7 @@ export const Dropdown: React.FC<DropdownProps> = ({
2121 className,
2222} ) => {
2323 const [ isOpen , setIsOpen ] = React . useState ( false ) ;
24+ const [ position , setPosition ] = React . useState < { top ?: number ; bottom ?: number ; left : number ; right : number } > ( { left : 0 , right : 0 } ) ;
2425 const dropdownRef = React . useRef < HTMLDivElement > ( null ) ;
2526 const triggerRef = React . useRef < HTMLDivElement > ( null ) ;
2627
@@ -50,6 +51,34 @@ export const Dropdown: React.FC<DropdownProps> = ({
5051 } ;
5152 } , [ variant ] ) ;
5253
54+ // Update position when scrolling or resizing
55+ React . useEffect ( ( ) => {
56+ if ( ! isOpen ) return ;
57+
58+ const updatePosition = ( ) => {
59+ if ( ! triggerRef . current ) return ;
60+ const rect = triggerRef . current . getBoundingClientRect ( ) ;
61+
62+ setPosition ( {
63+ left : rect . left ,
64+ right : window . innerWidth - rect . right ,
65+ ...( direction === 'down'
66+ ? { top : rect . bottom + 8 }
67+ : { bottom : window . innerHeight - rect . top + 8 }
68+ ) ,
69+ } ) ;
70+ } ;
71+
72+ updatePosition ( ) ;
73+ window . addEventListener ( 'scroll' , updatePosition , true ) ;
74+ window . addEventListener ( 'resize' , updatePosition ) ;
75+
76+ return ( ) => {
77+ window . removeEventListener ( 'scroll' , updatePosition , true ) ;
78+ window . removeEventListener ( 'resize' , updatePosition ) ;
79+ } ;
80+ } , [ isOpen , direction ] ) ;
81+
5382 const triggerClasses = cn ( {
5483 "cursor-pointer" : true ,
5584 "flex items-center gap-1 text-sm font-medium text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-gray-200 transition-colors" :
@@ -76,22 +105,6 @@ export const Dropdown: React.FC<DropdownProps> = ({
76105 className
77106 ) ;
78107
79- // Get position for the dropdown menu
80- const getDropdownPosition = ( ) => {
81- if ( ! triggerRef . current ) return { } ;
82- const rect = triggerRef . current . getBoundingClientRect ( ) ;
83-
84- return {
85- position : 'fixed' as const ,
86- left : rect . left ,
87- right : window . innerWidth - rect . right ,
88- ...( direction === 'down'
89- ? { top : rect . bottom + 8 }
90- : { bottom : window . innerHeight - rect . top + 8 }
91- ) ,
92- } ;
93- } ;
94-
95108 return (
96109 < div className = "relative inline-block" ref = { dropdownRef } >
97110 < div
@@ -105,7 +118,8 @@ export const Dropdown: React.FC<DropdownProps> = ({
105118 < div
106119 className = { menuClasses }
107120 style = { {
108- ...getDropdownPosition ( ) ,
121+ position : 'fixed' ,
122+ ...position ,
109123 zIndex : ZINDEX . dropdown ,
110124 } }
111125 >
@@ -117,22 +131,26 @@ export const Dropdown: React.FC<DropdownProps> = ({
117131 ) ;
118132} ;
119133
120- export const DropdownItem = React . forwardRef <
121- HTMLButtonElement ,
122- React . ButtonHTMLAttributes < HTMLButtonElement >
123- > ( ( { className, children, ...props } , ref ) => (
124- < button
125- ref = { ref }
126- className = { cn (
127- "flex w-full items-center px-3 py-2 text-sm" ,
128- "text-gray-700 dark:text-gray-200" ,
129- "hover:bg-gray-100 dark:hover:bg-gray-800" ,
130- "focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800" ,
131- className
132- ) }
133- { ...props }
134- >
135- { children }
136- </ button >
137- ) ) ;
134+ export interface DropdownItemProps extends React . ButtonHTMLAttributes < HTMLButtonElement > {
135+ icon ?: React . ReactNode ;
136+ }
137+
138+ export const DropdownItem = React . forwardRef < HTMLButtonElement , DropdownItemProps > (
139+ ( { className, children, icon, ...props } , ref ) => (
140+ < button
141+ ref = { ref }
142+ className = { cn (
143+ "flex w-full items-center px-3 py-2 text-sm gap-2" ,
144+ "text-gray-700 dark:text-gray-200" ,
145+ "hover:bg-gray-100 dark:hover:bg-gray-800" ,
146+ "focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800" ,
147+ className
148+ ) }
149+ { ...props }
150+ >
151+ { icon }
152+ { children }
153+ </ button >
154+ )
155+ ) ;
138156DropdownItem . displayName = "DropdownItem" ;
0 commit comments