From 164ac6879ad6317bd6b10b3cfdd809c29739b06e Mon Sep 17 00:00:00 2001 From: Christopher Ng Date: Thu, 12 Jan 2023 18:59:42 -0800 Subject: [PATCH] Handle keyboard focus Signed-off-by: Christopher Ng --- src/components/Select.vue | 42 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/components/Select.vue b/src/components/Select.vue index 222b4117..60c4e460 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