[REF] reimplement journal dashboard widget with Chart.js
This commit is one part of the task 'removing nvd3'. We use now Chart.js to draw charts. Task ID: 1946138
This commit is contained in:
committed by
Mathieu Duckerts-Antoine
parent
2af74a5f53
commit
769806ad13
@@ -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();
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onResize: function () {
|
||||
if (this.chart) {
|
||||
this.chart.update();
|
||||
this._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 = [];
|
||||
|
||||
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
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user