Skip to content

Commit f54e427

Browse files
authored
fix(b-button-toolbar): allow focus to leave toolbar by keyboard (#5737)
1 parent c11c237 commit f54e427

File tree

1 file changed

+37
-34
lines changed

1 file changed

+37
-34
lines changed

src/components/button-toolbar/button-toolbar.js

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import Vue from '../../utils/vue'
22
import KeyCodes from '../../utils/key-codes'
3-
import { attemptFocus, isVisible, selectAll } from '../../utils/dom'
3+
import { attemptFocus, contains, isVisible, selectAll } from '../../utils/dom'
44
import normalizeSlotMixin from '../../mixins/normalize-slot'
55

6+
// --- Constants ---
7+
68
const ITEM_SELECTOR = [
79
'.btn:not(.disabled):not([disabled]):not(.dropdown-item)',
810
'.form-control:not(.disabled):not([disabled])',
@@ -11,6 +13,15 @@ const ITEM_SELECTOR = [
1113
'input[type="radio"]:not(.disabled)'
1214
].join(',')
1315

16+
// --- Utility methods ---
17+
18+
const stopEvent = evt => {
19+
evt.preventDefault()
20+
evt.stopPropagation()
21+
}
22+
23+
// --- Main component ---
24+
1425
// @vue/component
1526
export const BButtonToolbar = /*#__PURE__*/ Vue.extend({
1627
name: 'BButtonToolbar',
@@ -26,37 +37,20 @@ export const BButtonToolbar = /*#__PURE__*/ Vue.extend({
2637
}
2738
},
2839
mounted() {
40+
// Pre-set the tabindexes if the markup does not include
41+
// `tabindex="-1"` on the toolbar items
2942
if (this.keyNav) {
30-
// Pre-set the tabindexes if the markup does not include tabindex="-1" on the toolbar items
3143
this.getItems()
3244
}
3345
},
3446
methods: {
35-
onFocusin(evt) {
36-
if (evt.target === this.$el) {
37-
evt.preventDefault()
38-
evt.stopPropagation()
39-
this.focusFirst(evt)
40-
}
41-
},
42-
stop(evt) {
43-
evt.preventDefault()
44-
evt.stopPropagation()
45-
},
46-
onKeydown(evt) {
47-
if (!this.keyNav) {
48-
/* istanbul ignore next: should never happen */
49-
return
50-
}
51-
const key = evt.keyCode
52-
const shift = evt.shiftKey
53-
if (key === KeyCodes.UP || key === KeyCodes.LEFT) {
54-
this.stop(evt)
55-
shift ? this.focusFirst(evt) : this.focusPrev(evt)
56-
} else if (key === KeyCodes.DOWN || key === KeyCodes.RIGHT) {
57-
this.stop(evt)
58-
shift ? this.focusLast(evt) : this.focusNext(evt)
59-
}
47+
getItems() {
48+
const items = selectAll(ITEM_SELECTOR, this.$el)
49+
// Ensure `tabindex="-1"` is set on every item
50+
items.forEach(item => {
51+
item.tabIndex = -1
52+
})
53+
return items.filter(el => isVisible(el))
6054
},
6155
focusFirst() {
6256
const items = this.getItems()
@@ -82,13 +76,22 @@ export const BButtonToolbar = /*#__PURE__*/ Vue.extend({
8276
const items = this.getItems().reverse()
8377
attemptFocus(items[0])
8478
},
85-
getItems() {
86-
const items = selectAll(ITEM_SELECTOR, this.$el)
87-
items.forEach(item => {
88-
// Ensure tabfocus is -1 on any new elements
89-
item.tabIndex = -1
90-
})
91-
return items.filter(el => isVisible(el))
79+
onFocusin(evt) {
80+
const { $el } = this
81+
if (evt.target === $el && !contains($el, evt.relatedTarget)) {
82+
stopEvent(evt)
83+
this.focusFirst(evt)
84+
}
85+
},
86+
onKeydown(evt) {
87+
const { keyCode, shiftKey } = evt
88+
if (keyCode === KeyCodes.UP || keyCode === KeyCodes.LEFT) {
89+
stopEvent(evt)
90+
shiftKey ? this.focusFirst(evt) : this.focusPrev(evt)
91+
} else if (keyCode === KeyCodes.DOWN || keyCode === KeyCodes.RIGHT) {
92+
stopEvent(evt)
93+
shiftKey ? this.focusLast(evt) : this.focusNext(evt)
94+
}
9295
}
9396
},
9497
render(h) {

0 commit comments

Comments
 (0)