@@ -105,18 +105,99 @@ const getDropdownAttributes = (option, item) => {
105105const handleClickOutside = (e ) => {
106106 closeDropdowns ();
107107};
108+
109+
110+ const moveToNextButton = (e ) => {
111+ const currentButton = e .target ;
112+ const nextButton = e .target .closest (' .toolbar-item-ctn' ).nextElementSibling ;
113+ if (nextButton) {
114+ currentButton .setAttribute (' tabindex' , ' -1' );
115+ nextButton .setAttribute (' tabindex' , ' 0' );
116+ nextButton .focus ();
117+ }
118+ };
119+
120+ const moveToPreviousButton = (e ) => {
121+ const currentButton = e .target ;
122+ const previousButton = e .target .closest (' .toolbar-item-ctn' ).previousElementSibling ;
123+ if (previousButton) {
124+ currentButton .setAttribute (' tabindex' , ' -1' );
125+ previousButton .setAttribute (' tabindex' , ' 0' );
126+ previousButton .focus ();
127+ }
128+ };
129+
130+ const moveToNextButtonGroup = (e ) => {
131+ const nextButtonGroup = e .target .closest (' .button-group' ).nextElementSibling ;
132+ if (nextButtonGroup) {
133+ nextButtonGroup .setAttribute (' tabindex' , ' 0' );
134+ nextButtonGroup .focus ();
135+ }
136+ };
137+
138+ const moveToPreviousButtonGroup = (e ) => {
139+ const previousButtonGroup = e .target .closest (' .button-group' ).previousElementSibling ;
140+ if (previousButtonGroup) {
141+ previousButtonGroup .setAttribute (' tabindex' , ' 0' );
142+ previousButtonGroup .focus ();
143+ }
144+ };
145+
146+ // Implement keyboard navigation using Roving Tabindex
147+ // https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_roving_tabindex
148+ // Set tabindex to 0 for the current focused button
149+ // Set tabindex to -1 for all other buttons
150+ const handleKeyDown = (e ) => {
151+ e .preventDefault ();
152+ e .stopPropagation ();
153+
154+ switch (e .key ) {
155+ case ' ArrowRight' :
156+ moveToNextButton (e);
157+ break ;
158+ case ' ArrowLeft' :
159+ moveToPreviousButton (e);
160+ break ;
161+ case ' Tab' :
162+ if (e .shiftKey ) {
163+ moveToPreviousButtonGroup (e);
164+ } else {
165+ moveToNextButtonGroup (e);
166+ }
167+ break ;
168+ default :
169+ break ;
170+ }
171+ };
172+ const handleFocus = (e ) => {
173+ // Set the focus to the first button inside the button group that is not disabled
174+ const firstButton = e .target .closest (' .button-group' ).querySelector (' .toolbar-item-ctn:not(.disabled)' );
175+ if (firstButton) {
176+ // Force focus on the first button
177+ firstButton .focus ();
178+ }
179+ };
108180< / script>
109181
110182< template>
111183 < div
112184 : style= " getPositionStyle"
113185 class = " button-group"
114186 role= " group"
187+ @keydown= " handleKeyDown"
188+ @focus= " handleFocus"
115189 >
116- < div v- for = " item in toolbarItems" : key= " item.id.value" : class = " {
190+ < div
191+ v- for = " (item, index) in toolbarItems"
192+ : key= " item.id.value"
193+ : class = " {
117194 narrow: item.isNarrow.value,
118195 wide: item.isWide.value,
119- }" class = " toolbar-item-ctn" >
196+ disabled: item.disabled.value,
197+ }"
198+ class = " toolbar-item-ctn"
199+ : tabindex= " index === 0 ? 0 : -1"
200+ >
120201 <!-- toolbar separator -->
121202 < ToolbarSeparator v- if = " isSeparator(item)" style= " width: 20px" / >
122203
@@ -140,8 +221,12 @@ const handleClickOutside = (e) => {
140221 >
141222 < n- tooltip trigger= " hover" : disabled= " !item.tooltip?.value" >
142223 < template #trigger>
143- < ToolbarButton : toolbar- item= " item" @textSubmit= " handleToolbarButtonTextSubmit(item, $event)"
144- @buttonClick= " handleToolbarButtonClick(item)" / >
224+ < ToolbarButton
225+ : toolbar- item= " item"
226+ : disabled= " item.disabled.value"
227+ @textSubmit= " handleToolbarButtonTextSubmit(item, $event)"
228+ @buttonClick= " handleToolbarButtonClick(item)"
229+ / >
145230 < / template>
146231 < div>
147232 {{ item .tooltip }}
0 commit comments