1
- import { Dispatch , Fragment , MouseEvent , SetStateAction } from 'react' ;
1
+ import { Dispatch , Fragment , MouseEvent , SetStateAction , useState } from 'react' ;
2
2
3
3
import { useSearchParams } from 'react-router-dom' ;
4
4
@@ -24,6 +24,21 @@ export default function Dropdown({
24
24
const { strings } = useSettings ( ) ;
25
25
const options = state . indexes [ filter ] ;
26
26
const values = state . input [ filter ] ;
27
+ const [ expanded , setExpanded ] = useState < string [ ] > ( [ ] ) ;
28
+
29
+ //handle expand toggle
30
+ const toggleExpanded = (
31
+ e : MouseEvent < HTMLButtonElement > ,
32
+ key : string
33
+ ) => {
34
+ e . preventDefault ( ) ;
35
+ e . stopPropagation ( ) ;
36
+ if ( ! expanded . includes ( key ) ) {
37
+ setExpanded ( expanded . concat ( key ) ) ;
38
+ } else {
39
+ setExpanded ( expanded . filter ( item => item !== key ) ) ;
40
+ }
41
+ }
27
42
28
43
//set filter: pass it up to parent
29
44
const setFilter = (
@@ -38,7 +53,7 @@ export default function Dropdown({
38
53
39
54
if ( value ) {
40
55
const index = currentValues . indexOf ( value ) ;
41
- if ( e . metaKey ) {
56
+ if ( e . metaKey || e . ctrlKey ) {
42
57
if ( index === - 1 ) {
43
58
currentValues . push ( value ) ;
44
59
} else {
@@ -71,26 +86,45 @@ export default function Dropdown({
71
86
72
87
const renderDropdownItem = ( { key, name, slugs, children } : Index ) => (
73
88
< Fragment key = { key } >
74
- < button
89
+ < div
90
+ className = "tsml-dropdown__item"
75
91
// @ts -expect-error TODO
76
92
data-active = { values . includes ( key ) }
77
- onClick = { e => setFilter ( e , filter , key ) }
78
93
>
79
- { name }
80
- < span
81
- aria-label = {
82
- slugs . length === 1
83
- ? strings . match_single
84
- : i18n ( strings . match_multiple , {
85
- count : slugs . length ,
86
- } )
87
- }
88
- >
89
- { slugs . length }
90
- </ span >
91
- </ button >
94
+ < button
95
+ className = "tsml-dropdown__button"
96
+ onClick = { e => setFilter ( e , filter , key ) }
97
+ >
98
+ { name }
99
+ < span
100
+ aria-label = {
101
+ slugs . length === 1
102
+ ? strings . match_single
103
+ : i18n ( strings . match_multiple , {
104
+ count : slugs . length ,
105
+ } )
106
+ }
107
+ >
108
+ { slugs . length }
109
+ </ span >
110
+ </ button >
111
+ { ! ! children ?. length && (
112
+ < button
113
+ className = "tsml-dropdown__expand"
114
+ data-expanded = { expanded . includes ( key ) }
115
+ onClick = { ( e ) => toggleExpanded ( e , key ) }
116
+ aria-label = { expanded . includes ( key ) ? strings . collapse : strings . expand }
117
+ >
118
+ </ button >
119
+ ) }
120
+ </ div >
92
121
{ ! ! children ?. length && (
93
- < div > { children . map ( child => renderDropdownItem ( child ) ) } </ div >
122
+ < div
123
+ className = "tsml-dropdown__children"
124
+ data-expanded = { expanded . includes ( key ) }
125
+ >
126
+ { children . map ( child => renderDropdownItem ( child ) ) }
127
+ </ div >
94
128
) }
95
129
</ Fragment >
96
130
) ;
@@ -117,14 +151,20 @@ export default function Dropdown({
117
151
</ button >
118
152
< div
119
153
aria-labelledby = { filter }
154
+ className = "tsml-dropdown"
120
155
style = { { display : open ? 'block' : 'none' } }
121
156
>
122
- < button
157
+ < div
123
158
data-active = { ! values . length }
124
- onClick = { e => setFilter ( e , filter , undefined ) }
125
- >
126
- { defaultValue }
127
- </ button >
159
+ className = "tsml-dropdown__item"
160
+ >
161
+ < button
162
+ className = "tsml-dropdown__button"
163
+ onClick = { e => setFilter ( e , filter , undefined ) }
164
+ >
165
+ { defaultValue }
166
+ </ button >
167
+ </ div >
128
168
{ [
129
169
options
130
170
?. filter ( option =>
0 commit comments