diff options
author | isteven <isteven@server.fake> | 2014-03-25 19:09:35 +0800 |
---|---|---|
committer | isteven <isteven@server.fake> | 2014-03-25 19:09:35 +0800 |
commit | 0c0a161a27584e96e7475057cba9d218074fbf4a (patch) | |
tree | a40797b832275be59b17ec8f3ab88822a25a3294 | |
download | angular-multi-select-0c0a161a27584e96e7475057cba9d218074fbf4a.zip angular-multi-select-0c0a161a27584e96e7475057cba9d218074fbf4a.tar.gz angular-multi-select-0c0a161a27584e96e7475057cba9d218074fbf4a.tar.bz2 |
First entry
First entry
-rw-r--r-- | angular-multi-select.css | 105 | ||||
-rw-r--r-- | angular-multi-select.js | 234 |
2 files changed, 339 insertions, 0 deletions
diff --git a/angular-multi-select.css b/angular-multi-select.css new file mode 100644 index 0000000..d54b5f5 --- /dev/null +++ b/angular-multi-select.css @@ -0,0 +1,105 @@ +/* app css stylesheet */ + +.multiSelect { +} + +.multiSelect.inlineBlock { + display: inline-block; +} + +.multiSelect .button { + display: inline-block; + margin-bottom: 0; + font-weight: normal; + text-align: center; + vertical-align: middle; + cursor: pointer; + background-image: none; + border: 1px solid transparent; + white-space: nowrap; + padding: 6px 12px; + font-size: 14px; + line-height: 1.428571429; + border-radius: 0px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; + user-select: none; +} + +.multiSelect .mainColor { + background-color: #5CB85C; + border-color: #4CAE4C; + color: #fff; +} + +.multiSelect .button > div { + float: left; +} + +.multiSelect .checkboxLayer { + background-color: #fff; + position: absolute; + z-index: 999; + border: 1px solid #cccccc; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); + background-clip: padding-box; + padding: 15px; +} + +.multiSelect .caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 15px !important; + vertical-align: middle; + border-top: 4px solid #fff; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + border-bottom: 0 dotted; +} + +.multiSelect label { + margin-right: 0px; + margin-bottom: 3px; + padding: 5px 8px 5px 8px; +} + +.multiSelect .checkboxSelected { + background-color: #428bca; + border-color: #357ebd; + color: #fff; + padding: 4px 7px 4px 7px; + border: 1px solid transparent; + border-radius: 1px; +} + +.multiSelect .vertical { + float: none; +} + +.multiSelect .horizontal { + float: left; +} + +.multiSelect input { + margin-right: 4px; +} + +.multiSelect .show { + display: inline-block; +} + +.multiSelect .hide { + display: none; +} + +.multiSelect .line { + margin-bottom: 15px; +} + + diff --git a/angular-multi-select.js b/angular-multi-select.js new file mode 100644 index 0000000..1ea6960 --- /dev/null +++ b/angular-multi-select.js @@ -0,0 +1,234 @@ +/* + * Ignatius Steven + * Tue, 14 Jan 2014 - 5:18:02 PM + * + * Angular JS Multi Select + * Creates a dropdown-like button with checkboxes. Accepts an array of object & modifies it based on the checkboxes status. + * + * Usage: + * + * <div + * multi-select + * list-items="data" + * item-label="firstName lastName" + * item-ticker="selected" + * orientation="vertical" + * max-labels="4" + * max-height="500" + * is-disabled="false" > + * </div> + * + * or + * + * <multi-select + * ... + * ... + * </multi-select> + * (*** be careful with browser compatibility) + * + * Attributes: + * + * list-items (REQUIRED): + * $scope variable. Array of objects such as + * [ + * { firstName: "John", lastName: "Robert" , ....., selected: false }, + * { firstName: "Jane", lastName: "Angel" , ....., selected: true }, + * { firstName: "Luke", lastName: "Skywalker" , ....., selected: true }, + * { firstName: "John", lastName: "Wayne" , ....., selected: true } + * ] + * item-label (String) (REQUIRED) + * The object property that you want to display on the button & checkboxes. Separate multiple values by space. + * Example: item-label="firstName lastName" + * item-ticker (String) (REQUIRED): + * Column name with a boolean value that represent the state of a checkbox. For example, in the list-items sample above, + * the item-ticker is "selected". So if selected === true, checkbox will be ticked. If selected === false, checkbox will not be ticked. + * If not specified, the default will be "selected" + * orientation (String - "vertical" | "horizontal") + * Orientation of the list of checkboxes. + * If not specified, the default will be "vertical". + * max-labels (String) + * Maximum number of items that will be displayed in the dropdown button. If not specified, will display all selected items. + * Example: "2" -> Using the list-items above, then button will display the first two selected: "Jane Angel, Luke Skywalker, ..." + * max-height (String) + * Maximum height of the list of checkboxes in pixels. Will show scrollbar on overflow. + * Example: "100". + * is-disabled (String - "true" | "false") + * Disable or enable the checkboxes. + * If not specified, the default will be "false". + * + */ + +angular.module( 'multi-select', ['ng'] ).directive( 'multiSelect' , [ '$timeout', '$compile' , function ( $timeout, $compile ) { + return { + restrict: + 'AE', + + replace: + true, + + scope: + { + listItems : '=', + itemLabel : '@', + itemTicker : '@', + orientation : '@', + maxLabels : '@', + maxHeight : '@', + isDisabled : '=', + }, + + template: + '<span class="multiSelect inlineBlock">' + + '<button type="button" class="multiSelect button mainColor multiSelectButton" ng-click="toggleCheckboxes( $event ); refreshSelectedItems();">' + + '<div ng-if="selectedItems.length <= 0">None selected</div>' + + '<div ng-if="selectedItems.length > 0" ng-repeat="item in selectedItems | limitTo: varMaxLabels">' + + '<span ng-if="$index > 0">, </span>{{writeLabel( item )}}' + + '</div>' + + '<span ng-if="more">, ...</span><span class="caret"></span>' + + '</button>' + + '<div class="multiSelect checkboxLayer hide" style="{{layerStyle}}">' + + '<div class="multiSelect line">' + + 'Tick: ' + + '<a ng-click="select( \'all\' )" class="multiSelect" >All</a> / ' + + '<a ng-click="select( \'none\' )" class="multiSelect" >None</a> / ' + + '<a ng-click="select( \'reset\' )" class="multiSelect" >Reset</a>' + + '</div>' + + '<div class="multiSelect line">' + + 'Filter: <input class="multiSelect" type="text" ng-model="labelFilter" />' + + '<a class="multiSelect" ng-click="labelFilter=\'\'">[Clear]</a>' + + '</div>' + + '<div ng-repeat="item in listItems | filter:labelFilter" ng-class="orientation" class="multiSelect">' + + '<label class="multiSelect" ng-class="{checkboxSelected:item[ itemSelector ]}">' + + '<input class="multiSelect" type="checkbox" ng-disabled="isDisabled" ng-checked="item[ itemSelector ]" ng-click="syncItems( item )" />' + + '{{writeLabel( item )}}' + + '</label> ' + + '</div>' + + '</div>' + + '</span>', + + link: function ( $scope, element, attrs ) { + + $scope.selectedItems = []; + $scope.backUp = []; + $scope.layerStyle = ''; + $scope.varMaxLabels = 0; + + $scope.syncItems = function( item ) { + item[ $scope.itemSelector ] = !item[ $scope.itemSelector ]; + $scope.refreshSelectedItems(); + } + + $scope.refreshSelectedItems = function() { + $scope.selectedItems = []; + angular.forEach( $scope.listItems, function( value, key ) { + if ( value[ $scope.itemSelector ] === true || value[ $scope.itemSelector ] === 'true' ) { + $scope.selectedItems.push( value ); + } + }); + if ( $scope.selectedItems.length > $scope.maxLabels ) { + $scope.more = true; + } + else { + $scope.more = false; + } + if ( typeof $scope.maxLabels === 'undefined' ) { + $scope.varMaxLabels = $scope.selectedItems.length; + } + else { + $scope.varMaxLabels = $scope.maxLabels; + } + } + + $scope.toggleCheckboxes = function( e ) { + + $scope.labelFilter = ''; + + var multiSelectIndex = -1; + var checkboxes = document.querySelectorAll( '.checkboxLayer' ); + var multiSelectButtons = document.querySelectorAll( '.multiSelectButton' ); + + for( i=0; i < multiSelectButtons.length; i++ ) { + if ( e.target == multiSelectButtons[ i ] ) { + multiSelectIndex = i; + break; + } + } + + for( i=0; i < checkboxes.length; i++ ) { + if ( i != multiSelectIndex ) { + checkboxes[i].className = 'multiSelect checkboxLayer hide'; + } + } + + if ( checkboxes[ multiSelectIndex ].className == 'multiSelect checkboxLayer hide' ) { + checkboxes[ multiSelectIndex ].className = 'multiSelect checkboxLayer show'; + } + else if ( checkboxes[ multiSelectIndex ].className == 'multiSelect checkboxLayer show' ) { + checkboxes[ multiSelectIndex ].className = 'multiSelect checkboxLayer hide'; + } + } + + $scope.select = function( type ) { + switch( type.toUpperCase() ) { + case 'ALL': + angular.forEach( $scope.listItems, function( value, key ) { + value[ $scope.itemSelector ] = true; + }); + break; + case 'NONE': + angular.forEach( $scope.listItems, function( value, key ) { + value[ $scope.itemSelector ] = false; + }); + break; + case 'RESET': + $scope.listItems = angular.copy( $scope.backUp ); + break; + default: + } + $scope.refreshSelectedItems(); + } + + $scope.writeLabel= function( item ) { + var label = ''; + angular.forEach( item, function( value1, key1 ) { + var temp = $scope.itemLabel.split( ' ' ); + angular.forEach( temp, function( value2, key2 ) { + if ( key1 == value2 ) { + label += ' ' + value1; + } + }); + }); + return label; + } + + angular.element( document ).bind( 'click' , function( e ) { + var checkboxes = document.querySelectorAll( '.checkboxLayer' ); + if ( e.target.className.indexOf( 'multiSelect' ) === -1 ) { + for( i=0; i < checkboxes.length; i++ ) { + checkboxes[i].className = 'multiSelect checkboxLayer hide'; + } + $scope.$apply(); + e.stopPropagation(); + } + }); + + // Start + $scope.itemSelector = ( typeof $scope.itemTicker === 'string' ? $scope.itemTicker : 'selected' ); + $scope.refreshSelectedItems(); + + $scope.$watch( 'listItems' , function( newVal ) { + $scope.refreshSelectedItems(); + $scope.backUp = angular.copy( $scope.listItems ); + }); + + $scope.$watch( 'isDisabled' , function( newVal ) { + $scope.isDisabled = newVal; + }); + + if ( typeof $scope.maxHeight !== 'undefined' ) { + $scope.layerStyle = 'max-height:' + $scope.maxHeight + 'px; overflow: auto; min-width: auto'; + } + } + } +}]); + |