11import { ChevronDown , X } from "lucide-react" ;
2+ import { useRef } from "react" ;
23
34function getSingleSelectedLabel < T > (
45 options : DropdownOption < T > [ ] ,
@@ -39,7 +40,7 @@ const Dropdown = <T,>(props: DropdownProps<T>) => {
3940 className = "" ,
4041 onResetValue,
4142 } = props ;
42-
43+ const dropdownRef = useRef < HTMLDivElement > ( null ) ;
4344 const hasCustomResetValue = "onResetValue" in props ;
4445
4546 const isSelected = ( val : T ) =>
@@ -53,8 +54,31 @@ const Dropdown = <T,>(props: DropdownProps<T>) => {
5354 ? Array . isArray ( value ) && value . length > 0
5455 : value !== null && value !== undefined ;
5556
57+ const handleDropdownClick = ( event : React . MouseEvent < HTMLButtonElement > ) => {
58+ event . stopPropagation ( ) ;
59+ onChange ( onResetValue ?? ( multiple ? [ ] : null ) ) ;
60+ dropdownRef . current ?. blur ( ) ;
61+ } ;
62+
63+ const handleOptionClick = (
64+ event : React . MouseEvent < HTMLButtonElement > ,
65+ option : DropdownOption < T >
66+ ) => {
67+ if ( multiple ) {
68+ const current = Array . isArray ( value ) ? value : [ ] ;
69+ const exists = current . includes ( option . value ) ;
70+ const updated = exists
71+ ? current . filter ( ( v ) => v !== option . value )
72+ : [ ...current , option . value ] ;
73+ onChange ( updated ) ;
74+ } else {
75+ onChange ( option . value ) ;
76+ dropdownRef . current ?. blur ( ) ;
77+ }
78+ } ;
79+
5680 return (
57- < div className = { `dropdown ${ className } ` } tabIndex = { 0 } >
81+ < div className = { `dropdown ${ className } ` } tabIndex = { 0 } ref = { dropdownRef } >
5882 { label && < div className = "mb-1 text-xs font-semibold" > { label } </ div > }
5983 < div className = "btn bg-base-100 w-full justify-between" >
6084 < span className = "flex gap-1 flex-wrap items-center font-normal" >
@@ -82,11 +106,7 @@ const Dropdown = <T,>(props: DropdownProps<T>) => {
82106 < button
83107 type = "button"
84108 className = "inline-flex p-2"
85- onClick = { ( e ) => {
86- e . stopPropagation ( ) ;
87- onChange ( onResetValue ?? ( multiple ? [ ] : null ) ) ;
88- ( e . currentTarget . closest ( ".dropdown" ) as HTMLElement ) ?. blur ( ) ;
89- } }
109+ onClick = { handleDropdownClick }
90110 >
91111 < X className = "w-4 h-4 text-gray-400 hover:text-neutral cursor-pointer" />
92112 </ button >
@@ -99,9 +119,9 @@ const Dropdown = <T,>(props: DropdownProps<T>) => {
99119 { ! multiple && hasCustomResetValue && (
100120 < li key = "none" >
101121 < button
102- onClick = { ( e ) => {
122+ onClick = { ( ) => {
103123 onChange ( null ) ;
104- ( e . currentTarget . closest ( ".dropdown" ) as HTMLElement ) ?. blur ( ) ;
124+ dropdownRef . current ?. blur ( ) ;
105125 } }
106126 className = { value === null || value === undefined ? "active" : "" }
107127 >
@@ -111,29 +131,19 @@ const Dropdown = <T,>(props: DropdownProps<T>) => {
111131 </ button >
112132 </ li >
113133 ) }
114- { options . map ( ( opt ) => (
115- < li key = { String ( opt . value ) } >
134+ { options . map ( ( option ) => (
135+ < li key = { String ( option . value ) } >
116136 < button
117- onClick = { ( e ) => {
118- if ( multiple ) {
119- const current = Array . isArray ( value ) ? value : [ ] ;
120- const exists = current . includes ( opt . value ) ;
121- const updated = exists
122- ? current . filter ( ( v ) => v !== opt . value )
123- : [ ...current , opt . value ] ;
124- onChange ( updated ) ;
125- } else {
126- onChange ( opt . value ) ;
127- ( e . currentTarget . closest ( ".dropdown" ) as HTMLElement ) ?. blur ( ) ;
128- }
129- } }
137+ onClick = { ( event : React . MouseEvent < HTMLButtonElement > ) =>
138+ handleOptionClick ( event , option )
139+ }
130140 className = { `hover:bg-base-200 ${
131- isSelected ( opt . value ) ? "active bg-base-300" : ""
141+ isSelected ( option . value ) ? "active bg-base-300" : ""
132142 } `}
133143 >
134144 < div className = "flex justify-between items-center" >
135- { opt . label }
136- { isSelected ( opt . value ) && (
145+ { option . label }
146+ { isSelected ( option . value ) && (
137147 < span className = "text-success text-xs ml-2" > ✔</ span >
138148 ) }
139149 </ div >
0 commit comments