[add] percentage chart

This commit is contained in:
pratu16x7 2017-10-21 21:55:00 +05:30
parent 498b80d3ab
commit 02f0c0a4b4
3 changed files with 222 additions and 74 deletions

View File

@ -1,16 +1,18 @@
let bar_data = { let bar_data = {
"labels": ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"], // "labels": ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"],
"labels": ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug"],
"datasets": [{ "datasets": [{
"color": "orange", "color": "orange",
"values": [50804, 10000, 20000, -61500, 82936.88, 24010, 4000, 6000, 25840, 50804.82, 116820, 6000], // "values": [50804, 10000, 20000, -61500, 82936.88, 24010, 4000, 6000, 25840, 50804.82, 116820, 6000],
"values": [50804, 10000, 20000, 61500, 82936.88, 24010, 40000, 60000, 25840, 50804.82, 116820],
// "values": [-108048, 0, 0, -101500, -50000.88, 24010, 0, 0, 25840, 108048.82, 51682, 0], // "values": [-108048, 0, 0, -101500, -50000.88, 24010, 0, 0, 25840, 108048.82, 51682, 0],
"formatted": ["₹ 0.00", "₹ 0.00", "₹ 0.00", "₹ 61,500.00", "₹ 82,936.88", "₹ 24,010.00", "₹ 0.00", "₹ 0.00", "₹ 25,840.00", "₹ 5,08,048.82", "₹ 1,16,820.00", "₹ 0.00"], "formatted": ["₹ 0.00", "₹ 0.00", "₹ 0.00", "₹ 61,500.00", "₹ 82,936.88", "₹ 24,010.00", "₹ 0.00", "₹ 0.00", "₹ 25,840.00", "₹ 5,08,048.82", "₹ 1,16,820.00", "₹ 0.00"],
} }
, ,
{ {
"color": "blue", "color": "blue",
// "values": [108048, 0, 0, 101500, 50000.88, 24010, 0, 0, 25840, 108048.82, 51682, 0], // "values": [-108048, 0, 0, -101500, -50000.88, 24010, 0, 0, 25840, 108048.82, 51682, 0],
"values": [-108048, 0, 0, -101500, -50000.88, 24010, 0, 0, 25840, 108048.82, 51682, 0], "values": [108048, 0, 0, 101500, 50000.88, 24010, 0, 0, 25840, 108048.82, 51682],
"formatted": ["₹ 0.00", "₹ 0.00", "₹ 0.00", "₹ 61,500.00", "₹ 82,936.88", "₹ 24,010.00", "₹ 0.00", "₹ 0.00", "₹ 25,840.00", "₹ 5,08,048.82", "₹ 1,16,820.00", "₹ 0.00"], "formatted": ["₹ 0.00", "₹ 0.00", "₹ 0.00", "₹ 61,500.00", "₹ 82,936.88", "₹ 24,010.00", "₹ 0.00", "₹ 0.00", "₹ 25,840.00", "₹ 5,08,048.82", "₹ 1,16,820.00", "₹ 0.00"],
} }
] ]
@ -72,16 +74,17 @@ let line_chart = new frappe.chart.FrappeChart ({
data: line_data, data: line_data,
type: 'line', type: 'line',
height: 340, height: 340,
region_fill: 1 region_fill: 1,
y_axis_mode: 'tick'
}) })
let bar_chart = new frappe.chart.FrappeChart ({ let bar_chart = new frappe.chart.FrappeChart ({
parent: "#charts-1", parent: "#charts-1",
data: bar_data, data: bar_data,
type: 'line', type: 'percentage',
height: 140, height: 140,
is_navigable: 1, is_navigable: 1,
region_fill: 1 // region_fill: 1
}) })
bar_chart.parent.addEventListener('data-select', (e) => { bar_chart.parent.addEventListener('data-select', (e) => {

View File

@ -153,7 +153,96 @@
border: 5px solid transparent; border: 5px solid transparent;
border-top-color: rgba(0, 0, 0, 0.8); border-top-color: rgba(0, 0, 0, 0.8);
} }
/*Indicators*/
.indicator,
.indicator-right {
background: none;
font-size: 12px;
vertical-align: middle;
font-weight: bold;
color: #6c7680;
}
.indicator::before,
.indicator-right::after {
content: '';
display: inline-block;
height: 8px;
width: 8px;
border-radius: 8px;
}
.indicator::before {
margin: 0 4px 0 0px;
}
.indicator-right::after {
margin: 0 0 0 4px;
}
.indicator.grey::before,
.indicator-right.grey::after {
background: #bdd3e6;
}
.indicator.light-grey::before,
.indicator-right.light-grey::after {
background: #F0F4F7;
}
.indicator.blue::before,
.indicator-right.blue::after {
background: #5e64ff;
}
.indicator.red::before,
.indicator-right.red::after {
background: #ff5858;
}
.indicator.green::before,
.indicator-right.green::after {
background: #28a745;
}
.indicator.light-green::before,
.indicator-right.light-green::after {
background: #98d85b;
}
.indicator.orange::before,
.indicator-right.orange::after {
background: #ffa00a;
}
.indicator.violet::before,
.indicator-right.violet::after {
background: #743ee2;
}
.indicator.darkgrey::before,
.indicator-right.darkgrey::after {
background: #b8c2cc;
}
.indicator.black::before,
.indicator-right.black::after {
background: #36414C;
}
.indicator.yellow::before,
.indicator-right.yellow::after {
background: #FEEF72;
}
.indicator.light-blue::before,
.indicator-right.light-blue::after {
background: #7CD6FD;
}
.indicator.light-blue::before,
.indicator-right.light-blue::after {
background: #7CD6FD;
}
.indicator.purple::before,
.indicator-right.purple::after {
background: #b554ff;
}
.indicator.magenta::before,
.indicator-right.magenta::after {
background: #ffa3ef;
}
/*Svg properties colors*/
.stroke.grey { .stroke.grey {
stroke: #bdd3e6;
}
.stroke.light-grey {
stroke: #F0F4F7; stroke: #F0F4F7;
} }
.stroke.blue { .stroke.blue {
@ -171,7 +260,7 @@
.stroke.orange { .stroke.orange {
stroke: #ffa00a; stroke: #ffa00a;
} }
.stroke.purple { .stroke.violet {
stroke: #743ee2; stroke: #743ee2;
} }
.stroke.darkgrey { .stroke.darkgrey {
@ -186,10 +275,17 @@
.stroke.light-blue { .stroke.light-blue {
stroke: #7CD6FD; stroke: #7CD6FD;
} }
.stroke.lightblue { .stroke.purple {
stroke: #7CD6FD; stroke: #b554ff;
} }
.stroke.magenta {
stroke: #ffa3ef;
}
.fill.grey { .fill.grey {
fill: #bdd3e6;
}
.fill.light-grey {
fill: #F0F4F7; fill: #F0F4F7;
} }
.fill.blue { .fill.blue {
@ -207,7 +303,7 @@
.fill.orange { .fill.orange {
fill: #ffa00a; fill: #ffa00a;
} }
.fill.purple { .fill.violet {
fill: #743ee2; fill: #743ee2;
} }
.fill.darkgrey { .fill.darkgrey {
@ -222,10 +318,17 @@
.fill.light-blue { .fill.light-blue {
fill: #7CD6FD; fill: #7CD6FD;
} }
.fill.lightblue { .fill.purple {
fill: #7CD6FD; fill: #b554ff;
} }
.fill.magenta {
fill: #ffa3ef;
}
.background.grey { .background.grey {
background: #bdd3e6;
}
.background.light-grey {
background: #F0F4F7; background: #F0F4F7;
} }
.background.blue { .background.blue {
@ -243,7 +346,7 @@
.background.orange { .background.orange {
background: #ffa00a; background: #ffa00a;
} }
.background.purple { .background.violet {
background: #743ee2; background: #743ee2;
} }
.background.darkgrey { .background.darkgrey {
@ -258,10 +361,17 @@
.background.light-blue { .background.light-blue {
background: #7CD6FD; background: #7CD6FD;
} }
.background.lightblue { .background.purple{
background: #7CD6FD; background: #b554ff;
} }
.background.magenta{
background: #ffa3ef;
}
.border-top.grey { .border-top.grey {
border-top: 3px solid #bdd3e6;
}
.border-top.light-grey {
border-top: 3px solid #F0F4F7; border-top: 3px solid #F0F4F7;
} }
.border-top.blue { .border-top.blue {
@ -279,7 +389,7 @@
.border-top.orange { .border-top.orange {
border-top: 3px solid #ffa00a; border-top: 3px solid #ffa00a;
} }
.border-top.purple { .border-top.violet {
border-top: 3px solid #743ee2; border-top: 3px solid #743ee2;
} }
.border-top.darkgrey { .border-top.darkgrey {
@ -294,6 +404,9 @@
.border-top.light-blue { .border-top.light-blue {
border-top: 3px solid #7CD6FD; border-top: 3px solid #7CD6FD;
} }
.border-top.lightblue { .border-top.purple {
border-top: 3px solid #7CD6FD; border-top: 3px solid #b554ff;
}
.border-top.magenta {
border-top: 3px solid #ffa3ef;
} }

View File

@ -9,7 +9,7 @@
// { // {
// title: "Total", // title: "Total",
// color: 'blue', // Indicator colors: 'grey', 'blue', 'red', 'green', 'orange', // color: 'blue', // Indicator colors: 'grey', 'blue', 'red', 'green', 'orange',
// // 'purple', 'darkgrey', 'black', 'yellow', 'lightblue' // // 'violet', 'darkgrey', 'black', 'yellow', 'light-blue'
// value: 80 // value: 80
// } // }
// ] // ]
@ -262,8 +262,8 @@ frappe.chart.AxisChart = class AxisChart extends frappe.chart.FrappeChart {
this.get_x_tooltip = this.format_lambdas.x_tooltip; this.get_x_tooltip = this.format_lambdas.x_tooltip;
this.get_y_tooltip = this.format_lambdas.y_tooltip; this.get_y_tooltip = this.format_lambdas.y_tooltip;
this.colors = ['lightblue', 'purple', 'blue', 'green', 'lightgreen', this.colors = ['green', 'blue', 'violet', 'red', 'orange',
'yellow', 'orange', 'red']; 'yellow', 'light-blue', 'light-green', 'purple', 'magenta'];
this.zero_line = this.height; this.zero_line = this.height;
} }
@ -487,14 +487,7 @@ frappe.chart.AxisChart = class AxisChart extends frappe.chart.FrappeChart {
bind_tooltip() { bind_tooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent // TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chart_wrapper.addEventListener('mousemove', (e) => { this.chart_wrapper.addEventListener('mousemove', (e) => {
let rect = this.chart_wrapper.getBoundingClientRect(); let offset = $$.offset(this.chart_wrapper);
let offset = {
// https://stackoverflow.com/a/7436602/6495043
// rect.top varies with scroll, so we add whatever has been
// scrolled to it to get absolute distance from actual page top
top: rect.top + (document.documentElement.scrollTop || document.body.scrollTop),
left: rect.left + (document.documentElement.scrollLeft || document.body.scrollLeft)
}
let relX = e.pageX - offset.left - this.translate_x; let relX = e.pageX - offset.left - this.translate_x;
let relY = e.pageY - offset.top - this.translate_y; let relY = e.pageY - offset.top - this.translate_y;
@ -672,7 +665,7 @@ frappe.chart.AxisChart = class AxisChart extends frappe.chart.FrappeChart {
} }
// Make both region parts even // Make both region parts even
if(pos_no_of_parts % 2 !== 0) pos_no_of_parts++; if(pos_no_of_parts % 2 !== 0 && neg_no_of_parts > 0) pos_no_of_parts++;
if(neg_no_of_parts % 2 !== 0) { if(neg_no_of_parts % 2 !== 0) {
// every increase in no_of_parts entails an increase in corresponding bound // every increase in no_of_parts entails an increase in corresponding bound
// except here, it happens implicitly after every calc_no_of_parts() call // except here, it happens implicitly after every calc_no_of_parts() call
@ -820,18 +813,18 @@ frappe.chart.AxisChart = class AxisChart extends frappe.chart.FrappeChart {
} }
frappe.chart.BarChart = class BarChart extends frappe.chart.AxisChart { frappe.chart.BarChart = class BarChart extends frappe.chart.AxisChart {
constructor() { constructor(args) {
super(arguments[0]); super(args);
this.type = 'bar-graph'; this.type = 'bar-graph';
this.x_axis_mode = args.x_axis_mode || 'tick';
this.y_axis_mode = args.y_axis_mode || 'span';
this.setup(); this.setup();
} }
setup_values() { setup_values() {
super.setup_values(); super.setup_values();
this.x_offset = this.avg_unit_width; this.x_offset = this.avg_unit_width;
this.y_axis_mode = 'span';
this.x_axis_mode = 'tick';
this.unit_args = { this.unit_args = {
type: 'bar', type: 'bar',
args: { args: {
@ -892,14 +885,14 @@ frappe.chart.LineChart = class LineChart extends frappe.chart.AxisChart {
this.type = 'line-graph'; this.type = 'line-graph';
this.region_fill = args.region_fill; this.region_fill = args.region_fill;
this.x_axis_mode = args.x_axis_mode || 'span';
this.y_axis_mode = args.y_axis_mode || 'span';
this.setup(); this.setup();
} }
setup_values() { setup_values() {
super.setup_values(); super.setup_values();
this.y_axis_mode = 'span';
this.x_axis_mode = 'span';
this.unit_args = { this.unit_args = {
type: 'dot', type: 'dot',
args: { radius: 4 } args: { radius: 4 }
@ -957,14 +950,21 @@ frappe.chart.PercentageChart = class PercentageChart extends frappe.chart.Frappe
constructor(args) { constructor(args) {
super(args); super(args);
this.x = this.data.labels;
this.y = this.data.datasets;
this.get_x_label = this.format_lambdas.x_label; this.get_x_label = this.format_lambdas.x_label;
this.get_y_label = this.format_lambdas.y_label; this.get_y_label = this.format_lambdas.y_label;
this.get_x_tooltip = this.format_lambdas.x_tooltip; this.get_x_tooltip = this.format_lambdas.x_tooltip;
this.get_y_tooltip = this.format_lambdas.y_tooltip; this.get_y_tooltip = this.format_lambdas.y_tooltip;
this.max_slices = 10;
this.max_legend_points = 6;
this.colors = args.colors;
if(!this.colors || this.colors.length < this.data.labels.length) {
this.colors = ['light-blue', 'blue', 'violet', 'red', 'orange',
'yellow', 'green', 'light-green', 'purple', 'magenta'];
}
this.setup(); this.setup();
} }
@ -991,22 +991,6 @@ frappe.chart.PercentageChart = class PercentageChart extends frappe.chart.Frappe
}); });
} }
setup_values() {
this.x.totals = this.x.map((d, i) => {
let total = 0;
this.y.map(e => {
total += e.values[i];
});
return total;
});
if(!this.x.colors) {
this.x.colors = ['green', 'blue', 'purple', 'red', 'orange',
'yellow', 'lightblue', 'lightgreen'];
}
}
setup_utils() { }
setup_components() { setup_components() {
this.percentage_bar = $$.create('div', { this.percentage_bar = $$.create('div', {
className: 'progress', className: 'progress',
@ -1014,46 +998,83 @@ frappe.chart.PercentageChart = class PercentageChart extends frappe.chart.Frappe
}); });
} }
setup_values() {
this.slice_totals = [];
let all_totals = this.data.labels.map((d, i) => {
let total = 0;
this.data.datasets.map(e => {
total += e.values[i];
});
return [total, d];
}).filter(d => { return d[0] > 0; }); // keep only positive results
let totals = all_totals;
if(all_totals.length > this.max_slices) {
all_totals.sort((a, b) => { return b[0] - a[0]; });
totals = all_totals.slice(0, this.max_slices-1);
let others = all_totals.slice(this.max_slices-1);
let sum_of_others = 0;
others.map(d => {sum_of_others += d[0]});
totals.push([sum_of_others, 'Rest']);
this.colors[this.max_slices-1] = 'grey';
}
this.labels = [];
totals.map(d => {
this.slice_totals.push(d[0]);
this.labels.push(d[1]);
});
this.legend_totals = this.slice_totals.slice(0, this.max_legend_points);
}
setup_utils() { }
make_graph_components() { make_graph_components() {
this.grand_total = this.x.totals.reduce((a, b) => a + b, 0); this.grand_total = this.slice_totals.reduce((a, b) => a + b, 0);
this.x.units = []; this.slices = [];
this.x.totals.map((total, i) => { this.slice_totals.map((total, i) => {
let part = $$.create('div', { let slice = $$.create('div', {
className: `progress-bar background ${this.x.colors[i]}`, className: `progress-bar background ${this.colors[i]}`,
style: `width: ${total*100/this.grand_total}%`, style: `width: ${total*100/this.grand_total}%`,
inside: this.percentage_bar inside: this.percentage_bar
}); });
this.x.units.push(part); this.slices.push(slice);
}); });
} }
bind_tooltip() { bind_tooltip() {
this.x.units.map((part, i) => { this.slices.map((slice, i) => {
part.addEventListener('mouseenter', () => { slice.addEventListener('mouseenter', () => {
let g_off = this.chart_wrapper.offset(), p_off = part.offset(); let g_off = $$.offset(this.chart_wrapper), p_off = $$.offset(slice);
let x = p_off.left - g_off.left + part.offsetWidth/2; let x = p_off.left - g_off.left + slice.offsetWidth/2;
let y = p_off.top - g_off.top - 6; let y = p_off.top - g_off.top - 6;
let title = (this.x.formatted && this.x.formatted.length>0 let title = (this.formatted_labels && this.formatted_labels.length>0
? this.x.formatted[i] : this.x[i]) + ': '; ? this.formatted_labels[i] : this.labels[i]) + ': ';
let percent = (this.x.totals[i]*100/this.grand_total).toFixed(1); let percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);
this.tip.set_values(x, y, title, percent); this.tip.set_values(x, y, title, percent + "%");
this.tip.show_tip(); this.tip.show_tip();
}); });
}); });
} }
show_summary() { show_summary() {
let x_values = this.x.formatted && this.x.formatted.length > 0 let x_values = this.formatted_labels && this.formatted_labels.length > 0
? this.x.formatted : this.x; ? this.formatted_labels : this.labels;
this.x.totals.map((d, i) => { this.legend_totals.map((d, i) => {
if(d) { if(d) {
let stats = $$.create('div', { let stats = $$.create('div', {
className: 'stats', className: 'stats',
inside: this.stats_wrapper inside: this.stats_wrapper
}); });
stats.innerHTML = `<span class="indicator ${this.x.colors[i]}"> stats.innerHTML = `<span class="indicator ${this.colors[i]}">
<span class="text-muted">${x_values[i]}:</span> <span class="text-muted">${x_values[i]}:</span>
${d} ${d}
</span>`; </span>`;
@ -1614,6 +1635,17 @@ $$.animateSVG = (element, props, dur, easing_type="linear") => {
return [anim_element, new_element]; return [anim_element, new_element];
} }
$$.offset = function(element) {
let rect = element.getBoundingClientRect();
return {
// https://stackoverflow.com/a/7436602/6495043
// rect.top varies with scroll, so we add whatever has been
// scrolled to it to get absolute distance from actual page top
top: rect.top + (document.documentElement.scrollTop || document.body.scrollTop),
left: rect.left + (document.documentElement.scrollLeft || document.body.scrollLeft)
}
};
$$.bind = function(element, o) { $$.bind = function(element, o) {
if (element) { if (element) {
for (var event in o) { for (var event in o) {