123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- /*
- The MIT License (MIT)
- Copyright (c) 2014 Muhammed Ashik
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- */
- /*jshint indent: 2 */
- /*global angular: false */
- (function () {
- 'use strict';
- angular.module('multi-select-tree', []);
- }());
- /*jshint indent: 2 */
- (function () {
- 'use strict';
- angular.module('multi-select-tree').run(['$templateCache', function ($templateCache) {
- 'use strict';
- $templateCache.put('src/multi-select-tree.tpl.html',
- "<div class=\"tree-control\">\n" +
- "\n" +
- " <div class=\"tree-input form-control\" ng-click=\"onControlClicked($event)\">\n" +
- " <span ng-if=\"selectedItems.length == 0\" class=\"noselected-items\">\n" +
- " <span ng-bind=\"defaultLabel\" class=\"pull-left\"></span>\n" +
- " </span>\n" +
- " <span ng-if=\"selectedItems.length > 0\" class=\"selected-items\">\n" +
- " <span ng-repeat=\"selectedItem in selectedItems\" class=\"selected-item pull-left\">{{selectedItem.name||selectedItem[transLabel]}} <span class=\"selected-item-close\"\n" +
- " ng-click=\"deselectItem(selectedItem, $event)\"></span></span>\n" +
- " <span class=\"caret\"></span>\n" +
- " </span>\n" +
- " <!-- <input type=\"text\" class=\"blend-in\" /> -->\n" +
- " <i class=\"caret pull-right\"></i>\n" +
- " </div>\n" +
- " <div class=\"tree-view\" ng-show=\"showTree\">\n" +
- " <div class=\"helper-container\">\n" +
- " <div class=\"line\" data-ng-if=\"switchView\">\n" +
- " <button type=\"button\" ng-click=\"switchCurrentView($event);\" class=\"helper-button\">{{switchViewLabel}}</button>\n" +
- " </div>\n" +
- " <div class=\"line\">\n" +
- " <input placeholder=\"查询...\" type=\"text\" ng-model=\"filterKeyword\" ng-click=\"onFilterClicked($event)\"\n" +
- " class=\"input-filter\">\n" +
- " <span class=\"clear-button\" ng-click=\"clearFilter($event)\"><span class=\"item-close\"></span></span>\n" +
- " </div>\n" +
- " </div>\n" +
- " <ul class=\"tree-container\">\n" +
- " <tree-item class=\"top-level\" ng-repeat=\"item in inputModel\" item=\"item\" ng-show=\"!item.isFiltered\"\n" +
- " use-callback=\"useCallback\" can-select-item=\"canSelectItem\"\n" +
- " multi-select=\"multiSelect\" item-selected=\"itemSelected(item)\"\n" +
- " on-active-item=\"onActiveItem(item)\" select-only-leafs=\"selectOnlyLeafs\" trans-label=\"labelProp\"></tree-item>\n" +
- " </ul>\n" +
- " </div>\n" +
- "</div>\n"
- );
- $templateCache.put('src/tree-item.tpl.html',
- "<li>\n" +
- " <div class=\"item-container\" ng-class=\"{active: item.isActive, selected: item.selected}\"\n" +
- " ng-click=\"clickSelectItem(item, $event)\" ng-mouseover=\"onMouseOver(item, $event)\">\n" +
- " <span ng-if=\"showExpand(item)\" class=\"expand\" ng-class=\"{'expand-opened': item.isExpanded}\"\n" +
- " ng-click=\"onExpandClicked(item, $event)\"></span>\n" +
- "\n" +
- " <div class=\"item-details\"><input class=\"tree-checkbox\" type=\"checkbox\" ng-if=\"showCheckbox()\"\n" +
- " ng-checked=\"item.selected\"/>{{item.name||item[transLabel]}}\n" +
- " </div>\n" +
- " </div>\n" +
- " <ul ng-repeat=\"child in item.children\" ng-if=\"item.isExpanded\">\n" +
- " <tree-item item=\"child\" ng-if=\"!child.show\" item-selected=\"subItemSelected(item)\" use-callback=\"useCallback\"\n" +
- " can-select-item=\"canSelectItem\" multi-select=\"multiSelect\"\n" +
- " on-active-item=\"activeSubItem(item, $event)\" trans-label=\"transLabel\"></tree-item>\n" +
- " </ul>\n" +
- "</li>\n"
- );
- }]);
- }());
- /*global angular: false */
- (function () {
- 'use strict';
- var mainModule = angular.module('multi-select-tree');
- /**
- * Controller for multi select tree.
- */
- mainModule.controller('multiSelectTreeCtrl', [
- '$scope',
- '$document',
- '$timeout',
- function ($scope, $document, $timeout) {
- var activeItem;
- console.log($document)
- $scope.showTree = false;
- $scope.selectedItems = [];
- $scope.multiSelect = $scope.multiSelect || false;
- var _refreshDelayPromise;
- /**
- * refresh
- */
- $scope._refresh = function (refreshAttr) {
- if (refreshAttr !== undefined) {
- // Debounce
- // See https://github.com/angular-ui/bootstrap/blob/0.10.0/src/typeahead/typeahead.js#L155
- // FYI AngularStrap typeahead does not have debouncing: https://github.com/mgcrea/angular-strap/blob/v2.0.0-rc.4/src/typeahead/typeahead.js#L177
- if (_refreshDelayPromise) {
- $timeout.cancel(_refreshDelayPromise);
- }
- _refreshDelayPromise = $timeout(function () {
- $scope.refresh({
- items: $scope
- });
- }, $scope.refreshDelay);
- }
- };
- /**
- * Clicking on document will hide the tree.
- */
- function docClickHide() {
- closePopup();
- $scope.$apply();
- }
- /**
- * Closes the tree popup.
- */
- function closePopup() {
- $scope.showTree = false;
- if (activeItem) {
- activeItem.isActive = false;
- activeItem = undefined;
- }
- $document.off('click', docClickHide);
- }
- /**
- * Sets the active item.
- *
- * @param item the item element.
- */
- $scope.onActiveItem = function (item) {
- if (activeItem !== item) {
- if (activeItem) {
- activeItem.isActive = false;
- }
- activeItem = item;
- activeItem.isActive = true;
- }
- };
- /**
- * Copies the selectedItems in to output model.
- */
- $scope.refreshOutputModel = function () {
- var outModel;
- if ($scope.multiSelect) {
- outModel = angular.copy($scope.selectedItems);
- angular.forEach(outModel, function (item) {
- delete item.selected;
- })
- $scope.outputModel = outModel;
- } else {
- if ($scope.selectedItems.length > 0) {
- outModel = angular.copy($scope.selectedItems[0]);
- delete outModel.selected;
- }
- }
- $scope.outputModel = outModel; //angular.copy($scope.selectedItems);
- };
- /**
- * Refreshes the selected Items model.
- */
- $scope.$root.refreshSelectedItems =$scope.refreshSelectedItems = function () {
- $scope.selectedItems = [];
- if ($scope.inputModel) {
- setSelectedChildren($scope.inputModel);
- }
- };
- /**
- * Iterates over children and sets the selected items.
- *
- * @param children the children element.
- */
- function setSelectedChildren(children) {
- for (var i = 0, len = children.length; i < len; i++) {
- if (!isItemSelected(children[i]) && children[i].selected === true) {
- $scope.selectedItems.push(children[i]);
- } else if (isItemSelected(children[i]) && children[i].selected === false) {
- children[i].selected = true;
- }
- if (children[i] && children[i].children) {
- setSelectedChildren(children[i].children);
- }
- }
- }
- /**
- * Checks of the item is already selected.
- *
- * @param item the item to be checked.
- * @return {boolean} if the item is already selected.
- */
- function isItemSelected(item) {
- var isSelected = false;
- if ($scope.selectedItems) {
- for (var i = 0; i < $scope.selectedItems.length; i++) {
- if ($scope.selectedItems[i].id === item.id) {
- isSelected = true;
- break;
- }
- }
- }
- return isSelected;
- }
- /**
- * Deselect the item.
- *
- * @param item the item element
- * @param $event
- */
- $scope.deselectItem = function (item, $event) {
- $event.stopPropagation();
- if ($scope.itemSelected) {
- $scope.itemSelected({
- item: item
- });
- }
- $scope.selectedItems.splice($scope.selectedItems.indexOf(item), 1);
- item.selected = false;
- this.refreshOutputModel();
- };
- /**
- * Swap the tree popup on control click event.
- *
- * @param $event the click event.
- */
- $scope.onControlClicked = function ($event) {
- $event.stopPropagation();
- $scope.showTree = !$scope.showTree;
- if ($scope.showTree) {
- $document.on('click', docClickHide);
- }
- };
- /**
- * Stop the event on filter clicked.
- *
- * @param $event the click event
- */
- $scope.onFilterClicked = function ($event) {
- $event.stopPropagation();
- };
- /**
- * Clears the filter text.
- *
- * @param $event the click event
- */
- $scope.clearFilter = function ($event) {
- $event.stopPropagation();
- $scope.filterKeyword = '';
- };
- /**
- * Wrapper function for can select item callback.
- *
- * @param item the item
- */
- $scope.canSelectItem = function (item) {
- return $scope.callback({
- item: item,
- selectedItems: $scope.selectedItems
- });
- };
- /**
- * The callback is used to switch the views.
- * based on the view type.
- *
- * @param $event the event object.
- */
- $scope.switchCurrentView = function ($event) {
- $event.stopPropagation();
- $scope.switchViewCallback({
- scopeObj: $scope
- });
- };
- /**
- * Handles the item select event.
- *
- * @param item the selected item.
- */
- $scope.itemSelected = function (item) {
- if ($scope.useCallback && $scope.canSelectItem(item) === false || $scope.selectOnlyLeafs && item.children && item.children.length > 0) {
- return;
- }
- if (!$scope.multiSelect) {
- closePopup();
- for (var i = 0; i < $scope.selectedItems.length; i++) {
- $scope.selectedItems[i].selected = false;
- }
- item.selected = true;
- $scope.selectedItems = [];
- $scope.selectedItems.push(item);
- } else {
- item.selected = true;
- var indexOfItem = $scope.selectedItems.indexOf(item);
- if (isItemSelected(item)) {
- item.selected = false;
- $scope.selectedItems.splice(indexOfItem, 1);
- } else {
- $scope.selectedItems.push(item);
- }
- }
- this.refreshOutputModel();
- };
- }
- ]);
- /**
- * sortableItem directive.
- */
- mainModule.directive('multiSelectTree', function () {
- return {
- restrict: 'E',
- templateUrl: 'src/multi-select-tree.tpl.html',
- scope: {
- inputModel: '=',
- outputModel: '=?',
- multiSelect: '=?',
- switchView: '=?',
- switchViewLabel: '@',
- switchViewCallback: '&',
- selectOnlyLeafs: '=?',
- callback: '&',
- refresh: '&',
- refreshDelay: '@',
- defaultLabel: '@',
- transLabel: '@'
- },
- link: function (scope, element, attrs) {
- if (attrs.callback) {
- scope.useCallback = true;
- }
- if (attrs.transLabel) {
- scope.labelProp = attrs.transLabel;
- }
- //refresh
- attrs.$observe('refreshDelay', function () {
- // $eval() is needed otherwise we get a string instead of a number
- var refreshDelay = scope.$eval(attrs.refreshDelay);
- scope.refreshDelay = refreshDelay !== undefined ? refreshDelay : 100;
- //refresh
- scope._refresh(attrs.refresh);
- });
- // watch for changes in input model as a whole
- // this on updates the multi-select when a user load a whole new input-model.
- scope.$watch('inputModel', function (newVal) {
- if (newVal) {
- scope.refreshSelectedItems();
- scope.refreshOutputModel();
- }
- });
- /**
- * Checks whether any of children match the keyword.
- *
- * @param item the parent item
- * @param keyword the filter keyword
- * @returns {boolean} false if matches.
- */
- function isChildrenFiltered(item, keyword) {
- var childNodes = getAllChildNodesFromNode(item, []);
- for (var i = 0, len = childNodes.length; i < len; i++) {
- if (childNodes[i][scope.labelProp || 'name'].indexOf(keyword) !== -1) {
- //if (childNodes[i].name.toLowerCase().indexOf(keyword.toLowerCase()) !== -1) {
- return false;
- }
- }
- return true;
- }
- /**
- * Return all childNodes of a given node (as Array of Nodes)
- */
- function getAllChildNodesFromNode(node, childNodes) {
- for (var i = 0; i < node.children.length; i++) {
- childNodes.push(node.children[i]);
- // add the childNodes from the children if available
- getAllChildNodesFromNode(node.children[i], childNodes);
- }
- return childNodes;
- }
- // 树形组件输入框输入
- scope.$watch('filterKeyword', function () {
- if (scope.filterKeyword !== undefined) {
- if (scope.$root.bala1) {
- scope.$root.bala1(scope, function (list) {
- scope.inputModel = scope.selectModel = list;
- var vals = [];
- if (scope.filterKeyword !== undefined) {
- setTimeout(() => {
- angular.forEach(scope.inputModel, function (item, index) {
- item.isExpanded = true;
- });
- }, 1);
- //console.log(vals);
- //if (vals && vals.length > 0) {
- // console.log(vals);
- setTimeout(() => {
- angular.forEach(scope.inputModel, function (item, index) {
- //item.isExpanded=true;
- if (item.children && item.children.length > 0) {
- item.children.forEach((val, idx) => {
- vals.push(val);
- })
- }
- });
- setTimeout(() => {
- if (vals && vals.length > 0) {
- vals.forEach((val, idx) => {
- val.isExpanded = true;
- })
- }
- }, 600);
- }, 1);
- }
- })
- }
- }
- // console.log(scope.$root.bala1)
- });
- },
- controller: 'multiSelectTreeCtrl'
- };
- });
- }());
- /*jshint indent: 2 */
- /*global angular: false */
- (function () {
- 'use strict';
- var mainModule = angular.module('multi-select-tree');
- /**
- * Controller for sortable item.
- *
- * @param $scope - drag item scope
- */
- mainModule.controller('treeItemCtrl', [
- '$scope',
- function ($scope) {
- $scope.item.isExpanded = false;
- /**
- * Shows the expand option.
- *
- * @param item the item
- * @returns {*|boolean}
- */
- $scope.showExpand = function (item) {
- return item.children && item.children.length > 0;
- };
- /**
- * On expand clicked toggle the option.
- *
- * @param item the item
- * @param $event
- */
- $scope.onExpandClicked = function (item, $event) {
- if ($scope.$root.checkClicked) {
- $scope.$root.checkClicked(item)
- }
- $event.stopPropagation();
- item.isExpanded = !item.isExpanded;
- };
- /**
- * Event on click of select item.
- *
- * @param item the item
- * @param $event
- */
- $scope.clickSelectItem = function (item, $event) {
- $scope.onExpandClicked(item, $event);
- $event.stopPropagation();
- if ($scope.itemSelected) {
- $scope.itemSelected({
- item: item
- });
- }
- };
- /**
- * Is leaf selected.
- *
- * @param item the item
- * @param $event
- */
- $scope.subItemSelected = function (item, $event) {
- if ($scope.itemSelected) {
- $scope.itemSelected({
- item: item
- });
- }
- };
- /**
- * Active sub item.
- *
- * @param item the item
- * @param $event
- */
- $scope.activeSubItem = function (item, $event) {
- if ($scope.onActiveItem) {
- $scope.onActiveItem({
- item: item
- });
- }
- };
- /**
- * On mouse over event.
- *
- * @param item the item
- * @param $event
- */
- $scope.onMouseOver = function (item, $event) {
- $event.stopPropagation();
- if ($scope.onActiveItem) {
- $scope.onActiveItem({
- item: item
- });
- }
- };
- /**
- * Can select item.
- *
- * @returns {*}
- */
- $scope.showCheckbox = function () {
- if (!$scope.multiSelect) {
- return false;
- }
- if ($scope.selectOnlyLeafs) {
- return false;
- }
- if ($scope.useCallback) {
- return $scope.canSelectItem($scope.item);
- }
- };
- }
- ]);
- /**
- * sortableItem directive.
- */
- mainModule.directive('treeItem', [
- '$compile',
- function ($compile) {
- return {
- restrict: 'E',
- templateUrl: 'src/tree-item.tpl.html',
- scope: {
- item: '=',
- itemSelected: '&',
- onActiveItem: '&',
- multiSelect: '=?',
- selectOnlyLeafs: '=?',
- isActive: '=',
- useCallback: '=',
- canSelectItem: '=',
- transLabel: '='
- },
- controller: 'treeItemCtrl',
- compile: function (element, attrs, link) {
- // Normalize the link parameter
- if (angular.isFunction(link)) {
- link = {
- post: link
- };
- }
- // Break the recursion loop by removing the contents
- var contents = element.contents().remove();
- var compiledContents;
- return {
- pre: link && link.pre ? link.pre : null,
- post: function (scope, element, attrs) {
- // Compile the contents
- if (!compiledContents) {
- compiledContents = $compile(contents);
- }
- // Re-add the compiled contents to the element
- compiledContents(scope, function (clone) {
- element.append(clone);
- });
- // Call the post-linking function, if any
- if (link && link.post) {
- link.post.apply(null, arguments);
- }
- }
- };
- }
- };
- }
- ]);
- }());
|