[component] axes components working
This commit is contained in:
parent
02685dab7e
commit
cbb1242fe7
1034
dist/frappe-charts.esm.js
vendored
1034
dist/frappe-charts.esm.js
vendored
File diff suppressed because it is too large
Load Diff
2
dist/frappe-charts.min.cjs.js
vendored
2
dist/frappe-charts.min.cjs.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.esm.js
vendored
2
dist/frappe-charts.min.esm.js
vendored
File diff suppressed because one or more lines are too long
2
dist/frappe-charts.min.iife.js
vendored
2
dist/frappe-charts.min.iife.js
vendored
File diff suppressed because one or more lines are too long
2
docs/assets/js/frappe-charts.min.js
vendored
2
docs/assets/js/frappe-charts.min.js
vendored
File diff suppressed because one or more lines are too long
@ -8,7 +8,7 @@ let bar_composite_data = {
|
|||||||
"2013", "2014", "2015", "2016", "2017"],
|
"2013", "2014", "2015", "2016", "2017"],
|
||||||
|
|
||||||
datasets: [{
|
datasets: [{
|
||||||
"title": "Events",
|
"label": "Events",
|
||||||
"values": report_count_list,
|
"values": report_count_list,
|
||||||
// "formatted": report_count_list.map(d => d + " reports")
|
// "formatted": report_count_list.map(d => d + " reports")
|
||||||
}]
|
}]
|
||||||
@ -77,15 +77,15 @@ let type_data = {
|
|||||||
|
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
title: "Some Data",
|
label: "Some Data",
|
||||||
values: [25, 40, 30, 35, 8, 52, 17, -4]
|
values: [25, 40, 30, 35, 8, 52, 17, -4]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Another Set",
|
label: "Another Set",
|
||||||
values: [25, 50, -10, 15, 18, 32, 27, 14]
|
values: [25, 50, -10, 15, 18, 32, 27, 14]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Yet Another",
|
label: "Yet Another",
|
||||||
values: [15, 20, -3, -15, 58, 12, -17, 37]
|
values: [15, 20, -3, -15, 58, 12, -17, 37]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -211,8 +211,8 @@ let update_data = {
|
|||||||
}],
|
}],
|
||||||
"specific_values": [
|
"specific_values": [
|
||||||
{
|
{
|
||||||
title: "Altitude",
|
label: "Altitude",
|
||||||
// title: "A very long text",
|
// label: "A very long text",
|
||||||
line_type: "dashed",
|
line_type: "dashed",
|
||||||
value: 38
|
value: 38
|
||||||
},
|
},
|
||||||
|
|||||||
@ -64,15 +64,15 @@
|
|||||||
|
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
title: "Some Data",
|
label: "Some Data",
|
||||||
values: [25, 40, 30, 35, 8, 52, 17, -4]
|
values: [25, 40, 30, 35, 8, 52, 17, -4]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Another Set",
|
label: "Another Set",
|
||||||
values: [25, 50, -10, 15, 18, 32, 27, 14]
|
values: [25, 50, -10, 15, 18, 32, 27, 14]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Yet Another",
|
label: "Yet Another",
|
||||||
values: [15, 20, -3, -15, 58, 12, -17, 37]
|
values: [15, 20, -3, -15, 58, 12, -17, 37]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -144,7 +144,7 @@
|
|||||||
|
|
||||||
data.specific_values = [
|
data.specific_values = [
|
||||||
{
|
{
|
||||||
title: "Altitude",
|
label: "Altitude",
|
||||||
line_type: "dashed", // or "solid"
|
line_type: "dashed", // or "solid"
|
||||||
value: 38
|
value: 38
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import BaseChart from './BaseChart';
|
import BaseChart from './BaseChart';
|
||||||
|
import { ChartComponent, IndexedChartComponent } from '../objects/ChartComponent';
|
||||||
import { getOffset, 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';
|
||||||
import { runSMILAnimation } from '../utils/animation';
|
import { runSMILAnimation } from '../utils/animation';
|
||||||
import { calcIntervals } from '../utils/intervals';
|
import { calcIntervals, getIntervalSize, getValueRange, getZeroIndex } from '../utils/intervals';
|
||||||
import { floatTwo } from '../utils/helpers';
|
import { floatTwo, fillArray } from '../utils/helpers';
|
||||||
|
|
||||||
export default class AxisChart extends BaseChart {
|
export default class AxisChart extends BaseChart {
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
@ -16,560 +17,14 @@ export default class AxisChart extends BaseChart {
|
|||||||
this.zeroLine = this.height;
|
this.zeroLine = this.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseData() {
|
checkData(data) {
|
||||||
let args = this.rawChartArgs;
|
|
||||||
this.xAxisLabels = args.data.labels || [];
|
|
||||||
this.y = args.data.datasets || [];
|
|
||||||
|
|
||||||
this.y.forEach(function(d, i) {
|
|
||||||
d.index = i;
|
|
||||||
}, this);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
reCalc() {
|
getFirstUpdateData(data) {
|
||||||
// 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.yAxisLabels = ['0', '5', '10'];
|
|
||||||
|
|
||||||
this.xPositions = [0, this.width/2, this.width];
|
|
||||||
this.xAxisLabels = ['0', '5', '10'];
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
calcInitStage() {
|
|
||||||
// will borrow from the full recalc function
|
|
||||||
}
|
|
||||||
|
|
||||||
calcIntermediateValues() {
|
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should be inherent in BaseChart
|
|
||||||
refreshRenderer() {
|
|
||||||
// These args are basically the current state of the chart,
|
|
||||||
// with constant and alive params mixed
|
|
||||||
this.renderer = new AxisChartRenderer({
|
|
||||||
totalHeight: this.height,
|
|
||||||
totalWidth: this.width,
|
|
||||||
zeroLine: this.zeroLine,
|
|
||||||
avgUnitWidth: this.avgUnitWidth,
|
|
||||||
xAxisMode: this.xAxisMode,
|
|
||||||
yAxisMode: this.yAxisMode
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setupComponents() {
|
|
||||||
// Must have access to all current data things
|
|
||||||
let self = this;
|
|
||||||
this.yAxis = {
|
|
||||||
layerClass: 'y axis',
|
|
||||||
layer: undefined,
|
|
||||||
make: self.makeYLines.bind(self),
|
|
||||||
makeArgs: [self.yAxisPositions, self.yAxisLabels],
|
|
||||||
store: [],
|
|
||||||
// animate? or update? will come to while implementing
|
|
||||||
animate: self.animateYLines,
|
|
||||||
// indexed: 1 // ?? As per datasets?
|
|
||||||
};
|
|
||||||
this.xAxis = {
|
|
||||||
layerClass: 'x axis',
|
|
||||||
layer: undefined,
|
|
||||||
make: self.makeXLines.bind(self),
|
|
||||||
// TODO: will implement series skip with avgUnitWidth and isSeries later
|
|
||||||
makeArgs: [self.xPositions, self.xAxisLabels],
|
|
||||||
store: [],
|
|
||||||
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 = {
|
|
||||||
// layerClass: 'y marker axis',
|
|
||||||
// layer: undefined,
|
|
||||||
// make: makeYMarkerLines,
|
|
||||||
// makeArgs: [this.yMarkerPositions, this.yMarker],
|
|
||||||
// store: [],
|
|
||||||
// animate: animateYMarkerLines
|
|
||||||
};
|
|
||||||
this.xMarkerLines = {
|
|
||||||
// layerClass: 'x marker axis',
|
|
||||||
// layer: undefined,
|
|
||||||
// make: makeXMarkerLines,
|
|
||||||
// makeArgs: [this.xMarkerPositions, this.xMarker],
|
|
||||||
// store: [],
|
|
||||||
// animate: animateXMarkerLines
|
|
||||||
};
|
|
||||||
|
|
||||||
// Marker Regions
|
|
||||||
|
|
||||||
this.components = [
|
|
||||||
this.yAxis,
|
|
||||||
this.xAxis,
|
|
||||||
// this.yMarkerLines,
|
|
||||||
// this.xMarkerLines,
|
|
||||||
// this.dataUnits,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_values() {
|
|
||||||
this.data.datasets.map(d => {
|
|
||||||
d.values = d.values.map(val => (!isNaN(val) ? val : 0));
|
|
||||||
});
|
|
||||||
this.setup_x();
|
|
||||||
this.setup_y();
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_x() {
|
|
||||||
this.set_avgUnitWidth_and_x_offset();
|
|
||||||
if(this.xPositions) {
|
|
||||||
this.x_old_axis_positions = this.xPositions.slice();
|
|
||||||
}
|
|
||||||
this.xPositions = this.xAxisLabels.map((d, i) =>
|
|
||||||
floatTwo(this.x_offset + i * this.avgUnitWidth));
|
|
||||||
|
|
||||||
if(!this.x_old_axis_positions) {
|
|
||||||
this.x_old_axis_positions = this.xPositions.slice();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_y() {
|
|
||||||
if(this.yAxisLabels) {
|
|
||||||
this.y_old_axis_values = this.yAxisLabels.slice();
|
|
||||||
}
|
|
||||||
|
|
||||||
let values = this.get_all_y_values();
|
|
||||||
|
|
||||||
if(this.y_sums && this.y_sums.length > 0) {
|
|
||||||
values = values.concat(this.y_sums);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.yAxisLabels = calcIntervals(values, this.type === 'line');
|
|
||||||
|
|
||||||
if(!this.y_old_axis_values) {
|
|
||||||
this.y_old_axis_values = this.yAxisLabels.slice();
|
|
||||||
}
|
|
||||||
|
|
||||||
const y_pts = this.yAxisLabels;
|
|
||||||
const value_range = y_pts[y_pts.length-1] - y_pts[0];
|
|
||||||
|
|
||||||
if(this.multiplier) this.old_multiplier = this.multiplier;
|
|
||||||
this.multiplier = this.height / value_range;
|
|
||||||
if(!this.old_multiplier) this.old_multiplier = this.multiplier;
|
|
||||||
|
|
||||||
const interval = y_pts[1] - y_pts[0];
|
|
||||||
const interval_height = interval * this.multiplier;
|
|
||||||
|
|
||||||
let zero_index;
|
|
||||||
|
|
||||||
if(y_pts.indexOf(0) >= 0) {
|
|
||||||
// the range has a given zero
|
|
||||||
// zero-line on the chart
|
|
||||||
zero_index = y_pts.indexOf(0);
|
|
||||||
} else if(y_pts[0] > 0) {
|
|
||||||
// Minimum value is positive
|
|
||||||
// zero-line is off the chart: below
|
|
||||||
let min = y_pts[0];
|
|
||||||
zero_index = (-1) * min / interval;
|
|
||||||
} else {
|
|
||||||
// Maximum value is negative
|
|
||||||
// zero-line is off the chart: above
|
|
||||||
let max = y_pts[y_pts.length - 1];
|
|
||||||
zero_index = (-1) * max / interval + (y_pts.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.zeroLine) this.old_zeroLine = this.zeroLine;
|
|
||||||
this.zeroLine = this.height - (zero_index * interval_height);
|
|
||||||
if(!this.old_zeroLine) this.old_zeroLine = this.zeroLine;
|
|
||||||
|
|
||||||
// Make positions arrays for y elements
|
|
||||||
if(this.yAxisPositions) this.oldYAxisPositions = this.yAxisPositions;
|
|
||||||
this.yAxisPositions = this.yAxisLabels.map(d => this.zeroLine - d * this.multiplier);
|
|
||||||
if(!this.oldYAxisPositions) this.oldYAxisPositions = this.yAxisPositions;
|
|
||||||
|
|
||||||
// if(this.yAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions;
|
|
||||||
// this.yAnnotationPositions = this.specific_values.map(d => this.zeroLine - d.value * this.multiplier);
|
|
||||||
// if(!this.oldYAnnotationPositions) this.oldYAnnotationPositions = this.yAnnotationPositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
makeXLines(positions, values) {
|
|
||||||
// TODO: draw as per condition (with/without label etc.)
|
|
||||||
return positions.map((position, i) => this.renderer.xLine(position, values[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
makeYLines(positions, values) {
|
|
||||||
return positions.map((position, i) => this.renderer.yLine(position, values[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_graph(init=false) {
|
|
||||||
// TODO: NO INIT!
|
|
||||||
if(this.raw_chart_args.hasOwnProperty("init") && !this.raw_chart_args.init) {
|
|
||||||
this.y.map((d, i) => {
|
|
||||||
d.svg_units = [];
|
|
||||||
this.make_path && this.make_path(d, this.xPositions, d.yUnitPositions, this.colors[i]);
|
|
||||||
this.makeUnits(d);
|
|
||||||
this.calcYDependencies();
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(init) {
|
|
||||||
this.draw_new_graph_and_animate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.y.map((d, i) => {
|
|
||||||
d.svg_units = [];
|
|
||||||
this.make_path && this.make_path(d, this.xPositions, d.yUnitPositions, this.colors[i]);
|
|
||||||
this.makeUnits(d);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_new_graph_and_animate() {
|
|
||||||
let data = [];
|
|
||||||
this.y.map((d, i) => {
|
|
||||||
// Anim: Don't draw initial values, store them and update later
|
|
||||||
d.yUnitPositions = new Array(d.values.length).fill(this.zeroLine); // no value
|
|
||||||
data.push({values: d.values});
|
|
||||||
d.svg_units = [];
|
|
||||||
|
|
||||||
this.make_path && this.make_path(d, this.xPositions, d.yUnitPositions, this.colors[i]);
|
|
||||||
this.makeUnits(d);
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.updateData(data);
|
|
||||||
}, 350);
|
|
||||||
}
|
|
||||||
|
|
||||||
setupNavigation(init) {
|
|
||||||
if(init) {
|
|
||||||
// Hack: defer nav till initial updateData
|
|
||||||
setTimeout(() => {
|
|
||||||
super.setupNavigation(init);
|
|
||||||
}, 500);
|
|
||||||
} else {
|
|
||||||
super.setupNavigation(init);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
makeUnits(d) {
|
|
||||||
this.makeDatasetUnits(
|
|
||||||
this.xPositions,
|
|
||||||
d.yUnitPositions,
|
|
||||||
this.colors[d.index],
|
|
||||||
d.index,
|
|
||||||
this.y.length
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
makeDatasetUnits(x_values, y_values, color, dataset_index,
|
|
||||||
no_of_datasets, units_group, units_array, unit) {
|
|
||||||
|
|
||||||
if(!units_group) units_group = this.svg_units_groups[dataset_index];
|
|
||||||
if(!units_array) units_array = this.y[dataset_index].svg_units;
|
|
||||||
if(!unit) unit = this.unit_args;
|
|
||||||
|
|
||||||
units_group.textContent = '';
|
|
||||||
units_array.length = 0;
|
|
||||||
|
|
||||||
let unit_AxisChartRenderer = new AxisChartRenderer(this.height, this.zeroLine, this.avgUnitWidth);
|
|
||||||
|
|
||||||
y_values.map((y, i) => {
|
|
||||||
let data_unit = unit_AxisChartRenderer[unit.type](
|
|
||||||
x_values[i],
|
|
||||||
y,
|
|
||||||
unit.args,
|
|
||||||
color,
|
|
||||||
i,
|
|
||||||
dataset_index,
|
|
||||||
no_of_datasets
|
|
||||||
);
|
|
||||||
units_group.appendChild(data_unit);
|
|
||||||
units_array.push(data_unit);
|
|
||||||
});
|
|
||||||
|
|
||||||
if(this.isNavigable) {
|
|
||||||
this.bind_units(units_array);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bindTooltip() {
|
|
||||||
// TODO: could be in tooltip itself, as it is a given functionality for its parent
|
|
||||||
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.translateY * 2) {
|
|
||||||
this.mapTooltipXPosition(relX);
|
|
||||||
} else {
|
|
||||||
this.tip.hide_tip();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
mapTooltipXPosition(relX) {
|
|
||||||
if(!this.y_min_tops) return;
|
|
||||||
|
|
||||||
let titles = this.xAxisLabels;
|
|
||||||
if(this.format_tooltip_x && this.format_tooltip_x(this.xAxisLabels[0])) {
|
|
||||||
titles = this.xAxisLabels.map(d=>this.format_tooltip_x(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
let y_format = this.format_tooltip_y && this.format_tooltip_y(this.y[0].values[0]);
|
|
||||||
|
|
||||||
for(var i=this.xPositions.length - 1; i >= 0 ; i--) {
|
|
||||||
let x_val = this.xPositions[i];
|
|
||||||
// let delta = i === 0 ? this.avgUnitWidth : x_val - this.xPositions[i-1];
|
|
||||||
if(relX > x_val - this.avgUnitWidth/2) {
|
|
||||||
let x = x_val + this.translateX;
|
|
||||||
let y = this.y_min_tops[i] + this.translateY;
|
|
||||||
|
|
||||||
let title = titles[i];
|
|
||||||
let values = this.y.map((set, j) => {
|
|
||||||
return {
|
|
||||||
title: set.title,
|
|
||||||
value: y_format ? this.format_tooltip_y(set.values[i]) : set.values[i],
|
|
||||||
color: this.colors[j],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.tip.set_values(x, y, title, '', values);
|
|
||||||
this.tip.show_tip();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// API
|
|
||||||
updateData(newY, newX) {
|
|
||||||
if(!newX) {
|
|
||||||
newX = this.xAxisLabels;
|
|
||||||
}
|
|
||||||
this.updating = true;
|
|
||||||
|
|
||||||
this.old_x_values = this.xAxisLabels.slice();
|
|
||||||
this.old_y_axis_tops = this.y.map(d => d.yUnitPositions.slice());
|
|
||||||
|
|
||||||
this.old_y_values = this.y.map(d => d.values);
|
|
||||||
|
|
||||||
// Just update values prop, setup_x/y() will do the rest
|
|
||||||
if(newY) this.y.map(d => {d.values = newY[d.index].values;});
|
|
||||||
if(newX) this.xAxisLabels = newX;
|
|
||||||
|
|
||||||
this.setup_x();
|
|
||||||
this.setup_y();
|
|
||||||
|
|
||||||
// Change in data, so calculate dependencies
|
|
||||||
this.calcYDependencies();
|
|
||||||
|
|
||||||
// Got the values? Now begin drawing
|
|
||||||
this.animator = new Animator(this.height, this.width, this.zeroLine, this.avgUnitWidth);
|
|
||||||
|
|
||||||
this.animate_graphs();
|
|
||||||
|
|
||||||
this.updating = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
animate_graphs() {
|
|
||||||
this.elements_to_animate = [];
|
|
||||||
// Pre-prep, equilize no of positions between old and new
|
|
||||||
let [old_x, newX] = equilizeNoOfElements(
|
|
||||||
this.x_old_axis_positions.slice(),
|
|
||||||
this.xPositions.slice()
|
|
||||||
);
|
|
||||||
|
|
||||||
let [oldYAxis, newYAxis] = equilizeNoOfElements(
|
|
||||||
this.oldYAxisPositions.slice(),
|
|
||||||
this.yAxisPositions.slice()
|
|
||||||
);
|
|
||||||
|
|
||||||
let newXValues = this.xAxisLabels.slice();
|
|
||||||
let newYValues = this.yAxisLabels.slice();
|
|
||||||
|
|
||||||
let extra_points = this.xPositions.slice().length - this.x_old_axis_positions.slice().length;
|
|
||||||
|
|
||||||
if(extra_points > 0) {
|
|
||||||
this.makeXLines(old_x, newXValues);
|
|
||||||
}
|
|
||||||
// No Y extra check?
|
|
||||||
this.makeYLines(oldYAxis, newYValues);
|
|
||||||
|
|
||||||
// Animation
|
|
||||||
if(extra_points !== 0) {
|
|
||||||
this.animateXLines(old_x, newX);
|
|
||||||
}
|
|
||||||
this.animateYLines(oldYAxis, newYAxis);
|
|
||||||
|
|
||||||
this.y.map(d => {
|
|
||||||
let [old_y, newY] = equilizeNoOfElements(
|
|
||||||
this.old_y_axis_tops[d.index].slice(),
|
|
||||||
d.yUnitPositions.slice()
|
|
||||||
);
|
|
||||||
if(extra_points > 0) {
|
|
||||||
this.make_path && this.make_path(d, old_x, old_y, this.colors[d.index]);
|
|
||||||
this.makeDatasetUnits(old_x, old_y, this.colors[d.index], d.index, this.y.length);
|
|
||||||
}
|
|
||||||
// Animation
|
|
||||||
d.path && this.animate_path(d, newX, newY);
|
|
||||||
this.animate_units(d, newX, newY);
|
|
||||||
});
|
|
||||||
|
|
||||||
runSMILAnimation(this.chartWrapper, this.svg, this.elements_to_animate);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.y.map(d => {
|
|
||||||
this.make_path && this.make_path(d, this.xPositions, d.yUnitPositions, this.colors[d.index]);
|
|
||||||
this.makeUnits(d);
|
|
||||||
|
|
||||||
this.makeYLines(this.yAxisPositions, this.yAxisLabels);
|
|
||||||
this.makeXLines(this.xPositions, this.xAxisLabels);
|
|
||||||
// this.make_y_specifics(this.yAnnotationPositions, this.specific_values);
|
|
||||||
});
|
|
||||||
}, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
animate_path(d, newX, newY) {
|
|
||||||
const newPointsList = newY.map((y, i) => (newX[i] + ',' + y));
|
|
||||||
this.elements_to_animate = this.elements_to_animate
|
|
||||||
.concat(this.animator.path(d, newPointsList.join("L")));
|
|
||||||
}
|
|
||||||
|
|
||||||
animate_units(d, newX, newY) {
|
|
||||||
let type = this.unit_args.type;
|
|
||||||
|
|
||||||
d.svg_units.map((unit, i) => {
|
|
||||||
if(newX[i] === undefined || newY[i] === undefined) return;
|
|
||||||
this.elements_to_animate.push(this.animator[type](
|
|
||||||
{unit:unit, array:d.svg_units, index: i}, // unit, with info to replace where it came from in the data
|
|
||||||
newX[i],
|
|
||||||
newY[i],
|
|
||||||
d.index,
|
|
||||||
this.y.length
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
animateXLines(oldX, newX) {
|
|
||||||
this.xAxisLines.map((xLine, i) => {
|
|
||||||
this.elements_to_animate.push(this.animator.verticalLine(
|
|
||||||
xLine, newX[i], oldX[i]
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
animateYLines(oldY, newY) {
|
|
||||||
this.yAxisLines.map((yLine, i) => {
|
|
||||||
this.elements_to_animate.push(this.animator.horizontalLine(
|
|
||||||
yLine, newY[i], oldY[i]
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
animateYAnnotations() {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
add_data_point(y_point, x_point, index=this.xAxisLabels.length) {
|
|
||||||
let newY = this.y.map(data_set => { return {values:data_set.values}; });
|
|
||||||
newY.map((d, i) => { d.values.splice(index, 0, y_point[i]); });
|
|
||||||
let newX = this.xAxisLabels.slice();
|
|
||||||
newX.splice(index, 0, x_point);
|
|
||||||
|
|
||||||
this.updateData(newY, newX);
|
|
||||||
}
|
|
||||||
|
|
||||||
remove_data_point(index = this.xAxisLabels.length-1) {
|
|
||||||
if(this.xAxisLabels.length < 3) return;
|
|
||||||
|
|
||||||
let newY = this.y.map(data_set => { return {values:data_set.values}; });
|
|
||||||
newY.map((d) => { d.values.splice(index, 1); });
|
|
||||||
let newX = this.xAxisLabels.slice();
|
|
||||||
newX.splice(index, 1);
|
|
||||||
|
|
||||||
this.updateData(newY, newX);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDataPoint(index=this.currentIndex) {
|
|
||||||
// check for length
|
|
||||||
let data_point = {
|
|
||||||
index: index
|
|
||||||
};
|
|
||||||
let y = this.y[0];
|
|
||||||
['svg_units', 'yUnitPositions', 'values'].map(key => {
|
|
||||||
let data_key = key.slice(0, key.length-1);
|
|
||||||
data_point[data_key] = y[key][index];
|
|
||||||
});
|
|
||||||
data_point.label = this.xAxisLabels[index];
|
|
||||||
return data_point;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCurrentDataPoint(index) {
|
|
||||||
index = parseInt(index);
|
|
||||||
if(index < 0) index = 0;
|
|
||||||
if(index >= this.xAxisLabels.length) index = this.xAxisLabels.length - 1;
|
|
||||||
if(index === this.currentIndex) return;
|
|
||||||
this.currentIndex = index;
|
|
||||||
fire(this.parent, "data-select", this.getDataPoint());
|
|
||||||
}
|
|
||||||
|
|
||||||
set_avgUnitWidth_and_x_offset() {
|
|
||||||
// Set the ... you get it
|
|
||||||
this.avgUnitWidth = this.width/(this.xAxisLabels.length - 1);
|
|
||||||
this.x_offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_all_y_values() {
|
|
||||||
let all_values = [];
|
|
||||||
|
|
||||||
// Add in all the y values in the datasets
|
|
||||||
this.y.map(d => {
|
|
||||||
all_values = all_values.concat(d.values);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add in all the specific values
|
|
||||||
return all_values.concat(this.specific_values.map(d => d.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
calcYDependencies() {
|
calcYDependencies() {
|
||||||
this.y_min_tops = new Array(this.xAxisLabels.length).fill(9999);
|
this.y_min_tops = new Array(this.xAxisLabels.length).fill(9999);
|
||||||
this.y.map(d => {
|
this.y.map(d => {
|
||||||
@ -583,4 +38,156 @@ export default class AxisChart extends BaseChart {
|
|||||||
// this.chartWrapper.removeChild(this.tip.container);
|
// this.chartWrapper.removeChild(this.tip.container);
|
||||||
// this.make_tooltip();
|
// this.make_tooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareData() {
|
||||||
|
let s = this.state;
|
||||||
|
s.xAxisLabels = this.data.labels || [];
|
||||||
|
s.datasetLength = s.xAxisLabels.length;
|
||||||
|
|
||||||
|
let zeroArray = new Array(s.datasetLength).fill(0);
|
||||||
|
|
||||||
|
s.datasets = this.data.datasets;
|
||||||
|
if(!this.data.datasets) {
|
||||||
|
// default
|
||||||
|
s.datasets = [{
|
||||||
|
values: zeroArray
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
s.datasets.map((d, i)=> {
|
||||||
|
let vals = d.values;
|
||||||
|
if(!vals) {
|
||||||
|
vals = zeroArray;
|
||||||
|
} else {
|
||||||
|
// Check for non values
|
||||||
|
vals = vals.map(val => (!isNaN(val) ? val : 0));
|
||||||
|
|
||||||
|
// Trim or extend
|
||||||
|
if(vals.length > s.datasetLength) {
|
||||||
|
vals = vals.slice(0, s.datasetLength);
|
||||||
|
} else {
|
||||||
|
vals = fillArray(vals, s.datasetLength - vals.length, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.index = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reCalc() {
|
||||||
|
let s = this.state;
|
||||||
|
|
||||||
|
// X
|
||||||
|
s.xAxisLabels = this.data.labels;
|
||||||
|
this.calcXPositions();
|
||||||
|
|
||||||
|
// Y
|
||||||
|
s.datasetsLabels = this.data.datasets.map(d => d.label);
|
||||||
|
|
||||||
|
// s.datasetsValues = [[]]; indexed component
|
||||||
|
// s.datasetsValues = [[[12, 34, 68], [10, 5, 46]], [[20, 20, 20]]]; // array of indexed components
|
||||||
|
s.datasetsValues = s.datasets.map(d => d.values); // indexed component
|
||||||
|
s.yAxisLabels = calcIntervals(this.getAllYValues(), this.type === 'line');
|
||||||
|
this.calcYAxisPositions();
|
||||||
|
|
||||||
|
// *** this.state.datasetsPoints =
|
||||||
|
}
|
||||||
|
|
||||||
|
calcXPositions() {
|
||||||
|
let s = this.state;
|
||||||
|
this.setUnitWidthAndXOffset();
|
||||||
|
s.xPositions = s.xAxisLabels.map((d, i) =>
|
||||||
|
floatTwo(s.xOffset + i * s.unitWidth));
|
||||||
|
}
|
||||||
|
|
||||||
|
calcYAxisPositions() {
|
||||||
|
let s = this.state;
|
||||||
|
const yPts = s.yAxisLabels;
|
||||||
|
|
||||||
|
s.scaleMultiplier = this.height / getValueRange(yPts);
|
||||||
|
const intervalHeight = getIntervalSize(yPts) * s.scaleMultiplier;
|
||||||
|
s.zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
|
||||||
|
|
||||||
|
s.yAxisPositions = yPts.map(d => s.zeroLine - d * s.scaleMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUnitWidthAndXOffset() {
|
||||||
|
this.state.unitWidth = this.width/(this.state.datasetLength - 1);
|
||||||
|
this.state.xOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllYValues() {
|
||||||
|
// TODO: yMarkers, regions, sums, every Y value ever
|
||||||
|
return [].concat(...this.state.datasetsValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
calcIntermedState() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should be inherent in BaseChart
|
||||||
|
refreshRenderer() {
|
||||||
|
// These args are basically the current state of the chart,
|
||||||
|
// with constant and alive params mixed
|
||||||
|
let state = {
|
||||||
|
totalHeight: this.height,
|
||||||
|
totalWidth: this.width,
|
||||||
|
|
||||||
|
xAxisMode: this.config.xAxisMode,
|
||||||
|
yAxisMode: this.config.yAxisMode,
|
||||||
|
|
||||||
|
zeroLine: this.state.zeroLine,
|
||||||
|
unitWidth: this.state.unitWidth,
|
||||||
|
};
|
||||||
|
if(!this.renderer) {
|
||||||
|
this.renderer = new AxisChartRenderer(state);
|
||||||
|
} else {
|
||||||
|
this.renderer.refreshState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupComponents() {
|
||||||
|
this.yAxis = new ChartComponent({
|
||||||
|
layerClass: 'y axis',
|
||||||
|
make: (renderer, positions, values) => {
|
||||||
|
return positions.map((position, i) => renderer.yLine(position, values[i]));
|
||||||
|
},
|
||||||
|
argsKeys: ['yAxisPositions', 'yAxisLabels'],
|
||||||
|
animate: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.xAxis = new ChartComponent({
|
||||||
|
layerClass: 'x axis',
|
||||||
|
make: (renderer, positions, values) => {
|
||||||
|
return positions.map((position, i) => renderer.xLine(position, values[i]));
|
||||||
|
},
|
||||||
|
argsKeys: ['xPositions', 'xAxisLabels'],
|
||||||
|
animate: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Indexed according to dataset
|
||||||
|
|
||||||
|
// this.dataUnits = new IndexedChartComponent({
|
||||||
|
// layerClass: 'x axis',
|
||||||
|
// make: (renderer, positions, values) => {
|
||||||
|
// return positions.map((position, i) => renderer.xLine(position, values[i]));
|
||||||
|
// },
|
||||||
|
// argsKeys: ['xPositions', 'xAxisLabels'],
|
||||||
|
// animate: () => {}
|
||||||
|
// });
|
||||||
|
|
||||||
|
this.yMarkerLines = {};
|
||||||
|
this.xMarkerLines = {};
|
||||||
|
|
||||||
|
// Marker Regions
|
||||||
|
|
||||||
|
this.components = [
|
||||||
|
this.yAxis,
|
||||||
|
this.xAxis,
|
||||||
|
// this.yMarkerLines,
|
||||||
|
// this.xMarkerLines,
|
||||||
|
// this.dataUnits,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,13 +3,21 @@ import AxisChart from './AxisChart';
|
|||||||
export default class BarChart extends AxisChart {
|
export default class BarChart extends AxisChart {
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
super(args);
|
super(args);
|
||||||
|
|
||||||
this.type = 'bar';
|
this.type = 'bar';
|
||||||
this.xAxisMode = args.xAxisMode || 'tick';
|
|
||||||
this.yAxisMode = args.yAxisMode || 'span';
|
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
this.config.xAxisMode = args.xAxisMode || 'tick';
|
||||||
|
this.config.yAxisMode = args.yAxisMode || 'span';
|
||||||
|
}
|
||||||
|
|
||||||
|
setUnitWidthAndXOffset() {
|
||||||
|
this.state.unitWidth = this.width/(this.state.datasetLength + 1);
|
||||||
|
this.state.xOffset = this.state.unitWidth;
|
||||||
|
}
|
||||||
|
|
||||||
setup_values() {
|
setup_values() {
|
||||||
super.setup_values();
|
super.setup_values();
|
||||||
this.x_offset = this.avgUnitWidth;
|
this.x_offset = this.avgUnitWidth;
|
||||||
@ -74,8 +82,5 @@ export default class BarChart extends AxisChart {
|
|||||||
this.updateCurrentDataPoint(this.currentIndex + 1);
|
this.updateCurrentDataPoint(this.currentIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_avgUnitWidth_and_x_offset() {
|
|
||||||
this.avgUnitWidth = this.width/(this.xAxisLabels.length + 1);
|
|
||||||
this.x_offset = this.avgUnitWidth;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export default class BaseChart {
|
|||||||
colors = [],
|
colors = [],
|
||||||
|
|
||||||
isNavigable = 0,
|
isNavigable = 0,
|
||||||
|
showLegend = 1,
|
||||||
|
|
||||||
type = '',
|
type = '',
|
||||||
|
|
||||||
@ -31,23 +32,26 @@ export default class BaseChart {
|
|||||||
this.currentIndex = 0;
|
this.currentIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setupConfiguration();
|
this.configure(arguments[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupConfiguration() {
|
configure(args) {
|
||||||
// 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();
|
this.setColors();
|
||||||
this.setMargins();
|
this.setMargins(args);
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
this.config = {
|
this.config = {
|
||||||
showTooltip: 1,
|
showTooltip: 1, // calculate
|
||||||
showLegend: 1,
|
showLegend: 1,
|
||||||
isNavigable: 0,
|
isNavigable: 0,
|
||||||
animate: 1
|
// animate: 1
|
||||||
|
animate: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.state = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
setColors() {
|
setColors() {
|
||||||
@ -67,8 +71,8 @@ export default class BaseChart {
|
|||||||
this.colors = this.colors.map(color => getColor(color));
|
this.colors = this.colors.map(color => getColor(color));
|
||||||
}
|
}
|
||||||
|
|
||||||
setMargins() {
|
setMargins(args) {
|
||||||
let height = this.rawChartArgs.height;
|
let height = args.height;
|
||||||
this.baseHeight = height;
|
this.baseHeight = height;
|
||||||
this.height = height - 40;
|
this.height = height - 40;
|
||||||
this.translateX = 60;
|
this.translateX = 60;
|
||||||
@ -103,7 +107,7 @@ export default class BaseChart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkData() {}
|
checkData() {}
|
||||||
getFirstUpdateData(data) {}
|
getFirstUpdateData() {}
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
if(this.validate()) {
|
if(this.validate()) {
|
||||||
@ -114,9 +118,7 @@ export default class BaseChart {
|
|||||||
_setup() {
|
_setup() {
|
||||||
this.bindWindowEvents();
|
this.bindWindowEvents();
|
||||||
this.setupConstants();
|
this.setupConstants();
|
||||||
|
this.setupComponents();
|
||||||
// this.setupComponents();
|
|
||||||
|
|
||||||
this.makeContainer();
|
this.makeContainer();
|
||||||
this.makeTooltip(); // without binding
|
this.makeTooltip(); // without binding
|
||||||
this.draw(true);
|
this.draw(true);
|
||||||
@ -158,32 +160,35 @@ export default class BaseChart {
|
|||||||
this.bindTooltip();
|
this.bindTooltip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindTooltip() {}
|
||||||
|
|
||||||
draw(init=false) {
|
draw(init=false) {
|
||||||
// difference from update(): draw the whole object due to groudbreaking event (init, resize, etc.)
|
// difference from update(): draw the whole object due to groudbreaking event (init, resize, etc.)
|
||||||
// (draw everything, layers, groups, units)
|
// (draw everything, layers, groups, units)
|
||||||
this.calc();
|
|
||||||
this.refreshRenderer() // this chart's rendered with the config
|
this.calcWidth();
|
||||||
this.setupComponents();
|
this.refresh(); // refresh conponent with chart a
|
||||||
|
|
||||||
this.makeChartArea();
|
this.makeChartArea();
|
||||||
this.makeLayers();
|
this.setComponentParent();
|
||||||
|
this.makeComponentLayers();
|
||||||
|
|
||||||
this.renderComponents(); // with zero values
|
|
||||||
this.renderLegend();
|
this.renderLegend();
|
||||||
this.setupNavigation(init);
|
this.setupNavigation(init);
|
||||||
|
|
||||||
|
this.renderComponents(); // first time plain render, so no rerender
|
||||||
|
|
||||||
if(this.config.animate) this.update(this.firstUpdateData);
|
if(this.config.animate) this.update(this.firstUpdateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
// difference from draw(): yes you do rerender everything here as well,
|
// difference from draw(): yes you do rerender everything here as well,
|
||||||
// but not things like the chart itself, mosty only at component level
|
// but not things like the chart itself or layers, mosty only at component level
|
||||||
this.reCalc();
|
// HERE IS WHERE THE ACTUAL STATE CHANGES, and old one matters, not in draw
|
||||||
|
this.refresh();
|
||||||
this.reRender();
|
this.reRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshRenderer() {}
|
|
||||||
|
|
||||||
calcWidth() {
|
calcWidth() {
|
||||||
let outerAnnotationsWidth = 0;
|
let outerAnnotationsWidth = 0;
|
||||||
// let charWidth = 8;
|
// let charWidth = 8;
|
||||||
@ -197,9 +202,12 @@ export default class BaseChart {
|
|||||||
this.width = this.baseWidth - this.translateX * 2;
|
this.width = this.baseWidth - this.translateX * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
calc() {
|
refresh() { //?? refresh?
|
||||||
this.calcWidth();
|
this.oldState = this.state ? Object.assign({}, this.state) : {};
|
||||||
|
this.prepareData();
|
||||||
this.reCalc();
|
this.reCalc();
|
||||||
|
this.refreshRenderer();
|
||||||
|
this.refreshComponents();
|
||||||
}
|
}
|
||||||
|
|
||||||
makeChartArea() {
|
makeChartArea() {
|
||||||
@ -218,33 +226,21 @@ export default class BaseChart {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prepareData() {}
|
||||||
|
|
||||||
makeLayers() {
|
reCalc() {}
|
||||||
this.components.forEach((component) => {
|
// Will update values(state)
|
||||||
component.layer = this.makeLayer(component.layerClass);
|
// Will recalc specific parts depending on the update
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateValues() {}
|
refreshRenderer() {}
|
||||||
|
|
||||||
renderComponents() {
|
|
||||||
this.components.forEach(c => {
|
|
||||||
c.store = c.make(...c.makeArgs);
|
|
||||||
c.layer.textContent = '';
|
|
||||||
c.store.forEach(element => {c.layer.appendChild(element);});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
reCalc() {
|
|
||||||
// Will update values(state)
|
|
||||||
// Will recalc specific parts depending on the update
|
|
||||||
}
|
|
||||||
|
|
||||||
reRender(animate=true) {
|
reRender(animate=true) {
|
||||||
if(!animate) {
|
if(!animate) {
|
||||||
this.renderComponents();
|
this.renderComponents();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.intermedState = this.calcIntermedState();
|
||||||
|
this.refreshComponents();
|
||||||
this.animateComponents();
|
this.animateComponents();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.renderComponents();
|
this.renderComponents();
|
||||||
@ -253,19 +249,23 @@ export default class BaseChart {
|
|||||||
// (opt, should not redraw if still in animate?)
|
// (opt, should not redraw if still in animate?)
|
||||||
}
|
}
|
||||||
|
|
||||||
animateComponents() {
|
calcIntermedState() {}
|
||||||
this.intermedValues = this.calcIntermediateValues();
|
|
||||||
this.components.forEach(c => {
|
// convenient component array abstractions
|
||||||
// c.store = c.animate(...c.animateArgs);
|
setComponentParent() { this.components.forEach(c => c.setupParent(this.drawArea)); };
|
||||||
// c.layer.textContent = '';
|
makeComponentLayers() { this.components.forEach(c => c.makeLayer()); }
|
||||||
// c.store.forEach(element => {c.layer.appendChild(element);});
|
renderComponents() { this.components.forEach(c => c.render()); }
|
||||||
});
|
animateComponents() { this.components.forEach(c => c.animate()); }
|
||||||
|
refreshComponents() {
|
||||||
|
let args = {
|
||||||
|
chartState: this.state,
|
||||||
|
oldChartState: this.oldState,
|
||||||
|
intermedState: this.intermedState,
|
||||||
|
chartRenderer: this.renderer
|
||||||
|
};
|
||||||
|
this.components.forEach(c => c.refresh(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
calcInitStage() {}
|
|
||||||
|
|
||||||
|
|
||||||
renderLegend() {}
|
renderLegend() {}
|
||||||
|
|
||||||
setupNavigation(init=false) {
|
setupNavigation(init=false) {
|
||||||
@ -309,10 +309,6 @@ export default class BaseChart {
|
|||||||
getDataPoint() {}
|
getDataPoint() {}
|
||||||
updateCurrentDataPoint() {}
|
updateCurrentDataPoint() {}
|
||||||
|
|
||||||
makeLayer(className, transform='') {
|
|
||||||
return makeSVGGroup(this.drawArea, className, transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
getDifferentChart(type) {
|
getDifferentChart(type) {
|
||||||
return getDifferentChart(type, this.type, this.rawChartArgs);
|
return getDifferentChart(type, this.type, this.rawChartArgs);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,27 +4,27 @@ import { makeSVGGroup, makePath, makeGradient } from '../utils/draw';
|
|||||||
export default class LineChart extends AxisChart {
|
export default class LineChart extends AxisChart {
|
||||||
constructor(args) {
|
constructor(args) {
|
||||||
super(args);
|
super(args);
|
||||||
|
this.type = 'line';
|
||||||
this.xAxisMode = args.xAxisMode || 'span';
|
|
||||||
this.yAxisMode = args.yAxisMode || 'span';
|
|
||||||
|
|
||||||
if(args.hasOwnProperty('show_dots')) {
|
|
||||||
this.show_dots = args.show_dots;
|
|
||||||
} else {
|
|
||||||
this.show_dots = 1;
|
|
||||||
}
|
|
||||||
this.region_fill = args.region_fill;
|
|
||||||
|
|
||||||
if(Object.getPrototypeOf(this) !== LineChart.prototype) {
|
if(Object.getPrototypeOf(this) !== LineChart.prototype) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.dot_radius = args.dot_radius || 4;
|
|
||||||
this.heatline = args.heatline;
|
|
||||||
this.type = 'line';
|
|
||||||
|
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configure(args) {
|
||||||
|
super.configure(args);
|
||||||
|
this.config.xAxisMode = args.xAxisMode || 'span';
|
||||||
|
this.config.yAxisMode = args.yAxisMode || 'span';
|
||||||
|
|
||||||
|
this.config.dot_radius = args.dot_radius || 4;
|
||||||
|
|
||||||
|
this.config.heatline = args.heatline || 0;
|
||||||
|
this.config.region_fill = args.region_fill || 0;
|
||||||
|
this.config.show_dots = args.show_dots || 1;
|
||||||
|
}
|
||||||
|
|
||||||
setupPreUnitLayers() {
|
setupPreUnitLayers() {
|
||||||
// Path groups
|
// Path groups
|
||||||
this.paths_groups = [];
|
this.paths_groups = [];
|
||||||
|
|||||||
@ -1,26 +1,51 @@
|
|||||||
// export default class ChartComponent {
|
import { makeSVGGroup } from '../utils/draw';
|
||||||
// constructor({
|
|
||||||
// parent = null,
|
|
||||||
// colors = []
|
|
||||||
// }) {
|
|
||||||
// this.parent = parent;
|
|
||||||
// this.colors = colors;
|
|
||||||
// this.title_name = '';
|
|
||||||
// this.title_value = '';
|
|
||||||
// this.list_values = [];
|
|
||||||
// this.title_value_first = 0;
|
|
||||||
|
|
||||||
// this.x = 0;
|
export class ChartComponent {
|
||||||
// this.y = 0;
|
constructor({
|
||||||
|
layerClass = '',
|
||||||
|
layerTransform = '',
|
||||||
|
make,
|
||||||
|
argsKeys,
|
||||||
|
animate
|
||||||
|
}) {
|
||||||
|
this.layerClass = layerClass; // 'y axis'
|
||||||
|
this.layerTransform = layerTransform;
|
||||||
|
this.make = make;
|
||||||
|
this.argsKeys = argsKeys;//['yAxisPositions', 'yAxisLabels'];
|
||||||
|
this.animate = animate;
|
||||||
|
|
||||||
// this.top = 0;
|
this.layer = undefined;
|
||||||
// this.left = 0;
|
this.store = []; //[[]] depends on indexed
|
||||||
|
}
|
||||||
|
|
||||||
// this.setup();
|
refresh(args) {
|
||||||
// }
|
this.chartState = args.chartState;
|
||||||
|
this.oldChartState = args.oldChartState;
|
||||||
|
this.intermedState = args.intermedState;
|
||||||
|
|
||||||
// setup() {
|
this.chartRenderer = args.chartRenderer;
|
||||||
// this.make_tooltip();
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
render() {
|
||||||
|
let args = this.argsKeys.map(key => this.chartState[key]);
|
||||||
|
args.unshift(this.chartRenderer);
|
||||||
|
this.store = this.make(...args);
|
||||||
|
|
||||||
|
this.layer.textContent = '';
|
||||||
|
this.store.forEach(element => {
|
||||||
|
this.layer.appendChild(element);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupParent(parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeLayer() {
|
||||||
|
this.layer = makeSVGGroup(this.parent, this.layerClass, this.layerTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IndexedChartComponent extends ChartComponent {
|
||||||
|
//
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { fillArray } from '../utils/helpers';
|
import { fillArray } from './helpers';
|
||||||
|
|
||||||
const MIN_BAR_PERCENT_HEIGHT = 0.01;
|
const MIN_BAR_PERCENT_HEIGHT = 0.01;
|
||||||
|
|
||||||
|
|||||||
@ -205,10 +205,10 @@ export function makeHoriYLine(y, label, totalWidth, mode) {
|
|||||||
|
|
||||||
export class AxisChartRenderer {
|
export class AxisChartRenderer {
|
||||||
constructor(state) {
|
constructor(state) {
|
||||||
this.updateState(state);
|
this.refreshState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState(state) {
|
refreshState(state) {
|
||||||
this.totalHeight = state.totalHeight;
|
this.totalHeight = state.totalHeight;
|
||||||
this.totalWidth = state.totalWidth;
|
this.totalWidth = state.totalWidth;
|
||||||
this.zeroLine = state.zeroLine;
|
this.zeroLine = state.zeroLine;
|
||||||
|
|||||||
@ -153,6 +153,35 @@ export function calcIntervals(values, withMinimum=false) {
|
|||||||
return intervals;
|
return intervals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getZeroIndex(yPts) {
|
||||||
|
let zeroIndex;
|
||||||
|
let interval = getIntervalSize(yPts);
|
||||||
|
if(yPts.indexOf(0) >= 0) {
|
||||||
|
// the range has a given zero
|
||||||
|
// zero-line on the chart
|
||||||
|
zeroIndex = yPts.indexOf(0);
|
||||||
|
} else if(yPts[0] > 0) {
|
||||||
|
// Minimum value is positive
|
||||||
|
// zero-line is off the chart: below
|
||||||
|
let min = yPts[0];
|
||||||
|
zeroIndex = (-1) * min / interval;
|
||||||
|
} else {
|
||||||
|
// Maximum value is negative
|
||||||
|
// zero-line is off the chart: above
|
||||||
|
let max = yPts[yPts.length - 1];
|
||||||
|
zeroIndex = (-1) * max / interval + (yPts.length - 1);
|
||||||
|
}
|
||||||
|
return zeroIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIntervalSize(orderedArray) {
|
||||||
|
return orderedArray[1] - orderedArray[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getValueRange(orderedArray) {
|
||||||
|
return orderedArray[orderedArray.length-1] - orderedArray[0];
|
||||||
|
}
|
||||||
|
|
||||||
export function calcDistribution(values, distributionSize) {
|
export function calcDistribution(values, distributionSize) {
|
||||||
// Assume non-negative values,
|
// Assume non-negative values,
|
||||||
// implying distribution minimum at zero
|
// implying distribution minimum at zero
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user