config.js, renaming

This commit is contained in:
pratu16x7 2017-12-03 15:39:29 +05:30
parent 863bd45724
commit f2616bfa08
17 changed files with 515 additions and 386 deletions

View File

@ -35,7 +35,7 @@ $.create = (tag, o) => {
return element; return element;
}; };
function get_offset(element) { function getOffset(element) {
let rect = element.getBoundingClientRect(); let rect = element.getBoundingClientRect();
return { return {
// https://stackoverflow.com/a/7436602/6495043 // https://stackoverflow.com/a/7436602/6495043
@ -662,6 +662,33 @@ const COLOR_COMPATIBLE_CHARTS = {
heatmap: [] heatmap: []
}; };
function getDifferentChart(type, current_type, args) {
if(type === current_type) return;
if(!ALL_CHART_TYPES.includes(type)) {
console.error(`'${type}' is not a valid chart type.`);
}
if(!COMPATIBLE_CHARTS[current_type].includes(type)) {
console.error(`'${current_type}' chart cannot be converted to a '${type}' chart.`);
}
// whether the new chart can use the existing colors
const useColor = COLOR_COMPATIBLE_CHARTS[current_type].includes(type);
// Okay, this is anticlimactic
// this function will need to actually be 'changeChartType(type)'
// that will update only the required elements, but for now ...
return new Chart({
parent: args.parent,
title: args.title,
data: args.data,
type: type,
height: args.height,
colors: useColor ? args.colors : undefined
});
}
class BaseChart { class BaseChart {
constructor({ constructor({
height = 240, height = 240,
@ -670,34 +697,33 @@ class BaseChart {
subtitle = '', subtitle = '',
colors = [], colors = [],
is_navigable = 0, isNavigable = 0,
type = '', type = '',
parent, parent,
data data
}) { }) {
this.raw_chart_args = arguments[0]; this.rawChartArgs = arguments[0];
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent; this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
this.title = title; this.title = title;
this.subtitle = subtitle; this.subtitle = subtitle;
this.data = data; this.isNavigable = isNavigable;
if(this.isNavigable) {
this.is_navigable = is_navigable; this.currentIndex = 0;
if(this.is_navigable) {
this.current_index = 0;
} }
this.setupConfiguration(arguments[0]); this.setupConfiguration();
} }
setupConfiguration(args) { setupConfiguration() {
// Make a this.config, that has stuff like showTooltip, // Make a this.config, that has stuff like showTooltip,
// showLegend, which then all functions will check // showLegend, which then all functions will check
this.setColors(args.colors, args.type);
this.set_margins(args.height); this.setColors();
this.setMargins();
this.config = { this.config = {
showTooltip: 1, showTooltip: 1,
@ -706,26 +732,29 @@ class BaseChart {
}; };
} }
setColors(colors, type) { setColors() {
this.colors = colors; let args = this.rawChartArgs;
// Needs structure as per only labels/datasets // Needs structure as per only labels/datasets, from config
const list = type === 'percentage' || type === 'pie' const list = args.type === 'percentage' || args.type === 'pie'
? this.data.labels ? args.data.labels
: this.data.datasets; : args.data.datasets;
if(!this.colors || (list && this.colors.length < list.length)) { if(!args.colors || (list && args.colors.length < list.length)) {
this.colors = DEFAULT_COLORS; this.colors = DEFAULT_COLORS;
} else {
this.colors = args.colors;
} }
this.colors = this.colors.map(color => getColor(color)); this.colors = this.colors.map(color => getColor(color));
} }
set_margins(height) { setMargins() {
let height = this.rawChartArgs.height;
this.baseHeight = height; this.baseHeight = height;
this.height = height - 40; this.height = height - 40;
this.translate_x = 60; this.translateX = 60;
this.translate_y = 10; this.translateY = 10;
} }
validate(){ validate(){
@ -733,13 +762,22 @@ class BaseChart {
console.error("No parent element to render on was provided."); console.error("No parent element to render on was provided.");
return false; return false;
} }
if(!this.validateAndPrepareData()) { if(!this.parseData()) {
return false; return false;
} }
return true; return true;
} }
validateAndPrepareData() { parseData() {
let data = this.rawChartArgs.data;
// Check and all
// If all good
this.data = data;
return true; return true;
} }
@ -753,8 +791,6 @@ class BaseChart {
this.bindWindowEvents(); this.bindWindowEvents();
this.setupConstants(); this.setupConstants();
// this.setupEmptyValues();
// this.setupComponents();
this.makeContainer(); this.makeContainer();
this.makeTooltip(); // without binding this.makeTooltip(); // without binding
@ -763,12 +799,11 @@ class BaseChart {
draw(init=false) { draw(init=false) {
// (draw everything, layers, groups, units) // (draw everything, layers, groups, units)
this.setWidth(); this.calc();
this.setupRenderer(); // this chart's rendered with the config
// these both dependent on width >.<, how can this be decoupled
this.setupEmptyValues();
this.setupComponents(); this.setupComponents();
this.makeChartArea(); this.makeChartArea();
this.makeLayers(); this.makeLayers();
@ -779,40 +814,32 @@ class BaseChart {
if(init) this.update(this.data); if(init) this.update(this.data);
} }
update(data, animate=true) {
this.oldData = Object.assign({}, this.data);
this.data = this.prepareNewData(data);
this.calculateValues();
this.updateComponents(animate);
}
prepareNewData(newData) {
// handle all types of passed data?
return newData;
}
bindWindowEvents() { bindWindowEvents() {
window.addEventListener('resize', () => this.draw()); window.addEventListener('resize', () => this.draw());
window.addEventListener('orientationchange', () => this.draw()); window.addEventListener('orientationchange', () => this.draw());
} }
setWidth() { calcWidth() {
let special_values_width = 0; let outerAnnotationsWidth = 0;
// let char_width = 8; // let charWidth = 8;
// this.specific_values.map(val => { // this.specificValues.map(val => {
// let str_width = getStringWidth((val.title + ""), char_width); // let strWidth = getStringWidth((val.title + ""), charWidth);
// if(str_width > special_values_width) { // if(strWidth > outerAnnotationsWidth) {
// special_values_width = str_width - 40; // outerAnnotationsWidth = strWidth - 40;
// } // }
// }); // });
this.baseWidth = getElementContentWidth(this.parent) - special_values_width; this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
this.width = this.baseWidth - this.translate_x * 2; this.width = this.baseWidth - this.translateX * 2;
} }
setupConstants() {} setupConstants() {}
setupEmptyValues() {} calc() {
this.calcWidth();
this.reCalc();
}
setupRenderer() {}
setupComponents() { setupComponents() {
// Components config // Components config
@ -832,13 +859,13 @@ class BaseChart {
this.parent.innerHTML = ''; this.parent.innerHTML = '';
this.parent.appendChild(this.container); this.parent.appendChild(this.container);
this.chart_wrapper = this.container.querySelector('.frappe-chart'); this.chartWrapper = this.container.querySelector('.frappe-chart');
this.stats_wrapper = this.container.querySelector('.graph-stats-container'); this.statsWrapper = this.container.querySelector('.graph-stats-container');
} }
makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chart_wrapper, this.chartWrapper,
'chart', 'chart',
this.baseWidth, this.baseWidth,
this.baseHeight this.baseHeight
@ -848,7 +875,7 @@ class BaseChart {
this.drawArea = makeSVGGroup( this.drawArea = makeSVGGroup(
this.svg, this.svg,
this.type + '-chart', this.type + '-chart',
`translate(${this.translate_x}, ${this.translate_y})` `translate(${this.translateX}, ${this.translateY})`
); );
} }
@ -869,72 +896,88 @@ class BaseChart {
}); });
} }
updateComponents() { update() {
// this.components.forEach((component) => { this.reCalc();
// // this.reRender();
// });
} }
reCalc() {
// Will update values(state)
// Will recalc specific parts depending on the update
}
reRender(animate=true) {
if(!animate) {
this.renderComponents();
return;
}
this.animateComponents();
setTimeout(() => {
this.renderComponents();
}, 400);
// TODO: should be max anim duration required
// (opt, should not redraw if still in animate?)
}
animateComponents() {
this.intermedValues = this.calcIntermediateValues();
this.components.forEach(c => {
// c.store = c.animate(...c.animateArgs);
// c.layer.textContent = '';
// c.store.forEach(element => {c.layer.appendChild(element);});
});
}
calcInitStage() {}
makeTooltip() { makeTooltip() {
this.tip = new SvgTip({ this.tip = new SvgTip({
parent: this.chart_wrapper, parent: this.chartWrapper,
colors: this.colors colors: this.colors
}); });
this.bind_tooltip(); this.bindTooltip();
} }
show_summary() {}
show_custom_summary() {
this.summary.map(d => {
let stats = $.create('div', {
className: 'stats',
innerHTML: `<span class="indicator">
<i style="background:${d.color}"></i>
${d.title}: ${d.value}
</span>`
});
this.stats_wrapper.appendChild(stats);
});
}
renderLegend() {} renderLegend() {}
setupNavigation(init=false) { setupNavigation(init=false) {
if(this.is_navigable) return; if(this.isNavigable) return;
this.make_overlay(); this.makeOverlay();
if(init) { if(init) {
this.bind_overlay(); this.bindOverlay();
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if(isElementInViewport(this.chart_wrapper)) { if(isElementInViewport(this.chartWrapper)) {
e = e || window.event; e = e || window.event;
if (e.keyCode == '37') { if (e.keyCode == '37') {
this.on_left_arrow(); this.onLeftArrow();
} else if (e.keyCode == '39') { } else if (e.keyCode == '39') {
this.on_right_arrow(); this.onRightArrow();
} else if (e.keyCode == '38') { } else if (e.keyCode == '38') {
this.on_up_arrow(); this.onUpArrow();
} else if (e.keyCode == '40') { } else if (e.keyCode == '40') {
this.on_down_arrow(); this.onDownArrow();
} else if (e.keyCode == '13') { } else if (e.keyCode == '13') {
this.on_enter_key(); this.onEnterKey();
} }
} }
}); });
} }
} }
make_overlay() {} makeOverlay() {}
bind_overlay() {} bindOverlay() {}
bind_units() {} bind_units() {}
on_left_arrow() {} onLeftArrow() {}
on_right_arrow() {} onRightArrow() {}
on_up_arrow() {} onUpArrow() {}
on_down_arrow() {} onDownArrow() {}
on_enter_key() {} onEnterKey() {}
getDataPoint() {} getDataPoint() {}
updateCurrentDataPoint() {} updateCurrentDataPoint() {}
@ -943,31 +986,8 @@ class BaseChart {
return makeSVGGroup(this.drawArea, className, transform); return makeSVGGroup(this.drawArea, className, transform);
} }
get_different_chart(type) { getDifferentChart(type) {
if(type === this.type) return; return getDifferentChart(type, this.type, this.rawChartArgs);
if(!ALL_CHART_TYPES.includes(type)) {
console.error(`'${type}' is not a valid chart type.`);
}
if(!COMPATIBLE_CHARTS[this.type].includes(type)) {
console.error(`'${this.type}' chart cannot be converted to a '${type}' chart.`);
}
// whether the new chart can use the existing colors
const use_color = COLOR_COMPATIBLE_CHARTS[this.type].includes(type);
// Okay, this is anticlimactic
// this function will need to actually be 'change_chart_type(type)'
// that will update only the required elements, but for now ...
return new Chart({
parent: this.raw_chart_args.parent,
title: this.title,
data: this.raw_chart_args.data,
type: type,
height: this.raw_chart_args.height,
colors: use_color ? this.colors : undefined
});
} }
} }
@ -1374,9 +1394,10 @@ class AxisChart extends BaseChart {
this.zero_line = this.height; this.zero_line = this.height;
} }
validateAndPrepareData() { parseData() {
this.xAxisLabels = this.data.labels || []; let args = this.rawChartArgs;
this.y = this.data.datasets || []; this.xAxisLabels = args.data.labels || [];
this.y = args.data.datasets || [];
this.y.forEach(function(d, i) { this.y.forEach(function(d, i) {
d.index = i; d.index = i;
@ -1384,25 +1405,67 @@ class AxisChart extends BaseChart {
return true; return true;
} }
setupEmptyValues() { reCalc() {
// examples:
// [A] Dimension change:
// [B] Data change:
// 1. X values update
// 2. Y values update
// Aka all the values(state), these all will be documented in an old values object
// Backup first!
this.oldValues = ["everything"];
// extracted, raw args will remain in their own object
this.datasetsLabels = [];
this.datasetsValues = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]];
// CALCULATION: we'll need the first batch of calcs
// List of what will happen:
// this.xOffset = 0;
// this.unitWidth = 0;
// this.scaleMultipliers = [];
// this.datasetsPoints =
// Now, the function calls
// var merged = [].concat(...arrays)
// INIT
// axes
this.yAxisPositions = [this.height, this.height/2, 0]; this.yAxisPositions = [this.height, this.height/2, 0];
this.yAxisLabels = ['0', '5', '10']; this.yAxisLabels = ['0', '5', '10'];
this.xPositions = [0, this.width/2, this.width]; this.xPositions = [0, this.width/2, this.width];
this.xAxisLabels = ['0', '5', '10']; this.xAxisLabels = ['0', '5', '10'];
}
calcInitStage() {
// will borrow from the full recalc function
}
calcIntermediateValues() {
//
} }
// this should be inherent in BaseChart // this should be inherent in BaseChart
getRenderer() { getRenderer() {
// These args are basically the current state/config of the chart, // These args are basically the current state/config of the chart,
// with constant and alive params mixed // with constant and alive params mixed
return new AxisChartRenderer(this.height, this.width, return new AxisChartRenderer(this.height, this.width,
this.zero_line, this.avg_unit_width, this.xAxisMode, this.yAxisMode); this.zero_line, this.avg_unit_width, this.xAxisMode, this.yAxisMode);
} }
setupComponents() { setupComponents() {
// Must have access to all current data things
let self = this; let self = this;
let renderer = this.getRenderer(); let renderer = this.getRenderer();
this.yAxis = { this.yAxis = {
@ -1411,6 +1474,7 @@ class AxisChart extends BaseChart {
make: self.makeYLines, make: self.makeYLines,
makeArgs: [renderer, self.yAxisPositions, self.yAxisLabels], makeArgs: [renderer, self.yAxisPositions, self.yAxisLabels],
store: [], store: [],
// animate? or update? will come to while implementing
animate: self.animateYLines, animate: self.animateYLines,
// indexed: 1 // ?? As per datasets? // indexed: 1 // ?? As per datasets?
}; };
@ -1424,6 +1488,16 @@ class AxisChart extends BaseChart {
store: [], store: [],
animate: self.animateXLines animate: self.animateXLines
}; };
// Indexed according to dataset
// this.dataUnits = {
// layerClass: 'y marker axis',
// layer: undefined,
// make: makeXLines,
// makeArgs: [this.xPositions, this.xAxisLabels],
// store: [],
// animate: animateXLines,
// indexed: 1
// };
this.yMarkerLines = { this.yMarkerLines = {
// layerClass: 'y marker axis', // layerClass: 'y marker axis',
// layer: undefined, // layer: undefined,
@ -1436,24 +1510,13 @@ class AxisChart extends BaseChart {
// layerClass: 'x marker axis', // layerClass: 'x marker axis',
// layer: undefined, // layer: undefined,
// make: makeXMarkerLines, // make: makeXMarkerLines,
// makeArgs: [this.yMarkerPositions, this.xMarker], // makeArgs: [this.xMarkerPositions, this.xMarker],
// store: [], // store: [],
// animate: animateXMarkerLines // animate: animateXMarkerLines
}; };
// Marker Regions // Marker Regions
// Indexed according to dataset
this.dataUnits = {
layerClass: 'y marker axis',
layer: undefined,
// make: makeXLines,
// makeArgs: [this.xPositions, this.xAxisLabels],
// store: [],
// animate: animateXLines,
indexed: 1
};
this.components = [ this.components = [
this.yAxis, this.yAxis,
this.xAxis, this.xAxis,
@ -1639,19 +1702,19 @@ class AxisChart extends BaseChart {
units_array.push(data_unit); units_array.push(data_unit);
}); });
if(this.is_navigable) { if(this.isNavigable) {
this.bind_units(units_array); this.bind_units(units_array);
} }
} }
bind_tooltip() { bindTooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent // TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chart_wrapper.addEventListener('mousemove', (e) => { this.chartWrapper.addEventListener('mousemove', (e) => {
let offset = get_offset(this.chart_wrapper); let offset = getOffset(this.chartWrapper);
let relX = e.pageX - offset.left - this.translate_x; let relX = e.pageX - offset.left - this.translateX;
let relY = e.pageY - offset.top - this.translate_y; let relY = e.pageY - offset.top - this.translateY;
if(relY < this.height + this.translate_y * 2) { if(relY < this.height + this.translateY * 2) {
this.mapTooltipXPosition(relX); this.mapTooltipXPosition(relX);
} else { } else {
this.tip.hide_tip(); this.tip.hide_tip();
@ -1673,8 +1736,8 @@ class AxisChart extends BaseChart {
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.avg_unit_width : x_val - this.xPositions[i-1];
if(relX > x_val - this.avg_unit_width/2) { if(relX > x_val - this.avg_unit_width/2) {
let x = x_val + this.translate_x; let x = x_val + this.translateX;
let y = this.y_min_tops[i] + this.translate_y; let y = this.y_min_tops[i] + this.translateY;
let title = titles[i]; let title = titles[i];
let values = this.y.map((set, j) => { let values = this.y.map((set, j) => {
@ -1766,7 +1829,7 @@ class AxisChart extends BaseChart {
this.animate_units(d, newX, newY); this.animate_units(d, newX, newY);
}); });
runSMILAnimation(this.chart_wrapper, this.svg, this.elements_to_animate); runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
setTimeout(() => { setTimeout(() => {
this.y.map(d => { this.y.map(d => {
@ -1841,7 +1904,7 @@ class AxisChart extends BaseChart {
this.updateData(newY, newX); this.updateData(newY, newX);
} }
getDataPoint(index=this.current_index) { getDataPoint(index=this.currentIndex) {
// check for length // check for length
let data_point = { let data_point = {
index: index index: index
@ -1859,8 +1922,8 @@ class AxisChart extends BaseChart {
index = parseInt(index); index = parseInt(index);
if(index < 0) index = 0; if(index < 0) index = 0;
if(index >= this.xAxisLabels.length) index = this.xAxisLabels.length - 1; if(index >= this.xAxisLabels.length) index = this.xAxisLabels.length - 1;
if(index === this.current_index) return; if(index === this.currentIndex) return;
this.current_index = index; this.currentIndex = index;
fire(this.parent, "data-select", this.getDataPoint()); fire(this.parent, "data-select", this.getDataPoint());
} }
@ -1892,7 +1955,7 @@ class AxisChart extends BaseChart {
} }
}); });
}); });
// this.chart_wrapper.removeChild(this.tip.container); // this.chartWrapper.removeChild(this.tip.container);
// this.make_tooltip(); // this.make_tooltip();
} }
} }
@ -1918,7 +1981,7 @@ class BarChart extends AxisChart {
}; };
} }
// make_overlay() { // makeOverlay() {
// // Just make one out of the first element // // Just make one out of the first element
// let index = this.xAxisLabels.length - 1; // let index = this.xAxisLabels.length - 1;
// let unit = this.y[0].svg_units[index]; // let unit = this.y[0].svg_units[index];
@ -1933,7 +1996,7 @@ class BarChart extends AxisChart {
// this.drawArea.appendChild(this.overlay); // this.drawArea.appendChild(this.overlay);
// } // }
// bind_overlay() { // bindOverlay() {
// // on event, update overlay // // on event, update overlay
// this.parent.addEventListener('data-select', (e) => { // this.parent.addEventListener('data-select', (e) => {
// this.update_overlay(e.svg_unit); // this.update_overlay(e.svg_unit);
@ -1963,12 +2026,12 @@ class BarChart extends AxisChart {
this.overlay.style.opacity = '0.4'; this.overlay.style.opacity = '0.4';
} }
on_left_arrow() { onLeftArrow() {
this.updateCurrentDataPoint(this.current_index - 1); this.updateCurrentDataPoint(this.currentIndex - 1);
} }
on_right_arrow() { onRightArrow() {
this.updateCurrentDataPoint(this.current_index + 1); this.updateCurrentDataPoint(this.currentIndex + 1);
} }
set_avg_unit_width_and_x_offset() { set_avg_unit_width_and_x_offset() {
@ -2101,16 +2164,16 @@ class PercentageChart extends BaseChart {
} }
makeChartArea() { makeChartArea() {
this.chart_wrapper.className += ' ' + 'graph-focus-margin'; this.chartWrapper.className += ' ' + 'graph-focus-margin';
this.chart_wrapper.style.marginTop = '45px'; this.chartWrapper.style.marginTop = '45px';
this.stats_wrapper.className += ' ' + 'graph-focus-margin'; this.statsWrapper.className += ' ' + 'graph-focus-margin';
this.stats_wrapper.style.marginBottom = '30px'; this.statsWrapper.style.marginBottom = '30px';
this.stats_wrapper.style.paddingTop = '0px'; this.statsWrapper.style.paddingTop = '0px';
this.chartDiv = $.create('div', { this.chartDiv = $.create('div', {
className: 'div', className: 'div',
inside: this.chart_wrapper inside: this.chartWrapper
}); });
this.chart = $.create('div', { this.chart = $.create('div', {
@ -2177,10 +2240,10 @@ class PercentageChart extends BaseChart {
}); });
} }
bind_tooltip() { bindTooltip() {
this.slices.map((slice, i) => { this.slices.map((slice, i) => {
slice.addEventListener('mouseenter', () => { slice.addEventListener('mouseenter', () => {
let g_off = get_offset(this.chart_wrapper), p_off = get_offset(slice); let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
let x = p_off.left - g_off.left + slice.offsetWidth/2; let x = p_off.left - g_off.left + slice.offsetWidth/2;
let y = p_off.top - g_off.top - 6; let y = p_off.top - g_off.top - 6;
@ -2201,7 +2264,7 @@ class PercentageChart extends BaseChart {
if(d) { if(d) {
let stats = $.create('div', { let stats = $.create('div', {
className: 'stats', className: 'stats',
inside: this.stats_wrapper inside: this.statsWrapper
}); });
stats.innerHTML = `<span class="indicator"> stats.innerHTML = `<span class="indicator">
<i style="background: ${this.colors[i]}"></i> <i style="background: ${this.colors[i]}"></i>
@ -2328,7 +2391,7 @@ class PieChart extends BaseChart {
}); });
if(init){ if(init){
runSMILAnimation(this.chart_wrapper, this.svg, this.elements_to_animate); runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
} }
} }
@ -2343,7 +2406,7 @@ class PieChart extends BaseChart {
if(flag){ if(flag){
transform(path,this.calTranslateByAngle(this.slicesProperties[i])); transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
path.style.fill = lightenDarkenColor(color,50); path.style.fill = lightenDarkenColor(color,50);
let g_off = get_offset(this.svg); let g_off = getOffset(this.svg);
let x = e.pageX - g_off.left + 10; let x = e.pageX - g_off.left + 10;
let y = e.pageY - g_off.top - 10; let y = e.pageY - g_off.top - 10;
let title = (this.formatted_labels && this.formatted_labels.length>0 let title = (this.formatted_labels && this.formatted_labels.length>0
@ -2375,7 +2438,7 @@ class PieChart extends BaseChart {
mouseLeave(){ mouseLeave(){
this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false); this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false);
} }
bind_tooltip() { bindTooltip() {
this.drawArea.addEventListener('mousemove',this.mouseMove); this.drawArea.addEventListener('mousemove',this.mouseMove);
this.drawArea.addEventListener('mouseleave',this.mouseLeave); this.drawArea.addEventListener('mouseleave',this.mouseLeave);
} }
@ -2389,7 +2452,7 @@ class PieChart extends BaseChart {
if(d) { if(d) {
let stats = $.create('div', { let stats = $.create('div', {
className: 'stats', className: 'stats',
inside: this.stats_wrapper inside: this.statsWrapper
}); });
stats.innerHTML = `<span class="indicator"> stats.innerHTML = `<span class="indicator">
<i style="background-color:${color};"></i> <i style="background-color:${color};"></i>
@ -2468,7 +2531,7 @@ class Heatmap extends BaseChart {
// More colors are difficult to parse visually // More colors are difficult to parse visually
this.distribution_size = 5; this.distribution_size = 5;
this.translate_x = 0; this.translateX = 0;
// this.setup(); // this.setup();
} }
@ -2504,7 +2567,7 @@ class Heatmap extends BaseChart {
this.no_of_cols = getWeeksBetween(this.first_week_start + '', this.last_week_start + '') + 1; this.no_of_cols = getWeeksBetween(this.first_week_start + '', this.last_week_start + '') + 1;
} }
setWidth() { calcWidth() {
this.baseWidth = (this.no_of_cols + 3) * 12 ; this.baseWidth = (this.no_of_cols + 3) * 12 ;
if(this.discrete_domains) { if(this.discrete_domains) {
@ -2659,11 +2722,11 @@ class Heatmap extends BaseChart {
).map(d => { ).map(d => {
d.style.display = 'None'; d.style.display = 'None';
}); });
this.chart_wrapper.style.marginTop = '0px'; this.chartWrapper.style.marginTop = '0px';
this.chart_wrapper.style.paddingTop = '0px'; this.chartWrapper.style.paddingTop = '0px';
} }
bind_tooltip() { bindTooltip() {
Array.prototype.slice.call( Array.prototype.slice.call(
document.querySelectorAll(".data-group .day") document.querySelectorAll(".data-group .day")
).map(el => { ).map(el => {
@ -2673,7 +2736,7 @@ class Heatmap extends BaseChart {
let month = this.month_names[parseInt(date_parts[1])-1].substring(0, 3); let month = this.month_names[parseInt(date_parts[1])-1].substring(0, 3);
let g_off = this.chart_wrapper.getBoundingClientRect(), p_off = e.target.getBoundingClientRect(); let g_off = this.chartWrapper.getBoundingClientRect(), p_off = e.target.getBoundingClientRect();
let width = parseInt(e.target.getAttribute('width')); let width = parseInt(e.target.getAttribute('width'));
let x = p_off.left - g_off.left + (width+2)/2; let x = p_off.left - g_off.left + (width+2)/2;
@ -2690,7 +2753,7 @@ class Heatmap extends BaseChart {
update(data) { update(data) {
this.data = data; this.data = data;
this.setup_values(); this.setup_values();
this.bind_tooltip(); this.bindTooltip();
} }
} }

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

File diff suppressed because one or more lines are too long

View File

@ -50,7 +50,7 @@ let bar_composite_chart = new Chart ({
type: 'bar', type: 'bar',
height: 180, height: 180,
colors: ['orange'], colors: ['orange'],
is_navigable: 1, isNavigable: 1,
is_series: 1 is_series: 1
// region_fill: 1 // region_fill: 1
}); });
@ -110,7 +110,7 @@ Array.prototype.slice.call(
let btn = e.target; let btn = e.target;
let type = btn.getAttribute('data-type'); let type = btn.getAttribute('data-type');
let newChart = type_chart.get_different_chart(type); let newChart = type_chart.getDifferentChart(type);
if(newChart){ if(newChart){
type_chart = newChart; type_chart = newChart;
} }
@ -300,7 +300,7 @@ let events_chart = new Chart({
type: 'bar', type: 'bar',
height: 250, height: 250,
colors: ['grey'], colors: ['grey'],
is_navigable: 1, isNavigable: 1,
}); });
let data_div = document.querySelector('.chart-events-data'); let data_div = document.querySelector('.chart-events-data');

View File

@ -205,7 +205,7 @@
</div> </div>
<pre><code class="hljs javascript margin-vertical-px"> ... <pre><code class="hljs javascript margin-vertical-px"> ...
type: 'bar', // Bar Chart specific properties: type: 'bar', // Bar Chart specific properties:
is_navigable: 1, // Navigate across bars; default 0 isNavigable: 1, // Navigate across bars; default 0
... ...
chart.parent.addEventListener('data-select', (e) => { chart.parent.addEventListener('data-select', (e) => {

View File

@ -15,7 +15,7 @@ import pkg from './package.json';
export default [ export default [
{ {
input: 'src/js/charts.js', input: 'src/js/chart.js',
output: [ output: [
{ {
file: 'docs/assets/js/frappe-charts.min.js', file: 'docs/assets/js/frappe-charts.min.js',
@ -56,7 +56,7 @@ export default [
] ]
}, },
{ {
input: 'src/js/charts.js', input: 'src/js/chart.js',
output: [ output: [
{ {
file: pkg.main, file: pkg.main,
@ -96,7 +96,7 @@ export default [
], ],
}, },
{ {
input: 'src/js/charts.js', input: 'src/js/chart.js',
output: [ output: [
{ {
file: pkg.src, file: pkg.src,

View File

@ -1,5 +1,5 @@
import BaseChart from './BaseChart'; import BaseChart from './BaseChart';
import { get_offset, fire } from '../utils/dom'; import { getOffset, fire } from '../utils/dom';
import { AxisChartRenderer } from '../utils/draw'; import { AxisChartRenderer } from '../utils/draw';
import { equilizeNoOfElements } from '../utils/draw-utils'; import { equilizeNoOfElements } from '../utils/draw-utils';
import { Animator } from '../utils/animate'; import { Animator } from '../utils/animate';
@ -16,9 +16,10 @@ export default class AxisChart extends BaseChart {
this.zero_line = this.height; this.zero_line = this.height;
} }
validateAndPrepareData() { parseData() {
this.xAxisLabels = this.data.labels || []; let args = this.rawChartArgs;
this.y = this.data.datasets || []; this.xAxisLabels = args.data.labels || [];
this.y = args.data.datasets || [];
this.y.forEach(function(d, i) { this.y.forEach(function(d, i) {
d.index = i; d.index = i;
@ -26,25 +27,67 @@ export default class AxisChart extends BaseChart {
return true; return true;
} }
setupEmptyValues() { reCalc() {
// examples:
// [A] Dimension change:
// [B] Data change:
// 1. X values update
// 2. Y values update
// Aka all the values(state), these all will be documented in an old values object
// Backup first!
this.oldValues = ["everything"];
// extracted, raw args will remain in their own object
this.datasetsLabels = [];
this.datasetsValues = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]];
// CALCULATION: we'll need the first batch of calcs
// List of what will happen:
// this.xOffset = 0;
// this.unitWidth = 0;
// this.scaleMultipliers = [];
// this.datasetsPoints =
// Now, the function calls
// var merged = [].concat(...arrays)
// INIT
// axes
this.yAxisPositions = [this.height, this.height/2, 0]; this.yAxisPositions = [this.height, this.height/2, 0];
this.yAxisLabels = ['0', '5', '10']; this.yAxisLabels = ['0', '5', '10'];
this.xPositions = [0, this.width/2, this.width]; this.xPositions = [0, this.width/2, this.width];
this.xAxisLabels = ['0', '5', '10']; this.xAxisLabels = ['0', '5', '10'];
}
calcInitStage() {
// will borrow from the full recalc function
}
calcIntermediateValues() {
//
} }
// this should be inherent in BaseChart // this should be inherent in BaseChart
getRenderer() { getRenderer() {
// These args are basically the current state/config of the chart, // These args are basically the current state/config of the chart,
// with constant and alive params mixed // with constant and alive params mixed
return new AxisChartRenderer(this.height, this.width, return new AxisChartRenderer(this.height, this.width,
this.zero_line, this.avg_unit_width, this.xAxisMode, this.yAxisMode); this.zero_line, this.avg_unit_width, this.xAxisMode, this.yAxisMode);
} }
setupComponents() { setupComponents() {
// Must have access to all current data things
let self = this; let self = this;
let renderer = this.getRenderer(); let renderer = this.getRenderer();
this.yAxis = { this.yAxis = {
@ -53,6 +96,7 @@ export default class AxisChart extends BaseChart {
make: self.makeYLines, make: self.makeYLines,
makeArgs: [renderer, self.yAxisPositions, self.yAxisLabels], makeArgs: [renderer, self.yAxisPositions, self.yAxisLabels],
store: [], store: [],
// animate? or update? will come to while implementing
animate: self.animateYLines, animate: self.animateYLines,
// indexed: 1 // ?? As per datasets? // indexed: 1 // ?? As per datasets?
}; };
@ -66,6 +110,16 @@ export default class AxisChart extends BaseChart {
store: [], store: [],
animate: self.animateXLines animate: self.animateXLines
}; };
// Indexed according to dataset
// this.dataUnits = {
// layerClass: 'y marker axis',
// layer: undefined,
// make: makeXLines,
// makeArgs: [this.xPositions, this.xAxisLabels],
// store: [],
// animate: animateXLines,
// indexed: 1
// };
this.yMarkerLines = { this.yMarkerLines = {
// layerClass: 'y marker axis', // layerClass: 'y marker axis',
// layer: undefined, // layer: undefined,
@ -78,24 +132,13 @@ export default class AxisChart extends BaseChart {
// layerClass: 'x marker axis', // layerClass: 'x marker axis',
// layer: undefined, // layer: undefined,
// make: makeXMarkerLines, // make: makeXMarkerLines,
// makeArgs: [this.yMarkerPositions, this.xMarker], // makeArgs: [this.xMarkerPositions, this.xMarker],
// store: [], // store: [],
// animate: animateXMarkerLines // animate: animateXMarkerLines
}; };
// Marker Regions // Marker Regions
// Indexed according to dataset
this.dataUnits = {
layerClass: 'y marker axis',
layer: undefined,
// make: makeXLines,
// makeArgs: [this.xPositions, this.xAxisLabels],
// store: [],
// animate: animateXLines,
indexed: 1
};
this.components = [ this.components = [
this.yAxis, this.yAxis,
this.xAxis, this.xAxis,
@ -281,19 +324,19 @@ export default class AxisChart extends BaseChart {
units_array.push(data_unit); units_array.push(data_unit);
}); });
if(this.is_navigable) { if(this.isNavigable) {
this.bind_units(units_array); this.bind_units(units_array);
} }
} }
bind_tooltip() { bindTooltip() {
// TODO: could be in tooltip itself, as it is a given functionality for its parent // TODO: could be in tooltip itself, as it is a given functionality for its parent
this.chart_wrapper.addEventListener('mousemove', (e) => { this.chartWrapper.addEventListener('mousemove', (e) => {
let offset = get_offset(this.chart_wrapper); let offset = getOffset(this.chartWrapper);
let relX = e.pageX - offset.left - this.translate_x; let relX = e.pageX - offset.left - this.translateX;
let relY = e.pageY - offset.top - this.translate_y; let relY = e.pageY - offset.top - this.translateY;
if(relY < this.height + this.translate_y * 2) { if(relY < this.height + this.translateY * 2) {
this.mapTooltipXPosition(relX); this.mapTooltipXPosition(relX);
} else { } else {
this.tip.hide_tip(); this.tip.hide_tip();
@ -315,8 +358,8 @@ export default class AxisChart extends BaseChart {
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.avg_unit_width : x_val - this.xPositions[i-1];
if(relX > x_val - this.avg_unit_width/2) { if(relX > x_val - this.avg_unit_width/2) {
let x = x_val + this.translate_x; let x = x_val + this.translateX;
let y = this.y_min_tops[i] + this.translate_y; let y = this.y_min_tops[i] + this.translateY;
let title = titles[i]; let title = titles[i];
let values = this.y.map((set, j) => { let values = this.y.map((set, j) => {
@ -408,7 +451,7 @@ export default class AxisChart extends BaseChart {
this.animate_units(d, newX, newY); this.animate_units(d, newX, newY);
}); });
runSMILAnimation(this.chart_wrapper, this.svg, this.elements_to_animate); runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
setTimeout(() => { setTimeout(() => {
this.y.map(d => { this.y.map(d => {
@ -483,7 +526,7 @@ export default class AxisChart extends BaseChart {
this.updateData(newY, newX); this.updateData(newY, newX);
} }
getDataPoint(index=this.current_index) { getDataPoint(index=this.currentIndex) {
// check for length // check for length
let data_point = { let data_point = {
index: index index: index
@ -501,8 +544,8 @@ export default class AxisChart extends BaseChart {
index = parseInt(index); index = parseInt(index);
if(index < 0) index = 0; if(index < 0) index = 0;
if(index >= this.xAxisLabels.length) index = this.xAxisLabels.length - 1; if(index >= this.xAxisLabels.length) index = this.xAxisLabels.length - 1;
if(index === this.current_index) return; if(index === this.currentIndex) return;
this.current_index = index; this.currentIndex = index;
fire(this.parent, "data-select", this.getDataPoint()); fire(this.parent, "data-select", this.getDataPoint());
} }
@ -534,7 +577,7 @@ export default class AxisChart extends BaseChart {
} }
}); });
}); });
// this.chart_wrapper.removeChild(this.tip.container); // this.chartWrapper.removeChild(this.tip.container);
// this.make_tooltip(); // this.make_tooltip();
} }
} }

View File

@ -21,7 +21,7 @@ export default class BarChart extends AxisChart {
}; };
} }
// make_overlay() { // makeOverlay() {
// // Just make one out of the first element // // Just make one out of the first element
// let index = this.xAxisLabels.length - 1; // let index = this.xAxisLabels.length - 1;
// let unit = this.y[0].svg_units[index]; // let unit = this.y[0].svg_units[index];
@ -36,7 +36,7 @@ export default class BarChart extends AxisChart {
// this.drawArea.appendChild(this.overlay); // this.drawArea.appendChild(this.overlay);
// } // }
// bind_overlay() { // bindOverlay() {
// // on event, update overlay // // on event, update overlay
// this.parent.addEventListener('data-select', (e) => { // this.parent.addEventListener('data-select', (e) => {
// this.update_overlay(e.svg_unit); // this.update_overlay(e.svg_unit);
@ -66,12 +66,12 @@ export default class BarChart extends AxisChart {
this.overlay.style.opacity = '0.4'; this.overlay.style.opacity = '0.4';
} }
on_left_arrow() { onLeftArrow() {
this.updateCurrentDataPoint(this.current_index - 1); this.updateCurrentDataPoint(this.currentIndex - 1);
} }
on_right_arrow() { onRightArrow() {
this.updateCurrentDataPoint(this.current_index + 1); this.updateCurrentDataPoint(this.currentIndex + 1);
} }
set_avg_unit_width_and_x_offset() { set_avg_unit_width_and_x_offset() {

View File

@ -3,28 +3,7 @@ import { $, isElementInViewport, getElementContentWidth } from '../utils/dom';
import { makeSVGContainer, makeSVGDefs, makeSVGGroup } from '../utils/draw'; import { makeSVGContainer, makeSVGDefs, makeSVGGroup } from '../utils/draw';
import { getStringWidth } from '../utils/helpers'; import { getStringWidth } from '../utils/helpers';
import { getColor, DEFAULT_COLORS } from '../utils/colors'; import { getColor, DEFAULT_COLORS } from '../utils/colors';
import Chart from '../charts'; import { getDifferentChart } from '../config';
const ALL_CHART_TYPES = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
const COMPATIBLE_CHARTS = {
bar: ['line', 'scatter', 'percentage', 'pie'],
line: ['scatter', 'bar', 'percentage', 'pie'],
pie: ['line', 'scatter', 'percentage', 'bar'],
scatter: ['line', 'bar', 'percentage', 'pie'],
percentage: ['bar', 'line', 'scatter', 'pie'],
heatmap: []
};
// Needs structure as per only labels/datasets
const COLOR_COMPATIBLE_CHARTS = {
bar: ['line', 'scatter'],
line: ['scatter', 'bar'],
pie: ['percentage'],
scatter: ['line', 'bar'],
percentage: ['pie'],
heatmap: []
};
export default class BaseChart { export default class BaseChart {
constructor({ constructor({
@ -34,34 +13,33 @@ export default class BaseChart {
subtitle = '', subtitle = '',
colors = [], colors = [],
is_navigable = 0, isNavigable = 0,
type = '', type = '',
parent, parent,
data data
}) { }) {
this.raw_chart_args = arguments[0]; this.rawChartArgs = arguments[0];
this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent; this.parent = typeof parent === 'string' ? document.querySelector(parent) : parent;
this.title = title; this.title = title;
this.subtitle = subtitle; this.subtitle = subtitle;
this.data = data; this.isNavigable = isNavigable;
if(this.isNavigable) {
this.is_navigable = is_navigable; this.currentIndex = 0;
if(this.is_navigable) {
this.current_index = 0;
} }
this.setupConfiguration(arguments[0]); this.setupConfiguration();
} }
setupConfiguration(args) { setupConfiguration() {
// Make a this.config, that has stuff like showTooltip, // Make a this.config, that has stuff like showTooltip,
// showLegend, which then all functions will check // showLegend, which then all functions will check
this.setColors(args.colors, args.type);
this.set_margins(args.height); this.setColors();
this.setMargins();
this.config = { this.config = {
showTooltip: 1, showTooltip: 1,
@ -70,40 +48,54 @@ export default class BaseChart {
}; };
} }
setColors(colors, type) { setColors() {
this.colors = colors; let args = this.rawChartArgs;
// Needs structure as per only labels/datasets // Needs structure as per only labels/datasets, from config
const list = type === 'percentage' || type === 'pie' const list = args.type === 'percentage' || args.type === 'pie'
? this.data.labels ? args.data.labels
: this.data.datasets; : args.data.datasets;
if(!this.colors || (list && this.colors.length < list.length)) { if(!args.colors || (list && args.colors.length < list.length)) {
this.colors = DEFAULT_COLORS; this.colors = DEFAULT_COLORS;
} else {
this.colors = args.colors;
} }
this.colors = this.colors.map(color => getColor(color)); this.colors = this.colors.map(color => getColor(color));
} }
set_margins(height) { setMargins() {
let height = this.rawChartArgs.height;
this.baseHeight = height; this.baseHeight = height;
this.height = height - 40; this.height = height - 40;
this.translate_x = 60; this.translateX = 60;
this.translate_y = 10; this.translateY = 10;
} }
validate(){ validate(){
let args = this.rawChartArgs;
// Now yo have the args, set this stuff only after validating
if(!this.parent) { if(!this.parent) {
console.error("No parent element to render on was provided."); console.error("No parent element to render on was provided.");
return false; return false;
} }
if(!this.validateAndPrepareData()) { if(!this.parseData()) {
return false; return false;
} }
return true; return true;
} }
validateAndPrepareData() { parseData() {
let data = this.rawChartArgs.data;
// Check and all
// If all good
this.data = data;
return true; return true;
} }
@ -117,8 +109,6 @@ export default class BaseChart {
this.bindWindowEvents(); this.bindWindowEvents();
this.setupConstants(); this.setupConstants();
// this.setupEmptyValues();
// this.setupComponents();
this.makeContainer(); this.makeContainer();
this.makeTooltip(); // without binding this.makeTooltip(); // without binding
@ -127,12 +117,11 @@ export default class BaseChart {
draw(init=false) { draw(init=false) {
// (draw everything, layers, groups, units) // (draw everything, layers, groups, units)
this.setWidth(); this.calc();
this.setupRenderer(); // this chart's rendered with the config
// these both dependent on width >.<, how can this be decoupled
this.setupEmptyValues();
this.setupComponents(); this.setupComponents();
this.makeChartArea(); this.makeChartArea();
this.makeLayers(); this.makeLayers();
@ -143,40 +132,32 @@ export default class BaseChart {
if(init) this.update(this.data); if(init) this.update(this.data);
} }
update(data, animate=true) {
this.oldData = Object.assign({}, this.data);
this.data = this.prepareNewData(data);
this.calculateValues();
this.updateComponents(animate);
}
prepareNewData(newData) {
// handle all types of passed data?
return newData;
}
bindWindowEvents() { bindWindowEvents() {
window.addEventListener('resize', () => this.draw()); window.addEventListener('resize', () => this.draw());
window.addEventListener('orientationchange', () => this.draw()); window.addEventListener('orientationchange', () => this.draw());
} }
setWidth() { calcWidth() {
let special_values_width = 0; let outerAnnotationsWidth = 0;
// let char_width = 8; // let charWidth = 8;
// this.specific_values.map(val => { // this.specificValues.map(val => {
// let str_width = getStringWidth((val.title + ""), char_width); // let strWidth = getStringWidth((val.title + ""), charWidth);
// if(str_width > special_values_width) { // if(strWidth > outerAnnotationsWidth) {
// special_values_width = str_width - 40; // outerAnnotationsWidth = strWidth - 40;
// } // }
// }); // });
this.baseWidth = getElementContentWidth(this.parent) - special_values_width; this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth;
this.width = this.baseWidth - this.translate_x * 2; this.width = this.baseWidth - this.translateX * 2;
} }
setupConstants() {} setupConstants() {}
setupEmptyValues() {} calc() {
this.calcWidth();
this.reCalc();
}
setupRenderer() {}
setupComponents() { setupComponents() {
// Components config // Components config
@ -196,13 +177,13 @@ export default class BaseChart {
this.parent.innerHTML = ''; this.parent.innerHTML = '';
this.parent.appendChild(this.container); this.parent.appendChild(this.container);
this.chart_wrapper = this.container.querySelector('.frappe-chart'); this.chartWrapper = this.container.querySelector('.frappe-chart');
this.stats_wrapper = this.container.querySelector('.graph-stats-container'); this.statsWrapper = this.container.querySelector('.graph-stats-container');
} }
makeChartArea() { makeChartArea() {
this.svg = makeSVGContainer( this.svg = makeSVGContainer(
this.chart_wrapper, this.chartWrapper,
'chart', 'chart',
this.baseWidth, this.baseWidth,
this.baseHeight this.baseHeight
@ -212,7 +193,7 @@ export default class BaseChart {
this.drawArea = makeSVGGroup( this.drawArea = makeSVGGroup(
this.svg, this.svg,
this.type + '-chart', this.type + '-chart',
`translate(${this.translate_x}, ${this.translate_y})` `translate(${this.translateX}, ${this.translateY})`
); );
} }
@ -233,72 +214,88 @@ export default class BaseChart {
}); });
} }
updateComponents() { update() {
// this.components.forEach((component) => { this.reCalc();
// // this.reRender();
// });
} }
reCalc() {
// Will update values(state)
// Will recalc specific parts depending on the update
}
reRender(animate=true) {
if(!animate) {
this.renderComponents();
return;
}
this.animateComponents();
setTimeout(() => {
this.renderComponents();
}, 400);
// TODO: should be max anim duration required
// (opt, should not redraw if still in animate?)
}
animateComponents() {
this.intermedValues = this.calcIntermediateValues();
this.components.forEach(c => {
// c.store = c.animate(...c.animateArgs);
// c.layer.textContent = '';
// c.store.forEach(element => {c.layer.appendChild(element);});
});
}
calcInitStage() {}
makeTooltip() { makeTooltip() {
this.tip = new SvgTip({ this.tip = new SvgTip({
parent: this.chart_wrapper, parent: this.chartWrapper,
colors: this.colors colors: this.colors
}); });
this.bind_tooltip(); this.bindTooltip();
} }
show_summary() {}
show_custom_summary() {
this.summary.map(d => {
let stats = $.create('div', {
className: 'stats',
innerHTML: `<span class="indicator">
<i style="background:${d.color}"></i>
${d.title}: ${d.value}
</span>`
});
this.stats_wrapper.appendChild(stats);
});
}
renderLegend() {} renderLegend() {}
setupNavigation(init=false) { setupNavigation(init=false) {
if(this.is_navigable) return; if(this.isNavigable) return;
this.make_overlay(); this.makeOverlay();
if(init) { if(init) {
this.bind_overlay(); this.bindOverlay();
document.addEventListener('keydown', (e) => { document.addEventListener('keydown', (e) => {
if(isElementInViewport(this.chart_wrapper)) { if(isElementInViewport(this.chartWrapper)) {
e = e || window.event; e = e || window.event;
if (e.keyCode == '37') { if (e.keyCode == '37') {
this.on_left_arrow(); this.onLeftArrow();
} else if (e.keyCode == '39') { } else if (e.keyCode == '39') {
this.on_right_arrow(); this.onRightArrow();
} else if (e.keyCode == '38') { } else if (e.keyCode == '38') {
this.on_up_arrow(); this.onUpArrow();
} else if (e.keyCode == '40') { } else if (e.keyCode == '40') {
this.on_down_arrow(); this.onDownArrow();
} else if (e.keyCode == '13') { } else if (e.keyCode == '13') {
this.on_enter_key(); this.onEnterKey();
} }
} }
}); });
} }
} }
make_overlay() {} makeOverlay() {}
bind_overlay() {} bindOverlay() {}
bind_units() {} bind_units() {}
on_left_arrow() {} onLeftArrow() {}
on_right_arrow() {} onRightArrow() {}
on_up_arrow() {} onUpArrow() {}
on_down_arrow() {} onDownArrow() {}
on_enter_key() {} onEnterKey() {}
getDataPoint() {} getDataPoint() {}
updateCurrentDataPoint() {} updateCurrentDataPoint() {}
@ -307,30 +304,7 @@ export default class BaseChart {
return makeSVGGroup(this.drawArea, className, transform); return makeSVGGroup(this.drawArea, className, transform);
} }
get_different_chart(type) { getDifferentChart(type) {
if(type === this.type) return; return getDifferentChart(type, this.type, this.rawChartArgs);
if(!ALL_CHART_TYPES.includes(type)) {
console.error(`'${type}' is not a valid chart type.`);
}
if(!COMPATIBLE_CHARTS[this.type].includes(type)) {
console.error(`'${this.type}' chart cannot be converted to a '${type}' chart.`);
}
// whether the new chart can use the existing colors
const use_color = COLOR_COMPATIBLE_CHARTS[this.type].includes(type);
// Okay, this is anticlimactic
// this function will need to actually be 'change_chart_type(type)'
// that will update only the required elements, but for now ...
return new Chart({
parent: this.raw_chart_args.parent,
title: this.title,
data: this.raw_chart_args.data,
type: type,
height: this.raw_chart_args.height,
colors: use_color ? this.colors : undefined
});
} }
} }

View File

@ -36,7 +36,7 @@ export default class Heatmap extends BaseChart {
// More colors are difficult to parse visually // More colors are difficult to parse visually
this.distribution_size = 5; this.distribution_size = 5;
this.translate_x = 0; this.translateX = 0;
// this.setup(); // this.setup();
} }
@ -72,7 +72,7 @@ export default class Heatmap extends BaseChart {
this.no_of_cols = getWeeksBetween(this.first_week_start + '', this.last_week_start + '') + 1; this.no_of_cols = getWeeksBetween(this.first_week_start + '', this.last_week_start + '') + 1;
} }
setWidth() { calcWidth() {
this.baseWidth = (this.no_of_cols + 3) * 12 ; this.baseWidth = (this.no_of_cols + 3) * 12 ;
if(this.discrete_domains) { if(this.discrete_domains) {
@ -227,11 +227,11 @@ export default class Heatmap extends BaseChart {
).map(d => { ).map(d => {
d.style.display = 'None'; d.style.display = 'None';
}); });
this.chart_wrapper.style.marginTop = '0px'; this.chartWrapper.style.marginTop = '0px';
this.chart_wrapper.style.paddingTop = '0px'; this.chartWrapper.style.paddingTop = '0px';
} }
bind_tooltip() { bindTooltip() {
Array.prototype.slice.call( Array.prototype.slice.call(
document.querySelectorAll(".data-group .day") document.querySelectorAll(".data-group .day")
).map(el => { ).map(el => {
@ -241,7 +241,7 @@ export default class Heatmap extends BaseChart {
let month = this.month_names[parseInt(date_parts[1])-1].substring(0, 3); let month = this.month_names[parseInt(date_parts[1])-1].substring(0, 3);
let g_off = this.chart_wrapper.getBoundingClientRect(), p_off = e.target.getBoundingClientRect(); let g_off = this.chartWrapper.getBoundingClientRect(), p_off = e.target.getBoundingClientRect();
let width = parseInt(e.target.getAttribute('width')); let width = parseInt(e.target.getAttribute('width'));
let x = p_off.left - g_off.left + (width+2)/2; let x = p_off.left - g_off.left + (width+2)/2;
@ -258,6 +258,6 @@ export default class Heatmap extends BaseChart {
update(data) { update(data) {
this.data = data; this.data = data;
this.setup_values(); this.setup_values();
this.bind_tooltip(); this.bindTooltip();
} }
} }

View File

@ -1,5 +1,5 @@
import BaseChart from './BaseChart'; import BaseChart from './BaseChart';
import { $, get_offset } from '../utils/dom'; import { $, getOffset } from '../utils/dom';
export default class PercentageChart extends BaseChart { export default class PercentageChart extends BaseChart {
constructor(args) { constructor(args) {
@ -13,16 +13,16 @@ export default class PercentageChart extends BaseChart {
} }
makeChartArea() { makeChartArea() {
this.chart_wrapper.className += ' ' + 'graph-focus-margin'; this.chartWrapper.className += ' ' + 'graph-focus-margin';
this.chart_wrapper.style.marginTop = '45px'; this.chartWrapper.style.marginTop = '45px';
this.stats_wrapper.className += ' ' + 'graph-focus-margin'; this.statsWrapper.className += ' ' + 'graph-focus-margin';
this.stats_wrapper.style.marginBottom = '30px'; this.statsWrapper.style.marginBottom = '30px';
this.stats_wrapper.style.paddingTop = '0px'; this.statsWrapper.style.paddingTop = '0px';
this.chartDiv = $.create('div', { this.chartDiv = $.create('div', {
className: 'div', className: 'div',
inside: this.chart_wrapper inside: this.chartWrapper
}); });
this.chart = $.create('div', { this.chart = $.create('div', {
@ -89,10 +89,10 @@ export default class PercentageChart extends BaseChart {
}); });
} }
bind_tooltip() { bindTooltip() {
this.slices.map((slice, i) => { this.slices.map((slice, i) => {
slice.addEventListener('mouseenter', () => { slice.addEventListener('mouseenter', () => {
let g_off = get_offset(this.chart_wrapper), p_off = get_offset(slice); let g_off = getOffset(this.chartWrapper), p_off = getOffset(slice);
let x = p_off.left - g_off.left + slice.offsetWidth/2; let x = p_off.left - g_off.left + slice.offsetWidth/2;
let y = p_off.top - g_off.top - 6; let y = p_off.top - g_off.top - 6;
@ -113,7 +113,7 @@ export default class PercentageChart extends BaseChart {
if(d) { if(d) {
let stats = $.create('div', { let stats = $.create('div', {
className: 'stats', className: 'stats',
inside: this.stats_wrapper inside: this.statsWrapper
}); });
stats.innerHTML = `<span class="indicator"> stats.innerHTML = `<span class="indicator">
<i style="background: ${this.colors[i]}"></i> <i style="background: ${this.colors[i]}"></i>

View File

@ -1,5 +1,5 @@
import BaseChart from './BaseChart'; import BaseChart from './BaseChart';
import { $, get_offset } from '../utils/dom'; import { $, getOffset } from '../utils/dom';
import { makePath } from '../utils/draw'; import { makePath } from '../utils/draw';
import { lightenDarkenColor } from '../utils/colors'; import { lightenDarkenColor } from '../utils/colors';
import { runSMILAnimation, transform } from '../utils/animation'; import { runSMILAnimation, transform } from '../utils/animation';
@ -118,7 +118,7 @@ export default class PieChart extends BaseChart {
}); });
if(init){ if(init){
runSMILAnimation(this.chart_wrapper, this.svg, this.elements_to_animate); runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
} }
} }
@ -133,7 +133,7 @@ export default class PieChart extends BaseChart {
if(flag){ if(flag){
transform(path,this.calTranslateByAngle(this.slicesProperties[i])); transform(path,this.calTranslateByAngle(this.slicesProperties[i]));
path.style.fill = lightenDarkenColor(color,50); path.style.fill = lightenDarkenColor(color,50);
let g_off = get_offset(this.svg); let g_off = getOffset(this.svg);
let x = e.pageX - g_off.left + 10; let x = e.pageX - g_off.left + 10;
let y = e.pageY - g_off.top - 10; let y = e.pageY - g_off.top - 10;
let title = (this.formatted_labels && this.formatted_labels.length>0 let title = (this.formatted_labels && this.formatted_labels.length>0
@ -165,7 +165,7 @@ export default class PieChart extends BaseChart {
mouseLeave(){ mouseLeave(){
this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false); this.hoverSlice(this.curActiveSlice,this.curActiveSliceIndex,false);
} }
bind_tooltip() { bindTooltip() {
this.drawArea.addEventListener('mousemove',this.mouseMove); this.drawArea.addEventListener('mousemove',this.mouseMove);
this.drawArea.addEventListener('mouseleave',this.mouseLeave); this.drawArea.addEventListener('mouseleave',this.mouseLeave);
} }
@ -179,7 +179,7 @@ export default class PieChart extends BaseChart {
if(d) { if(d) {
let stats = $.create('div', { let stats = $.create('div', {
className: 'stats', className: 'stats',
inside: this.stats_wrapper inside: this.statsWrapper
}); });
stats.innerHTML = `<span class="indicator"> stats.innerHTML = `<span class="indicator">
<i style="background-color:${color};"></i> <i style="background-color:${color};"></i>

49
src/js/config.js Normal file
View File

@ -0,0 +1,49 @@
import Chart from './chart';
const ALL_CHART_TYPES = ['line', 'scatter', 'bar', 'percentage', 'heatmap', 'pie'];
const COMPATIBLE_CHARTS = {
bar: ['line', 'scatter', 'percentage', 'pie'],
line: ['scatter', 'bar', 'percentage', 'pie'],
pie: ['line', 'scatter', 'percentage', 'bar'],
scatter: ['line', 'bar', 'percentage', 'pie'],
percentage: ['bar', 'line', 'scatter', 'pie'],
heatmap: []
};
// Needs structure as per only labels/datasets
const COLOR_COMPATIBLE_CHARTS = {
bar: ['line', 'scatter'],
line: ['scatter', 'bar'],
pie: ['percentage'],
scatter: ['line', 'bar'],
percentage: ['pie'],
heatmap: []
};
export function getDifferentChart(type, current_type, args) {
if(type === current_type) return;
if(!ALL_CHART_TYPES.includes(type)) {
console.error(`'${type}' is not a valid chart type.`);
}
if(!COMPATIBLE_CHARTS[current_type].includes(type)) {
console.error(`'${current_type}' chart cannot be converted to a '${type}' chart.`);
}
// whether the new chart can use the existing colors
const useColor = COLOR_COMPATIBLE_CHARTS[current_type].includes(type);
// Okay, this is anticlimactic
// this function will need to actually be 'changeChartType(type)'
// that will update only the required elements, but for now ...
return new Chart({
parent: args.parent,
title: args.title,
data: args.data,
type: type,
height: args.height,
colors: useColor ? args.colors : undefined
});
}

View File

@ -43,7 +43,7 @@ $.create = (tag, o) => {
return element; return element;
}; };
export function get_offset(element) { export function getOffset(element) {
let rect = element.getBoundingClientRect(); let rect = element.getBoundingClientRect();
return { return {
// https://stackoverflow.com/a/7436602/6495043 // https://stackoverflow.com/a/7436602/6495043