render multiple y axis
This commit is contained in:
parent
405f8fb18b
commit
b3ca8f0819
560
dist/frappe-charts.esm.js
vendored
560
dist/frappe-charts.esm.js
vendored
@ -281,6 +281,7 @@ function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
|
|||||||
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;
|
||||||
|
const BASE_LINE_COLOR = '#dadada';
|
||||||
|
|
||||||
function $$1(expr, con) {
|
function $$1(expr, con) {
|
||||||
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
|
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
|
||||||
@ -416,20 +417,22 @@ function makeText(className, x, y, content) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeVertXLine(x, label, totalHeight, mode, stroke='#dadada') {
|
function makeVertLine(x, label, y1, y2, options={}) {
|
||||||
let height = mode === 'span' ? -1 * AXIS_TICK_LENGTH : totalHeight;
|
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
|
|
||||||
let l = createSVG('line', {
|
let l = createSVG('line', {
|
||||||
|
className: 'line-vertical ' + options.className,
|
||||||
x1: 0,
|
x1: 0,
|
||||||
x2: 0,
|
x2: 0,
|
||||||
y1: totalHeight + AXIS_TICK_LENGTH,
|
y1: y1,
|
||||||
y2: height,
|
y2: y2,
|
||||||
stroke: stroke
|
styles: {
|
||||||
|
stroke: options.stroke
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let text = createSVG('text', {
|
let text = createSVG('text', {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: totalHeight + AXIS_TICK_LENGTH + LABEL_MARGIN,
|
y: y1 > y2 ? y1 + LABEL_MARGIN : y1 - LABEL_MARGIN - FONT_SIZE,
|
||||||
dy: FONT_SIZE + 'px',
|
dy: FONT_SIZE + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': 'middle',
|
'text-anchor': 'middle',
|
||||||
@ -446,50 +449,34 @@ function makeVertXLine(x, label, totalHeight, mode, stroke='#dadada') {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeHoriYLine(y, label, totalWidth, mode, pos='left') {
|
function makeHoriLine(y, label, x1, x2, options={}) {
|
||||||
let lineType = '';
|
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
let w2 = mode === 'span' ? totalWidth + AXIS_TICK_LENGTH : 0;
|
if(!options.lineType) options.lineType = '';
|
||||||
|
let className = 'line-horizontal ' + options.className +
|
||||||
// temp : works correctly
|
(options.lineType === "dashed" ? "dashed": "");
|
||||||
let x1, x2, textX, anchor;
|
|
||||||
if(mode === 'tick') {
|
|
||||||
if(pos === 'right') {
|
|
||||||
x1 = totalWidth;
|
|
||||||
x2 = totalWidth + AXIS_TICK_LENGTH;
|
|
||||||
textX = totalWidth + AXIS_TICK_LENGTH + LABEL_MARGIN;
|
|
||||||
anchor = 'start';
|
|
||||||
} else {
|
|
||||||
x1 = -1 * AXIS_TICK_LENGTH;
|
|
||||||
x2 = w2;
|
|
||||||
textX = -1 * (LABEL_MARGIN + AXIS_TICK_LENGTH);
|
|
||||||
anchor = 'end';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
x1 = -1 * AXIS_TICK_LENGTH;
|
|
||||||
x2 = w2;
|
|
||||||
textX = -1 * (LABEL_MARGIN + AXIS_TICK_LENGTH);
|
|
||||||
anchor = 'end';
|
|
||||||
}
|
|
||||||
|
|
||||||
let l = createSVG('line', {
|
let l = createSVG('line', {
|
||||||
className: lineType === "dashed" ? "dashed": "",
|
className: className,
|
||||||
x1: x1,
|
x1: x1,
|
||||||
x2: x2,
|
x2: x2,
|
||||||
y1: 0,
|
y1: 0,
|
||||||
y2: 0
|
y2: 0,
|
||||||
|
styles: {
|
||||||
|
stroke: options.stroke
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let text = createSVG('text', {
|
let text = createSVG('text', {
|
||||||
x: textX,
|
x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN,
|
||||||
y: 0,
|
y: 0,
|
||||||
dy: (FONT_SIZE / 2 - 2) + 'px',
|
dy: (FONT_SIZE / 2 - 2) + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': anchor,
|
'text-anchor': x1 < x2 ? 'end' : 'start',
|
||||||
innerHTML: label+""
|
innerHTML: label+""
|
||||||
});
|
});
|
||||||
|
|
||||||
let line = createSVG('g', {
|
let line = createSVG('g', {
|
||||||
transform: `translate(0, ${y})`,
|
transform: `translate(0, ${ y })`,
|
||||||
'stroke-opacity': 1
|
'stroke-opacity': 1
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -517,6 +504,10 @@ class AxisChartRenderer {
|
|||||||
this.yAxisMode = state.yAxisMode;
|
this.yAxisMode = state.yAxisMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setZeroline(zeroLine) {
|
||||||
|
this.zeroLine = zeroLine;
|
||||||
|
}
|
||||||
|
|
||||||
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets, prevX, prevY) {
|
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets, prevX, prevY) {
|
||||||
|
|
||||||
let totalWidth = this.unitWidth - args.spaceWidth;
|
let totalWidth = this.unitWidth - args.spaceWidth;
|
||||||
@ -553,14 +544,61 @@ class AxisChartRenderer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// temp: stroke
|
xLine(x, label, options={}) {
|
||||||
xLine(x, label, pos='bottom', stroke='', mode=this.xAxisMode) {
|
if(!options.pos) options.pos = 'bottom';
|
||||||
|
if(!options.offset) options.offset = 0;
|
||||||
|
if(!options.mode) options.mode = this.xAxisMode;
|
||||||
|
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
|
if(!options.className) options.className = '';
|
||||||
|
|
||||||
// Draw X axis line in span/tick mode with optional label
|
// Draw X axis line in span/tick mode with optional label
|
||||||
return makeVertXLine(x, label, this.totalHeight, mode);
|
// y2(span)
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// x line |
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// ---------------------+-- y2(tick)
|
||||||
|
// |
|
||||||
|
// y1
|
||||||
|
|
||||||
|
let y1 = this.totalHeight + AXIS_TICK_LENGTH;
|
||||||
|
let y2 = options.mode === 'span' ? -1 * AXIS_TICK_LENGTH : this.totalHeight;
|
||||||
|
|
||||||
|
if(options.mode === 'tick' && options.pos === 'top') {
|
||||||
|
// top axis ticks
|
||||||
|
y1 = -1 * AXIS_TICK_LENGTH;
|
||||||
|
y2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeVertLine(x, label, y1, y2, {
|
||||||
|
stroke: options.stroke,
|
||||||
|
className: options.className
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
yLine(y, label, pos='left', mode=this.yAxisMode) {
|
yLine(y, label, options={}) {
|
||||||
return makeHoriYLine(y, label, this.totalWidth, mode, pos);
|
if(!options.pos) options.pos = 'left';
|
||||||
|
if(!options.offset) options.offset = 0;
|
||||||
|
if(!options.mode) options.mode = this.yAxisMode;
|
||||||
|
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
|
if(!options.className) options.className = '';
|
||||||
|
|
||||||
|
let x1 = -1 * AXIS_TICK_LENGTH;
|
||||||
|
let x2 = options.mode === 'span' ? this.totalWidth + AXIS_TICK_LENGTH : 0;
|
||||||
|
|
||||||
|
if(options.mode === 'tick' && options.pos === 'right') {
|
||||||
|
x1 = this.totalWidth + AXIS_TICK_LENGTH;
|
||||||
|
x2 = this.totalWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
x1 += options.offset;
|
||||||
|
x2 += options.offset;
|
||||||
|
|
||||||
|
return makeHoriLine(y, label, x1, x2, {
|
||||||
|
stroke: options.stroke,
|
||||||
|
className: options.className
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
xMarker() {}
|
xMarker() {}
|
||||||
@ -688,6 +726,7 @@ class BaseChart {
|
|||||||
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
|
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.subtitle = subtitle;
|
this.subtitle = subtitle;
|
||||||
|
this.argHeight = height;
|
||||||
|
|
||||||
this.isNavigable = isNavigable;
|
this.isNavigable = isNavigable;
|
||||||
if(this.isNavigable) {
|
if(this.isNavigable) {
|
||||||
@ -702,7 +741,6 @@ class BaseChart {
|
|||||||
// showLegend, which then all functions will check
|
// showLegend, which then all functions will check
|
||||||
|
|
||||||
this.setColors();
|
this.setColors();
|
||||||
this.setMargins(args);
|
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
this.config = {
|
this.config = {
|
||||||
@ -735,12 +773,19 @@ class BaseChart {
|
|||||||
this.colors = this.colors.map(color => getColor(color));
|
this.colors = this.colors.map(color => getColor(color));
|
||||||
}
|
}
|
||||||
|
|
||||||
setMargins(args) {
|
setMargins() {
|
||||||
let height = args.height;
|
// TODO: think for all
|
||||||
|
let height = this.argHeight;
|
||||||
this.baseHeight = height;
|
this.baseHeight = height;
|
||||||
this.height = height - 40;
|
this.height = height - 40; // change
|
||||||
this.translateX = 60;
|
this.translateY = 20;
|
||||||
this.translateY = 10;
|
|
||||||
|
this.setHorizontalMargin();
|
||||||
|
}
|
||||||
|
|
||||||
|
setHorizontalMargin() {
|
||||||
|
this.translateXLeft = 60;
|
||||||
|
this.translateXRight = 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(){
|
validate(){
|
||||||
@ -780,7 +825,10 @@ class BaseChart {
|
|||||||
_setup() {
|
_setup() {
|
||||||
this.bindWindowEvents();
|
this.bindWindowEvents();
|
||||||
this.setupConstants();
|
this.setupConstants();
|
||||||
|
this.prepareData();
|
||||||
this.setupComponents();
|
this.setupComponents();
|
||||||
|
|
||||||
|
this.setMargins();
|
||||||
this.makeContainer();
|
this.makeContainer();
|
||||||
this.makeTooltip(); // without binding
|
this.makeTooltip(); // without binding
|
||||||
this.draw(true);
|
this.draw(true);
|
||||||
@ -864,15 +912,16 @@ class BaseChart {
|
|||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
|
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
|
||||||
this.width = this.baseWidth - this.translateX * 2;
|
this.width = this.baseWidth - (this.translateXLeft + this.translateXRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() { //?? refresh?
|
refresh() { //?? refresh?
|
||||||
this.oldState = this.state ? Object.assign({}, this.state) : {};
|
this.oldState = this.state ? Object.assign({}, this.state) : {};
|
||||||
|
this.intermedState = {};
|
||||||
|
|
||||||
this.prepareData();
|
this.prepareData();
|
||||||
this.reCalc();
|
this.reCalc();
|
||||||
this.refreshRenderer();
|
this.refreshRenderer();
|
||||||
this.refreshComponents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeChartArea() {
|
makeChartArea() {
|
||||||
@ -887,7 +936,7 @@ class BaseChart {
|
|||||||
this.drawArea = makeSVGGroup(
|
this.drawArea = makeSVGGroup(
|
||||||
this.svg,
|
this.svg,
|
||||||
this.type + '-chart',
|
this.type + '-chart',
|
||||||
`translate(${this.translateX}, ${this.translateY})`
|
`translate(${this.translateXLeft}, ${this.translateY})`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,7 +954,6 @@ class BaseChart {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.intermedState = this.calcIntermedState();
|
this.intermedState = this.calcIntermedState();
|
||||||
this.refreshComponents();
|
|
||||||
this.animateComponents();
|
this.animateComponents();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.renderComponents();
|
this.renderComponents();
|
||||||
@ -914,22 +962,15 @@ class BaseChart {
|
|||||||
// (opt, should not redraw if still in animate?)
|
// (opt, should not redraw if still in animate?)
|
||||||
}
|
}
|
||||||
|
|
||||||
calcIntermedState() {}
|
calcIntermedState() {
|
||||||
|
this.intermedState = {};
|
||||||
|
}
|
||||||
|
|
||||||
// convenient component array abstractions
|
// convenient component array abstractions
|
||||||
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
|
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
|
||||||
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
|
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
|
||||||
renderComponents() { this.components.forEach(c => c.render()); }
|
renderComponents() { this.components.forEach(c => c.render()); }
|
||||||
animateComponents() { this.components.forEach(c => c.animate()); }
|
animateComponents() { this.components.forEach(c => c.animate()); }
|
||||||
refreshComponents() {
|
|
||||||
let args = {
|
|
||||||
chartState: this.state,
|
|
||||||
oldChartState: this.oldState,
|
|
||||||
intermedState: this.intermedState,
|
|
||||||
chartRenderer: this.renderer
|
|
||||||
};
|
|
||||||
this.components.forEach(c => c.refresh(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLegend() {}
|
renderLegend() {}
|
||||||
|
|
||||||
@ -979,36 +1020,28 @@ class BaseChart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Y_AXIS_MARGIN = 60;
|
||||||
|
|
||||||
class ChartComponent {
|
class ChartComponent {
|
||||||
constructor({
|
constructor({
|
||||||
layerClass = '',
|
layerClass = '',
|
||||||
layerTransform = '',
|
layerTransform = '',
|
||||||
make,
|
make,
|
||||||
argsKeys,
|
|
||||||
animate
|
animate
|
||||||
}) {
|
}) {
|
||||||
this.layerClass = layerClass; // 'y axis'
|
this.layerClass = layerClass; // 'y axis'
|
||||||
this.layerTransform = layerTransform;
|
this.layerTransform = layerTransform;
|
||||||
this.make = make;
|
this.make = make;
|
||||||
this.argsKeys = argsKeys;//['yAxisPositions', 'yAxisLabels'];
|
|
||||||
this.animate = animate;
|
this.animate = animate;
|
||||||
|
|
||||||
this.layer = undefined;
|
this.layer = undefined;
|
||||||
this.store = []; //[[]] depends on indexed
|
this.store = []; //[[]] depends on indexed
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(args) {
|
refresh(args) {}
|
||||||
this.chartState = args.chartState;
|
|
||||||
this.oldChartState = args.oldChartState;
|
|
||||||
this.intermedState = args.intermedState;
|
|
||||||
|
|
||||||
this.chartRenderer = args.chartRenderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let args = this.argsKeys.map(key => this.chartState[key]);
|
this.store = this.make();
|
||||||
args.unshift(this.chartRenderer);
|
|
||||||
this.store = this.make(...args);
|
|
||||||
|
|
||||||
this.layer.textContent = '';
|
this.layer.textContent = '';
|
||||||
this.store.forEach(element => {
|
this.store.forEach(element => {
|
||||||
@ -1026,53 +1059,6 @@ class ChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Indexed according to dataset
|
// Indexed according to dataset
|
||||||
class IndexedChartComponent extends ChartComponent {
|
|
||||||
constructor(args) {
|
|
||||||
super(args);
|
|
||||||
this.stores = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh(args) {
|
|
||||||
super.refresh(args);
|
|
||||||
this.totalIndices = this.chartState[this.argsKeys[0]].length;
|
|
||||||
}
|
|
||||||
|
|
||||||
makeLayer() {
|
|
||||||
super.makeLayer();
|
|
||||||
this.layers = [];
|
|
||||||
for(var i = 0; i < this.totalIndices; i++) {
|
|
||||||
this.layers[i] = makeSVGGroup(this.layer, this.layerClass + '-' + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addLayer() {}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let datasetArrays = this.argsKeys.map(key => this.chartState[key]);
|
|
||||||
|
|
||||||
// datasetArrays will have something like an array of X positions sets
|
|
||||||
// datasetArrays = [
|
|
||||||
// xUnitPositions, yUnitPositions, colors, unitTypes, yUnitValues
|
|
||||||
// ]
|
|
||||||
// where xUnitPositions = [[0,0,0], [1,1,1]]
|
|
||||||
// i.e.: [ [[0,0,0], [1,1,1]], ... ]
|
|
||||||
for(var i = 0; i < this.totalIndices; i++) {
|
|
||||||
let args = datasetArrays.map(datasetArray => datasetArray[i]);
|
|
||||||
args.unshift(this.chartRenderer);
|
|
||||||
|
|
||||||
args.push(i);
|
|
||||||
args.push(this.totalIndices);
|
|
||||||
|
|
||||||
this.stores.push(this.make(...args));
|
|
||||||
|
|
||||||
let layer = this.layers[i];
|
|
||||||
layer.textContent = '';
|
|
||||||
this.stores[i].forEach(element => {
|
|
||||||
layer.appendChild(element);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const REPLACE_ALL_NEW_DUR = 250;
|
const REPLACE_ALL_NEW_DUR = 250;
|
||||||
|
|
||||||
@ -1428,9 +1414,15 @@ class AxisChart extends BaseChart {
|
|||||||
this.is_series = args.is_series;
|
this.is_series = args.is_series;
|
||||||
this.format_tooltip_y = args.format_tooltip_y;
|
this.format_tooltip_y = args.format_tooltip_y;
|
||||||
this.format_tooltip_x = args.format_tooltip_x;
|
this.format_tooltip_x = args.format_tooltip_x;
|
||||||
|
|
||||||
this.zeroLine = this.height;
|
this.zeroLine = this.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setHorizontalMargin() {
|
||||||
|
this.translateXLeft = Y_AXIS_MARGIN;
|
||||||
|
this.translateXRight = Y_AXIS_MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
checkData(data) {
|
checkData(data) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1441,7 +1433,10 @@ class AxisChart extends BaseChart {
|
|||||||
|
|
||||||
prepareData() {
|
prepareData() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
|
|
||||||
s.xAxisLabels = this.data.labels || [];
|
s.xAxisLabels = this.data.labels || [];
|
||||||
|
s.xAxisPositions = [];
|
||||||
|
|
||||||
s.datasetLength = s.xAxisLabels.length;
|
s.datasetLength = s.xAxisLabels.length;
|
||||||
|
|
||||||
let zeroArray = new Array(s.datasetLength).fill(0);
|
let zeroArray = new Array(s.datasetLength).fill(0);
|
||||||
@ -1474,6 +1469,16 @@ class AxisChart extends BaseChart {
|
|||||||
});
|
});
|
||||||
|
|
||||||
s.noOfDatasets = s.datasets.length;
|
s.noOfDatasets = s.datasets.length;
|
||||||
|
|
||||||
|
// s.yAxis = [];
|
||||||
|
this.prepareYAxis();
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareYAxis() {
|
||||||
|
this.state.yAxis = {
|
||||||
|
labels: [],
|
||||||
|
positions: []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
reCalc() {
|
reCalc() {
|
||||||
@ -1484,15 +1489,15 @@ class AxisChart extends BaseChart {
|
|||||||
this.calcXPositions();
|
this.calcXPositions();
|
||||||
|
|
||||||
// Y
|
// Y
|
||||||
s.datasetsLabels = this.data.datasets.map(d => d.label);
|
s.datasetsLabels = this.data.datasets.map(d => d.name);
|
||||||
|
|
||||||
// s.yUnitValues = [[]]; indexed component
|
// s.yUnitValues = [[]]; indexed component
|
||||||
// s.yUnitValues = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]]; // array of indexed components
|
// s.yUnitValues = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]]; // array of indexed components
|
||||||
s.yUnitValues = s.datasets.map(d => d.values); // indexed component
|
s.yUnitValues = s.datasets.map(d => d.values); // indexed component
|
||||||
s.yAxisLabels = calcIntervals(this.getAllYValues(), this.type === 'line');
|
|
||||||
this.calcYAxisPositions();
|
|
||||||
|
|
||||||
this.calcYUnitPositions();
|
this.setYAxis();
|
||||||
|
|
||||||
|
this.calcYUnits();
|
||||||
|
|
||||||
// should be state
|
// should be state
|
||||||
this.configUnits();
|
this.configUnits();
|
||||||
@ -1501,6 +1506,11 @@ class AxisChart extends BaseChart {
|
|||||||
s.unitTypes = s.datasets.map(d => d.unitArgs ? d.unitArgs : this.state.unitArgs);
|
s.unitTypes = s.datasets.map(d => d.unitArgs ? d.unitArgs : this.state.unitArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setYAxis() {
|
||||||
|
this.calcYAxisParameters(this.state.yAxis, this.getAllYValues(), this.type === 'line');
|
||||||
|
this.state.zeroLine = this.state.yAxis.zeroLine;
|
||||||
|
}
|
||||||
|
|
||||||
calcXPositions() {
|
calcXPositions() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
this.setUnitWidthAndXOffset();
|
this.setUnitWidthAndXOffset();
|
||||||
@ -1510,18 +1520,18 @@ class AxisChart extends BaseChart {
|
|||||||
s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
|
s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYAxisPositions() {
|
calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') {
|
||||||
let s = this.state;
|
yAxis.labels = calcIntervals(dataValues, withMinimum);
|
||||||
const yPts = s.yAxisLabels;
|
const yPts = yAxis.labels;
|
||||||
|
|
||||||
s.scaleMultiplier = this.height / getValueRange(yPts);
|
yAxis.scaleMultiplier = this.height / getValueRange(yPts);
|
||||||
const intervalHeight = getIntervalSize(yPts) * s.scaleMultiplier;
|
const intervalHeight = getIntervalSize(yPts) * yAxis.scaleMultiplier;
|
||||||
s.zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
|
yAxis.zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
|
||||||
|
|
||||||
s.yAxisPositions = yPts.map(d => s.zeroLine - d * s.scaleMultiplier);
|
yAxis.positions = yPts.map(d => yAxis.zeroLine - d * yAxis.scaleMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYUnitPositions() {
|
calcYUnits() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
s.yUnitPositions = s.yUnitValues.map(values =>
|
s.yUnitPositions = s.yUnitValues.map(values =>
|
||||||
values.map(val => floatTwo(s.zeroLine - val * s.scaleMultiplier))
|
values.map(val => floatTwo(s.zeroLine - val * s.scaleMultiplier))
|
||||||
@ -1557,6 +1567,105 @@ class AxisChart extends BaseChart {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupValues() {}
|
||||||
|
|
||||||
|
setupComponents() {
|
||||||
|
// temp : will be an indexedchartcomponent
|
||||||
|
// this.yAxisAux = new ChartComponent({
|
||||||
|
// layerClass: 'y axis aux',
|
||||||
|
// make: (renderer, positions, values) => {
|
||||||
|
// positions = [0, 70, 140, 270];
|
||||||
|
// values = [300, 200, 100, 0];
|
||||||
|
// return positions.map((position, i) => renderer.yLine(position, values[i], 'right'));
|
||||||
|
// },
|
||||||
|
// animate: () => {}
|
||||||
|
// });
|
||||||
|
|
||||||
|
this.setupYAxesComponents();
|
||||||
|
|
||||||
|
this.xAxis = new ChartComponent({
|
||||||
|
layerClass: 'x axis',
|
||||||
|
make: () => {
|
||||||
|
let s = this.state;
|
||||||
|
return s.xAxisPositions.map((position, i) =>
|
||||||
|
this.renderer.xLine(position, s.xAxisLabels[i], {pos:'top'})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// animate: (animator, lines, oldX, newX) => {
|
||||||
|
// lines.map((xLine, i) => {
|
||||||
|
// elements_to_animate.push(animator.verticalLine(
|
||||||
|
// xLine, newX[i], oldX[i]
|
||||||
|
// ));
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
|
// this.dataUnits = new IndexedChartComponent({
|
||||||
|
// layerClass: 'dataset-units',
|
||||||
|
// make: (renderer, xPosSet, yPosSet, color, unitType,
|
||||||
|
// yValueSet, datasetIndex, noOfDatasets) => {;
|
||||||
|
|
||||||
|
// let unitSet = yPosSet.map((y, i) => {
|
||||||
|
// return renderer[unitType.type](
|
||||||
|
// xPosSet[i],
|
||||||
|
// y,
|
||||||
|
// unitType.args,
|
||||||
|
// color,
|
||||||
|
// i,
|
||||||
|
// datasetIndex,
|
||||||
|
// noOfDatasets
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if(this.type === 'line') {
|
||||||
|
// let pointsList = yPosSet.map((y, i) => (xPosSet[i] + ',' + y));
|
||||||
|
// let pointsStr = pointsList.join("L");
|
||||||
|
|
||||||
|
// unitSet.unshift(makePath("M"+pointsStr, 'line-graph-path', color));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return unitSet;
|
||||||
|
// },
|
||||||
|
// argsKeys: ['xUnitPositions', 'yUnitPositions',
|
||||||
|
// 'colors', 'unitTypes', 'yUnitValues'],
|
||||||
|
// animate: () => {}
|
||||||
|
// });
|
||||||
|
|
||||||
|
// TODO: rebind new units
|
||||||
|
// if(this.isNavigable) {
|
||||||
|
// this.bind_units(units_array);
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.yMarkerLines = {};
|
||||||
|
this.xMarkerLines = {};
|
||||||
|
|
||||||
|
// Marker Regions
|
||||||
|
|
||||||
|
this.components = [
|
||||||
|
// temp
|
||||||
|
// this.yAxesAux,
|
||||||
|
...this.yAxesComponents,
|
||||||
|
this.xAxis,
|
||||||
|
// this.yMarkerLines,
|
||||||
|
// this.xMarkerLines,
|
||||||
|
|
||||||
|
// this.dataUnits,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
setupYAxesComponents() {
|
||||||
|
this.yAxesComponents = [ new ChartComponent({
|
||||||
|
layerClass: 'y axis',
|
||||||
|
make: () => {
|
||||||
|
let s = this.state;
|
||||||
|
return s.yAxis.positions.map((position, i) =>
|
||||||
|
this.renderer.yLine(position, s.yAxis.labels[i], {pos:'right'})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
animate: () => {}
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
refreshRenderer() {
|
refreshRenderer() {
|
||||||
// These args are basically the current state of the chart,
|
// These args are basically the current state of the chart,
|
||||||
// with constant and alive params mixed
|
// with constant and alive params mixed
|
||||||
@ -1577,95 +1686,6 @@ class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setupComponents() {
|
|
||||||
// temp : will be an indexedchartcomponent
|
|
||||||
// this.yAxisAux = new ChartComponent({
|
|
||||||
// layerClass: 'y axis aux',
|
|
||||||
// make: (renderer, positions, values) => {
|
|
||||||
// positions = [0, 70, 140, 270];
|
|
||||||
// values = [300, 200, 100, 0];
|
|
||||||
// return positions.map((position, i) => renderer.yLine(position, values[i], 'right'));
|
|
||||||
// },
|
|
||||||
// argsKeys: ['yAxisPositions', 'yAxisLabels'],
|
|
||||||
// animate: () => {}
|
|
||||||
// });
|
|
||||||
|
|
||||||
this.yAxis = new ChartComponent({
|
|
||||||
layerClass: 'y axis',
|
|
||||||
make: (renderer, positions, values) => {
|
|
||||||
return positions.map((position, i) => renderer.yLine(position, values[i]));
|
|
||||||
},
|
|
||||||
argsKeys: ['yAxisPositions', 'yAxisLabels'],
|
|
||||||
animate: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.xAxis = new ChartComponent({
|
|
||||||
layerClass: 'x axis',
|
|
||||||
make: (renderer, positions, values) => {
|
|
||||||
return positions.map((position, i) => renderer.xLine(position, values[i]));
|
|
||||||
},
|
|
||||||
argsKeys: ['xAxisPositions', 'xAxisLabels'],
|
|
||||||
animate: (animator, lines, oldX, newX) => {
|
|
||||||
lines.map((xLine, i) => {
|
|
||||||
elements_to_animate.push(animator.verticalLine(
|
|
||||||
xLine, newX[i], oldX[i]
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dataUnits = new IndexedChartComponent({
|
|
||||||
layerClass: 'dataset-units',
|
|
||||||
make: (renderer, xPosSet, yPosSet, color, unitType,
|
|
||||||
yValueSet, datasetIndex, noOfDatasets) => {
|
|
||||||
|
|
||||||
let unitSet = yPosSet.map((y, i) => {
|
|
||||||
return renderer[unitType.type](
|
|
||||||
xPosSet[i],
|
|
||||||
y,
|
|
||||||
unitType.args,
|
|
||||||
color,
|
|
||||||
i,
|
|
||||||
datasetIndex,
|
|
||||||
noOfDatasets
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.type === 'line') {
|
|
||||||
let pointsList = yPosSet.map((y, i) => (xPosSet[i] + ',' + y));
|
|
||||||
let pointsStr = pointsList.join("L");
|
|
||||||
|
|
||||||
unitSet.unshift(makePath("M"+pointsStr, 'line-graph-path', color));
|
|
||||||
}
|
|
||||||
|
|
||||||
return unitSet;
|
|
||||||
},
|
|
||||||
argsKeys: ['xUnitPositions', 'yUnitPositions',
|
|
||||||
'colors', 'unitTypes', 'yUnitValues'],
|
|
||||||
animate: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: rebind new units
|
|
||||||
// if(this.isNavigable) {
|
|
||||||
// this.bind_units(units_array);
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.yMarkerLines = {};
|
|
||||||
this.xMarkerLines = {};
|
|
||||||
|
|
||||||
// Marker Regions
|
|
||||||
|
|
||||||
this.components = [
|
|
||||||
// temp
|
|
||||||
// this.yAxisAux,
|
|
||||||
this.yAxis,
|
|
||||||
this.xAxis,
|
|
||||||
// this.yMarkerLines,
|
|
||||||
// this.xMarkerLines,
|
|
||||||
this.dataUnits,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BarChart extends AxisChart {
|
class BarChart extends AxisChart {
|
||||||
@ -1891,6 +1911,103 @@ class ScatterChart extends LineChart {
|
|||||||
make_path() {}
|
make_path() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MultiAxisChart extends AxisChart {
|
||||||
|
constructor(args) {
|
||||||
|
super(args);
|
||||||
|
this.type = 'multiaxis';
|
||||||
|
this.unitType = args.unitType || 'line';
|
||||||
|
this.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
setHorizontalMargin() {
|
||||||
|
let noOfLeftAxes = this.data.datasets.filter(d => d.axisPosition === 'left').length;
|
||||||
|
this.translateXLeft = (noOfLeftAxes) * Y_AXIS_MARGIN;
|
||||||
|
this.translateXRight = (this.data.datasets.length - noOfLeftAxes) * Y_AXIS_MARGIN || Y_AXIS_MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareYAxis() {
|
||||||
|
this.state.yAxes = [];
|
||||||
|
let sets = this.state.datasets;
|
||||||
|
// let axesLeft = sets.filter(d => d.axisPosition === 'left');
|
||||||
|
// let axesRight = sets.filter(d => d.axisPosition === 'right');
|
||||||
|
// let axesNone = sets.filter(d => !d.axisPosition ||
|
||||||
|
// !['left', 'right'].includes(d.axisPosition));
|
||||||
|
|
||||||
|
let leftCount = 0, rightCount = 0;
|
||||||
|
|
||||||
|
sets.forEach((d, i) => {
|
||||||
|
this.state.yAxes.push({
|
||||||
|
position: d.axisPosition,
|
||||||
|
color: d.color,
|
||||||
|
dataValues: d.values,
|
||||||
|
index: d.axisPosition === 'left' ? leftCount++ : rightCount++
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
this.config.xAxisMode = args.xAxisMode || 'tick';
|
||||||
|
this.config.yAxisMode = args.yAxisMode || 'span';
|
||||||
|
}
|
||||||
|
|
||||||
|
// setUnitWidthAndXOffset() {
|
||||||
|
// this.state.unitWidth = this.width/(this.state.datasetLength);
|
||||||
|
// this.state.xOffset = this.state.unitWidth/2;
|
||||||
|
// }
|
||||||
|
|
||||||
|
configUnits() {
|
||||||
|
this.state.unitArgs = {
|
||||||
|
type: 'bar',
|
||||||
|
args: {
|
||||||
|
spaceWidth: this.state.unitWidth/2,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setYAxis() {
|
||||||
|
this.state.yAxes.map(yAxis => {
|
||||||
|
// console.log(yAxis);
|
||||||
|
this.calcYAxisParameters(yAxis, yAxis.dataValues, this.unitType === 'line');
|
||||||
|
// console.log(yAxis);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupYAxesComponents() {
|
||||||
|
this.yAxesComponents = this.state.yAxes.map((e, i) => {
|
||||||
|
return new ChartComponent({
|
||||||
|
layerClass: 'y axis y-axis-' + i,
|
||||||
|
make: () => {
|
||||||
|
let d = this.state.yAxes[i];
|
||||||
|
this.renderer.setZeroline(d.zeroline);
|
||||||
|
let axis = d.positions.map((position, j) =>
|
||||||
|
this.renderer.yLine(position, d.labels[j], {
|
||||||
|
pos: d.position,
|
||||||
|
mode: 'tick',
|
||||||
|
offset: d.index * Y_AXIS_MARGIN,
|
||||||
|
stroke: this.colors[i]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let guidePos = d.position === 'left'
|
||||||
|
? -1 * d.index * Y_AXIS_MARGIN
|
||||||
|
: this.width + d.index * Y_AXIS_MARGIN;
|
||||||
|
|
||||||
|
axis.push(this.renderer.xLine(guidePos, '', {
|
||||||
|
pos:'top',
|
||||||
|
mode: 'span',
|
||||||
|
stroke: this.colors[i],
|
||||||
|
className: 'y-axis-guide'
|
||||||
|
}));
|
||||||
|
|
||||||
|
return axis;
|
||||||
|
},
|
||||||
|
animate: () => {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PercentageChart extends BaseChart {
|
class PercentageChart extends BaseChart {
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
super(args);
|
super(args);
|
||||||
@ -2499,6 +2616,7 @@ class Heatmap extends BaseChart {
|
|||||||
const chartTypes = {
|
const chartTypes = {
|
||||||
line: LineChart,
|
line: LineChart,
|
||||||
bar: BarChart,
|
bar: BarChart,
|
||||||
|
multiaxis: MultiAxisChart,
|
||||||
scatter: ScatterChart,
|
scatter: ScatterChart,
|
||||||
percentage: PercentageChart,
|
percentage: PercentageChart,
|
||||||
heatmap: Heatmap,
|
heatmap: Heatmap,
|
||||||
|
|||||||
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.css
vendored
2
dist/frappe-charts.min.css
vendored
@ -1 +1 @@
|
|||||||
.chart-container{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.chart-container .graph-focus-margin{margin:0 5%}.chart-container>.title{margin-top:25px;margin-left:25px;text-align:left;font-weight:400;font-size:12px;color:#6c7680}.chart-container .graphics{margin-top:10px;padding-top:10px;padding-bottom:10px;position:relative}.chart-container .graph-stats-group{-ms-flex-pack:distribute;-webkit-box-flex:1;-ms-flex:1;flex:1}.chart-container .graph-stats-container,.chart-container .graph-stats-group{display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:space-around}.chart-container .graph-stats-container{-ms-flex-pack:distribute;padding-top:10px}.chart-container .graph-stats-container .stats{padding-bottom:15px}.chart-container .graph-stats-container .stats-title{color:#8d99a6}.chart-container .graph-stats-container .stats-value{font-size:20px;font-weight:300}.chart-container .graph-stats-container .stats-description{font-size:12px;color:#8d99a6}.chart-container .graph-stats-container .graph-data .stats-value{color:#98d85b}.chart-container .axis,.chart-container .chart-label{fill:#555b51}.chart-container .percentage-graph .progress{margin-bottom:0}.chart-container .dataset-units circle{stroke:#fff;stroke-width:2}.chart-container .dataset-units path,.chart-container .path-group path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container line.dashed{stroke-dasharray:5,3}.chart-container .axis-line .specific-value{text-anchor:start}.chart-container .axis-line .y-line{text-anchor:end}.chart-container .axis-line .x-line{text-anchor:middle}.chart-container .progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.chart-container .progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#36414c;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.chart-container .graph-svg-tip{position:absolute;z-index:1;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.8);border-radius:3px}.chart-container .graph-svg-tip ol,.chart-container .graph-svg-tip ul{padding-left:0;display:-webkit-box;display:-ms-flexbox;display:flex}.chart-container .graph-svg-tip ul.data-point-list li{min-width:90px;-webkit-box-flex:1;-ms-flex:1;flex:1;font-weight:600}.chart-container .graph-svg-tip strong{color:#dfe2e5;font-weight:600}.chart-container .graph-svg-tip .svg-pointer{position:absolute;height:5px;margin:0 0 0 -5px;content:" ";border:5px solid transparent;border-top-color:rgba(0,0,0,.8)}.chart-container .graph-svg-tip.comparison{padding:0;text-align:left;pointer-events:none}.chart-container .graph-svg-tip.comparison .title{display:block;padding:10px;margin:0;font-weight:600;line-height:1;pointer-events:none}.chart-container .graph-svg-tip.comparison ul{margin:0;white-space:nowrap;list-style:none}.chart-container .graph-svg-tip.comparison li{display:inline-block;padding:5px 10px}.chart-container .indicator,.chart-container .indicator-right{background:none;font-size:12px;vertical-align:middle;font-weight:700;color:#6c7680}.chart-container .indicator i{content:"";display:inline-block;height:8px;width:8px;border-radius:8px}.chart-container .indicator:before,.chart-container .indicator i{margin:0 4px 0 0}.chart-container .indicator-right:after{margin:0 0 0 4px}
|
.chart-container{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.chart-container .multiaxis-chart .line-horizontal,.chart-container .multiaxis-chart .y-axis-guide{stroke-width:2px}.chart-container .graph-focus-margin{margin:0 5%}.chart-container>.title{margin-top:25px;margin-left:25px;text-align:left;font-weight:400;font-size:12px;color:#6c7680}.chart-container .graphics{margin-top:10px;padding-top:10px;padding-bottom:10px;position:relative}.chart-container .graph-stats-group{-ms-flex-pack:distribute;-webkit-box-flex:1;-ms-flex:1;flex:1}.chart-container .graph-stats-container,.chart-container .graph-stats-group{display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:space-around}.chart-container .graph-stats-container{-ms-flex-pack:distribute;padding-top:10px}.chart-container .graph-stats-container .stats{padding-bottom:15px}.chart-container .graph-stats-container .stats-title{color:#8d99a6}.chart-container .graph-stats-container .stats-value{font-size:20px;font-weight:300}.chart-container .graph-stats-container .stats-description{font-size:12px;color:#8d99a6}.chart-container .graph-stats-container .graph-data .stats-value{color:#98d85b}.chart-container .axis,.chart-container .chart-label{fill:#555b51}.chart-container .axis line,.chart-container .chart-label line{stroke:#dadada}.chart-container .percentage-graph .progress{margin-bottom:0}.chart-container .dataset-units circle{stroke:#fff;stroke-width:2}.chart-container .dataset-units path,.chart-container .path-group path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container line.dashed{stroke-dasharray:5,3}.chart-container .axis-line .specific-value{text-anchor:start}.chart-container .axis-line .y-line{text-anchor:end}.chart-container .axis-line .x-line{text-anchor:middle}.chart-container .progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.chart-container .progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#36414c;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.chart-container .graph-svg-tip{position:absolute;z-index:1;padding:10px;font-size:12px;color:#959da5;text-align:center;background:rgba(0,0,0,.8);border-radius:3px}.chart-container .graph-svg-tip ol,.chart-container .graph-svg-tip ul{padding-left:0;display:-webkit-box;display:-ms-flexbox;display:flex}.chart-container .graph-svg-tip ul.data-point-list li{min-width:90px;-webkit-box-flex:1;-ms-flex:1;flex:1;font-weight:600}.chart-container .graph-svg-tip strong{color:#dfe2e5;font-weight:600}.chart-container .graph-svg-tip .svg-pointer{position:absolute;height:5px;margin:0 0 0 -5px;content:" ";border:5px solid transparent;border-top-color:rgba(0,0,0,.8)}.chart-container .graph-svg-tip.comparison{padding:0;text-align:left;pointer-events:none}.chart-container .graph-svg-tip.comparison .title{display:block;padding:10px;margin:0;font-weight:600;line-height:1;pointer-events:none}.chart-container .graph-svg-tip.comparison ul{margin:0;white-space:nowrap;list-style:none}.chart-container .graph-svg-tip.comparison li{display:inline-block;padding:5px 10px}.chart-container .indicator,.chart-container .indicator-right{background:none;font-size:12px;vertical-align:middle;font-weight:700;color:#6c7680}.chart-container .indicator i{content:"";display:inline-block;height:8px;width:8px;border-radius:8px}.chart-container .indicator:before,.chart-container .indicator i{margin:0 4px 0 0}.chart-container .indicator-right:after{margin:0 0 0 4px}
|
||||||
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
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
@ -8,7 +8,7 @@ let bar_composite_data = {
|
|||||||
"2013", "2014", "2015", "2016", "2017"],
|
"2013", "2014", "2015", "2016", "2017"],
|
||||||
|
|
||||||
datasets: [{
|
datasets: [{
|
||||||
"label": "Events",
|
"name": "Events",
|
||||||
"values": report_count_list,
|
"values": report_count_list,
|
||||||
// "formatted": report_count_list.map(d => d + " reports")
|
// "formatted": report_count_list.map(d => d + " reports")
|
||||||
}]
|
}]
|
||||||
@ -77,29 +77,31 @@ let type_data = {
|
|||||||
|
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: "Some Data",
|
name: "Some Data",
|
||||||
values: [18, 40, 30, 35, 8, 52, 17, -4]
|
values: [18, 40, 30, 35, 8, 52, 17, -4],
|
||||||
|
axisPosition: 'right'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Another Set",
|
name: "Another Set",
|
||||||
values: [30, 50, -10, 15, 18, 32, 27, 14]
|
values: [30, 50, -10, 15, 18, 32, 27, 14],
|
||||||
|
axisPosition: 'right'
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
// label: "Yet Another",
|
// name: "Yet Another",
|
||||||
// values: [15, 20, -3, -15, 58, 12, -17, 37]
|
// values: [15, 20, -3, -15, 58, 12, -17, 37]
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// temp : Stacked
|
// temp : Stacked
|
||||||
// {
|
// {
|
||||||
// label: "Some Data",
|
// name: "Some Data",
|
||||||
// values:[25, 30, 50, 45, 18, 12, 27, 14]
|
// values:[25, 30, 50, 45, 18, 12, 27, 14]
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// label: "Another Set",
|
// name: "Another Set",
|
||||||
// values: [18, 20, 30, 35, 8, 7, 17, 4]
|
// values: [18, 20, 30, 35, 8, 7, 17, 4]
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
// label: "Another Set",
|
// name: "Another Set",
|
||||||
// values: [11, 8, 19, 15, 3, 4, 10, 2]
|
// values: [11, 8, 19, 15, 3, 4, 10, 2]
|
||||||
// },
|
// },
|
||||||
]
|
]
|
||||||
@ -109,7 +111,7 @@ let type_chart = new Chart({
|
|||||||
parent: "#chart-types",
|
parent: "#chart-types",
|
||||||
// title: "My Awesome Chart",
|
// title: "My Awesome Chart",
|
||||||
data: type_data,
|
data: type_data,
|
||||||
type: 'bar',
|
type: 'multiaxis',
|
||||||
height: 250,
|
height: 250,
|
||||||
colors: ['purple', 'magenta'],
|
colors: ['purple', 'magenta'],
|
||||||
is_series: 1,
|
is_series: 1,
|
||||||
@ -225,8 +227,8 @@ let update_data = {
|
|||||||
}],
|
}],
|
||||||
"specific_values": [
|
"specific_values": [
|
||||||
{
|
{
|
||||||
label: "Altitude",
|
name: "Altitude",
|
||||||
// label: "A very long text",
|
// name: "A very long text",
|
||||||
line_type: "dashed",
|
line_type: "dashed",
|
||||||
value: 38
|
value: 38
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import '../scss/charts.scss';
|
|||||||
import BarChart from './charts/BarChart';
|
import BarChart from './charts/BarChart';
|
||||||
import LineChart from './charts/LineChart';
|
import LineChart from './charts/LineChart';
|
||||||
import ScatterChart from './charts/ScatterChart';
|
import ScatterChart from './charts/ScatterChart';
|
||||||
|
import MultiAxisChart from './charts/MultiAxisChart';
|
||||||
import PercentageChart from './charts/PercentageChart';
|
import PercentageChart from './charts/PercentageChart';
|
||||||
import PieChart from './charts/PieChart';
|
import PieChart from './charts/PieChart';
|
||||||
import Heatmap from './charts/Heatmap';
|
import Heatmap from './charts/Heatmap';
|
||||||
@ -18,6 +19,7 @@ import Heatmap from './charts/Heatmap';
|
|||||||
const chartTypes = {
|
const chartTypes = {
|
||||||
line: LineChart,
|
line: LineChart,
|
||||||
bar: BarChart,
|
bar: BarChart,
|
||||||
|
multiaxis: MultiAxisChart,
|
||||||
scatter: ScatterChart,
|
scatter: ScatterChart,
|
||||||
percentage: PercentageChart,
|
percentage: PercentageChart,
|
||||||
heatmap: Heatmap,
|
heatmap: Heatmap,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import BaseChart from './BaseChart';
|
import BaseChart from './BaseChart';
|
||||||
import { ChartComponent, IndexedChartComponent } from '../objects/ChartComponent';
|
import { Y_AXIS_MARGIN } from '../utils/margins';
|
||||||
|
import { ChartComponent } from '../objects/ChartComponent';
|
||||||
import { getOffset, fire } from '../utils/dom';
|
import { getOffset, fire } from '../utils/dom';
|
||||||
import { AxisChartRenderer, makePath, makeGradient } from '../utils/draw';
|
import { AxisChartRenderer, makePath, makeGradient } from '../utils/draw';
|
||||||
import { equilizeNoOfElements } from '../utils/draw-utils';
|
import { equilizeNoOfElements } from '../utils/draw-utils';
|
||||||
@ -14,9 +15,15 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.is_series = args.is_series;
|
this.is_series = args.is_series;
|
||||||
this.format_tooltip_y = args.format_tooltip_y;
|
this.format_tooltip_y = args.format_tooltip_y;
|
||||||
this.format_tooltip_x = args.format_tooltip_x;
|
this.format_tooltip_x = args.format_tooltip_x;
|
||||||
|
|
||||||
this.zeroLine = this.height;
|
this.zeroLine = this.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setHorizontalMargin() {
|
||||||
|
this.translateXLeft = Y_AXIS_MARGIN;
|
||||||
|
this.translateXRight = Y_AXIS_MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
checkData(data) {
|
checkData(data) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -27,7 +34,10 @@ export default class AxisChart extends BaseChart {
|
|||||||
|
|
||||||
prepareData() {
|
prepareData() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
|
|
||||||
s.xAxisLabels = this.data.labels || [];
|
s.xAxisLabels = this.data.labels || [];
|
||||||
|
s.xAxisPositions = [];
|
||||||
|
|
||||||
s.datasetLength = s.xAxisLabels.length;
|
s.datasetLength = s.xAxisLabels.length;
|
||||||
|
|
||||||
let zeroArray = new Array(s.datasetLength).fill(0);
|
let zeroArray = new Array(s.datasetLength).fill(0);
|
||||||
@ -60,6 +70,16 @@ export default class AxisChart extends BaseChart {
|
|||||||
});
|
});
|
||||||
|
|
||||||
s.noOfDatasets = s.datasets.length;
|
s.noOfDatasets = s.datasets.length;
|
||||||
|
|
||||||
|
// s.yAxis = [];
|
||||||
|
this.prepareYAxis();
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareYAxis() {
|
||||||
|
this.state.yAxis = {
|
||||||
|
labels: [],
|
||||||
|
positions: []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
reCalc() {
|
reCalc() {
|
||||||
@ -70,15 +90,15 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.calcXPositions();
|
this.calcXPositions();
|
||||||
|
|
||||||
// Y
|
// Y
|
||||||
s.datasetsLabels = this.data.datasets.map(d => d.label);
|
s.datasetsLabels = this.data.datasets.map(d => d.name);
|
||||||
|
|
||||||
// s.yUnitValues = [[]]; indexed component
|
// s.yUnitValues = [[]]; indexed component
|
||||||
// s.yUnitValues = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]]; // array of indexed components
|
// s.yUnitValues = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]]; // array of indexed components
|
||||||
s.yUnitValues = s.datasets.map(d => d.values); // indexed component
|
s.yUnitValues = s.datasets.map(d => d.values); // indexed component
|
||||||
s.yAxisLabels = calcIntervals(this.getAllYValues(), this.type === 'line');
|
|
||||||
this.calcYAxisPositions();
|
|
||||||
|
|
||||||
this.calcYUnitPositions();
|
this.setYAxis();
|
||||||
|
|
||||||
|
this.calcYUnits();
|
||||||
|
|
||||||
// should be state
|
// should be state
|
||||||
this.configUnits();
|
this.configUnits();
|
||||||
@ -87,6 +107,11 @@ export default class AxisChart extends BaseChart {
|
|||||||
s.unitTypes = s.datasets.map(d => d.unitArgs ? d.unitArgs : this.state.unitArgs);
|
s.unitTypes = s.datasets.map(d => d.unitArgs ? d.unitArgs : this.state.unitArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setYAxis() {
|
||||||
|
this.calcYAxisParameters(this.state.yAxis, this.getAllYValues(), this.type === 'line');
|
||||||
|
this.state.zeroLine = this.state.yAxis.zeroLine;
|
||||||
|
}
|
||||||
|
|
||||||
calcXPositions() {
|
calcXPositions() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
this.setUnitWidthAndXOffset();
|
this.setUnitWidthAndXOffset();
|
||||||
@ -96,18 +121,18 @@ export default class AxisChart extends BaseChart {
|
|||||||
s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
|
s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYAxisPositions() {
|
calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') {
|
||||||
let s = this.state;
|
yAxis.labels = calcIntervals(dataValues, withMinimum);
|
||||||
const yPts = s.yAxisLabels;
|
const yPts = yAxis.labels;
|
||||||
|
|
||||||
s.scaleMultiplier = this.height / getValueRange(yPts);
|
yAxis.scaleMultiplier = this.height / getValueRange(yPts);
|
||||||
const intervalHeight = getIntervalSize(yPts) * s.scaleMultiplier;
|
const intervalHeight = getIntervalSize(yPts) * yAxis.scaleMultiplier;
|
||||||
s.zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
|
yAxis.zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
|
||||||
|
|
||||||
s.yAxisPositions = yPts.map(d => s.zeroLine - d * s.scaleMultiplier);
|
yAxis.positions = yPts.map(d => yAxis.zeroLine - d * yAxis.scaleMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYUnitPositions() {
|
calcYUnits() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
s.yUnitPositions = s.yUnitValues.map(values =>
|
s.yUnitPositions = s.yUnitValues.map(values =>
|
||||||
values.map(val => floatTwo(s.zeroLine - val * s.scaleMultiplier))
|
values.map(val => floatTwo(s.zeroLine - val * s.scaleMultiplier))
|
||||||
@ -143,6 +168,105 @@ export default class AxisChart extends BaseChart {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupValues() {}
|
||||||
|
|
||||||
|
setupComponents() {
|
||||||
|
// temp : will be an indexedchartcomponent
|
||||||
|
// this.yAxisAux = new ChartComponent({
|
||||||
|
// layerClass: 'y axis aux',
|
||||||
|
// make: (renderer, positions, values) => {
|
||||||
|
// positions = [0, 70, 140, 270];
|
||||||
|
// values = [300, 200, 100, 0];
|
||||||
|
// return positions.map((position, i) => renderer.yLine(position, values[i], 'right'));
|
||||||
|
// },
|
||||||
|
// animate: () => {}
|
||||||
|
// });
|
||||||
|
|
||||||
|
this.setupYAxesComponents();
|
||||||
|
|
||||||
|
this.xAxis = new ChartComponent({
|
||||||
|
layerClass: 'x axis',
|
||||||
|
make: () => {
|
||||||
|
let s = this.state;
|
||||||
|
return s.xAxisPositions.map((position, i) =>
|
||||||
|
this.renderer.xLine(position, s.xAxisLabels[i], {pos:'top'})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// animate: (animator, lines, oldX, newX) => {
|
||||||
|
// lines.map((xLine, i) => {
|
||||||
|
// elements_to_animate.push(animator.verticalLine(
|
||||||
|
// xLine, newX[i], oldX[i]
|
||||||
|
// ));
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
|
// this.dataUnits = new IndexedChartComponent({
|
||||||
|
// layerClass: 'dataset-units',
|
||||||
|
// make: (renderer, xPosSet, yPosSet, color, unitType,
|
||||||
|
// yValueSet, datasetIndex, noOfDatasets) => {;
|
||||||
|
|
||||||
|
// let unitSet = yPosSet.map((y, i) => {
|
||||||
|
// return renderer[unitType.type](
|
||||||
|
// xPosSet[i],
|
||||||
|
// y,
|
||||||
|
// unitType.args,
|
||||||
|
// color,
|
||||||
|
// i,
|
||||||
|
// datasetIndex,
|
||||||
|
// noOfDatasets
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if(this.type === 'line') {
|
||||||
|
// let pointsList = yPosSet.map((y, i) => (xPosSet[i] + ',' + y));
|
||||||
|
// let pointsStr = pointsList.join("L");
|
||||||
|
|
||||||
|
// unitSet.unshift(makePath("M"+pointsStr, 'line-graph-path', color));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return unitSet;
|
||||||
|
// },
|
||||||
|
// argsKeys: ['xUnitPositions', 'yUnitPositions',
|
||||||
|
// 'colors', 'unitTypes', 'yUnitValues'],
|
||||||
|
// animate: () => {}
|
||||||
|
// });
|
||||||
|
|
||||||
|
// TODO: rebind new units
|
||||||
|
// if(this.isNavigable) {
|
||||||
|
// this.bind_units(units_array);
|
||||||
|
// }
|
||||||
|
|
||||||
|
this.yMarkerLines = {};
|
||||||
|
this.xMarkerLines = {};
|
||||||
|
|
||||||
|
// Marker Regions
|
||||||
|
|
||||||
|
this.components = [
|
||||||
|
// temp
|
||||||
|
// this.yAxesAux,
|
||||||
|
...this.yAxesComponents,
|
||||||
|
this.xAxis,
|
||||||
|
// this.yMarkerLines,
|
||||||
|
// this.xMarkerLines,
|
||||||
|
|
||||||
|
// this.dataUnits,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
setupYAxesComponents() {
|
||||||
|
this.yAxesComponents = [ new ChartComponent({
|
||||||
|
layerClass: 'y axis',
|
||||||
|
make: () => {
|
||||||
|
let s = this.state;
|
||||||
|
return s.yAxis.positions.map((position, i) =>
|
||||||
|
this.renderer.yLine(position, s.yAxis.labels[i], {pos:'right'})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
animate: () => {}
|
||||||
|
})];
|
||||||
|
}
|
||||||
|
|
||||||
refreshRenderer() {
|
refreshRenderer() {
|
||||||
// These args are basically the current state of the chart,
|
// These args are basically the current state of the chart,
|
||||||
// with constant and alive params mixed
|
// with constant and alive params mixed
|
||||||
@ -163,93 +287,4 @@ export default class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setupComponents() {
|
|
||||||
// temp : will be an indexedchartcomponent
|
|
||||||
// this.yAxisAux = new ChartComponent({
|
|
||||||
// layerClass: 'y axis aux',
|
|
||||||
// make: (renderer, positions, values) => {
|
|
||||||
// positions = [0, 70, 140, 270];
|
|
||||||
// values = [300, 200, 100, 0];
|
|
||||||
// return positions.map((position, i) => renderer.yLine(position, values[i], 'right'));
|
|
||||||
// },
|
|
||||||
// argsKeys: ['yAxisPositions', 'yAxisLabels'],
|
|
||||||
// animate: () => {}
|
|
||||||
// });
|
|
||||||
|
|
||||||
this.yAxis = new ChartComponent({
|
|
||||||
layerClass: 'y axis',
|
|
||||||
make: (renderer, positions, values) => {
|
|
||||||
return positions.map((position, i) => renderer.yLine(position, values[i]));
|
|
||||||
},
|
|
||||||
argsKeys: ['yAxisPositions', 'yAxisLabels'],
|
|
||||||
animate: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.xAxis = new ChartComponent({
|
|
||||||
layerClass: 'x axis',
|
|
||||||
make: (renderer, positions, values) => {
|
|
||||||
return positions.map((position, i) => renderer.xLine(position, values[i]));
|
|
||||||
},
|
|
||||||
argsKeys: ['xAxisPositions', 'xAxisLabels'],
|
|
||||||
animate: (animator, lines, oldX, newX) => {
|
|
||||||
lines.map((xLine, i) => {
|
|
||||||
elements_to_animate.push(animator.verticalLine(
|
|
||||||
xLine, newX[i], oldX[i]
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dataUnits = new IndexedChartComponent({
|
|
||||||
layerClass: 'dataset-units',
|
|
||||||
make: (renderer, xPosSet, yPosSet, color, unitType,
|
|
||||||
yValueSet, datasetIndex, noOfDatasets) => {;
|
|
||||||
|
|
||||||
let unitSet = yPosSet.map((y, i) => {
|
|
||||||
return renderer[unitType.type](
|
|
||||||
xPosSet[i],
|
|
||||||
y,
|
|
||||||
unitType.args,
|
|
||||||
color,
|
|
||||||
i,
|
|
||||||
datasetIndex,
|
|
||||||
noOfDatasets
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.type === 'line') {
|
|
||||||
let pointsList = yPosSet.map((y, i) => (xPosSet[i] + ',' + y));
|
|
||||||
let pointsStr = pointsList.join("L");
|
|
||||||
|
|
||||||
unitSet.unshift(makePath("M"+pointsStr, 'line-graph-path', color));
|
|
||||||
}
|
|
||||||
|
|
||||||
return unitSet;
|
|
||||||
},
|
|
||||||
argsKeys: ['xUnitPositions', 'yUnitPositions',
|
|
||||||
'colors', 'unitTypes', 'yUnitValues'],
|
|
||||||
animate: () => {}
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: rebind new units
|
|
||||||
// if(this.isNavigable) {
|
|
||||||
// this.bind_units(units_array);
|
|
||||||
// }
|
|
||||||
|
|
||||||
this.yMarkerLines = {};
|
|
||||||
this.xMarkerLines = {};
|
|
||||||
|
|
||||||
// Marker Regions
|
|
||||||
|
|
||||||
this.components = [
|
|
||||||
// temp
|
|
||||||
// this.yAxisAux,
|
|
||||||
this.yAxis,
|
|
||||||
this.xAxis,
|
|
||||||
// this.yMarkerLines,
|
|
||||||
// this.xMarkerLines,
|
|
||||||
this.dataUnits,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ export default class BaseChart {
|
|||||||
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
|
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.subtitle = subtitle;
|
this.subtitle = subtitle;
|
||||||
|
this.argHeight = height;
|
||||||
|
|
||||||
this.isNavigable = isNavigable;
|
this.isNavigable = isNavigable;
|
||||||
if(this.isNavigable) {
|
if(this.isNavigable) {
|
||||||
@ -40,7 +41,6 @@ export default class BaseChart {
|
|||||||
// showLegend, which then all functions will check
|
// showLegend, which then all functions will check
|
||||||
|
|
||||||
this.setColors();
|
this.setColors();
|
||||||
this.setMargins(args);
|
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
this.config = {
|
this.config = {
|
||||||
@ -73,12 +73,19 @@ export default class BaseChart {
|
|||||||
this.colors = this.colors.map(color => getColor(color));
|
this.colors = this.colors.map(color => getColor(color));
|
||||||
}
|
}
|
||||||
|
|
||||||
setMargins(args) {
|
setMargins() {
|
||||||
let height = args.height;
|
// TODO: think for all
|
||||||
|
let height = this.argHeight;
|
||||||
this.baseHeight = height;
|
this.baseHeight = height;
|
||||||
this.height = height - 40;
|
this.height = height - 40; // change
|
||||||
this.translateX = 60;
|
this.translateY = 20;
|
||||||
this.translateY = 10;
|
|
||||||
|
this.setHorizontalMargin();
|
||||||
|
}
|
||||||
|
|
||||||
|
setHorizontalMargin() {
|
||||||
|
this.translateXLeft = 60;
|
||||||
|
this.translateXRight = 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(){
|
validate(){
|
||||||
@ -120,7 +127,10 @@ export default class BaseChart {
|
|||||||
_setup() {
|
_setup() {
|
||||||
this.bindWindowEvents();
|
this.bindWindowEvents();
|
||||||
this.setupConstants();
|
this.setupConstants();
|
||||||
|
this.prepareData();
|
||||||
this.setupComponents();
|
this.setupComponents();
|
||||||
|
|
||||||
|
this.setMargins();
|
||||||
this.makeContainer();
|
this.makeContainer();
|
||||||
this.makeTooltip(); // without binding
|
this.makeTooltip(); // without binding
|
||||||
this.draw(true);
|
this.draw(true);
|
||||||
@ -204,15 +214,16 @@ export default class BaseChart {
|
|||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
|
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
|
||||||
this.width = this.baseWidth - this.translateX * 2;
|
this.width = this.baseWidth - (this.translateXLeft + this.translateXRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() { //?? refresh?
|
refresh() { //?? refresh?
|
||||||
this.oldState = this.state ? Object.assign({}, this.state) : {};
|
this.oldState = this.state ? Object.assign({}, this.state) : {};
|
||||||
|
this.intermedState = {};
|
||||||
|
|
||||||
this.prepareData();
|
this.prepareData();
|
||||||
this.reCalc();
|
this.reCalc();
|
||||||
this.refreshRenderer();
|
this.refreshRenderer();
|
||||||
this.refreshComponents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeChartArea() {
|
makeChartArea() {
|
||||||
@ -227,7 +238,7 @@ export default class BaseChart {
|
|||||||
this.drawArea = makeSVGGroup(
|
this.drawArea = makeSVGGroup(
|
||||||
this.svg,
|
this.svg,
|
||||||
this.type + '-chart',
|
this.type + '-chart',
|
||||||
`translate(${this.translateX}, ${this.translateY})`
|
`translate(${this.translateXLeft}, ${this.translateY})`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,7 +256,6 @@ export default class BaseChart {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.intermedState = this.calcIntermedState();
|
this.intermedState = this.calcIntermedState();
|
||||||
this.refreshComponents();
|
|
||||||
this.animateComponents();
|
this.animateComponents();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.renderComponents();
|
this.renderComponents();
|
||||||
@ -254,22 +264,15 @@ export default class BaseChart {
|
|||||||
// (opt, should not redraw if still in animate?)
|
// (opt, should not redraw if still in animate?)
|
||||||
}
|
}
|
||||||
|
|
||||||
calcIntermedState() {}
|
calcIntermedState() {
|
||||||
|
this.intermedState = {};
|
||||||
|
}
|
||||||
|
|
||||||
// convenient component array abstractions
|
// convenient component array abstractions
|
||||||
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
|
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
|
||||||
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
|
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
|
||||||
renderComponents() { this.components.forEach(c => c.render()); }
|
renderComponents() { this.components.forEach(c => c.render()); }
|
||||||
animateComponents() { this.components.forEach(c => c.animate()); }
|
animateComponents() { this.components.forEach(c => c.animate()); }
|
||||||
refreshComponents() {
|
|
||||||
let args = {
|
|
||||||
chartState: this.state,
|
|
||||||
oldChartState: this.oldState,
|
|
||||||
intermedState: this.intermedState,
|
|
||||||
chartRenderer: this.renderer
|
|
||||||
};
|
|
||||||
this.components.forEach(c => c.refresh(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLegend() {}
|
renderLegend() {}
|
||||||
|
|
||||||
|
|||||||
100
src/js/charts/MultiAxisChart.js
Normal file
100
src/js/charts/MultiAxisChart.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import AxisChart from './AxisChart';
|
||||||
|
import { Y_AXIS_MARGIN } from '../utils/margins';
|
||||||
|
import { ChartComponent } from '../objects/ChartComponent';
|
||||||
|
|
||||||
|
export default class MultiAxisChart extends AxisChart {
|
||||||
|
constructor(args) {
|
||||||
|
super(args);
|
||||||
|
this.type = 'multiaxis';
|
||||||
|
this.unitType = args.unitType || 'line';
|
||||||
|
this.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
setHorizontalMargin() {
|
||||||
|
let noOfLeftAxes = this.data.datasets.filter(d => d.axisPosition === 'left').length;
|
||||||
|
this.translateXLeft = (noOfLeftAxes) * Y_AXIS_MARGIN;
|
||||||
|
this.translateXRight = (this.data.datasets.length - noOfLeftAxes) * Y_AXIS_MARGIN || Y_AXIS_MARGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareYAxis() {
|
||||||
|
this.state.yAxes = [];
|
||||||
|
let sets = this.state.datasets;
|
||||||
|
// let axesLeft = sets.filter(d => d.axisPosition === 'left');
|
||||||
|
// let axesRight = sets.filter(d => d.axisPosition === 'right');
|
||||||
|
// let axesNone = sets.filter(d => !d.axisPosition ||
|
||||||
|
// !['left', 'right'].includes(d.axisPosition));
|
||||||
|
|
||||||
|
let leftCount = 0, rightCount = 0;
|
||||||
|
|
||||||
|
sets.forEach((d, i) => {
|
||||||
|
this.state.yAxes.push({
|
||||||
|
position: d.axisPosition,
|
||||||
|
color: d.color,
|
||||||
|
dataValues: d.values,
|
||||||
|
index: d.axisPosition === 'left' ? leftCount++ : rightCount++
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
this.config.xAxisMode = args.xAxisMode || 'tick';
|
||||||
|
this.config.yAxisMode = args.yAxisMode || 'span';
|
||||||
|
}
|
||||||
|
|
||||||
|
// setUnitWidthAndXOffset() {
|
||||||
|
// this.state.unitWidth = this.width/(this.state.datasetLength);
|
||||||
|
// this.state.xOffset = this.state.unitWidth/2;
|
||||||
|
// }
|
||||||
|
|
||||||
|
configUnits() {
|
||||||
|
this.state.unitArgs = {
|
||||||
|
type: 'bar',
|
||||||
|
args: {
|
||||||
|
spaceWidth: this.state.unitWidth/2,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setYAxis() {
|
||||||
|
this.state.yAxes.map(yAxis => {
|
||||||
|
// console.log(yAxis);
|
||||||
|
this.calcYAxisParameters(yAxis, yAxis.dataValues, this.unitType === 'line');
|
||||||
|
// console.log(yAxis);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupYAxesComponents() {
|
||||||
|
this.yAxesComponents = this.state.yAxes.map((e, i) => {
|
||||||
|
return new ChartComponent({
|
||||||
|
layerClass: 'y axis y-axis-' + i,
|
||||||
|
make: () => {
|
||||||
|
let d = this.state.yAxes[i];
|
||||||
|
this.renderer.setZeroline(d.zeroline);
|
||||||
|
let axis = d.positions.map((position, j) =>
|
||||||
|
this.renderer.yLine(position, d.labels[j], {
|
||||||
|
pos: d.position,
|
||||||
|
mode: 'tick',
|
||||||
|
offset: d.index * Y_AXIS_MARGIN,
|
||||||
|
stroke: this.colors[i]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
let guidePos = d.position === 'left'
|
||||||
|
? -1 * d.index * Y_AXIS_MARGIN
|
||||||
|
: this.width + d.index * Y_AXIS_MARGIN;
|
||||||
|
|
||||||
|
axis.push(this.renderer.xLine(guidePos, '', {
|
||||||
|
pos:'top',
|
||||||
|
mode: 'span',
|
||||||
|
stroke: this.colors[i],
|
||||||
|
className: 'y-axis-guide'
|
||||||
|
}));
|
||||||
|
|
||||||
|
return axis;
|
||||||
|
},
|
||||||
|
animate: () => {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,31 +5,21 @@ export class ChartComponent {
|
|||||||
layerClass = '',
|
layerClass = '',
|
||||||
layerTransform = '',
|
layerTransform = '',
|
||||||
make,
|
make,
|
||||||
argsKeys,
|
|
||||||
animate
|
animate
|
||||||
}) {
|
}) {
|
||||||
this.layerClass = layerClass; // 'y axis'
|
this.layerClass = layerClass; // 'y axis'
|
||||||
this.layerTransform = layerTransform;
|
this.layerTransform = layerTransform;
|
||||||
this.make = make;
|
this.make = make;
|
||||||
this.argsKeys = argsKeys;//['yAxisPositions', 'yAxisLabels'];
|
|
||||||
this.animate = animate;
|
this.animate = animate;
|
||||||
|
|
||||||
this.layer = undefined;
|
this.layer = undefined;
|
||||||
this.store = []; //[[]] depends on indexed
|
this.store = []; //[[]] depends on indexed
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh(args) {
|
refresh(args) {}
|
||||||
this.chartState = args.chartState;
|
|
||||||
this.oldChartState = args.oldChartState;
|
|
||||||
this.intermedState = args.intermedState;
|
|
||||||
|
|
||||||
this.chartRenderer = args.chartRenderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let args = this.argsKeys.map(key => this.chartState[key]);
|
this.store = this.make();
|
||||||
args.unshift(this.chartRenderer);
|
|
||||||
this.store = this.make(...args);
|
|
||||||
|
|
||||||
this.layer.textContent = '';
|
this.layer.textContent = '';
|
||||||
this.store.forEach(element => {
|
this.store.forEach(element => {
|
||||||
@ -79,7 +69,6 @@ export class IndexedChartComponent extends ChartComponent {
|
|||||||
// i.e.: [ [[0,0,0], [1,1,1]], ... ]
|
// i.e.: [ [[0,0,0], [1,1,1]], ... ]
|
||||||
for(var i = 0; i < this.totalIndices; i++) {
|
for(var i = 0; i < this.totalIndices; i++) {
|
||||||
let args = datasetArrays.map(datasetArray => datasetArray[i]);
|
let args = datasetArrays.map(datasetArray => datasetArray[i]);
|
||||||
args.unshift(this.chartRenderer);
|
|
||||||
|
|
||||||
args.push(i);
|
args.push(i);
|
||||||
args.push(this.totalIndices);
|
args.push(this.totalIndices);
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { getBarHeightAndYAttr } from './draw-utils';
|
|||||||
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;
|
||||||
|
const BASE_LINE_COLOR = '#dadada';
|
||||||
|
|
||||||
function $(expr, con) {
|
function $(expr, con) {
|
||||||
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
|
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
|
||||||
@ -138,20 +139,22 @@ export function makeText(className, x, y, content) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeVertXLine(x, label, totalHeight, mode, stroke='#dadada') {
|
export function makeVertLine(x, label, y1, y2, options={}) {
|
||||||
let height = mode === 'span' ? -1 * AXIS_TICK_LENGTH : totalHeight;
|
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
|
|
||||||
let l = createSVG('line', {
|
let l = createSVG('line', {
|
||||||
|
className: 'line-vertical ' + options.className,
|
||||||
x1: 0,
|
x1: 0,
|
||||||
x2: 0,
|
x2: 0,
|
||||||
y1: totalHeight + AXIS_TICK_LENGTH,
|
y1: y1,
|
||||||
y2: height,
|
y2: y2,
|
||||||
stroke: stroke
|
styles: {
|
||||||
|
stroke: options.stroke
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let text = createSVG('text', {
|
let text = createSVG('text', {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: totalHeight + AXIS_TICK_LENGTH + LABEL_MARGIN,
|
y: y1 > y2 ? y1 + LABEL_MARGIN : y1 - LABEL_MARGIN - FONT_SIZE,
|
||||||
dy: FONT_SIZE + 'px',
|
dy: FONT_SIZE + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': 'middle',
|
'text-anchor': 'middle',
|
||||||
@ -168,50 +171,34 @@ export function makeVertXLine(x, label, totalHeight, mode, stroke='#dadada') {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeHoriYLine(y, label, totalWidth, mode, pos='left') {
|
export function makeHoriLine(y, label, x1, x2, options={}) {
|
||||||
let lineType = '';
|
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
let w2 = mode === 'span' ? totalWidth + AXIS_TICK_LENGTH : 0;
|
if(!options.lineType) options.lineType = '';
|
||||||
|
let className = 'line-horizontal ' + options.className +
|
||||||
// temp : works correctly
|
(options.lineType === "dashed" ? "dashed": "");
|
||||||
let x1, x2, textX, anchor;
|
|
||||||
if(mode === 'tick') {
|
|
||||||
if(pos === 'right') {
|
|
||||||
x1 = totalWidth;
|
|
||||||
x2 = totalWidth + AXIS_TICK_LENGTH;
|
|
||||||
textX = totalWidth + AXIS_TICK_LENGTH + LABEL_MARGIN;
|
|
||||||
anchor = 'start';
|
|
||||||
} else {
|
|
||||||
x1 = -1 * AXIS_TICK_LENGTH;
|
|
||||||
x2 = w2;
|
|
||||||
textX = -1 * (LABEL_MARGIN + AXIS_TICK_LENGTH);
|
|
||||||
anchor = 'end';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
x1 = -1 * AXIS_TICK_LENGTH;
|
|
||||||
x2 = w2;
|
|
||||||
textX = -1 * (LABEL_MARGIN + AXIS_TICK_LENGTH);
|
|
||||||
anchor = 'end';
|
|
||||||
}
|
|
||||||
|
|
||||||
let l = createSVG('line', {
|
let l = createSVG('line', {
|
||||||
className: lineType === "dashed" ? "dashed": "",
|
className: className,
|
||||||
x1: x1,
|
x1: x1,
|
||||||
x2: x2,
|
x2: x2,
|
||||||
y1: 0,
|
y1: 0,
|
||||||
y2: 0
|
y2: 0,
|
||||||
|
styles: {
|
||||||
|
stroke: options.stroke
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let text = createSVG('text', {
|
let text = createSVG('text', {
|
||||||
x: textX,
|
x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN,
|
||||||
y: 0,
|
y: 0,
|
||||||
dy: (FONT_SIZE / 2 - 2) + 'px',
|
dy: (FONT_SIZE / 2 - 2) + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': anchor,
|
'text-anchor': x1 < x2 ? 'end' : 'start',
|
||||||
innerHTML: label+""
|
innerHTML: label+""
|
||||||
});
|
});
|
||||||
|
|
||||||
let line = createSVG('g', {
|
let line = createSVG('g', {
|
||||||
transform: `translate(0, ${y})`,
|
transform: `translate(0, ${ y })`,
|
||||||
'stroke-opacity': 1
|
'stroke-opacity': 1
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -239,6 +226,10 @@ export class AxisChartRenderer {
|
|||||||
this.yAxisMode = state.yAxisMode;
|
this.yAxisMode = state.yAxisMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setZeroline(zeroLine) {
|
||||||
|
this.zeroLine = zeroLine;
|
||||||
|
}
|
||||||
|
|
||||||
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets, prevX, prevY) {
|
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets, prevX, prevY) {
|
||||||
|
|
||||||
let totalWidth = this.unitWidth - args.spaceWidth;
|
let totalWidth = this.unitWidth - args.spaceWidth;
|
||||||
@ -275,14 +266,63 @@ export class AxisChartRenderer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// temp: stroke
|
xLine(x, label, options={}) {
|
||||||
xLine(x, label, pos='bottom', stroke='', mode=this.xAxisMode) {
|
if(!options.pos) options.pos = 'bottom';
|
||||||
|
if(!options.offset) options.offset = 0;
|
||||||
|
if(!options.mode) options.mode = this.xAxisMode;
|
||||||
|
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
|
if(!options.className) options.className = '';
|
||||||
|
|
||||||
// Draw X axis line in span/tick mode with optional label
|
// Draw X axis line in span/tick mode with optional label
|
||||||
return makeVertXLine(x, label, this.totalHeight, mode);
|
// y2(span)
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// x line |
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// ---------------------+-- y2(tick)
|
||||||
|
// |
|
||||||
|
// y1
|
||||||
|
|
||||||
|
let y1 = this.totalHeight + AXIS_TICK_LENGTH;
|
||||||
|
let y2 = options.mode === 'span' ? -1 * AXIS_TICK_LENGTH : this.totalHeight;
|
||||||
|
|
||||||
|
if(options.mode === 'tick' && options.pos === 'top') {
|
||||||
|
// top axis ticks
|
||||||
|
y1 = -1 * AXIS_TICK_LENGTH;
|
||||||
|
y2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeVertLine(x, label, y1, y2, {
|
||||||
|
stroke: options.stroke,
|
||||||
|
className: options.className
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
yLine(y, label, pos='left', mode=this.yAxisMode) {
|
yLine(y, label, options={}) {
|
||||||
return makeHoriYLine(y, label, this.totalWidth, mode, pos);
|
if(!options.pos) options.pos = 'left';
|
||||||
|
if(!options.offset) options.offset = 0;
|
||||||
|
if(!options.mode) options.mode = this.yAxisMode;
|
||||||
|
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
|
if(!options.className) options.className = '';
|
||||||
|
|
||||||
|
let x1 = -1 * AXIS_TICK_LENGTH;
|
||||||
|
let x2 = options.mode === 'span' ? this.totalWidth + AXIS_TICK_LENGTH : 0;
|
||||||
|
|
||||||
|
if(options.mode === 'tick' && options.pos === 'right') {
|
||||||
|
x1 = this.totalWidth + AXIS_TICK_LENGTH
|
||||||
|
x2 = this.totalWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset = options.pos === 'left' ? -1 * options.offset : options.offset;
|
||||||
|
|
||||||
|
x1 += options.offset;
|
||||||
|
x2 += options.offset;
|
||||||
|
|
||||||
|
return makeHoriLine(y, label, x1, x2, {
|
||||||
|
stroke: options.stroke,
|
||||||
|
className: options.className
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
xMarker() {}
|
xMarker() {}
|
||||||
|
|||||||
1
src/js/utils/margins.js
Normal file
1
src/js/utils/margins.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const Y_AXIS_MARGIN = 60;
|
||||||
@ -4,6 +4,12 @@
|
|||||||
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
|
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
|
||||||
"Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
"Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||||||
|
|
||||||
|
.multiaxis-chart {
|
||||||
|
.line-horizontal, .y-axis-guide {
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.graph-focus-margin {
|
.graph-focus-margin {
|
||||||
margin: 0px 5%;
|
margin: 0px 5%;
|
||||||
}
|
}
|
||||||
@ -53,9 +59,9 @@
|
|||||||
.axis, .chart-label {
|
.axis, .chart-label {
|
||||||
fill: #555b51;
|
fill: #555b51;
|
||||||
// temp commented
|
// temp commented
|
||||||
// line {
|
line {
|
||||||
// stroke: #dadada;
|
stroke: #dadada;
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
.percentage-graph {
|
.percentage-graph {
|
||||||
.progress {
|
.progress {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user