Bladeren bron

Gantt: fixes and features (#2169)

* [gantt] refactor code, hide progress handle for 0% progress

* [gantt] limit progress resize

* [gantt] invalid bar in task
version-14
Faris Ansari 8 jaren geleden
committed by Rushabh Mehta
bovenliggende
commit
c650d6a87d
4 gewijzigde bestanden met toevoegingen van 163 en 73 verwijderingen
  1. +9
    -0
      frappe/public/css/gantt.css
  2. +15
    -15
      frappe/public/js/frappe/list/doclistview.js
  3. +129
    -58
      frappe/public/js/frappe/views/gantt.js
  4. +10
    -0
      frappe/public/less/gantt.less

+ 9
- 0
frappe/public/css/gantt.css Bestand weergeven

@@ -40,6 +40,15 @@
.gantt .bar-progress {
fill: #a3a3ff;
}
.gantt .bar-invalid {
fill: transparent;
stroke: #8D99A6;
stroke-width: 1;
stroke-dasharray: 5;
}
.gantt .bar-invalid ~ .bar-label {
fill: #555;
}
.gantt .bar-label {
fill: #fff;
dominant-baseline: central;


+ 15
- 15
frappe/public/js/frappe/list/doclistview.js Bestand weergeven

@@ -437,13 +437,25 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
var gantt_area = $('<svg height="400" width="6000"></svg>')
.appendTo(this.wrapper.find('.result-list').css("overflow", "scroll"));
var id = frappe.dom.set_unique_id(gantt_area);

var me = this;
var field_map = frappe.views.calendar[this.doctype].field_map;

var view_modes;
var tasks = values.map(function(item) {
return {
start: item[field_map.start],
end: item[field_map.end],
name: item[field_map.title],
id: item[field_map.id],
doctype: me.doctype,
progress: item.progress,
dependent: item.depends_on_tasks || ""
};
});
frappe.require(["assets/frappe/js/lib/snap.svg-min.js", "assets/frappe/css/gantt.css"], function() {
me.gantt = new Gantt({
parent_selector: '#' + id,
tasks: tasks,
date_format: "YYYY-MM-DD",
bar: {
height: 20
},
@@ -468,21 +480,9 @@ frappe.views.DocListView = frappe.ui.Listing.extend({
}
}
});

view_modes = me.gantt.opts.valid_view_modes || [];
values.forEach(function(item) {
me.gantt.add_task({
start: item[field_map.start],
end: item[field_map.end],
name: item[field_map.title],
id: item[field_map.id],
doctype: me.doctype,
progress: item.progress,
dependent: item.depends_on_tasks || ""
});
})
me.gantt.render();

var view_modes = me.gantt.get_view_modes() || [];
var dropdown = "<div class='dropdown pull-right'>" +
"<a class='text-muted dropdown-toggle' data-toggle='dropdown'>" +
"<span class='dropdown-text'>"+__('Day')+"</span><i class='caret'></i></a>" +


+ 129
- 58
frappe/public/js/frappe/views/gantt.js Bestand weergeven

@@ -15,16 +15,13 @@ var Gantt = Class.extend({
init: function(opts) {
this.opts = opts;
this.events = this.opts.events;
this.tasks = [];
this._bars = [];
this._arrows = [];
this.set_defaults();
this.groups = {};
this.make();
this.prepare();
this.render();
},
set_defaults: function() {
var defaults = {
label_width: 40,
label_width: 38,
header_height: 50,
column_width: 30,
step: 24,
@@ -43,27 +40,48 @@ var Gantt = Class.extend({
},
view_mode: 'Day',
padding: 18,
date_format: 'YYYY-MM-DD'
date_format: 'DD-MM-YYYY'
};
for(var key in defaults) {
if(defaults.hasOwnProperty(key)) {
if(!this.opts[key]) this.opts[key] = defaults[key];
}
}
},
make: function() {
this.canvas = Snap(this.opts.parent_selector);
this.canvas.addClass("gantt");
this.prepare_filters();

this._bars = [];
this._arrows = [];
this.groups = {};

//prepare tasks
var me = this;
this.tasks = this.opts.tasks.map(function(task, i) {
// momentify
task._start = moment(task.start, me.opts.date_format);
task._end = moment(task.end, me.opts.date_format);
//index
task._index = i;
//invalid dates
if(!task.start || !task.end) {
task._start = moment().startOf('day');
task._end = moment().startOf('day').add(2, 'days');
task.invalid = true;
}
return task;
});
//default view mode
this.set_scale(this.opts.view_mode);
},
prepare: function() {
//TODO: check for valid dates
this.start = this.end = undefined;
this.prepare_dates();
this.render_canvas();
},
render: function() {
this.clear();

this.setup_groups();
this.make_grid();
this.make_dates();
// this.make_label();
this.make_bars();
this.make_arrows();
this.set_arrows_on_bars();
@@ -75,8 +93,9 @@ var Gantt = Class.extend({
bind: function() {
this.bind_grid_click();
},
prepare_filters: function () {
this.filters = {};
render_canvas: function() {
this.canvas = Snap(this.opts.parent_selector);
this.canvas.addClass("gantt");
},
clear: function () {
this.canvas.clear();
@@ -86,10 +105,6 @@ var Gantt = Class.extend({
prepare_dates: function() {
var me = this;
this.tasks.forEach(function(task) {
// momentify
task._start = moment(task.start, me.opts.date_format);
task._end = moment(task.end, me.opts.date_format);

// set global start and end date
if(!me.start || task._start < me.start) {
me.start = task._start;
@@ -98,19 +113,22 @@ var Gantt = Class.extend({
me.end = task._end;
}
});
if(me.view_mode === 'Quarter Day' || me.view_mode === 'Half Day') {
me.start = me.start.clone().subtract(1, 'day');
me.end = me.end.clone().add(1, 'day');
} else if(me.view_mode === 'Month') {
this.set_gantt_dates();
this.setup_dates();
},
set_gantt_dates: function() {
var me = this;
if(me.view_is(['Quarter Day','Half Day'])) {
me.start = me.start.clone().subtract(7, 'day');
me.end = me.end.clone().add(7, 'day');
} else if(me.view_is('Month')) {
me.start = me.start.clone().startOf('year');
me.end = me.end.clone().endOf('month').add(1, 'year');
} else {
me.start = me.start.clone().startOf('month');
me.end = me.end.clone().endOf('month');
me.start = me.start.clone().startOf('month').subtract(1, 'month');
me.end = me.end.clone().endOf('month').add(1, 'month');
}
this.setup_dates();
},

setup_dates: function() {
this.dates = [];
var cur_date = null;
@@ -118,21 +136,33 @@ var Gantt = Class.extend({
if(!cur_date) {
cur_date = this.start.clone();
} else {
cur_date = (this.view_mode === 'Month') ?
cur_date = this.view_is('Month') ?
cur_date = cur_date.clone().add(1, 'month'):
cur_date.clone().add(this.opts.step, 'hours');
}
this.dates.push(cur_date);
}
},
setup_groups: function() {
var me = this;
// make group layers
["grid", "date", "arrow",
"progress", "bar", "details"].forEach(function(name) {
me.groups[name] = me.canvas.group().attr({'id': name});
});
},
get_view_modes: function() {
return this.opts.valid_view_modes || [];
},
set_view_mode: function(mode) {
this.set_scale(mode);
this.start = this.end = undefined;
this.prepare_dates();
this.prepare();
this.render();
},
set_scale: function (scale) {
this.view_mode = scale;

//fire viewmode_change event
this.events.on_viewmode_change(scale);
if(scale === 'Day') {
this.opts.step = 24;
@@ -160,14 +190,6 @@ var Gantt = Class.extend({
this.tasks.push(task);
this.prepare_dates();
},
setup_groups: function() {
var me = this;
// make groups
["grid", "controls", "label", "date",
"arrow", "progress", "bar", "details"].forEach(function(name) {
me.groups[name] = me.canvas.group().attr({'id': name});
});
},
set_width: function () {
var cur_width = this.canvas.node.getBoundingClientRect().width;
var actual_width = this.canvas.getBBox().width;
@@ -401,15 +423,7 @@ var Gantt = Class.extend({
}
});
},
get_task: function (id) {
var result = null;
this.tasks.forEach(function (task) {
if (task.id === id){
result = task;
}
});
return result;
},

make_label: function () {
var me = this;
var label_x = me.opts.label_width - me.opts.padding,
@@ -471,6 +485,29 @@ var Gantt = Class.extend({
el.removeClass('active');
});
});
},
view_is: function(modes) {
var me = this;
if (typeof modes === 'string') {
return me.view_mode === modes;
} else {
modes.reduce(function(acc, curr) {
return (me.view_mode === curr) || acc
}, false);
// for (var i = 0; i < modes.length; i++) {
// if(me.gantt.view_mode === modes[i]) return true;
// }
// return false;
}
},
get_task: function (id) {
var result = null;
this.tasks.forEach(function (task) {
if (task.id === id){
result = task;
}
});
return result;
}
});

@@ -501,12 +538,6 @@ var Bar = Class.extend({
var defaults = {
height: 20,
corner_radius: 3,
color: {
bar: "#a3a3ff",
progress: "#7575ff",
hover_bar: "#7575ff",
hover_progress: "#4d4da8"
},
events: {}
};
for(var key in defaults) {
@@ -519,6 +550,9 @@ var Bar = Class.extend({
this.prepare_plugins();
},
prepare_values: function() {
if(!this.task.start || !this.task.end){
this.invalid = true;
}
this.x = this.compute_x();
this.y = this.compute_y();
this.duration = (this.task._end.diff(this.task._start, 'hours') + 24)/this.gantt.step;
@@ -557,8 +591,12 @@ var Bar = Class.extend({
this.corner_radius, this.corner_radius)
.addClass("bar")
.appendTo(this.bar_group);
if(this.invalid) {
this.bar.addClass('bar-invalid');
}
},
draw_progress_bar: function() {
if(this.invalid) return;
this.bar_progress = this.canvas.rect(this.x, this.y,
this.progress_width, this.height,
this.corner_radius, this.corner_radius)
@@ -574,6 +612,7 @@ var Bar = Class.extend({
this.update_label_position(this);
},
draw_resize_handles: function() {
if(this.invalid) return;
var bar = this.group.select('.bar');
this.canvas.rect(bar.getX() + bar.getWidth() - 9, bar.getY() + 1,
8, this.height - 2, this.corner_radius, this.corner_radius)
@@ -583,15 +622,37 @@ var Bar = Class.extend({
8, this.height - 2, this.corner_radius, this.corner_radius)
.addClass('handle left')
.appendTo(this.handle_group);
this.canvas.polygon(

if(this.task.progress && this.task.progress < 100) {
this.canvas.polygon(
bar.getX() + this.progress_width - 5, bar.getY() + bar.get("height"),
bar.getX() + this.progress_width + 5, bar.getY() + bar.get("height"),
bar.getX() + this.progress_width, bar.getY() + bar.get("height") - 8.66
)
.addClass('handle progress')
.appendTo(this.handle_group);
.appendTo(this.handle_group)
}
},
draw_invalid_bar: function() {
var x = this.gantt.offset +
(moment().startOf('day').diff(this.gantt.start, 'hours') /
this.gantt.step *
this.gantt.unit_width);

this.canvas.rect(x, this.y,
this.gantt.unit_width*2, this.height,
this.corner_radius, this.corner_radius)
.addClass("bar-invalid")
.appendTo(this.bar_group);
//continue here
this.canvas.text(x + this.gantt.unit_width,
this.y + this.height/2,
'Dates not set')
.addClass("bar-label big")
.appendTo(this.bar_group);
},
bind: function () {
if(this.invalid) return;
// this.show_details();
this.bind_resize();
this.bind_drag();
@@ -728,11 +789,19 @@ var Bar = Class.extend({
},
bind_resize_progress: function() {
var me = this;
var bar = me.group.select('.bar');
var bar_progress = me.group.select('.bar-progress');
var handle = me.group.select('.handle.progress');
handle.drag(onmove, onstart, onstop);
handle && handle.drag(onmove, onstart, onstop);

function onmove(dx, dy) {
if(dx > bar_progress.max_dx) {
dx = bar_progress.max_dx;
}
if(dx < bar_progress.min_dx) {
dx = bar_progress.min_dx;
}

bar_progress.attr("width", bar_progress.owidth + dx);
handle.transform("t"+dx+",0");
bar_progress.finaldx = dx;
@@ -743,8 +812,10 @@ var Bar = Class.extend({
me.set_action_completed();
}
function onstart() {
bar_progress.owidth = bar_progress.getWidth();
bar_progress.finaldx = 0;
bar_progress.owidth = bar_progress.getWidth();
bar_progress.min_dx = -bar_progress.getWidth();
bar_progress.max_dx = bar.getWidth() - bar_progress.getWidth();
}
},
view_is: function(modes) {


+ 10
- 0
frappe/public/less/gantt.less Bestand weergeven

@@ -47,6 +47,16 @@
.bar-progress {
fill: #a3a3ff;
}
.bar-invalid {
fill: transparent;
stroke: #8D99A6;
stroke-width: 1;
stroke-dasharray: 5;

&~.bar-label {
fill: #555;
}
}
.bar-label {
fill: #fff;
dominant-baseline: central;


Laden…
Annuleren
Opslaan