diff options
author | Lukas Reschke <lukas@statuscode.ch> | 2013-01-11 10:23:04 -0800 |
---|---|---|
committer | Lukas Reschke <lukas@statuscode.ch> | 2013-01-11 10:23:04 -0800 |
commit | 954b559d48ad6910daab498c8d1981eac2ab47a0 (patch) | |
tree | 47c0c6d4bd483be573f3f3e024d63ced3d1b5f26 /core | |
parent | 1f6eb68ef91c5743b058d6d8f10a4a20ee3d6d96 (diff) | |
parent | 5f795d3aac1db8981874e06e56c7cb18eed9ae7c (diff) | |
download | nextcloud-server-954b559d48ad6910daab498c8d1981eac2ab47a0.tar.gz nextcloud-server-954b559d48ad6910daab498c8d1981eac2ab47a0.zip |
Merge pull request #994 from owncloud/enhanced_multiSelect
Enhanced multiSelect
Diffstat (limited to 'core')
-rw-r--r-- | core/css/multiselect.css | 27 | ||||
-rw-r--r-- | core/js/multiselect.js | 231 |
2 files changed, 202 insertions, 56 deletions
diff --git a/core/css/multiselect.css b/core/css/multiselect.css index 99f0e039334..31c8ef88eb9 100644 --- a/core/css/multiselect.css +++ b/core/css/multiselect.css @@ -5,15 +5,25 @@ ul.multiselectoptions { background-color:#fff; border:1px solid #ddd; - border-bottom-left-radius:.5em; - border-bottom-right-radius:.5em; border-top:none; box-shadow:0 1px 1px #ddd; padding-top:.5em; position:absolute; + max-height: 20em; + overflow-y: auto; z-index:49; } + ul.multiselectoptions.down { + border-bottom-left-radius:.5em; + border-bottom-right-radius:.5em; + } + + ul.multiselectoptions.up { + border-top-left-radius:.5em; + border-top-right-radius:.5em; + } + ul.multiselectoptions>li { overflow:hidden; white-space:nowrap; @@ -30,11 +40,20 @@ div.multiselect.active { background-color:#fff; + position:relative; + z-index:50; + } + + div.multiselect.up { + border-top:0 none; + border-top-left-radius:0; + border-top-right-radius:0; + } + + div.multiselect.down { border-bottom:none; border-bottom-left-radius:0; border-bottom-right-radius:0; - position:relative; - z-index:50; } div.multiselect>span:first-child { diff --git a/core/js/multiselect.js b/core/js/multiselect.js index c4fd74b0475..623c6e0f7e1 100644 --- a/core/js/multiselect.js +++ b/core/js/multiselect.js @@ -1,20 +1,44 @@ +/** + * @param 'createCallback' A function to be called when a new entry is created. Two arguments are supplied to this function: + * The select element used and the value of the option. If the function returns false addition will be cancelled. If it returns + * anything else it will be used as the value of the newly added option. + * @param 'createText' The placeholder text for the create action. + * @param 'title' The title to show if no options are selected. + * @param 'checked' An array containing values for options that should be checked. Any options which are already selected will be added to this array. + * @param 'labels' The corresponding labels to show for the checked items. + * @param 'oncheck' Callback function which will be called when a checkbox/radiobutton is selected. If the function returns false the input will be unchecked. + * @param 'onuncheck' @see 'oncheck'. + * @param 'singleSelect' If true radiobuttons will be used instead of checkboxes. + */ (function( $ ){ var multiSelectId=-1; - $.fn.multiSelect=function(options){ + $.fn.multiSelect=function(options) { multiSelectId++; var settings = { 'createCallback':false, 'createText':false, + 'singleSelect':false, + 'selectedFirst':false, + 'sort':true, 'title':this.attr('title'), 'checked':[], + 'labels':[], 'oncheck':false, 'onuncheck':false, 'minWidth': 'default;', }; + $(this).attr('data-msid', multiSelectId); $.extend(settings,options); - $.each(this.children(),function(i,option){ - if($(option).attr('selected') && settings.checked.indexOf($(option).val())==-1){ + $.each(this.children(),function(i,option) { + // If the option is selected, but not in the checked array, add it. + if($(option).attr('selected') && settings.checked.indexOf($(option).val()) === -1) { settings.checked.push($(option).val()); + settings.labels.push($(option).text().trim()); + } + // If the option is in the checked array but not selected, select it. + else if(settings.checked.indexOf($(option).val()) !== -1 && !$(option).attr('selected')) { + $(option).attr('selected', 'selected'); + settings.labels.push($(option).text().trim()); } }); var button=$('<div class="multiselect button"><span>'+settings.title+'</span><span>▾</span></div>'); @@ -24,24 +48,36 @@ button.selectedItems=[]; this.hide(); this.before(span); - if(settings.minWidth=='default'){ + if(settings.minWidth=='default') { settings.minWidth=button.width(); } button.css('min-width',settings.minWidth); settings.minOuterWidth=button.outerWidth()-2; button.data('settings',settings); - if(settings.checked.length>0){ - button.children('span').first().text(settings.checked.join(', ')); + + if(!settings.singleSelect && settings.checked.length>0) { + button.children('span').first().text(settings.labels.join(', ')); + } else if(settings.singleSelect) { + button.children('span').first().text(this.find(':selected').text()); } + var self = this; + self.menuDirection = 'down'; button.click(function(event){ var button=$(this); - if(button.parent().children('ul').length>0){ - button.parent().children('ul').slideUp(400,function(){ - button.parent().children('ul').remove(); - button.removeClass('active'); - }); + if(button.parent().children('ul').length>0) { + if(self.menuDirection === 'down') { + button.parent().children('ul').slideUp(400,function() { + button.parent().children('ul').remove(); + button.removeClass('active down'); + }); + } else { + button.parent().children('ul').fadeOut(400,function() { + button.parent().children('ul').remove(); + button.removeClass('active up'); + }); + } return; } var lists=$('ul.multiselectoptions'); @@ -54,49 +90,69 @@ event.stopPropagation(); var options=$(this).parent().next().children(); var list=$('<ul class="multiselectoptions"/>').hide().appendTo($(this).parent()); - function createItem(element,checked){ + var inputType = settings.singleSelect ? 'radio' : 'checkbox'; + function createItem(element, checked){ element=$(element); var item=element.val(); var id='ms'+multiSelectId+'-option-'+item; - var input=$('<input type="checkbox"/>'); + var input=$('<input type="' + inputType + '"/>'); input.attr('id',id); + if(settings.singleSelect) { + input.attr('name', 'ms'+multiSelectId+'-option'); + } var label=$('<label/>'); label.attr('for',id); - label.text(item); - if(settings.checked.indexOf(item)!=-1 || checked){ - input.attr('checked',true); + label.text(element.text() || item); + if(settings.checked.indexOf(item)!=-1 || checked) { + input.attr('checked', true); } if(checked){ - settings.checked.push(item); + if(settings.singleSelect) { + settings.checked = [item]; + settings.labels = [item]; + } else { + settings.checked.push(item); + settings.labels.push(item); + } } input.change(function(){ - var groupname=$(this).next().text(); - if($(this).is(':checked')){ + var value = $(this).attr('id').substring(String('ms'+multiSelectId+'-option').length+1); + var label = $(this).next().text().trim(); + if($(this).is(':checked')) { + if(settings.singleSelect) { + settings.checked = []; + settings.labels = []; + $.each(self.find('option'), function() { + $(this).removeAttr('selected'); + }); + } element.attr('selected','selected'); - if(settings.oncheck){ - if(settings.oncheck(groupname)===false){ + if(typeof settings.oncheck === 'function') { + if(settings.oncheck(value)===false) { $(this).attr('checked', false); return; } } - settings.checked.push(groupname); - }else{ - var index=settings.checked.indexOf(groupname); + settings.checked.push(value); + settings.labels.push(label); + $(this).parent().addClass('checked'); + } else { + var index=settings.checked.indexOf(value); element.attr('selected',null); - if(settings.onuncheck){ - if(settings.onuncheck(groupname)===false){ + if(typeof settings.onuncheck === 'function') { + if(settings.onuncheck(value)===false) { $(this).attr('checked',true); return; } } + $(this).parent().removeClass('checked'); settings.checked.splice(index,1); + settings.labels.splice(index,1); } var oldWidth=button.width(); - if(settings.checked.length>0){ - button.children('span').first().text(settings.checked.join(', ')); - }else{ - button.children('span').first().text(settings.title); - } + button.children('span').first().text(settings.labels.length > 0 + ? settings.labels.join(', ') + : settings.title); var newOuterWidth=Math.max((button.outerWidth()-2),settings.minOuterWidth)+'px'; var newWidth=Math.max(button.width(),settings.minWidth); var pos=button.position(); @@ -110,6 +166,9 @@ }); var li=$('<li></li>'); li.append(input).append(label); + if(input.is(':checked')) { + li.addClass('checked'); + } return li; } $.each(options,function(index,item){ @@ -117,13 +176,13 @@ }); button.parent().data('preventHide',false); if(settings.createText){ - var li=$('<li>+ <em>'+settings.createText+'<em></li>'); + var li=$('<li class="creator">+ <em>'+settings.createText+'<em></li>'); li.click(function(event){ li.empty(); var input=$('<input class="new">'); li.append(input); input.focus(); - input.css('width',button.width()); + input.css('width',button.innerWidth()); button.parent().data('preventHide',true); input.keypress(function(event) { if(event.keyCode == 13) { @@ -132,7 +191,7 @@ var value = $(this).val(); var exists = false; $.each(options,function(index, item) { - if ($(item).val() == value) { + if ($(item).val() == value || $(item).text() == value) { exists = true; return false; } @@ -141,22 +200,39 @@ return false; } var li=$(this).parent(); + var val = $(this).val() + var select=button.parent().next(); + if(typeof settings.createCallback === 'function') { + var response = settings.createCallback(select, val); + if(response === false) { + return false; + } else if(typeof response !== 'undefined') { + val = response; + } + } + if(settings.singleSelect) { + $.each(select.find('option:selected'), function() { + $(this).removeAttr('selected'); + }); + } $(this).remove(); li.text('+ '+settings.createText); li.before(createItem(this)); - var select=button.parent().next(); var option=$('<option selected="selected"/>'); - option.attr('value',value); - option.text($(this).val()); + option.text($(this).val()).val(val).attr('selected', 'selected'); select.append(option); - li.prev().children('input').trigger('click'); + li.prev().children('input').prop('checked', true).trigger('change'); button.parent().data('preventHide',false); - if(settings.createCallback){ - settings.createCallback($(this).val()); + button.children('span').first().text(settings.labels.length > 0 + ? settings.labels.join(', ') + : settings.title); + if(self.menuDirection === 'up') { + var list = li.parent(); + list.css('top', list.position().top-li.outerHeight()); } } }); - input.blur(function(){ + input.blur(function() { event.preventDefault(); event.stopPropagation(); $(this).remove(); @@ -168,21 +244,72 @@ }); list.append(li); } + + var doSort = function(list, selector) { + var rows = list.find('li'+selector).get(); + + if(settings.sort) { + rows.sort(function(a, b) { + return $(a).text().toUpperCase().localeCompare($(b).text().toUpperCase()); + }); + } + + $.each(rows, function(index, row) { + list.append(row); + }); + }; + if(settings.sort && settings.selectedFirst) { + doSort(list, '.checked'); + doSort(list, ':not(.checked)'); + } else if(settings.sort && !settings.selectedFirst) { + doSort(list, ''); + } + list.append(list.find('li.creator')); var pos=button.position(); - list.css('top',pos.top+button.outerHeight()-5); - list.css('left',pos.left+3); - list.css('width',(button.outerWidth()-2)+'px'); - list.slideDown(); - list.click(function(event){ + if($(document).height() > (button.offset().top+button.outerHeight() + list.children().length * button.height()) + || $(document).height()/2 > pos.top + ) { + list.css({ + top:pos.top+button.outerHeight()-5, + left:pos.left+3, + width:(button.outerWidth()-2)+'px', + 'max-height':($(document).height()-(button.offset().top+button.outerHeight()+10))+'px' + }); + list.addClass('down'); + button.addClass('down'); + list.slideDown(); + } else { + list.css('max-height', $(document).height()-($(document).height()-(pos.top)+50)+'px'); + list.css({ + top:pos.top - list.height(), + left:pos.left+3, + width:(button.outerWidth()-2)+'px' + + }); + list.detach().insertBefore($(this)); + list.addClass('up'); + button.addClass('up'); + list.fadeIn(); + self.menuDirection = 'up'; + } + list.click(function(event) { event.stopPropagation(); }); }); - $(window).click(function(){ - if(!button.parent().data('preventHide')){ - button.parent().children('ul').slideUp(400,function(){ - button.parent().children('ul').remove(); - button.removeClass('active'); - }); + $(window).click(function() { + if(!button.parent().data('preventHide')) { + // How can I save the effect in a var? + if(self.menuDirection === 'down') { + button.parent().children('ul').slideUp(400,function() { + button.parent().children('ul').remove(); + button.removeClass('active down'); + }); + } else { + button.parent().children('ul').fadeOut(400,function() { + button.parent().children('ul').remove(); + button.removeClass('active up'); + }); + } } }); |