From 8b9b5a0cf5decf8b2fa53ed69f9314d1ef872c91 Mon Sep 17 00:00:00 2001 From: tarteo Date: Fri, 17 Aug 2018 11:53:44 +0200 Subject: [PATCH] [ADD] web_timeline: New dependency_arrow attribute Update README.rst [FIX] Remove console.log [ADD] Make timeline.fit optional [FIX] Use stringified points [IMP] Reversed the arrow head and fixed lint issues [IMP] Use options parameter for line color and width [FIX] Version number [IMP] Minor improvements --- web_timeline/README.rst | 11 +- web_timeline/__manifest__.py | 2 +- web_timeline/models/__init__.py | 2 +- web_timeline/static/src/css/web_timeline.css | 9 ++ web_timeline/static/src/js/timeline_canvas.js | 89 +++++++++++++ .../static/src/js/timeline_controller.js | 49 ++++--- .../static/src/js/timeline_renderer.js | 122 +++++++++++++----- web_timeline/static/src/js/timeline_view.js | 6 + web_timeline/static/src/xml/web_timeline.xml | 9 ++ web_timeline/views/web_timeline.xml | 1 + 10 files changed, 246 insertions(+), 54 deletions(-) create mode 100644 web_timeline/static/src/js/timeline_canvas.js diff --git a/web_timeline/README.rst b/web_timeline/README.rst index 78704ad0..cfc190c1 100755 --- a/web_timeline/README.rst +++ b/web_timeline/README.rst @@ -39,9 +39,11 @@ the possible attributes for the tag: view. * colors (optional): it allows to set certain specific colors if the expressed condition (JS syntax) is met. - -Optionally you can declare a custom template, which will be used to render the -timeline items. You have to name the template 'timeline-item'. +* dependency_arrow (optional): set this attribute to a x2many field to draw + arrows between the records referenced in the x2many field. + +Optionally you can declare a custom template, which will be used to render the +timeline items. You have to name the template 'timeline-item'. These are the variables available in template rendering: * ``record``: to access the fields values selected in the timeline definition. @@ -65,7 +67,8 @@ Example: default_group_by="user_id" event_open_popup="true" zoomKey="ctrlKey" - colors="#ec7063:user_id == false;#2ecb71:kanban_state=='done';"> + colors="#ec7063:user_id == false;#2ecb71:kanban_state=='done';" + dependency_arrow="task_dependency_ids">
diff --git a/web_timeline/__manifest__.py b/web_timeline/__manifest__.py index bb064f9d..ca4bd862 100644 --- a/web_timeline/__manifest__.py +++ b/web_timeline/__manifest__.py @@ -4,7 +4,7 @@ { 'name': "Web timeline", 'summary': "Interactive visualization chart to show events in time", - "version": "11.0.1.2.1", + "version": "11.0.1.3.0", 'author': 'ACSONE SA/NV, ' 'Tecnativa, ' 'Monk Software, ' diff --git a/web_timeline/models/__init__.py b/web_timeline/models/__init__.py index e18b8bde..fd67675a 100644 --- a/web_timeline/models/__init__.py +++ b/web_timeline/models/__init__.py @@ -1,4 +1,4 @@ -# © 2016 ACSONE SA/NV () +# Copyright 2016 ACSONE SA/NV () # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import ir_view diff --git a/web_timeline/static/src/css/web_timeline.css b/web_timeline/static/src/css/web_timeline.css index bfbcaa62..d32d3616 100644 --- a/web_timeline/static/src/css/web_timeline.css +++ b/web_timeline/static/src/css/web_timeline.css @@ -20,3 +20,12 @@ .oe_timeline_view .vlabel .inner:hover{ cursor: pointer; } + +.oe_timeline_view svg.oe_timeline_view_canvas { + display: block; + width: 100%; + height: 100%; + position: absolute; + left: 0px; + top: 0px; +} diff --git a/web_timeline/static/src/js/timeline_canvas.js b/web_timeline/static/src/js/timeline_canvas.js new file mode 100644 index 00000000..e589b559 --- /dev/null +++ b/web_timeline/static/src/js/timeline_canvas.js @@ -0,0 +1,89 @@ +/* Copyright 2018 Onestein + * License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */ + +odoo.define('web_timeline.TimelineCanvas', function (require) { + "use strict"; + var Widget = require('web.Widget'); + + var TimelineCanvas = Widget.extend({ + template: 'TimelineView.Canvas', + + clear: function() { + this.$el.find(' > :not(defs)').remove(); + }, + + 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; + var y1 = coordy1 + halfHeight1; + var x2 = coordx2 + width2; + var y2 = coordy2 + halfHeight2; + var xDiff = x1 - x2; + var yDiff = y1 - y2; + var threshold = breakAt + widthMarker; + var spaceY = halfHeight2 + 6; + + var points = [[x1, y1]]; + if (y1 !== y2) { + if (xDiff > threshold) { + points.push([x1 - breakAt, y1]); + points.push([x1 - breakAt, y1 - yDiff]); + } else if (xDiff <= threshold) { + var yDiffSpace = yDiff > 0 ? spaceY : -spaceY; + points.push([x1 - breakAt, y1]); + points.push([x1 - breakAt, y2 + yDiffSpace]); + points.push([x2 + breakAt, y2 + yDiffSpace]); + points.push([x2 + breakAt, y2]); + } + } else if(x1 < x2) { + points.push([x1 - breakAt, y1]); + points.push([x1 - breakAt, y1 + spaceY]); + points.push([x2 + breakAt, y2 + spaceY]); + points.push([x2 + breakAt, y2]); + } + points.push([x2, y2]); + + return points; + }, + + draw_arrow: function(from, to, color, width) { + return this.draw_line(from, to, color, width, '#arrowhead', 10, 12); + }, + + draw_line: function(from, to, color, width, markerStart, widthMarker, breakLineAt) { + var x1 = from.offsetLeft, + y1 = from.offsetTop + from.parentElement.offsetTop, + x2 = to.offsetLeft, + y2 = to.offsetTop + to.parentElement.offsetTop, + width1 = from.clientWidth, + height1 = from.clientHeight, + width2 = to.clientWidth, + height2 = to.clientHeight; + + var points = this.get_polyline_points( + x1, y1, x2, y2, width1, height1, width2, height2, widthMarker, breakLineAt + ); + + var polyline_points = _.map(points, function(point) { + return point.join(','); + }).join(); + + var line = document.createElementNS( + 'http://www.w3.org/2000/svg', 'polyline' + ); + line.setAttribute('points', polyline_points); + line.setAttribute('stroke', color || '#000'); + line.setAttribute('stroke-width', width || 1); + line.setAttribute('fill', 'none'); + if (markerStart) { + line.setAttribute('marker-start', 'url(' + markerStart + ')'); + } + this.$el.append(line); + return line; + } + + }); + + return TimelineCanvas; +}); diff --git a/web_timeline/static/src/js/timeline_controller.js b/web_timeline/static/src/js/timeline_controller.js index cdb19742..a6901297 100644 --- a/web_timeline/static/src/js/timeline_controller.js +++ b/web_timeline/static/src/js/timeline_controller.js @@ -5,6 +5,7 @@ var AbstractController = require('web.AbstractController'); var dialogs = require('web.view_dialogs'); var core = require('web.core'); var time = require('web.time'); +var Dialog = require('web.Dialog'); var _t = core._t; @@ -31,6 +32,9 @@ var CalendarController = AbstractController.extend({ if (_.isEmpty(params)){ return; } + var defaults = _.defaults({}, options, { + adjust_window: true + }); var self = this; var domains = params.domain; var contexts = params.context; @@ -59,7 +63,7 @@ var CalendarController = AbstractController.extend({ }, context: self.getSession().user_context, }).then(function(data) { - return self.renderer.on_data_loaded(data, n_group_bys); + return self.renderer.on_data_loaded(data, n_group_bys, defaults.adjust_window); }); }, @@ -82,13 +86,13 @@ var CalendarController = AbstractController.extend({ var id = item.evt.id; var title = item.evt.__name; if (this.open_popup_action) { - var dialog = new dialogs.FormViewDialog(this, { + new dialogs.FormViewDialog(this, { res_model: this.model.modelName, - res_id: parseInt(id).toString() == id ? parseInt(id) : id, + res_id: parseInt(id, 10).toString() === id ? parseInt(id, 10) : id, context: this.getSession().user_context, title: title, - view_id: +this.open_popup_action, - on_saved: function (record) { + view_id: Number(this.open_popup_action), + on_saved: function () { self.write_completed(); }, }).open(); @@ -99,7 +103,7 @@ var CalendarController = AbstractController.extend({ } this.trigger_up('switch_view', { view_type: 'form', - res_id: parseInt(id).toString() == id ? parseInt(id) : id, + res_id: parseInt(id, 10).toString() === id ? parseInt(id, 10) : id, mode: mode, model: this.model.modelName, }); @@ -114,7 +118,7 @@ var CalendarController = AbstractController.extend({ var event_start = item.start; var event_end = item.end; var group = false; - if (item.group != -1) { + if (item.group !== -1) { group = item.group; } var data = {}; @@ -145,10 +149,13 @@ var CalendarController = AbstractController.extend({ context: self.getSession().user_context, }).then(function() { event.data.callback(event.data.item); + self.write_completed({ + adjust_window: false + }); }); }, - _onRemove: function(event) { + _onRemove: function(e) { var self = this; function do_it(event) { @@ -162,8 +169,9 @@ var CalendarController = AbstractController.extend({ }).then(function() { var unlink_index = false; for (var i=0; i
+ + + + + + + + diff --git a/web_timeline/views/web_timeline.xml b/web_timeline/views/web_timeline.xml index 4ce387c5..f6a7db2c 100644 --- a/web_timeline/views/web_timeline.xml +++ b/web_timeline/views/web_timeline.xml @@ -11,6 +11,7 @@