render multiple y axis

This commit is contained in:
Prateeksha Singh 2018-01-01 20:29:13 +05:30
parent 405f8fb18b
commit b3ca8f0819
15 changed files with 713 additions and 417 deletions

View File

@ -281,6 +281,7 @@ function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
const AXIS_TICK_LENGTH = 6;
const LABEL_MARGIN = 4;
const FONT_SIZE = 10;
const BASE_LINE_COLOR = '#dadada';
function $$1(expr, con) {
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') {
let height = mode === 'span' ? -1 * AXIS_TICK_LENGTH : totalHeight;
function makeVertLine(x, label, y1, y2, options={}) {
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
let l = createSVG('line', {
className: 'line-vertical ' + options.className,
x1: 0,
x2: 0,
y1: totalHeight + AXIS_TICK_LENGTH,
y2: height,
stroke: stroke
y1: y1,
y2: y2,
styles: {
stroke: options.stroke
}
});
let text = createSVG('text', {
x: 0,
y: totalHeight + AXIS_TICK_LENGTH + LABEL_MARGIN,
y: y1 > y2 ? y1 + LABEL_MARGIN : y1 - LABEL_MARGIN - FONT_SIZE,
dy: FONT_SIZE + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'middle',
@ -446,50 +449,34 @@ function makeVertXLine(x, label, totalHeight, mode, stroke='#dadada') {
return line;
}
function makeHoriYLine(y, label, totalWidth, mode, pos='left') {
let lineType = '';
let w2 = mode === 'span' ? totalWidth + AXIS_TICK_LENGTH : 0;
// temp : works correctly
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';
}
function makeHoriLine(y, label, x1, x2, options={}) {
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.lineType) options.lineType = '';
let className = 'line-horizontal ' + options.className +
(options.lineType === "dashed" ? "dashed": "");
let l = createSVG('line', {
className: lineType === "dashed" ? "dashed": "",
className: className,
x1: x1,
x2: x2,
y1: 0,
y2: 0
y2: 0,
styles: {
stroke: options.stroke
}
});
let text = createSVG('text', {
x: textX,
x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN,
y: 0,
dy: (FONT_SIZE / 2 - 2) + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': anchor,
'text-anchor': x1 < x2 ? 'end' : 'start',
innerHTML: label+""
});
let line = createSVG('g', {
transform: `translate(0, ${y})`,
transform: `translate(0, ${ y })`,
'stroke-opacity': 1
});
@ -517,6 +504,10 @@ class AxisChartRenderer {
this.yAxisMode = state.yAxisMode;
}
setZeroline(zeroLine) {
this.zeroLine = zeroLine;
}
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets, prevX, prevY) {
let totalWidth = this.unitWidth - args.spaceWidth;
@ -553,14 +544,61 @@ class AxisChartRenderer {
});
}
// temp: stroke
xLine(x, label, pos='bottom', stroke='', mode=this.xAxisMode) {
xLine(x, label, options={}) {
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
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) {
return makeHoriYLine(y, label, this.totalWidth, mode, pos);
yLine(y, label, options={}) {
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() {}
@ -688,6 +726,7 @@ class BaseChart {
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
this.title = title;
this.subtitle = subtitle;
this.argHeight = height;
this.isNavigable = isNavigable;
if(this.isNavigable) {
@ -702,7 +741,6 @@ class BaseChart {
// showLegend, which then all functions will check
this.setColors();
this.setMargins(args);
// constants
this.config = {
@ -735,12 +773,19 @@ class BaseChart {
this.colors = this.colors.map(color => getColor(color));
}
setMargins(args) {
let height = args.height;
setMargins() {
// TODO: think for all
let height = this.argHeight;
this.baseHeight = height;
this.height = height - 40;
this.translateX = 60;
this.translateY = 10;
this.height = height - 40; // change
this.translateY = 20;
this.setHorizontalMargin();
}
setHorizontalMargin() {
this.translateXLeft = 60;
this.translateXRight = 40;
}
validate(){
@ -780,7 +825,10 @@ class BaseChart {
_setup() {
this.bindWindowEvents();
this.setupConstants();
this.prepareData();
this.setupComponents();
this.setMargins();
this.makeContainer();
this.makeTooltip(); // without binding
this.draw(true);
@ -864,15 +912,16 @@ class BaseChart {
// }
// });
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
this.width = this.baseWidth - this.translateX * 2;
this.width = this.baseWidth - (this.translateXLeft + this.translateXRight);
}
refresh() { //?? refresh?
this.oldState = this.state ? Object.assign({}, this.state) : {};
this.intermedState = {};
this.prepareData();
this.reCalc();
this.refreshRenderer();
this.refreshComponents();
}
makeChartArea() {
@ -887,7 +936,7 @@ class BaseChart {
this.drawArea = makeSVGGroup(
this.svg,
this.type + '-chart',
`translate(${this.translateX}, ${this.translateY})`
`translate(${this.translateXLeft}, ${this.translateY})`
);
}
@ -905,7 +954,6 @@ class BaseChart {
return;
}
this.intermedState = this.calcIntermedState();
this.refreshComponents();
this.animateComponents();
setTimeout(() => {
this.renderComponents();
@ -914,22 +962,15 @@ class BaseChart {
// (opt, should not redraw if still in animate?)
}
calcIntermedState() {}
calcIntermedState() {
this.intermedState = {};
}
// convenient component array abstractions
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
renderComponents() { this.components.forEach(c => c.render()); }
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() {}
@ -979,36 +1020,28 @@ class BaseChart {
}
}
const Y_AXIS_MARGIN = 60;
class ChartComponent {
constructor({
layerClass = '',
layerTransform = '',
make,
argsKeys,
animate
}) {
this.layerClass = layerClass; // 'y axis'
this.layerTransform = layerTransform;
this.make = make;
this.argsKeys = argsKeys;//['yAxisPositions', 'yAxisLabels'];
this.animate = animate;
this.layer = undefined;
this.store = []; //[[]] depends on indexed
}
refresh(args) {
this.chartState = args.chartState;
this.oldChartState = args.oldChartState;
this.intermedState = args.intermedState;
this.chartRenderer = args.chartRenderer;
}
refresh(args) {}
render() {
let args = this.argsKeys.map(key => this.chartState[key]);
args.unshift(this.chartRenderer);
this.store = this.make(...args);
this.store = this.make();
this.layer.textContent = '';
this.store.forEach(element => {
@ -1026,53 +1059,6 @@ class ChartComponent {
}
// 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;
@ -1428,9 +1414,15 @@ class AxisChart extends BaseChart {
this.is_series = args.is_series;
this.format_tooltip_y = args.format_tooltip_y;
this.format_tooltip_x = args.format_tooltip_x;
this.zeroLine = this.height;
}
setHorizontalMargin() {
this.translateXLeft = Y_AXIS_MARGIN;
this.translateXRight = Y_AXIS_MARGIN;
}
checkData(data) {
return true;
}
@ -1441,7 +1433,10 @@ class AxisChart extends BaseChart {
prepareData() {
let s = this.state;
s.xAxisLabels = this.data.labels || [];
s.xAxisPositions = [];
s.datasetLength = s.xAxisLabels.length;
let zeroArray = new Array(s.datasetLength).fill(0);
@ -1474,6 +1469,16 @@ class AxisChart extends BaseChart {
});
s.noOfDatasets = s.datasets.length;
// s.yAxis = [];
this.prepareYAxis();
}
prepareYAxis() {
this.state.yAxis = {
labels: [],
positions: []
};
}
reCalc() {
@ -1484,15 +1489,15 @@ class AxisChart extends BaseChart {
this.calcXPositions();
// 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 = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]]; // array of indexed components
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
this.configUnits();
@ -1501,6 +1506,11 @@ class AxisChart extends BaseChart {
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() {
let s = this.state;
this.setUnitWidthAndXOffset();
@ -1510,18 +1520,18 @@ class AxisChart extends BaseChart {
s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
}
calcYAxisPositions() {
let s = this.state;
const yPts = s.yAxisLabels;
calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') {
yAxis.labels = calcIntervals(dataValues, withMinimum);
const yPts = yAxis.labels;
s.scaleMultiplier = this.height / getValueRange(yPts);
const intervalHeight = getIntervalSize(yPts) * s.scaleMultiplier;
s.zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
yAxis.scaleMultiplier = this.height / getValueRange(yPts);
const intervalHeight = getIntervalSize(yPts) * yAxis.scaleMultiplier;
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;
s.yUnitPositions = s.yUnitValues.map(values =>
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() {
// These args are basically the current state of the chart,
// 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 {
@ -1891,6 +1911,103 @@ class ScatterChart extends LineChart {
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 {
constructor(args) {
super(args);
@ -2499,6 +2616,7 @@ class Heatmap extends BaseChart {
const chartTypes = {
line: LineChart,
bar: BarChart,
multiaxis: MultiAxisChart,
scatter: ScatterChart,
percentage: PercentageChart,
heatmap: Heatmap,

File diff suppressed because one or more lines are too long

View File

@ -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}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,7 @@ let bar_composite_data = {
"2013", "2014", "2015", "2016", "2017"],
datasets: [{
"label": "Events",
"name": "Events",
"values": report_count_list,
// "formatted": report_count_list.map(d => d + " reports")
}]
@ -77,29 +77,31 @@ let type_data = {
datasets: [
{
label: "Some Data",
values: [18, 40, 30, 35, 8, 52, 17, -4]
name: "Some Data",
values: [18, 40, 30, 35, 8, 52, 17, -4],
axisPosition: 'right'
},
{
label: "Another Set",
values: [30, 50, -10, 15, 18, 32, 27, 14]
name: "Another Set",
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]
// }
// temp : Stacked
// {
// label: "Some Data",
// name: "Some Data",
// values:[25, 30, 50, 45, 18, 12, 27, 14]
// },
// {
// label: "Another Set",
// name: "Another Set",
// values: [18, 20, 30, 35, 8, 7, 17, 4]
// },
// {
// label: "Another Set",
// name: "Another Set",
// values: [11, 8, 19, 15, 3, 4, 10, 2]
// },
]
@ -109,7 +111,7 @@ let type_chart = new Chart({
parent: "#chart-types",
// title: "My Awesome Chart",
data: type_data,
type: 'bar',
type: 'multiaxis',
height: 250,
colors: ['purple', 'magenta'],
is_series: 1,
@ -225,8 +227,8 @@ let update_data = {
}],
"specific_values": [
{
label: "Altitude",
// label: "A very long text",
name: "Altitude",
// name: "A very long text",
line_type: "dashed",
value: 38
},

View File

@ -3,6 +3,7 @@ import '../scss/charts.scss';
import BarChart from './charts/BarChart';
import LineChart from './charts/LineChart';
import ScatterChart from './charts/ScatterChart';
import MultiAxisChart from './charts/MultiAxisChart';
import PercentageChart from './charts/PercentageChart';
import PieChart from './charts/PieChart';
import Heatmap from './charts/Heatmap';
@ -18,6 +19,7 @@ import Heatmap from './charts/Heatmap';
const chartTypes = {
line: LineChart,
bar: BarChart,
multiaxis: MultiAxisChart,
scatter: ScatterChart,
percentage: PercentageChart,
heatmap: Heatmap,

View File

@ -1,5 +1,6 @@
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 { AxisChartRenderer, makePath, makeGradient } from '../utils/draw';
import { equilizeNoOfElements } from '../utils/draw-utils';
@ -14,9 +15,15 @@ export default class AxisChart extends BaseChart {
this.is_series = args.is_series;
this.format_tooltip_y = args.format_tooltip_y;
this.format_tooltip_x = args.format_tooltip_x;
this.zeroLine = this.height;
}
setHorizontalMargin() {
this.translateXLeft = Y_AXIS_MARGIN;
this.translateXRight = Y_AXIS_MARGIN;
}
checkData(data) {
return true;
}
@ -27,7 +34,10 @@ export default class AxisChart extends BaseChart {
prepareData() {
let s = this.state;
s.xAxisLabels = this.data.labels || [];
s.xAxisPositions = [];
s.datasetLength = s.xAxisLabels.length;
let zeroArray = new Array(s.datasetLength).fill(0);
@ -60,6 +70,16 @@ export default class AxisChart extends BaseChart {
});
s.noOfDatasets = s.datasets.length;
// s.yAxis = [];
this.prepareYAxis();
}
prepareYAxis() {
this.state.yAxis = {
labels: [],
positions: []
};
}
reCalc() {
@ -70,15 +90,15 @@ export default class AxisChart extends BaseChart {
this.calcXPositions();
// 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 = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]]; // array of indexed components
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
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);
}
setYAxis() {
this.calcYAxisParameters(this.state.yAxis, this.getAllYValues(), this.type === 'line');
this.state.zeroLine = this.state.yAxis.zeroLine;
}
calcXPositions() {
let s = this.state;
this.setUnitWidthAndXOffset();
@ -96,18 +121,18 @@ export default class AxisChart extends BaseChart {
s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
}
calcYAxisPositions() {
let s = this.state;
const yPts = s.yAxisLabels;
calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') {
yAxis.labels = calcIntervals(dataValues, withMinimum);
const yPts = yAxis.labels;
s.scaleMultiplier = this.height / getValueRange(yPts);
const intervalHeight = getIntervalSize(yPts) * s.scaleMultiplier;
s.zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
yAxis.scaleMultiplier = this.height / getValueRange(yPts);
const intervalHeight = getIntervalSize(yPts) * yAxis.scaleMultiplier;
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;
s.yUnitPositions = s.yUnitValues.map(values =>
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() {
// These args are basically the current state of the chart,
// 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,
];
}
}

View File

@ -26,6 +26,7 @@ export default class BaseChart {
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
this.title = title;
this.subtitle = subtitle;
this.argHeight = height;
this.isNavigable = isNavigable;
if(this.isNavigable) {
@ -40,7 +41,6 @@ export default class BaseChart {
// showLegend, which then all functions will check
this.setColors();
this.setMargins(args);
// constants
this.config = {
@ -73,12 +73,19 @@ export default class BaseChart {
this.colors = this.colors.map(color => getColor(color));
}
setMargins(args) {
let height = args.height;
setMargins() {
// TODO: think for all
let height = this.argHeight;
this.baseHeight = height;
this.height = height - 40;
this.translateX = 60;
this.translateY = 10;
this.height = height - 40; // change
this.translateY = 20;
this.setHorizontalMargin();
}
setHorizontalMargin() {
this.translateXLeft = 60;
this.translateXRight = 40;
}
validate(){
@ -120,7 +127,10 @@ export default class BaseChart {
_setup() {
this.bindWindowEvents();
this.setupConstants();
this.prepareData();
this.setupComponents();
this.setMargins();
this.makeContainer();
this.makeTooltip(); // without binding
this.draw(true);
@ -204,15 +214,16 @@ export default class BaseChart {
// }
// });
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
this.width = this.baseWidth - this.translateX * 2;
this.width = this.baseWidth - (this.translateXLeft + this.translateXRight);
}
refresh() { //?? refresh?
this.oldState = this.state ? Object.assign({}, this.state) : {};
this.intermedState = {};
this.prepareData();
this.reCalc();
this.refreshRenderer();
this.refreshComponents();
}
makeChartArea() {
@ -227,7 +238,7 @@ export default class BaseChart {
this.drawArea = makeSVGGroup(
this.svg,
this.type + '-chart',
`translate(${this.translateX}, ${this.translateY})`
`translate(${this.translateXLeft}, ${this.translateY})`
);
}
@ -245,7 +256,6 @@ export default class BaseChart {
return;
}
this.intermedState = this.calcIntermedState();
this.refreshComponents();
this.animateComponents();
setTimeout(() => {
this.renderComponents();
@ -254,22 +264,15 @@ export default class BaseChart {
// (opt, should not redraw if still in animate?)
}
calcIntermedState() {}
calcIntermedState() {
this.intermedState = {};
}
// convenient component array abstractions
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
renderComponents() { this.components.forEach(c => c.render()); }
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() {}

View 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: () => {}
});
});
}
}

View File

@ -5,31 +5,21 @@ export class ChartComponent {
layerClass = '',
layerTransform = '',
make,
argsKeys,
animate
}) {
this.layerClass = layerClass; // 'y axis'
this.layerTransform = layerTransform;
this.make = make;
this.argsKeys = argsKeys;//['yAxisPositions', 'yAxisLabels'];
this.animate = animate;
this.layer = undefined;
this.store = []; //[[]] depends on indexed
}
refresh(args) {
this.chartState = args.chartState;
this.oldChartState = args.oldChartState;
this.intermedState = args.intermedState;
this.chartRenderer = args.chartRenderer;
}
refresh(args) {}
render() {
let args = this.argsKeys.map(key => this.chartState[key]);
args.unshift(this.chartRenderer);
this.store = this.make(...args);
this.store = this.make();
this.layer.textContent = '';
this.store.forEach(element => {
@ -79,7 +69,6 @@ export class IndexedChartComponent extends ChartComponent {
// 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);

View File

@ -3,6 +3,7 @@ import { getBarHeightAndYAttr } from './draw-utils';
const AXIS_TICK_LENGTH = 6;
const LABEL_MARGIN = 4;
const FONT_SIZE = 10;
const BASE_LINE_COLOR = '#dadada';
function $(expr, con) {
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') {
let height = mode === 'span' ? -1 * AXIS_TICK_LENGTH : totalHeight;
export function makeVertLine(x, label, y1, y2, options={}) {
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
let l = createSVG('line', {
className: 'line-vertical ' + options.className,
x1: 0,
x2: 0,
y1: totalHeight + AXIS_TICK_LENGTH,
y2: height,
stroke: stroke
y1: y1,
y2: y2,
styles: {
stroke: options.stroke
}
});
let text = createSVG('text', {
x: 0,
y: totalHeight + AXIS_TICK_LENGTH + LABEL_MARGIN,
y: y1 > y2 ? y1 + LABEL_MARGIN : y1 - LABEL_MARGIN - FONT_SIZE,
dy: FONT_SIZE + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'middle',
@ -168,50 +171,34 @@ export function makeVertXLine(x, label, totalHeight, mode, stroke='#dadada') {
return line;
}
export function makeHoriYLine(y, label, totalWidth, mode, pos='left') {
let lineType = '';
let w2 = mode === 'span' ? totalWidth + AXIS_TICK_LENGTH : 0;
// temp : works correctly
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';
}
export function makeHoriLine(y, label, x1, x2, options={}) {
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
if(!options.lineType) options.lineType = '';
let className = 'line-horizontal ' + options.className +
(options.lineType === "dashed" ? "dashed": "");
let l = createSVG('line', {
className: lineType === "dashed" ? "dashed": "",
className: className,
x1: x1,
x2: x2,
y1: 0,
y2: 0
y2: 0,
styles: {
stroke: options.stroke
}
});
let text = createSVG('text', {
x: textX,
x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN,
y: 0,
dy: (FONT_SIZE / 2 - 2) + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': anchor,
'text-anchor': x1 < x2 ? 'end' : 'start',
innerHTML: label+""
});
let line = createSVG('g', {
transform: `translate(0, ${y})`,
transform: `translate(0, ${ y })`,
'stroke-opacity': 1
});
@ -239,6 +226,10 @@ export class AxisChartRenderer {
this.yAxisMode = state.yAxisMode;
}
setZeroline(zeroLine) {
this.zeroLine = zeroLine;
}
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets, prevX, prevY) {
let totalWidth = this.unitWidth - args.spaceWidth;
@ -275,14 +266,63 @@ export class AxisChartRenderer {
});
}
// temp: stroke
xLine(x, label, pos='bottom', stroke='', mode=this.xAxisMode) {
xLine(x, label, options={}) {
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
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) {
return makeHoriYLine(y, label, this.totalWidth, mode, pos);
yLine(y, label, options={}) {
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() {}

1
src/js/utils/margins.js Normal file
View File

@ -0,0 +1 @@
export const Y_AXIS_MARGIN = 60;

View File

@ -4,6 +4,12 @@
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
"Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
.multiaxis-chart {
.line-horizontal, .y-axis-guide {
stroke-width: 2px;
}
}
.graph-focus-margin {
margin: 0px 5%;
}
@ -53,9 +59,9 @@
.axis, .chart-label {
fill: #555b51;
// temp commented
// line {
// stroke: #dadada;
// }
line {
stroke: #dadada;
}
}
.percentage-graph {
.progress {