axis chart renderer

This commit is contained in:
pratu16x7 2017-12-04 05:00:05 +05:30
parent f2616bfa08
commit 02685dab7e
14 changed files with 492 additions and 522 deletions

View File

@ -247,62 +247,6 @@ function fillArray(array, count, element, start=false) {
const MIN_BAR_PERCENT_HEIGHT = 0.01; const MIN_BAR_PERCENT_HEIGHT = 0.01;
function getXLineProps(totalHeight, mode) {
let startAt = totalHeight + 6, height, textStartAt, axisLineClass = '';
if(mode === 'span') { // long spanning lines
startAt = -7;
height = totalHeight + 15;
textStartAt = totalHeight + 25;
} else if(mode === 'tick'){ // short label lines
startAt = totalHeight;
height = 6;
textStartAt = 9;
axisLineClass = 'x-axis-label';
}
return [startAt, height, textStartAt, axisLineClass];
}
// export function getYLineProps(totalWidth, mode, specific=false) {
function getYLineProps(totalWidth, mode) {
// if(specific) {
// return[totalWidth, totalWidth + 5, 'specific-value', 0];
// }
let width, text_end_at = -9, axisLineClass = '', startAt = 0;
if(mode === 'span') { // long spanning lines
width = totalWidth + 6;
startAt = -6;
} else if(mode === 'tick'){ // short label lines
width = -6;
axisLineClass = 'y-axis-label';
}
return [width, text_end_at, axisLineClass, startAt];
}
// let char_width = 8;
// let allowed_space = avg_unit_width * 1.5;
// let allowed_letters = allowed_space / 8;
// return values.map((value, i) => {
// let space_taken = getStringWidth(value, char_width) + 2;
// if(space_taken > allowed_space) {
// if(is_series) {
// // Skip some axis lines if X axis is a series
// let skips = 1;
// while((space_taken/skips)*2 > allowed_space) {
// skips++;
// }
// if(i % skips !== 0) {
// return;
// }
// } else {
// value = value.slice(0, allowed_letters-3) + " ...";
// }
// }
function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) { function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
let height, y; let height, y;
if (yTop <= zeroLine) { if (yTop <= zeroLine) {
@ -338,11 +282,30 @@ function equilizeNoOfElements(array1, array2,
return [array1, array2]; return [array1, array2];
} }
const X_LABEL_CLASS = 'x-value-text'; // let char_width = 8;
const Y_LABEL_CLASS = 'y-value-text'; // let allowed_space = avgUnitWidth * 1.5;
// let allowed_letters = allowed_space / 8;
// const X_AXIS_LINE_CLASS = 'x-value-text'; // return values.map((value, i) => {
// const Y_AXIS_LINE_CLASS = 'y-value-text'; // let space_taken = getStringWidth(value, char_width) + 2;
// if(space_taken > allowed_space) {
// if(is_series) {
// // Skip some axis lines if X axis is a series
// let skips = 1;
// while((space_taken/skips)*2 > allowed_space) {
// skips++;
// }
// if(i % skips !== 0) {
// return;
// }
// } else {
// value = value.slice(0, allowed_letters-3) + " ...";
// }
// }
const AXIS_TICK_LENGTH = 6;
const LABEL_MARGIN = 4;
const FONT_SIZE = 10;
function $$1(expr, con) { function $$1(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null; return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
@ -472,23 +435,92 @@ function makeText(className, x, y, content) {
className: className, className: className,
x: x, x: x,
y: y, y: y,
dy: '.32em', dy: (FONT_SIZE / 2) + 'px',
'font-size': FONT_SIZE + 'px',
innerHTML: content innerHTML: content
}); });
} }
var AxisChartRenderer = (function() { function makeVertXLine(x, label, totalHeight, mode) {
var AxisChartRenderer = function(totalHeight, totalWidth, zeroLine, avgUnitWidth, xAxisMode, yAxisMode) { let height = mode === 'span' ? -1 * AXIS_TICK_LENGTH : totalHeight;
this.totalHeight = totalHeight;
this.totalWidth = totalWidth;
this.zeroLine = zeroLine;
this.avgUnitWidth = avgUnitWidth;
this.xAxisMode = xAxisMode;
this.yAxisMode = yAxisMode;
};
AxisChartRenderer.prototype = { let l = createSVG('line', {
bar: function (x, yTop, args, color, index, datasetIndex, noOfDatasets) { x1: 0,
x2: 0,
y1: totalHeight + AXIS_TICK_LENGTH,
y2: height
});
let text = createSVG('text', {
x: 0,
y: totalHeight + AXIS_TICK_LENGTH + LABEL_MARGIN,
dy: FONT_SIZE + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'middle',
innerHTML: label
});
let line = createSVG('g', {
transform: `translate(${ x }, 0)`
});
line.appendChild(l);
line.appendChild(text);
return line;
}
function makeHoriYLine(y, label, totalWidth, mode) {
let lineType = '';
let width = mode === 'span' ? totalWidth + AXIS_TICK_LENGTH : AXIS_TICK_LENGTH;
let l = createSVG('line', {
className: lineType === "dashed" ? "dashed": "",
x1: -1 * AXIS_TICK_LENGTH,
x2: width,
y1: 0,
y2: 0
});
let text = createSVG('text', {
x: -1 * (LABEL_MARGIN + AXIS_TICK_LENGTH),
y: 0,
dy: (FONT_SIZE / 2 - 2) + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'end',
innerHTML: label+""
});
let line = createSVG('g', {
transform: `translate(0, ${y})`,
'stroke-opacity': 1
});
if(text === 0 || text === '0') {
line.style.stroke = "rgba(27, 31, 35, 0.6)";
}
line.appendChild(l);
line.appendChild(text);
return line;
}
class AxisChartRenderer {
constructor(state) {
this.updateState(state);
}
updateState(state) {
this.totalHeight = state.totalHeight;
this.totalWidth = state.totalWidth;
this.zeroLine = state.zeroLine;
this.avgUnitWidth = state.avgUnitWidth;
this.xAxisMode = state.xAxisMode;
this.yAxisMode = state.yAxisMode;
}
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets) {
let totalWidth = this.avgUnitWidth - args.spaceWidth; let totalWidth = this.avgUnitWidth - args.spaceWidth;
let startX = x - totalWidth/2; let startX = x - totalWidth/2;
@ -506,9 +538,9 @@ var AxisChartRenderer = (function() {
width: width, width: width,
height: height height: height
}); });
}, }
dot: function(x, y, args, color, index) { dot(x, y, args, color, index) {
return createSVG('circle', { return createSVG('circle', {
style: `fill: ${color}`, style: `fill: ${color}`,
'data-point-index': index, 'data-point-index': index,
@ -516,81 +548,23 @@ var AxisChartRenderer = (function() {
cy: y, cy: y,
r: args.radius r: args.radius
}); });
}, }
xLine: function(x, label, mode=this.xAxisMode) { xLine(x, label, mode=this.xAxisMode) {
// Draw X axis line in span/tick mode with optional label // Draw X axis line in span/tick mode with optional label
let [startAt, height, textStartAt, axisLineClass] = getXLineProps(this.totalHeight, mode); return makeVertXLine(x, label, this.totalHeight, mode);
let l = createSVG('line', { }
x1: 0,
x2: 0,
y1: startAt,
y2: height
});
let text = createSVG('text', { yLine(y, label, mode=this.yAxisMode) {
className: X_LABEL_CLASS, return makeHoriYLine(y, label, this.totalWidth, mode);
x: 0, }
y: textStartAt,
dy: '.71em',
innerHTML: label
});
let line = createSVG('g', { xMarker() {}
className: `tick ${axisLineClass}`, yMarker() {}
transform: `translate(${ x }, 0)`
});
line.appendChild(l); xRegion() {}
line.appendChild(text); yRegion() {}
}
return line;
},
yLine: function(y, label, mode=this.yAxisMode) {
// TODO: stroke type
let lineType = '';
let [width, textEndAt, axisLineClass, startAt] = getYLineProps(this.totalWidth, mode);
let l = createSVG('line', {
className: lineType === "dashed" ? "dashed": "",
x1: startAt,
x2: width,
y1: 0,
y2: 0
});
let text = createSVG('text', {
className: Y_LABEL_CLASS,
x: textEndAt,
y: 0,
dy: '.32em',
innerHTML: label+""
});
let line = createSVG('g', {
className: `tick ${axisLineClass}`,
transform: `translate(0, ${y})`,
'stroke-opacity': 1
});
// if(darker) {
// line.style.stroke = "rgba(27, 31, 35, 0.6)";
// }
line.appendChild(l);
line.appendChild(text);
return line;
},
xRegion: function(x1, x2, label) { },
yRegion: function(y1, y2, label) { }
};
return AxisChartRenderer;
})();
const PRESET_COLOR_MAP = { const PRESET_COLOR_MAP = {
'light-blue': '#7cd6fd', 'light-blue': '#7cd6fd',
@ -725,10 +699,12 @@ class BaseChart {
this.setColors(); this.setColors();
this.setMargins(); this.setMargins();
// constants
this.config = { this.config = {
showTooltip: 1, showTooltip: 1,
showLegend: 1, showLegend: 1,
isNavigable: 0 isNavigable: 0,
animate: 1
}; };
} }
@ -770,17 +746,21 @@ class BaseChart {
parseData() { parseData() {
let data = this.rawChartArgs.data; let data = this.rawChartArgs.data;
// Check and all let valid = this.checkData(data);
if(!valid) return false;
if(!this.config.animate) {
// If all good
this.data = data; this.data = data;
} else {
[this.data, this.firstUpdateData] =
this.getFirstUpdateData(data);
}
return true; return true;
} }
checkData() {}
getFirstUpdateData(data) {}
setup() { setup() {
if(this.validate()) { if(this.validate()) {
this._setup(); this._setup();
@ -791,56 +771,19 @@ class BaseChart {
this.bindWindowEvents(); this.bindWindowEvents();
this.setupConstants(); this.setupConstants();
// this.setupComponents();
this.makeContainer(); this.makeContainer();
this.makeTooltip(); // without binding this.makeTooltip(); // without binding
this.draw(true); this.draw(true);
} }
draw(init=false) {
// (draw everything, layers, groups, units)
this.calc();
this.setupRenderer(); // this chart's rendered with the config
this.setupComponents();
this.makeChartArea();
this.makeLayers();
this.renderComponents(); // with zero values
this.renderLegend();
this.setupNavigation(init);
if(init) this.update(this.data);
}
bindWindowEvents() { bindWindowEvents() {
window.addEventListener('resize', () => this.draw()); window.addEventListener('resize orientationchange', () => this.draw());
window.addEventListener('orientationchange', () => this.draw());
}
calcWidth() {
let outerAnnotationsWidth = 0;
// let charWidth = 8;
// this.specificValues.map(val => {
// let strWidth = getStringWidth((val.title + ""), charWidth);
// if(strWidth > outerAnnotationsWidth) {
// outerAnnotationsWidth = strWidth - 40;
// }
// });
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
this.width = this.baseWidth - this.translateX * 2;
} }
setupConstants() {} setupConstants() {}
calc() {
this.calcWidth();
this.reCalc();
}
setupRenderer() {}
setupComponents() { setupComponents() {
// Components config // Components config
this.components = []; this.components = [];
@ -863,6 +806,58 @@ class BaseChart {
this.statsWrapper = this.container.querySelector('.graph-stats-container'); this.statsWrapper = this.container.querySelector('.graph-stats-container');
} }
makeTooltip() {
this.tip = new SvgTip({
parent: this.chartWrapper,
colors: this.colors
});
this.bindTooltip();
}
draw(init=false) {
// difference from update(): draw the whole object due to groudbreaking event (init, resize, etc.)
// (draw everything, layers, groups, units)
this.calc();
this.refreshRenderer(); // this chart's rendered with the config
this.setupComponents();
this.makeChartArea();
this.makeLayers();
this.renderComponents(); // with zero values
this.renderLegend();
this.setupNavigation(init);
if(this.config.animate) this.update(this.firstUpdateData);
}
update() {
// difference from draw(): yes you do rerender everything here as well,
// but not things like the chart itself, mosty only at component level
this.reCalc();
this.reRender();
}
refreshRenderer() {}
calcWidth() {
let outerAnnotationsWidth = 0;
// let charWidth = 8;
// this.specificValues.map(val => {
// let strWidth = getStringWidth((val.title + ""), charWidth);
// if(strWidth > outerAnnotationsWidth) {
// outerAnnotationsWidth = strWidth - 40;
// }
// });
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
this.width = this.baseWidth - this.translateX * 2;
}
calc() {
this.calcWidth();
this.reCalc();
}
makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chartWrapper, this.chartWrapper,
@ -896,11 +891,6 @@ class BaseChart {
}); });
} }
update() {
this.reCalc();
this.reRender();
}
reCalc() { reCalc() {
// Will update values(state) // Will update values(state)
// Will recalc specific parts depending on the update // Will recalc specific parts depending on the update
@ -931,13 +921,6 @@ class BaseChart {
calcInitStage() {} calcInitStage() {}
makeTooltip() {
this.tip = new SvgTip({
parent: this.chartWrapper,
colors: this.colors
});
this.bindTooltip();
}
renderLegend() {} renderLegend() {}
@ -1391,7 +1374,7 @@ class AxisChart extends BaseChart {
this.is_series = args.is_series; this.is_series = args.is_series;
this.format_tooltip_y = args.format_tooltip_y; this.format_tooltip_y = args.format_tooltip_y;
this.format_tooltip_x = args.format_tooltip_x; this.format_tooltip_x = args.format_tooltip_x;
this.zero_line = this.height; this.zeroLine = this.height;
} }
parseData() { parseData() {
@ -1457,22 +1440,27 @@ class AxisChart extends BaseChart {
} }
// this should be inherent in BaseChart // this should be inherent in BaseChart
getRenderer() { refreshRenderer() {
// These args are basically the current state/config of the chart, // These args are basically the current state of the chart,
// with constant and alive params mixed // with constant and alive params mixed
return new AxisChartRenderer(this.height, this.width, this.renderer = new AxisChartRenderer({
this.zero_line, this.avg_unit_width, this.xAxisMode, this.yAxisMode); totalHeight: this.height,
totalWidth: this.width,
zeroLine: this.zeroLine,
avgUnitWidth: this.avgUnitWidth,
xAxisMode: this.xAxisMode,
yAxisMode: this.yAxisMode
});
} }
setupComponents() { setupComponents() {
// Must have access to all current data things // Must have access to all current data things
let self = this; let self = this;
let renderer = this.getRenderer();
this.yAxis = { this.yAxis = {
layerClass: 'y axis', layerClass: 'y axis',
layer: undefined, layer: undefined,
make: self.makeYLines, make: self.makeYLines.bind(self),
makeArgs: [renderer, self.yAxisPositions, self.yAxisLabels], makeArgs: [self.yAxisPositions, self.yAxisLabels],
store: [], store: [],
// animate? or update? will come to while implementing // animate? or update? will come to while implementing
animate: self.animateYLines, animate: self.animateYLines,
@ -1481,10 +1469,9 @@ class AxisChart extends BaseChart {
this.xAxis = { this.xAxis = {
layerClass: 'x axis', layerClass: 'x axis',
layer: undefined, layer: undefined,
make: self.makeXLines, make: self.makeXLines.bind(self),
// TODO: better context of renderer
// TODO: will implement series skip with avgUnitWidth and isSeries later // TODO: will implement series skip with avgUnitWidth and isSeries later
makeArgs: [renderer, self.xPositions, self.xAxisLabels], makeArgs: [self.xPositions, self.xAxisLabels],
store: [], store: [],
animate: self.animateXLines animate: self.animateXLines
}; };
@ -1535,12 +1522,12 @@ class AxisChart extends BaseChart {
} }
setup_x() { setup_x() {
this.set_avg_unit_width_and_x_offset(); this.set_avgUnitWidth_and_x_offset();
if(this.xPositions) { if(this.xPositions) {
this.x_old_axis_positions = this.xPositions.slice(); this.x_old_axis_positions = this.xPositions.slice();
} }
this.xPositions = this.xAxisLabels.map((d, i) => this.xPositions = this.xAxisLabels.map((d, i) =>
floatTwo(this.x_offset + i * this.avg_unit_width)); floatTwo(this.x_offset + i * this.avgUnitWidth));
if(!this.x_old_axis_positions) { if(!this.x_old_axis_positions) {
this.x_old_axis_positions = this.xPositions.slice(); this.x_old_axis_positions = this.xPositions.slice();
@ -1592,28 +1579,27 @@ class AxisChart extends BaseChart {
zero_index = (-1) * max / interval + (y_pts.length - 1); zero_index = (-1) * max / interval + (y_pts.length - 1);
} }
if(this.zero_line) this.old_zero_line = this.zero_line; if(this.zeroLine) this.old_zeroLine = this.zeroLine;
this.zero_line = this.height - (zero_index * interval_height); this.zeroLine = this.height - (zero_index * interval_height);
if(!this.old_zero_line) this.old_zero_line = this.zero_line; if(!this.old_zeroLine) this.old_zeroLine = this.zeroLine;
// Make positions arrays for y elements // Make positions arrays for y elements
if(this.yAxisPositions) this.oldYAxisPositions = this.yAxisPositions; if(this.yAxisPositions) this.oldYAxisPositions = this.yAxisPositions;
this.yAxisPositions = this.yAxisLabels.map(d => this.zero_line - d * this.multiplier); this.yAxisPositions = this.yAxisLabels.map(d => this.zeroLine - d * this.multiplier);
if(!this.oldYAxisPositions) this.oldYAxisPositions = this.yAxisPositions; if(!this.oldYAxisPositions) this.oldYAxisPositions = this.yAxisPositions;
// if(this.yAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions; // if(this.yAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions;
// this.yAnnotationPositions = this.specific_values.map(d => this.zero_line - d.value * this.multiplier); // this.yAnnotationPositions = this.specific_values.map(d => this.zeroLine - d.value * this.multiplier);
// if(!this.oldYAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions; // if(!this.oldYAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions;
} }
makeXLines(renderer, positions, values) { makeXLines(positions, values) {
// TODO: draw as per condition // TODO: draw as per condition (with/without label etc.)
return positions.map((position, i) => this.renderer.xLine(position, values[i]));
return positions.map((position, i) => renderer.xLine(position, values[i]));
} }
makeYLines(renderer, positions, values) { makeYLines(positions, values) {
return positions.map((position, i) => renderer.yLine(position, values[i])); return positions.map((position, i) => this.renderer.yLine(position, values[i]));
} }
draw_graph(init=false) { draw_graph(init=false) {
@ -1642,7 +1628,7 @@ class AxisChart extends BaseChart {
let data = []; let data = [];
this.y.map((d, i) => { this.y.map((d, i) => {
// Anim: Don't draw initial values, store them and update later // Anim: Don't draw initial values, store them and update later
d.yUnitPositions = new Array(d.values.length).fill(this.zero_line); // no value d.yUnitPositions = new Array(d.values.length).fill(this.zeroLine); // no value
data.push({values: d.values}); data.push({values: d.values});
d.svg_units = []; d.svg_units = [];
@ -1686,7 +1672,7 @@ class AxisChart extends BaseChart {
units_group.textContent = ''; units_group.textContent = '';
units_array.length = 0; units_array.length = 0;
let unit_AxisChartRenderer = new AxisChartRenderer(this.height, this.zero_line, this.avg_unit_width); let unit_AxisChartRenderer = new AxisChartRenderer(this.height, this.zeroLine, this.avgUnitWidth);
y_values.map((y, i) => { y_values.map((y, i) => {
let data_unit = unit_AxisChartRenderer[unit.type]( let data_unit = unit_AxisChartRenderer[unit.type](
@ -1734,8 +1720,8 @@ class AxisChart extends BaseChart {
for(var i=this.xPositions.length - 1; i >= 0 ; i--) { for(var i=this.xPositions.length - 1; i >= 0 ; i--) {
let x_val = this.xPositions[i]; let x_val = this.xPositions[i];
// let delta = i === 0 ? this.avg_unit_width : x_val - this.xPositions[i-1]; // let delta = i === 0 ? this.avgUnitWidth : x_val - this.xPositions[i-1];
if(relX > x_val - this.avg_unit_width/2) { if(relX > x_val - this.avgUnitWidth/2) {
let x = x_val + this.translateX; let x = x_val + this.translateX;
let y = this.y_min_tops[i] + this.translateY; let y = this.y_min_tops[i] + this.translateY;
@ -1778,7 +1764,7 @@ class AxisChart extends BaseChart {
this.calcYDependencies(); this.calcYDependencies();
// Got the values? Now begin drawing // Got the values? Now begin drawing
this.animator = new Animator(this.height, this.width, this.zero_line, this.avg_unit_width); this.animator = new Animator(this.height, this.width, this.zeroLine, this.avgUnitWidth);
this.animate_graphs(); this.animate_graphs();
@ -1927,9 +1913,9 @@ class AxisChart extends BaseChart {
fire(this.parent, "data-select", this.getDataPoint()); fire(this.parent, "data-select", this.getDataPoint());
} }
set_avg_unit_width_and_x_offset() { set_avgUnitWidth_and_x_offset() {
// Set the ... you get it // Set the ... you get it
this.avg_unit_width = this.width/(this.xAxisLabels.length - 1); this.avgUnitWidth = this.width/(this.xAxisLabels.length - 1);
this.x_offset = 0; this.x_offset = 0;
} }
@ -1948,7 +1934,7 @@ class AxisChart extends BaseChart {
calcYDependencies() { calcYDependencies() {
this.y_min_tops = new Array(this.xAxisLabels.length).fill(9999); this.y_min_tops = new Array(this.xAxisLabels.length).fill(9999);
this.y.map(d => { this.y.map(d => {
d.yUnitPositions = d.values.map( val => floatTwo(this.zero_line - val * this.multiplier)); d.yUnitPositions = d.values.map( val => floatTwo(this.zeroLine - val * this.multiplier));
d.yUnitPositions.map( (yUnitPosition, i) => { d.yUnitPositions.map( (yUnitPosition, i) => {
if(yUnitPosition < this.y_min_tops[i]) { if(yUnitPosition < this.y_min_tops[i]) {
this.y_min_tops[i] = yUnitPosition; this.y_min_tops[i] = yUnitPosition;
@ -1972,11 +1958,11 @@ class BarChart extends AxisChart {
setup_values() { setup_values() {
super.setup_values(); super.setup_values();
this.x_offset = this.avg_unit_width; this.x_offset = this.avgUnitWidth;
this.unit_args = { this.unit_args = {
type: 'bar', type: 'bar',
args: { args: {
spaceWidth: this.avg_unit_width/2, spaceWidth: this.avgUnitWidth/2,
} }
}; };
} }
@ -2034,9 +2020,9 @@ class BarChart extends AxisChart {
this.updateCurrentDataPoint(this.currentIndex + 1); this.updateCurrentDataPoint(this.currentIndex + 1);
} }
set_avg_unit_width_and_x_offset() { set_avgUnitWidth_and_x_offset() {
this.avg_unit_width = this.width/(this.xAxisLabels.length + 1); this.avgUnitWidth = this.width/(this.xAxisLabels.length + 1);
this.x_offset = this.avg_unit_width; this.x_offset = this.avgUnitWidth;
} }
} }
@ -2118,7 +2104,7 @@ class LineChart extends AxisChart {
fill_region_for_dataset(d, color, points_str) { fill_region_for_dataset(d, color, points_str) {
let gradient_id = makeGradient(this.svg_defs, color, true); let gradient_id = makeGradient(this.svg_defs, color, true);
let pathStr = "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`; let pathStr = "M" + `0,${this.zeroLine}L` + points_str + `L${this.width},${this.zeroLine}`;
d.regionPath = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`); d.regionPath = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`);
this.paths_groups[d.index].appendChild(d.regionPath); this.paths_groups[d.index].appendChild(d.regionPath);

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{font-size:11px;fill:#555b51}.chart-container .axis line,.chart-container .chart-label line{stroke:#dadada}.chart-container .percentage-graph .progress{margin-bottom:0}.chart-container .data-points circle{stroke:#fff;stroke-width:2}.chart-container .path-group path{fill:none;stroke-opacity:1;stroke-width:2px}.chart-container line.dashed{stroke-dasharray:5,3}.chart-container .tick.x-axis-label{display:block}.chart-container .tick .specific-value{text-anchor:start}.chart-container .tick .y-value-text{text-anchor:end}.chart-container .tick .x-value-text{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 .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 .data-points circle{stroke:#fff;stroke-width:2}.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

@ -13,7 +13,7 @@ export default class AxisChart extends BaseChart {
this.is_series = args.is_series; this.is_series = args.is_series;
this.format_tooltip_y = args.format_tooltip_y; this.format_tooltip_y = args.format_tooltip_y;
this.format_tooltip_x = args.format_tooltip_x; this.format_tooltip_x = args.format_tooltip_x;
this.zero_line = this.height; this.zeroLine = this.height;
} }
parseData() { parseData() {
@ -79,22 +79,27 @@ export default class AxisChart extends BaseChart {
} }
// this should be inherent in BaseChart // this should be inherent in BaseChart
getRenderer() { refreshRenderer() {
// These args are basically the current state/config of the chart, // These args are basically the current state of the chart,
// with constant and alive params mixed // with constant and alive params mixed
return new AxisChartRenderer(this.height, this.width, this.renderer = new AxisChartRenderer({
this.zero_line, this.avg_unit_width, this.xAxisMode, this.yAxisMode); totalHeight: this.height,
totalWidth: this.width,
zeroLine: this.zeroLine,
avgUnitWidth: this.avgUnitWidth,
xAxisMode: this.xAxisMode,
yAxisMode: this.yAxisMode
});
} }
setupComponents() { setupComponents() {
// Must have access to all current data things // Must have access to all current data things
let self = this; let self = this;
let renderer = this.getRenderer();
this.yAxis = { this.yAxis = {
layerClass: 'y axis', layerClass: 'y axis',
layer: undefined, layer: undefined,
make: self.makeYLines, make: self.makeYLines.bind(self),
makeArgs: [renderer, self.yAxisPositions, self.yAxisLabels], makeArgs: [self.yAxisPositions, self.yAxisLabels],
store: [], store: [],
// animate? or update? will come to while implementing // animate? or update? will come to while implementing
animate: self.animateYLines, animate: self.animateYLines,
@ -103,10 +108,9 @@ export default class AxisChart extends BaseChart {
this.xAxis = { this.xAxis = {
layerClass: 'x axis', layerClass: 'x axis',
layer: undefined, layer: undefined,
make: self.makeXLines, make: self.makeXLines.bind(self),
// TODO: better context of renderer
// TODO: will implement series skip with avgUnitWidth and isSeries later // TODO: will implement series skip with avgUnitWidth and isSeries later
makeArgs: [renderer, self.xPositions, self.xAxisLabels], makeArgs: [self.xPositions, self.xAxisLabels],
store: [], store: [],
animate: self.animateXLines animate: self.animateXLines
}; };
@ -157,12 +161,12 @@ export default class AxisChart extends BaseChart {
} }
setup_x() { setup_x() {
this.set_avg_unit_width_and_x_offset(); this.set_avgUnitWidth_and_x_offset();
if(this.xPositions) { if(this.xPositions) {
this.x_old_axis_positions = this.xPositions.slice(); this.x_old_axis_positions = this.xPositions.slice();
} }
this.xPositions = this.xAxisLabels.map((d, i) => this.xPositions = this.xAxisLabels.map((d, i) =>
floatTwo(this.x_offset + i * this.avg_unit_width)); floatTwo(this.x_offset + i * this.avgUnitWidth));
if(!this.x_old_axis_positions) { if(!this.x_old_axis_positions) {
this.x_old_axis_positions = this.xPositions.slice(); this.x_old_axis_positions = this.xPositions.slice();
@ -214,28 +218,27 @@ export default class AxisChart extends BaseChart {
zero_index = (-1) * max / interval + (y_pts.length - 1); zero_index = (-1) * max / interval + (y_pts.length - 1);
} }
if(this.zero_line) this.old_zero_line = this.zero_line; if(this.zeroLine) this.old_zeroLine = this.zeroLine;
this.zero_line = this.height - (zero_index * interval_height); this.zeroLine = this.height - (zero_index * interval_height);
if(!this.old_zero_line) this.old_zero_line = this.zero_line; if(!this.old_zeroLine) this.old_zeroLine = this.zeroLine;
// Make positions arrays for y elements // Make positions arrays for y elements
if(this.yAxisPositions) this.oldYAxisPositions = this.yAxisPositions; if(this.yAxisPositions) this.oldYAxisPositions = this.yAxisPositions;
this.yAxisPositions = this.yAxisLabels.map(d => this.zero_line - d * this.multiplier); this.yAxisPositions = this.yAxisLabels.map(d => this.zeroLine - d * this.multiplier);
if(!this.oldYAxisPositions) this.oldYAxisPositions = this.yAxisPositions; if(!this.oldYAxisPositions) this.oldYAxisPositions = this.yAxisPositions;
// if(this.yAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions; // if(this.yAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions;
// this.yAnnotationPositions = this.specific_values.map(d => this.zero_line - d.value * this.multiplier); // this.yAnnotationPositions = this.specific_values.map(d => this.zeroLine - d.value * this.multiplier);
// if(!this.oldYAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions; // if(!this.oldYAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions;
} }
makeXLines(renderer, positions, values) { makeXLines(positions, values) {
// TODO: draw as per condition // TODO: draw as per condition (with/without label etc.)
return positions.map((position, i) => this.renderer.xLine(position, values[i]));
return positions.map((position, i) => renderer.xLine(position, values[i]));
} }
makeYLines(renderer, positions, values) { makeYLines(positions, values) {
return positions.map((position, i) => renderer.yLine(position, values[i])); return positions.map((position, i) => this.renderer.yLine(position, values[i]));
} }
draw_graph(init=false) { draw_graph(init=false) {
@ -264,7 +267,7 @@ export default class AxisChart extends BaseChart {
let data = []; let data = [];
this.y.map((d, i) => { this.y.map((d, i) => {
// Anim: Don't draw initial values, store them and update later // Anim: Don't draw initial values, store them and update later
d.yUnitPositions = new Array(d.values.length).fill(this.zero_line); // no value d.yUnitPositions = new Array(d.values.length).fill(this.zeroLine); // no value
data.push({values: d.values}); data.push({values: d.values});
d.svg_units = []; d.svg_units = [];
@ -308,7 +311,7 @@ export default class AxisChart extends BaseChart {
units_group.textContent = ''; units_group.textContent = '';
units_array.length = 0; units_array.length = 0;
let unit_AxisChartRenderer = new AxisChartRenderer(this.height, this.zero_line, this.avg_unit_width); let unit_AxisChartRenderer = new AxisChartRenderer(this.height, this.zeroLine, this.avgUnitWidth);
y_values.map((y, i) => { y_values.map((y, i) => {
let data_unit = unit_AxisChartRenderer[unit.type]( let data_unit = unit_AxisChartRenderer[unit.type](
@ -356,8 +359,8 @@ export default class AxisChart extends BaseChart {
for(var i=this.xPositions.length - 1; i >= 0 ; i--) { for(var i=this.xPositions.length - 1; i >= 0 ; i--) {
let x_val = this.xPositions[i]; let x_val = this.xPositions[i];
// let delta = i === 0 ? this.avg_unit_width : x_val - this.xPositions[i-1]; // let delta = i === 0 ? this.avgUnitWidth : x_val - this.xPositions[i-1];
if(relX > x_val - this.avg_unit_width/2) { if(relX > x_val - this.avgUnitWidth/2) {
let x = x_val + this.translateX; let x = x_val + this.translateX;
let y = this.y_min_tops[i] + this.translateY; let y = this.y_min_tops[i] + this.translateY;
@ -400,7 +403,7 @@ export default class AxisChart extends BaseChart {
this.calcYDependencies(); this.calcYDependencies();
// Got the values? Now begin drawing // Got the values? Now begin drawing
this.animator = new Animator(this.height, this.width, this.zero_line, this.avg_unit_width); this.animator = new Animator(this.height, this.width, this.zeroLine, this.avgUnitWidth);
this.animate_graphs(); this.animate_graphs();
@ -549,9 +552,9 @@ export default class AxisChart extends BaseChart {
fire(this.parent, "data-select", this.getDataPoint()); fire(this.parent, "data-select", this.getDataPoint());
} }
set_avg_unit_width_and_x_offset() { set_avgUnitWidth_and_x_offset() {
// Set the ... you get it // Set the ... you get it
this.avg_unit_width = this.width/(this.xAxisLabels.length - 1); this.avgUnitWidth = this.width/(this.xAxisLabels.length - 1);
this.x_offset = 0; this.x_offset = 0;
} }
@ -570,7 +573,7 @@ export default class AxisChart extends BaseChart {
calcYDependencies() { calcYDependencies() {
this.y_min_tops = new Array(this.xAxisLabels.length).fill(9999); this.y_min_tops = new Array(this.xAxisLabels.length).fill(9999);
this.y.map(d => { this.y.map(d => {
d.yUnitPositions = d.values.map( val => floatTwo(this.zero_line - val * this.multiplier)); d.yUnitPositions = d.values.map( val => floatTwo(this.zeroLine - val * this.multiplier));
d.yUnitPositions.map( (yUnitPosition, i) => { d.yUnitPositions.map( (yUnitPosition, i) => {
if(yUnitPosition < this.y_min_tops[i]) { if(yUnitPosition < this.y_min_tops[i]) {
this.y_min_tops[i] = yUnitPosition; this.y_min_tops[i] = yUnitPosition;

View File

@ -12,11 +12,11 @@ export default class BarChart extends AxisChart {
setup_values() { setup_values() {
super.setup_values(); super.setup_values();
this.x_offset = this.avg_unit_width; this.x_offset = this.avgUnitWidth;
this.unit_args = { this.unit_args = {
type: 'bar', type: 'bar',
args: { args: {
spaceWidth: this.avg_unit_width/2, spaceWidth: this.avgUnitWidth/2,
} }
}; };
} }
@ -74,8 +74,8 @@ export default class BarChart extends AxisChart {
this.updateCurrentDataPoint(this.currentIndex + 1); this.updateCurrentDataPoint(this.currentIndex + 1);
} }
set_avg_unit_width_and_x_offset() { set_avgUnitWidth_and_x_offset() {
this.avg_unit_width = this.width/(this.xAxisLabels.length + 1); this.avgUnitWidth = this.width/(this.xAxisLabels.length + 1);
this.x_offset = this.avg_unit_width; this.x_offset = this.avgUnitWidth;
} }
} }

View File

@ -41,10 +41,12 @@ export default class BaseChart {
this.setColors(); this.setColors();
this.setMargins(); this.setMargins();
// constants
this.config = { this.config = {
showTooltip: 1, showTooltip: 1,
showLegend: 1, showLegend: 1,
isNavigable: 0 isNavigable: 0,
animate: 1
}; };
} }
@ -88,17 +90,21 @@ export default class BaseChart {
parseData() { parseData() {
let data = this.rawChartArgs.data; let data = this.rawChartArgs.data;
// Check and all let valid = this.checkData(data);
if(!valid) return false;
if(!this.config.animate) {
// If all good
this.data = data; this.data = data;
} else {
[this.data, this.firstUpdateData] =
this.getFirstUpdateData(data);
}
return true; return true;
} }
checkData() {}
getFirstUpdateData(data) {}
setup() { setup() {
if(this.validate()) { if(this.validate()) {
this._setup(); this._setup();
@ -109,56 +115,19 @@ export default class BaseChart {
this.bindWindowEvents(); this.bindWindowEvents();
this.setupConstants(); this.setupConstants();
// this.setupComponents();
this.makeContainer(); this.makeContainer();
this.makeTooltip(); // without binding this.makeTooltip(); // without binding
this.draw(true); this.draw(true);
} }
draw(init=false) {
// (draw everything, layers, groups, units)
this.calc();
this.setupRenderer(); // this chart's rendered with the config
this.setupComponents();
this.makeChartArea();
this.makeLayers();
this.renderComponents(); // with zero values
this.renderLegend();
this.setupNavigation(init);
if(init) this.update(this.data);
}
bindWindowEvents() { bindWindowEvents() {
window.addEventListener('resize', () => this.draw()); window.addEventListener('resize orientationchange', () => this.draw());
window.addEventListener('orientationchange', () => this.draw());
}
calcWidth() {
let outerAnnotationsWidth = 0;
// let charWidth = 8;
// this.specificValues.map(val => {
// let strWidth = getStringWidth((val.title + ""), charWidth);
// if(strWidth > outerAnnotationsWidth) {
// outerAnnotationsWidth = strWidth - 40;
// }
// });
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
this.width = this.baseWidth - this.translateX * 2;
} }
setupConstants() {} setupConstants() {}
calc() {
this.calcWidth();
this.reCalc();
}
setupRenderer() {}
setupComponents() { setupComponents() {
// Components config // Components config
this.components = []; this.components = [];
@ -181,6 +150,58 @@ export default class BaseChart {
this.statsWrapper = this.container.querySelector('.graph-stats-container'); this.statsWrapper = this.container.querySelector('.graph-stats-container');
} }
makeTooltip() {
this.tip = new SvgTip({
parent: this.chartWrapper,
colors: this.colors
});
this.bindTooltip();
}
draw(init=false) {
// difference from update(): draw the whole object due to groudbreaking event (init, resize, etc.)
// (draw everything, layers, groups, units)
this.calc();
this.refreshRenderer() // this chart's rendered with the config
this.setupComponents();
this.makeChartArea();
this.makeLayers();
this.renderComponents(); // with zero values
this.renderLegend();
this.setupNavigation(init);
if(this.config.animate) this.update(this.firstUpdateData);
}
update() {
// difference from draw(): yes you do rerender everything here as well,
// but not things like the chart itself, mosty only at component level
this.reCalc();
this.reRender();
}
refreshRenderer() {}
calcWidth() {
let outerAnnotationsWidth = 0;
// let charWidth = 8;
// this.specificValues.map(val => {
// let strWidth = getStringWidth((val.title + ""), charWidth);
// if(strWidth > outerAnnotationsWidth) {
// outerAnnotationsWidth = strWidth - 40;
// }
// });
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
this.width = this.baseWidth - this.translateX * 2;
}
calc() {
this.calcWidth();
this.reCalc();
}
makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chartWrapper, this.chartWrapper,
@ -214,11 +235,6 @@ export default class BaseChart {
}); });
} }
update() {
this.reCalc();
this.reRender();
}
reCalc() { reCalc() {
// Will update values(state) // Will update values(state)
// Will recalc specific parts depending on the update // Will recalc specific parts depending on the update
@ -249,13 +265,6 @@ export default class BaseChart {
calcInitStage() {} calcInitStage() {}
makeTooltip() {
this.tip = new SvgTip({
parent: this.chartWrapper,
colors: this.colors
});
this.bindTooltip();
}
renderLegend() {} renderLegend() {}

View File

@ -79,7 +79,7 @@ export default class LineChart extends AxisChart {
fill_region_for_dataset(d, color, points_str) { fill_region_for_dataset(d, color, points_str) {
let gradient_id = makeGradient(this.svg_defs, color, true); let gradient_id = makeGradient(this.svg_defs, color, true);
let pathStr = "M" + `0,${this.zero_line}L` + points_str + `L${this.width},${this.zero_line}`; let pathStr = "M" + `0,${this.zeroLine}L` + points_str + `L${this.width},${this.zeroLine}`;
d.regionPath = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`); d.regionPath = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id})`);
this.paths_groups[d.index].appendChild(d.regionPath); this.paths_groups[d.index].appendChild(d.regionPath);

View File

@ -0,0 +1,2 @@
import { getBarHeightAndYAttr } from '../utils/draw-utils';

View File

@ -1,67 +1,7 @@
import { fillArray } from '../utils/helpers'; import { fillArray } from '../utils/helpers';
const AXIS_TICK_LENGTH = 6;
const LABEL_MARGIN = 4;
const MIN_BAR_PERCENT_HEIGHT = 0.01; const MIN_BAR_PERCENT_HEIGHT = 0.01;
export function verticalLineProps(start, height, label='down') {
//
}
export function getXLineProps(totalHeight, mode) {
let startAt = totalHeight + 6, height, textStartAt, axisLineClass = '';
if(mode === 'span') { // long spanning lines
startAt = -7;
height = totalHeight + 15;
textStartAt = totalHeight + 25;
} else if(mode === 'tick'){ // short label lines
startAt = totalHeight;
height = 6;
textStartAt = 9;
axisLineClass = 'x-axis-label';
}
return [startAt, height, textStartAt, axisLineClass];
}
// export function getYLineProps(totalWidth, mode, specific=false) {
export function getYLineProps(totalWidth, mode) {
// if(specific) {
// return[totalWidth, totalWidth + 5, 'specific-value', 0];
// }
let width, text_end_at = -9, axisLineClass = '', startAt = 0;
if(mode === 'span') { // long spanning lines
width = totalWidth + 6;
startAt = -6;
} else if(mode === 'tick'){ // short label lines
width = -6;
axisLineClass = 'y-axis-label';
}
return [width, text_end_at, axisLineClass, startAt];
}
// let char_width = 8;
// let allowed_space = avg_unit_width * 1.5;
// let allowed_letters = allowed_space / 8;
// return values.map((value, i) => {
// let space_taken = getStringWidth(value, char_width) + 2;
// if(space_taken > allowed_space) {
// if(is_series) {
// // Skip some axis lines if X axis is a series
// let skips = 1;
// while((space_taken/skips)*2 > allowed_space) {
// skips++;
// }
// if(i % skips !== 0) {
// return;
// }
// } else {
// value = value.slice(0, allowed_letters-3) + " ...";
// }
// }
export function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) { export function getBarHeightAndYAttr(yTop, zeroLine, totalHeight) {
let height, y; let height, y;
if (yTop <= zeroLine) { if (yTop <= zeroLine) {
@ -96,3 +36,24 @@ export function equilizeNoOfElements(array1, array2,
} }
return [array1, array2]; return [array1, array2];
} }
// let char_width = 8;
// let allowed_space = avgUnitWidth * 1.5;
// let allowed_letters = allowed_space / 8;
// return values.map((value, i) => {
// let space_taken = getStringWidth(value, char_width) + 2;
// if(space_taken > allowed_space) {
// if(is_series) {
// // Skip some axis lines if X axis is a series
// let skips = 1;
// while((space_taken/skips)*2 > allowed_space) {
// skips++;
// }
// if(i % skips !== 0) {
// return;
// }
// } else {
// value = value.slice(0, allowed_letters-3) + " ...";
// }
// }

View File

@ -1,10 +1,8 @@
import { getBarHeightAndYAttr, getXLineProps, getYLineProps } from './draw-utils'; import { getBarHeightAndYAttr } from './draw-utils';
const X_LABEL_CLASS = 'x-value-text'; const AXIS_TICK_LENGTH = 6;
const Y_LABEL_CLASS = 'y-value-text'; const LABEL_MARGIN = 4;
const FONT_SIZE = 10;
// const X_AXIS_LINE_CLASS = 'x-value-text';
// const Y_AXIS_LINE_CLASS = 'y-value-text';
function $(expr, con) { function $(expr, con) {
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null; return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
@ -134,23 +132,92 @@ export function makeText(className, x, y, content) {
className: className, className: className,
x: x, x: x,
y: y, y: y,
dy: '.32em', dy: (FONT_SIZE / 2) + 'px',
'font-size': FONT_SIZE + 'px',
innerHTML: content innerHTML: content
}); });
} }
export var AxisChartRenderer = (function() { export function makeVertXLine(x, label, totalHeight, mode) {
var AxisChartRenderer = function(totalHeight, totalWidth, zeroLine, avgUnitWidth, xAxisMode, yAxisMode) { let height = mode === 'span' ? -1 * AXIS_TICK_LENGTH : totalHeight;
this.totalHeight = totalHeight;
this.totalWidth = totalWidth;
this.zeroLine = zeroLine;
this.avgUnitWidth = avgUnitWidth;
this.xAxisMode = xAxisMode;
this.yAxisMode = yAxisMode;
};
AxisChartRenderer.prototype = { let l = createSVG('line', {
bar: function (x, yTop, args, color, index, datasetIndex, noOfDatasets) { x1: 0,
x2: 0,
y1: totalHeight + AXIS_TICK_LENGTH,
y2: height
});
let text = createSVG('text', {
x: 0,
y: totalHeight + AXIS_TICK_LENGTH + LABEL_MARGIN,
dy: FONT_SIZE + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'middle',
innerHTML: label
});
let line = createSVG('g', {
transform: `translate(${ x }, 0)`
});
line.appendChild(l);
line.appendChild(text);
return line;
}
export function makeHoriYLine(y, label, totalWidth, mode) {
let lineType = '';
let width = mode === 'span' ? totalWidth + AXIS_TICK_LENGTH : AXIS_TICK_LENGTH;
let l = createSVG('line', {
className: lineType === "dashed" ? "dashed": "",
x1: -1 * AXIS_TICK_LENGTH,
x2: width,
y1: 0,
y2: 0
});
let text = createSVG('text', {
x: -1 * (LABEL_MARGIN + AXIS_TICK_LENGTH),
y: 0,
dy: (FONT_SIZE / 2 - 2) + 'px',
'font-size': FONT_SIZE + 'px',
'text-anchor': 'end',
innerHTML: label+""
});
let line = createSVG('g', {
transform: `translate(0, ${y})`,
'stroke-opacity': 1
});
if(text === 0 || text === '0') {
line.style.stroke = "rgba(27, 31, 35, 0.6)";
}
line.appendChild(l);
line.appendChild(text);
return line;
}
export class AxisChartRenderer {
constructor(state) {
this.updateState(state);
}
updateState(state) {
this.totalHeight = state.totalHeight;
this.totalWidth = state.totalWidth;
this.zeroLine = state.zeroLine;
this.avgUnitWidth = state.avgUnitWidth;
this.xAxisMode = state.xAxisMode;
this.yAxisMode = state.yAxisMode;
}
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets) {
let totalWidth = this.avgUnitWidth - args.spaceWidth; let totalWidth = this.avgUnitWidth - args.spaceWidth;
let startX = x - totalWidth/2; let startX = x - totalWidth/2;
@ -168,9 +235,9 @@ export var AxisChartRenderer = (function() {
width: width, width: width,
height: height height: height
}); });
}, }
dot: function(x, y, args, color, index) { dot(x, y, args, color, index) {
return createSVG('circle', { return createSVG('circle', {
style: `fill: ${color}`, style: `fill: ${color}`,
'data-point-index': index, 'data-point-index': index,
@ -178,78 +245,20 @@ export var AxisChartRenderer = (function() {
cy: y, cy: y,
r: args.radius r: args.radius
}); });
}, }
xLine: function(x, label, mode=this.xAxisMode) { xLine(x, label, mode=this.xAxisMode) {
// Draw X axis line in span/tick mode with optional label // Draw X axis line in span/tick mode with optional label
let [startAt, height, textStartAt, axisLineClass] = getXLineProps(this.totalHeight, mode); return makeVertXLine(x, label, this.totalHeight, mode);
let l = createSVG('line', { }
x1: 0,
x2: 0,
y1: startAt,
y2: height
});
let text = createSVG('text', { yLine(y, label, mode=this.yAxisMode) {
className: X_LABEL_CLASS, return makeHoriYLine(y, label, this.totalWidth, mode);
x: 0, }
y: textStartAt,
dy: '.71em',
innerHTML: label
});
let line = createSVG('g', { xMarker() {}
className: `tick ${axisLineClass}`, yMarker() {}
transform: `translate(${ x }, 0)`
});
line.appendChild(l); xRegion() {}
line.appendChild(text); yRegion() {}
}
return line;
},
yLine: function(y, label, mode=this.yAxisMode) {
// TODO: stroke type
let lineType = '';
let [width, textEndAt, axisLineClass, startAt] = getYLineProps(this.totalWidth, mode);
let l = createSVG('line', {
className: lineType === "dashed" ? "dashed": "",
x1: startAt,
x2: width,
y1: 0,
y2: 0
});
let text = createSVG('text', {
className: Y_LABEL_CLASS,
x: textEndAt,
y: 0,
dy: '.32em',
innerHTML: label+""
});
let line = createSVG('g', {
className: `tick ${axisLineClass}`,
transform: `translate(0, ${y})`,
'stroke-opacity': 1
});
// if(darker) {
// line.style.stroke = "rgba(27, 31, 35, 0.6)";
// }
line.appendChild(l);
line.appendChild(text);
return line;
},
xRegion: function(x1, x2, label) { },
yRegion: function(y1, y2, label) { }
};
return AxisChartRenderer;
})();

View File

@ -51,7 +51,6 @@
} }
} }
.axis, .chart-label { .axis, .chart-label {
font-size: 11px;
fill: #555b51; fill: #555b51;
line { line {
stroke: #dadada; stroke: #dadada;
@ -78,17 +77,18 @@
line.dashed { line.dashed {
stroke-dasharray: 5,3; stroke-dasharray: 5,3;
} }
.tick { .axis-line {
&.x-axis-label { // &.x-axis-label {
display: block; // display: block;
} // }
// TODO: hack dy attr to be settable via styles
.specific-value { .specific-value {
text-anchor: start; text-anchor: start;
} }
.y-value-text { .y-line {
text-anchor: end; text-anchor: end;
} }
.x-value-text { .x-line {
text-anchor: middle; text-anchor: middle;
} }
} }