diff --git a/src/components/Select.vue b/src/components/Select.vue index 222b4117f..60c4e4602 100644 --- a/src/components/Select.vue +++ b/src/components/Select.vue @@ -108,10 +108,11 @@ isOptionDeselectable(option) && index === typeAheadPointer, 'vs__dropdown-option--selected': isOptionSelected(option), 'vs__dropdown-option--highlight': index === typeAheadPointer, + 'vs__dropdown-option--kb-focus': hasKeyboardFocusBorder(index), 'vs__dropdown-option--disabled': !selectable(option), }" :aria-selected="optionAriaSelected(option)" - @mouseover="selectable(option) ? (typeAheadPointer = index) : null" + @mousemove="onMouseMove(option, index)" @click.prevent.stop="selectable(option) ? select(option) : null" > @@ -661,6 +662,15 @@ export default { }, }, + /** + * Display a visible border around dropdown options + * which have keyboard focus. + */ + keyboardFocusBorder: { + type: Boolean, + default: false, + }, + /** * A unique identifier used to generate IDs in HTML. * Must be unique for every instance of the component. @@ -676,6 +686,7 @@ export default { search: '', open: false, isComposing: false, + isKeyboardNavigation: false, pushedTags: [], // eslint-disable-next-line vue/no-reserved-keys _value: [], // Internal value managed by Vue Select if no `value` prop is passed @@ -1135,6 +1146,19 @@ export default { return this.isOptionSelected(option) && this.deselectFromDropdown }, + /** + * Check if the option at the given index should display a + * keyboard focus border. + * @param {Number} index + * @return {Boolean} + */ + hasKeyboardFocusBorder(index) { + if (this.keyboardFocusBorder && this.isKeyboardNavigation) { + return index === this.typeAheadPointer + } + return false + }, + /** * Determine if two option objects are matching. * @@ -1324,6 +1348,20 @@ export default { this.mousedown = false }, + /** + * Event-Handler for option mousemove + * @param {Object|String} option + * @param {Number} index + * @return {void} + */ + onMouseMove(option, index) { + this.isKeyboardNavigation = false + if (!this.selectable(option)) { + return + } + this.typeAheadPointer = index + }, + /** * Search KeyBoardEvent handler. * @param {KeyboardEvent} e @@ -1349,6 +1387,7 @@ export default { // up.prevent 38: (e) => { e.preventDefault() + this.isKeyboardNavigation = true if (!this.open) { this.open = true return @@ -1358,6 +1397,7 @@ export default { // down.prevent 40: (e) => { e.preventDefault() + this.isKeyboardNavigation = true if (!this.open) { this.open = true return diff --git a/src/css/global/variables.css b/src/css/global/variables.css index 8847e1505..343971085 100644 --- a/src/css/global/variables.css +++ b/src/css/global/variables.css @@ -54,9 +54,12 @@ --vs-dropdown-option-padding: 3px 20px; /* Active State */ - --vs-dropdown-option--active-bg: #5897fb; + --vs-dropdown-option--active-bg: #136cfb; --vs-dropdown-option--active-color: #fff; + /* Keyboard Focus State */ + --vs-dropdown-option--kb-focus-box-shadow: inset 0px 0px 0px 2px #949494; + /* Deselect State */ --vs-dropdown-option--deselect-bg: #fb5858; --vs-dropdown-option--deselect-color: #fff; diff --git a/src/css/modules/dropdown-option.css b/src/css/modules/dropdown-option.css index 0c16db462..e21a70bb8 100644 --- a/src/css/modules/dropdown-option.css +++ b/src/css/modules/dropdown-option.css @@ -14,6 +14,10 @@ color: var(--vs-dropdown-option--active-color); } +.vs__dropdown-option--kb-focus { + box-shadow: var(--vs-dropdown-option--kb-focus-box-shadow); +} + .vs__dropdown-option--deselect { background: var(--vs-dropdown-option--deselect-bg); color: var(--vs-dropdown-option--deselect-color);