Skip to content
Snippets Groups Projects
Commit 8dcd03f2 authored by Jacob Schatz's avatar Jacob Schatz
Browse files

initial new menu

parent d33cc4e5
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -16,12 +16,12 @@
else
$(this).html totalIssues - 1
$("body").on "click", ".issues-other-filters .dropdown-menu a", ->
$('.issues-list').block(
message: null,
overlayCSS:
backgroundColor: '#DDD'
opacity: .4
)
# $('.issues-list').block(
# message: null,
# overlayCSS:
# backgroundColor: '#DDD'
# opacity: .4
# )
 
reload: ->
Issues.initSelects()
Loading
Loading
//= require sifter.js
/**
* multiawesome.js (v0.12.1)
* Copyright (c) 2015 Jacob Schatz & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Jacob Schatz <jschatz@gitlab.com>
*/
/*jshint curly:false */
/*jshint browser:true */
(function ( $ ) {
$.fn.multiawesome = function( settings ) {
settings = settings || {};
var MultiAwesome = {
searchTemplate: '<li><div class="input-with-icon"><i class="fa fa-search"></i><input type="text" id="multiawesome-search-input" /></div></li>',
itemContainerTemplate: '<li class="dropdown-multi-menu-selections"><ul></ul></li>',
itemTemplate: '<li><a href="#" class="item" tabIndex="-1"><input type="checkbox" name="{{name}}" value="{{data}}"/>{{label}}</a></li>',
seperatorTemplate: '<li role="separator" class="divider"></li>',
titleTemplate: '<li class=dropdown-multi-menu-title><div class=dropdown-multi-menu-title-area><h3>{{title}}</h3></div></li>',
categoryContainerTemplate: '<li class=dropdown-multi-menu-category><ul></ul></li>',
categoryItemTemplate: '<li><a href="#" class="category" tabIndex="-1"><input type="checkbox" value="{{category}}"/>{{category}}</a></li>',
tipTemplate: '<li class=dropdown-multi-menu-tip><div class=dropdown-multi-menu-tip-area><p>{{tip}}</p></div></li>',
minSearchLength: 2,
};
return this.each(function() {
var self = this,
categories = [],
$self = $(self),
$form = $(self).closest('form'),
$itemContainer,
$itemContainerUL,
itemTemplate,
itemContainerTemplate,
categoriesSet = false,
selectedItems = [],
selectedCategories = [],
$searchInput,
multiple = typeof $self.attr('data-multiple') !== 'undefined',
currentData = settings.data || $self.data('data'),
inputName = $(self).data('name'),
sifter,
dataObject = {
label: 'label',
data: 'data',
category: 'category'
};
if( self.tagName !== 'UL') {
return;
}
var prepareDropdown = function() {
var placeholder = $self.data('placeholder');
if( typeof settings.searchTemplate !== 'undefined' ) {
MultiAwesome.searchTemplate = settings.searchTemplate;
}
$self.prepend(MultiAwesome.searchTemplate);
if( placeholder ) {
$('#multiawesome-search-input').prop( 'placeholder', placeholder );
}
};
var attachBtnListeners = function() {
$( 'ul.dropdown-multi-menu' ).on( 'click', 'li.dropdown-multi-menu-category a' , dropdownCategoryLinkClicked );
$( 'ul.dropdown-multi-menu' ).on( 'click', 'li.dropdown-multi-menu-selections a', dropdownSelectionLinkClicked );
$searchInput = $('#multiawesome-search-input');
$searchInput.on( 'keydown keyup update', inputSearched );
};
var parseSearchResults = function(results) {
var finalData = [];
results.forEach( function( result ) {
finalData.push(currentData[result.id]);
});
renderData( finalData );
};
var inputSearched = function() {
var minSearchLength = settings.minSearchLength || MultiAwesome.minSearchLength;
//remove current hidden inputs
$('input[type="hidden"][name="' + inputName + '"]').remove();
if( $searchInput.val().length > minSearchLength ) {
var results = sifter.search($searchInput.val(), {
fields: [dataObject.label],
sort: [{field: dataObject.label, direction: 'asc'}]
});
parseSearchResults(results.items);
} else {
renderData( currentData );
}
};
var configureData = function() {
if ( typeof settings.itemLabelTitle !== 'undefined' ) {
dataObject.label = settings.itemLabelTitle;
}
if ( typeof settings.itemDataTitle !== 'undefined' ) {
dataObject.data = settings.itemDataTitle;
}
if ( typeof settings.itemCategoryTitle !== 'undefined' ) {
dataObject.category = settings.itemCategoryTitle;
}
};
var addCategories = function() {
var $categoryContainer;
var $categoryContainerUL;
if( categories.length ) {
if( typeof settings.categoryContainerTemplate !== 'undefined' ) {
$categoryContainer = $(settings.categoryContainerTemplate);
} else {
$categoryContainer = $(MultiAwesome.categoryContainerTemplate);
}
$categoryContainerUL = $categoryContainer.find('ul');
$self.prepend(MultiAwesome.seperatorTemplate);
categories.forEach(function( category ) {
$categoryContainerUL.prepend( MultiAwesome.categoryItemTemplate
.replace( /{{category}}/g, category ) );
});
$self.prepend($categoryContainer);
}
};
var addTitle = function() {
var titleTemplate;
var titleData = $self.data('title') || settings.title;
if( typeof titleData !== 'undefined') {
if(typeof settings.titleTemplate !== 'undefined') {
titleTemplate = settings.titleTemplate;
} else {
titleTemplate = MultiAwesome.titleTemplate;
}
$self.prepend(MultiAwesome.seperatorTemplate);
$self.prepend(titleTemplate
.replace(/{{title}}/g, titleData)
);
}
};
var addData = function(callback) {
function parseDataWhenReady() {
itemTemplate = MultiAwesome.itemTemplate;
itemContainerTemplate = MultiAwesome.itemContainerTemplate;
if( typeof settings.itemTemplate !== 'undefined' ) {
itemTemplate = settings.itemTemplate;
}
if( typeof settings.itemContainerTemplate !== 'undefined' ) {
itemContainerTemplate = settings.itemContainerTemplate;
}
$itemContainer = $(itemContainerTemplate);
$itemContainerUL = $itemContainer.find('ul');
sifter = new Sifter(currentData);
renderData( currentData,callback );
}
configureData();
if ( typeof currentData !== 'undefined' ) {
if ( typeof currentData === 'string') {
$.getJSON(currentData, function(data) {
currentData = data;
parseDataWhenReady();
});
} else if ( typeof currentData === 'object' ) {
parseDataWhenReady();
}
}
};
var renderData = function( data, callback ) {
selectedItems = [];
$itemContainerUL.empty();
var emptyObj = {};
var skipMatch = false;
if( !data.length ) {
emptyObj[dataObject.label] = 'No matches found';
emptyObj[dataObject.data] = '';
emptyObj[dataObject.category] = '';
data.push(emptyObj);
skipMatch = true;
}
data.forEach( function( item ) {
if( !categoriesSet && !skipMatch ) {
var addCategory = item[dataObject.category];
if( item.hasOwnProperty( dataObject.category ) && categories.indexOf( addCategory ) === -1 ) {
categories.push( addCategory );
}
} else {
// only do this if the categories are already set... they won't search categories on the first time.
if( selectedCategories.length &&
selectedCategories.indexOf(item[dataObject.category]) === -1 &&
!skipMatch) {
return;
}
}
$itemContainerUL.append(
itemTemplate
.replace(/{{data}}/g,item[dataObject.data])
.replace(/{{label}}/g,item[dataObject.label])
.replace(/{{name}}/g, '_' + inputName)
);
});
if( !categoriesSet ) {
$self.append($itemContainer);
}
if( categories.length ){
categoriesSet = true;
}
if( callback ) {
callback();
}
};
var addToForm = function( val ) {
$form.prepend('<input type="hidden" name="' + inputName + '" value="' + val + '" />');
};
var removeFromForm = function( val ) {
$form
.find('input[name="' + inputName + '"][value="' + val + '"]')
.remove();
};
/* * * * * * * * * * * * * * * */
/* listeners
/* * * * * * * * * * * * * * * */
var dropdownInputClicked = function( e ) {
return false;
};
var dropdownCategoryLinkClicked = function ( e ) {
var $target = $( e.currentTarget ),
$inp = $target.find( 'input' ),
val = $inp.val(),
i = selectedCategories.indexOf( val );
if ( i > -1 ) {
var spliced = selectedCategories.splice( i, 1 );
$target.removeClass('selected');
setTimeout( function() {
$inp.prop( 'checked', false );
}, 0);
} else {
selectedCategories.push( val );
$target.addClass('selected');
setTimeout( function() {
$inp.prop( 'checked', true );
}, 0);
}
inputSearched();
$( e.target ).blur();
return false;
};
var dropdownSelectionLinkClicked = function ( e ) {
var $target = $( e.currentTarget ),
$inp = $target.find( 'input' ),
val = $inp.val(),
i = selectedItems.indexOf( val );
if ( i > -1 ) {
var spliced = selectedItems.splice( i, 1 );
if( multiple ) {
removeFromForm( spliced );
} else {
$form
.find('input[name="' + inputName + '"]')
.remove();
}
$target.removeClass('selected');
setTimeout( function() {
$inp.prop( 'checked', false );
}, 0);
} else {
if( !multiple ) {
selectedItems = [];
$form
.find('input[name="' + inputName + '"]')
.remove();
$form
.find('input[name="_' + inputName + '"]')
.parent()
.removeClass('selected');
}
selectedItems.push( val );
addToForm( val );
$target.addClass('selected');
setTimeout( function() {
$inp.prop( 'checked', true );
}, 0);
}
$( e.target ).blur();
return false;
};
var addTip = function() {
var tipTemplate;
var tipData = $self.data('tip') || settings.tip;
if( typeof tipData !== 'undefined') {
if(typeof settings.tipTemplate !== 'undefined') {
tipTemplate = settings.tipTemplate;
} else {
tipTemplate = MultiAwesome.tipTemplate;
}
$self.append(MultiAwesome.seperatorTemplate);
$self.append(tipTemplate
.replace(/{{tip}}/g, tipData)
);
}
};
/* * * * * * * * * * * * * * * */
/* setup
/* * * * * * * * * * * * * * * */
var setup = function() {
addData(function(){
addCategories();
prepareDropdown();
addTitle();
attachBtnListeners();
addTip();
});
};
setup();
});
};
})( jQuery );
\ No newline at end of file
//= require sifter
/**
* multiawesome.js (v0.12.1)
* Copyright (c) 2015 Jacob Schatz & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Jacob Schatz <jschatz@gitlab.com>
*/
/*jshint curly:false */
/*jshint browser:true */
(function ( $ ) {
'use strict';
$.fn.multiawesome = function( settings ) {
settings = settings || {};
var MultiAwesome = {
searchTemplate: '<li><div class="input-with-icon"><i class="fa fa-search"></i><input type="text" id="multiawesome-search-input" /></div></li>',
itemContainerTemplate: '<li class="dropdown-multi-menu-selections"><ul></ul></li>',
itemTemplate: '<li><a href="#" class="item" tabIndex="-1"><input type="checkbox" name="{{name}}" value="{{data}}"/>{{label}}</a></li>',
seperatorTemplate: '<li role="separator" class="divider"></li>',
titleTemplate: '<li class=dropdown-multi-menu-title><div class=dropdown-multi-menu-title-area><h3>{{title}}</h3></div></li>',
categoryContainerTemplate: '<li class=dropdown-multi-menu-category><ul></ul></li>',
categoryItemTemplate: '<li><a href="#" class="category" tabIndex="-1"><input type="checkbox" value="{{category}}"/>{{category}}</a></li>',
tipTemplate: '<li class=dropdown-multi-menu-tip><div class=dropdown-multi-menu-tip-area><p>{{tip}}</p></div></li>',
minSearchLength: 2
};
return this.each(function() {
var self = this,
categories = [],
$self = $(self),
$form = $(self).closest('form'),
$itemContainer,
$itemContainerUL,
itemTemplate,
itemContainerTemplate,
categoriesSet = false,
selectedItems = [],
selectedCategories = [],
$searchInput,
onChange = settings.onChange || function(){},
multiple = typeof $self.attr('data-multiple') !== 'undefined',
currentData = settings.data || $self.data('data'),
inputName = $(self).data('name'),
sifter,
alwaysData = settings.always || [],
dataObject = {
label: 'label',
data: 'data',
category: 'category'
};
if( self.tagName !== 'UL') {
return;
}
var prepareDropdown = function() {
var placeholder = $self.data('placeholder');
if( typeof settings.searchTemplate !== 'undefined' ) {
MultiAwesome.searchTemplate = settings.searchTemplate;
}
$self.prepend(MultiAwesome.searchTemplate);
if( placeholder ) {
$('#multiawesome-search-input').prop( 'placeholder', placeholder );
}
};
var attachBtnListeners = function() {
$( 'ul.dropdown-multi-menu' ).on( 'click', 'li.dropdown-multi-menu-category a' , dropdownCategoryLinkClicked );
$( 'ul.dropdown-multi-menu' ).on( 'click', 'li.dropdown-multi-menu-selections a', dropdownSelectionLinkClicked );
$searchInput = $('#multiawesome-search-input');
$searchInput.on( 'keydown keyup update', inputSearched );
};
var parseSearchResults = function(results) {
var finalData = [];
results.forEach( function( result ) {
finalData.push(currentData[result.id]);
});
renderData( finalData );
};
var inputSearched = function() {
var minSearchLength = settings.minSearchLength || MultiAwesome.minSearchLength;
//remove current hidden inputs
$('input[type="hidden"][name="' + inputName + '"]').remove();
if( $searchInput.val().length > minSearchLength ) {
var results = sifter.search($searchInput.val(), {
fields: [dataObject.label],
sort: [{field: dataObject.label, direction: 'asc'}]
});
parseSearchResults(results.items);
} else {
renderData( currentData );
}
};
var configureData = function() {
if ( typeof settings.itemLabelTitle !== 'undefined' ) {
dataObject.label = settings.itemLabelTitle;
}
if ( typeof settings.itemDataTitle !== 'undefined' ) {
dataObject.data = settings.itemDataTitle;
}
if ( typeof settings.itemCategoryTitle !== 'undefined' ) {
dataObject.category = settings.itemCategoryTitle;
}
};
var addCategories = function() {
var $categoryContainer;
var $categoryContainerUL;
if( categories.length ) {
if( typeof settings.categoryContainerTemplate !== 'undefined' ) {
$categoryContainer = $(settings.categoryContainerTemplate);
} else {
$categoryContainer = $(MultiAwesome.categoryContainerTemplate);
}
$categoryContainerUL = $categoryContainer.find('ul');
$self.prepend(MultiAwesome.seperatorTemplate);
categories.forEach(function( category ) {
$categoryContainerUL.prepend( MultiAwesome.categoryItemTemplate
.replace( /\{\{category\}\}/g, category ) );
});
$self.prepend($categoryContainer);
}
};
var addTitle = function() {
var titleTemplate;
var titleData = $self.data('title') || settings.title;
if( typeof titleData !== 'undefined') {
if(typeof settings.titleTemplate !== 'undefined') {
titleTemplate = settings.titleTemplate;
} else {
titleTemplate = MultiAwesome.titleTemplate;
}
$self.prepend(MultiAwesome.seperatorTemplate);
$self.prepend(titleTemplate
.replace(/\{\{title\}\}/g, titleData)
);
}
};
var addData = function(callback) {
function parseDataWhenReady() {
itemTemplate = MultiAwesome.itemTemplate;
itemContainerTemplate = MultiAwesome.itemContainerTemplate;
if( typeof settings.itemTemplate !== 'undefined' ) {
itemTemplate = settings.itemTemplate;
}
if( typeof settings.itemContainerTemplate !== 'undefined' ) {
itemContainerTemplate = settings.itemContainerTemplate;
}
$itemContainer = $(itemContainerTemplate);
$itemContainerUL = $itemContainer.find('ul');
sifter = new Sifter(currentData);
renderData( currentData,callback );
}
configureData();
if ( typeof currentData !== 'undefined' ) {
if ( typeof currentData === 'string') {
$.getJSON(currentData, function(data) {
currentData = data;
parseDataWhenReady();
});
} else if ( typeof currentData === 'object' ) {
parseDataWhenReady();
}
}
};
var renderData = function( data, callback ) {
selectedItems = [];
$itemContainerUL.empty();
var emptyObj = {};
var skipMatch = false;
if( !data.length ) {
emptyObj[dataObject.label] = 'No matches found';
emptyObj[dataObject.data] = '';
emptyObj[dataObject.category] = '';
data.push(emptyObj);
skipMatch = true;
}
data = data.concat(alwaysData);
data.forEach( function( item ) {
if( !categoriesSet && !skipMatch ) {
var addCategory = item[dataObject.category];
if( item.hasOwnProperty( dataObject.category ) && categories.indexOf( addCategory ) === -1 ) {
categories.push( addCategory );
}
} else {
// only do this if the categories are already set... they won't search categories on the first time.
if( selectedCategories.length &&
selectedCategories.indexOf(item[dataObject.category]) === -1 &&
!skipMatch) {
return;
}
}
$itemContainerUL.append(
itemTemplate
.replace(/\{\{data\}\}/g,item[dataObject.data])
.replace(/\{\{label\}\}/g,item[dataObject.label])
.replace(/\{\{name\}\}/g, '_' + inputName)
);
});
if( !categoriesSet ) {
$self.append($itemContainer);
}
if( categories.length ){
categoriesSet = true;
}
if( callback ) {
callback();
}
};
var addToForm = function( val ) {
$form.prepend('<input type="hidden" name="' + inputName + '" value="' + val + '" />');
};
var removeFromForm = function( val ) {
$form
.find('input[name="' + inputName + '"][value="' + val + '"]')
.remove();
};
/* * * * * * * * * * * * * * * */
/* listeners
/* * * * * * * * * * * * * * * */
var dropdownInputClicked = function( e ) {
return false;
};
var dropdownCategoryLinkClicked = function ( e ) {
var $target = $( e.currentTarget ),
$inp = $target.find( 'input' ),
val = $inp.val(),
i = selectedCategories.indexOf( val );
e.preventDefault();
if ( i > -1 ) {
var spliced = selectedCategories.splice( i, 1 );
$target.removeClass('selected');
setTimeout( function() {
$inp.prop( 'checked', false );
}, 0);
} else {
selectedCategories.push( val );
$target.addClass('selected');
setTimeout( function() {
$inp.prop( 'checked', true );
}, 0);
}
inputSearched();
$( e.target ).blur();
return false;
};
var findItemWithData = function(searchData, id) {
var item = {};
for (var i = searchData.length - 1; i >= 0; i--) {
item = searchData[i];
if( item.hasOwnProperty(dataObject.data) && item[dataObject.data] == id ) {
return item;
}
};
return undefined;
};
var dropdownSelectionLinkClicked = function ( e ) {
var $target = $( e.currentTarget ),
$inp = $target.find( 'input' ),
findItemInData,
val = $inp.val(),
i = selectedItems.indexOf( val );
e.preventDefault();
if ( i > -1 ) {
var spliced = selectedItems.splice( i, 1 );
if( multiple ) {
removeFromForm( spliced );
} else {
$form
.find('input[name="' + inputName + '"]')
.remove();
}
$target.removeClass('selected');
setTimeout( function() {
$inp.prop( 'checked', false );
}, 0);
} else {
if( !multiple ) {
selectedItems = [];
$form
.find('input[name="' + inputName + '"]')
.remove();
$form
.find('input[name="_' + inputName + '"]')
.parent()
.removeClass('selected');
}
findItemInData = findItemWithData(currentData, val);
if( typeof findItemInData === 'undefined' ) {
findItemInData = findItemWithData(alwaysData, val)
}
onChange(findItemInData);
selectedItems.push( val );
addToForm( val );
$target.addClass('selected');
setTimeout( function() {
$inp.prop( 'checked', true );
}, 0);
}
if( multiple ) {
// close the dropdown if single selection
$( e.target ).blur();
return false;
} else {
var button = $self.prevAll('.dropdown-toggle');
$self.prevAll('.dropdown-toggle')
.contents()
.each(
function(){
if ( this.nodeType === 3 && this.nodeValue.trim() ) {
this.textContent = $target.text();
}
});
}
};
var addTip = function() {
var tipTemplate;
var tipData = $self.data('tip') || settings.tip;
if( typeof tipData !== 'undefined') {
if(typeof settings.tipTemplate !== 'undefined') {
tipTemplate = settings.tipTemplate;
} else {
tipTemplate = MultiAwesome.tipTemplate;
}
$self.append(MultiAwesome.seperatorTemplate);
$self.append(tipTemplate
.replace(/\{\{tip\}\}/g, tipData)
);
}
};
/* * * * * * * * * * * * * * * */
/* setup
/* * * * * * * * * * * * * * * */
var setup = function() {
addData(function(){
addCategories();
prepareDropdown();
addTitle();
attachBtnListeners();
addTip();
});
};
setup();
});
};
})( jQuery );
\ No newline at end of file
Loading
Loading
@@ -49,3 +49,8 @@
* Styles for JS behaviors.
*/
@import "behaviors.scss";
/*
* Styles for multiawesome
*/
@import "multiawesome";
\ No newline at end of file
.open>.dropdown-multi-menu {
max-width: 320px;
border-radius: 0px;
}
.dropdown-multi-menu input[type='text'] {
margin: 0 5px;
width: 309px;
height: 35px;
padding-left: 6px;
}
.dropdown-multi-menu input[type='checkbox'] {
margin: 0 5px;
display: none;
}
.dropdown-multi-menu>li>a {
padding-left: 0;
}
.dropdown-multi-menu .dropdown-multi-menu-title-area h3 {
font-size: 15px;
text-align: center;
margin-top: 5px;
}
.dropdown-multi-menu .dropdown-multi-menu-title-area h3::after {
content: "";
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAACGUlEQVQ4jZ2VMXbiMBCGf83KKcxzntOmI1VMEyj2BLpD9gRbwQVygeUCpMsdcgKfQaYJqeIu1RYGC/stNjNbJOaBY8hu/lrz69PoH0mJCBaLhVEArqMoxhf0vFgYARBFUawWT08my7KxiMD3/fhmOLz/H7PE2ruyLL8rpRCG4b0WEQjzeVXXxq3X59ba8zAMZ/1+Pz9l9PLyEiyXy0lRFGZb18bTOhYR6GgwiOdJcu3Wa9R1bZxz5r1mespwuVxOnHO/mBla69jv9R6jwSAmALgZDu9934+JCMyMoiiMtfYuTdOgbZSmaWCtvSuKwjAziOigVbpZGIbhDACKojCnSLMsOyTz/bipPTB879nUWgvnnNkjxdnZ2TMAbDab6zbZaDQ62FCjpS5SIgIAMDOOkTVSItLZ9CRJxnmez5gZzRqlFIgIQRBMhkfiRZ1uADzPeyUiiMiOTERARPA87/VY3VHDr+pDDxtVVXXJzLtjAm9HZmZUVXV5rO5DD9M0DbIsmzSXQkQ4dSntifpA2JWzdmxO5XRnuE92KmddOd0n3Rl+NgGNPpsoAoB5koy7yLpenH6/n49Go2l79udJMgaAbz9ub02+Wv2s69poreNer/cQhuHs4uJi0zbbV1mWcxHJt9sttnVtmPmPc+63VkpBEa205z36vh8fm4C2rq6ucgDTxFqUZbkiolwplavmCwDenvB/MWtr/wv5CwCanfXE6iK0AAAAAElFTkSuQmCC');
background-repeat: no-repeat;
background-size: 11px;
width: 12px;
height: 12px;
display: inline-block;
position: absolute;
right: 13px;
top: 13px;
cursor: pointer;
}
.dropdown-multi-menu .dropdown-multi-menu-selections {
max-height: 150px;
overflow-y: scroll;
margin-top: 15px;
}
.dropdown-multi-menu .dropdown-multi-menu-category ul{
padding-left: 15px;
list-style: none;
max-height: 65px;
overflow-y: scroll;
}
.dropdown-multi-menu .dropdown-multi-menu-selections ul{
padding-left: 15px;
list-style: none;
}
.dropdown-multi-menu .dropdown-multi-menu-selections li{
margin-bottom: 10px;
}
.dropdown-multi-menu .dropdown-multi-menu-selections ul a, .dropdown-multi-menu .dropdown-multi-menu-category ul a{
text-decoration: none;
color: #333;
display: inline-block;
width: 285px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
height: 30px;
padding-left: 30px;
}
.dropdown-multi-menu .dropdown-multi-menu-selections ul a.selected, .dropdown-multi-menu .dropdown-multi-menu-category ul a.selected {
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAYCAMAAADat72NAAABtlBMVEX////b29vc29vc3NzZ2dna2dnb2trY19fZ2NjV1dXW1tbT09PU09PV1NTR0dHS0dHS0tLPz8/Qz8/Q0NDNzc3Ozc3Ly8vMy8vMzMzJycnKycnKysrIyMjJyMjKyMjFxcXGxcXGxsbHxcXHxsbEw8PExMTFxMTBwcHBwsLCwcG/v7+9vb2+vr6/vr67u7u8vLy5ubm6ubm7ubm3t7e4t7e4uLi1tbW3tra0s7OysbGysrKzsbGvr6+wr6+urq6vrq6sq6utrKypqamrqqqsqqqop6epp6eqqKimpaWnpaWnpqako6OkpKSlo6OioaGjoqKkoqKgn5+goKChn5+hoKCenp6fnp6cm5ucnJydm5udnJyenJx+fX1/fHx/fX2Afn6Af3+Bf3+BgICCgICCgYGDgYGDgoKEgoKFgoKFg4OGhISGhYWHhYWIh4eJh4eJiIiJiYmKiIiLiYmLioqMioqMi4uNi4uNjIyOjIyPjY2Qjo6Qj4+SkJCSkZGTkZGTkpKUk5OVk5OVlJSWlZWXlZWYlpaYl5eYmJiZmJiZmZmamJibmZmbmpqcm5uenp6fn5+op6erqqouFWZ5AAAAXHRSTlMADw8PFhYWHR0kJCwsLDMzMzo6OkJCSUlJUFBQV1dXX19fX19mZmZtbW11fHx8g4OKioqSkpKZmaCoqKivr7a2vb3FxcXMzMzT09Pb29vi4uLp6enp8PD4+Pj4+E8lS9YAAAEjSURBVCjPY2DACYRtrDhwywralTZL45RldymLb5LEJctkWBOXHiSAS9q8OSOzxRiXrGJPWmqdNTcOWY3ggtQyN15cXvIpTCnxVcIhK+pYnZwZLYdDls22Nj2rywiHLKNeW2p8oxaMK6vGgiKt05edVGXPDOWJB7S6KyPJSgVnxZRb8sC4Co2JJV4icFkJl6KYXB8hOJ8/KiW5wl8TyuN0KonP81BEMs24Iye10A8S+Kwm1bGZESoobtHszkwtDtUGMS0aUjM7TdG8YhRZmJIdLc/IpNqWktpoxonuVeWw3OTC3gkT+/NTK525MINCN6QsNSuvND+93FUGW1CJeZekJiSn5wSKYw9KNc/q9IS8cH1cCYDPoT63XR13ypQwczbAEAQAnkJCZAp/V+QAAAAASUVORK5CYII=');
background-repeat: no-repeat;
background-size: 16px;
background-position: 2px 4px;
}
.dropdown-multi-menu .dropdown-multi-menu-tip-area {
margin: 10px;
text-align: left;
max-height: 40px;
overflow-y: scroll;
}
.dropdown-multi-menu .dropdown-multi-menu-tip-area p {
margin-bottom: 0;
}
.dropdown-multi-menu .input-with-icon {
position: relative;
margin: 10px 0;
}
.dropdown-multi-menu .input-with-icon i {
position: absolute;
right: 0;
padding: 10px 12px;
color: #817F7F;
pointer-events: none;
}
Loading
Loading
@@ -42,6 +42,11 @@
class: 'select2 trigger-submit', include_blank: true,
data: {placeholder: 'Milestone'})
 
.filter-item.inline.people-filter.btn-group
%button.btn.btn-default.dropdown-toggle{ "data-toggle" => 'dropdown' }
People
%span.caret
%ul.dropdown-menu.dropdown-multi-menu
.filter-item.inline.labels-filter
= select_tag('label_name', projects_labels_options,
class: 'select2 trigger-submit', include_blank: true,
Loading
Loading
@@ -70,3 +75,11 @@
event.preventDefault();
Turbolinks.visit(this.action + '&' + $(this).serialize());
});
$('ul.dropdown-multi-menu').multiawesome({
data: [{label:'milestone',data:1},{label:'no milestone',data:1}],
title: "Milestones",
onChange: function(item) {
console.log("changed",item)
}
});
This diff is collapsed.
/**
* sifter.js
* Copyright (c) 2013 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.Sifter = factory();
}
}(this, function() {
/**
* Textually searches arrays and hashes of objects
* by property (or multiple properties). Designed
* specifically for autocomplete.
*
* @constructor
* @param {array|object} items
* @param {object} items
*/
var Sifter = function(items, settings) {
this.items = items;
this.settings = settings || {diacritics: true};
};
/**
* Splits a search string into an array of individual
* regexps to be used to match results.
*
* @param {string} query
* @returns {array}
*/
Sifter.prototype.tokenize = function(query) {
query = trim(String(query || '').toLowerCase());
if (!query || !query.length) return [];
var i, n, regex, letter;
var tokens = [];
var words = query.split(/ +/);
for (i = 0, n = words.length; i < n; i++) {
regex = escape_regex(words[i]);
if (this.settings.diacritics) {
for (letter in DIACRITICS) {
if (DIACRITICS.hasOwnProperty(letter)) {
regex = regex.replace(new RegExp(letter, 'g'), DIACRITICS[letter]);
}
}
}
tokens.push({
string : words[i],
regex : new RegExp(regex, 'i')
});
}
return tokens;
};
/**
* Iterates over arrays and hashes.
*
* ```
* this.iterator(this.items, function(item, id) {
* // invoked for each item
* });
* ```
*
* @param {array|object} object
*/
Sifter.prototype.iterator = function(object, callback) {
var iterator;
if (is_array(object)) {
iterator = Array.prototype.forEach || function(callback) {
for (var i = 0, n = this.length; i < n; i++) {
callback(this[i], i, this);
}
};
} else {
iterator = function(callback) {
for (var key in this) {
if (this.hasOwnProperty(key)) {
callback(this[key], key, this);
}
}
};
}
iterator.apply(object, [callback]);
};
/**
* Returns a function to be used to score individual results.
*
* Good matches will have a higher score than poor matches.
* If an item is not a match, 0 will be returned by the function.
*
* @param {object|string} search
* @param {object} options (optional)
* @returns {function}
*/
Sifter.prototype.getScoreFunction = function(search, options) {
var self, fields, tokens, token_count;
self = this;
search = self.prepareSearch(search, options);
tokens = search.tokens;
fields = search.options.fields;
token_count = tokens.length;
/**
* Calculates how close of a match the
* given value is against a search token.
*
* @param {mixed} value
* @param {object} token
* @return {number}
*/
var scoreValue = function(value, token) {
var score, pos;
if (!value) return 0;
value = String(value || '');
pos = value.search(token.regex);
if (pos === -1) return 0;
score = token.string.length / value.length;
if (pos === 0) score += 0.5;
return score;
};
/**
* Calculates the score of an object
* against the search query.
*
* @param {object} token
* @param {object} data
* @return {number}
*/
var scoreObject = (function() {
var field_count = fields.length;
if (!field_count) {
return function() { return 0; };
}
if (field_count === 1) {
return function(token, data) {
return scoreValue(data[fields[0]], token);
};
}
return function(token, data) {
for (var i = 0, sum = 0; i < field_count; i++) {
sum += scoreValue(data[fields[i]], token);
}
return sum / field_count;
};
})();
if (!token_count) {
return function() { return 0; };
}
if (token_count === 1) {
return function(data) {
return scoreObject(tokens[0], data);
};
}
if (search.options.conjunction === 'and') {
return function(data) {
var score;
for (var i = 0, sum = 0; i < token_count; i++) {
score = scoreObject(tokens[i], data);
if (score <= 0) return 0;
sum += score;
}
return sum / token_count;
};
} else {
return function(data) {
for (var i = 0, sum = 0; i < token_count; i++) {
sum += scoreObject(tokens[i], data);
}
return sum / token_count;
};
}
};
/**
* Returns a function that can be used to compare two
* results, for sorting purposes. If no sorting should
* be performed, `null` will be returned.
*
* @param {string|object} search
* @param {object} options
* @return function(a,b)
*/
Sifter.prototype.getSortFunction = function(search, options) {
var i, n, self, field, fields, fields_count, multiplier, multipliers, get_field, implicit_score, sort;
self = this;
search = self.prepareSearch(search, options);
sort = (!search.query && options.sort_empty) || options.sort;
/**
* Fetches the specified sort field value
* from a search result item.
*
* @param {string} name
* @param {object} result
* @return {mixed}
*/
get_field = function(name, result) {
if (name === '$score') return result.score;
return self.items[result.id][name];
};
// parse options
fields = [];
if (sort) {
for (i = 0, n = sort.length; i < n; i++) {
if (search.query || sort[i].field !== '$score') {
fields.push(sort[i]);
}
}
}
// the "$score" field is implied to be the primary
// sort field, unless it's manually specified
if (search.query) {
implicit_score = true;
for (i = 0, n = fields.length; i < n; i++) {
if (fields[i].field === '$score') {
implicit_score = false;
break;
}
}
if (implicit_score) {
fields.unshift({field: '$score', direction: 'desc'});
}
} else {
for (i = 0, n = fields.length; i < n; i++) {
if (fields[i].field === '$score') {
fields.splice(i, 1);
break;
}
}
}
multipliers = [];
for (i = 0, n = fields.length; i < n; i++) {
multipliers.push(fields[i].direction === 'desc' ? -1 : 1);
}
// build function
fields_count = fields.length;
if (!fields_count) {
return null;
} else if (fields_count === 1) {
field = fields[0].field;
multiplier = multipliers[0];
return function(a, b) {
return multiplier * cmp(
get_field(field, a),
get_field(field, b)
);
};
} else {
return function(a, b) {
var i, result, a_value, b_value, field;
for (i = 0; i < fields_count; i++) {
field = fields[i].field;
result = multipliers[i] * cmp(
get_field(field, a),
get_field(field, b)
);
if (result) return result;
}
return 0;
};
}
};
/**
* Parses a search query and returns an object
* with tokens and fields ready to be populated
* with results.
*
* @param {string} query
* @param {object} options
* @returns {object}
*/
Sifter.prototype.prepareSearch = function(query, options) {
if (typeof query === 'object') return query;
options = extend({}, options);
var option_fields = options.fields;
var option_sort = options.sort;
var option_sort_empty = options.sort_empty;
if (option_fields && !is_array(option_fields)) options.fields = [option_fields];
if (option_sort && !is_array(option_sort)) options.sort = [option_sort];
if (option_sort_empty && !is_array(option_sort_empty)) options.sort_empty = [option_sort_empty];
return {
options : options,
query : String(query || '').toLowerCase(),
tokens : this.tokenize(query),
total : 0,
items : []
};
};
/**
* Searches through all items and returns a sorted array of matches.
*
* The `options` parameter can contain:
*
* - fields {string|array}
* - sort {array}
* - score {function}
* - filter {bool}
* - limit {integer}
*
* Returns an object containing:
*
* - options {object}
* - query {string}
* - tokens {array}
* - total {int}
* - items {array}
*
* @param {string} query
* @param {object} options
* @returns {object}
*/
Sifter.prototype.search = function(query, options) {
var self = this, value, score, search, calculateScore;
var fn_sort;
var fn_score;
search = this.prepareSearch(query, options);
options = search.options;
query = search.query;
// generate result scoring function
fn_score = options.score || self.getScoreFunction(search);
// perform search and sort
if (query.length) {
self.iterator(self.items, function(item, id) {
score = fn_score(item);
if (options.filter === false || score > 0) {
search.items.push({'score': score, 'id': id});
}
});
} else {
self.iterator(self.items, function(item, id) {
search.items.push({'score': 1, 'id': id});
});
}
fn_sort = self.getSortFunction(search, options);
if (fn_sort) search.items.sort(fn_sort);
// apply limits
search.total = search.items.length;
if (typeof options.limit === 'number') {
search.items = search.items.slice(0, options.limit);
}
return search;
};
// utilities
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
var cmp = function(a, b) {
if (typeof a === 'number' && typeof b === 'number') {
return a > b ? 1 : (a < b ? -1 : 0);
}
a = asciifold(String(a || ''));
b = asciifold(String(b || ''));
if (a > b) return 1;
if (b > a) return -1;
return 0;
};
var extend = function(a, b) {
var i, n, k, object;
for (i = 1, n = arguments.length; i < n; i++) {
object = arguments[i];
if (!object) continue;
for (k in object) {
if (object.hasOwnProperty(k)) {
a[k] = object[k];
}
}
}
return a;
};
var trim = function(str) {
return (str + '').replace(/^\s+|\s+$|/g, '');
};
var escape_regex = function(str) {
return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
};
var is_array = Array.isArray || (typeof $ !== 'undefined' && $.isArray) || function(object) {
return Object.prototype.toString.call(object) === '[object Array]';
};
var DIACRITICS = {
'a': '[aÀÁÂÃÄÅàáâãäåĀāąĄ]',
'c': '[cÇçćĆčČ]',
'd': '[dđĐďĎð]',
'e': '[eÈÉÊËèéêëěĚĒēęĘ]',
'i': '[iÌÍÎÏìíîïĪī]',
'l': '[lłŁ]',
'n': '[nÑñňŇńŃ]',
'o': '[oÒÓÔÕÕÖØòóôõöøŌō]',
'r': '[rřŘ]',
's': '[sŠšśŚ]',
't': '[tťŤ]',
'u': '[uÙÚÛÜùúûüůŮŪū]',
'y': '[yŸÿýÝ]',
'z': '[zŽžżŻźŹ]'
};
var asciifold = (function() {
var i, n, k, chunk;
var foreignletters = '';
var lookup = {};
for (k in DIACRITICS) {
if (DIACRITICS.hasOwnProperty(k)) {
chunk = DIACRITICS[k].substring(2, DIACRITICS[k].length - 1);
foreignletters += chunk;
for (i = 0, n = chunk.length; i < n; i++) {
lookup[chunk.charAt(i)] = k;
}
}
}
var regexp = new RegExp('[' + foreignletters + ']', 'g');
return function(str) {
return str.replace(regexp, function(foreignletter) {
return lookup[foreignletter];
}).toLowerCase();
};
})();
// export
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
return Sifter;
}));
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment