[add] percentage chart
This commit is contained in:
parent
498b80d3ab
commit
02f0c0a4b4
@ -1,16 +1,18 @@
|
||||
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": [{
|
||||
"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],
|
||||
"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",
|
||||
// "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"],
|
||||
}
|
||||
]
|
||||
@ -72,16 +74,17 @@ let line_chart = new frappe.chart.FrappeChart ({
|
||||
data: line_data,
|
||||
type: 'line',
|
||||
height: 340,
|
||||
region_fill: 1
|
||||
region_fill: 1,
|
||||
y_axis_mode: 'tick'
|
||||
})
|
||||
|
||||
let bar_chart = new frappe.chart.FrappeChart ({
|
||||
parent: "#charts-1",
|
||||
data: bar_data,
|
||||
type: 'line',
|
||||
type: 'percentage',
|
||||
height: 140,
|
||||
is_navigable: 1,
|
||||
region_fill: 1
|
||||
// region_fill: 1
|
||||
})
|
||||
|
||||
bar_chart.parent.addEventListener('data-select', (e) => {
|
||||
|
||||
137
src/charts.css
137
src/charts.css
@ -153,7 +153,96 @@
|
||||
border: 5px solid transparent;
|
||||
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: #bdd3e6;
|
||||
}
|
||||
.stroke.light-grey {
|
||||
stroke: #F0F4F7;
|
||||
}
|
||||
.stroke.blue {
|
||||
@ -171,7 +260,7 @@
|
||||
.stroke.orange {
|
||||
stroke: #ffa00a;
|
||||
}
|
||||
.stroke.purple {
|
||||
.stroke.violet {
|
||||
stroke: #743ee2;
|
||||
}
|
||||
.stroke.darkgrey {
|
||||
@ -186,10 +275,17 @@
|
||||
.stroke.light-blue {
|
||||
stroke: #7CD6FD;
|
||||
}
|
||||
.stroke.lightblue {
|
||||
stroke: #7CD6FD;
|
||||
.stroke.purple {
|
||||
stroke: #b554ff;
|
||||
}
|
||||
.stroke.magenta {
|
||||
stroke: #ffa3ef;
|
||||
}
|
||||
|
||||
.fill.grey {
|
||||
fill: #bdd3e6;
|
||||
}
|
||||
.fill.light-grey {
|
||||
fill: #F0F4F7;
|
||||
}
|
||||
.fill.blue {
|
||||
@ -207,7 +303,7 @@
|
||||
.fill.orange {
|
||||
fill: #ffa00a;
|
||||
}
|
||||
.fill.purple {
|
||||
.fill.violet {
|
||||
fill: #743ee2;
|
||||
}
|
||||
.fill.darkgrey {
|
||||
@ -222,10 +318,17 @@
|
||||
.fill.light-blue {
|
||||
fill: #7CD6FD;
|
||||
}
|
||||
.fill.lightblue {
|
||||
fill: #7CD6FD;
|
||||
.fill.purple {
|
||||
fill: #b554ff;
|
||||
}
|
||||
.fill.magenta {
|
||||
fill: #ffa3ef;
|
||||
}
|
||||
|
||||
.background.grey {
|
||||
background: #bdd3e6;
|
||||
}
|
||||
.background.light-grey {
|
||||
background: #F0F4F7;
|
||||
}
|
||||
.background.blue {
|
||||
@ -243,7 +346,7 @@
|
||||
.background.orange {
|
||||
background: #ffa00a;
|
||||
}
|
||||
.background.purple {
|
||||
.background.violet {
|
||||
background: #743ee2;
|
||||
}
|
||||
.background.darkgrey {
|
||||
@ -258,10 +361,17 @@
|
||||
.background.light-blue {
|
||||
background: #7CD6FD;
|
||||
}
|
||||
.background.lightblue {
|
||||
background: #7CD6FD;
|
||||
.background.purple{
|
||||
background: #b554ff;
|
||||
}
|
||||
.background.magenta{
|
||||
background: #ffa3ef;
|
||||
}
|
||||
|
||||
.border-top.grey {
|
||||
border-top: 3px solid #bdd3e6;
|
||||
}
|
||||
.border-top.light-grey {
|
||||
border-top: 3px solid #F0F4F7;
|
||||
}
|
||||
.border-top.blue {
|
||||
@ -279,7 +389,7 @@
|
||||
.border-top.orange {
|
||||
border-top: 3px solid #ffa00a;
|
||||
}
|
||||
.border-top.purple {
|
||||
.border-top.violet {
|
||||
border-top: 3px solid #743ee2;
|
||||
}
|
||||
.border-top.darkgrey {
|
||||
@ -294,6 +404,9 @@
|
||||
.border-top.light-blue {
|
||||
border-top: 3px solid #7CD6FD;
|
||||
}
|
||||
.border-top.lightblue {
|
||||
border-top: 3px solid #7CD6FD;
|
||||
.border-top.purple {
|
||||
border-top: 3px solid #b554ff;
|
||||
}
|
||||
.border-top.magenta {
|
||||
border-top: 3px solid #ffa3ef;
|
||||
}
|
||||
|
||||
142
src/charts.js
142
src/charts.js
@ -9,7 +9,7 @@
|
||||
// {
|
||||
// title: "Total",
|
||||
// color: 'blue', // Indicator colors: 'grey', 'blue', 'red', 'green', 'orange',
|
||||
// // 'purple', 'darkgrey', 'black', 'yellow', 'lightblue'
|
||||
// // 'violet', 'darkgrey', 'black', 'yellow', 'light-blue'
|
||||
// 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_y_tooltip = this.format_lambdas.y_tooltip;
|
||||
|
||||
this.colors = ['lightblue', 'purple', 'blue', 'green', 'lightgreen',
|
||||
'yellow', 'orange', 'red'];
|
||||
this.colors = ['green', 'blue', 'violet', 'red', 'orange',
|
||||
'yellow', 'light-blue', 'light-green', 'purple', 'magenta'];
|
||||
|
||||
this.zero_line = this.height;
|
||||
}
|
||||
@ -487,14 +487,7 @@ frappe.chart.AxisChart = class AxisChart extends frappe.chart.FrappeChart {
|
||||
bind_tooltip() {
|
||||
// TODO: could be in tooltip itself, as it is a given functionality for its parent
|
||||
this.chart_wrapper.addEventListener('mousemove', (e) => {
|
||||
let rect = this.chart_wrapper.getBoundingClientRect();
|
||||
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 offset = $$.offset(this.chart_wrapper);
|
||||
let relX = e.pageX - offset.left - this.translate_x;
|
||||
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
|
||||
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) {
|
||||
// every increase in no_of_parts entails an increase in corresponding bound
|
||||
// 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 {
|
||||
constructor() {
|
||||
super(arguments[0]);
|
||||
constructor(args) {
|
||||
super(args);
|
||||
|
||||
this.type = 'bar-graph';
|
||||
this.x_axis_mode = args.x_axis_mode || 'tick';
|
||||
this.y_axis_mode = args.y_axis_mode || 'span';
|
||||
this.setup();
|
||||
}
|
||||
|
||||
setup_values() {
|
||||
super.setup_values();
|
||||
this.x_offset = this.avg_unit_width;
|
||||
this.y_axis_mode = 'span';
|
||||
this.x_axis_mode = 'tick';
|
||||
this.unit_args = {
|
||||
type: 'bar',
|
||||
args: {
|
||||
@ -892,14 +885,14 @@ frappe.chart.LineChart = class LineChart extends frappe.chart.AxisChart {
|
||||
|
||||
this.type = 'line-graph';
|
||||
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();
|
||||
}
|
||||
|
||||
setup_values() {
|
||||
super.setup_values();
|
||||
this.y_axis_mode = 'span';
|
||||
this.x_axis_mode = 'span';
|
||||
this.unit_args = {
|
||||
type: 'dot',
|
||||
args: { radius: 4 }
|
||||
@ -957,14 +950,21 @@ frappe.chart.PercentageChart = class PercentageChart extends frappe.chart.Frappe
|
||||
constructor(args) {
|
||||
super(args);
|
||||
|
||||
this.x = this.data.labels;
|
||||
this.y = this.data.datasets;
|
||||
|
||||
this.get_x_label = this.format_lambdas.x_label;
|
||||
this.get_y_label = this.format_lambdas.y_label;
|
||||
this.get_x_tooltip = this.format_lambdas.x_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();
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
this.percentage_bar = $$.create('div', {
|
||||
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() {
|
||||
this.grand_total = this.x.totals.reduce((a, b) => a + b, 0);
|
||||
this.x.units = [];
|
||||
this.x.totals.map((total, i) => {
|
||||
let part = $$.create('div', {
|
||||
className: `progress-bar background ${this.x.colors[i]}`,
|
||||
this.grand_total = this.slice_totals.reduce((a, b) => a + b, 0);
|
||||
this.slices = [];
|
||||
this.slice_totals.map((total, i) => {
|
||||
let slice = $$.create('div', {
|
||||
className: `progress-bar background ${this.colors[i]}`,
|
||||
style: `width: ${total*100/this.grand_total}%`,
|
||||
inside: this.percentage_bar
|
||||
});
|
||||
this.x.units.push(part);
|
||||
this.slices.push(slice);
|
||||
});
|
||||
}
|
||||
|
||||
bind_tooltip() {
|
||||
this.x.units.map((part, i) => {
|
||||
part.addEventListener('mouseenter', () => {
|
||||
let g_off = this.chart_wrapper.offset(), p_off = part.offset();
|
||||
this.slices.map((slice, i) => {
|
||||
slice.addEventListener('mouseenter', () => {
|
||||
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 title = (this.x.formatted && this.x.formatted.length>0
|
||||
? this.x.formatted[i] : this.x[i]) + ': ';
|
||||
let percent = (this.x.totals[i]*100/this.grand_total).toFixed(1);
|
||||
let title = (this.formatted_labels && this.formatted_labels.length>0
|
||||
? this.formatted_labels[i] : this.labels[i]) + ': ';
|
||||
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();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
show_summary() {
|
||||
let x_values = this.x.formatted && this.x.formatted.length > 0
|
||||
? this.x.formatted : this.x;
|
||||
this.x.totals.map((d, i) => {
|
||||
let x_values = this.formatted_labels && this.formatted_labels.length > 0
|
||||
? this.formatted_labels : this.labels;
|
||||
this.legend_totals.map((d, i) => {
|
||||
if(d) {
|
||||
let stats = $$.create('div', {
|
||||
className: 'stats',
|
||||
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>
|
||||
${d}
|
||||
</span>`;
|
||||
@ -1614,6 +1635,17 @@ $$.animateSVG = (element, props, dur, easing_type="linear") => {
|
||||
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) {
|
||||
if (element) {
|
||||
for (var event in o) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user