Added keyboard-based navigation within dropdowns
authorAlexander Ebert <ebert@woltlab.com>
Wed, 24 Oct 2012 01:58:25 +0000 (03:58 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 24 Oct 2012 01:58:25 +0000 (03:58 +0200)
Currently not all dropdowns properly support this, as they override the _createListItem() method which is responsible for counting the amount of selectable items.

See #797

wcfsetup/install/files/acp/js/WCF.ACP.js
wcfsetup/install/files/js/WCF.js
wcfsetup/install/files/style/dropdown.less

index fee364e95a0861706dacada53ab4b7c2c68278e9..c30e8398e61c55d26634a1571505e49a7a2c033c 100644 (file)
@@ -1004,6 +1004,23 @@ WCF.ACP.Search = WCF.Search.Base.extend({
                        var $item = resultList.items[$i];
                        
                        $('<li><a href="' + $item.link + '">' + $item.title + '</a></li>').appendTo(this._list);
+                       
+                       this._itemCount++;
                }
+       },
+       
+       /**
+        * @see WCF.Search.Base._highlightSelectedElement()
+        */
+       _highlightSelectedElement: function() {
+               this._list.find('li').removeClass('dropdownNavigationItem');
+               this._list.find('li:not(.dropdownDivider):not(.dropdownText)').eq(this._itemIndex).addClass('dropdownNavigationItem');
+       },
+       
+       /**
+        * @see WCF.Search.Base._selectElement()
+        */
+       _selectElement: function(event) {
+               window.location = this._list.find('li.dropdownNavigationItem > a').attr('href');
        }
 });
index 1d4a45edbb7f57da9ac4550c50d64ab90e5df25f..0d4cc98ff2191e6c58fb216a6b360605cb4eef18 100755 (executable)
@@ -4321,6 +4321,18 @@ WCF.Search.Base = Class.extend({
         */
        _excludedSearchValues: [],
        
+       /**
+        * count of available results
+        * @var integer
+        */
+       _itemCount: 0,
+       
+       /**
+        * item index, -1 if none is selected
+        * @var integer
+        */
+       _itemIndex: -1,
+       
        /**
         * result list
         * @var jQuery
@@ -4382,6 +4394,9 @@ WCF.Search.Base = Class.extend({
                this._commaSeperated = (commaSeperated) ? true : false;
                this._oldSearchString = [ ];
                
+               this._itemCount = 0;
+               this._itemIndex = -1;
+               
                this._proxy = new WCF.Action.Proxy({
                        success: $.proxy(this._success, this)
                });
@@ -4397,6 +4412,28 @@ WCF.Search.Base = Class.extend({
         * @param       object          event
         */
        _keyUp: function(event) {
+               // handle arrow keys and return key
+               switch (event.which) {
+                       case 37: // arrow-left
+                       case 39: // arrow-right
+                               return;
+                       break;
+                       
+                       case 38: // arrow up
+                               this._selectPreviousItem();
+                               return;
+                       break;
+                       
+                       case 40: // arrow down
+                               this._selectNextItem();
+                               return;
+                       break;
+                       
+                       case 13: // return key
+                               return this._selectElement(event);
+                       break;
+               }
+               
                var $content = this._getSearchString(event);
                if ($content === '') {
                        this._clearList(true);
@@ -4422,6 +4459,63 @@ WCF.Search.Base = Class.extend({
                }
        },
        
+       /**
+        * Selects the next item in list.
+        */
+       _selectNextItem: function() {
+               if (this._itemCount === 0) {
+                       return;
+               }
+               
+               // remove previous marking
+               this._itemIndex++;
+               if (this._itemIndex === this._itemCount) {
+                       this._itemIndex = 0;
+               }
+               
+               this._highlightSelectedElement();
+       },
+       
+       /**
+        * Selects the previous item in list.
+        */
+       _selectPreviousItem: function() {
+               if (this._itemCount === 0) {
+                       return;
+               }
+               
+               this._itemIndex--;
+               if (this._itemIndex === -1) {
+                       this._itemIndex = this._itemCount - 1;
+               }
+               
+               this._highlightSelectedElement();
+       },
+       
+       /**
+        * Highlights the active item.
+        */
+       _highlightSelectedElement: function() {
+               this._list.find('li').removeClass('dropdownNavigationItem');
+               this._list.find('li:eq(' + this._itemIndex + ')').addClass('dropdownNavigationItem');
+       },
+       
+       /**
+        * Selects the active item by pressing the return key.
+        * 
+        * @param       object          event
+        * @return      boolean
+        */
+       _selectElement: function(event) {
+               if (this._itemCount === 0) {
+                       return true;
+               }
+               
+               this._list.find('li.dropdownNavigationItem').trigger('click');
+               
+               return false;
+       },
+       
        /**
         * Returns search string.
         * 
@@ -4510,6 +4604,8 @@ WCF.Search.Base = Class.extend({
                var $listItem = $('<li><span>' + item.label + '</span></li>').appendTo(this._list);
                $listItem.data('objectID', item.objectID).data('label', item.label).click($.proxy(this._executeCallback, this));
                
+               this._itemCount++;
+               
                return $listItem;
        },
        
@@ -4567,6 +4663,10 @@ WCF.Search.Base = Class.extend({
                this._list.parent().removeClass('dropdownOpen').end().empty();
                
                WCF.CloseOverlayHandler.removeCallback('WCF.Search.Base');
+               
+               // reset item navigation
+               this._itemCount = 0;
+               this._itemIndex = -1;
        },
        
        /**
index 10cbd08cc7fa770da37de179e531355ed2a2d6dc..c9ae50634339163d960e06050ca4c34c740d27f5 100644 (file)
                        display: block;
                        
                        &:hover:not(.dropdownDivider):not(.dropdownList):not(.dropdownText),
-                       &.dropdownList > li:hover:not(.dropdownDivider) {
+                       &.dropdownList > li:hover:not(.dropdownDivider),
+                       &.dropdownNavigationItem {
                                background-color: @wcfDropdownHoverBackgroundColor;
                        }