[chart] Aggregation chart for pie/percantage
This commit is contained in:
parent
59ad41427f
commit
fb61523b1a
352
dist/frappe-charts.esm.js
vendored
352
dist/frappe-charts.esm.js
vendored
@ -202,10 +202,30 @@ class SvgTip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const VERT_SPACE_OUTSIDE_BASE_CHART = 40;
|
||||||
* Returns the value of a number upto 2 decimal places.
|
const TRANSLATE_Y_BASE_CHART = 20;
|
||||||
* @param {Number} d Any number
|
const LEFT_MARGIN_BASE_CHART = 60;
|
||||||
*/
|
const RIGHT_MARGIN_BASE_CHART = 40;
|
||||||
|
const Y_AXIS_MARGIN = 60;
|
||||||
|
|
||||||
|
const INIT_CHART_UPDATE_TIMEOUT = 700;
|
||||||
|
const CHART_POST_ANIMATE_TIMEOUT = 400;
|
||||||
|
|
||||||
|
const DEFAULT_AXIS_CHART_TYPE = 'line';
|
||||||
|
const AXIS_DATASET_CHART_TYPES = ['line', 'bar'];
|
||||||
|
|
||||||
|
const BAR_CHART_SPACE_RATIO = 0.5;
|
||||||
|
const MIN_BAR_PERCENT_HEIGHT = 0.01;
|
||||||
|
|
||||||
|
const LINE_CHART_DOT_SIZE = 4;
|
||||||
|
const DOT_OVERLAY_SIZE_INCR = 4;
|
||||||
|
|
||||||
|
const DEFAULT_CHAR_WIDTH = 8;
|
||||||
|
|
||||||
|
// Universal constants
|
||||||
|
const ANGLE_RATIO = Math.PI / 180;
|
||||||
|
const FULL_ANGLE = 360;
|
||||||
|
|
||||||
function floatTwo(d) {
|
function floatTwo(d) {
|
||||||
return parseFloat(d.toFixed(2));
|
return parseFloat(d.toFixed(2));
|
||||||
}
|
}
|
||||||
@ -248,6 +268,15 @@ function getStringWidth(string, charWidth) {
|
|||||||
return (string+"").length * charWidth;
|
return (string+"").length * charWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function getPositionByAngle(angle, radius) {
|
||||||
|
return {
|
||||||
|
x:Math.sin(angle * ANGLE_RATIO) * radius,
|
||||||
|
y:Math.cos(angle * ANGLE_RATIO) * radius,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getBarHeightAndYAttr(yTop, zeroLine) {
|
function getBarHeightAndYAttr(yTop, zeroLine) {
|
||||||
let height, y;
|
let height, y;
|
||||||
if (yTop <= zeroLine) {
|
if (yTop <= zeroLine) {
|
||||||
@ -372,26 +401,6 @@ function animatePath(paths, newXList, newYList, zeroLine) {
|
|||||||
return pathComponents;
|
return pathComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VERT_SPACE_OUTSIDE_BASE_CHART = 40;
|
|
||||||
const TRANSLATE_Y_BASE_CHART = 20;
|
|
||||||
const LEFT_MARGIN_BASE_CHART = 60;
|
|
||||||
const RIGHT_MARGIN_BASE_CHART = 40;
|
|
||||||
const Y_AXIS_MARGIN = 60;
|
|
||||||
|
|
||||||
const INIT_CHART_UPDATE_TIMEOUT = 700;
|
|
||||||
const CHART_POST_ANIMATE_TIMEOUT = 400;
|
|
||||||
|
|
||||||
const DEFAULT_AXIS_CHART_TYPE = 'line';
|
|
||||||
const AXIS_DATASET_CHART_TYPES = ['line', 'bar'];
|
|
||||||
|
|
||||||
const BAR_CHART_SPACE_RATIO = 0.5;
|
|
||||||
const MIN_BAR_PERCENT_HEIGHT = 0.01;
|
|
||||||
|
|
||||||
const LINE_CHART_DOT_SIZE = 4;
|
|
||||||
const DOT_OVERLAY_SIZE_INCR = 4;
|
|
||||||
|
|
||||||
const DEFAULT_CHAR_WIDTH = 8;
|
|
||||||
|
|
||||||
const AXIS_TICK_LENGTH = 6;
|
const AXIS_TICK_LENGTH = 6;
|
||||||
const LABEL_MARGIN = 4;
|
const LABEL_MARGIN = 4;
|
||||||
const FONT_SIZE = 10;
|
const FONT_SIZE = 10;
|
||||||
@ -489,6 +498,16 @@ function makePath(pathStr, className='', stroke='none', fill='none') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function makeArcPathStr(startPosition, endPosition, center, radius, clockWise=1){
|
||||||
|
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
||||||
|
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
||||||
|
|
||||||
|
return `M${center.x} ${center.y}
|
||||||
|
L${arcStartX} ${arcStartY}
|
||||||
|
A ${radius} ${radius} 0 0 ${clockWise ? 1 : 0}
|
||||||
|
${arcEndX} ${arcEndY} z`;
|
||||||
|
}
|
||||||
|
|
||||||
function makeGradient(svgDefElem, color, lighter = false) {
|
function makeGradient(svgDefElem, color, lighter = false) {
|
||||||
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
||||||
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||||
@ -917,7 +936,7 @@ const PRESET_COLOR_MAP = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
|
const DEFAULT_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
|
||||||
'yellow', 'green', 'light-green', 'purple', 'magenta'];
|
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
|
||||||
|
|
||||||
function limitColor(r){
|
function limitColor(r){
|
||||||
if (r > 255) return 255;
|
if (r > 255) return 255;
|
||||||
@ -986,13 +1005,11 @@ function getDifferentChart(type, current_type, parent, args) {
|
|||||||
// Okay, this is anticlimactic
|
// Okay, this is anticlimactic
|
||||||
// this function will need to actually be 'changeChartType(type)'
|
// this function will need to actually be 'changeChartType(type)'
|
||||||
// that will update only the required elements, but for now ...
|
// that will update only the required elements, but for now ...
|
||||||
return new Chart(parent, {
|
|
||||||
title: args.title,
|
args.type = type;
|
||||||
data: args.data,
|
args.colors = useColor ? args.colors : undefined;
|
||||||
type: type,
|
|
||||||
height: args.height,
|
return new Chart(parent, args);
|
||||||
colors: useColor ? args.colors : undefined
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leveraging SMIL Animations
|
// Leveraging SMIL Animations
|
||||||
@ -1384,14 +1401,84 @@ class BaseChart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PercentageChart extends BaseChart {
|
class AggregationChart extends BaseChart {
|
||||||
|
constructor(parent, args) {
|
||||||
|
super(parent, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
|
||||||
|
this.config.maxSlices = args.maxSlices || 20;
|
||||||
|
this.config.maxLegendPoints = args.maxLegendPoints || 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
calc() {
|
||||||
|
let s = this.state;
|
||||||
|
let maxSlices = this.config.maxSlices;
|
||||||
|
s.sliceTotals = [];
|
||||||
|
|
||||||
|
let allTotals = this.data.labels.map((label, i) => {
|
||||||
|
let total = 0;
|
||||||
|
this.data.datasets.map(e => {
|
||||||
|
total += e.values[i];
|
||||||
|
});
|
||||||
|
return [total, label];
|
||||||
|
}).filter(d => { return d[0] > 0; }); // keep only positive results
|
||||||
|
|
||||||
|
let totals = allTotals;
|
||||||
|
if(allTotals.length > maxSlices) {
|
||||||
|
// Prune and keep a grey area for rest as per maxSlices
|
||||||
|
allTotals.sort((a, b) => { return b[0] - a[0]; });
|
||||||
|
|
||||||
|
totals = allTotals.slice(0, maxSlices-1);
|
||||||
|
let remaining = allTotals.slice(maxSlices-1);
|
||||||
|
|
||||||
|
let sumOfRemaining = 0;
|
||||||
|
remaining.map(d => {sumOfRemaining += d[0];});
|
||||||
|
totals.push([sumOfRemaining, 'Rest']);
|
||||||
|
this.colors[maxSlices-1] = 'grey';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.labels = [];
|
||||||
|
totals.map(d => {
|
||||||
|
s.sliceTotals.push(d[0]);
|
||||||
|
this.labels.push(d[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() { }
|
||||||
|
|
||||||
|
bindTooltip() { }
|
||||||
|
|
||||||
|
renderLegend() {
|
||||||
|
let s = this.state;
|
||||||
|
|
||||||
|
this.legendTotals = s.sliceTotals.slice(0, this.config.maxLegendPoints);
|
||||||
|
|
||||||
|
let x_values = this.formatted_labels && this.formatted_labels.length > 0
|
||||||
|
? this.formatted_labels : this.labels;
|
||||||
|
this.legendTotals.map((d, i) => {
|
||||||
|
if(d) {
|
||||||
|
let stats = $.create('div', {
|
||||||
|
className: 'stats',
|
||||||
|
inside: this.statsWrapper
|
||||||
|
});
|
||||||
|
stats.innerHTML = `<span class="indicator">
|
||||||
|
<i style="background: ${this.colors[i]}"></i>
|
||||||
|
<span class="text-muted">${x_values[i]}:</span>
|
||||||
|
${d}
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PercentageChart extends AggregationChart {
|
||||||
constructor(parent, args) {
|
constructor(parent, args) {
|
||||||
super(parent, args);
|
super(parent, args);
|
||||||
this.type = 'percentage';
|
this.type = 'percentage';
|
||||||
|
|
||||||
this.max_slices = 10;
|
|
||||||
this.max_legend_points = 6;
|
|
||||||
|
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1420,9 +1507,10 @@ class PercentageChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.grand_total = this.sliceTotals.reduce((a, b) => a + b, 0);
|
let s = this.state;
|
||||||
|
this.grand_total = s.sliceTotals.reduce((a, b) => a + b, 0);
|
||||||
this.slices = [];
|
this.slices = [];
|
||||||
this.sliceTotals.map((total, i) => {
|
s.sliceTotals.map((total, i) => {
|
||||||
let slice = $.create('div', {
|
let slice = $.create('div', {
|
||||||
className: `progress-bar`,
|
className: `progress-bar`,
|
||||||
inside: this.percentageBar,
|
inside: this.percentageBar,
|
||||||
@ -1435,42 +1523,8 @@ class PercentageChart extends BaseChart {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
calc() {
|
|
||||||
this.sliceTotals = [];
|
|
||||||
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.sliceTotals.push(d[0]);
|
|
||||||
this.labels.push(d[1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.legend_totals = this.sliceTotals.slice(0, this.max_legend_points);
|
|
||||||
}
|
|
||||||
|
|
||||||
bindTooltip() {
|
bindTooltip() {
|
||||||
|
|
||||||
// this.slices.map((slice, i) => {
|
// this.slices.map((slice, i) => {
|
||||||
// slice.addEventListener('mouseenter', () => {
|
// slice.addEventListener('mouseenter', () => {
|
||||||
// let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
|
// let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
|
||||||
@ -1479,114 +1533,57 @@ class PercentageChart extends BaseChart {
|
|||||||
// let y = p_off.top - g_off.top - 6;
|
// let y = p_off.top - g_off.top - 6;
|
||||||
// let title = (this.formatted_labels && this.formatted_labels.length>0
|
// let title = (this.formatted_labels && this.formatted_labels.length>0
|
||||||
// ? this.formatted_labels[i] : this.labels[i]) + ': ';
|
// ? this.formatted_labels[i] : this.labels[i]) + ': ';
|
||||||
// let percent = (this.sliceTotals[i]*100/this.grand_total).toFixed(1);
|
// let percent = (s.sliceTotals[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();
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLegend() {
|
|
||||||
// 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.statsWrapper
|
|
||||||
// });
|
|
||||||
// stats.innerHTML = `<span class="indicator">
|
|
||||||
// <i style="background: ${this.colors[i]}"></i>
|
|
||||||
// <span class="text-muted">${x_values[i]}:</span>
|
|
||||||
// ${d}
|
|
||||||
// </span>`;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ANGLE_RATIO = Math.PI / 180;
|
class PieChart extends AggregationChart {
|
||||||
const FULL_ANGLE = 360;
|
|
||||||
|
|
||||||
class PieChart extends BaseChart {
|
|
||||||
constructor(parent, args) {
|
constructor(parent, args) {
|
||||||
super(parent, args);
|
super(parent, args);
|
||||||
this.type = 'pie';
|
this.type = 'pie';
|
||||||
this.elements_to_animate = null;
|
|
||||||
this.hoverRadio = args.hoverRadio || 0.1;
|
this.hoverRadio = args.hoverRadio || 0.1;
|
||||||
this.max_slices = 10;
|
|
||||||
this.max_legend_points = 6;
|
|
||||||
this.isAnimate = false;
|
|
||||||
this.startAngle = args.startAngle || 0;
|
this.startAngle = args.startAngle || 0;
|
||||||
this.clockWise = args.clockWise || false;
|
this.clockWise = args.clockWise || false;
|
||||||
this.mouseMove = this.mouseMove.bind(this);
|
|
||||||
this.mouseLeave = this.mouseLeave.bind(this);
|
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
this.mouseMove = this.mouseMove.bind(this);
|
||||||
|
this.mouseLeave = this.mouseLeave.bind(this);
|
||||||
|
}
|
||||||
calc() {
|
calc() {
|
||||||
this.centerX = this.width / 2;
|
super.calc();
|
||||||
this.centerY = this.height / 2;
|
this.center = {
|
||||||
this.radius = (this.height > this.width ? this.centerX : this.centerY);
|
x: this.width / 2,
|
||||||
this.slice_totals = [];
|
y: this.height / 2
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static getPositionByAngle(angle,radius) {
|
|
||||||
return {
|
|
||||||
x:Math.sin(angle * ANGLE_RATIO) * radius,
|
|
||||||
y:Math.cos(angle * ANGLE_RATIO) * radius,
|
|
||||||
};
|
};
|
||||||
|
this.radius = (this.height > this.width ? this.center.x : this.center.y);
|
||||||
}
|
}
|
||||||
makeArcPath(startPosition,endPosition){
|
|
||||||
const{centerX,centerY,radius,clockWise} = this;
|
|
||||||
return `M${centerX} ${centerY} L${centerX+startPosition.x} ${centerY+startPosition.y} A ${radius} ${radius} 0 0 ${clockWise ? 1 : 0} ${centerX+endPosition.x} ${centerY+endPosition.y} z`;
|
|
||||||
}
|
|
||||||
render(init) {
|
render(init) {
|
||||||
const{radius,clockWise} = this;
|
const{radius,clockWise} = this;
|
||||||
this.grand_total = this.slice_totals.reduce((a, b) => a + b, 0);
|
this.grand_total = this.state.sliceTotals.reduce((a, b) => a + b, 0);
|
||||||
const prevSlicesProperties = this.slicesProperties || [];
|
const prevSlicesProperties = this.slicesProperties || [];
|
||||||
this.slices = [];
|
this.slices = [];
|
||||||
this.elements_to_animate = [];
|
this.elements_to_animate = [];
|
||||||
this.slicesProperties = [];
|
this.slicesProperties = [];
|
||||||
let curAngle = 180 - this.startAngle;
|
let curAngle = 180 - this.startAngle;
|
||||||
this.slice_totals.map((total, i) => {
|
|
||||||
|
this.state.sliceTotals.map((total, i) => {
|
||||||
const startAngle = curAngle;
|
const startAngle = curAngle;
|
||||||
const originDiffAngle = (total / this.grand_total) * FULL_ANGLE;
|
const originDiffAngle = (total / this.grand_total) * FULL_ANGLE;
|
||||||
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
|
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
|
||||||
const endAngle = curAngle = curAngle + diffAngle;
|
const endAngle = curAngle = curAngle + diffAngle;
|
||||||
const startPosition = PieChart.getPositionByAngle(startAngle,radius);
|
const startPosition = getPositionByAngle(startAngle, radius);
|
||||||
const endPosition = PieChart.getPositionByAngle(endAngle,radius);
|
const endPosition = getPositionByAngle(endAngle, radius);
|
||||||
const prevProperty = init && prevSlicesProperties[i];
|
const prevProperty = init && prevSlicesProperties[i];
|
||||||
let curStart,curEnd;
|
let curStart,curEnd;
|
||||||
if(init){
|
if(init){
|
||||||
@ -1596,7 +1593,7 @@ class PieChart extends BaseChart {
|
|||||||
curStart = startPosition;
|
curStart = startPosition;
|
||||||
curEnd = endPosition;
|
curEnd = endPosition;
|
||||||
}
|
}
|
||||||
const curPath = this.makeArcPath(curStart,curEnd);
|
const curPath = makeArcPathStr(curStart, curEnd, this.center, this.radius, this.clockWise);
|
||||||
let slice = makePath(curPath, 'pie-path', 'none', this.colors[i]);
|
let slice = makePath(curPath, 'pie-path', 'none', this.colors[i]);
|
||||||
slice.style.transition = 'transform .3s;';
|
slice.style.transition = 'transform .3s;';
|
||||||
this.drawArea.appendChild(slice);
|
this.drawArea.appendChild(slice);
|
||||||
@ -1609,42 +1606,44 @@ class PieChart extends BaseChart {
|
|||||||
total: this.grand_total,
|
total: this.grand_total,
|
||||||
startAngle,
|
startAngle,
|
||||||
endAngle,
|
endAngle,
|
||||||
angle:diffAngle
|
angle: diffAngle
|
||||||
});
|
});
|
||||||
if(init){
|
if(init){
|
||||||
this.elements_to_animate.push([{unit: slice, array: this.slices, index: this.slices.length - 1},
|
this.elements_to_animate.push([slice,
|
||||||
{d:this.makeArcPath(startPosition,endPosition)},
|
{d: makeArcPathStr(startPosition, endPosition, this.center, this.radius, this.clockWise)},
|
||||||
650, "easein",null,{
|
650, "easein",null,{
|
||||||
d:curPath
|
d:curPath
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
if(init){
|
// if(init){
|
||||||
runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
|
// runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
calTranslateByAngle(property){
|
calTranslateByAngle(property){
|
||||||
const{radius,hoverRadio} = this;
|
const{radius,hoverRadio} = this;
|
||||||
const position = PieChart.getPositionByAngle(property.startAngle+(property.angle / 2),radius);
|
const position = getPositionByAngle(property.startAngle+(property.angle / 2),radius);
|
||||||
return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`;
|
return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
hoverSlice(path,i,flag,e){
|
hoverSlice(path,i,flag,e){
|
||||||
if(!path) return;
|
if(!path) return;
|
||||||
const color = this.colors[i];
|
const color = this.colors[i];
|
||||||
if(flag){
|
if(flag) {
|
||||||
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
|
|
||||||
path.style.fill = lightenDarkenColor(color,50);
|
transform(path, this.calTranslateByAngle(this.slicesProperties[i]));
|
||||||
|
path.style.fill = lightenDarkenColor(color, 50);
|
||||||
let g_off = getOffset(this.svg);
|
let g_off = getOffset(this.svg);
|
||||||
let x = e.pageX - g_off.left + 10;
|
let x = e.pageX - g_off.left + 10;
|
||||||
let y = e.pageY - g_off.top - 10;
|
let y = e.pageY - g_off.top - 10;
|
||||||
let title = (this.formatted_labels && this.formatted_labels.length>0
|
let title = (this.formatted_labels && this.formatted_labels.length > 0
|
||||||
? this.formatted_labels[i] : this.labels[i]) + ': ';
|
? this.formatted_labels[i] : this.labels[i]) + ': ';
|
||||||
let percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);
|
let percent = (this.state.sliceTotals[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();
|
||||||
}else{
|
} else {
|
||||||
transform(path,'translate3d(0,0,0)');
|
transform(path,'translate3d(0,0,0)');
|
||||||
this.tip.hide_tip();
|
this.tip.hide_tip();
|
||||||
path.style.fill = color;
|
path.style.fill = color;
|
||||||
@ -1672,26 +1671,6 @@ class PieChart extends BaseChart {
|
|||||||
// this.drawArea.addEventListener('mousemove',this.mouseMove);
|
// this.drawArea.addEventListener('mousemove',this.mouseMove);
|
||||||
// this.drawArea.addEventListener('mouseleave',this.mouseLeave);
|
// this.drawArea.addEventListener('mouseleave',this.mouseLeave);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLegend() {
|
|
||||||
let x_values = this.formatted_labels && this.formatted_labels.length > 0
|
|
||||||
? this.formatted_labels : this.labels;
|
|
||||||
this.legend_totals.map((d, i) => {
|
|
||||||
const color = this.colors[i];
|
|
||||||
|
|
||||||
if(d) {
|
|
||||||
let stats = $.create('div', {
|
|
||||||
className: 'stats',
|
|
||||||
inside: this.statsWrapper
|
|
||||||
});
|
|
||||||
stats.innerHTML = `<span class="indicator">
|
|
||||||
<i style="background-color:${color};"></i>
|
|
||||||
<span class="text-muted">${x_values[i]}:</span>
|
|
||||||
${d}
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Playing around with dates
|
// Playing around with dates
|
||||||
@ -2711,7 +2690,8 @@ class AxisChart extends BaseChart {
|
|||||||
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
||||||
}
|
}
|
||||||
|
|
||||||
calcXPositions(s=this.state) {
|
calcXPositions() {
|
||||||
|
let s = this.state;
|
||||||
let labels = this.data.labels;
|
let labels = this.data.labels;
|
||||||
s.datasetLength = labels.length;
|
s.datasetLength = labels.length;
|
||||||
|
|
||||||
|
|||||||
2
dist/frappe-charts.min.cjs.js
vendored
2
dist/frappe-charts.min.cjs.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.esm.js
vendored
2
dist/frappe-charts.min.esm.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.iife.js
vendored
2
dist/frappe-charts.min.iife.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.iife.js.map
vendored
2
dist/frappe-charts.min.iife.js.map
vendored
File diff suppressed because one or more lines are too long
2
docs/assets/js/frappe-charts.min.js
vendored
2
docs/assets/js/frappe-charts.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -176,6 +176,8 @@ let type_chart = new Chart("#chart-types", {
|
|||||||
xAxisMode: 'tick',
|
xAxisMode: 'tick',
|
||||||
yAxisMode: 'span',
|
yAxisMode: 'span',
|
||||||
valuesOverPoints: 1,
|
valuesOverPoints: 1,
|
||||||
|
// maxLegendPoints: 6,
|
||||||
|
// maxSlices: 3,
|
||||||
isNavigable: 1,
|
isNavigable: 1,
|
||||||
barOptions: {
|
barOptions: {
|
||||||
stacked: 1
|
stacked: 1
|
||||||
|
|||||||
75
src/js/charts/AggregationChart.js
Normal file
75
src/js/charts/AggregationChart.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import BaseChart from './BaseChart';
|
||||||
|
import { $, getOffset } from '../utils/dom';
|
||||||
|
|
||||||
|
export default class AggregationChart extends BaseChart {
|
||||||
|
constructor(parent, args) {
|
||||||
|
super(parent, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
|
||||||
|
this.config.maxSlices = args.maxSlices || 20;
|
||||||
|
this.config.maxLegendPoints = args.maxLegendPoints || 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
calc() {
|
||||||
|
let s = this.state;
|
||||||
|
let maxSlices = this.config.maxSlices;
|
||||||
|
s.sliceTotals = [];
|
||||||
|
|
||||||
|
let allTotals = this.data.labels.map((label, i) => {
|
||||||
|
let total = 0;
|
||||||
|
this.data.datasets.map(e => {
|
||||||
|
total += e.values[i];
|
||||||
|
});
|
||||||
|
return [total, label];
|
||||||
|
}).filter(d => { return d[0] > 0; }); // keep only positive results
|
||||||
|
|
||||||
|
let totals = allTotals;
|
||||||
|
if(allTotals.length > maxSlices) {
|
||||||
|
// Prune and keep a grey area for rest as per maxSlices
|
||||||
|
allTotals.sort((a, b) => { return b[0] - a[0]; });
|
||||||
|
|
||||||
|
totals = allTotals.slice(0, maxSlices-1);
|
||||||
|
let remaining = allTotals.slice(maxSlices-1);
|
||||||
|
|
||||||
|
let sumOfRemaining = 0;
|
||||||
|
remaining.map(d => {sumOfRemaining += d[0];});
|
||||||
|
totals.push([sumOfRemaining, 'Rest']);
|
||||||
|
this.colors[maxSlices-1] = 'grey';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.labels = [];
|
||||||
|
totals.map(d => {
|
||||||
|
s.sliceTotals.push(d[0]);
|
||||||
|
this.labels.push(d[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() { }
|
||||||
|
|
||||||
|
bindTooltip() { }
|
||||||
|
|
||||||
|
renderLegend() {
|
||||||
|
let s = this.state;
|
||||||
|
|
||||||
|
this.legendTotals = s.sliceTotals.slice(0, this.config.maxLegendPoints);
|
||||||
|
|
||||||
|
let x_values = this.formatted_labels && this.formatted_labels.length > 0
|
||||||
|
? this.formatted_labels : this.labels;
|
||||||
|
this.legendTotals.map((d, i) => {
|
||||||
|
if(d) {
|
||||||
|
let stats = $.create('div', {
|
||||||
|
className: 'stats',
|
||||||
|
inside: this.statsWrapper
|
||||||
|
});
|
||||||
|
stats.innerHTML = `<span class="indicator">
|
||||||
|
<i style="background: ${this.colors[i]}"></i>
|
||||||
|
<span class="text-muted">${x_values[i]}:</span>
|
||||||
|
${d}
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -56,7 +56,8 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
||||||
}
|
}
|
||||||
|
|
||||||
calcXPositions(s=this.state) {
|
calcXPositions() {
|
||||||
|
let s = this.state;
|
||||||
let labels = this.data.labels;
|
let labels = this.data.labels;
|
||||||
s.datasetLength = labels.length;
|
s.datasetLength = labels.length;
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,11 @@
|
|||||||
import BaseChart from './BaseChart';
|
import AggregationChart from './AggregationChart';
|
||||||
import { $, getOffset } from '../utils/dom';
|
import { $, getOffset } from '../utils/dom';
|
||||||
|
|
||||||
export default class PercentageChart extends BaseChart {
|
export default class PercentageChart extends AggregationChart {
|
||||||
constructor(parent, args) {
|
constructor(parent, args) {
|
||||||
super(parent, args);
|
super(parent, args);
|
||||||
this.type = 'percentage';
|
this.type = 'percentage';
|
||||||
|
|
||||||
this.max_slices = 10;
|
|
||||||
this.max_legend_points = 6;
|
|
||||||
|
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,9 +34,10 @@ export default class PercentageChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.grand_total = this.sliceTotals.reduce((a, b) => a + b, 0);
|
let s = this.state;
|
||||||
|
this.grand_total = s.sliceTotals.reduce((a, b) => a + b, 0);
|
||||||
this.slices = [];
|
this.slices = [];
|
||||||
this.sliceTotals.map((total, i) => {
|
s.sliceTotals.map((total, i) => {
|
||||||
let slice = $.create('div', {
|
let slice = $.create('div', {
|
||||||
className: `progress-bar`,
|
className: `progress-bar`,
|
||||||
inside: this.percentageBar,
|
inside: this.percentageBar,
|
||||||
@ -52,42 +50,8 @@ export default class PercentageChart extends BaseChart {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
calc() {
|
|
||||||
this.sliceTotals = [];
|
|
||||||
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.sliceTotals.push(d[0]);
|
|
||||||
this.labels.push(d[1]);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.legend_totals = this.sliceTotals.slice(0, this.max_legend_points);
|
|
||||||
}
|
|
||||||
|
|
||||||
bindTooltip() {
|
bindTooltip() {
|
||||||
|
let s = this.state;
|
||||||
// this.slices.map((slice, i) => {
|
// this.slices.map((slice, i) => {
|
||||||
// slice.addEventListener('mouseenter', () => {
|
// slice.addEventListener('mouseenter', () => {
|
||||||
// let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
|
// let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
|
||||||
@ -96,29 +60,11 @@ export default class PercentageChart extends BaseChart {
|
|||||||
// let y = p_off.top - g_off.top - 6;
|
// let y = p_off.top - g_off.top - 6;
|
||||||
// let title = (this.formatted_labels && this.formatted_labels.length>0
|
// let title = (this.formatted_labels && this.formatted_labels.length>0
|
||||||
// ? this.formatted_labels[i] : this.labels[i]) + ': ';
|
// ? this.formatted_labels[i] : this.labels[i]) + ': ';
|
||||||
// let percent = (this.sliceTotals[i]*100/this.grand_total).toFixed(1);
|
// let percent = (s.sliceTotals[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();
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLegend() {
|
|
||||||
// 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.statsWrapper
|
|
||||||
// });
|
|
||||||
// stats.innerHTML = `<span class="indicator">
|
|
||||||
// <i style="background: ${this.colors[i]}"></i>
|
|
||||||
// <span class="text-muted">${x_values[i]}:</span>
|
|
||||||
// ${d}
|
|
||||||
// </span>`;
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,89 +1,53 @@
|
|||||||
import BaseChart from './BaseChart';
|
import AggregationChart from './AggregationChart';
|
||||||
import { $, getOffset } from '../utils/dom';
|
import { $, getOffset } from '../utils/dom';
|
||||||
import { makePath } from '../utils/draw';
|
import { getPositionByAngle } from '../utils/helpers';
|
||||||
|
import { makePath, makeArcPathStr } from '../utils/draw';
|
||||||
import { lightenDarkenColor } from '../utils/colors';
|
import { lightenDarkenColor } from '../utils/colors';
|
||||||
import { runSMILAnimation, transform } from '../utils/animation';
|
import { transform } from '../utils/animation';
|
||||||
const ANGLE_RATIO = Math.PI / 180;
|
import { FULL_ANGLE } from '../utils/constants';
|
||||||
const FULL_ANGLE = 360;
|
|
||||||
|
|
||||||
export default class PieChart extends BaseChart {
|
export default class PieChart extends AggregationChart {
|
||||||
constructor(parent, args) {
|
constructor(parent, args) {
|
||||||
super(parent, args);
|
super(parent, args);
|
||||||
this.type = 'pie';
|
this.type = 'pie';
|
||||||
this.elements_to_animate = null;
|
|
||||||
this.hoverRadio = args.hoverRadio || 0.1;
|
this.hoverRadio = args.hoverRadio || 0.1;
|
||||||
this.max_slices = 10;
|
|
||||||
this.max_legend_points = 6;
|
|
||||||
this.isAnimate = false;
|
|
||||||
this.startAngle = args.startAngle || 0;
|
this.startAngle = args.startAngle || 0;
|
||||||
this.clockWise = args.clockWise || false;
|
this.clockWise = args.clockWise || false;
|
||||||
this.mouseMove = this.mouseMove.bind(this);
|
|
||||||
this.mouseLeave = this.mouseLeave.bind(this);
|
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
this.mouseMove = this.mouseMove.bind(this);
|
||||||
|
this.mouseLeave = this.mouseLeave.bind(this);
|
||||||
|
}
|
||||||
calc() {
|
calc() {
|
||||||
this.centerX = this.width / 2;
|
super.calc();
|
||||||
this.centerY = this.height / 2;
|
this.center = {
|
||||||
this.radius = (this.height > this.width ? this.centerX : this.centerY);
|
x: this.width / 2,
|
||||||
this.slice_totals = [];
|
y: this.height / 2
|
||||||
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.radius = (this.height > this.width ? this.center.x : this.center.y);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getPositionByAngle(angle,radius) {
|
|
||||||
return {
|
|
||||||
x:Math.sin(angle * ANGLE_RATIO) * radius,
|
|
||||||
y:Math.cos(angle * ANGLE_RATIO) * radius,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
makeArcPath(startPosition,endPosition){
|
|
||||||
const{centerX,centerY,radius,clockWise} = this;
|
|
||||||
return `M${centerX} ${centerY} L${centerX+startPosition.x} ${centerY+startPosition.y} A ${radius} ${radius} 0 0 ${clockWise ? 1 : 0} ${centerX+endPosition.x} ${centerY+endPosition.y} z`;
|
|
||||||
}
|
|
||||||
render(init) {
|
render(init) {
|
||||||
const{radius,clockWise} = this;
|
const{radius,clockWise} = this;
|
||||||
this.grand_total = this.slice_totals.reduce((a, b) => a + b, 0);
|
this.grand_total = this.state.sliceTotals.reduce((a, b) => a + b, 0);
|
||||||
const prevSlicesProperties = this.slicesProperties || [];
|
const prevSlicesProperties = this.slicesProperties || [];
|
||||||
this.slices = [];
|
this.slices = [];
|
||||||
this.elements_to_animate = [];
|
this.elements_to_animate = [];
|
||||||
this.slicesProperties = [];
|
this.slicesProperties = [];
|
||||||
let curAngle = 180 - this.startAngle;
|
let curAngle = 180 - this.startAngle;
|
||||||
this.slice_totals.map((total, i) => {
|
|
||||||
|
this.state.sliceTotals.map((total, i) => {
|
||||||
const startAngle = curAngle;
|
const startAngle = curAngle;
|
||||||
const originDiffAngle = (total / this.grand_total) * FULL_ANGLE;
|
const originDiffAngle = (total / this.grand_total) * FULL_ANGLE;
|
||||||
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
|
const diffAngle = clockWise ? -originDiffAngle : originDiffAngle;
|
||||||
const endAngle = curAngle = curAngle + diffAngle;
|
const endAngle = curAngle = curAngle + diffAngle;
|
||||||
const startPosition = PieChart.getPositionByAngle(startAngle,radius);
|
const startPosition = getPositionByAngle(startAngle, radius);
|
||||||
const endPosition = PieChart.getPositionByAngle(endAngle,radius);
|
const endPosition = getPositionByAngle(endAngle, radius);
|
||||||
const prevProperty = init && prevSlicesProperties[i];
|
const prevProperty = init && prevSlicesProperties[i];
|
||||||
let curStart,curEnd;
|
let curStart,curEnd;
|
||||||
if(init){
|
if(init){
|
||||||
@ -93,7 +57,7 @@ export default class PieChart extends BaseChart {
|
|||||||
curStart = startPosition;
|
curStart = startPosition;
|
||||||
curEnd = endPosition;
|
curEnd = endPosition;
|
||||||
}
|
}
|
||||||
const curPath = this.makeArcPath(curStart,curEnd);
|
const curPath = makeArcPathStr(curStart, curEnd, this.center, this.radius, this.clockWise);
|
||||||
let slice = makePath(curPath, 'pie-path', 'none', this.colors[i]);
|
let slice = makePath(curPath, 'pie-path', 'none', this.colors[i]);
|
||||||
slice.style.transition = 'transform .3s;';
|
slice.style.transition = 'transform .3s;';
|
||||||
this.drawArea.appendChild(slice);
|
this.drawArea.appendChild(slice);
|
||||||
@ -106,42 +70,44 @@ export default class PieChart extends BaseChart {
|
|||||||
total: this.grand_total,
|
total: this.grand_total,
|
||||||
startAngle,
|
startAngle,
|
||||||
endAngle,
|
endAngle,
|
||||||
angle:diffAngle
|
angle: diffAngle
|
||||||
});
|
});
|
||||||
if(init){
|
if(init){
|
||||||
this.elements_to_animate.push([{unit: slice, array: this.slices, index: this.slices.length - 1},
|
this.elements_to_animate.push([slice,
|
||||||
{d:this.makeArcPath(startPosition,endPosition)},
|
{d: makeArcPathStr(startPosition, endPosition, this.center, this.radius, this.clockWise)},
|
||||||
650, "easein",null,{
|
650, "easein",null,{
|
||||||
d:curPath
|
d:curPath
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
if(init){
|
// if(init){
|
||||||
runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
|
// runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
calTranslateByAngle(property){
|
calTranslateByAngle(property){
|
||||||
const{radius,hoverRadio} = this;
|
const{radius,hoverRadio} = this;
|
||||||
const position = PieChart.getPositionByAngle(property.startAngle+(property.angle / 2),radius);
|
const position = getPositionByAngle(property.startAngle+(property.angle / 2),radius);
|
||||||
return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`;
|
return `translate3d(${(position.x) * hoverRadio}px,${(position.y) * hoverRadio}px,0)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
hoverSlice(path,i,flag,e){
|
hoverSlice(path,i,flag,e){
|
||||||
if(!path) return;
|
if(!path) return;
|
||||||
const color = this.colors[i];
|
const color = this.colors[i];
|
||||||
if(flag){
|
if(flag) {
|
||||||
transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
|
|
||||||
path.style.fill = lightenDarkenColor(color,50);
|
transform(path, this.calTranslateByAngle(this.slicesProperties[i]));
|
||||||
|
path.style.fill = lightenDarkenColor(color, 50);
|
||||||
let g_off = getOffset(this.svg);
|
let g_off = getOffset(this.svg);
|
||||||
let x = e.pageX - g_off.left + 10;
|
let x = e.pageX - g_off.left + 10;
|
||||||
let y = e.pageY - g_off.top - 10;
|
let y = e.pageY - g_off.top - 10;
|
||||||
let title = (this.formatted_labels && this.formatted_labels.length>0
|
let title = (this.formatted_labels && this.formatted_labels.length > 0
|
||||||
? this.formatted_labels[i] : this.labels[i]) + ': ';
|
? this.formatted_labels[i] : this.labels[i]) + ': ';
|
||||||
let percent = (this.slice_totals[i]*100/this.grand_total).toFixed(1);
|
let percent = (this.state.sliceTotals[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();
|
||||||
}else{
|
} else {
|
||||||
transform(path,'translate3d(0,0,0)');
|
transform(path,'translate3d(0,0,0)');
|
||||||
this.tip.hide_tip();
|
this.tip.hide_tip();
|
||||||
path.style.fill = color;
|
path.style.fill = color;
|
||||||
@ -169,24 +135,4 @@ export default class PieChart extends BaseChart {
|
|||||||
// this.drawArea.addEventListener('mousemove',this.mouseMove);
|
// this.drawArea.addEventListener('mousemove',this.mouseMove);
|
||||||
// this.drawArea.addEventListener('mouseleave',this.mouseLeave);
|
// this.drawArea.addEventListener('mouseleave',this.mouseLeave);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLegend() {
|
|
||||||
let x_values = this.formatted_labels && this.formatted_labels.length > 0
|
|
||||||
? this.formatted_labels : this.labels;
|
|
||||||
this.legend_totals.map((d, i) => {
|
|
||||||
const color = this.colors[i];
|
|
||||||
|
|
||||||
if(d) {
|
|
||||||
let stats = $.create('div', {
|
|
||||||
className: 'stats',
|
|
||||||
inside: this.statsWrapper
|
|
||||||
});
|
|
||||||
stats.innerHTML = `<span class="indicator">
|
|
||||||
<i style="background-color:${color};"></i>
|
|
||||||
<span class="text-muted">${x_values[i]}:</span>
|
|
||||||
${d}
|
|
||||||
</span>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,11 +38,9 @@ export function getDifferentChart(type, current_type, parent, args) {
|
|||||||
// Okay, this is anticlimactic
|
// Okay, this is anticlimactic
|
||||||
// this function will need to actually be 'changeChartType(type)'
|
// this function will need to actually be 'changeChartType(type)'
|
||||||
// that will update only the required elements, but for now ...
|
// that will update only the required elements, but for now ...
|
||||||
return new Chart(parent, {
|
|
||||||
title: args.title,
|
args.type = type;
|
||||||
data: args.data,
|
args.colors = useColor ? args.colors : undefined;
|
||||||
type: type,
|
|
||||||
height: args.height,
|
return new Chart(parent, args);
|
||||||
colors: useColor ? args.colors : undefined
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
@ -16,7 +16,7 @@ const PRESET_COLOR_MAP = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
|
export const DEFAULT_COLORS = ['light-blue', 'blue', 'violet', 'red', 'orange',
|
||||||
'yellow', 'green', 'light-green', 'purple', 'magenta'];
|
'yellow', 'green', 'light-green', 'purple', 'magenta', 'light-grey', 'dark-grey'];
|
||||||
|
|
||||||
function limitColor(r){
|
function limitColor(r){
|
||||||
if (r > 255) return 255;
|
if (r > 255) return 255;
|
||||||
|
|||||||
@ -17,3 +17,7 @@ export const LINE_CHART_DOT_SIZE = 4;
|
|||||||
export const DOT_OVERLAY_SIZE_INCR = 4;
|
export const DOT_OVERLAY_SIZE_INCR = 4;
|
||||||
|
|
||||||
export const DEFAULT_CHAR_WIDTH = 8;
|
export const DEFAULT_CHAR_WIDTH = 8;
|
||||||
|
|
||||||
|
// Universal constants
|
||||||
|
export const ANGLE_RATIO = Math.PI / 180;
|
||||||
|
export const FULL_ANGLE = 360;
|
||||||
@ -108,6 +108,16 @@ export function makePath(pathStr, className='', stroke='none', fill='none') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function makeArcPathStr(startPosition, endPosition, center, radius, clockWise=1){
|
||||||
|
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
||||||
|
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
||||||
|
|
||||||
|
return `M${center.x} ${center.y}
|
||||||
|
L${arcStartX} ${arcStartY}
|
||||||
|
A ${radius} ${radius} 0 0 ${clockWise ? 1 : 0}
|
||||||
|
${arcEndX} ${arcEndY} z`;
|
||||||
|
}
|
||||||
|
|
||||||
export function makeGradient(svgDefElem, color, lighter = false) {
|
export function makeGradient(svgDefElem, color, lighter = false) {
|
||||||
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
||||||
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { ANGLE_RATIO } from './constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of a number upto 2 decimal places.
|
* Returns the value of a number upto 2 decimal places.
|
||||||
* @param {Number} d Any number
|
* @param {Number} d Any number
|
||||||
@ -74,3 +76,10 @@ export function bindChange(obj, getFn, setFn) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPositionByAngle(angle, radius) {
|
||||||
|
return {
|
||||||
|
x:Math.sin(angle * ANGLE_RATIO) * radius,
|
||||||
|
y:Math.cos(angle * ANGLE_RATIO) * radius,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user