UNPKG

angular-gantt

Version:

Gantt chart component for AngularJS

447 lines (367 loc) 18.8 kB
(function(){ 'use strict'; angular.module('gantt').factory('GanttColumnsManager', ['GanttColumnGenerator', 'GanttColumnBuilder', 'GanttHeadersGenerator', '$filter', '$timeout', 'ganttLayout', 'ganttBinarySearch', 'moment', function(ColumnGenerator, ColumnBuilder, HeadersGenerator, $filter, $timeout, layout, bs, moment) { var ColumnsManager = function(gantt) { var self = this; this.gantt = gantt; this.from = undefined; this.to = undefined; this.columns = []; this.visibleColumns = []; this.previousColumns = []; this.nextColumns = []; this.headers = []; this.visibleHeaders = []; this.scrollAnchor = undefined; this.columnBuilder = new ColumnBuilder(this); // Add a watcher if a view related setting changed from outside of the Gantt. Update the gantt accordingly if so. // All those changes need a recalculation of the header columns this.gantt.$scope.$watchGroup(['viewScale', 'columnWidth', 'timeFramesWorkingMode', 'timeFramesNonWorkingMode', 'fromDate', 'toDate', 'autoExpand', 'taskOutOfRange'], function(newValues, oldValues) { if (newValues !== oldValues && self.gantt.rendered) { self.generateColumns(); } }); this.gantt.$scope.$watchCollection('headers', function(newValues, oldValues) { if (newValues !== oldValues && self.gantt.rendered) { self.generateColumns(); } }); this.gantt.$scope.$watchCollection('headersFormats', function(newValues, oldValues) { if (newValues !== oldValues && self.gantt.rendered) { self.generateColumns(); } }); this.gantt.$scope.$watchGroup(['ganttElementWidth', 'showSide', 'sideWidth', 'maxHeight', 'daily'], function(newValues, oldValues) { if (newValues !== oldValues && self.gantt.rendered) { self.updateColumnsMeta(); } }); this.gantt.api.data.on.load(this.gantt.$scope, function() { if ((self.from === undefined || self.to === undefined || self.from > self.gantt.rowsManager.getDefaultFrom() || self.to < self.gantt.rowsManager.getDefaultTo()) && self.gantt.rendered) { self.generateColumns(); } self.gantt.rowsManager.sortRows(); }); this.gantt.api.data.on.remove(this.gantt.$scope, function() { self.gantt.rowsManager.sortRows(); }); this.gantt.api.registerMethod('columns', 'clear', this.clearColumns, this); this.gantt.api.registerMethod('columns', 'generate', this.generateColumns, this); this.gantt.api.registerMethod('columns', 'refresh', this.updateColumnsMeta, this); this.gantt.api.registerMethod('columns', 'getColumnsWidth', this.getColumnsWidth, this); this.gantt.api.registerMethod('columns', 'getColumnsWidthToFit', this.getColumnsWidthToFit, this); this.gantt.api.registerMethod('columns', 'getDateRange', this.getDateRange, this); this.gantt.api.registerEvent('columns', 'clear'); this.gantt.api.registerEvent('columns', 'generate'); this.gantt.api.registerEvent('columns', 'refresh'); }; ColumnsManager.prototype.setScrollAnchor = function() { if (this.gantt.scroll.$element && this.columns.length > 0) { var el = this.gantt.scroll.$element[0]; var center = el.scrollLeft + el.offsetWidth / 2; this.scrollAnchor = this.gantt.getDateByPosition(center); } }; ColumnsManager.prototype.scrollToScrollAnchor = function() { var self = this; if (this.columns.length > 0 && this.scrollAnchor !== undefined) { // Ugly but prevents screen flickering (unlike $timeout) this.gantt.$scope.$$postDigest(function() { self.gantt.api.scroll.toDate(self.scrollAnchor); }); } }; ColumnsManager.prototype.clearColumns = function() { this.setScrollAnchor(); this.from = undefined; this.to = undefined; this.columns = []; this.visibleColumns = []; this.previousColumns = []; this.nextColumns = []; this.headers = []; this.visibleHeaders = []; this.gantt.api.columns.raise.clear(); }; ColumnsManager.prototype.generateColumns = function(from, to) { if (!from) { from = this.gantt.options.value('fromDate'); } if (!to) { to = this.gantt.options.value('toDate'); } if (!from || (moment.isMoment(from) && !from.isValid())) { from = this.gantt.rowsManager.getDefaultFrom(); if (!from) { return false; } } if (!to || (moment.isMoment(to) && !to.isValid())) { to = this.gantt.rowsManager.getDefaultTo(); if (!to) { return false; } } if (from !== undefined && !moment.isMoment(from)) { from = moment(from); } if (to !== undefined && !moment.isMoment(to)) { to = moment(to); } if (this.gantt.options.value('taskOutOfRange') === 'expand') { from = this.gantt.rowsManager.getExpandedFrom(from); to = this.gantt.rowsManager.getExpandedTo(to); } this.setScrollAnchor(); this.from = from; this.to = to; this.columns = ColumnGenerator.generate(this.columnBuilder, from, to, this.gantt.options.value('viewScale'), this.getColumnsWidth()); this.headers = HeadersGenerator.generate(this); this.previousColumns = []; this.nextColumns = []; this.updateColumnsMeta(); this.scrollToScrollAnchor(); this.gantt.api.columns.raise.generate(this.columns, this.headers); }; ColumnsManager.prototype.updateColumnsMeta = function() { this.gantt.isRefreshingColumns = true; var lastColumn = this.getLastColumn(); this.gantt.originalWidth = lastColumn !== undefined ? lastColumn.originalSize.left + lastColumn.originalSize.width : 0; var columnsWidthChanged = this.updateColumnsWidths(this.columns, this.headers, this.previousColumns, this.nextColumns); this.gantt.width = lastColumn !== undefined ? lastColumn.left + lastColumn.width : 0; var showSide = this.gantt.options.value('showSide'); var sideShown = this.gantt.side.isShown(); var sideVisibilityChanged = showSide !== sideShown; if (sideVisibilityChanged && !showSide) { // Prevent unnecessary v-scrollbar if side is hidden here this.gantt.side.show(false); } this.gantt.rowsManager.updateTasksPosAndSize(); this.gantt.timespansManager.updateTimespansPosAndSize(); this.updateVisibleColumns(columnsWidthChanged); this.gantt.rowsManager.updateVisibleObjects(); var currentDateValue = this.gantt.options.value('currentDateValue'); this.gantt.currentDateManager.setCurrentDate(currentDateValue); if (sideVisibilityChanged && showSide) { // Prevent unnecessary v-scrollbar if side is shown here this.gantt.side.show(true); } this.gantt.isRefreshingColumns = false; this.gantt.api.columns.raise.refresh(this.columns, this.headers); }; // Returns the last Gantt column or undefined ColumnsManager.prototype.getLastColumn = function(extended) { var columns = this.columns; if (extended) { columns = this.nextColumns; } if (columns && columns.length > 0) { return columns[columns.length - 1]; } else { return undefined; } }; // Returns the first Gantt column or undefined ColumnsManager.prototype.getFirstColumn = function(extended) { var columns = this.columns; if (extended) { columns = this.previousColumns; } if (columns && columns.length > 0) { return columns[0]; } else { return undefined; } }; // Returns the column at the given or next possible date ColumnsManager.prototype.getColumnByDate = function(date, disableExpand) { if (!disableExpand) { this.expandExtendedColumnsForDate(date); } var extendedColumns = this.previousColumns.concat(this.columns, this.nextColumns); var columns = bs.get(extendedColumns, date, function(c) { return c.date; }, true); return columns[0] === undefined ? columns[1] : columns[0]; }; // Returns the column at the given position x (in em) ColumnsManager.prototype.getColumnByPosition = function(x, disableExpand) { if (!disableExpand) { this.expandExtendedColumnsForPosition(x); } var extendedColumns = this.previousColumns.concat(this.columns, this.nextColumns); var columns = bs.get(extendedColumns, x, function(c) { return c.left; }, true); return columns[0] === undefined ? columns[1]: columns[0]; }; ColumnsManager.prototype.updateColumnsWidths = function(columns, headers, previousColumns, nextColumns) { var columnWidth = this.gantt.options.value('columnWidth'); var expandToFit = this.gantt.options.value('expandToFit'); var shrinkToFit = this.gantt.options.value('shrinkToFit'); if (columnWidth === undefined || expandToFit || shrinkToFit) { var newWidth = this.gantt.getBodyAvailableWidth(); var lastColumn = this.gantt.columnsManager.getLastColumn(false); if (lastColumn !== undefined) { var currentWidth = lastColumn.originalSize.left + lastColumn.originalSize.width; if (expandToFit && currentWidth < newWidth || shrinkToFit && currentWidth > newWidth || columnWidth === undefined ) { var widthFactor = newWidth / currentWidth; layout.setColumnsWidthFactor(columns, widthFactor); angular.forEach(headers, function(header) { layout.setColumnsWidthFactor(header, widthFactor); }); // previous and next columns will be generated again on need. previousColumns.splice(0, this.previousColumns.length); nextColumns.splice(0, this.nextColumns.length); return true; } } } return false; }; ColumnsManager.prototype.getColumnsWidth = function() { var columnWidth = this.gantt.options.value('columnWidth'); if (columnWidth === undefined) { if (!this.gantt.width || this.gantt.width <= 0) { columnWidth = 20; } else { columnWidth = this.gantt.width / this.columns.length; } } return columnWidth; }; ColumnsManager.prototype.getColumnsWidthToFit = function() { return this.gantt.getBodyAvailableWidth() / this.columns.length; }; ColumnsManager.prototype.expandExtendedColumnsForPosition = function(x) { var viewScale; if (x < 0) { var firstColumn = this.getFirstColumn(); var from = firstColumn.date; var firstExtendedColumn = this.getFirstColumn(true); if (!firstExtendedColumn || firstExtendedColumn.left > x) { viewScale = this.gantt.options.value('viewScale'); this.previousColumns = ColumnGenerator.generate(this.columnBuilder, from, undefined, viewScale, this.getColumnsWidth(), -x, 0, true); } return true; } else if (x > this.gantt.width) { var lastColumn = this.getLastColumn(); var endDate = lastColumn.getDateByPosition(lastColumn.width); var lastExtendedColumn = this.getLastColumn(true); if (!lastExtendedColumn || lastExtendedColumn.left + lastExtendedColumn.width < x) { viewScale = this.gantt.options.value('viewScale'); this.nextColumns = ColumnGenerator.generate(this.columnBuilder, endDate, undefined, viewScale, this.getColumnsWidth(), x - this.gantt.width, this.gantt.width, false); } return true; } return false; }; ColumnsManager.prototype.expandExtendedColumnsForDate = function(date) { var firstColumn = this.getFirstColumn(); var from; if (firstColumn) { from = firstColumn.date; } var lastColumn = this.getLastColumn(); var endDate; if (lastColumn) { endDate = lastColumn.getDateByPosition(lastColumn.width); } var viewScale; if (from && date < from) { var firstExtendedColumn = this.getFirstColumn(true); if (!firstExtendedColumn || firstExtendedColumn.date > date) { viewScale = this.gantt.options.value('viewScale'); this.previousColumns = ColumnGenerator.generate(this.columnBuilder, from, date, viewScale, this.getColumnsWidth(), undefined, 0, true); } return true; } else if (endDate && date >= endDate) { var lastExtendedColumn = this.getLastColumn(true); if (!lastExtendedColumn || lastExtendedColumn.date < endDate) { viewScale = this.gantt.options.value('viewScale'); this.nextColumns = ColumnGenerator.generate(this.columnBuilder, endDate, date, viewScale, this.getColumnsWidth(), undefined, this.gantt.width, false); } return true; } return false; }; // Returns the number of active headers ColumnsManager.prototype.getActiveHeadersCount = function() { return this.headers.length; }; ColumnsManager.prototype.updateVisibleColumns = function(includeViews) { this.visibleColumns = $filter('ganttColumnLimit')(this.columns, this.gantt); this.visibleHeaders = []; angular.forEach(this.headers, function(header) { this.visibleHeaders.push($filter('ganttColumnLimit')(header, this.gantt)); }, this); if (includeViews) { angular.forEach(this.visibleColumns, function(c) { c.updateView(); }); angular.forEach(this.visibleHeaders, function(headerRow) { angular.forEach(headerRow, function(header) { header.updateView(); }); }); } var currentDateValue = this.gantt.options.value('currentDateValue'); this.gantt.currentDateManager.setCurrentDate(currentDateValue); }; var defaultHeadersFormats = {'year': 'YYYY', 'quarter': '[Q]Q YYYY', month: 'MMMM YYYY', week: 'w', day: 'D', hour: 'H', minute:'HH:mm'}; var defaultDayHeadersFormats = {day: 'LL', hour: 'H', minute:'HH:mm'}; var defaultYearHeadersFormats = {'year': 'YYYY', 'quarter': '[Q]Q', month: 'MMMM'}; ColumnsManager.prototype.getHeaderFormat = function(unit) { var format; var headersFormats = this.gantt.options.value('headersFormats'); if (headersFormats !== undefined) { format = headersFormats[unit]; } if (format === undefined) { var viewScale = this.gantt.options.value('viewScale'); viewScale = viewScale.trim(); if (viewScale.charAt(viewScale.length - 1) === 's') { viewScale = viewScale.substring(0, viewScale.length - 1); } var viewScaleUnit; var splittedViewScale; if (viewScale) { splittedViewScale = viewScale.split(' '); } if (splittedViewScale && splittedViewScale.length > 1) { viewScaleUnit = splittedViewScale[splittedViewScale.length - 1]; } else { viewScaleUnit = viewScale; } if (['millisecond', 'second', 'minute', 'hour'].indexOf(viewScaleUnit) > -1) { format = defaultDayHeadersFormats[unit]; } else if (['month', 'quarter', 'year'].indexOf(viewScaleUnit) > -1) { format = defaultYearHeadersFormats[unit]; } if (format === undefined) { format = defaultHeadersFormats[unit]; } } return format; }; ColumnsManager.prototype.getDateRange = function(visibleOnly) { var firstColumn, lastColumn; if (visibleOnly) { if (this.visibleColumns && this.visibleColumns.length > 0) { firstColumn = this.visibleColumns[0]; lastColumn = this.visibleColumns[this.visibleColumns.length - 1]; } } else { firstColumn = this.getFirstColumn(); lastColumn = this.getLastColumn(); } return firstColumn && lastColumn ? [firstColumn.date, lastColumn.endDate]: undefined; }; return ColumnsManager; }]); }());