From 35fc079c7f3b9c64e8da6f67dbc63b5b7f25bd04 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Fri, 27 Feb 2015 17:31:44 +0000 Subject: [PATCH] * Additional columns for Gantt chart --- etemplate/js/et2_widget_gantt.js | 182 ++++++++++++++++++++- etemplate/templates/default/etemplate2.css | 22 +++ 2 files changed, 202 insertions(+), 2 deletions(-) diff --git a/etemplate/js/et2_widget_gantt.js b/etemplate/js/et2_widget_gantt.js index 60e86b1ea5..ab4d4f04e5 100644 --- a/etemplate/js/et2_widget_gantt.js +++ b/etemplate/js/et2_widget_gantt.js @@ -57,6 +57,14 @@ var et2_gantt = et2_inputWidget.extend([et2_IResizeable,et2_IInput], "default": "minute", "description": "The unit for task duration values. One of minute, hour, week, year." }, + columns: { + name: "Columns", + type: "any", + default: [ + {name: "text", label: egw.lang('Title'), tree: true, width: '*'} + ], + description: "Columns for the grid portion of the gantt chart. An array of objects with keys name, label, etc. See http://docs.dhtmlx.com/gantt/api__gantt_columns_config.html" + }, value: {type: 'any'}, needed: {ignore: true}, onfocus: {ignore: true}, @@ -76,6 +84,7 @@ var et2_gantt = et2_inputWidget.extend([et2_IResizeable,et2_IInput], show_progress: true, order_branch: false, min_column_width: 30, + min_grid_column_width: 30, task_height: 25, fit_tasks: true, autosize: '', @@ -177,6 +186,10 @@ var et2_gantt = et2_inputWidget.extend([et2_IResizeable,et2_IInput], { this.set_value(this.options.value); } + if(this.options.columns) + { + this.set_columns(this.options.columns); + } // Update start & end dates with chart values for consistency if(start_date && this.options.value.data && this.options.value.data.length) @@ -244,6 +257,82 @@ var et2_gantt = et2_inputWidget.extend([et2_IResizeable,et2_IInput], } }, + /** + * Set the columns for the grid (left) portion + * + * @param {Object[]} columns - A list of columns + * @param {string} columns[].name The column's ID + * @param {string} columns[].label The title for the column header + * @param {string} columns[].width Width of the column + * + * @see http://docs.dhtmlx.com/gantt/api__gantt_columns_config.html for full options + */ + set_columns: function(columns) + { + this.gantt_config.columns = columns; + + var displayed_columns = []; + var gantt_widget = this; + + // Make sure there's enough room for them all + var width = 0; + for(var col in columns) + { + // Preserve original width, gantt will resize column to fit + if(!columns[col]._width) + { + columns[col]._width = columns[col].width; + } + columns[col].width = columns[col]._width; + if(!columns[col].template) + { + // Use an et2 widget to render the column value, if one was provided + // otherwise, just display the value + columns[col].template = function(task) { + var value = typeof task[this.name] == 'undefined'||task[this.name] == null ? '':task[this.name]; + + // No value, but there's a project title. Try reading the project value. + if(!value && this.name.indexOf('pe_') == 0 && task.pm_title) + { + var pm_col = this.name.replace('pe_','pm_'); + value = typeof task[pm_col] == 'undefined' || task[pm_col] == null ? '':task[pm_col]; + } + if(this.widget && typeof this.widget == 'string') + { + this.widget = et2_createWidget(this.widget, {readonly:true}, gantt_widget); + } + if (this.widget) + { + this.widget.set_value(value); + value = $j(this.widget.getDOMNode()).html(); + } + return '
' + value + '
'; + }; + } + + // Actual hiding is available in the pro version of gantt chart + if(!columns[col].hide) + { + displayed_columns.push(columns[col]); + width += parseInt(columns[col]._width) || 0; + } + + } + // Add in add column + displayed_columns.push({name: 'add', width: 26}); + width += 26; + + if(width != this.gantt_config.grid_width || typeof this.gantt_config.grid_width == 'undefined') + { + this.gantt_config.grid_width = Math.min(Math.max(200, width), this.htmlNode.width()); + } + + if(this.gantt == null) return; + this.gantt.config.columns = displayed_columns; + this.gantt.config.grid_width = this.gantt_config.grid_width; + this.gantt.render(); + }, + /** * Sets the data to be displayed in the gantt chart. * @@ -700,11 +789,9 @@ var et2_gantt = et2_inputWidget.extend([et2_IResizeable,et2_IInput], // Some crazy stuff make sure timing is OK to scroll after re-render // TODO: Make this more consistently go to where you click var id = gantt_widget.gantt.attachEvent("onGanttRender", function() { - console.log('Render'); gantt_widget.gantt.detachEvent(id); gantt_widget.gantt.scrollTo(parseInt($j('.gantt_task_scale',gantt_widget.gantt_node).width() *current_position),0); window.setTimeout(function() { - console.log("Scroll to"); gantt_widget.gantt.scrollTo(parseInt($j('.gantt_task_scale',gantt_widget.gantt_node).width() *current_position),0); },100); }); @@ -729,6 +816,12 @@ var et2_gantt = et2_inputWidget.extend([et2_IResizeable,et2_IInput], */ }); + this.gantt.attachEvent("onGridHeaderClick", function(column_name, e) { + if(column_name === "add") + { + gantt_widget._column_selection(e); + } + }); this.gantt.attachEvent("onContextMenu",function(taskId, linkId, e) { if(gantt_widget.options.readonly) return false; if(taskId) @@ -901,6 +994,91 @@ var et2_gantt = et2_inputWidget.extend([et2_IResizeable,et2_IInput], }, this, et2_inputWidget); }, + /** + * Start UI for selecting among defined columns + */ + _column_selection: function(e) + { + var self = this; + var columns = []; + var columns_selected = []; + for (var i = 0; i < this.gantt_config.columns.length; i++) + { + var col = this.gantt_config.columns[i]; + columns.push({ + value: col.name, + label: col.label + }) + if(!col.hide) + { + columns_selected.push(col.name); + } + } + + // Build the popup + if(!this.selectPopup) + { + var select = et2_createWidget("select", { + multiple: true, + rows: 8, + empty_label:this.egw().lang("select columns"), + selected_first: false + }, this); + select.set_select_options(columns); + select.set_value(columns_selected); + + var okButton = et2_createWidget("buttononly", {}, this); + okButton.set_label(this.egw().lang("ok")); + okButton.onclick = function() { + // Update columns + var value = select.getValue() || []; + for (var i = 0; i < columns.length; i++) + { + self.gantt_config.columns[i].hide = value.indexOf(columns[i].value) < 0 ; + } + self.set_columns(self.gantt_config.columns); + + // Update Implicit preference + this.egw().set_preference(self.getInstanceManager().app, 'gantt_columns_' + self.id, value); + + // Hide popup + self.selectPopup.toggle(); + self.selectPopup.remove(); + self.selectPopup = null; + $j('body').off('click.gantt'); + }; + + var cancelButton = et2_createWidget("buttononly", {}, this); + cancelButton.set_label(this.egw().lang("cancel")); + cancelButton.onclick = function() { + self.selectPopup.toggle(); + self.selectPopup.remove(); + self.selectPopup = null; + $j('body').off('click.gantt'); + }; + + // Create and add popup + this.selectPopup = jQuery(document.createElement("div")) + .addClass("colselection ui-dialog ui-widget-content") + .append(select.getDOMNode()) + .append(okButton.getDOMNode()) + .append(cancelButton.getDOMNode()) + .appendTo(this.getInstanceManager().DOMContainer); + // Bind so if you click elsewhere, it closes + window.setTimeout(function() {$j(document).one('mouseup.gantt', function(e){ + if(!self.selectPopup.is(e.target) && self.selectPopup.has(e.target).length === 0) + { + cancelButton.onclick(); + } + });},1); + } + else + { + this.selectPopup.toggle(); + } + this.selectPopup.position({my:'right top', at:'right bottom', of: e.target}); + }, + /** * Link the actions to the DOM nodes / widget bits. * Overridden to make the gantt chart a container, so it can't be selected. diff --git a/etemplate/templates/default/etemplate2.css b/etemplate/templates/default/etemplate2.css index 3374d1291c..b1305da749 100644 --- a/etemplate/templates/default/etemplate2.css +++ b/etemplate/templates/default/etemplate2.css @@ -1623,6 +1623,28 @@ div.et2_toolbar_activeList h.ui-accordion-header { { overflow: visible; } +/* Style the gantt grid (left side) allowing 2 lines */ +.et2_gantt .gantt_grid_scale :not(.gantt_grid_head_add) { + white-space: normal; + line-height: 16px; + height: auto; +} +/* Column selector */ +.et2_gantt .gantt_grid_scale .gantt_grid_head_add { + background-image: url(images/selectcols.png); + padding: 0px; + margin: 0px; +} +.et2_gantt .gantt_grid_data .gantt_add { + display: none; + padding: 0px; + margin: 0px; +} +/* Display inline, since there's only 1 line*/ +.et2_gantt .gantt_grid_data li { + display: inline-block; + padding-right: 0.5ex; +} .et2_gantt .gantt_task_progress { text-align: left;