mirror of https://github.com/OCA/web.git
parent
fad3ec6c27
commit
086080e0da
|
@ -4,7 +4,7 @@
|
|||
{
|
||||
'name': "Web timeline",
|
||||
'summary': "Interactive visualization chart to show events in time",
|
||||
"version": "11.0.1.4.0",
|
||||
"version": "11.0.1.4.1",
|
||||
'author': 'ACSONE SA/NV, '
|
||||
'Tecnativa, '
|
||||
'Monk Software, '
|
||||
|
|
|
@ -5,14 +5,37 @@ odoo.define('web_timeline.TimelineCanvas', function (require) {
|
|||
"use strict";
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
/**
|
||||
* Used to draw stuff on upon the timeline view.
|
||||
*/
|
||||
var TimelineCanvas = Widget.extend({
|
||||
template: 'TimelineView.Canvas',
|
||||
|
||||
/**
|
||||
* Clears all drawings (svg elements) from the canvas.
|
||||
*/
|
||||
clear: function () {
|
||||
this.$el.find(' > :not(defs)').remove();
|
||||
},
|
||||
|
||||
get_polyline_points: function(coordx1, coordy1, coordx2, coordy2, width1, height1, width2, height2, widthMarker, breakAt) {
|
||||
/**
|
||||
* Gets the path from one point to another.
|
||||
*
|
||||
* @param {Number} coordx1
|
||||
* @param {Number} coordy1
|
||||
* @param {Number} coordx2
|
||||
* @param {Number} coordy2
|
||||
* @param {Number} width1
|
||||
* @param {Number} height1
|
||||
* @param {Number} width2
|
||||
* @param {Number} height2
|
||||
* @param {Number} widthMarker The marker's width of the polyline
|
||||
* @param {Number} breakAt The space between the line turns
|
||||
* @returns {Array} Each item represents a coordinate
|
||||
*/
|
||||
get_polyline_points: function (coordx1, coordy1, coordx2, coordy2,
|
||||
width1, height1, width2, height2,
|
||||
widthMarker, breakAt) {
|
||||
var halfHeight1 = height1 / 2;
|
||||
var halfHeight2 = height2 / 2;
|
||||
var x1 = coordx1 - widthMarker;
|
||||
|
@ -47,10 +70,31 @@ odoo.define('web_timeline.TimelineCanvas', function (require) {
|
|||
return points;
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws an arrow.
|
||||
*
|
||||
* @param {HTMLElement} from Element to draw the arrow from
|
||||
* @param {HTMLElement} to Element to draw the arrow to
|
||||
* @param {String} color Color of the line
|
||||
* @param {Number} width Width of the line
|
||||
* @returns {HTMLElement} The created SVG polyline
|
||||
*/
|
||||
draw_arrow: function (from, to, color, width) {
|
||||
return this.draw_line(from, to, color, width, '#arrowhead', 10, 12);
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws a line.
|
||||
*
|
||||
* @param {HTMLElement} from Element to draw the line from
|
||||
* @param {HTMLElement} to Element to draw the line to
|
||||
* @param {String} color Color of the line
|
||||
* @param {Number} width Width of the line
|
||||
* @param {String} markerStart Start marker of the line
|
||||
* @param {Number} widthMarker The marker's width of the polyline
|
||||
* @param {Number} breakLineAt The space between the line turns
|
||||
* @returns {HTMLElement} The created SVG polyline
|
||||
*/
|
||||
draw_line: function (from, to, color, width, markerStart, widthMarker, breakLineAt) {
|
||||
var x1 = from.offsetLeft,
|
||||
y1 = from.offsetTop + from.parentElement.offsetTop,
|
||||
|
@ -81,8 +125,7 @@ odoo.define('web_timeline.TimelineCanvas', function (require) {
|
|||
}
|
||||
this.$el.append(line);
|
||||
return line;
|
||||
}
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
return TimelineCanvas;
|
||||
|
|
|
@ -9,7 +9,7 @@ var Dialog = require('web.Dialog');
|
|||
|
||||
var _t = core._t;
|
||||
|
||||
var CalendarController = AbstractController.extend({
|
||||
var TimelineController = AbstractController.extend({
|
||||
custom_events: _.extend({}, AbstractController.prototype.custom_events, {
|
||||
onGroupClick: '_onGroupClick',
|
||||
onUpdate: '_onUpdate',
|
||||
|
@ -18,6 +18,10 @@ var CalendarController = AbstractController.extend({
|
|||
onAdd: '_onAdd',
|
||||
}),
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @override
|
||||
*/
|
||||
init: function (parent, model, renderer, params) {
|
||||
this._super.apply(this, arguments);
|
||||
this.open_popup_action = params.open_popup_action;
|
||||
|
@ -29,6 +33,9 @@ var CalendarController = AbstractController.extend({
|
|||
this.debouncedInternalMove = _.debounce(this.internalMove, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
update: function (params, options) {
|
||||
this._super.apply(this, arguments);
|
||||
if (_.isEmpty(params)){
|
||||
|
@ -69,6 +76,12 @@ var CalendarController = AbstractController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets triggered when a group in the timeline is clicked (by the TimelineRenderer).
|
||||
*
|
||||
* @private
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
_onGroupClick: function (event) {
|
||||
var groupField = this.renderer.last_group_bys[0];
|
||||
return this.do_action({
|
||||
|
@ -80,6 +93,11 @@ var CalendarController = AbstractController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens a form view of a clicked timeline item (triggered by the TimelineRenderer).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onUpdate: function (event) {
|
||||
var self = this;
|
||||
this.renderer = event.data.renderer;
|
||||
|
@ -112,6 +130,11 @@ var CalendarController = AbstractController.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets triggered when a timeline item is moved (triggered by the TimelineRenderer).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onMove: function (event) {
|
||||
var item = event.data.item;
|
||||
var view = this.renderer.view;
|
||||
|
@ -150,6 +173,12 @@ var CalendarController = AbstractController.extend({
|
|||
this.debouncedInternalMove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Write enqueued moves to Odoo. After all writes are finished it updates the view once
|
||||
* (prevents flickering of the view when multiple timeline items are moved at once).
|
||||
*
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
internalMove: function () {
|
||||
var self = this;
|
||||
var queue = this.moveQueue.slice();
|
||||
|
@ -175,6 +204,13 @@ var CalendarController = AbstractController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered when a timeline item gets removed from the view.
|
||||
* Requires user confirmation before it gets actually deleted.
|
||||
*
|
||||
* @private
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
_onRemove: function (e) {
|
||||
var self = this;
|
||||
|
||||
|
@ -214,6 +250,11 @@ var CalendarController = AbstractController.extend({
|
|||
return def.promise();
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered when a timeline item gets added and opens a form view.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onAdd: function (event) {
|
||||
var self = this;
|
||||
var item = event.data.item;
|
||||
|
@ -248,6 +289,12 @@ var CalendarController = AbstractController.extend({
|
|||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered upon completion of a new record.
|
||||
* Updates the timeline view with the new record.
|
||||
*
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
create_completed: function (id) {
|
||||
var self = this;
|
||||
return this._rpc({
|
||||
|
@ -268,6 +315,9 @@ var CalendarController = AbstractController.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered upon completion of writing a record.
|
||||
*/
|
||||
write_completed: function (options) {
|
||||
var params = {
|
||||
domain: this.renderer.last_domains,
|
||||
|
@ -279,5 +329,5 @@ var CalendarController = AbstractController.extend({
|
|||
},
|
||||
});
|
||||
|
||||
return CalendarController;
|
||||
return TimelineController;
|
||||
});
|
||||
|
|
|
@ -4,10 +4,17 @@ odoo.define('web_timeline.TimelineModel', function (require) {
|
|||
var AbstractModel = require('web.AbstractModel');
|
||||
|
||||
var TimelineModel = AbstractModel.extend({
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
load: function (params) {
|
||||
var self = this;
|
||||
this.modelName = params.modelName;
|
||||
|
@ -34,6 +41,12 @@ var TimelineModel = AbstractModel.extend({
|
|||
return this.preload_def.then(this._loadTimeline.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Read the records for the timeline.
|
||||
*
|
||||
* @private
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
_loadTimeline: function () {
|
||||
var self = this;
|
||||
return self._rpc({
|
||||
|
|
|
@ -13,11 +13,14 @@ var TimelineCanvas = require('web_timeline.TimelineCanvas');
|
|||
|
||||
var _t = core._t;
|
||||
|
||||
var CalendarRenderer = AbstractRenderer.extend({
|
||||
var TimelineRenderer = AbstractRenderer.extend({
|
||||
template: "TimelineView",
|
||||
events: _.extend({}, AbstractRenderer.prototype.events, {
|
||||
}),
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
init: function (parent, state, params) {
|
||||
this._super.apply(this, arguments);
|
||||
this.modelName = params.model;
|
||||
|
@ -36,6 +39,9 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
this.modelClass = this.view.model;
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
var attrs = this.arch.attrs;
|
||||
|
@ -53,6 +59,9 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
this._super.apply(this, self);
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered when the timeline is attached to the DOM.
|
||||
*/
|
||||
on_attach_callback: function() {
|
||||
var height = this.$el.parent().height() - this.$el.find('.oe_timeline_buttons').height();
|
||||
if (height > this.min_height) {
|
||||
|
@ -62,6 +71,9 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_render: function () {
|
||||
this.add_events();
|
||||
var self = this;
|
||||
|
@ -74,6 +86,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Binds events to buttons.
|
||||
|
||||
* @private
|
||||
*/
|
||||
add_events: function () {
|
||||
var self = this;
|
||||
this.$(".oe_timeline_button_today").click(function() {
|
||||
|
@ -93,6 +110,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the timeline window to today (day).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onTodayClicked: function () {
|
||||
this.current_window = {
|
||||
start: new moment(),
|
||||
|
@ -104,22 +126,48 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the timeline window to a day.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onScaleDayClicked: function () {
|
||||
this._scaleCurrentWindow(24);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the timeline window to a week.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onScaleWeekClicked: function () {
|
||||
this._scaleCurrentWindow(24 * 7);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the timeline window to a month.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onScaleMonthClicked: function () {
|
||||
this._scaleCurrentWindow(24 * 30);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the timeline window to a year.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onScaleYearClicked: function () {
|
||||
this._scaleCurrentWindow(24 * 365);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scales the timeline window based on the current window.
|
||||
*
|
||||
* @param {Integer} factor The timespan (in hours) the window must be scaled to.
|
||||
* @private
|
||||
*/
|
||||
_scaleCurrentWindow: function (factor) {
|
||||
if (this.timeline) {
|
||||
this.current_window = this.timeline.getWindow();
|
||||
|
@ -128,6 +176,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Computes the initial visible window.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_computeMode: function () {
|
||||
if (this.mode) {
|
||||
var start = false, end = false;
|
||||
|
@ -154,6 +207,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the timeline (http://visjs.org/docs/timeline/).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
init_timeline: function () {
|
||||
var self = this;
|
||||
this._computeMode();
|
||||
|
@ -205,6 +263,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears and draws the canvas items.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
draw_canvas: function () {
|
||||
this.canvas.clear();
|
||||
if (this.dependency_arrow) {
|
||||
|
@ -212,6 +275,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw item dependencies on canvas.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
draw_dependencies: function () {
|
||||
var self = this;
|
||||
var items = this.timeline.itemSet.items;
|
||||
|
@ -227,6 +295,16 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws a dependency arrow between 2 timeline items.
|
||||
*
|
||||
* @param {Object} from Start timeline item
|
||||
* @param {Object} to Destination timeline item
|
||||
* @param {Object} options
|
||||
* @param {Object} options.line_color Color of the line
|
||||
* @param {Object} options.line_width The width of the line
|
||||
* @private
|
||||
*/
|
||||
draw_dependency: function (from, to, options) {
|
||||
if (!from.displayed || !to.displayed) {
|
||||
return;
|
||||
|
@ -240,6 +318,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
this.canvas.draw_arrow(from.dom.box, to.dom.box, defaults.line_color, defaults.line_width);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load display_name of records.
|
||||
*
|
||||
* @private
|
||||
* @returns {jQuery.Deferred}
|
||||
*/
|
||||
on_data_loaded: function (events, group_bys, adjust_window) {
|
||||
var self = this;
|
||||
var ids = _.pluck(events, "id");
|
||||
|
@ -262,6 +346,11 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Set groups and events.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_data_loaded_2: function (events, group_bys, adjust_window) {
|
||||
var self = this;
|
||||
var data = [];
|
||||
|
@ -282,7 +371,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
// get the groups
|
||||
/**
|
||||
* Get the groups.
|
||||
*
|
||||
* @private
|
||||
* @returns {Array}
|
||||
*/
|
||||
split_groups: function (events, group_bys) {
|
||||
if (group_bys.length === 0) {
|
||||
return events;
|
||||
|
@ -310,7 +404,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
return groups;
|
||||
},
|
||||
|
||||
/* Transform Odoo event object to timeline event object */
|
||||
/**
|
||||
* Transform Odoo event object to timeline event object.
|
||||
*
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
event_data_transform: function (evt) {
|
||||
var self = this;
|
||||
var date_start = new moment();
|
||||
|
@ -368,6 +467,13 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
return r;
|
||||
},
|
||||
|
||||
/**
|
||||
* Render timeline item template.
|
||||
*
|
||||
* @param {Object} evt Record
|
||||
* @private
|
||||
* @returns {String} Rendered template
|
||||
*/
|
||||
render_timeline_item: function (evt) {
|
||||
if(this.qweb.has_template('timeline-item')) {
|
||||
return this.qweb.render('timeline-item', {
|
||||
|
@ -381,8 +487,12 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a click on a group header.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_group_click: function (e) {
|
||||
// handle a click on a group header
|
||||
if (e.what === 'group-label' && e.group !== -1) {
|
||||
this._trigger(e, function() {
|
||||
// Do nothing
|
||||
|
@ -390,22 +500,47 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onUpdate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_update: function (item, callback) {
|
||||
this._trigger(item, callback, 'onUpdate');
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onMove.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_move: function (item, callback) {
|
||||
this._trigger(item, callback, 'onMove');
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onRemove.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_remove: function (item, callback) {
|
||||
this._trigger(item, callback, 'onRemove');
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onAdd.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
on_add: function (item, callback) {
|
||||
this._trigger(item, callback, 'onAdd');
|
||||
},
|
||||
|
||||
/**
|
||||
* trigger_up encapsulation adds by default the rights, and the renderer.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_trigger: function (item, callback, trigger) {
|
||||
this.trigger_up(trigger, {
|
||||
'item': item,
|
||||
|
@ -417,5 +552,5 @@ var CalendarRenderer = AbstractRenderer.extend({
|
|||
|
||||
});
|
||||
|
||||
return CalendarRenderer;
|
||||
return TimelineRenderer;
|
||||
});
|
||||
|
|
|
@ -39,6 +39,10 @@ odoo.define('web_timeline.TimelineView', function (require) {
|
|||
Renderer: TimelineRenderer,
|
||||
},
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @override
|
||||
*/
|
||||
init: function (viewInfo, params) {
|
||||
this._super.apply(this, arguments);
|
||||
var self = this;
|
||||
|
@ -144,6 +148,9 @@ odoo.define('web_timeline.TimelineView', function (require) {
|
|||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Order function for groups.
|
||||
*/
|
||||
group_order: function (grp1, grp2) {
|
||||
// display non grouped elements first
|
||||
if (grp1.id === -1) {
|
||||
|
@ -156,6 +163,11 @@ odoo.define('web_timeline.TimelineView', function (require) {
|
|||
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the colors attribute.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
parse_colors: function () {
|
||||
if (this.arch.attrs.colors) {
|
||||
this.colors = _(this.arch.attrs.colors.split(';')).chain().compact().map(function (color_pair) {
|
||||
|
|
Loading…
Reference in New Issue