@@ -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.is_navigable = is_navigable; | |||||
if(this.is_navigable) { | |||||
this.current_index = 0; | |||||
this.isNavigable = isNavigable; | |||||
if(this.isNavigable) { | |||||
this.currentIndex = 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) { | |||||
this.colors = colors; | |||||
setColors() { | |||||
let args = this.rawChartArgs; | |||||
// Needs structure as per only labels/datasets | |||||
const list = type === 'percentage' || type === 'pie' | |||||
? this.data.labels | |||||
: this.data.datasets; | |||||
// Needs structure as per only labels/datasets, from config | |||||
const list = args.type === 'percentage' || args.type === 'pie' | |||||
? args.data.labels | |||||
: 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.translate_y = 10; | |||||
this.translateX = 60; | |||||
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(); | |||||
// these both dependent on width >.<, how can this be decoupled | |||||
this.setupEmptyValues(); | |||||
this.calc(); | |||||
this.setupRenderer(); // this chart's rendered with the config | |||||
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() { | |||||
let special_values_width = 0; | |||||
// let char_width = 8; | |||||
// this.specific_values.map(val => { | |||||
// let str_width = getStringWidth((val.title + ""), char_width); | |||||
// if(str_width > special_values_width) { | |||||
// special_values_width = str_width - 40; | |||||
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) - special_values_width; | |||||
this.width = this.baseWidth - this.translate_x * 2; | |||||
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth; | |||||
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.stats_wrapper = this.container.querySelector('.graph-stats-container'); | |||||
this.chartWrapper = this.container.querySelector('.frappe-chart'); | |||||
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() { | |||||
// this.components.forEach((component) => { | |||||
// // | |||||
// }); | |||||
update() { | |||||
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(); | |||||
} | |||||
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); | |||||
}); | |||||
this.bindTooltip(); | |||||
} | } | ||||
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() {} | |||||
bind_overlay() {} | |||||
makeOverlay() {} | |||||
bindOverlay() {} | |||||
bind_units() {} | bind_units() {} | ||||
on_left_arrow() {} | |||||
on_right_arrow() {} | |||||
on_up_arrow() {} | |||||
on_down_arrow() {} | |||||
on_enter_key() {} | |||||
onLeftArrow() {} | |||||
onRightArrow() {} | |||||
onUpArrow() {} | |||||
onDownArrow() {} | |||||
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) { | |||||
if(type === this.type) return; | |||||
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 | |||||
}); | |||||
getDifferentChart(type) { | |||||
return getDifferentChart(type, this.type, this.rawChartArgs); | |||||
} | } | ||||
} | } | ||||
@@ -1374,9 +1394,10 @@ class AxisChart extends BaseChart { | |||||
this.zero_line = this.height; | this.zero_line = this.height; | ||||
} | } | ||||
validateAndPrepareData() { | |||||
this.xAxisLabels = this.data.labels || []; | |||||
this.y = this.data.datasets || []; | |||||
parseData() { | |||||
let args = this.rawChartArgs; | |||||
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) => { | |||||
let offset = get_offset(this.chart_wrapper); | |||||
let relX = e.pageX - offset.left - this.translate_x; | |||||
let relY = e.pageY - offset.top - this.translate_y; | |||||
this.chartWrapper.addEventListener('mousemove', (e) => { | |||||
let offset = getOffset(this.chartWrapper); | |||||
let relX = e.pageX - offset.left - this.translateX; | |||||
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 y = this.y_min_tops[i] + this.translate_y; | |||||
let x = x_val + this.translateX; | |||||
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; | |||||
this.current_index = index; | |||||
if(index === this.currentIndex) return; | |||||
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() { | |||||
this.updateCurrentDataPoint(this.current_index - 1); | |||||
onLeftArrow() { | |||||
this.updateCurrentDataPoint(this.currentIndex - 1); | |||||
} | } | ||||
on_right_arrow() { | |||||
this.updateCurrentDataPoint(this.current_index + 1); | |||||
onRightArrow() { | |||||
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.chart_wrapper.style.marginTop = '45px'; | |||||
this.chartWrapper.className += ' ' + 'graph-focus-margin'; | |||||
this.chartWrapper.style.marginTop = '45px'; | |||||
this.stats_wrapper.className += ' ' + 'graph-focus-margin'; | |||||
this.stats_wrapper.style.marginBottom = '30px'; | |||||
this.stats_wrapper.style.paddingTop = '0px'; | |||||
this.statsWrapper.className += ' ' + 'graph-focus-margin'; | |||||
this.statsWrapper.style.marginBottom = '30px'; | |||||
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.chart_wrapper.style.paddingTop = '0px'; | |||||
this.chartWrapper.style.marginTop = '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(); | |||||
} | } | ||||
} | } | ||||
@@ -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'); | ||||
@@ -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) => { | ||||
@@ -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, | ||||
@@ -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() { | |||||
this.xAxisLabels = this.data.labels || []; | |||||
this.y = this.data.datasets || []; | |||||
parseData() { | |||||
let args = this.rawChartArgs; | |||||
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) => { | |||||
let offset = get_offset(this.chart_wrapper); | |||||
let relX = e.pageX - offset.left - this.translate_x; | |||||
let relY = e.pageY - offset.top - this.translate_y; | |||||
this.chartWrapper.addEventListener('mousemove', (e) => { | |||||
let offset = getOffset(this.chartWrapper); | |||||
let relX = e.pageX - offset.left - this.translateX; | |||||
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 y = this.y_min_tops[i] + this.translate_y; | |||||
let x = x_val + this.translateX; | |||||
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; | |||||
this.current_index = index; | |||||
if(index === this.currentIndex) return; | |||||
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(); | ||||
} | } | ||||
} | } |
@@ -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() { | |||||
this.updateCurrentDataPoint(this.current_index - 1); | |||||
onLeftArrow() { | |||||
this.updateCurrentDataPoint(this.currentIndex - 1); | |||||
} | } | ||||
on_right_arrow() { | |||||
this.updateCurrentDataPoint(this.current_index + 1); | |||||
onRightArrow() { | |||||
this.updateCurrentDataPoint(this.currentIndex + 1); | |||||
} | } | ||||
set_avg_unit_width_and_x_offset() { | set_avg_unit_width_and_x_offset() { | ||||
@@ -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'; | |||||
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: [] | |||||
}; | |||||
import { getDifferentChart } from '../config'; | |||||
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.is_navigable = is_navigable; | |||||
if(this.is_navigable) { | |||||
this.current_index = 0; | |||||
this.isNavigable = isNavigable; | |||||
if(this.isNavigable) { | |||||
this.currentIndex = 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) { | |||||
this.colors = colors; | |||||
setColors() { | |||||
let args = this.rawChartArgs; | |||||
// Needs structure as per only labels/datasets | |||||
const list = type === 'percentage' || type === 'pie' | |||||
? this.data.labels | |||||
: this.data.datasets; | |||||
// Needs structure as per only labels/datasets, from config | |||||
const list = args.type === 'percentage' || args.type === 'pie' | |||||
? args.data.labels | |||||
: 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.translate_y = 10; | |||||
this.translateX = 60; | |||||
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(); | |||||
// these both dependent on width >.<, how can this be decoupled | |||||
this.setupEmptyValues(); | |||||
this.calc(); | |||||
this.setupRenderer(); // this chart's rendered with the config | |||||
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() { | |||||
let special_values_width = 0; | |||||
// let char_width = 8; | |||||
// this.specific_values.map(val => { | |||||
// let str_width = getStringWidth((val.title + ""), char_width); | |||||
// if(str_width > special_values_width) { | |||||
// special_values_width = str_width - 40; | |||||
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) - special_values_width; | |||||
this.width = this.baseWidth - this.translate_x * 2; | |||||
this.baseWidth = getElementContentWidth(this.parent) - outerAnnotationsWidth; | |||||
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.stats_wrapper = this.container.querySelector('.graph-stats-container'); | |||||
this.chartWrapper = this.container.querySelector('.frappe-chart'); | |||||
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() { | |||||
// this.components.forEach((component) => { | |||||
// // | |||||
// }); | |||||
update() { | |||||
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() {} | |||||
bind_overlay() {} | |||||
makeOverlay() {} | |||||
bindOverlay() {} | |||||
bind_units() {} | bind_units() {} | ||||
on_left_arrow() {} | |||||
on_right_arrow() {} | |||||
on_up_arrow() {} | |||||
on_down_arrow() {} | |||||
on_enter_key() {} | |||||
onLeftArrow() {} | |||||
onRightArrow() {} | |||||
onUpArrow() {} | |||||
onDownArrow() {} | |||||
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) { | |||||
if(type === this.type) return; | |||||
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 | |||||
}); | |||||
getDifferentChart(type) { | |||||
return getDifferentChart(type, this.type, this.rawChartArgs); | |||||
} | } | ||||
} | } |
@@ -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.chart_wrapper.style.paddingTop = '0px'; | |||||
this.chartWrapper.style.marginTop = '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(); | |||||
} | } | ||||
} | } |
@@ -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.chart_wrapper.style.marginTop = '45px'; | |||||
this.chartWrapper.className += ' ' + 'graph-focus-margin'; | |||||
this.chartWrapper.style.marginTop = '45px'; | |||||
this.stats_wrapper.className += ' ' + 'graph-focus-margin'; | |||||
this.stats_wrapper.style.marginBottom = '30px'; | |||||
this.stats_wrapper.style.paddingTop = '0px'; | |||||
this.statsWrapper.className += ' ' + 'graph-focus-margin'; | |||||
this.statsWrapper.style.marginBottom = '30px'; | |||||
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> | ||||
@@ -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> | ||||
@@ -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 | |||||
}); | |||||
} |
@@ -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 | ||||