From cd1452eb76191f35b0db52cdffc27380d3885648 Mon Sep 17 00:00:00 2001 From: NikG Date: Mon, 7 Nov 2022 12:53:39 +0200 Subject: [PATCH] Enable up/down keys for navigating the ULS menu --- css/jquery.uls.lcd.css | 4 ++ src/jquery.uls.languagefilter.js | 12 ++++++ src/jquery.uls.lcd.js | 73 ++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/css/jquery.uls.lcd.css b/css/jquery.uls.lcd.css index 4b04e13..ca0ca00 100644 --- a/css/jquery.uls.lcd.css +++ b/css/jquery.uls.lcd.css @@ -138,3 +138,7 @@ bottom: 0; left: 0; } + +.language-option--highlighted { + background-color: #eaeff7; +} diff --git a/src/jquery.uls.languagefilter.js b/src/jquery.uls.languagefilter.js index c87c680..c9d8005 100644 --- a/src/jquery.uls.languagefilter.js +++ b/src/jquery.uls.languagefilter.js @@ -115,6 +115,14 @@ this.options.onSelect( query, e ); } + break; + case 38: // arrow up + this.options.lcd.navigateUp(); + + break; + case 40: // arrow down + this.options.lcd.navigateDown(); + break; } }, @@ -163,6 +171,10 @@ results = [], query = ( this.$element.val() || '' ).trim().toLowerCase(); + // Reset the keyboard navigation index inside LanguageCategoryDisplay (lcd) + // before re-rendering the language options + this.options.lcd.resetNavigationIndex(); + if ( query === '' ) { this.options.lcd.setGroupByRegionOverride( null ); this.resultHandler( query, languages ); diff --git a/src/jquery.uls.lcd.js b/src/jquery.uls.lcd.js index 47f0820..a6ce325 100644 --- a/src/jquery.uls.lcd.js +++ b/src/jquery.uls.lcd.js @@ -59,6 +59,11 @@ this.$cachedQuicklist = null; this.groupByRegionOverride = null; + // The index of the language option that is currently visited using arrow key navigation + // Can take values in the [0, languageOptionListItemsLength - 1] range for top to bottom navigation, + // or in the [-1, -languageOptionListItemsLength + 1] range for bottom to top navigation. + this.navigationIndex = null + this.render(); this.listen(); } @@ -66,6 +71,74 @@ LanguageCategoryDisplay.prototype = { constructor: LanguageCategoryDisplay, + /** + * Returns a jQuery object containing a collection of all the + * language option
  • elements + * @return {jQuery} + */ + getLanguageOptionListItems() { + return this.$element.find( 'li[data-code]' ); + }, + + /** + * Increases the keyboard navigation index by one and applies a specific + * class to the n-th language option
  • element (where n = navigation index) + * Currently used as event handler for the arrow down 'keydown' event, inside + * LanguageFilter. + */ + navigateDown: function () { + // We support navigation starting both from the top and the bottom of the language list. + // The navigation should stop when the last language option is already highlighted (for top to bottom navigation). + // For top to bottom navigation, that happens when navigation index is equal to languageOptionListItemsLength - 1. + // For bottom to top navigation, that happens when navigation index is equal to -1. + if ( this.navigationIndex === this.getLanguageOptionListItems().length - 1 || this.navigationIndex === -1 ) { + return; + } + + if ( this.navigationIndex === null ) { + this.navigationIndex = 0 + } else { + this.navigationIndex++; + } + this.highlightLanguageOption(); + }, + + /** + * Decreases the keyboard navigation index by one and applies a specific + * class to the n-th language option
  • element (where n = navigation index) + * Currently used as event handler for the arrow down 'keydown' event, inside + * LanguageFilter. + */ + navigateUp: function () { + // We support navigation starting both from the top and the bottom of the language list. + // The navigation should stop when the first language option is already highlighted (for bottom to top navigation). + // For top to bottom navigation, that happens when navigation index is equal to 0. + // For bottom to top navigation, that happens when navigation index is equal to -languageOptionListItemsLength + 1. + if ( this.navigationIndex === 0 || this.navigationIndex === -this.getLanguageOptionListItems().length + 1 ) { + return; + } + + this.navigationIndex-- + this.highlightLanguageOption(); + }, + + /** + * Adds a specific class ("language-option--highlighted") only to the n-th + * language option
  • element (where n = navigation index) + */ + highlightLanguageOption() { + this.getLanguageOptionListItems().removeClass( 'language-option--highlighted' ); + this.getLanguageOptionListItems().eq( this.navigationIndex ).addClass( 'language-option--highlighted' ); + }, + + /** + * Resets the navigation index to null. + * Currently used inside LanguageFilter search method, to reset the keyboard navigation + */ + resetNavigationIndex: function () { + this.navigationIndex = null; + }, + /** * Adds language to the language list. *