assets/javascripts/semantic_ui/definitions/modules/dropdown.js in less-rails-semantic_ui-1.7.3.0 vs assets/javascripts/semantic_ui/definitions/modules/dropdown.js in less-rails-semantic_ui-1.8.0.0
- old
+ new
@@ -71,11 +71,18 @@
module = {
initialize: function() {
module.debug('Initializing dropdown', settings);
- module.setup.layout();
+
+ if( module.is.alreadySetup() ) {
+ module.error(error.alreadySetup);
+ }
+ else {
+ module.setup.layout();
+ }
+
module.save.defaults();
module.set.selected();
module.create.id();
@@ -102,20 +109,29 @@
module.remove.tabbable();
$module
.off(eventNamespace)
.removeData(moduleNamespace)
;
+ $menu
+ .off(eventNamespace)
+ ;
$document
.off(elementNamespace)
;
},
observeChanges: function() {
if('MutationObserver' in window) {
observer = new MutationObserver(function(mutations) {
- module.debug('DOM tree modified, updating selector cache');
- module.refresh();
+ if( module.is.selectMutation(mutations) ) {
+ module.debug('<select> modified, recreating menu');
+ module.setup.select();
+ }
+ else {
+ module.debug('DOM tree modified, updating selector cache');
+ module.refresh();
+ }
});
observer.observe(element, {
childList : true,
subtree : true
});
@@ -160,28 +176,31 @@
module.set.tabbable();
}
},
select: function() {
var
- selectValues = module.get.selectValues()
+ selectValues = module.get.selectValues()
;
module.debug('Dropdown initialized on a select', selectValues);
- // see if select exists inside a dropdown
- $input = $module;
- if($input.parents(selector.dropdown).length > 0) {
- module.debug('Creating dropdown menu only from template');
+ if( $module.is('select') ) {
+ $input = $module;
+ }
+ // see if select is placed correctly already
+ if($input.parent(selector.dropdown).length > 0) {
+ module.debug('UI dropdown already exists. Creating dropdown menu only');
$module = $input.closest(selector.dropdown);
- if($module.find('.' + className.dropdown).length === 0) {
- $('<div />')
+ $menu = $module.children(selector.menu);
+ if($menu.length === 0) {
+ $menu = $('<div />')
.addClass(className.menu)
- .html( settings.templates.menu( selectValues ))
.appendTo($module)
;
}
+ $menu.html( settings.templates.menu( selectValues ));
}
else {
- module.debug('Creating entire dropdown from template');
+ module.debug('Creating entire dropdown from select');
$module = $('<div />')
.attr('class', $input.attr('class') )
.addClass(className.selection)
.addClass(className.dropdown)
.html( settings.templates.dropdown(selectValues) )
@@ -195,13 +214,18 @@
module.refresh();
}
},
refresh: function() {
+ module.verbose('Refreshing selector cache');
$text = $module.find(selector.text);
$search = $module.find(selector.search);
$input = $module.find(selector.input);
+ $combo = ($module.prev().find(selector.text).length > 0)
+ ? $module.prev().find(selector.text)
+ : $module.prev()
+ ;
$menu = $module.children(selector.menu);
$item = $menu.find(selector.item);
},
toggle: function() {
@@ -217,11 +241,14 @@
show: function(callback) {
callback = $.isFunction(callback)
? callback
: function(){}
;
- if( module.can.show() && !module.is.active() && !module.is.allFiltered() ) {
+ if( module.is.searchSelection() && module.is.allFiltered() ) {
+ return;
+ }
+ if( module.can.show() && !module.is.active() ) {
module.debug('Showing dropdown');
module.animate.show(function() {
if( module.can.click() ) {
module.bind.intent();
}
@@ -289,11 +316,10 @@
.on('touchstart' + eventNamespace, selector.item, module.event.item.mouseenter)
;
},
mouseEvents: function() {
module.verbose('Mouse detected binding mouse events');
-
if( module.is.searchSelection() ) {
$module
.on('mousedown' + eventNamespace, selector.menu, module.event.menu.activate)
.on('mouseup' + eventNamespace, selector.menu, module.event.menu.deactivate)
.on('click' + eventNamespace, selector.search, module.show)
@@ -463,91 +489,141 @@
clearTimeout(module.timer);
module.timer = setTimeout(module.search, settings.delay.search);
},
keydown: function(event) {
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 = $menu.children('.' + 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],
+ inVisibleMenu = $parentMenu.is(':visible'),
pressedKey = event.which,
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,
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()) {
+ // enter (select or sub-menu)
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();
- 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 && inVisibleMenu)
+ ? $selectedItem.prevAll(selector.item + ':not(.' + className.filtered + ')').eq(0)
+ : $item.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');
- $item
- .removeClass(selectedClass)
+ $selectedItem
+ .removeClass(className.selected)
;
$nextItem
- .addClass(selectedClass)
+ .addClass(className.selected)
;
module.set.scrollPosition($nextItem);
}
event.preventDefault();
}
- else if(pressedKey == keys.downArrow) {
- if(!hasSelectedItem) {
- $nextItem = $visibleItems.eq(0);
+ // down arrow (traverse menu down)
+ if(pressedKey == keys.downArrow) {
+ $nextItem = (hasSelectedItem && inVisibleMenu)
+ ? $nextItem = $selectedItem.nextAll(selector.item + ':not(.' + className.filtered + ')').eq(0)
+ : $item.eq(0)
+ ;
+ if($nextItem.length === 0) {
+ module.verbose('Down key pressed but reached bottom of current menu');
+ return;
}
else {
- $nextItem = $selectedItem.nextAll(selector.item + ':not(.' + className.filtered + ')').eq(0);
- }
- if(currentIndex + 1 < $visibleItems.length ) {
module.verbose('Down key pressed, changing active item');
$item
- .removeClass(selectedClass)
+ .removeClass(className.selected)
;
$nextItem
- .addClass(selectedClass)
+ .addClass(className.selected)
;
module.set.scrollPosition($nextItem);
}
event.preventDefault();
}
}
else {
+ // enter (open menu)
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();
+ }
}
},
test: {
toggle: function(event) {
if( module.determine.eventInMenu(event, module.toggle) ) {
@@ -579,39 +655,37 @@
}
},
item: {
mouseenter: function(event) {
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);
module.itemTimer = setTimeout(function() {
+ module.verbose('Showing sub-menu', $subMenu);
$.each($otherMenus, function() {
module.animate.hide(false, $(this));
});
- module.verbose('Showing sub-menu', $currentMenu);
- module.animate.show(false, $currentMenu);
+ module.animate.show(false, $subMenu);
}, settings.delay.show);
event.preventDefault();
}
},
-
mouseleave: function(event) {
var
- $currentMenu = $(this).children(selector.menu)
+ $subMenu = $(this).children(selector.menu)
;
- if($currentMenu.length > 0) {
+ if($subMenu.length > 0) {
clearTimeout(module.itemTimer);
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);
}
},
-
click: function (event) {
var
$choice = $(this),
$target = $(event.target),
$subMenu = $choice.find(selector.menu),
@@ -619,27 +693,21 @@
value = module.get.choiceValue($choice, text),
callback = function() {
module.remove.searchTerm();
module.determine.selectAction(text, value);
},
- openingSubMenu = ($subMenu.length > 0),
- isSubItem = ($subMenu.find($target).length > 0)
+ hasSubMenu = ($subMenu.length > 0),
+ isBubbledEvent = ($subMenu.find($target).length > 0)
;
- if(isSubItem) {
- return false;
- }
- if(!openingSubMenu || settings.allowCategorySelection) {
+ if(!isBubbledEvent && (!hasSubMenu || settings.allowCategorySelection)) {
callback();
}
}
-
},
-
resetStyle: function() {
$(this).removeAttr('style');
}
-
},
determine: {
selectAction: function(text, value) {
module.verbose('Determining action', settings.action);
@@ -689,35 +757,27 @@
action: {
nothing: function() {},
- hide: function() {
- module.hide(function() {
- module.remove.filteredItem();
- });
- },
-
- select: function(text, value) {
+ activate: function(text, value) {
value = (value !== undefined)
? value
: text
;
module.set.selected(value);
- module.set.value(value);
module.hide(function() {
module.remove.filteredItem();
});
},
- activate: function(text, value) {
+ select: function(text, value) {
value = (value !== undefined)
? value
: text
;
module.set.selected(value);
- module.set.value(value);
module.hide(function() {
module.remove.filteredItem();
});
},
@@ -725,14 +785,19 @@
value = (value !== undefined)
? value
: text
;
module.set.selected(value);
- module.set.value(value);
module.hide(function() {
module.remove.filteredItem();
});
+ },
+
+ hide: function() {
+ module.hide(function() {
+ module.remove.filteredItem();
+ });
}
},
get: {
@@ -764,11 +829,11 @@
: $choice.text().trim()
;
}
},
choiceValue: function($choice, choiceText) {
- choiceText = choiceText || module.get.choiceText($text);
+ choiceText = choiceText || module.get.choiceText($choice);
return ($choice.data(metadata.value) !== undefined)
? $choice.data(metadata.value)
: (typeof choiceText === 'string')
? choiceText.toLowerCase().trim()
: choiceText.trim()
@@ -898,21 +963,20 @@
var
defaultText = $module.data(metadata.defaultText)
;
module.debug('Restoring default text', defaultText);
module.set.text(defaultText);
- $text.addClass(settings.className.placeholder);
+ $text.addClass(className.placeholder);
},
defaultValue: function() {
var
defaultValue = $module.data(metadata.defaultValue)
;
if(defaultValue !== undefined) {
module.debug('Restoring default value', defaultValue);
if(defaultValue.length) {
module.set.selected(defaultValue);
- module.set.value(defaultValue);
}
else {
module.remove.activeItem();
module.remove.selectedItem();
}
@@ -921,20 +985,37 @@
},
save: {
defaults: function() {
module.save.defaultText();
+ module.save.placeholderText();
module.save.defaultValue();
},
defaultValue: function() {
$module.data(metadata.defaultValue, module.get.value() );
},
defaultText: function() {
$module.data(metadata.defaultText, $text.text() );
+ },
+ placeholderText: function() {
+ if($text.hasClass(className.placeholder)) {
+ $module.data(metadata.placeholderText, $text.text());
+ }
}
},
+ clear: function() {
+ var
+ placeholderText = $module.data(metadata.placeholderText)
+ ;
+ module.set.text(placeholderText);
+ module.set.value('');
+ module.remove.activeItem();
+ module.remove.selectedItem();
+ $text.addClass(className.placeholder);
+ },
+
set: {
filtered: function() {
var
searchValue = $search.val(),
hasSearchValue = (typeof searchValue === 'string' && searchValue.length > 0)
@@ -1058,24 +1139,25 @@
$module.addClass(className.visible);
},
selected: function(value) {
var
$selectedItem = module.get.item(value),
- selectedText
+ selectedText,
+ selectedValue
;
if($selectedItem) {
module.debug('Setting selected menu item to', $selectedItem);
-
module.remove.activeItem();
module.remove.selectedItem();
$selectedItem
.addClass(className.active)
.addClass(className.selected)
;
-
- selectedText = module.get.choiceText($selectedItem);
+ selectedText = module.get.choiceText($selectedItem);
+ selectedValue = module.get.choiceValue($selectedItem, selectedText);
module.set.text(selectedText);
+ module.set.value(selectedValue);
settings.onChange.call(element, value, selectedText, $selectedItem);
}
}
},
@@ -1122,10 +1204,13 @@
is: {
active: function() {
return $module.hasClass(className.active);
},
+ alreadySetup: function() {
+ return ($module.is('select') && $module.parent(selector.dropdown).length > 0);
+ },
animating: function($subMenu) {
return ($subMenu)
? $subMenu.is(':animated') || $subMenu.transition && $subMenu.transition('is animating')
: $menu.is(':animated') || $menu.transition && $menu.transition('is animating')
;
@@ -1137,10 +1222,22 @@
return ($subMenu)
? $subMenu.is(':hidden')
: $menu.is(':hidden')
;
},
+ selectMutation: function(mutations) {
+ var
+ selectChanged = false
+ ;
+ $.each(mutations, function(index, mutation) {
+ if(mutation.target && $(mutation.target).is('select')) {
+ selectChanged = true;
+ return true;
+ }
+ });
+ return selectChanged;
+ },
search: function() {
return $module.hasClass(className.search);
},
searchable: function() {
return ($search.length > 0);
@@ -1194,12 +1291,12 @@
if(settings.transition == 'auto') {
settings.transition = module.is.upward()
? 'slide up'
: 'slide down'
;
+ module.verbose('Automatically determining animation based on animation direction', settings.transition);
}
-
if(settings.transition == 'none') {
callback.call(element);
}
else if($.fn.transition !== undefined && $module.transition('is supported')) {
$currentMenu
@@ -1569,19 +1666,21 @@
name : 'Dropdown',
namespace : 'dropdown',
error : {
- action : 'You called a dropdown action that was not defined',
- method : 'The method you called is not defined.',
- transition : 'The requested transition was not found'
+ action : 'You called a dropdown action that was not defined',
+ alreadySetup : 'Once a select has been initialized behaviors must be called on the created ui dropdown',
+ method : 'The method you called is not defined.',
+ transition : 'The requested transition was not found'
},
metadata: {
- defaultText : 'defaultText',
- defaultValue : 'defaultValue',
- text : 'text',
- value : 'value'
+ defaultText : 'defaultText',
+ defaultValue : 'defaultValue',
+ placeholderText : 'placeholderText',
+ text : 'text',
+ value : 'value'
},
selector : {
dropdown : '.ui.dropdown',
input : '> input[type="hidden"], > select',