Permalink
Browse files

fix(dropdowns): Focus dropdown item on hover (#823)

* fix(dropdowns): Remove focusing of headers

* [dropdown] Minor tweaks

* [nav-item-dropdown] Adjustments

* [dropdown]: namespaced classes

* [dropdown.js] add onMouseEnter handler

* [dropdown] focus on hover

* Update dropdown.js

* [dropdown] mouseover event

* [nav-item-dropdown] mouseover event

* Update dropdown.js

* Update dropdown-header.vue

remove tabindex
  • Loading branch information...
tmorehouse committed Aug 12, 2017
1 parent 6891e9f commit 2e863d9296ff409a69c0f8dabcc28153e4efdaf7
Showing with 70 additions and 31 deletions.
  1. +1 −1 lib/components/dropdown-header.vue
  2. +25 −9 lib/components/dropdown.vue
  3. +30 −11 lib/components/nav-item-dropdown.vue
  4. +14 −10 lib/mixins/dropdown.js
@@ -1,5 +1,5 @@
<template>
<component :is="tag" tabindex="-1" class="dropdown-header">
<component :is="tag" class="dropdown-header">
<slot></slot>
</component>
</template>
@@ -1,5 +1,5 @@
<template>
<div :id="id || null" :class="['dropdown', 'btn-group', { dropup, show: visible }]">
<div :id="id || null" :class="dropdownClasses">

<b-button :class="{'dropdown-toggle': !split}"
ref="button"
@@ -28,10 +28,11 @@
<span class="sr-only">{{toggleText}}</span>
</b-button>

<div :class="['dropdown-menu',{'dropdown-menu-right': right, show: visible}]"
<div :class="menuClasses"
ref="menu"
role="menu"
:aria-labelledby="id ? (id + (split ? '__BV_toggle_' : '__BV_button_')) : null"
@mouseover="onMouseOver"
@keyup.esc="onEsc"
@keydown.tab="onTab"
@keydown.up="focusNext($event,true)"
@@ -67,21 +68,36 @@
type: String,
default: null
}
},
computed: {
dropdownClasses() {
return [
'b-dropdown',
'dropdown',
'btn-group',
this.dropup ? 'dropup' : '',
this.visible ? 'show' : ''
];
},
menuClasses() {
return [
'dropdown-menu',
this.right ? 'dropdown-menu-right' : '',
this.visible ? 'show' : ''
];
}
}
};
</script>

<style>
.dropdown-item:focus:not(.active),
.dropdown-item:hover:not(.active),
.dropdown-header:focus:not(.active)
.dropdown-header:hover:not(.active) {
.b-dropdown.dropdown-item:focus:not(.active),
.b-dropdown.dropdown-item:hover:not(.active) {
/* @See https://github.com/twbs/bootstrap/issues/23329 */
box-shadow: inset 0px 0px 400px 110px rgba(0, 0, 0, .09);
}
.dropdown-item:active,
.dropdown-header:active {
.b-dropdown.dropdown-item:active {
box-shadow: initial;
}
</style>
</style>
@@ -1,5 +1,5 @@
<template>
<li :id="id || null" :class="['nav-item','dropdown', {dropup, show: visible}]">
<li :id="id || null" :class="dropdownClasses">

<a :class="['nav-link', dropdownToggle, {disabled}]"
href="#"
@@ -15,10 +15,11 @@
<slot name="button-content"><slot name="text"><span v-html="text"></span></slot></slot>
</a>

<div :class="['dropdown-menu',{'dropdown-menu-right': right, show: visible}]"
<div :class="menuClasses"
role="menu"
ref="menu"
:aria-labelledby="id ? (id + '__BV_button_') : null"
@mouseover="onMouseOver"
@keyup.esc="onEsc"
@keydown.tab="onTab"
@keydown.up="focusNext($event,true)"
@@ -30,15 +31,6 @@
</li>
</template>

<style scoped>
.dropdown-item:focus,
.dropdown-item:hover,
.dropdown-header:focus {
background-color: #eaeaea;
outline: none;
}
</style>

<script>
import { dropdownMixin } from '../mixins';
@@ -47,6 +39,22 @@
computed: {
dropdownToggle() {
return this.noCaret ? '' : 'dropdown-toggle';
},
dropdownClasses() {
return [
'nav-item',
'b-nav-dropdown',
'dropdown',
this.dropup ? 'dropup' : '',
this.visible ? 'show' : ''
];
},
menuClasses() {
return [
'dropdown-menu',
this.right ? 'dropdown-menu-right': '',
this.visible ? 'show' : ''
];
}
},
props: {
@@ -57,3 +65,14 @@
}
};
</script>

<style>
.b-nav-dropdown.dropdown-item:focus:not(.active),
.b-nav-dropdown.dropdown-item:hover:not(.active) {
/* @See https://github.com/twbs/bootstrap/issues/23329 */
box-shadow: inset 0px 0px 400px 110px rgba(0, 0, 0, .09);
}
.b-nav-dropdown.dropdown-item:active {
box-shadow: initial;
}
</style>
@@ -14,8 +14,6 @@ function filterVisible(els) {

// Dropdown item CSS selectors
const ITEM_SELECTOR = '.dropdown-item:not(.disabled):not([disabled])';
const HEADER_SELECTOR = '.dropdown-header';
const ALL_SELECTOR = [ITEM_SELECTOR, HEADER_SELECTOR].join(',');

export default {
mixins: [listenOnRootMixin],
@@ -149,6 +147,16 @@ export default {
this.$nextTick(() => { this.focusToggler() });
}
},
onMouseOver(evt) {
// Focus the item on hover
const item = evt.target;
if (item.classList.contains('dropdown-item')
&& !item.disabled
&& !item.classList.contains('disabled')
&& item.focus) {
item.focus();
}
},
focusNext(e, up) {
if (!this.visible) {
return;
@@ -179,16 +187,12 @@ export default {
}
},
getItems() {
// Get all items and headers
return filterVisible(arrayFrom(this.$refs.menu.querySelectorAll(ALL_SELECTOR)));
// Get all items
return filterVisible(arrayFrom(this.$refs.menu.querySelectorAll(ITEM_SELECTOR)));
},
getFirstItem() {
// Get the first non-header non-disabled item
let item = filterVisible(arrayFrom(this.$refs.menu.querySelectorAll(ITEM_SELECTOR)))[0];
if (!item) {
// If no items then try a header
item = filterVisible(arrayFrom(this.$refs.menu.querySelectorAll(HEADER_SELECTOR)))[0];
}
// Get the first non-disabled item
let item = this.getItems()[0];
return item || null;
},
focusFirstItem() {

0 comments on commit 2e863d9

Please sign in to comment.