[10.0][IMP] web_timeline improvements:

* Update the vis.js framework to latest stable version (v4.20.1) to add new functionality and save almost
400ko of source code in odoo assets (the vis dev team has isolate the code for timeline widget from the
 rest of their widgets)

* Add the possibility to display events with no duration (instantaneous) as the same time has regular events

* Add the possibility to choose between two scroll mode :
  -  zoom in time resolution (default)
  or
  -  vertical scroll (web browser default behaviour)

Signed-off-by: adrien.didenot <adrien.didenot@horanet.com>
This commit is contained in:
adrien.didenot
2017-09-27 14:07:10 +02:00
committed by CarlosRoca13
parent eec796a3ae
commit 86bf786981
28 changed files with 227 additions and 36595 deletions

View File

@@ -40,7 +40,7 @@ odoo.define('web_timeline.TimelineView', function (require) {
return this._super.apply(this, arguments);
},
get_perm: function(name){
get_perm: function (name) {
var self = this;
var promise = self.permissions[name];
if (self.permissions[name]) {
@@ -62,12 +62,17 @@ odoo.define('web_timeline.TimelineView', function (require) {
// });
// },
parse_colors: function(){
if(this.fields_view.arch.attrs.colors) {
this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain().compact().map(function(color_pair) {
parse_colors: function () {
if (this.fields_view.arch.attrs.colors) {
this.colors = _(this.fields_view.arch.attrs.colors.split(';')).chain().compact().map(function (color_pair) {
var pair = color_pair.split(':'), color = pair[0], expr = pair[1];
var temp = py.parse(py.tokenize(expr));
return {'color': color, 'field': temp.expressions[0].value, 'opt': temp.operators[0], 'value': temp.expressions[1].value};
return {
'color': color,
'field': temp.expressions[0].value,
'opt': temp.operators[0],
'value': temp.expressions[1].value
};
}).value();
}
},
@@ -90,7 +95,7 @@ odoo.define('web_timeline.TimelineView', function (require) {
this.proxy(this.on_scale_year_clicked));
this.current_window = {
start: new moment(),
end : new moment().add(24, 'hours')
end: new moment().add(24, 'hours')
};
this.$el.addClass(attrs['class']);
@@ -103,6 +108,8 @@ odoo.define('web_timeline.TimelineView', function (require) {
}
this.date_start = attrs.date_start;
this.date_stop = attrs.date_stop;
this.no_period = this.date_start == this.date_stop;
this.zoomKey = attrs.zoomKey || '';
if (!isNullOrUndef(attrs.quick_create_instance)) {
self.quick_create_instance = 'instance.' + attrs.quick_create_instance;
@@ -123,24 +130,24 @@ odoo.define('web_timeline.TimelineView', function (require) {
}
var fields_get = new Model(this.dataset.model)
.call('fields_get')
.then(function (fields) {
self.fields = fields;
});
.call('fields_get')
.then(function (fields) {
self.fields = fields;
});
this._super.apply(this, self);
return $.when(
self.fields_get,
self.get_perm('unlink'),
self.get_perm('write'),
self.get_perm('create')
).then(function() {
).then(function () {
self.init_timeline();
$(window).trigger('resize');
self.trigger('timeline_view_loaded', fv);
});
},
init_timeline: function() {
init_timeline: function () {
var self = this;
var options = {
groupOrder: self.group_order,
@@ -161,6 +168,7 @@ odoo.define('web_timeline.TimelineView', function (require) {
onMove: self.on_move,
onUpdate: self.on_update,
onRemove: self.on_remove,
zoomKey: this.zoomKey
};
self.timeline = new vis.Timeline(self.$timeline.empty().get(0));
self.timeline.setOptions(options);
@@ -170,12 +178,12 @@ odoo.define('web_timeline.TimelineView', function (require) {
self.timeline.on('click', self.on_click);
},
group_order: function(grp1, grp2) {
group_order: function (grp1, grp2) {
// display non grouped elements first
if (grp1.id === -1){
if (grp1.id === -1) {
return -1;
}
if (grp2.id === -1){
if (grp2.id === -1) {
return +1;
}
return grp1.content - grp2.content;
@@ -183,7 +191,7 @@ odoo.define('web_timeline.TimelineView', function (require) {
},
/* Transform Odoo event object to timeline event object */
event_data_transform: function(evt) {
event_data_transform: function (evt) {
var self = this;
var date_start = new moment();
var date_stop = new moment();
@@ -199,35 +207,38 @@ odoo.define('web_timeline.TimelineView', function (require) {
date_stop = this.date_stop ? time.auto_str_to_date(evt[this.date_stop]) : null;
}
else {
date_start = time.auto_str_to_date(evt[this.date_start].split(' ')[0],'start');
date_stop = this.date_stop ? time.auto_str_to_date(evt[this.date_stop].split(' ')[0],'stop') : null;
date_start = time.auto_str_to_date(evt[this.date_start].split(' ')[0], 'start');
date_stop = this.date_stop ? time.auto_str_to_date(evt[this.date_stop].split(' ')[0], 'stop') : null;
}
if (!date_start){
if (!date_start) {
date_start = new moment();
}
if(!date_stop) {
if (!date_stop && !this.no_period) {
date_stop = moment(date_start).add(date_delay, 'hours').toDate();
}
var group = evt[self.last_group_bys[0]];
if (group){
group = _.first(group);
if (group) {
group = _.first(group);
} else {
group = -1;
group = -1;
}
_.each(self.colors, function(color){
if(eval("'" + evt[color.field] + "' " + color.opt + " '" + color.value + "'"))
_.each(self.colors, function (color) {
if (eval("'" + evt[color.field] + "' " + color.opt + " '" + color.value + "'"))
self.color = color.color;
});
var r = {
'start': date_start,
'end': date_stop,
'content': evt.__name != undefined ? evt.__name : evt.display_name,
'id': evt.id,
'group': group,
'evt': evt,
'style': 'background-color: ' + self.color + ';'
};
// Check if the event is instantaneous, if so, display it with a point on the timeline (no 'end')
if (!this.no_period && !moment(date_start).isSame(date_stop)) {
r.end = date_stop;
}
self.color = undefined;
return r;
},
@@ -246,60 +257,66 @@ odoo.define('web_timeline.TimelineView', function (require) {
}
self.last_group_bys = n_group_bys;
// gather the fields to get
var fields = _.compact(_.map(["date_start", "date_delay", "date_stop", "progress"], function(key) {
var fields = _.compact(_.map(["date_start", "date_delay", "date_stop", "progress"], function (key) {
return self.fields_view.arch.attrs[key] || '';
}));
fields = _.uniq(fields.concat(_.pluck(this.colors, "field").concat(n_group_bys)));
return $.when(this.has_been_loaded).then(function() {
return $.when(this.has_been_loaded).then(function () {
return self.dataset.read_slice(fields, {
domain: domains,
context: contexts
}).then(function(data) {
}).then(function (data) {
return self.on_data_loaded(data, n_group_bys);
});
});
},
reload: function() {
reload: function () {
var self = this;
if (this.last_domains !== undefined){
if (this.last_domains !== undefined) {
self.current_window = self.timeline.getWindow();
return this.do_search(this.last_domains, this.last_contexts, this.last_group_bys);
}
},
on_data_loaded: function(events, group_bys) {
on_data_loaded: function (events, group_bys) {
var self = this;
var ids = _.pluck(events, "id");
return this.dataset.name_get(ids).then(function(names) {
var nevents = _.map(events, function(event) {
return _.extend({__name: _.detect(names, function(name) { return name[0] == event.id; })[1]}, event);
return this.dataset.name_get(ids).then(function (names) {
var nevents = _.map(events, function (event) {
return _.extend({
__name: _.detect(names, function (name) {
return name[0] == event.id;
})[1]
}, event);
});
return self.on_data_loaded_2(nevents, group_bys);
});
},
on_data_loaded_2: function(events, group_bys) {
on_data_loaded_2: function (events, group_bys) {
var self = this;
var data = [];
var groups = [];
this.grouped_by = group_bys;
_.each(events, function(event) {
if (event[self.date_start]){
_.each(events, function (event) {
if (event[self.date_start]) {
data.push(self.event_data_transform(event));
}
});
// get the groups
var split_groups = function(events, group_bys) {
var split_groups = function (events, group_bys) {
if (group_bys.length === 0)
return events;
var groups = [];
groups.push({id:-1, content: _t('-')})
_.each(events, function(event) {
groups.push({id: -1, content: _t('-')})
_.each(events, function (event) {
var group_name = event[_.first(group_bys)];
if (group_name) {
var group = _.find(groups, function(group) { return _.isEqual(group.id, group_name[0]); });
var group = _.find(groups, function (group) {
return _.isEqual(group.id, group_name[0]);
});
if (group === undefined) {
group = {id: group_name[0], content: group_name[1]};
groups.push(group);
@@ -314,23 +331,23 @@ odoo.define('web_timeline.TimelineView', function (require) {
this.timeline.fit();
},
do_show: function() {
do_show: function () {
this.do_push_state({});
return this._super();
},
is_action_enabled: function(action) {
is_action_enabled: function (action) {
if (action === 'create' && !this.options.creatable) {
return false;
}
return this._super(action);
},
create_completed: function(id) {
create_completed: function (id) {
var self = this;
this.dataset.ids = this.dataset.ids.concat([id]);
this.dataset.trigger("dataset_changed", id);
this.dataset.read_ids([id], this.fields).done(function(records) {
this.dataset.read_ids([id], this.fields).done(function (records) {
var new_event = self.event_data_transform(records[0]);
var items = self.timeline.itemsData;
items.add(new_event);
@@ -338,7 +355,7 @@ odoo.define('web_timeline.TimelineView', function (require) {
});
},
on_add: function(item, callback) {
on_add: function (item, callback) {
var self = this;
var context = this.dataset.get_context();
// Initialize default values for creation
@@ -360,24 +377,24 @@ odoo.define('web_timeline.TimelineView', function (require) {
return false;
},
write_completed: function(id) {
write_completed: function (id) {
this.dataset.trigger("dataset_changed", id);
this.current_window = this.timeline.getWindow();
this.reload();
this.timeline.setWindow(this.current_window);
},
on_update: function(item, callback) {
on_update: function (item, callback) {
var self = this;
var id = item.evt.id;
var title = item.evt.__name;
if (! this.open_popup_action) {
if (!this.open_popup_action) {
var index = this.dataset.get_id_index(id);
this.dataset.index = index;
if (this.write_right) {
this.do_switch_view('form', null, { mode: "edit" });
this.do_switch_view('form', null, {mode: "edit"});
} else {
this.do_switch_view('form', null, { mode: "view" });
this.do_switch_view('form', null, {mode: "view"});
}
}
else {
@@ -393,7 +410,7 @@ odoo.define('web_timeline.TimelineView', function (require) {
return false;
},
on_move: function(item, callback) {
on_move: function (item, callback) {
var self = this;
var start = item.start;
var end = item.end;
@@ -405,21 +422,23 @@ odoo.define('web_timeline.TimelineView', function (require) {
data[self.fields_view.arch.attrs.date_start] =
time.auto_date_to_str(start, self.fields[self.fields_view.arch.attrs.date_start].type);
data[self.fields_view.arch.attrs.date_stop] =
time.auto_date_to_str(end, self.fields[self.fields_view.arch.attrs.date_stop].type);
if (self.grouped_by){
time.auto_date_to_str(end, self.fields[self.fields_view.arch.attrs.date_stop].type);
if (self.grouped_by) {
data[self.grouped_by[0]] = group;
}
var id = item.evt.id;
this.dataset.write(id, data);
},
on_remove: function(item, callback) {
on_remove: function (item, callback) {
var self = this;
function do_it() {
return $.when(self.dataset.unlink([item.evt.id])).then(function() {
return $.when(self.dataset.unlink([item.evt.id])).then(function () {
callback(item);
});
}
if (this.options.confirm_on_delete) {
if (confirm(_t("Are you sure you want to delete this record ?"))) {
return do_it();
@@ -428,14 +447,14 @@ odoo.define('web_timeline.TimelineView', function (require) {
return do_it();
},
on_click: function(e) {
on_click: function (e) {
// handle a click on a group header
if (e.what == 'group-label') {
return this.on_group_click(e);
}
},
on_group_click: function(e) {
on_group_click: function (e) {
if (e.group == -1) {
return;
}
@@ -448,18 +467,18 @@ odoo.define('web_timeline.TimelineView', function (require) {
});
},
scale_current_window: function(factor){
if (this.timeline){
scale_current_window: function (factor) {
if (this.timeline) {
this.current_window = this.timeline.getWindow();
this.current_window.end = moment(this.current_window.start).add(factor, 'hours');
this.timeline.setWindow(this.current_window);
}
},
on_today_clicked: function(){
on_today_clicked: function () {
this.current_window = {
start: new moment(),
end : new moment().add(24, 'hours')
end: new moment().add(24, 'hours')
};
if (this.timeline) {
@@ -467,19 +486,19 @@ odoo.define('web_timeline.TimelineView', function (require) {
}
},
on_scale_day_clicked: function() {
on_scale_day_clicked: function () {
this.scale_current_window(24);
},
on_scale_week_clicked: function() {
on_scale_week_clicked: function () {
this.scale_current_window(24 * 7);
},
on_scale_month_clicked: function() {
on_scale_month_clicked: function () {
this.scale_current_window(24 * 30);
},
on_scale_year_clicked: function() {
on_scale_year_clicked: function () {
this.scale_current_window(24 * 365);
}
});