@@ -87,10 +87,10 @@ export function MultiSelectDropdown({ values, onChange, options, className, plac
8787 < span className = "text-muted-foreground font-medium" > { placeholder || "Select labels..." } </ span >
8888 ) : (
8989 selectedOptions . map ( ( option ) => {
90- // For "Any" options, show the full label. For specific values, show key:value
91- const displayLabel = option . value . endsWith ( ':*' )
92- ? option . label
93- : ` ${ option . group } : ${ option . label } ` ;
90+ // Show group: label for all options
91+ const displayLabel = option . group
92+ ? ` ${ option . group } : ${ option . label } `
93+ : option . label ;
9494
9595 return (
9696 < Badge
@@ -122,34 +122,68 @@ export function MultiSelectDropdown({ values, onChange, options, className, plac
122122 { isOpen && (
123123 < div className = "absolute z-50 mt-1 w-full rounded-md border bg-popover shadow-lg" >
124124 < div className = "max-h-80 overflow-auto p-1" >
125- { Object . entries ( groupedOptions ) . map ( ( [ groupName , groupOptions ] ) => (
126- < div key = { groupName } >
127- < div className = "px-2 py-1.5 text-[11px] font-semibold text-muted-foreground uppercase tracking-wider" >
128- { groupName }
129- </ div >
130- { groupOptions . map ( ( option ) => (
125+ { Object . entries ( groupedOptions ) . map ( ( [ groupName , groupOptions ] ) => {
126+ const groupValues = groupOptions . map ( opt => opt . value ) ;
127+ const allGroupSelected = groupValues . every ( val => values . includes ( val ) ) ;
128+
129+ const toggleGroupAll = ( ) => {
130+ if ( allGroupSelected ) {
131+ // Deselect all in this group
132+ onChange ( values . filter ( v => ! groupValues . includes ( v ) ) ) ;
133+ } else {
134+ // Select all in this group
135+ const newValues = [ ...values ] ;
136+ groupValues . forEach ( v => {
137+ if ( ! newValues . includes ( v ) ) {
138+ newValues . push ( v ) ;
139+ }
140+ } ) ;
141+ onChange ( newValues ) ;
142+ }
143+ } ;
144+
145+ return (
146+ < div key = { groupName } >
131147 < button
132- key = { option . value }
133148 type = "button"
134- onClick = { ( ) => toggleOption ( option . value ) }
135- className = { cn (
136- "w-full rounded-sm px-2 py-1.5 pl-6 text-[13px] text-left cursor-pointer transition-colors flex items-center gap-2" ,
137- "hover:bg-accent hover:text-accent-foreground"
138- ) }
149+ onClick = { toggleGroupAll }
150+ className = "flex items-center gap-2 w-full px-2 py-1.5 hover:bg-accent/50 transition-colors rounded-sm"
151+ title = { allGroupSelected ? 'Deselect all' : 'Select all' }
139152 >
140153 < input
141154 type = "checkbox"
142- checked = { values . includes ( option . value ) }
143- onChange = { ( ) => { } }
155+ checked = { allGroupSelected }
156+ readOnly
144157 className = "h-3.5 w-3.5 rounded border-gray-300"
145158 />
146- < span className = { cn ( values . includes ( option . value ) && " font-medium" ) } >
147- { option . label }
148- </ span >
159+ < div className = "text-[11px] font-semibold text-muted-foreground uppercase tracking-wider" >
160+ { groupName }
161+ </ div >
149162 </ button >
150- ) ) }
151- </ div >
152- ) ) }
163+ { groupOptions . map ( ( option ) => (
164+ < button
165+ key = { option . value }
166+ type = "button"
167+ onClick = { ( ) => toggleOption ( option . value ) }
168+ className = { cn (
169+ "w-full rounded-sm px-2 py-1.5 pl-6 text-[13px] text-left cursor-pointer transition-colors flex items-center gap-2" ,
170+ "hover:bg-accent hover:text-accent-foreground"
171+ ) }
172+ >
173+ < input
174+ type = "checkbox"
175+ checked = { values . includes ( option . value ) }
176+ onChange = { ( ) => { } }
177+ className = "h-3.5 w-3.5 rounded border-gray-300"
178+ />
179+ < span className = { cn ( values . includes ( option . value ) && "font-medium" ) } >
180+ { option . label }
181+ </ span >
182+ </ button >
183+ ) ) }
184+ </ div >
185+ ) ;
186+ } ) }
153187 </ div >
154188 </ div >
155189 ) }
0 commit comments