[data] begin with zero data, bars rendered
This commit is contained in:
parent
8d2246e6c3
commit
8452e909bb
516
dist/frappe-charts.esm.js
vendored
516
dist/frappe-charts.esm.js
vendored
@ -232,6 +232,19 @@ function getStringWidth(string, charWidth) {
|
|||||||
return (string+"").length * charWidth;
|
return (string+"").length * charWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBarHeightAndYAttr(yTop, zeroLine) {
|
||||||
|
let height, y;
|
||||||
|
if (yTop < zeroLine) {
|
||||||
|
height = zeroLine - yTop;
|
||||||
|
y = yTop;
|
||||||
|
} else {
|
||||||
|
height = yTop - zeroLine;
|
||||||
|
y = zeroLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [height, y];
|
||||||
|
}
|
||||||
|
|
||||||
function equilizeNoOfElements(array1, array2,
|
function equilizeNoOfElements(array1, array2,
|
||||||
extra_count=array2.length - array1.length) {
|
extra_count=array2.length - array1.length) {
|
||||||
|
|
||||||
@ -347,26 +360,6 @@ function createSVG(tag, o) {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderVerticalGradient(svgDefElem, gradientId) {
|
|
||||||
return createSVG('linearGradient', {
|
|
||||||
inside: svgDefElem,
|
|
||||||
id: gradientId,
|
|
||||||
x1: 0,
|
|
||||||
x2: 0,
|
|
||||||
y1: 0,
|
|
||||||
y2: 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setGradientStop(gradElem, offset, color, opacity) {
|
|
||||||
return createSVG('stop', {
|
|
||||||
'inside': gradElem,
|
|
||||||
'style': `stop-color: ${color}`,
|
|
||||||
'offset': offset,
|
|
||||||
'stop-opacity': opacity
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeSVGContainer(parent, className, width, height) {
|
function makeSVGContainer(parent, className, width, height) {
|
||||||
return createSVG('svg', {
|
return createSVG('svg', {
|
||||||
className: className,
|
className: className,
|
||||||
@ -390,7 +383,13 @@ function makeSVGGroup(parent, className, transform='') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wrapInSVGGroup(elements, className='') {
|
||||||
|
let g = createSVG('g', {
|
||||||
|
className: className
|
||||||
|
});
|
||||||
|
elements.forEach(e => g.appendChild(e));
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
function makePath(pathStr, className='', stroke='none', fill='none') {
|
function makePath(pathStr, className='', stroke='none', fill='none') {
|
||||||
return createSVG('path', {
|
return createSVG('path', {
|
||||||
@ -403,20 +402,7 @@ function makePath(pathStr, className='', stroke='none', fill='none') {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeGradient(svgDefElem, color, lighter = false) {
|
|
||||||
let gradientId ='path-fill-gradient' + '-' + color;
|
|
||||||
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
|
||||||
let opacities = [1, 0.6, 0.2];
|
|
||||||
if(lighter) {
|
|
||||||
opacities = [0.4, 0.2, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
setGradientStop(gradientDef, "0%", color, opacities[0]);
|
|
||||||
setGradientStop(gradientDef, "50%", color, opacities[1]);
|
|
||||||
setGradientStop(gradientDef, "100%", color, opacities[2]);
|
|
||||||
|
|
||||||
return gradientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeHeatSquare(className, x, y, size, fill='none', data={}) {
|
function makeHeatSquare(className, x, y, size, fill='none', data={}) {
|
||||||
let args = {
|
let args = {
|
||||||
@ -638,6 +624,37 @@ function yRegion(y1, y2, width, label) {
|
|||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function datasetBar(x, yTop, width, color, label='', index=0, offset=0, meta={}) {
|
||||||
|
let [height, y] = getBarHeightAndYAttr(yTop, meta.zeroLine);
|
||||||
|
// console.log(yTop, meta.zeroLine, y, offset);
|
||||||
|
|
||||||
|
let rect = createSVG('rect', {
|
||||||
|
className: `bar mini`,
|
||||||
|
style: `fill: ${color}`,
|
||||||
|
'data-point-index': index,
|
||||||
|
x: x - meta.barsWidth/2,
|
||||||
|
y: y - offset,
|
||||||
|
width: width,
|
||||||
|
height: height || meta.minHeight
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!label && !label.length) {
|
||||||
|
return rect;
|
||||||
|
} else {
|
||||||
|
let text = createSVG('text', {
|
||||||
|
className: 'data-point-value',
|
||||||
|
x: x,
|
||||||
|
y: y - offset,
|
||||||
|
dy: (FONT_SIZE / 2 * -1) + 'px',
|
||||||
|
'font-size': FONT_SIZE + 'px',
|
||||||
|
'text-anchor': 'middle',
|
||||||
|
innerHTML: label
|
||||||
|
});
|
||||||
|
|
||||||
|
return wrapInSVGGroup([rect, text]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const PRESET_COLOR_MAP = {
|
const PRESET_COLOR_MAP = {
|
||||||
'light-blue': '#7cd6fd',
|
'light-blue': '#7cd6fd',
|
||||||
'blue': '#5e64ff',
|
'blue': '#5e64ff',
|
||||||
@ -872,13 +889,15 @@ class BaseChart {
|
|||||||
this.title = title;
|
this.title = title;
|
||||||
this.subtitle = subtitle;
|
this.subtitle = subtitle;
|
||||||
this.argHeight = height;
|
this.argHeight = height;
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
this.isNavigable = isNavigable;
|
this.isNavigable = isNavigable;
|
||||||
if(this.isNavigable) {
|
if(this.isNavigable) {
|
||||||
this.currentIndex = 0;
|
this.currentIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data = this.prepareData(data);
|
this.realData = this.prepareData(data);
|
||||||
|
this.data = this.prepareFirstData(this.realData);
|
||||||
this.colors = [];
|
this.colors = [];
|
||||||
this.config = {};
|
this.config = {};
|
||||||
this.state = {};
|
this.state = {};
|
||||||
@ -990,6 +1009,7 @@ class BaseChart {
|
|||||||
this.calcWidth();
|
this.calcWidth();
|
||||||
this.makeChartArea();
|
this.makeChartArea();
|
||||||
|
|
||||||
|
this.calc();
|
||||||
this.initComponents(); // Only depend on the drawArea made in makeChartArea
|
this.initComponents(); // Only depend on the drawArea made in makeChartArea
|
||||||
|
|
||||||
this.setupComponents();
|
this.setupComponents();
|
||||||
@ -1002,6 +1022,7 @@ class BaseChart {
|
|||||||
|
|
||||||
// TODO: remove timeout and decrease post animate time in chart component
|
// TODO: remove timeout and decrease post animate time in chart component
|
||||||
if(init) {
|
if(init) {
|
||||||
|
this.data = this.realData;
|
||||||
setTimeout(() => {this.update();}, 1000);
|
setTimeout(() => {this.update();}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1029,10 +1050,10 @@ class BaseChart {
|
|||||||
|
|
||||||
calc() {} // builds state
|
calc() {} // builds state
|
||||||
|
|
||||||
render(animate=true) {
|
render(components=this.components, animate=true) {
|
||||||
// Can decouple to this.refreshComponents() first to save animation timeout
|
// Can decouple to this.refreshComponents() first to save animation timeout
|
||||||
let elementsToAnimate = [];
|
let elementsToAnimate = [];
|
||||||
this.components.forEach(c => {
|
components.forEach(c => {
|
||||||
elementsToAnimate = elementsToAnimate.concat(c.update(animate));
|
elementsToAnimate = elementsToAnimate.concat(c.update(animate));
|
||||||
});
|
});
|
||||||
if(elementsToAnimate.length > 0) {
|
if(elementsToAnimate.length > 0) {
|
||||||
@ -1138,8 +1159,10 @@ class BaseChart {
|
|||||||
|
|
||||||
const Y_AXIS_MARGIN = 60;
|
const Y_AXIS_MARGIN = 60;
|
||||||
|
|
||||||
|
const MIN_BAR_PERCENT_HEIGHT = 0.01;
|
||||||
const DEFAULT_AXIS_CHART_TYPE = 'line';
|
const DEFAULT_AXIS_CHART_TYPE = 'line';
|
||||||
|
const AXIS_DATASET_CHART_TYPES = ['line', 'bar'];
|
||||||
|
const BAR_CHART_SPACE_RATIO = 0.5;
|
||||||
|
|
||||||
function dataPrep(data, type) {
|
function dataPrep(data, type) {
|
||||||
data.labels = data.labels || [];
|
data.labels = data.labels || [];
|
||||||
@ -1176,23 +1199,60 @@ function dataPrep(data, type) {
|
|||||||
// Set labels
|
// Set labels
|
||||||
//
|
//
|
||||||
|
|
||||||
// Set index
|
|
||||||
d.index = i;
|
|
||||||
|
|
||||||
// Set type
|
// Set type
|
||||||
if(!d.chartType ) {
|
if(!d.chartType ) {
|
||||||
d.chartType = type || DEFAULT_AXIS_CHART_TYPE;
|
if(!AXIS_DATASET_CHART_TYPES.includes(type)) type === DEFAULT_AXIS_CHART_TYPE;
|
||||||
|
d.chartType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Markers
|
// Markers
|
||||||
|
|
||||||
// Regions
|
// Regions
|
||||||
// Set start and end
|
// data.yRegions = data.yRegions || [];
|
||||||
|
if(data.yRegions) {
|
||||||
|
data.yRegions.map(d => {
|
||||||
|
if(d.end < d.start) {
|
||||||
|
[d.start, d.end] = [d.end, start];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function zeroDataPrep(realData) {
|
||||||
|
let datasetLength = realData.labels.length;
|
||||||
|
let zeroArray = new Array(datasetLength).fill(0);
|
||||||
|
|
||||||
|
let zeroData = {
|
||||||
|
labels: realData.labels,
|
||||||
|
datasets: realData.datasets.map(d => {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
values: zeroArray,
|
||||||
|
chartType: d.chartType
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
yRegions: [
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
label: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yMarkers: [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
return zeroData;
|
||||||
|
}
|
||||||
|
|
||||||
class ChartComponent$1 {
|
class ChartComponent$1 {
|
||||||
constructor({
|
constructor({
|
||||||
layerClass = '',
|
layerClass = '',
|
||||||
@ -1215,8 +1275,9 @@ class ChartComponent$1 {
|
|||||||
|
|
||||||
this.store = [];
|
this.store = [];
|
||||||
|
|
||||||
this.layerClass = typeof(layerClass) === 'function'
|
this.layerClass = layerClass;
|
||||||
? layerClass() : layerClass;
|
this.layerClass = typeof(this.layerClass) === 'function'
|
||||||
|
? this.layerClass() : this.layerClass;
|
||||||
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
@ -1394,81 +1455,61 @@ let componentConfigs = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
barGraph: {
|
barGraph: {
|
||||||
// opt:[
|
layerClass: function() { return 'dataset-units dataset-' + this.constants.index; },
|
||||||
// 'barGraph',
|
|
||||||
// this.drawArea,
|
|
||||||
// {
|
|
||||||
// controller: barController,
|
|
||||||
// index: index,
|
|
||||||
// color: this.colors[index],
|
|
||||||
// valuesOverPoints: this.valuesOverPoints,
|
|
||||||
// stacked: this.barOptions && this.barOptions.stacked,
|
|
||||||
// spaceRatio: 0.5,
|
|
||||||
// minHeight: this.height * MIN_BAR_PERCENT_HEIGHT
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
|
|
||||||
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
|
|
||||||
|
|
||||||
// },
|
|
||||||
// function() {
|
|
||||||
// let s = this.state;
|
|
||||||
// return {
|
|
||||||
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
|
|
||||||
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
|
|
||||||
// positions: s.xAxisPositions,
|
|
||||||
// labels: s.xAxisLabels,
|
|
||||||
// }
|
|
||||||
// }.bind(this)
|
|
||||||
// ],
|
|
||||||
layerClass() { return 'y-regions' + this.constants.index; },
|
|
||||||
makeElements(data) {
|
makeElements(data) {
|
||||||
let c = this.constants;
|
let c = this.constants;
|
||||||
return data.yPositions.map((y, j) =>
|
return data.yPositions.map((y, j) => {
|
||||||
barController.draw(
|
// console.log(data.cumulativeYPos, data.cumulativeYPos[j]);
|
||||||
|
return datasetBar(
|
||||||
data.xPositions[j],
|
data.xPositions[j],
|
||||||
y,
|
y,
|
||||||
color,
|
c.barWidth,
|
||||||
|
c.color,
|
||||||
(c.valuesOverPoints ? (c.stacked ? data.cumulativeYs[j] : data.values[j]) : ''),
|
(c.valuesOverPoints ? (c.stacked ? data.cumulativeYs[j] : data.values[j]) : ''),
|
||||||
j,
|
j,
|
||||||
y - (data.cumulativePositions ? data.cumulativePositions[j] : y)
|
y - (c.stacked ? data.cumulativeYPos[j] : y),
|
||||||
|
{
|
||||||
|
zeroLine: c.zeroLine,
|
||||||
|
barsWidth: c.barsWidth,
|
||||||
|
minHeight: c.minHeight
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
});
|
||||||
},
|
},
|
||||||
postMake() {
|
postMake() {
|
||||||
if((!this.constants.stacked)) {
|
if((!this.constants.stacked)) {
|
||||||
this.layer.setAttribute('transform',
|
this.layer.setAttribute('transform',
|
||||||
`translate(${unitRenderer.consts.width * index}, 0)`);
|
`translate(${this.constants.width * this.constants.index}, 0)`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
animateElements(newData) {
|
animateElements(newData) {
|
||||||
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
|
// [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
|
||||||
|
|
||||||
let newPos = newData.map(d => d.end);
|
// let newPos = newData.map(d => d.end);
|
||||||
let newLabels = newData.map(d => d.label);
|
// let newLabels = newData.map(d => d.label);
|
||||||
let newStarts = newData.map(d => d.start);
|
// let newStarts = newData.map(d => d.start);
|
||||||
|
|
||||||
let oldPos = this.oldData.map(d => d.end);
|
// let oldPos = this.oldData.map(d => d.end);
|
||||||
let oldLabels = this.oldData.map(d => d.label);
|
// let oldLabels = this.oldData.map(d => d.label);
|
||||||
let oldStarts = this.oldData.map(d => d.start);
|
// let oldStarts = this.oldData.map(d => d.start);
|
||||||
|
|
||||||
this.render(oldPos.map((pos, i) => {
|
// this.render(oldPos.map((pos, i) => {
|
||||||
return {
|
// return {
|
||||||
start: oldStarts[i],
|
// start: oldStarts[i],
|
||||||
end: oldPos[i],
|
// end: oldPos[i],
|
||||||
label: newLabels[i]
|
// label: newLabels[i]
|
||||||
}
|
// }
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
let animateElements = [];
|
// let animateElements = [];
|
||||||
|
|
||||||
this.store.map((rectGroup, i) => {
|
// this.store.map((rectGroup, i) => {
|
||||||
animateElements = animateElements.concat(animateRegion(
|
// animateElements = animateElements.concat(animateRegion(
|
||||||
rectGroup, newStarts[i], newPos[i], oldPos[i]
|
// rectGroup, newStarts[i], newPos[i], oldPos[i]
|
||||||
));
|
// ));
|
||||||
});
|
// });
|
||||||
|
|
||||||
return animateElements;
|
// return animateElements;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1486,101 +1527,6 @@ function getComponent(name, constants, getData) {
|
|||||||
return new ChartComponent$1(config);
|
return new ChartComponent$1(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPaths(yList, xList, color, heatline=false, regionFill=false) {
|
|
||||||
let pointsList = yList.map((y, i) => (xList[i] + ',' + y));
|
|
||||||
let pointsStr = pointsList.join("L");
|
|
||||||
let path = makePath("M"+pointsStr, 'line-graph-path', color);
|
|
||||||
|
|
||||||
// HeatLine
|
|
||||||
if(heatline) {
|
|
||||||
let gradient_id = makeGradient(this.svgDefs, color);
|
|
||||||
path.style.stroke = `url(#${gradient_id})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let components = [path];
|
|
||||||
|
|
||||||
// Region
|
|
||||||
if(regionFill) {
|
|
||||||
let gradient_id_region = makeGradient(this.svgDefs, color, true);
|
|
||||||
|
|
||||||
let zeroLine = this.state.yAxis.zeroLine;
|
|
||||||
// TODO: use zeroLine OR minimum
|
|
||||||
let pathStr = "M" + `0,${zeroLine}L` + pointsStr + `L${this.width},${zeroLine}`;
|
|
||||||
components.push(makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id_region})`));
|
|
||||||
}
|
|
||||||
|
|
||||||
return components;
|
|
||||||
}
|
|
||||||
|
|
||||||
// class BarChart extends AxisChart {
|
|
||||||
// constructor(args) {
|
|
||||||
// super(args);
|
|
||||||
// this.type = 'bar';
|
|
||||||
// this.setup();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// configure(args) {
|
|
||||||
// super.configure(args);
|
|
||||||
// this.config.xAxisMode = args.xAxisMode || 'tick';
|
|
||||||
// this.config.yAxisMode = args.yAxisMode || 'span';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // =================================
|
|
||||||
|
|
||||||
// makeOverlay() {
|
|
||||||
// // Just make one out of the first element
|
|
||||||
// let index = this.xAxisLabels.length - 1;
|
|
||||||
// let unit = this.y[0].svg_units[index];
|
|
||||||
// this.setCurrentDataPoint(index);
|
|
||||||
|
|
||||||
// if(this.overlay) {
|
|
||||||
// this.overlay.parentNode.removeChild(this.overlay);
|
|
||||||
// }
|
|
||||||
// this.overlay = unit.cloneNode();
|
|
||||||
// this.overlay.style.fill = '#000000';
|
|
||||||
// this.overlay.style.opacity = '0.4';
|
|
||||||
// this.drawArea.appendChild(this.overlay);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// bindOverlay() {
|
|
||||||
// // on event, update overlay
|
|
||||||
// this.parent.addEventListener('data-select', (e) => {
|
|
||||||
// this.update_overlay(e.svg_unit);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// bind_units(units_array) {
|
|
||||||
// units_array.map(unit => {
|
|
||||||
// unit.addEventListener('click', () => {
|
|
||||||
// let index = unit.getAttribute('data-point-index');
|
|
||||||
// this.setCurrentDataPoint(index);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// update_overlay(unit) {
|
|
||||||
// let attributes = [];
|
|
||||||
// Object.keys(unit.attributes).map(index => {
|
|
||||||
// attributes.push(unit.attributes[index]);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// attributes.filter(attr => attr.specified).map(attr => {
|
|
||||||
// this.overlay.setAttribute(attr.name, attr.nodeValue);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// this.overlay.style.fill = '#000000';
|
|
||||||
// this.overlay.style.opacity = '0.4';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onLeftArrow() {
|
|
||||||
// this.setCurrentDataPoint(this.currentIndex - 1);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onRightArrow() {
|
|
||||||
// this.setCurrentDataPoint(this.currentIndex + 1);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
function normalize(x) {
|
function normalize(x) {
|
||||||
// Calculates mantissa and exponent of a number
|
// Calculates mantissa and exponent of a number
|
||||||
// Returns normalized number and exponent
|
// Returns normalized number and exponent
|
||||||
@ -1757,17 +1703,7 @@ function getZeroIndex(yPts) {
|
|||||||
return zeroIndex;
|
return zeroIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRealIntervals(max, noOfIntervals, min = 0, asc = 1) {
|
|
||||||
let range = max - min;
|
|
||||||
let part = range * 1.0 / noOfIntervals;
|
|
||||||
let intervals = [];
|
|
||||||
|
|
||||||
for(var i = 0; i <= noOfIntervals; i++) {
|
|
||||||
intervals.push(min + part * i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return asc ? intervals : intervals.reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIntervalSize(orderedArray) {
|
function getIntervalSize(orderedArray) {
|
||||||
return orderedArray[1] - orderedArray[0];
|
return orderedArray[1] - orderedArray[0];
|
||||||
@ -1777,6 +1713,10 @@ function getValueRange(orderedArray) {
|
|||||||
return orderedArray[orderedArray.length-1] - orderedArray[0];
|
return orderedArray[orderedArray.length-1] - orderedArray[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scale(val, yAxis) {
|
||||||
|
return floatTwo(yAxis.zeroLine - val * yAxis.scaleMultiplier)
|
||||||
|
}
|
||||||
|
|
||||||
function calcDistribution(values, distributionSize) {
|
function calcDistribution(values, distributionSize) {
|
||||||
// Assume non-negative values,
|
// Assume non-negative values,
|
||||||
// implying distribution minimum at zero
|
// implying distribution minimum at zero
|
||||||
@ -1805,19 +1745,19 @@ class AxisChart extends BaseChart {
|
|||||||
this.valuesOverPoints = args.valuesOverPoints;
|
this.valuesOverPoints = args.valuesOverPoints;
|
||||||
this.formatTooltipY = args.formatTooltipY;
|
this.formatTooltipY = args.formatTooltipY;
|
||||||
this.formatTooltipX = args.formatTooltipX;
|
this.formatTooltipX = args.formatTooltipX;
|
||||||
this.barOptions = args.barOptions;
|
this.barOptions = args.barOptions || {};
|
||||||
this.lineOptions = args.lineOptions;
|
this.lineOptions = args.lineOptions || {};
|
||||||
this.type = args.type || 'line';
|
this.type = args.type || 'line';
|
||||||
|
|
||||||
this.xAxisMode = args.xAxisMode || 'span';
|
this.xAxisMode = args.xAxisMode || 'span';
|
||||||
this.yAxisMode = args.yAxisMode || 'span';
|
this.yAxisMode = args.yAxisMode || 'span';
|
||||||
|
|
||||||
this.zeroLine = this.height;
|
this.zeroLine = this.height;
|
||||||
this.setTrivialState();
|
// this.setTrivialState();
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(args) {
|
configure(args) {3;
|
||||||
super.configure();
|
super.configure();
|
||||||
|
|
||||||
// TODO: set in options and use
|
// TODO: set in options and use
|
||||||
@ -1826,38 +1766,6 @@ class AxisChart extends BaseChart {
|
|||||||
this.config.yAxisMode = args.yAxisMode;
|
this.config.yAxisMode = args.yAxisMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTrivialState() {
|
|
||||||
// Define data and stuff
|
|
||||||
let yTempPos = getRealIntervals(this.height, 4, 0, 0);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
xAxis: {
|
|
||||||
positions: [],
|
|
||||||
labels: [],
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
positions: yTempPos,
|
|
||||||
labels: yTempPos.map(d => ""),
|
|
||||||
},
|
|
||||||
yRegions: [
|
|
||||||
{
|
|
||||||
start: this.height,
|
|
||||||
end: this.height,
|
|
||||||
label: ''
|
|
||||||
}
|
|
||||||
],
|
|
||||||
yMarkers: [
|
|
||||||
{
|
|
||||||
position: this.height,
|
|
||||||
label: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
this.calcWidth();
|
|
||||||
this.calcXPositions(this.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
setMargins() {
|
setMargins() {
|
||||||
super.setMargins();
|
super.setMargins();
|
||||||
this.translateXLeft = Y_AXIS_MARGIN;
|
this.translateXLeft = Y_AXIS_MARGIN;
|
||||||
@ -1868,10 +1776,12 @@ class AxisChart extends BaseChart {
|
|||||||
return dataPrep(data, this.type);
|
return dataPrep(data, this.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareFirstData(data=this.data) {
|
||||||
|
return zeroDataPrep(data);
|
||||||
|
}
|
||||||
|
|
||||||
calc() {
|
calc() {
|
||||||
|
|
||||||
this.calcXPositions();
|
this.calcXPositions();
|
||||||
|
|
||||||
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1905,61 +1815,60 @@ class AxisChart extends BaseChart {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.calcYUnits();
|
this.calcYUnits();
|
||||||
this.calcYMaximums();
|
this.calcYExtremes();
|
||||||
this.calcYRegions();
|
this.calcYRegions();
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYUnits() {
|
calcYUnits() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
this.data.datasets.map(d => {
|
let scaleAll = values => values.map(val => scale(val, s.yAxis));
|
||||||
d.positions = d.values.map(val =>
|
|
||||||
floatTwo(s.yAxis.zeroLine - val * s.yAxis.scaleMultiplier));
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.barOptions && this.barOptions.stacked) {
|
s.datasets = this.data.datasets.map((d, i) => {
|
||||||
this.data.datasets.map((d, i) => {
|
let values = d.values;
|
||||||
d.cumulativePositions = d.cumulativeYs.map(val =>
|
let cumulativeYs = d.cumulativeYs || [];
|
||||||
floatTwo(s.yAxis.zeroLine - val * s.yAxis.scaleMultiplier));
|
return {
|
||||||
});
|
name: d.name,
|
||||||
}
|
index: i,
|
||||||
|
chartType: d.chartType,
|
||||||
|
|
||||||
|
values: values,
|
||||||
|
yPositions: scaleAll(values),
|
||||||
|
|
||||||
|
cumulativeYs: cumulativeYs,
|
||||||
|
cumulativeYPos: scaleAll(cumulativeYs),
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYMaximums() {
|
calcYExtremes() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
if(this.barOptions && this.barOptions.stacked) {
|
if(this.barOptions && this.barOptions.stacked) {
|
||||||
s.yExtremes = this.data.datasets[this.data.datasets.length - 1].cumulativePositions;
|
s.yExtremes = s.datasets[s.datasets.length - 1].cumulativeYPos;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s.yExtremes = new Array(s.datasetLength).fill(9999);
|
s.yExtremes = new Array(s.datasetLength).fill(9999);
|
||||||
this.data.datasets.map((d, i) => {
|
s.datasets.map((d, i) => {
|
||||||
d.positions.map((pos, j) => {
|
d.yPositions.map((pos, j) => {
|
||||||
if(pos < s.yExtremes[j]) {
|
if(pos < s.yExtremes[j]) {
|
||||||
s.yExtremes[j] = pos;
|
s.yExtremes[j] = pos;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tooltip refresh should not be needed?
|
|
||||||
// this.chartWrapper.removeChild(this.tip.container);
|
|
||||||
// this.make_tooltip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYRegions() {
|
calcYRegions() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
if(this.data.yMarkers) {
|
if(this.data.yMarkers) {
|
||||||
this.state.yMarkers = this.data.yMarkers.map(d => {
|
this.state.yMarkers = this.data.yMarkers.map(d => {
|
||||||
d.position = floatTwo(s.yAxis.zeroLine - d.value * s.yAxis.scaleMultiplier);
|
d.position = scale(d.value, s.yAxis);
|
||||||
d.label += ': ' + d.value;
|
d.label += ': ' + d.value;
|
||||||
return d;
|
return d;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if(this.data.yRegions) {
|
if(this.data.yRegions) {
|
||||||
this.state.yRegions = this.data.yRegions.map(d => {
|
this.state.yRegions = this.data.yRegions.map(d => {
|
||||||
if(d.end < d.start) {
|
d.start = scale(d.start, s.yAxis);
|
||||||
[d.start, d.end] = [d.end, start];
|
d.end = scale(d.end, s.yAxis);
|
||||||
}
|
|
||||||
d.start = floatTwo(s.yAxis.zeroLine - d.start * s.yAxis.scaleMultiplier);
|
|
||||||
d.end = floatTwo(s.yAxis.zeroLine - d.end * s.yAxis.scaleMultiplier);
|
|
||||||
return d;
|
return d;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1982,6 +1891,9 @@ class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initComponents() {
|
initComponents() {
|
||||||
|
let s = this.state;
|
||||||
|
// console.log('this.state', Object.assign({}, this.state));
|
||||||
|
// console.log('this.state', this.state);
|
||||||
this.componentConfigs = [
|
this.componentConfigs = [
|
||||||
[
|
[
|
||||||
'yAxis',
|
'yAxis',
|
||||||
@ -2008,7 +1920,57 @@ class AxisChart extends BaseChart {
|
|||||||
pos: 'right'
|
pos: 'right'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
this.componentConfigs.map(args => {
|
||||||
|
args.push(
|
||||||
|
function() {
|
||||||
|
return this.state[args[0]];
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let barDatasets = this.state.datasets.filter(d => d.chartType === 'bar');
|
||||||
|
let lineDatasets = this.state.datasets.filter(d => d.chartType === 'line');
|
||||||
|
|
||||||
|
// console.log('barDatasets', barDatasets, this.state.datasets);
|
||||||
|
|
||||||
|
// Bars
|
||||||
|
let spaceRatio = this.barOptions.spaceRatio || BAR_CHART_SPACE_RATIO;
|
||||||
|
let barsWidth = s.unitWidth * (1 - spaceRatio);
|
||||||
|
let barWidth = barsWidth/(this.barOptions.stacked ? 1 : barDatasets.length);
|
||||||
|
|
||||||
|
let barsConfigs = barDatasets.map(d => {
|
||||||
|
let index = d.index;
|
||||||
|
return [
|
||||||
|
'barGraph',
|
||||||
|
{
|
||||||
|
index: index,
|
||||||
|
color: this.colors[index],
|
||||||
|
|
||||||
|
// same for all datasets
|
||||||
|
valuesOverPoints: this.valuesOverPoints,
|
||||||
|
minHeight: this.height * MIN_BAR_PERCENT_HEIGHT,
|
||||||
|
barsWidth: barsWidth,
|
||||||
|
barWidth: barWidth,
|
||||||
|
zeroLine: s.yAxis.zeroLine
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
let s = this.state;
|
||||||
|
let d = s.datasets[index];
|
||||||
|
return {
|
||||||
|
xPositions: s.xAxis.positions,
|
||||||
|
yPositions: d.yPositions,
|
||||||
|
cumulativeYPos: d.cumulativeYPos,
|
||||||
|
|
||||||
|
values: d.values,
|
||||||
|
cumulativeYs: d.cumulativeYs
|
||||||
|
};
|
||||||
|
}.bind(this)
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
let markerConfigs = [
|
||||||
[
|
[
|
||||||
'yMarkers',
|
'yMarkers',
|
||||||
{
|
{
|
||||||
@ -2017,17 +1979,23 @@ class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
markerConfigs.map(args => {
|
||||||
|
args.push(
|
||||||
|
function() {
|
||||||
|
return this.state[args[0]];
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.componentConfigs = this.componentConfigs.concat(barsConfigs, markerConfigs);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupComponents() {
|
setupComponents() {
|
||||||
let optionals = ['yMarkers', 'yRegions'];
|
let optionals = ['yMarkers', 'yRegions'];
|
||||||
this.components = new Map(this.componentConfigs
|
this.components = new Map(this.componentConfigs
|
||||||
.filter(args => !optionals.includes(args[0]) || this.data[args[0]])
|
.filter(args => !optionals.includes(args[0]) || this.state[args[0]] || args[0] === 'barGraph')
|
||||||
.map(args => {
|
.map(args => {
|
||||||
args.push(
|
|
||||||
function() {
|
|
||||||
return this.state[args[0]];
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
return [args[0], getComponent(...args)];
|
return [args[0], getComponent(...args)];
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
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
@ -177,7 +177,7 @@ let type_data = {
|
|||||||
{
|
{
|
||||||
name: "Yet Another",
|
name: "Yet Another",
|
||||||
values: [15, 20, -3, -15, 58, 12, -17, 37],
|
values: [15, 20, -3, -15, 58, 12, -17, 37],
|
||||||
chartType: 'line'
|
chartType: 'bar'
|
||||||
}
|
}
|
||||||
|
|
||||||
// temp : Stacked
|
// temp : Stacked
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
import BaseChart from './BaseChart';
|
import BaseChart from './BaseChart';
|
||||||
import { dataPrep } from './axis-chart-utils';
|
import { dataPrep, zeroDataPrep } from './axis-chart-utils';
|
||||||
import { Y_AXIS_MARGIN } from '../utils/constants';
|
import { Y_AXIS_MARGIN } from '../utils/constants';
|
||||||
import { getComponent } from '../objects/ChartComponents';
|
import { getComponent } from '../objects/ChartComponents';
|
||||||
import { BarChartController, LineChartController, getPaths } from '../objects/AxisChartControllers';
|
|
||||||
import { AxisChartRenderer } from '../utils/draw';
|
import { AxisChartRenderer } from '../utils/draw';
|
||||||
import { getOffset, fire } from '../utils/dom';
|
import { getOffset, fire } from '../utils/dom';
|
||||||
import { equilizeNoOfElements } from '../utils/draw-utils';
|
import { equilizeNoOfElements } from '../utils/draw-utils';
|
||||||
import { Animator, translateHoriLine } from '../utils/animate';
|
import { Animator, translateHoriLine } from '../utils/animate';
|
||||||
import { runSMILAnimation } from '../utils/animation';
|
import { runSMILAnimation } from '../utils/animation';
|
||||||
import { getRealIntervals, calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex } from '../utils/intervals';
|
import { getRealIntervals, calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex, scale } from '../utils/intervals';
|
||||||
import { floatTwo, fillArray, bindChange } from '../utils/helpers';
|
import { floatTwo, fillArray, bindChange } from '../utils/helpers';
|
||||||
import { MIN_BAR_PERCENT_HEIGHT, DEFAULT_AXIS_CHART_TYPE } from '../utils/constants';
|
import { MIN_BAR_PERCENT_HEIGHT, DEFAULT_AXIS_CHART_TYPE, BAR_CHART_SPACE_RATIO } from '../utils/constants';
|
||||||
|
|
||||||
export default class AxisChart extends BaseChart {
|
export default class AxisChart extends BaseChart {
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
@ -19,19 +18,19 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.valuesOverPoints = args.valuesOverPoints;
|
this.valuesOverPoints = args.valuesOverPoints;
|
||||||
this.formatTooltipY = args.formatTooltipY;
|
this.formatTooltipY = args.formatTooltipY;
|
||||||
this.formatTooltipX = args.formatTooltipX;
|
this.formatTooltipX = args.formatTooltipX;
|
||||||
this.barOptions = args.barOptions;
|
this.barOptions = args.barOptions || {};
|
||||||
this.lineOptions = args.lineOptions;
|
this.lineOptions = args.lineOptions || {};
|
||||||
this.type = args.type || 'line';
|
this.type = args.type || 'line';
|
||||||
|
|
||||||
this.xAxisMode = args.xAxisMode || 'span';
|
this.xAxisMode = args.xAxisMode || 'span';
|
||||||
this.yAxisMode = args.yAxisMode || 'span';
|
this.yAxisMode = args.yAxisMode || 'span';
|
||||||
|
|
||||||
this.zeroLine = this.height;
|
this.zeroLine = this.height;
|
||||||
this.setTrivialState();
|
// this.setTrivialState();
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
configure(args) {
|
configure(args) {3
|
||||||
super.configure();
|
super.configure();
|
||||||
|
|
||||||
// TODO: set in options and use
|
// TODO: set in options and use
|
||||||
@ -40,38 +39,6 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.config.yAxisMode = args.yAxisMode;
|
this.config.yAxisMode = args.yAxisMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTrivialState() {
|
|
||||||
// Define data and stuff
|
|
||||||
let yTempPos = getRealIntervals(this.height, 4, 0, 0);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
xAxis: {
|
|
||||||
positions: [],
|
|
||||||
labels: [],
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
positions: yTempPos,
|
|
||||||
labels: yTempPos.map(d => ""),
|
|
||||||
},
|
|
||||||
yRegions: [
|
|
||||||
{
|
|
||||||
start: this.height,
|
|
||||||
end: this.height,
|
|
||||||
label: ''
|
|
||||||
}
|
|
||||||
],
|
|
||||||
yMarkers: [
|
|
||||||
{
|
|
||||||
position: this.height,
|
|
||||||
label: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
this.calcWidth();
|
|
||||||
this.calcXPositions(this.state);
|
|
||||||
}
|
|
||||||
|
|
||||||
setMargins() {
|
setMargins() {
|
||||||
super.setMargins();
|
super.setMargins();
|
||||||
this.translateXLeft = Y_AXIS_MARGIN;
|
this.translateXLeft = Y_AXIS_MARGIN;
|
||||||
@ -82,10 +49,12 @@ export default class AxisChart extends BaseChart {
|
|||||||
return dataPrep(data, this.type);
|
return dataPrep(data, this.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareFirstData(data=this.data) {
|
||||||
|
return zeroDataPrep(data);
|
||||||
|
}
|
||||||
|
|
||||||
calc() {
|
calc() {
|
||||||
|
|
||||||
this.calcXPositions();
|
this.calcXPositions();
|
||||||
|
|
||||||
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
this.calcYAxisParameters(this.getAllYValues(), this.type === 'line');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,61 +88,60 @@ export default class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.calcYUnits();
|
this.calcYUnits();
|
||||||
this.calcYMaximums();
|
this.calcYExtremes();
|
||||||
this.calcYRegions();
|
this.calcYRegions();
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYUnits() {
|
calcYUnits() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
this.data.datasets.map(d => {
|
let scaleAll = values => values.map(val => scale(val, s.yAxis));
|
||||||
d.positions = d.values.map(val =>
|
|
||||||
floatTwo(s.yAxis.zeroLine - val * s.yAxis.scaleMultiplier));
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.barOptions && this.barOptions.stacked) {
|
s.datasets = this.data.datasets.map((d, i) => {
|
||||||
this.data.datasets.map((d, i) => {
|
let values = d.values;
|
||||||
d.cumulativePositions = d.cumulativeYs.map(val =>
|
let cumulativeYs = d.cumulativeYs || [];
|
||||||
floatTwo(s.yAxis.zeroLine - val * s.yAxis.scaleMultiplier));
|
return {
|
||||||
});
|
name: d.name,
|
||||||
}
|
index: i,
|
||||||
|
chartType: d.chartType,
|
||||||
|
|
||||||
|
values: values,
|
||||||
|
yPositions: scaleAll(values),
|
||||||
|
|
||||||
|
cumulativeYs: cumulativeYs,
|
||||||
|
cumulativeYPos: scaleAll(cumulativeYs),
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYMaximums() {
|
calcYExtremes() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
if(this.barOptions && this.barOptions.stacked) {
|
if(this.barOptions && this.barOptions.stacked) {
|
||||||
s.yExtremes = this.data.datasets[this.data.datasets.length - 1].cumulativePositions;
|
s.yExtremes = s.datasets[s.datasets.length - 1].cumulativeYPos;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
s.yExtremes = new Array(s.datasetLength).fill(9999);
|
s.yExtremes = new Array(s.datasetLength).fill(9999);
|
||||||
this.data.datasets.map((d, i) => {
|
s.datasets.map((d, i) => {
|
||||||
d.positions.map((pos, j) => {
|
d.yPositions.map((pos, j) => {
|
||||||
if(pos < s.yExtremes[j]) {
|
if(pos < s.yExtremes[j]) {
|
||||||
s.yExtremes[j] = pos;
|
s.yExtremes[j] = pos;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tooltip refresh should not be needed?
|
|
||||||
// this.chartWrapper.removeChild(this.tip.container);
|
|
||||||
// this.make_tooltip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYRegions() {
|
calcYRegions() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
if(this.data.yMarkers) {
|
if(this.data.yMarkers) {
|
||||||
this.state.yMarkers = this.data.yMarkers.map(d => {
|
this.state.yMarkers = this.data.yMarkers.map(d => {
|
||||||
d.position = floatTwo(s.yAxis.zeroLine - d.value * s.yAxis.scaleMultiplier);
|
d.position = scale(d.value, s.yAxis);
|
||||||
d.label += ': ' + d.value;
|
d.label += ': ' + d.value;
|
||||||
return d;
|
return d;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if(this.data.yRegions) {
|
if(this.data.yRegions) {
|
||||||
this.state.yRegions = this.data.yRegions.map(d => {
|
this.state.yRegions = this.data.yRegions.map(d => {
|
||||||
if(d.end < d.start) {
|
d.start = scale(d.start, s.yAxis);
|
||||||
[d.start, d.end] = [d.end, start];
|
d.end = scale(d.end, s.yAxis);
|
||||||
}
|
|
||||||
d.start = floatTwo(s.yAxis.zeroLine - d.start * s.yAxis.scaleMultiplier);
|
|
||||||
d.end = floatTwo(s.yAxis.zeroLine - d.end * s.yAxis.scaleMultiplier);
|
|
||||||
return d;
|
return d;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -196,6 +164,9 @@ export default class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initComponents() {
|
initComponents() {
|
||||||
|
let s = this.state;
|
||||||
|
// console.log('this.state', Object.assign({}, this.state));
|
||||||
|
// console.log('this.state', this.state);
|
||||||
this.componentConfigs = [
|
this.componentConfigs = [
|
||||||
[
|
[
|
||||||
'yAxis',
|
'yAxis',
|
||||||
@ -222,7 +193,57 @@ export default class AxisChart extends BaseChart {
|
|||||||
pos: 'right'
|
pos: 'right'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
this.componentConfigs.map(args => {
|
||||||
|
args.push(
|
||||||
|
function() {
|
||||||
|
return this.state[args[0]];
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let barDatasets = this.state.datasets.filter(d => d.chartType === 'bar');
|
||||||
|
let lineDatasets = this.state.datasets.filter(d => d.chartType === 'line');
|
||||||
|
|
||||||
|
// console.log('barDatasets', barDatasets, this.state.datasets);
|
||||||
|
|
||||||
|
// Bars
|
||||||
|
let spaceRatio = this.barOptions.spaceRatio || BAR_CHART_SPACE_RATIO;
|
||||||
|
let barsWidth = s.unitWidth * (1 - spaceRatio);
|
||||||
|
let barWidth = barsWidth/(this.barOptions.stacked ? 1 : barDatasets.length);
|
||||||
|
|
||||||
|
let barsConfigs = barDatasets.map(d => {
|
||||||
|
let index = d.index;
|
||||||
|
return [
|
||||||
|
'barGraph',
|
||||||
|
{
|
||||||
|
index: index,
|
||||||
|
color: this.colors[index],
|
||||||
|
|
||||||
|
// same for all datasets
|
||||||
|
valuesOverPoints: this.valuesOverPoints,
|
||||||
|
minHeight: this.height * MIN_BAR_PERCENT_HEIGHT,
|
||||||
|
barsWidth: barsWidth,
|
||||||
|
barWidth: barWidth,
|
||||||
|
zeroLine: s.yAxis.zeroLine
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
let s = this.state;
|
||||||
|
let d = s.datasets[index];
|
||||||
|
return {
|
||||||
|
xPositions: s.xAxis.positions,
|
||||||
|
yPositions: d.yPositions,
|
||||||
|
cumulativeYPos: d.cumulativeYPos,
|
||||||
|
|
||||||
|
values: d.values,
|
||||||
|
cumulativeYs: d.cumulativeYs
|
||||||
|
};
|
||||||
|
}.bind(this)
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
let markerConfigs = [
|
||||||
[
|
[
|
||||||
'yMarkers',
|
'yMarkers',
|
||||||
{
|
{
|
||||||
@ -231,17 +252,23 @@ export default class AxisChart extends BaseChart {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
markerConfigs.map(args => {
|
||||||
|
args.push(
|
||||||
|
function() {
|
||||||
|
return this.state[args[0]];
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.componentConfigs = this.componentConfigs.concat(barsConfigs, markerConfigs);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupComponents() {
|
setupComponents() {
|
||||||
let optionals = ['yMarkers', 'yRegions'];
|
let optionals = ['yMarkers', 'yRegions'];
|
||||||
this.components = new Map(this.componentConfigs
|
this.components = new Map(this.componentConfigs
|
||||||
.filter(args => !optionals.includes(args[0]) || this.data[args[0]])
|
.filter(args => !optionals.includes(args[0]) || this.state[args[0]] || args[0] === 'barGraph')
|
||||||
.map(args => {
|
.map(args => {
|
||||||
args.push(
|
|
||||||
function() {
|
|
||||||
return this.state[args[0]];
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
return [args[0], getComponent(...args)];
|
return [args[0], getComponent(...args)];
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,13 +28,15 @@ export default class BaseChart {
|
|||||||
this.title = title;
|
this.title = title;
|
||||||
this.subtitle = subtitle;
|
this.subtitle = subtitle;
|
||||||
this.argHeight = height;
|
this.argHeight = height;
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
this.isNavigable = isNavigable;
|
this.isNavigable = isNavigable;
|
||||||
if(this.isNavigable) {
|
if(this.isNavigable) {
|
||||||
this.currentIndex = 0;
|
this.currentIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.data = this.prepareData(data);;
|
this.realData = this.prepareData(data);
|
||||||
|
this.data = this.prepareFirstData(this.realData);
|
||||||
this.colors = [];
|
this.colors = [];
|
||||||
this.config = {};
|
this.config = {};
|
||||||
this.state = {};
|
this.state = {};
|
||||||
@ -148,6 +150,7 @@ export default class BaseChart {
|
|||||||
this.calcWidth();
|
this.calcWidth();
|
||||||
this.makeChartArea();
|
this.makeChartArea();
|
||||||
|
|
||||||
|
this.calc();
|
||||||
this.initComponents(); // Only depend on the drawArea made in makeChartArea
|
this.initComponents(); // Only depend on the drawArea made in makeChartArea
|
||||||
|
|
||||||
this.setupComponents();
|
this.setupComponents();
|
||||||
@ -160,6 +163,7 @@ export default class BaseChart {
|
|||||||
|
|
||||||
// TODO: remove timeout and decrease post animate time in chart component
|
// TODO: remove timeout and decrease post animate time in chart component
|
||||||
if(init) {
|
if(init) {
|
||||||
|
this.data = this.realData;
|
||||||
setTimeout(() => {this.update();}, 1000);
|
setTimeout(() => {this.update();}, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -187,10 +191,10 @@ export default class BaseChart {
|
|||||||
|
|
||||||
calc() {} // builds state
|
calc() {} // builds state
|
||||||
|
|
||||||
render(animate=true) {
|
render(components=this.components, animate=true) {
|
||||||
// Can decouple to this.refreshComponents() first to save animation timeout
|
// Can decouple to this.refreshComponents() first to save animation timeout
|
||||||
let elementsToAnimate = [];
|
let elementsToAnimate = [];
|
||||||
this.components.forEach(c => {
|
components.forEach(c => {
|
||||||
elementsToAnimate = elementsToAnimate.concat(c.update(animate));
|
elementsToAnimate = elementsToAnimate.concat(c.update(animate));
|
||||||
});
|
});
|
||||||
if(elementsToAnimate.length > 0) {
|
if(elementsToAnimate.length > 0) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { floatTwo, fillArray } from '../utils/helpers';
|
import { floatTwo, fillArray } from '../utils/helpers';
|
||||||
import { DEFAULT_AXIS_CHART_TYPE } from '../utils/constants';
|
import { DEFAULT_AXIS_CHART_TYPE, AXIS_DATASET_CHART_TYPES } from '../utils/constants';
|
||||||
|
|
||||||
export function dataPrep(data, type) {
|
export function dataPrep(data, type) {
|
||||||
data.labels = data.labels || [];
|
data.labels = data.labels || [];
|
||||||
@ -36,19 +36,56 @@ export function dataPrep(data, type) {
|
|||||||
// Set labels
|
// Set labels
|
||||||
//
|
//
|
||||||
|
|
||||||
// Set index
|
|
||||||
d.index = i;
|
|
||||||
|
|
||||||
// Set type
|
// Set type
|
||||||
if(!d.chartType ) {
|
if(!d.chartType ) {
|
||||||
d.chartType = type || DEFAULT_AXIS_CHART_TYPE;
|
if(!AXIS_DATASET_CHART_TYPES.includes(type)) type === DEFAULT_AXIS_CHART_TYPE;
|
||||||
|
d.chartType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Markers
|
// Markers
|
||||||
|
|
||||||
// Regions
|
// Regions
|
||||||
// Set start and end
|
// data.yRegions = data.yRegions || [];
|
||||||
|
if(data.yRegions) {
|
||||||
|
data.yRegions.map(d => {
|
||||||
|
if(d.end < d.start) {
|
||||||
|
[d.start, d.end] = [d.end, start];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function zeroDataPrep(realData) {
|
||||||
|
let datasetLength = realData.labels.length;
|
||||||
|
let zeroArray = new Array(datasetLength).fill(0);
|
||||||
|
|
||||||
|
let zeroData = {
|
||||||
|
labels: realData.labels,
|
||||||
|
datasets: realData.datasets.map(d => {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
values: zeroArray,
|
||||||
|
chartType: d.chartType
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
yRegions: [
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
label: ''
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yMarkers: [
|
||||||
|
{
|
||||||
|
value: 0,
|
||||||
|
label: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
return zeroData;
|
||||||
}
|
}
|
||||||
@ -67,36 +67,6 @@ export class BarChartController extends AxisChartController {
|
|||||||
? m.options.stacked : m.noOfDatasets);
|
? m.options.stacked : m.noOfDatasets);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(x, yTop, color, label='', index=0, offset=0) {
|
|
||||||
let [height, y] = getBarHeightAndYAttr(yTop, this.meta.zeroLine);
|
|
||||||
|
|
||||||
let rect = createSVG('rect', {
|
|
||||||
className: `bar mini`,
|
|
||||||
style: `fill: ${color}`,
|
|
||||||
'data-point-index': index,
|
|
||||||
x: x - this.consts.barsWidth/2,
|
|
||||||
y: y - offset,
|
|
||||||
width: this.consts.width,
|
|
||||||
height: height || this.consts.minHeight
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!label && !label.length) {
|
|
||||||
return rect;
|
|
||||||
} else {
|
|
||||||
let text = createSVG('text', {
|
|
||||||
className: 'data-point-value',
|
|
||||||
x: x,
|
|
||||||
y: y - offset,
|
|
||||||
dy: (FONT_SIZE / 2 * -1) + 'px',
|
|
||||||
'font-size': FONT_SIZE + 'px',
|
|
||||||
'text-anchor': 'middle',
|
|
||||||
innerHTML: label
|
|
||||||
});
|
|
||||||
|
|
||||||
return wrapInSVGGroup([rect, text]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
animate(bar, x, yTop, index, noOfDatasets) {
|
animate(bar, x, yTop, index, noOfDatasets) {
|
||||||
let start = x - this.meta.unitWidth/4;
|
let start = x - this.meta.unitWidth/4;
|
||||||
let width = (this.meta.unitWidth/2)/noOfDatasets;
|
let width = (this.meta.unitWidth/2)/noOfDatasets;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { makeSVGGroup } from '../utils/draw';
|
import { makeSVGGroup } from '../utils/draw';
|
||||||
import { xLine, yLine, yMarker, yRegion } from '../utils/draw';
|
import { xLine, yLine, yMarker, yRegion, datasetBar } from '../utils/draw';
|
||||||
import { equilizeNoOfElements } from '../utils/draw-utils';
|
import { equilizeNoOfElements } from '../utils/draw-utils';
|
||||||
import { Animator, translateHoriLine, translateVertLine, animateRegion } from '../utils/animate';
|
import { Animator, translateHoriLine, translateVertLine, animateRegion } from '../utils/animate';
|
||||||
|
|
||||||
@ -25,8 +25,9 @@ class ChartComponent {
|
|||||||
|
|
||||||
this.store = [];
|
this.store = [];
|
||||||
|
|
||||||
this.layerClass = typeof(layerClass) === 'function'
|
this.layerClass = layerClass;
|
||||||
? layerClass() : layerClass;
|
this.layerClass = typeof(this.layerClass) === 'function'
|
||||||
|
? this.layerClass() : this.layerClass;
|
||||||
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
@ -204,81 +205,61 @@ let componentConfigs = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
barGraph: {
|
barGraph: {
|
||||||
// opt:[
|
layerClass: function() { return 'dataset-units dataset-' + this.constants.index; },
|
||||||
// 'barGraph',
|
|
||||||
// this.drawArea,
|
|
||||||
// {
|
|
||||||
// controller: barController,
|
|
||||||
// index: index,
|
|
||||||
// color: this.colors[index],
|
|
||||||
// valuesOverPoints: this.valuesOverPoints,
|
|
||||||
// stacked: this.barOptions && this.barOptions.stacked,
|
|
||||||
// spaceRatio: 0.5,
|
|
||||||
// minHeight: this.height * MIN_BAR_PERCENT_HEIGHT
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
|
|
||||||
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
|
|
||||||
|
|
||||||
// },
|
|
||||||
// function() {
|
|
||||||
// let s = this.state;
|
|
||||||
// return {
|
|
||||||
// barsWidth: this.state.unitWidth * (1 - spaceRatio),
|
|
||||||
// barWidth: barsWidth/(stacked ? 1 : this.state.noOfDatasets),
|
|
||||||
// positions: s.xAxisPositions,
|
|
||||||
// labels: s.xAxisLabels,
|
|
||||||
// }
|
|
||||||
// }.bind(this)
|
|
||||||
// ],
|
|
||||||
layerClass() { return 'y-regions' + this.constants.index; },
|
|
||||||
makeElements(data) {
|
makeElements(data) {
|
||||||
let c = this.constants;
|
let c = this.constants;
|
||||||
return data.yPositions.map((y, j) =>
|
return data.yPositions.map((y, j) => {
|
||||||
barController.draw(
|
// console.log(data.cumulativeYPos, data.cumulativeYPos[j]);
|
||||||
|
return datasetBar(
|
||||||
data.xPositions[j],
|
data.xPositions[j],
|
||||||
y,
|
y,
|
||||||
color,
|
c.barWidth,
|
||||||
|
c.color,
|
||||||
(c.valuesOverPoints ? (c.stacked ? data.cumulativeYs[j] : data.values[j]) : ''),
|
(c.valuesOverPoints ? (c.stacked ? data.cumulativeYs[j] : data.values[j]) : ''),
|
||||||
j,
|
j,
|
||||||
y - (data.cumulativePositions ? data.cumulativePositions[j] : y)
|
y - (c.stacked ? data.cumulativeYPos[j] : y),
|
||||||
|
{
|
||||||
|
zeroLine: c.zeroLine,
|
||||||
|
barsWidth: c.barsWidth,
|
||||||
|
minHeight: c.minHeight
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
});
|
||||||
},
|
},
|
||||||
postMake() {
|
postMake() {
|
||||||
if((!this.constants.stacked)) {
|
if((!this.constants.stacked)) {
|
||||||
this.layer.setAttribute('transform',
|
this.layer.setAttribute('transform',
|
||||||
`translate(${unitRenderer.consts.width * index}, 0)`);
|
`translate(${this.constants.width * this.constants.index}, 0)`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
animateElements(newData) {
|
animateElements(newData) {
|
||||||
[this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
|
// [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
|
||||||
|
|
||||||
let newPos = newData.map(d => d.end);
|
// let newPos = newData.map(d => d.end);
|
||||||
let newLabels = newData.map(d => d.label);
|
// let newLabels = newData.map(d => d.label);
|
||||||
let newStarts = newData.map(d => d.start);
|
// let newStarts = newData.map(d => d.start);
|
||||||
|
|
||||||
let oldPos = this.oldData.map(d => d.end);
|
// let oldPos = this.oldData.map(d => d.end);
|
||||||
let oldLabels = this.oldData.map(d => d.label);
|
// let oldLabels = this.oldData.map(d => d.label);
|
||||||
let oldStarts = this.oldData.map(d => d.start);
|
// let oldStarts = this.oldData.map(d => d.start);
|
||||||
|
|
||||||
this.render(oldPos.map((pos, i) => {
|
// this.render(oldPos.map((pos, i) => {
|
||||||
return {
|
// return {
|
||||||
start: oldStarts[i],
|
// start: oldStarts[i],
|
||||||
end: oldPos[i],
|
// end: oldPos[i],
|
||||||
label: newLabels[i]
|
// label: newLabels[i]
|
||||||
}
|
// }
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
let animateElements = [];
|
// let animateElements = [];
|
||||||
|
|
||||||
this.store.map((rectGroup, i) => {
|
// this.store.map((rectGroup, i) => {
|
||||||
animateElements = animateElements.concat(animateRegion(
|
// animateElements = animateElements.concat(animateRegion(
|
||||||
rectGroup, newStarts[i], newPos[i], oldPos[i]
|
// rectGroup, newStarts[i], newPos[i], oldPos[i]
|
||||||
));
|
// ));
|
||||||
});
|
// });
|
||||||
|
|
||||||
return animateElements;
|
// return animateElements;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
export const Y_AXIS_MARGIN = 60;
|
export const Y_AXIS_MARGIN = 60;
|
||||||
|
|
||||||
export const MIN_BAR_PERCENT_HEIGHT = 0.01;
|
export const MIN_BAR_PERCENT_HEIGHT = 0.01;
|
||||||
export const DEFAULT_AXIS_CHART_TYPE = 'line';
|
export const DEFAULT_AXIS_CHART_TYPE = 'line';
|
||||||
|
export const AXIS_DATASET_CHART_TYPES = ['line', 'bar'];
|
||||||
|
export const BAR_CHART_SPACE_RATIO = 0.5;
|
||||||
@ -2,15 +2,9 @@ import { fillArray } from './helpers';
|
|||||||
|
|
||||||
export function getBarHeightAndYAttr(yTop, zeroLine) {
|
export function getBarHeightAndYAttr(yTop, zeroLine) {
|
||||||
let height, y;
|
let height, y;
|
||||||
if (yTop <= zeroLine) {
|
if (yTop < zeroLine) {
|
||||||
height = zeroLine - yTop;
|
height = zeroLine - yTop;
|
||||||
y = yTop;
|
y = yTop;
|
||||||
|
|
||||||
// In case of invisible bars
|
|
||||||
if(height === 0) {
|
|
||||||
height = totalHeight * MIN_BAR_PERCENT_HEIGHT;
|
|
||||||
y -= height;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
height = yTop - zeroLine;
|
height = yTop - zeroLine;
|
||||||
y = zeroLine;
|
y = zeroLine;
|
||||||
|
|||||||
@ -360,6 +360,37 @@ export function yRegion(y1, y2, width, label) {
|
|||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, meta={}) {
|
||||||
|
let [height, y] = getBarHeightAndYAttr(yTop, meta.zeroLine);
|
||||||
|
// console.log(yTop, meta.zeroLine, y, offset);
|
||||||
|
|
||||||
|
let rect = createSVG('rect', {
|
||||||
|
className: `bar mini`,
|
||||||
|
style: `fill: ${color}`,
|
||||||
|
'data-point-index': index,
|
||||||
|
x: x - meta.barsWidth/2,
|
||||||
|
y: y - offset,
|
||||||
|
width: width,
|
||||||
|
height: height || meta.minHeight
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!label && !label.length) {
|
||||||
|
return rect;
|
||||||
|
} else {
|
||||||
|
let text = createSVG('text', {
|
||||||
|
className: 'data-point-value',
|
||||||
|
x: x,
|
||||||
|
y: y - offset,
|
||||||
|
dy: (FONT_SIZE / 2 * -1) + 'px',
|
||||||
|
'font-size': FONT_SIZE + 'px',
|
||||||
|
'text-anchor': 'middle',
|
||||||
|
innerHTML: label
|
||||||
|
});
|
||||||
|
|
||||||
|
return wrapInSVGGroup([rect, text]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class AxisChartRenderer {
|
export class AxisChartRenderer {
|
||||||
constructor(state) {
|
constructor(state) {
|
||||||
this.refreshState(state);
|
this.refreshState(state);
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import { floatTwo } from './helpers';
|
||||||
|
|
||||||
function normalize(x) {
|
function normalize(x) {
|
||||||
// Calculates mantissa and exponent of a number
|
// Calculates mantissa and exponent of a number
|
||||||
// Returns normalized number and exponent
|
// Returns normalized number and exponent
|
||||||
@ -194,6 +196,10 @@ export function getValueRange(orderedArray) {
|
|||||||
return orderedArray[orderedArray.length-1] - orderedArray[0];
|
return orderedArray[orderedArray.length-1] - orderedArray[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function scale(val, yAxis) {
|
||||||
|
return floatTwo(yAxis.zeroLine - val * yAxis.scaleMultiplier)
|
||||||
|
}
|
||||||
|
|
||||||
export function calcDistribution(values, distributionSize) {
|
export function calcDistribution(values, distributionSize) {
|
||||||
// Assume non-negative values,
|
// Assume non-negative values,
|
||||||
// implying distribution minimum at zero
|
// implying distribution minimum at zero
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user