Przeglądaj źródła

[start] state and data separation

tags/1.2.0
Prateeksha Singh 7 lat temu
rodzic
commit
f41c501c3a
17 zmienionych plików z 377 dodań i 421 usunięć
  1. +134
    -157
      dist/frappe-charts.esm.js
  2. +1
    -1
      dist/frappe-charts.min.cjs.js
  3. +1
    -1
      dist/frappe-charts.min.esm.js
  4. +1
    -1
      dist/frappe-charts.min.iife.js
  5. +1
    -1
      dist/frappe-charts.min.iife.js.map
  6. +1
    -1
      docs/assets/js/frappe-charts.min.js
  7. +1
    -1
      docs/assets/js/frappe-charts.min.js.map
  8. +94
    -94
      docs/assets/js/index.js
  9. +47
    -93
      src/js/charts/AxisChart.js
  10. +35
    -64
      src/js/charts/BaseChart.js
  11. +2
    -1
      src/js/charts/Heatmap.js
  12. +4
    -2
      src/js/charts/MultiAxisChart.js
  13. +49
    -0
      src/js/charts/axis-chart-utils.js
  14. +0
    -2
      src/js/objects/AxisChartControllers.js
  15. +4
    -1
      src/js/utils/constants.js
  16. +2
    -0
      src/js/utils/helpers.js
  17. +0
    -1
      src/js/utils/margins.js

+ 134
- 157
dist/frappe-charts.esm.js Wyświetl plik

@@ -878,27 +878,29 @@ class BaseChart {
this.currentIndex = 0;
}

this.data = this.prepareData(data);
this.colors = [];
this.config = {};
this.state = {};
this.options = {};

this.configure(arguments[0]);
}

configure(args) {
// Make a this.config, that has stuff like showTooltip,
// showLegend, which then all functions will check

this.setColors();

// constants
this.config = {
showTooltip: 1, // calculate
showLegend: 1,
isNavigable: 0,
// animate: 1
animate: 0
animate: 1
};

this.state = {
colors: this.colors
};
this.setMargins();

// Bind window events
window.addEventListener('resize', () => this.draw());
window.addEventListener('orientationchange', () => this.draw());
}

setColors() {
@@ -925,10 +927,7 @@ class BaseChart {
this.height = height - 40; // change
this.translateY = 20;

this.setHorizontalMargin();
}

setHorizontalMargin() {
// Horizontal margins
this.translateXLeft = 60;
this.translateXRight = 40;
}
@@ -938,18 +937,6 @@ class BaseChart {
console.error("No parent element to render on was provided.");
return false;
}
if(!this.parseData()) {
return false;
}
return true;
}

parseData() {
let data = this.rawChartArgs.data;
let valid = this.checkData(data);
if(!valid) return false;

this.data = data;
return true;
}

@@ -960,29 +947,15 @@ class BaseChart {
}

_setup() {
this.bindWindowEvents();
this.setupConstants();

this.setMargins();
this.makeContainer();
this.makeTooltip(); // without binding

this.calcWidth();
this.makeChartArea();
this.initComponents();

this.setupComponents();
this.draw(true);
}

bindWindowEvents() {
window.addEventListener('resize orientationchange', () => this.draw());
}

setupConstants() {}
initComponents() {}

setupComponents() {
// Components config
this.components = [];
}

@@ -1014,13 +987,22 @@ class BaseChart {
bindTooltip() {}

draw(init=false) {
this.calcWidth();
this.makeChartArea();

this.initComponents(); // Only depend on the drawArea made in makeChartArea

this.setupComponents();

this.components.forEach(c => c.make()); // or c.build()
this.renderLegend();

this.setupNavigation(init);

// TODO: remove timeout and decrease post animate time in chart component
setTimeout(() => {this.update();}, 1000);
if(init) {
setTimeout(() => {this.update();}, 1000);
}
}

calcWidth() {
@@ -1037,15 +1019,13 @@ class BaseChart {
}

update(data=this.data) {
this.prepareData(data);
this.data = this.prepareData(data);
this.calc(); // builds state
this.render();
}

prepareData() {}

renderConstants() {}

calc() {} // builds state

render(animate=true) {
@@ -1063,6 +1043,9 @@ class BaseChart {
}

makeChartArea() {
if(this.svg) {
this.chartWrapper.removeChild(this.svg);
}
this.svg = makeSVGContainer(
this.chartWrapper,
'chart',
@@ -1125,37 +1108,25 @@ class BaseChart {
onDownArrow() {}
onEnterKey() {}

// updateData() {
// update();
// }

getDataPoint() {}
setCurrentDataPoint() {}


// ????????????
// Update the data here, then do relevant updates
// and drawing in child classes by overriding
// The Child chart will only know what a particular update means
// and what components are affected,
// BaseChart shouldn't be doing the animating

updateDataset(dataset, index) {}

updateDatasets(datasets) {
//
}
getDataPoint(index = 0) {}
setCurrentDataPoint(point) {}

updateDataset(dataset, index) {}
addDataset(dataset, index) {}

removeDataset(index = 0) {}

addDataPoint(dataPoint, index = 0) {}

removeDataPoint(index = 0) {}
updateDatasets(datasets) {}

updateDataPoint(dataPoint, index = 0) {}
addDataPoint(dataPoint, index = 0) {}
removeDataPoint(index = 0) {}

getDifferentChart(type) {
return getDifferentChart(type, this.type, this.rawChartArgs);
@@ -1164,6 +1135,56 @@ class BaseChart {

const Y_AXIS_MARGIN = 60;


const DEFAULT_AXIS_CHART_TYPE = 'line';

function dataPrep(data, type) {
data.labels = data.labels || [];

let datasetLength = data.labels.length;

// Datasets
let datasets = data.datasets;
let zeroArray = new Array(datasetLength).fill(0);
if(!datasets) {
// default
datasets = [{
values: zeroArray
}];
}

datasets.map((d, i)=> {
// Set values
if(!d.values) {
d.values = zeroArray;
} else {
// Check for non values
let vals = d.values;
vals = vals.map(val => (!isNaN(val) ? val : 0));

// Trim or extend
if(vals.length > datasetLength) {
vals = vals.slice(0, datasetLength);
} else {
vals = fillArray(vals, datasetLength - vals.length, 0);
}
}

// Set index
d.index = i;

// Set type
if(!d.chartType ) {
d.chartType = type || DEFAULT_AXIS_CHART_TYPE;
}
});

// Markers
// Regions

return data;
}

class ChartComponent$1 {
constructor({
layerClass = '',
@@ -1790,7 +1811,7 @@ class AxisChart extends BaseChart {
this.yAxisMode = args.yAxisMode || 'span';

this.zeroLine = this.height;
this.setPrimitiveData();
this.setTrivialState();
this.setup();
}

@@ -1801,8 +1822,20 @@ class AxisChart extends BaseChart {
this.config.yAxisMode = args.yAxisMode;
}

setPrimitiveData() {
setTrivialState() {
// Define data and stuff
let xLabels = this.data.labels;
this.state = {
xAxis: {
positions: [],
labels: xLabels,
},
yAxis: {
positions: [],
labels: [],
},
datasetLength: xLabels.length
};
this.setObservers();
}

@@ -1811,78 +1844,19 @@ class AxisChart extends BaseChart {
// set an observe() on each of those keys for that component
}

setHorizontalMargin() {
setMargins() {
super.setMargins();
this.translateXLeft = Y_AXIS_MARGIN;
this.translateXRight = Y_AXIS_MARGIN;
}

checkData(data) {
return true;
}

setupConstants() {
this.state = {
xAxisLabels: [],
xAxisPositions: [],
xAxisMode: this.config.xAxisMode,
yAxisMode: this.config.yAxisMode
};

this.data.datasets.map(d => {
if(!d.chartType ) {
d.chartType = this.type;
}
});

// Prepare Y Axis
this.state.yAxis = {
labels: [],
positions: []
};
}

prepareData(data) {
let s = this.state;
s.xAxisLabels = data.labels || [];
s.datasetLength = s.xAxisLabels.length;

let zeroArray = new Array(s.datasetLength).fill(0);
s.datasets = data.datasets; // whole dataset info too
if(!data.datasets) {
// default
s.datasets = [{
values: zeroArray // Proof that state version will be seen instead of this.data
}];
}

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;
});

s.noOfDatasets = s.datasets.length;
s.yMarkers = data.yMarkers;
s.yRegions = data.yRegions;
prepareData(data=this.data) {
return dataPrep(data, this.type);
}

calc() {
let s = this.state;

s.xAxisLabels = this.data.labels;
this.calcXPositions();

s.datasetsLabels = this.data.datasets.map(d => d.name);
@@ -1901,11 +1875,11 @@ class AxisChart extends BaseChart {
calcXPositions() {
let s = this.state;
this.setUnitWidthAndXOffset();
s.xAxisPositions = s.xAxisLabels.map((d, i) =>
s.xAxisPositions = s.xAxis.labels.map((d, i) =>
floatTwo(s.xOffset + i * s.unitWidth)
);

s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
s.xUnitPositions = new Array(this.data.datasets.length).fill(s.xAxisPositions);
}

calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') {
@@ -1921,13 +1895,13 @@ class AxisChart extends BaseChart {

calcYUnits() {
let s = this.state;
s.datasets.map(d => {
this.data.datasets.map(d => {
d.positions = d.values.map(val =>
floatTwo(s.yAxis.zeroLine - val * s.yAxis.scaleMultiplier));
});

if(this.barOptions && this.barOptions.stacked) {
s.datasets.map((d, i) => {
this.data.datasets.map((d, i) => {
d.cumulativePositions = d.cumulativeYs.map(val =>
floatTwo(s.yAxis.zeroLine - val * s.yAxis.scaleMultiplier));
});
@@ -1937,11 +1911,11 @@ class AxisChart extends BaseChart {
calcYMaximums() {
let s = this.state;
if(this.barOptions && this.barOptions.stacked) {
s.yExtremes = s.datasets[s.datasets.length - 1].cumulativePositions;
s.yExtremes = this.data.datasets[this.data.datasets.length - 1].cumulativePositions;
return;
}
s.yExtremes = new Array(s.datasetLength).fill(9999);
s.datasets.map((d, i) => {
this.data.datasets.map((d, i) => {
d.positions.map((pos, j) => {
if(pos < s.yExtremes[j]) {
s.yExtremes[j] = pos;
@@ -1956,15 +1930,15 @@ class AxisChart extends BaseChart {

calcYRegions() {
let s = this.state;
if(s.yMarkers) {
s.yMarkers = s.yMarkers.map(d => {
if(this.data.yMarkers) {
this.data.yMarkers = this.data.yMarkers.map(d => {
d.position = floatTwo(s.yAxis.zeroLine - d.value * s.yAxis.scaleMultiplier);
d.label += ': ' + d.value;
return d;
});
}
if(s.yRegions) {
s.yRegions = s.yRegions.map(d => {
if(this.data.yRegions) {
this.data.yRegions = this.data.yRegions.map(d => {
if(d.end < d.start) {
[d.start, d.end] = [d.end, start];
}
@@ -1989,13 +1963,13 @@ class AxisChart extends BaseChart {
if(this.barOptions && this.barOptions.stacked) {
key = 'cumulativeYs';
let cumulative = new Array(this.state.datasetLength).fill(0);
this.state.datasets.map((d, i) => {
let values = this.state.datasets[i].values;
this.data.datasets.map((d, i) => {
let values = this.data.datasets[i].values;
d[key] = cumulative = cumulative.map((c, i) => c + values[i]);
});
}

return [].concat(...this.state.datasets.map(d => d[key]));
return [].concat(...this.data.datasets.map(d => d[key]));
}

initComponents() {
@@ -2037,7 +2011,7 @@ class AxisChart extends BaseChart {
let s = this.state;
return {
positions: s.xAxisPositions,
labels: s.xAxisLabels,
labels: s.xAxis.labels,
}
}.bind(this)
],
@@ -2058,7 +2032,7 @@ class AxisChart extends BaseChart {
}
],
function() {
return this.state.yRegions || [];
return this.data.yRegions || [];
}.bind(this)
],

@@ -2077,7 +2051,7 @@ class AxisChart extends BaseChart {
}
],
function() {
return this.state.yMarkers || [];
return this.data.yMarkers || [];
}.bind(this)
]
];
@@ -2110,7 +2084,7 @@ class AxisChart extends BaseChart {
layerClass: 'dataset-units dataset-' + index,
makeElements: () => {
// yPositions, xPostions, color, valuesOverPoints,
let d = this.state.datasets[index];
let d = this.data.datasets[index];

return d.positions.map((y, j) => {
return unitRenderer.draw(
@@ -2129,7 +2103,7 @@ class AxisChart extends BaseChart {
this.layer.setAttribute('transform', `translate(${unitRenderer.consts.width * index}, 0)`);
};

// let d = this.state.datasets[index];
// let d = this.data.datasets[index];

if(this.meta.type === 'bar' && (!this.meta.barOptions
|| !this.meta.barOptions.stacked)) {
@@ -2140,7 +2114,7 @@ class AxisChart extends BaseChart {
animate: (svgUnits) => {
// have been updated in axis render;
let newX = this.state.xAxisPositions;
let newY = this.state.datasets[index].positions;
let newY = this.data.datasets[index].positions;

let lastUnit = svgUnits[svgUnits.length - 1];
let parentNode = lastUnit.parentNode;
@@ -2160,7 +2134,7 @@ class AxisChart extends BaseChart {
newX[i],
newY[i],
index,
this.state.noOfDatasets
this.data.datasets.length
));
});
}
@@ -2172,7 +2146,7 @@ class AxisChart extends BaseChart {
layerClass: 'path dataset-path',
setData: () => {},
makeElements: () => {
let d = this.state.datasets[index];
let d = this.data.datasets[index];
let color = this.colors[index];

return getPaths(
@@ -2185,7 +2159,7 @@ class AxisChart extends BaseChart {
},
animate: (paths) => {
let newX = this.state.xAxisPositions;
let newY = this.state.datasets[index].positions;
let newY = this.data.datasets[index].positions;

let oldX = this.oldState.xAxisPositions;
let oldY = this.oldState.datasets[index].positions;
@@ -2232,7 +2206,7 @@ class AxisChart extends BaseChart {
let s = this.state;
if(!s.yExtremes) return;

let titles = s.xAxisLabels;
let titles = s.xAxis.labels;
if(this.formatTooltipX && this.formatTooltipX(titles[0])) {
titles = titles.map(d=>this.formatTooltipX(d));
}
@@ -2246,7 +2220,7 @@ class AxisChart extends BaseChart {
let x = xVal + this.translateXLeft;
let y = s.yExtremes[i] + this.translateY;

let values = s.datasets.map((set, j) => {
let values = this.data.datasets.map((set, j) => {
return {
title: set.title,
value: formatY ? this.formatTooltipY(set.values[i]) : set.values[i],
@@ -2271,14 +2245,14 @@ class AxisChart extends BaseChart {
let data_key = key.slice(0, key.length-1);
data_point[data_key] = y[key][index];
});
data_point.label = this.xAxisLabels[index];
data_point.label = this.xAxis.labels[index];
return data_point;
}

setCurrentDataPoint(index) {
index = parseInt(index);
if(index < 0) index = 0;
if(index >= this.xAxisLabels.length) index = this.xAxisLabels.length - 1;
if(index >= this.xAxis.labels.length) index = this.xAxis.labels.length - 1;
if(index === this.current_index) return;
this.current_index = index;
$.fire(this.parent, "data-select", this.getDataPoint());
@@ -2394,7 +2368,8 @@ class MultiAxisChart extends AxisChart {
this.type = 'multiaxis';
}

setHorizontalMargin() {
setMargins() {
super.setMargins();
let noOfLeftAxes = this.data.datasets.filter(d => d.axisPosition === 'left').length;
this.translateXLeft = (noOfLeftAxes) * Y_AXIS_MARGIN || Y_AXIS_MARGIN;
this.translateXRight = (this.data.datasets.length - noOfLeftAxes) * Y_AXIS_MARGIN || Y_AXIS_MARGIN;
@@ -2452,6 +2427,7 @@ class MultiAxisChart extends AxisChart {
});
}

// TODO: function doesn't exist, handle with components
renderConstants() {
this.state.datasets.map(d => {
let guidePos = d.yAxis.position === 'left'
@@ -2949,7 +2925,8 @@ class Heatmap extends BaseChart {
return valid;
}

setupConstants() {
configure() {
super.configure();
this.today = new Date();

if(!this.start) {


+ 1
- 1
dist/frappe-charts.min.cjs.js
Plik diff jest za duży
Wyświetl plik


+ 1
- 1
dist/frappe-charts.min.esm.js
Plik diff jest za duży
Wyświetl plik


+ 1
- 1
dist/frappe-charts.min.iife.js
Plik diff jest za duży
Wyświetl plik


+ 1
- 1
dist/frappe-charts.min.iife.js.map
Plik diff jest za duży
Wyświetl plik


+ 1
- 1
docs/assets/js/frappe-charts.min.js
Plik diff jest za duży
Wyświetl plik


+ 1
- 1
docs/assets/js/frappe-charts.min.js.map
Plik diff jest za duży
Wyświetl plik


+ 94
- 94
docs/assets/js/index.js Wyświetl plik

@@ -476,100 +476,100 @@ document.querySelector('[data-aggregation="average"]').addEventListener("click",
// Heatmap
// ================================================================================

let heatmap_data = {};
let current_date = new Date();
let timestamp = current_date.getTime()/1000;
timestamp = Math.floor(timestamp - (timestamp % 86400)).toFixed(1); // convert to midnight
for (var i = 0; i< 375; i++) {
heatmap_data[parseInt(timestamp)] = Math.floor(Math.random() * 5);
timestamp = Math.floor(timestamp - 86400).toFixed(1);
}
new Chart({
parent: "#chart-heatmap",
data: heatmap_data,
type: 'heatmap',
legend_scale: [0, 1, 2, 4, 5],
height: 115,
discrete_domains: 1 // default 0
});
Array.prototype.slice.call(
document.querySelectorAll('.heatmap-mode-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let mode = btn.getAttribute('data-mode');
let discrete_domains = 0;
if(mode === 'discrete') {
discrete_domains = 1;
}
let colors = [];
let colors_mode = document
.querySelector('.heatmap-color-buttons .active')
.getAttribute('data-color');
if(colors_mode === 'halloween') {
colors = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
}
new Chart({
parent: "#chart-heatmap",
data: heatmap_data,
type: 'heatmap',
legend_scale: [0, 1, 2, 4, 5],
height: 115,
discrete_domains: discrete_domains,
legend_colors: colors
});
Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});
Array.prototype.slice.call(
document.querySelectorAll('.heatmap-color-buttons button')
).map(el => {
el.addEventListener('click', (e) => {
let btn = e.target;
let colors_mode = btn.getAttribute('data-color');
let colors = [];
if(colors_mode === 'halloween') {
colors = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
}
let discrete_domains = 1;
let view_mode = document
.querySelector('.heatmap-mode-buttons .active')
.getAttribute('data-mode');
if(view_mode === 'continuous') {
discrete_domains = 0;
}
new Chart({
parent: "#chart-heatmap",
data: heatmap_data,
type: 'heatmap',
legend_scale: [0, 1, 2, 4, 5],
height: 115,
discrete_domains: discrete_domains,
legend_colors: colors
});
Array.prototype.slice.call(
btn.parentNode.querySelectorAll('button')).map(el => {
el.classList.remove('active');
});
btn.classList.add('active');
});
});
// let heatmap_data = {};
// let current_date = new Date();
// let timestamp = current_date.getTime()/1000;
// timestamp = Math.floor(timestamp - (timestamp % 86400)).toFixed(1); // convert to midnight
// for (var i = 0; i< 375; i++) {
// heatmap_data[parseInt(timestamp)] = Math.floor(Math.random() * 5);
// timestamp = Math.floor(timestamp - 86400).toFixed(1);
// }
// new Chart({
// parent: "#chart-heatmap",
// data: heatmap_data,
// type: 'heatmap',
// legend_scale: [0, 1, 2, 4, 5],
// height: 115,
// discrete_domains: 1 // default 0
// });
// Array.prototype.slice.call(
// document.querySelectorAll('.heatmap-mode-buttons button')
// ).map(el => {
// el.addEventListener('click', (e) => {
// let btn = e.target;
// let mode = btn.getAttribute('data-mode');
// let discrete_domains = 0;
// if(mode === 'discrete') {
// discrete_domains = 1;
// }
// let colors = [];
// let colors_mode = document
// .querySelector('.heatmap-color-buttons .active')
// .getAttribute('data-color');
// if(colors_mode === 'halloween') {
// colors = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
// }
// new Chart({
// parent: "#chart-heatmap",
// data: heatmap_data,
// type: 'heatmap',
// legend_scale: [0, 1, 2, 4, 5],
// height: 115,
// discrete_domains: discrete_domains,
// legend_colors: colors
// });
// Array.prototype.slice.call(
// btn.parentNode.querySelectorAll('button')).map(el => {
// el.classList.remove('active');
// });
// btn.classList.add('active');
// });
// });
// Array.prototype.slice.call(
// document.querySelectorAll('.heatmap-color-buttons button')
// ).map(el => {
// el.addEventListener('click', (e) => {
// let btn = e.target;
// let colors_mode = btn.getAttribute('data-color');
// let colors = [];
// if(colors_mode === 'halloween') {
// colors = ['#ebedf0', '#fdf436', '#ffc700', '#ff9100', '#06001c'];
// }
// let discrete_domains = 1;
// let view_mode = document
// .querySelector('.heatmap-mode-buttons .active')
// .getAttribute('data-mode');
// if(view_mode === 'continuous') {
// discrete_domains = 0;
// }
// new Chart({
// parent: "#chart-heatmap",
// data: heatmap_data,
// type: 'heatmap',
// legend_scale: [0, 1, 2, 4, 5],
// height: 115,
// discrete_domains: discrete_domains,
// legend_colors: colors
// });
// Array.prototype.slice.call(
// btn.parentNode.querySelectorAll('button')).map(el => {
// el.classList.remove('active');
// });
// btn.classList.add('active');
// });
// });

// Helpers
// ================================================================================


+ 47
- 93
src/js/charts/AxisChart.js Wyświetl plik

@@ -1,5 +1,6 @@
import BaseChart from './BaseChart';
import { Y_AXIS_MARGIN } from '../utils/margins';
import { dataPrep } from './axis-chart-utils';
import { Y_AXIS_MARGIN } from '../utils/constants';
import { getComponent } from '../objects/ChartComponents';
import { BarChartController, LineChartController, getPaths } from '../objects/AxisChartControllers';
import { AxisChartRenderer } from '../utils/draw';
@@ -9,7 +10,7 @@ import { Animator, translateHoriLine } from '../utils/animate';
import { runSMILAnimation } from '../utils/animation';
import { getRealIntervals, calcChartIntervals, getIntervalSize, getValueRange, getZeroIndex } from '../utils/intervals';
import { floatTwo, fillArray } from '../utils/helpers';
import { MIN_BAR_PERCENT_HEIGHT } from '../utils/constants';
import { MIN_BAR_PERCENT_HEIGHT, DEFAULT_AXIS_CHART_TYPE } from '../utils/constants';

export default class AxisChart extends BaseChart {
constructor(args) {
@@ -26,7 +27,7 @@ export default class AxisChart extends BaseChart {
this.yAxisMode = args.yAxisMode || 'span';

this.zeroLine = this.height;
this.setPrimitiveData();
this.setTrivialState();
this.setup();
}

@@ -37,8 +38,20 @@ export default class AxisChart extends BaseChart {
this.config.yAxisMode = args.yAxisMode;
}

setPrimitiveData() {
setTrivialState() {
// Define data and stuff
let xLabels = this.data.labels;
this.state = {
xAxis: {
positions: [],
labels: xLabels,
},
yAxis: {
positions: [],
labels: [],
},
datasetLength: xLabels.length
}
this.setObservers();
}

@@ -47,78 +60,19 @@ export default class AxisChart extends BaseChart {
// set an observe() on each of those keys for that component
}

setHorizontalMargin() {
setMargins() {
super.setMargins();
this.translateXLeft = Y_AXIS_MARGIN;
this.translateXRight = Y_AXIS_MARGIN;
}

checkData(data) {
return true;
}

setupConstants() {
this.state = {
xAxisLabels: [],
xAxisPositions: [],
xAxisMode: this.config.xAxisMode,
yAxisMode: this.config.yAxisMode
}

this.data.datasets.map(d => {
if(!d.chartType ) {
d.chartType = this.type;
}
});

// Prepare Y Axis
this.state.yAxis = {
labels: [],
positions: []
};
}

prepareData(data) {
let s = this.state;
s.xAxisLabels = data.labels || [];
s.datasetLength = s.xAxisLabels.length;

let zeroArray = new Array(s.datasetLength).fill(0);
s.datasets = data.datasets; // whole dataset info too
if(!data.datasets) {
// default
s.datasets = [{
values: zeroArray // Proof that state version will be seen instead of this.data
}];
}

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;
});

s.noOfDatasets = s.datasets.length;
s.yMarkers = data.yMarkers;
s.yRegions = data.yRegions;
prepareData(data=this.data) {
return dataPrep(data, this.type);
}

calc() {
let s = this.state;

s.xAxisLabels = this.data.labels;
this.calcXPositions();

s.datasetsLabels = this.data.datasets.map(d => d.name);
@@ -137,11 +91,11 @@ export default class AxisChart extends BaseChart {
calcXPositions() {
let s = this.state;
this.setUnitWidthAndXOffset();
s.xAxisPositions = s.xAxisLabels.map((d, i) =>
s.xAxisPositions = s.xAxis.labels.map((d, i) =>
floatTwo(s.xOffset + i * s.unitWidth)
);

s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
s.xUnitPositions = new Array(this.data.datasets.length).fill(s.xAxisPositions);
}

calcYAxisParameters(yAxis, dataValues, withMinimum = 'false') {
@@ -157,13 +111,13 @@ export default class AxisChart extends BaseChart {

calcYUnits() {
let s = this.state;
s.datasets.map(d => {
this.data.datasets.map(d => {
d.positions = d.values.map(val =>
floatTwo(s.yAxis.zeroLine - val * s.yAxis.scaleMultiplier));
});

if(this.barOptions && this.barOptions.stacked) {
s.datasets.map((d, i) => {
this.data.datasets.map((d, i) => {
d.cumulativePositions = d.cumulativeYs.map(val =>
floatTwo(s.yAxis.zeroLine - val * s.yAxis.scaleMultiplier));
});
@@ -173,11 +127,11 @@ export default class AxisChart extends BaseChart {
calcYMaximums() {
let s = this.state;
if(this.barOptions && this.barOptions.stacked) {
s.yExtremes = s.datasets[s.datasets.length - 1].cumulativePositions;
s.yExtremes = this.data.datasets[this.data.datasets.length - 1].cumulativePositions;
return;
}
s.yExtremes = new Array(s.datasetLength).fill(9999);
s.datasets.map((d, i) => {
this.data.datasets.map((d, i) => {
d.positions.map((pos, j) => {
if(pos < s.yExtremes[j]) {
s.yExtremes[j] = pos;
@@ -192,15 +146,15 @@ export default class AxisChart extends BaseChart {

calcYRegions() {
let s = this.state;
if(s.yMarkers) {
s.yMarkers = s.yMarkers.map(d => {
if(this.data.yMarkers) {
this.data.yMarkers = this.data.yMarkers.map(d => {
d.position = floatTwo(s.yAxis.zeroLine - d.value * s.yAxis.scaleMultiplier);
d.label += ': ' + d.value;
return d;
});
}
if(s.yRegions) {
s.yRegions = s.yRegions.map(d => {
if(this.data.yRegions) {
this.data.yRegions = this.data.yRegions.map(d => {
if(d.end < d.start) {
[d.start, d.end] = [d.end, start];
}
@@ -225,13 +179,13 @@ export default class AxisChart extends BaseChart {
if(this.barOptions && this.barOptions.stacked) {
key = 'cumulativeYs';
let cumulative = new Array(this.state.datasetLength).fill(0);
this.state.datasets.map((d, i) => {
let values = this.state.datasets[i].values;
this.data.datasets.map((d, i) => {
let values = this.data.datasets[i].values;
d[key] = cumulative = cumulative.map((c, i) => c + values[i]);
});
}

return [].concat(...this.state.datasets.map(d => d[key]));
return [].concat(...this.data.datasets.map(d => d[key]));
}

initComponents() {
@@ -273,7 +227,7 @@ export default class AxisChart extends BaseChart {
let s = this.state;
return {
positions: s.xAxisPositions,
labels: s.xAxisLabels,
labels: s.xAxis.labels,
}
}.bind(this)
],
@@ -294,7 +248,7 @@ export default class AxisChart extends BaseChart {
}
],
function() {
return this.state.yRegions || [];
return this.data.yRegions || [];
}.bind(this)
],

@@ -313,7 +267,7 @@ export default class AxisChart extends BaseChart {
}
],
function() {
return this.state.yMarkers || [];
return this.data.yMarkers || [];
}.bind(this)
]
];
@@ -346,7 +300,7 @@ export default class AxisChart extends BaseChart {
layerClass: 'dataset-units dataset-' + index,
makeElements: () => {
// yPositions, xPostions, color, valuesOverPoints,
let d = this.state.datasets[index];
let d = this.data.datasets[index];

return d.positions.map((y, j) => {
return unitRenderer.draw(
@@ -365,7 +319,7 @@ export default class AxisChart extends BaseChart {
this.layer.setAttribute('transform', `translate(${unitRenderer.consts.width * index}, 0)`);
}

// let d = this.state.datasets[index];
// let d = this.data.datasets[index];

if(this.meta.type === 'bar' && (!this.meta.barOptions
|| !this.meta.barOptions.stacked)) {
@@ -376,7 +330,7 @@ export default class AxisChart extends BaseChart {
animate: (svgUnits) => {
// have been updated in axis render;
let newX = this.state.xAxisPositions;
let newY = this.state.datasets[index].positions;
let newY = this.data.datasets[index].positions;

let lastUnit = svgUnits[svgUnits.length - 1];
let parentNode = lastUnit.parentNode;
@@ -396,7 +350,7 @@ export default class AxisChart extends BaseChart {
newX[i],
newY[i],
index,
this.state.noOfDatasets
this.data.datasets.length
));
});
}
@@ -408,7 +362,7 @@ export default class AxisChart extends BaseChart {
layerClass: 'path dataset-path',
setData: () => {},
makeElements: () => {
let d = this.state.datasets[index];
let d = this.data.datasets[index];
let color = this.colors[index];

return getPaths(
@@ -421,7 +375,7 @@ export default class AxisChart extends BaseChart {
},
animate: (paths) => {
let newX = this.state.xAxisPositions;
let newY = this.state.datasets[index].positions;
let newY = this.data.datasets[index].positions;

let oldX = this.oldState.xAxisPositions;
let oldY = this.oldState.datasets[index].positions;
@@ -468,7 +422,7 @@ export default class AxisChart extends BaseChart {
let s = this.state;
if(!s.yExtremes) return;

let titles = s.xAxisLabels;
let titles = s.xAxis.labels;
if(this.formatTooltipX && this.formatTooltipX(titles[0])) {
titles = titles.map(d=>this.formatTooltipX(d));
}
@@ -482,7 +436,7 @@ export default class AxisChart extends BaseChart {
let x = xVal + this.translateXLeft;
let y = s.yExtremes[i] + this.translateY;

let values = s.datasets.map((set, j) => {
let values = this.data.datasets.map((set, j) => {
return {
title: set.title,
value: formatY ? this.formatTooltipY(set.values[i]) : set.values[i],
@@ -507,14 +461,14 @@ export default class AxisChart extends BaseChart {
let data_key = key.slice(0, key.length-1);
data_point[data_key] = y[key][index];
});
data_point.label = this.xAxisLabels[index];
data_point.label = this.xAxis.labels[index];
return data_point;
}

setCurrentDataPoint(index) {
index = parseInt(index);
if(index < 0) index = 0;
if(index >= this.xAxisLabels.length) index = this.xAxisLabels.length - 1;
if(index >= this.xAxis.labels.length) index = this.xAxis.labels.length - 1;
if(index === this.current_index) return;
this.current_index = index;
$.fire(this.parent, "data-select", this.getDataPoint());


+ 35
- 64
src/js/charts/BaseChart.js Wyświetl plik

@@ -34,27 +34,29 @@ export default class BaseChart {
this.currentIndex = 0;
}

this.data = this.prepareData(data);;
this.colors = [];
this.config = {};
this.state = {};
this.options = {};

this.configure(arguments[0]);
}

configure(args) {
// Make a this.config, that has stuff like showTooltip,
// showLegend, which then all functions will check

this.setColors();

// constants
this.config = {
showTooltip: 1, // calculate
showLegend: 1,
isNavigable: 0,
// animate: 1
animate: 0
animate: 1
};

this.state = {
colors: this.colors
};
this.setMargins();

// Bind window events
window.addEventListener('resize', () => this.draw());
window.addEventListener('orientationchange', () => this.draw());
}

setColors() {
@@ -81,10 +83,7 @@ export default class BaseChart {
this.height = height - 40; // change
this.translateY = 20;

this.setHorizontalMargin();
}

setHorizontalMargin() {
// Horizontal margins
this.translateXLeft = 60;
this.translateXRight = 40;
}
@@ -96,18 +95,6 @@ export default class BaseChart {
console.error("No parent element to render on was provided.");
return false;
}
if(!this.parseData()) {
return false;
}
return true;
}

parseData() {
let data = this.rawChartArgs.data;
let valid = this.checkData(data);
if(!valid) return false;

this.data = data;
return true;
}

@@ -118,29 +105,15 @@ export default class BaseChart {
}

_setup() {
this.bindWindowEvents();
this.setupConstants();

this.setMargins();
this.makeContainer();
this.makeTooltip(); // without binding

this.calcWidth();
this.makeChartArea();
this.initComponents();

this.setupComponents();
this.draw(true);
}

bindWindowEvents() {
window.addEventListener('resize orientationchange', () => this.draw());
}

setupConstants() {}
initComponents() {}

setupComponents() {
// Components config
this.components = [];
}

@@ -172,13 +145,22 @@ export default class BaseChart {
bindTooltip() {}

draw(init=false) {
this.calcWidth();
this.makeChartArea();

this.initComponents(); // Only depend on the drawArea made in makeChartArea

this.setupComponents();

this.components.forEach(c => c.make()); // or c.build()
this.renderLegend();

this.setupNavigation(init);

// TODO: remove timeout and decrease post animate time in chart component
setTimeout(() => {this.update();}, 1000);
if(init) {
setTimeout(() => {this.update();}, 1000);
}
}

calcWidth() {
@@ -195,15 +177,13 @@ export default class BaseChart {
}

update(data=this.data) {
this.prepareData(data);
this.data = this.prepareData(data);
this.calc(); // builds state
this.render();
}

prepareData() {}

renderConstants() {}

calc() {} // builds state

render(animate=true) {
@@ -221,6 +201,9 @@ export default class BaseChart {
}

makeChartArea() {
if(this.svg) {
this.chartWrapper.removeChild(this.svg);
}
this.svg = makeSVGContainer(
this.chartWrapper,
'chart',
@@ -283,37 +266,25 @@ export default class BaseChart {
onDownArrow() {}
onEnterKey() {}

// updateData() {
// update();
// }

getDataPoint() {}
setCurrentDataPoint() {}


// ????????????
// Update the data here, then do relevant updates
// and drawing in child classes by overriding
// The Child chart will only know what a particular update means
// and what components are affected,
// BaseChart shouldn't be doing the animating

updateDataset(dataset, index) {}

updateDatasets(datasets) {
//
}
getDataPoint(index = 0) {}
setCurrentDataPoint(point) {}

updateDataset(dataset, index) {}
addDataset(dataset, index) {}

removeDataset(index = 0) {}

addDataPoint(dataPoint, index = 0) {}

removeDataPoint(index = 0) {}
updateDatasets(datasets) {}

updateDataPoint(dataPoint, index = 0) {}
addDataPoint(dataPoint, index = 0) {}
removeDataPoint(index = 0) {}

getDifferentChart(type) {
return getDifferentChart(type, this.type, this.rawChartArgs);


+ 2
- 1
src/js/charts/Heatmap.js Wyświetl plik

@@ -54,7 +54,8 @@ export default class Heatmap extends BaseChart {
return valid;
}

setupConstants() {
configure() {
super.configure();
this.today = new Date();

if(!this.start) {


+ 4
- 2
src/js/charts/MultiAxisChart.js Wyświetl plik

@@ -1,5 +1,5 @@
import AxisChart from './AxisChart';
import { Y_AXIS_MARGIN } from '../utils/margins';
import { Y_AXIS_MARGIN } from '../utils/constants';
// import { ChartComponent } from '../objects/ChartComponents';
import { floatTwo } from '../utils/helpers';

@@ -14,7 +14,8 @@ export default class MultiAxisChart extends AxisChart {
this.type = 'multiaxis';
}

setHorizontalMargin() {
setMargins() {
super.setMargins();
let noOfLeftAxes = this.data.datasets.filter(d => d.axisPosition === 'left').length;
this.translateXLeft = (noOfLeftAxes) * Y_AXIS_MARGIN || Y_AXIS_MARGIN;
this.translateXRight = (this.data.datasets.length - noOfLeftAxes) * Y_AXIS_MARGIN || Y_AXIS_MARGIN;
@@ -72,6 +73,7 @@ export default class MultiAxisChart extends AxisChart {
});
}

// TODO: function doesn't exist, handle with components
renderConstants() {
this.state.datasets.map(d => {
let guidePos = d.yAxis.position === 'left'


+ 49
- 0
src/js/charts/axis-chart-utils.js Wyświetl plik

@@ -0,0 +1,49 @@
import { floatTwo, fillArray } from '../utils/helpers';
import { DEFAULT_AXIS_CHART_TYPE } from '../utils/constants';

export function dataPrep(data, type) {
data.labels = data.labels || [];

let datasetLength = data.labels.length;

// Datasets
let datasets = data.datasets;
let zeroArray = new Array(datasetLength).fill(0);
if(!datasets) {
// default
datasets = [{
values: zeroArray
}];
}

datasets.map((d, i)=> {
// Set values
if(!d.values) {
d.values = zeroArray;
} else {
// Check for non values
let vals = d.values;
vals = vals.map(val => (!isNaN(val) ? val : 0));

// Trim or extend
if(vals.length > datasetLength) {
vals = vals.slice(0, datasetLength);
} else {
vals = fillArray(vals, datasetLength - vals.length, 0);
}
}

// Set index
d.index = i;

// Set type
if(!d.chartType ) {
d.chartType = type || DEFAULT_AXIS_CHART_TYPE;
}
});

// Markers
// Regions

return data;
}

+ 0
- 2
src/js/objects/AxisChartControllers.js Wyświetl plik

@@ -2,8 +2,6 @@ import { getBarHeightAndYAttr } from '../utils/draw-utils';
import { createSVG, makePath, makeGradient, wrapInSVGGroup, FONT_SIZE } from '../utils/draw';
import { STD_EASING, UNIT_ANIM_DUR, MARKER_LINE_ANIM_DUR, PATH_ANIM_DUR } from '../utils/animate';

const MIN_BAR_PERCENT_HEIGHT = 0.01;

class AxisChartController {
constructor(meta) {
// TODO: make configurable passing args


+ 4
- 1
src/js/utils/constants.js Wyświetl plik

@@ -1 +1,4 @@
export const MIN_BAR_PERCENT_HEIGHT = 0.01;
export const Y_AXIS_MARGIN = 60;

export const MIN_BAR_PERCENT_HEIGHT = 0.01;
export const DEFAULT_AXIS_CHART_TYPE = 'line';

+ 2
- 0
src/js/utils/helpers.js Wyświetl plik

@@ -62,6 +62,8 @@ export function getStringWidth(string, charWidth) {
return (string+"").length * charWidth;
}



function observe(obj, componentNames) {
let components = this.components.get(name);



+ 0
- 1
src/js/utils/margins.js Wyświetl plik

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

Ładowanie…
Anuluj
Zapisz