angular-gantt
Version:
Gantt chart component for AngularJS
471 lines (393 loc) • 17.8 kB
JavaScript
/*
Project: angular-gantt v1.2.9 - Gantt chart component for AngularJS
Authors: Marco Schweighauser, Rémi Alvergnat
License: MIT
Homepage: https://www.angular-gantt.com
Github: https://github.com/angular-gantt/angular-gantt.git
*/
(function(){
'use strict';
angular.module('gantt.tree', ['gantt', 'gantt.tree.templates', 'ui.tree']).directive('ganttTree', ['ganttUtils', '$compile', '$document', function(utils, $compile, $document) {
// Provides the row sort functionality to any Gantt row
// Uses the sortableState to share the current row
return {
restrict: 'E',
require: '^gantt',
scope: {
enabled: '=?',
header: '=?',
content: '=?',
headerContent: '=?',
keepAncestorOnFilterRow: '=?'
},
link: function(scope, element, attrs, ganttCtrl) {
var api = ganttCtrl.gantt.api;
// Load options from global options attribute.
if (scope.options && typeof(scope.options.sortable) === 'object') {
for (var option in scope.options.sortable) {
scope[option] = scope.options[option];
}
}
if (scope.enabled === undefined) {
scope.enabled = true;
}
if (scope.header === undefined) {
scope.header = 'Name';
}
if (scope.headerContent === undefined) {
scope.headerContent = '{{getHeader()}}';
}
if (scope.keepAncestorOnFilterRow === undefined) {
scope.keepAncestorOnFilterRow = false;
}
api.directives.on.new(scope, function(directiveName, sideContentScope, sideContentElement) {
if (directiveName === 'ganttSideContent') {
var labelsScope = sideContentScope.$new();
labelsScope.pluginScope = scope;
var ifElement = $document[0].createElement('div');
angular.element(ifElement).attr('data-ng-if', 'pluginScope.enabled');
angular.element(ifElement).addClass('side-element');
var labelsElement = $document[0].createElement('gantt-side-content-tree');
angular.element(ifElement).append(labelsElement);
sideContentElement.append($compile(ifElement)(labelsScope));
}
});
}
};
}]);
}());
(function(){
'use strict';
angular.module('gantt.tree').directive('ganttRowTreeLabel', ['GanttDirectiveBuilder', function(Builder) {
var builder = new Builder('ganttRowTreeLabel');
builder.restrict = 'A';
builder.templateUrl = undefined;
return builder.build();
}]);
}());
(function(){
'use strict';
angular.module('gantt.tree').directive('ganttSideContentTree', ['GanttDirectiveBuilder', function(Builder) {
var builder = new Builder('ganttSideContentTree', 'plugins/tree/sideContentTree.tmpl.html');
return builder.build();
}]);
}());
(function(){
'use strict';
angular.module('gantt.tree').controller('GanttTreeController', ['$scope', '$filter', 'GanttHierarchy', function($scope, $filter, Hierarchy) {
$scope.rootRows = [];
$scope.getHeader = function() {
return $scope.pluginScope.header;
};
var hierarchy = new Hierarchy();
$scope.pluginScope.$watchGroup(['keepAncestorOnFilterRow', 'enabled'], function(value) {
var keepAncestor = value[0] && value[1];
if (keepAncestor) {
var filterImpl = function(sortedRows, filterRow, filterRowComparator) {
hierarchy.refresh(sortedRows);
var leaves = [];
angular.forEach(sortedRows, function(row) {
var children = hierarchy.children(row);
if (!children || children.length === 0) {
leaves.push(row);
}
});
var filteredLeaves = $filter('filter')(leaves, filterRow, filterRowComparator);
var filterRowKeepAncestor = function(row) {
if (filteredLeaves.indexOf(row) > -1) {
return true;
}
var descendants = hierarchy.descendants(row);
for (var i=0; i < descendants.length; i++) {
if (filteredLeaves.indexOf(descendants[i]) > -1) {
return true;
}
}
return false;
};
return $filter('filter')(sortedRows, filterRowKeepAncestor, filterRowComparator);
};
$scope.gantt.rowsManager.setFilterImpl(filterImpl);
} else {
$scope.gantt.rowsManager.setFilterImpl(false);
}
});
var isVisible = function(row) {
var parentRow = $scope.parent(row);
while (parentRow !== undefined) {
if (parentRow !== undefined && parentRow._collapsed) {
return false;
}
parentRow = $scope.parent(parentRow);
}
return true;
};
var filterRowsFunction = function(rows) {
return rows.filter(function(row) {
return isVisible(row);
});
};
var sortRowsFunction = function(rows) {
var sortedRows = [];
var rootRows = [];
var hasParent = false;
angular.forEach(rows, function(row) {
var rowParent = $scope.parent(row);
if (rowParent === undefined) {
rootRows.push(row);
} else {
hasParent = true;
}
});
var handleChildren = function(row) {
sortedRows.push(row);
var children = $scope.children(row);
if (children !== undefined && children.length > 0) {
var sortedChildren = children.sort(function(a, b) {
return rows.indexOf(a) - rows.indexOf(b);
});
angular.forEach(sortedChildren, function(child) {
handleChildren(child);
});
}
};
angular.forEach(rootRows, function(row) {
handleChildren(row);
});
return sortedRows;
};
$scope.gantt.api.rows.addRowSorter(sortRowsFunction);
$scope.gantt.api.rows.addRowFilter(filterRowsFunction);
$scope.$on('$destroy', function() {
$scope.gantt.api.rows.removeRowSorter(sortRowsFunction);
$scope.gantt.api.rows.removeRowFilter(filterRowsFunction);
});
var refresh = function() {
$scope.rootRows = hierarchy.refresh($scope.gantt.rowsManager.filteredRows);
if ($scope.gantt.rowsManager.filteredRows.length > 0) {
$scope.gantt.api.rows.sort();
$scope.gantt.api.rows.refresh();
}
};
$scope.gantt.api.rows.on.remove($scope, refresh);
$scope.gantt.api.rows.on.add($scope, refresh);
var isRowCollapsed = function(rowId) {
var row;
if (typeof rowId === 'string') {
row = $scope.gantt.rowsManager.rowsMap[rowId];
} else {
row = rowId;
}
if (row === undefined) {
return undefined;
}
if (row._collapsed === undefined) {
return false;
}
return row._collapsed;
};
var expandRow = function(rowId) {
var row;
if (typeof rowId === 'string') {
row = $scope.gantt.rowsManager.rowsMap[rowId];
} else {
row = rowId;
}
if (row === undefined) {
return;
}
var rowScope = $scope.nodeScopes[row.model.id];
if (rowScope.collapsed) {
rowScope.toggle();
}
};
var collapseRow = function(rowId) {
var row;
if (typeof rowId === 'string') {
row = $scope.gantt.rowsManager.rowsMap[rowId];
} else {
row = rowId;
}
if (row === undefined) {
return;
}
var rowScope = $scope.nodeScopes[row.model.id];
if (!rowScope.collapsed) {
rowScope.toggle();
}
};
var getHierarchy = function() {
return hierarchy;
};
$scope.getHeaderContent = function() {
return $scope.pluginScope.headerContent;
};
$scope.gantt.api.registerMethod('tree', 'refresh', refresh, this);
$scope.gantt.api.registerMethod('tree', 'isCollapsed', isRowCollapsed, this);
$scope.gantt.api.registerMethod('tree', 'expand', expandRow, this);
$scope.gantt.api.registerMethod('tree', 'collapse', collapseRow, this);
$scope.gantt.api.registerEvent('tree', 'collapsed');
$scope.gantt.api.registerMethod('tree', 'getHierarchy', getHierarchy, this);
$scope.$watchCollection('gantt.rowsManager.filteredRows', function() {
refresh();
});
$scope.children = function(row) {
if (row === undefined) {
return $scope.rootRows;
}
return hierarchy.children(row);
};
$scope.parent = function(row) {
return hierarchy.parent(row);
};
$scope.nodeScopes = {};
}]).controller('GanttUiTreeController', ['$scope', function($scope) {
var collapseAll = function() {
$scope.collapseAll();
};
var expandAll = function() {
$scope.expandAll();
};
$scope.gantt.api.registerMethod('tree', 'collapseAll', collapseAll, $scope);
$scope.gantt.api.registerMethod('tree', 'expandAll', expandAll, $scope);
}]).controller('GanttTreeNodeController', ['$scope', function($scope) {
$scope.$parent.nodeScopes[$scope.row.model.id] = $scope;
$scope.$on('$destroy', function() {
delete $scope.$parent.nodeScopes[$scope.row.model.id];
});
$scope.$watch('children(row)', function(newValue) {
if (newValue) {
// Children rows may have been filtered out
// So we need to filter the raw hierarchy before displaying children in tree.
var visibleRows = $scope.row.rowsManager.filteredRows;
var filteredChildrenRows = [];
for (var i=0; i < newValue.length; i++) {
var childRow = newValue[i];
if (visibleRows.indexOf(childRow) > -1) {
filteredChildrenRows.push(childRow);
}
}
$scope.$parent.childrenRows = filteredChildrenRows;
} else {
$scope.$parent.childrenRows = newValue;
}
});
$scope.isCollapseDisabled = function(){
return !$scope.$parent.childrenRows || $scope.$parent.childrenRows.length === 0;
};
$scope.getValue = function() {
return $scope.row.model.name;
};
$scope.getRowContent = function() {
if ($scope.row.model.content !== undefined) {
return $scope.row.model.content;
}
if ($scope.pluginScope.content !== undefined) {
return $scope.pluginScope.content;
}
var content = $scope.row.rowsManager.gantt.options.value('rowContent');
if (content === undefined) {
content = '{{row.model.name}}';
}
return content;
};
$scope.$watch('collapsed', function(newValue) {
if ($scope.$modelValue._collapsed !== newValue) {
var oldValue = $scope.$modelValue._collapsed;
$scope.$modelValue._collapsed = newValue; // $modelValue contains the Row object
if (oldValue !== undefined && newValue !== oldValue) {
$scope.gantt.api.tree.raise.collapsed($scope, $scope.$modelValue, newValue);
$scope.gantt.api.rows.refresh();
}
}
});
}]);
}());
(function(){
'use strict';
angular.module('gantt.tree').directive('ganttTreeBody', ['GanttDirectiveBuilder', 'ganttLayout', function(Builder, layout) {
var builder = new Builder('ganttTreeBody', 'plugins/tree/treeBody.tmpl.html');
builder.controller = function($scope) {
var hScrollBarHeight = layout.getScrollBarHeight();
$scope.getLabelsCss = function() {
var css = {};
if ($scope.maxHeight) {
var bodyScrollBarHeight = $scope.gantt.scroll.isHScrollbarVisible() ? hScrollBarHeight : 0;
css['max-height'] = $scope.maxHeight - bodyScrollBarHeight - $scope.gantt.header.getHeight() + 'px';
}
return css;
};
};
return builder.build();
}]);
}());
(function(){
'use strict';
angular.module('gantt.tree').directive('ganttTreeHeader', ['GanttDirectiveBuilder', function(Builder) {
var builder = new Builder('ganttTreeHeader', 'plugins/tree/treeHeader.tmpl.html');
return builder.build();
}]);
}());
angular.module('gantt.tree.templates', []).run(['$templateCache', function($templateCache) {
$templateCache.put('plugins/tree/sideContentTree.tmpl.html',
'<div class="gantt-side-content-tree" ng-controller="GanttTreeController">\n' +
' <gantt-tree-header>\n' +
' </gantt-tree-header>\n' +
' <gantt-tree-body>\n' +
' </gantt-tree-body>\n' +
'</div>\n' +
'');
$templateCache.put('plugins/tree/treeBody.tmpl.html',
'<div class="gantt-tree-body" ng-style="getLabelsCss()">\n' +
' <div gantt-vertical-scroll-receiver>\n' +
' <div class="gantt-row-label-background">\n' +
' <div class="gantt-row-label gantt-row-height"\n' +
' ng-class="row.model.classes"\n' +
' ng-style="{\'height\': row.model.height}"\n' +
' ng-repeat="row in gantt.rowsManager.visibleRows track by row.model.id">\n' +
' \n' +
' </div>\n' +
' </div>\n' +
' <div ui-tree ng-controller="GanttUiTreeController" data-drag-enabled="false" data-empty-place-holder-enabled="false">\n' +
' <ol class="gantt-tree-root" ui-tree-nodes ng-model="rootRows">\n' +
' <li ng-repeat="row in rootRows" ui-tree-node\n' +
' ng-include="\'plugins/tree/treeBodyChildren.tmpl.html\'">\n' +
' </li>\n' +
' </ol>\n' +
' </div>\n' +
' </div>\n' +
'</div>\n' +
'');
$templateCache.put('plugins/tree/treeBodyChildren.tmpl.html',
'<div ng-controller="GanttTreeNodeController"\n' +
' class="gantt-row-label gantt-row-height"\n' +
' ng-class="row.model.classes"\n' +
' ng-style="{\'height\': row.model.height}">\n' +
' <div class="gantt-valign-container">\n' +
' <div class="gantt-valign-content">\n' +
' <a ng-disabled="isCollapseDisabled()" data-nodrag\n' +
' class="gantt-tree-handle-button btn btn-xs"\n' +
' ng-class="{\'gantt-tree-collapsed\': collapsed, \'gantt-tree-expanded\': !collapsed}"\n' +
' ng-click="!isCollapseDisabled() && toggle()"><span\n' +
' class="gantt-tree-handle glyphicon glyphicon-chevron-down"\n' +
' ng-class="{\n' +
' \'glyphicon-chevron-right\': collapsed, \'glyphicon-chevron-down\': !collapsed,\n' +
' \'gantt-tree-collapsed\': collapsed, \'gantt-tree-expanded\': !collapsed}"></span>\n' +
' </a>\n' +
' <span gantt-row-label class="gantt-label-text" gantt-bind-compile-html="getRowContent()"/>\n' +
' </div>\n' +
' </div>\n' +
'</div>\n' +
'<ol ui-tree-nodes ng-class="{hidden: collapsed}" ng-model="childrenRows">\n' +
' <li ng-repeat="row in childrenRows" ui-tree-node>\n' +
' <div ng-include="\'plugins/tree/treeBodyChildren.tmpl.html\'"></div>\n' +
' </li>\n' +
'</ol>\n' +
'');
$templateCache.put('plugins/tree/treeHeader.tmpl.html',
'<div class="gantt-tree-header" ng-style="{height: $parent.ganttHeaderHeight + \'px\'}">\n' +
' <div ng-if="$parent.ganttHeaderHeight" class="gantt-row-label gantt-row-label-header gantt-tree-row gantt-tree-header-row"><span class="gantt-label-text" gantt-bind-compile-html="getHeaderContent()"/></div>\n' +
'</div>\n' +
'');
}]);
//# sourceMappingURL=angular-gantt-tree-plugin.js.map