Browse Source

Rewrite dropdown keyboard shortcuts to support sub-menus

pull/1698/merge
jlukic 9 years ago
parent
commit
13886b2eb5
2 changed files with 111 additions and 64 deletions
  1. 3
      RELEASE-NOTES.md
  2. 172
      src/definitions/modules/dropdown.js

3
RELEASE-NOTES.md

@ -4,12 +4,15 @@
**Enhancements** **Enhancements**
- **Dropdown** - Keyboard navigation will now allow opening of sub menus with right/left arrow. Enter will open sub-menus on an unselectable category (`allowCategorySelection: false`) as well.
- **API** - Added new behavior `$.api('abort')` which cancels current request - **API** - Added new behavior `$.api('abort')` which cancels current request
- **Search** - Search `onSelect` now recieves JSON object matching currently selected element, you can now programmatically retrieve result JSON using `.search('get result')`. Defaults to current value unless value specified as first parameter. - **Search** - Search `onSelect` now recieves JSON object matching currently selected element, you can now programmatically retrieve result JSON using `.search('get result')`. Defaults to current value unless value specified as first parameter.
- **Search** - Search `onSelect` and `onResultsAdd` can now cancel default actions by returning `false`. - **Search** - Search `onSelect` and `onResultsAdd` can now cancel default actions by returning `false`.
- **Search** - Greatly reduced search delay from `300ms` to `100ms`. Previous request will automatically abort `xhr` when new request made - **Search** - Greatly reduced search delay from `300ms` to `100ms`. Previous request will automatically abort `xhr` when new request made
**Bugs** **Bugs**
- **Dropdown** - Fixed bug where link items would not open in sub-menus due to `event.preventDefault`
- **Dropdown** - Dropdown no longer will not show menu when no `item` are present in menu. Dropdown will now only filter results for `ui search dropdown` #1632 **Thanks PSyton**.
- **List** - `relaxed list` and `very relaxed list` no longer add padding to child menu items - **List** - `relaxed list` and `very relaxed list` no longer add padding to child menu items
- **Button** - Fixes formatting for `disabled button` inside `ui buttons` - **Button** - Fixes formatting for `disabled button` inside `ui buttons`
- **Button** - ``ui vertical basic buttons` now have dividers in default theme - **Button** - ``ui vertical basic buttons` now have dividers in default theme

172
src/definitions/modules/dropdown.js

@ -468,77 +468,114 @@ $.fn.dropdown = function(parameters) {
}, },
keydown: function(event) { keydown: function(event) {
var var
$selectedItem = $item.not(className.filtered).filter('.' + className.selected).eq(0),
$visibleItems = $item.not('.' + className.filtered),
$currentlySelected = $item.not(className.filtered).filter('.' + className.selected).eq(0),
$activeItem = $item.filter('.' + className.active).eq(0),
$selectedItem = ($currentlySelected.length > 0)
? $currentlySelected
: $activeItem,
$visibleItems = ($selectedItem.length > 0)
? $selectedItem.siblings(':not(.' + className.filtered +')').andSelf()
: $menu.children(':not(.' + className.filtered +')'),
$subMenu = $selectedItem.children(selector.menu),
$parentMenu = $selectedItem.closest(selector.menu),
isSubMenuItem = $parentMenu[0] !== $menu[0],
pressedKey = event.which, pressedKey = event.which,
keys = { keys = {
enter : 13,
escape : 27,
upArrow : 38,
downArrow : 40
enter : 13,
escape : 27,
leftArrow : 37,
upArrow : 38,
rightArrow : 39,
downArrow : 40
}, },
selectedClass = className.selected,
currentIndex = $visibleItems.index( $selectedItem ),
hasSelectedItem = ($selectedItem.length > 0),
hasSubMenu = ($subMenu.length> 0),
hasSelectedItem = ($selectedItem.length > 0),
lastVisibleIndex = ($visibleItems.size() - 1),
$nextItem, $nextItem,
newIndex newIndex
; ;
// default to activated choice if no selection present
if(!hasSelectedItem) {
$selectedItem = $item.filter('.' + className.active).eq(0);
hasSelectedItem = ($selectedItem.length > 0);
}
// close shortcuts
if(pressedKey == keys.escape) {
module.verbose('Escape key pressed, closing dropdown');
module.hide();
}
// open menu
if(pressedKey == keys.downArrow) {
module.verbose('Down key pressed, showing dropdown');
module.show();
}
// result shortcuts
// visible menu keyboard shortcuts
if(module.is.visible()) { if(module.is.visible()) {
// enter (select or sub-menu)
if(pressedKey == keys.enter && hasSelectedItem) { if(pressedKey == keys.enter && hasSelectedItem) {
module.verbose('Enter key pressed, choosing selected item');
module.event.item.click.call($selectedItem, event);
if(hasSubMenu && !settings.allowCategorySelection) {
module.verbose('Pressed enter on unselectable category, opening sub menu');
pressedKey = keys.rightArrow;
}
else {
module.verbose('Enter key pressed, choosing selected item');
module.event.item.click.call($selectedItem, event);
}
}
// left arrow (hide sub-menu)
if(pressedKey == keys.leftArrow) {
if(isSubMenuItem) {
module.verbose('Left key pressed, closing sub-menu');
module.animate.hide(false, $parentMenu);
$selectedItem
.removeClass(className.selected)
;
$parentMenu
.closest(selector.item)
.addClass(className.selected)
;
}
event.preventDefault(); event.preventDefault();
return false;
} }
else if(pressedKey == keys.upArrow) {
if(!hasSelectedItem) {
$nextItem = $visibleItems.eq(0);
// right arrow (show sub-menu)
if(pressedKey == keys.rightArrow) {
if(hasSubMenu) {
module.verbose('Right key pressed, opening sub-menu');
module.animate.show(false, $subMenu);
$selectedItem
.removeClass(className.selected)
;
$subMenu
.find(selector.item).eq(0)
.addClass(className.selected)
;
} }
else {
$nextItem = $selectedItem.prevAll(selector.item + ':not(.' + className.filtered + ')').eq(0);
event.preventDefault();
}
// up arrow (traverse menu up)
if(pressedKey == keys.upArrow) {
$nextItem = (hasSelectedItem)
? $selectedItem.prevAll(selector.item + ':not(.' + className.filtered + ')').eq(0)
: $visibleItems.eq(0)
;
if($visibleItems.index( $nextItem ) < 0) {
module.verbose('Up key pressed but reached top of current menu');
return;
} }
if(currentIndex !== 0) {
else {
module.verbose('Up key pressed, changing active item'); module.verbose('Up key pressed, changing active item');
$item
.removeClass(selectedClass)
$selectedItem
.removeClass(className.selected)
; ;
$nextItem $nextItem
.addClass(selectedClass)
.addClass(className.selected)
; ;
module.set.scrollPosition($nextItem); module.set.scrollPosition($nextItem);
} }
event.preventDefault(); event.preventDefault();
} }
else if(pressedKey == keys.downArrow) {
if(!hasSelectedItem) {
$nextItem = $visibleItems.eq(0);
// down arrow (traverse menu down)
if(pressedKey == keys.downArrow) {
$nextItem = (hasSelectedItem)
? $nextItem = $selectedItem.nextAll(selector.item + ':not(.' + className.filtered + ')').eq(0)
: $visibleItems.eq(0)
;
if($nextItem.length === 0) {
module.verbose('Down key pressed but reached bottom of current menu');
return;
} }
else { else {
$nextItem = $selectedItem.nextAll(selector.item + ':not(.' + className.filtered + ')').eq(0);
}
if(currentIndex + 1 < $visibleItems.length ) {
module.verbose('Down key pressed, changing active item'); module.verbose('Down key pressed, changing active item');
$item $item
.removeClass(selectedClass)
.removeClass(className.selected)
; ;
$nextItem $nextItem
.addClass(selectedClass)
.addClass(className.selected)
; ;
module.set.scrollPosition($nextItem); module.set.scrollPosition($nextItem);
} }
@ -546,7 +583,19 @@ $.fn.dropdown = function(parameters) {
} }
} }
else { else {
// enter (open menu)
if(pressedKey == keys.enter) { if(pressedKey == keys.enter) {
module.verbose('Enter key pressed, showing dropdown');
module.show();
}
// escape (close menu)
if(pressedKey == keys.escape) {
module.verbose('Escape key pressed, closing dropdown');
module.hide();
}
// down arrow (open menu)
if(pressedKey == keys.downArrow) {
module.verbose('Down key pressed, showing dropdown');
module.show(); module.show();
} }
} }
@ -584,35 +633,33 @@ $.fn.dropdown = function(parameters) {
item: { item: {
mouseenter: function(event) { mouseenter: function(event) {
var var
$currentMenu = $(this).children(selector.menu),
$otherMenus = $(this).siblings(selector.item).children(selector.menu)
$subMenu = $(this).children(selector.menu),
$otherMenus = $(this).siblings(selector.item).children(selector.menu)
; ;
if( $currentMenu.length > 0 ) {
if( $subMenu.length > 0 ) {
clearTimeout(module.itemTimer); clearTimeout(module.itemTimer);
module.itemTimer = setTimeout(function() { module.itemTimer = setTimeout(function() {
module.verbose('Showing sub-menu', $subMenu);
$.each($otherMenus, function() { $.each($otherMenus, function() {
module.animate.hide(false, $(this)); module.animate.hide(false, $(this));
}); });
module.verbose('Showing sub-menu', $currentMenu);
module.animate.show(false, $currentMenu);
module.animate.show(false, $subMenu);
}, settings.delay.show); }, settings.delay.show);
event.preventDefault(); event.preventDefault();
} }
}, },
mouseleave: function(event) { mouseleave: function(event) {
var var
$currentMenu = $(this).children(selector.menu)
$subMenu = $(this).children(selector.menu)
; ;
if($currentMenu.length > 0) {
if($subMenu.length > 0) {
clearTimeout(module.itemTimer); clearTimeout(module.itemTimer);
module.itemTimer = setTimeout(function() { module.itemTimer = setTimeout(function() {
module.verbose('Hiding sub-menu', $currentMenu);
module.animate.hide(false, $currentMenu);
module.verbose('Hiding sub-menu', $subMenu);
module.animate.hide(false, $subMenu);
}, settings.delay.hide); }, settings.delay.hide);
} }
}, },
click: function (event) { click: function (event) {
var var
$choice = $(this), $choice = $(this),
@ -624,23 +671,20 @@ $.fn.dropdown = function(parameters) {
module.remove.searchTerm(); module.remove.searchTerm();
module.determine.selectAction(text, value); module.determine.selectAction(text, value);
}, },
openingSubMenu = ($subMenu.length > 0),
isSubItem = ($subMenu.find($target).length > 0)
hasSubMenu = ($subMenu.length > 0),
isSubMenuItem = ($subMenu.find($target).length > 0)
; ;
if(isSubItem) {
if(isSubMenuItem) {
return false; return false;
} }
if(!openingSubMenu || settings.allowCategorySelection) {
if(!hasSubMenu || settings.allowCategorySelection) {
callback(); callback();
} }
} }
}, },
resetStyle: function() { resetStyle: function() {
$(this).removeAttr('style'); $(this).removeAttr('style');
} }
}, },
determine: { determine: {
@ -1199,8 +1243,8 @@ $.fn.dropdown = function(parameters) {
? 'slide up' ? 'slide up'
: 'slide down' : 'slide down'
; ;
module.verbose('Automatically determining animation based on animation direction', settings.transition);
} }
if(settings.transition == 'none') { if(settings.transition == 'none') {
callback.call(element); callback.call(element);
} }

Loading…
Cancel
Save