@@ -2,6 +2,7 @@ import { Menu, Transition } from '@headlessui/react'
22import React , { ChangeEvent , ReactNode , forwardRef , useEffect , useRef , useState } from 'react'
33
44import classNames from 'classnames'
5+ import { useOutsideClick } from '../utils/useOutsideClick'
56
67export interface Props {
78 options : Option [ ]
@@ -64,10 +65,19 @@ export const Dropdown = forwardRef<HTMLDivElement, Props>(function Dropdown(
6465 const [ internalSelectedOptions , setInternalSelectedOptions ] = useState < Option [ ] > ( [ ] )
6566 const [ filteredOptions , setFilteredOptions ] = useState < Option [ ] > ( options )
6667 const [ searchTerm , setSearchTerm ] = useState ( '' )
68+ const [ isOpen , setIsOpen ] = useState ( false )
6769 const searchInputRef = useRef < HTMLInputElement > ( null )
70+ const dropdownRef = useRef < HTMLDivElement > ( null )
6871
6972 const currentSelectedOptions = selectedOptions || internalSelectedOptions
7073
74+ // Close dropdown when clicking outside in multi-select mode
75+ useOutsideClick ( dropdownRef , ( ) => {
76+ if ( isMultiSelect && isOpen ) {
77+ setIsOpen ( false )
78+ }
79+ } )
80+
7181 useEffect ( ( ) => {
7282 if ( selectedOption ) {
7383 setActiveOption ( selectedOption )
@@ -100,10 +110,12 @@ export const Dropdown = forwardRef<HTMLDivElement, Props>(function Dropdown(
100110 if ( onSelectionChange ) {
101111 onSelectionChange ( newSelection )
102112 }
113+ // Keep dropdown open in multi-select mode
103114 } else {
104115 setActiveOption ( option )
105116 if ( option . onClick ) option . onClick ( )
106117 if ( onSelect ) onSelect ( option )
118+ setIsOpen ( false ) // Close dropdown in single-select mode
107119 }
108120 }
109121
@@ -144,30 +156,51 @@ export const Dropdown = forwardRef<HTMLDivElement, Props>(function Dropdown(
144156
145157 const canClear = onClear && ( isMultiSelect ? currentSelectedOptions . length > 0 : ! ! activeOption )
146158
159+ // Merge refs to support both internal and forwarded refs
160+ const setRefs = ( node : HTMLDivElement | null ) => {
161+ // @ts -ignore - TypeScript has issues with ref assignment
162+ dropdownRef . current = node
163+ if ( typeof ref === 'function' ) {
164+ ref ( node )
165+ } else if ( ref ) {
166+ // @ts -ignore - TypeScript has issues with ref assignment
167+ ref . current = node
168+ }
169+ }
170+
147171 return (
148172 < Menu
149173 as = "div"
150174 className = { classNames ( 'relative inline-block' , className ) }
151- ref = { ref }
175+ ref = { setRefs }
152176 data-testid = "dropdown"
153177 { ...other }
154178 >
155179 { ( { open } ) => {
156- if ( open && isSearchable ) {
180+ // Sync internal state with Menu's open state
181+ const menuIsOpen = isMultiSelect ? isOpen : open
182+
183+ if ( menuIsOpen && isSearchable ) {
157184 setTimeout ( ( ) => searchInputRef . current ?. focus ( ) , 50 )
158185 }
159186
160187 return (
161188 < >
162189 { trigger ? (
163- < Menu . Button className = { buttonClassName } > { trigger } </ Menu . Button >
190+ < Menu . Button
191+ className = { buttonClassName }
192+ onClick = { ( ) => isMultiSelect && setIsOpen ( ! isOpen ) }
193+ >
194+ { trigger }
195+ </ Menu . Button >
164196 ) : (
165197 < div className = "relative" >
166198 < Menu . Button
167199 className = { classNames (
168200 'flex items-center justify-between w-full px-4 py-2 text-sm font-medium border rounded-md shadow-sm text-gray-800 bg-white border-gray-200 group hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-cool-gray-100 focus:ring-gray-300 dark:ring-gray-500 dark:bg-gray-800 dark:text-gray-200 dark:border-gray-500 dark:hover:bg-gray-700' ,
169201 buttonClassName
170202 ) }
203+ onClick = { ( ) => isMultiSelect && setIsOpen ( ! isOpen ) }
171204 >
172205 { displayLabel }
173206 < svg
@@ -208,7 +241,7 @@ export const Dropdown = forwardRef<HTMLDivElement, Props>(function Dropdown(
208241 </ div >
209242 ) }
210243 < Transition
211- show = { open }
244+ show = { menuIsOpen }
212245 enter = "transition ease-out duration-300"
213246 enterFrom = "transform opacity-0 scale-95"
214247 enterTo = "transform opacity-100 scale-100"
@@ -218,6 +251,7 @@ export const Dropdown = forwardRef<HTMLDivElement, Props>(function Dropdown(
218251 className = "min-w-sm"
219252 >
220253 < Menu . Items
254+ static = { isMultiSelect }
221255 data-testid = "dropdown-items"
222256 className = { classNames (
223257 'absolute shadow-sm z-10 bg-white origin-top-right dark:bg-gray-800 border divide-y rounded-md outline-none border-cool-gray-200 divide-cool-gray-100 dark:divide-gray-400 dark:border-gray-700' ,
0 commit comments