@@ -14,6 +14,8 @@ class Picker {
14
14
container : HTMLElement ;
15
15
label : HTMLElement ;
16
16
17
+ hasRovingTabindex : boolean = false ;
18
+
17
19
constructor ( select : HTMLSelectElement ) {
18
20
this . select = select ;
19
21
this . container = document . createElement ( 'span' ) ;
@@ -22,6 +24,11 @@ class Picker {
22
24
// @ts -expect-error Fix me later
23
25
this . select . parentNode . insertBefore ( this . container , this . select ) ;
24
26
27
+ // Set tabIndex for the first item in the toolbar
28
+ this . hasRovingTabindex = this . container . closest ( '.roving-tabindex' ) !== null ;
29
+ this . setTabIndexes ( ) ;
30
+
31
+
25
32
this . label . addEventListener ( 'mousedown' , ( ) => {
26
33
this . togglePicker ( ) ;
27
34
} ) ;
@@ -34,9 +41,6 @@ class Picker {
34
41
this . escape ( ) ;
35
42
event . preventDefault ( ) ;
36
43
break ;
37
- case 'ArrowLeft' :
38
- case 'ArrowRight' :
39
- this . toggleTabIndex ( ) ;
40
44
default :
41
45
}
42
46
} ) ;
@@ -51,10 +55,6 @@ class Picker {
51
55
toggleAriaAttribute ( this . options , 'aria-hidden' ) ;
52
56
}
53
57
54
- toggleTabIndex ( ) {
55
- this . label . tabIndex = this . label . tabIndex === 0 ? - 1 : 0 ;
56
- }
57
-
58
58
buildItem ( option : HTMLOptionElement ) {
59
59
const item = document . createElement ( 'span' ) ;
60
60
// @ts -expect-error
@@ -93,16 +93,71 @@ class Picker {
93
93
label . classList . add ( 'ql-picker-label' ) ;
94
94
label . innerHTML = DropdownIcon ;
95
95
96
- // TODO: @cofi set all tabindex to -1 initially and then per JS set first one to 0. Then per keyboard right/left navigation set the next/prev to 0 and the rest to -1
96
+ // Set tabIndex to -1 by default to prevent focus
97
97
// @ts -expect-error
98
98
label . tabIndex = '-1' ;
99
+ label . addEventListener ( 'keydown' , ( event ) => {
100
+ this . handleKeyboardEvent ( event ) ;
101
+ } ) ;
102
+
99
103
100
104
label . setAttribute ( 'role' , 'button' ) ;
101
105
label . setAttribute ( 'aria-expanded' , 'false' ) ;
102
106
this . container . appendChild ( label ) ;
103
107
return label ;
104
108
}
105
109
110
+ setTabIndexes ( ) {
111
+ const toolbar = this . select . closest ( '.ql-toolbar' ) ;
112
+ if ( ! toolbar ) return ;
113
+ const items = Array . from ( toolbar . querySelectorAll ( '.ql-picker .ql-picker-label, .ql-toolbar button' ) ) ;
114
+
115
+ if ( this . hasRovingTabindex ) {
116
+ if ( items [ 0 ] === this . label ) {
117
+ items [ 0 ] . setAttribute ( 'tabindex' , '0' )
118
+ }
119
+
120
+ } else {
121
+ items . forEach ( ( item ) => {
122
+ item . setAttribute ( 'tabindex' , '0' ) ;
123
+ } ) ;
124
+ }
125
+ }
126
+
127
+ handleKeyboardEvent ( e : KeyboardEvent ) {
128
+ if ( ! this . hasRovingTabindex ) return ;
129
+ var target = e . currentTarget ;
130
+ if ( ! target ) return ;
131
+
132
+ switch ( e . key ) {
133
+ case 'ArrowLeft' :
134
+ case 'ArrowRight' :
135
+ this . updateTabIndexes ( target , e . key ) ;
136
+ break ;
137
+ }
138
+ }
139
+
140
+ updateTabIndexes ( target : EventTarget , key : string ) {
141
+ this . label . setAttribute ( 'tabindex' , '-1' ) ;
142
+
143
+ const toolbar = this . container . closest ( '.ql-toolbar' ) ;
144
+ if ( ! toolbar ) return ;
145
+ const items = Array . from ( toolbar . querySelectorAll ( '.ql-picker .ql-picker-label, .ql-toolbar button' ) ) ;
146
+ const currentIndex = items . indexOf ( target as HTMLElement ) ;
147
+ let newIndex ;
148
+
149
+ if ( key === 'ArrowLeft' ) {
150
+ newIndex = ( currentIndex - 1 + items . length ) % items . length ;
151
+ } else if ( key === 'ArrowRight' ) {
152
+ newIndex = ( currentIndex + 1 ) % items . length ;
153
+ }
154
+
155
+ if ( ! newIndex ) return ;
156
+
157
+ items [ newIndex ] . setAttribute ( 'tabindex' , '0' ) ;
158
+ ( items [ newIndex ] as HTMLElement ) . focus ( ) ;
159
+ }
160
+
106
161
buildOptions ( ) {
107
162
const options = document . createElement ( 'span' ) ;
108
163
options . classList . add ( 'ql-picker-options' ) ;
@@ -190,7 +245,7 @@ class Picker {
190
245
const item =
191
246
// @ts -expect-error Fix me later
192
247
this . container . querySelector ( '.ql-picker-options' ) . children [
193
- this . select . selectedIndex
248
+ this . select . selectedIndex
194
249
] ;
195
250
option = this . select . options [ this . select . selectedIndex ] ;
196
251
// @ts -expect-error
0 commit comments