diff options
| author | Robot Odoo <xmo+roboto@odoo.com> | 2019-04-02 10:47:52 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-04-02 10:47:52 +0200 |
| commit | d2c75b7e864369490f1780fc017b8e91ae487788 (patch) | |
| tree | c99e39ae448be812a117f01eaf1918d133aee782 /addons/web/static/src/js/fields/basic_fields.js | |
| parent | a742279ad27f221326f8823f55ac6eaa2eb3856c (diff) | |
| parent | 596206cdf6d3fc31d20c44951f3f0f1ba49aa953 (diff) | |
[MERGE] *: Improve graph view, replace nvd3 with Chart.js
The present work is part of an ongoing plan aiming to refactor/improve the reporting views (in particular the graph view).
The most notable changes are
- better support of comparison mode (comparison of different time periods)
- better tooltips and legend (content/style)
It has also been decided to use Chart.js instead of nvd3 to draw charts.
The main reasons are that Chart.js is better maintained/designed and offers many interesting features.
Thus we have rewritten all the code depending on nvd3, adapt it to the new library, and remove completely the nvd3 library and the references to it.
Task Ids: 1911201, 1946138
closes odoo/odoo#31766
Signed-off-by: Géry Debongnie (ged) <ged@openerp.com>
Diffstat (limited to 'addons/web/static/src/js/fields/basic_fields.js')
| -rw-r--r-- | addons/web/static/src/js/fields/basic_fields.js | 211 |
1 files changed, 94 insertions, 117 deletions
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 7439c36037f..4940839e612 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -2483,35 +2483,16 @@ var FieldToggleBoolean = AbstractField.extend({ var JournalDashboardGraph = AbstractField.extend({ className: "o_dashboard_graph", - cssLibs: [ - '/web/static/lib/nvd3/nv.d3.css' - ], jsLibs: [ - '/web/static/lib/nvd3/d3.v3.js', - '/web/static/lib/nvd3/nv.d3.js', - '/web/static/src/js/libs/nvd3.js' + '/web/static/lib/Chart/Chart.js', ], init: function () { this._super.apply(this, arguments); this.graph_type = this.attrs.graph_type; this.data = JSON.parse(this.value); }, - start: function () { - this._onResize = this._onResize.bind(this); - nv.utils.windowResize(this._onResize); - return this._super.apply(this, arguments); - }, - destroy: function () { - if ('nv' in window && nv.utils && nv.utils.offWindowResize) { - // if the widget is destroyed before the lazy loaded libs (nv) are - // actually loaded (i.e. after the widget has actually started), - // nv is undefined, but the handler isn't bound yet anyway - nv.utils.offWindowResize(this._onResize); - } - this._super.apply(this, arguments); - }, /** - * The widget view uses the nv(d3) lib to render the graph. This lib + * The widget view uses the ChartJS lib to render the graph. This lib * requires that the rendering is done directly into the DOM (so that it can * correctly compute positions). However, the views are always rendered in * fragments, and appended to the DOM once ready (to prevent them from @@ -2535,23 +2516,7 @@ var JournalDashboardGraph = AbstractField.extend({ //-------------------------------------------------------------------------- /** - * @private - */ - _customizeChart: function () { - if (this.graph_type === 'bar') { - // Add classes related to time on each bar of the bar chart - var bar_classes = _.map(this.data[0].values, function (v) {return v.type; }); - - _.each(this.$('.nv-bar'), function (v, k){ - // classList doesn't work with phantomJS & addClass doesn't work with a SVG element - $(v).attr('class', $(v).attr('class') + ' ' + bar_classes[k]); - }); - } - }, - /** - * Render the widget only when it is in the DOM. This is because nvd3 - * renders graph only when it is in DOM, apparently to compute available - * height and width for instance. + * Render the widget only when it is in the DOM. * * @override * @private @@ -2568,89 +2533,101 @@ var JournalDashboardGraph = AbstractField.extend({ * @private */ _renderInDOM: function () { - // note: the rendering of this widget is aynchronous as nvd3 does a - // setTimeout(0) before executing the callback given to addGraph - var self = this; this.$el.empty(); - this.chart = null; - nv.addGraph(function () { - self.$svg = self.$el.append('<svg>'); - switch (self.graph_type) { - case "line": - self.$svg.addClass('o_graph_linechart'); - - self.chart = nv.models.lineChart(); - self.chart.forceY([0]); - self.chart.options({ - x: function (d, u) { return u; }, - margin: {'left': 0, 'right': 0, 'top': 5, 'bottom': 0}, - showYAxis: false, - showLegend: false, - }); - self.chart.xAxis.tickFormat(function (d) { - var label = ''; - _.each(self.data, function (v){ - if (v.values[d] && v.values[d].x){ - label = v.values[d].x; - } - }); - return label; - }); - self.chart.yAxis.tickFormat(d3.format(',.2f')); - - self.chart.tooltip.contentGenerator(function (key) { - return qweb.render('GraphCustomTooltip', { - 'color': key.point.color, - 'key': self.data[0].key, - 'value': d3.format(',.2f')(key.point.y) - }); - }); - break; - - case "bar": - self.$svg.addClass('o_graph_barchart'); - - self.chart = nv.models.discreteBarChart() - .x(function (d) { return d.label; }) - .y(function (d) { return d.value; }) - .showValues(false) - .showYAxis(false) - .color(['#875A7B', '#526774', '#FA8072']) - .margin({'left': 0, 'right': 0, 'top': 20, 'bottom': 20}); - - self.chart.xAxis.axisLabel(self.data[0].title); - self.chart.yAxis.tickFormat(d3.format(',.2f')); - - self.chart.tooltip.contentGenerator(function (key) { - return qweb.render('GraphCustomTooltip', { - 'color': key.color, - 'key': self.data[0].key, - 'value': d3.format(',.2f')(key.data.value) - }); - }); - break; - } - d3.select(self.$('svg')[0]) - .datum(self.data) - .transition().duration(600) - .call(self.chart); - - self._customizeChart(); + var config, cssClass; + if (this.graph_type === 'line') { + config = this._getLineChartConfig(); + cssClass = 'o_graph_linechart'; + } else if (this.graph_type === 'bar') { + config = this._getBarChartConfig(); + cssClass = 'o_graph_barchart'; + } + this.$canvas = $('<canvas/>'); + this.$el.addClass(cssClass); + this.$el.empty(); + this.$el.append(this.$canvas); + var context = this.$canvas[0].getContext('2d'); + this.chart = new Chart(context, config); + }, + _getLineChartConfig: function () { + var labels = this.data[0].values.map(function (pt) { + return pt.x; }); + var borderColor = this.data[0].is_sample_data ? '#dddddd' : '#875a7b'; + var backgroundColor = this.data[0].is_sample_data ? '#ebebeb' : '#dcd0d9'; + return { + type: 'line', + data: { + labels: labels, + datasets: [{ + data: this.data[0].values, + fill: 'start', + label: this.data[0].key, + backgroundColor: backgroundColor, + borderColor: borderColor, + borderWidth: 2, + }] + }, + options: { + legend: {display: false}, + scales: { + yAxes: [{display: false}], + xAxes: [{display: false}] + }, + maintainAspectRatio: false, + elements: { + line: { + tension: 0.000001 + } + }, + tooltips: { + intersect: false, + position: 'nearest', + caretSize: 0, + }, + }, + }; }, + _getBarChartConfig: function () { + var data = []; + var labels = []; + var backgroundColor = []; - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * @private - */ - _onResize: function () { - if (this.chart) { - this.chart.update(); - this._customizeChart(); - } + this.data[0].values.forEach(function (pt) { + data.push(pt.value); + labels.push(pt.label); + var color = pt.type === 'past' ? '#ccbdc8' : (pt.type === 'future' ? '#a5d8d7' : '#ebebeb'); + backgroundColor.push(color); + }); + return { + type: 'bar', + data: { + labels: labels, + datasets: [{ + data: data, + fill: 'start', + label: this.data[0].key, + backgroundColor: backgroundColor, + }] + }, + options: { + legend: {display: false}, + scales: { + yAxes: [{display: false}], + }, + maintainAspectRatio: false, + tooltips: { + intersect: false, + position: 'nearest', + caretSize: 0, + }, + elements: { + line: { + tension: 0.000001 + } + }, + }, + }; }, }); |
