[major] regions, markers, tooltips, unit control
This commit is contained in:
parent
5e705db263
commit
55c8b6861f
900
dist/frappe-charts.esm.js
vendored
900
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
@ -51,7 +51,7 @@ let bar_composite_chart = new Chart ({
|
||||
height: 180,
|
||||
colors: ['orange'],
|
||||
isNavigable: 1,
|
||||
is_series: 1
|
||||
isSeries: 1
|
||||
// regionFill: 1
|
||||
});
|
||||
|
||||
@ -59,9 +59,12 @@ let line_composite_chart = new Chart ({
|
||||
parent: c2,
|
||||
data: line_composite_data,
|
||||
type: 'line',
|
||||
options: {
|
||||
dotSize: 10
|
||||
},
|
||||
height: 180,
|
||||
colors: ['green'],
|
||||
is_series: 1
|
||||
isSeries: 1
|
||||
});
|
||||
|
||||
bar_composite_chart.parent.addEventListener('data-select', (e) => {
|
||||
@ -75,16 +78,48 @@ let type_data = {
|
||||
labels: ["12am-3am", "3am-6am", "6am-9am", "9am-12pm",
|
||||
"12pm-3pm", "3pm-6pm", "6pm-9pm", "9pm-12am"],
|
||||
|
||||
yMarkers: [
|
||||
{
|
||||
name: "Marker 1",
|
||||
value: 42,
|
||||
type: 'dashed'
|
||||
},
|
||||
{
|
||||
name: "Marker 2",
|
||||
value: 25,
|
||||
type: 'dashed'
|
||||
}
|
||||
],
|
||||
|
||||
yRegions: [
|
||||
{
|
||||
name: "Region Y 1",
|
||||
start: 10,
|
||||
end: 50
|
||||
},
|
||||
],
|
||||
|
||||
// will depend on series code for calculating X values
|
||||
// xRegions: [
|
||||
// {
|
||||
// name: "Region X 2",
|
||||
// start: ,
|
||||
// end: ,
|
||||
// }
|
||||
// ],
|
||||
|
||||
datasets: [
|
||||
{
|
||||
name: "Some Data",
|
||||
values: [18, 40, 30, 35, 8, 52, 17, -4],
|
||||
axisPosition: 'right'
|
||||
axisPosition: 'right',
|
||||
chartType: 'bar'
|
||||
},
|
||||
{
|
||||
name: "Another Set",
|
||||
values: [30, 50, -10, 15, 18, 32, 27, 14],
|
||||
axisPosition: 'right'
|
||||
axisPosition: 'right',
|
||||
chartType: 'line'
|
||||
},
|
||||
// {
|
||||
// name: "Yet Another",
|
||||
@ -111,12 +146,14 @@ let type_chart = new Chart({
|
||||
parent: "#chart-types",
|
||||
// title: "My Awesome Chart",
|
||||
data: type_data,
|
||||
type: 'multiaxis',
|
||||
type: 'line',
|
||||
height: 250,
|
||||
colors: ['purple', 'magenta'],
|
||||
is_series: 1,
|
||||
format_tooltip_x: d => (d + '').toUpperCase(),
|
||||
format_tooltip_y: d => d + ' pts'
|
||||
isSeries: 1,
|
||||
xAxisMode: 'tick',
|
||||
yAxisMode: 'span',
|
||||
// formatTooltipX: d => (d + '').toUpperCase(),
|
||||
// formatTooltipY: d => d + ' pts'
|
||||
});
|
||||
|
||||
Array.prototype.slice.call(
|
||||
@ -164,7 +201,7 @@ let plot_chart_args = {
|
||||
type: 'line',
|
||||
height: 250,
|
||||
colors: ['blue'],
|
||||
is_series: 1,
|
||||
isSeries: 1,
|
||||
showDots: 0,
|
||||
heatline: 1,
|
||||
xAxisMode: 'tick',
|
||||
@ -241,7 +278,7 @@ let update_chart = new Chart({
|
||||
type: 'line',
|
||||
height: 250,
|
||||
colors: ['red'],
|
||||
is_series: 1,
|
||||
isSeries: 1,
|
||||
regionFill: 1
|
||||
});
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
<div class="container">
|
||||
<div class="row hero" style="padding-top: 30px; padding-bottom: 0px;">
|
||||
<div class="jumbotron" style="background: transparent;">
|
||||
<h1>Frappé Charts</h1>
|
||||
<h1>Frappe Charts</h1>
|
||||
<p class="mt-2">GitHub-inspired simple and modern charts for the web</p>
|
||||
<p class="mt-2">with zero dependencies.</p>
|
||||
<!--<p class="mt-2">Because dumb charts are hard to come by.</p>-->
|
||||
@ -162,7 +162,7 @@
|
||||
xAxisMode: 'tick', // for short label ticks
|
||||
// or 'span' for long spanning vertical axis lines
|
||||
yAxisMode: 'span', // for long horizontal lines, or 'tick'
|
||||
is_series: 1, // to allow for skipping of X values
|
||||
isSeries: 1, // to allow for skipping of X values
|
||||
...</code></pre>
|
||||
<div id="chart-trends" class="border"></div>
|
||||
<div class="btn-group chart-plot-buttons mt-1 mx-auto" role="group">
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import '../scss/charts.scss';
|
||||
|
||||
import BarChart from './charts/BarChart';
|
||||
import LineChart from './charts/LineChart';
|
||||
import ScatterChart from './charts/ScatterChart';
|
||||
import MultiAxisChart from './charts/MultiAxisChart';
|
||||
import PercentageChart from './charts/PercentageChart';
|
||||
import PieChart from './charts/PieChart';
|
||||
import Heatmap from './charts/Heatmap';
|
||||
import AxisChart from './charts/AxisChart';
|
||||
|
||||
// if (ENV !== 'production') {
|
||||
// // Enable LiveReload
|
||||
@ -16,9 +15,13 @@ import Heatmap from './charts/Heatmap';
|
||||
// );
|
||||
// }
|
||||
|
||||
// If type is bar
|
||||
|
||||
|
||||
|
||||
|
||||
const chartTypes = {
|
||||
line: LineChart,
|
||||
bar: BarChart,
|
||||
mixed: AxisChart,
|
||||
multiaxis: MultiAxisChart,
|
||||
scatter: ScatterChart,
|
||||
percentage: PercentageChart,
|
||||
@ -27,8 +30,17 @@ const chartTypes = {
|
||||
};
|
||||
|
||||
function getChartByType(chartType = 'line', options) {
|
||||
if(chartType === 'line') {
|
||||
options.unitType = 'line';
|
||||
return new AxisChart(options);
|
||||
} else if (chartType === 'bar') {
|
||||
options.unitType = 'bar';
|
||||
return new AxisChart(options);
|
||||
}
|
||||
|
||||
if (!chartTypes[chartType]) {
|
||||
return new LineChart(options);
|
||||
console.error("Undefined chart type: " + chartType);
|
||||
return;
|
||||
}
|
||||
|
||||
return new chartTypes[chartType](options);
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import BaseChart from './BaseChart';
|
||||
import { Y_AXIS_MARGIN } from '../utils/margins';
|
||||
import { ChartComponent } from '../objects/ChartComponent';
|
||||
import { BarChartController, LineChartController, getPaths } from '../objects/AxisChartControllers';
|
||||
import { getOffset, fire } from '../utils/dom';
|
||||
import { AxisChartRenderer, makePath, makeGradient } from '../utils/draw';
|
||||
import { AxisChartRenderer } from '../utils/draw';
|
||||
import { equilizeNoOfElements } from '../utils/draw-utils';
|
||||
import { Animator } from '../utils/animate';
|
||||
import { runSMILAnimation } from '../utils/animation';
|
||||
@ -12,11 +13,33 @@ import { floatTwo, fillArray } from '../utils/helpers';
|
||||
export default class AxisChart extends BaseChart {
|
||||
constructor(args) {
|
||||
super(args);
|
||||
this.is_series = args.is_series;
|
||||
this.format_tooltip_y = args.format_tooltip_y;
|
||||
this.format_tooltip_x = args.format_tooltip_x;
|
||||
this.isSeries = args.isSeries;
|
||||
this.formatTooltipY = args.formatTooltipY;
|
||||
this.formatTooltipX = args.formatTooltipX;
|
||||
this.unitType = args.unitType || 'line';
|
||||
|
||||
this.setupUnitRenderer();
|
||||
|
||||
this.zeroLine = this.height;
|
||||
this.preSetup();
|
||||
this.setup();
|
||||
}
|
||||
|
||||
configure(args) {
|
||||
super.configure();
|
||||
|
||||
this.config.xAxisMode = args.xAxisMode;
|
||||
this.config.yAxisMode = args.yAxisMode;
|
||||
}
|
||||
|
||||
preSetup() {}
|
||||
|
||||
setupUnitRenderer() {
|
||||
let options = this.rawChartArgs.options;
|
||||
this.unitRenderers = {
|
||||
bar: new BarChartController(options),
|
||||
line: new LineChartController(options)
|
||||
};
|
||||
}
|
||||
|
||||
setHorizontalMargin() {
|
||||
@ -36,8 +59,16 @@ export default class AxisChart extends BaseChart {
|
||||
this.state = {
|
||||
xAxisLabels: [],
|
||||
xAxisPositions: [],
|
||||
xAxisMode: this.config.xAxisMode,
|
||||
yAxisMode: this.config.yAxisMode
|
||||
}
|
||||
|
||||
this.data.datasets.map(d => {
|
||||
if(!d.chartType ) {
|
||||
d.chartType = this.unitType;
|
||||
}
|
||||
});
|
||||
|
||||
this.prepareYAxis();
|
||||
}
|
||||
|
||||
@ -78,6 +109,8 @@ export default class AxisChart extends BaseChart {
|
||||
});
|
||||
|
||||
s.noOfDatasets = s.datasets.length;
|
||||
s.yMarkers = data.yMarkers;
|
||||
s.yRegions = data.yRegions;
|
||||
}
|
||||
|
||||
prepareYAxis() {
|
||||
@ -97,6 +130,7 @@ export default class AxisChart extends BaseChart {
|
||||
this.setYAxis();
|
||||
this.calcYUnits();
|
||||
this.calcYMaximums();
|
||||
this.calcYRegions();
|
||||
|
||||
// should be state
|
||||
this.configUnits();
|
||||
@ -111,7 +145,8 @@ export default class AxisChart extends BaseChart {
|
||||
let s = this.state;
|
||||
this.setUnitWidthAndXOffset();
|
||||
s.xAxisPositions = s.xAxisLabels.map((d, i) =>
|
||||
floatTwo(s.xOffset + i * s.unitWidth));
|
||||
floatTwo(s.xOffset + i * s.unitWidth)
|
||||
);
|
||||
|
||||
s.xUnitPositions = new Array(s.noOfDatasets).fill(s.xAxisPositions);
|
||||
}
|
||||
@ -150,8 +185,26 @@ export default class AxisChart extends BaseChart {
|
||||
// this.make_tooltip();
|
||||
}
|
||||
|
||||
calcYRegions() {
|
||||
let s = this.state;
|
||||
if(s.yMarkers) {
|
||||
s.yMarkers = s.yMarkers.map(d => {
|
||||
d.value = floatTwo(s.yAxis.zeroLine - d.value * s.yAxis.scaleMultiplier);
|
||||
return d;
|
||||
});
|
||||
}
|
||||
if(s.yRegions) {
|
||||
s.yRegions = s.yRegions.map(d => {
|
||||
d.start = floatTwo(s.yAxis.zeroLine - d.start * s.yAxis.scaleMultiplier);
|
||||
d.end = floatTwo(s.yAxis.zeroLine - d.end * s.yAxis.scaleMultiplier);
|
||||
return d;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
configUnits() {}
|
||||
|
||||
// Default, as per bar, and mixed. Only line will be a special case
|
||||
setUnitWidthAndXOffset() {
|
||||
this.state.unitWidth = this.width/(this.state.datasetLength);
|
||||
this.state.xOffset = this.state.unitWidth/2;
|
||||
@ -180,11 +233,11 @@ export default class AxisChart extends BaseChart {
|
||||
// this.yAxesAux,
|
||||
...this.getYAxesComponents(),
|
||||
this.getXAxisComponents(),
|
||||
// this.getYMarkerLines(),
|
||||
// this.getXMarkerLines(),
|
||||
// TODO: regions too?
|
||||
...this.getPathComponents(),
|
||||
...this.getDataUnitsComponents(this.config),
|
||||
...this.getYRegions(),
|
||||
...this.getXRegions(),
|
||||
...this.getYMarkerLines(),
|
||||
// ...this.getXMarkerLines(),
|
||||
...this.getChartComponents(),
|
||||
];
|
||||
}
|
||||
|
||||
@ -233,7 +286,9 @@ export default class AxisChart extends BaseChart {
|
||||
let s = this.state;
|
||||
// TODO: xAxis Label spacing
|
||||
return s.xAxisPositions.map((position, i) =>
|
||||
this.renderer.xLine(position, s.xAxisLabels[i], {pos:'top'})
|
||||
this.renderer.xLine(position, s.xAxisLabels[i]
|
||||
// , {pos:'top'}
|
||||
)
|
||||
);
|
||||
},
|
||||
animate: (xLines) => {
|
||||
@ -262,68 +317,156 @@ export default class AxisChart extends BaseChart {
|
||||
});
|
||||
}
|
||||
|
||||
getDataUnitsComponents() {
|
||||
return this.data.datasets.map((d, index) => {
|
||||
return new ChartComponent({
|
||||
layerClass: 'dataset-units dataset-' + index,
|
||||
make: () => {
|
||||
let d = this.state.datasets[index];
|
||||
let unitType = this.unitArgs;
|
||||
getChartComponents() {
|
||||
let dataUnitsComponents = []
|
||||
// this.state is not defined at this stage
|
||||
this.data.datasets.forEach((d, index) => {
|
||||
if(d.chartType === 'line') {
|
||||
dataUnitsComponents.push(this.getPathComponent(d, index));
|
||||
}
|
||||
console.log(this.unitRenderers[d.chartType], d.chartType);
|
||||
dataUnitsComponents.push(this.getDataUnitComponent(
|
||||
d, index, this.unitRenderers[d.chartType]
|
||||
));
|
||||
});
|
||||
return dataUnitsComponents;
|
||||
}
|
||||
|
||||
return d.positions.map((y, j) => {
|
||||
return this.renderer[unitType.type](
|
||||
this.state.xAxisPositions[j],
|
||||
y,
|
||||
unitType.args,
|
||||
this.colors[index],
|
||||
j,
|
||||
index,
|
||||
this.state.noOfDatasets
|
||||
);
|
||||
});
|
||||
},
|
||||
animate: (svgUnits) => {
|
||||
let unitType = this.unitArgs.type;
|
||||
getDataUnitComponent(d, index, unitRenderer) {
|
||||
return new ChartComponent({
|
||||
layerClass: 'dataset-units dataset-' + index,
|
||||
make: () => {
|
||||
let d = this.state.datasets[index];
|
||||
|
||||
// have been updated in axis render;
|
||||
let newX = this.state.xAxisPositions;
|
||||
let newY = this.state.datasets[index].positions;
|
||||
return d.positions.map((y, j) => {
|
||||
return unitRenderer.draw(
|
||||
this.state.xAxisPositions[j],
|
||||
y,
|
||||
this.colors[index],
|
||||
j,
|
||||
index,
|
||||
this.state.noOfDatasets
|
||||
);
|
||||
});
|
||||
},
|
||||
animate: (svgUnits) => {
|
||||
// have been updated in axis render;
|
||||
let newX = this.state.xAxisPositions;
|
||||
let newY = this.state.datasets[index].positions;
|
||||
|
||||
let lastUnit = svgUnits[svgUnits.length - 1];
|
||||
let parentNode = lastUnit.parentNode;
|
||||
let lastUnit = svgUnits[svgUnits.length - 1];
|
||||
let parentNode = lastUnit.parentNode;
|
||||
|
||||
if(this.oldState.xExtra > 0) {
|
||||
for(var i = 0; i<this.oldState.xExtra; i++) {
|
||||
let unit = lastUnit.cloneNode(true);
|
||||
parentNode.appendChild(unit);
|
||||
svgUnits.push(unit);
|
||||
}
|
||||
if(this.oldState.xExtra > 0) {
|
||||
for(var i = 0; i<this.oldState.xExtra; i++) {
|
||||
let unit = lastUnit.cloneNode(true);
|
||||
parentNode.appendChild(unit);
|
||||
svgUnits.push(unit);
|
||||
}
|
||||
|
||||
svgUnits.map((unit, i) => {
|
||||
if(newX[i] === undefined || newY[i] === undefined) return;
|
||||
this.elementsToAnimate.push(this.renderer['animate' + unitType](
|
||||
unit, // unit, with info to replace where it came from in the data
|
||||
newX[i],
|
||||
newY[i],
|
||||
index,
|
||||
this.state.noOfDatasets
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
svgUnits.map((unit, i) => {
|
||||
if(newX[i] === undefined || newY[i] === undefined) return;
|
||||
this.elementsToAnimate.push(unitRenderer.animate(
|
||||
unit, // unit, with info to replace where it came from in the data
|
||||
newX[i],
|
||||
newY[i],
|
||||
index,
|
||||
this.state.noOfDatasets
|
||||
));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getPathComponent(d, index) {
|
||||
return new ChartComponent({
|
||||
layerClass: 'path dataset-path',
|
||||
make: () => {
|
||||
let d = this.state.datasets[index];
|
||||
let color = this.colors[index];
|
||||
|
||||
return getPaths(
|
||||
d.positions,
|
||||
this.state.xAxisPositions,
|
||||
color,
|
||||
this.config.heatline,
|
||||
this.config.regionFill
|
||||
);
|
||||
},
|
||||
animate: (paths) => {
|
||||
let newX = this.state.xAxisPositions;
|
||||
let newY = this.state.datasets[index].positions;
|
||||
|
||||
let oldX = this.oldState.xAxisPositions;
|
||||
let oldY = this.oldState.datasets[index].positions;
|
||||
|
||||
|
||||
let parentNode = paths[0].parentNode;
|
||||
|
||||
[oldX, newX] = equilizeNoOfElements(oldX, newX);
|
||||
[oldY, newY] = equilizeNoOfElements(oldY, newY);
|
||||
|
||||
if(this.oldState.xExtra > 0) {
|
||||
paths = getPaths(
|
||||
oldY, oldX, this.colors[index],
|
||||
this.config.heatline,
|
||||
this.config.regionFill
|
||||
);
|
||||
parentNode.textContent = '';
|
||||
paths.map(path => parentNode.appendChild(path));
|
||||
}
|
||||
|
||||
const newPointsList = newY.map((y, i) => (newX[i] + ',' + y));
|
||||
this.elementsToAnimate = this.elementsToAnimate
|
||||
.concat(this.renderer.animatepath(paths, newPointsList.join("L")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getYMarkerLines() {
|
||||
if(!this.data.yMarkers) {
|
||||
return [];
|
||||
}
|
||||
return this.data.yMarkers.map((d, index) => {
|
||||
return new ChartComponent({
|
||||
layerClass: 'y-markers',
|
||||
make: () => {
|
||||
let s = this.state;
|
||||
return s.yMarkers.map(marker =>
|
||||
this.renderer.yMarker(marker.value, marker.name,
|
||||
{pos:'right', mode: 'span', lineType: marker.type})
|
||||
);
|
||||
},
|
||||
animate: () => {}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPathComponents() {
|
||||
return [];
|
||||
// getXMarkerLines() {
|
||||
// return [];
|
||||
// }
|
||||
|
||||
getYRegions() {
|
||||
if(!this.data.yRegions) {
|
||||
return [];
|
||||
}
|
||||
// return [];
|
||||
return this.data.yRegions.map((d, index) => {
|
||||
return new ChartComponent({
|
||||
layerClass: 'y-regions',
|
||||
make: () => {
|
||||
let s = this.state;
|
||||
return s.yRegions.map(region =>
|
||||
this.renderer.yRegion(region.start, region.end, region.name)
|
||||
);
|
||||
},
|
||||
animate: () => {}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getYMarkerLines() {
|
||||
return [];
|
||||
}
|
||||
|
||||
getXMarkerLines() {
|
||||
getXRegions() {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -345,6 +488,88 @@ export default class AxisChart extends BaseChart {
|
||||
} else {
|
||||
this.renderer.refreshState(state);
|
||||
}
|
||||
|
||||
let meta = {
|
||||
totalHeight: this.height,
|
||||
totalWidth: this.width,
|
||||
zeroLine: this.state.zeroLine,
|
||||
unitWidth: this.state.unitWidth,
|
||||
};
|
||||
|
||||
Object.keys(this.unitRenderers).map(key => {
|
||||
this.unitRenderers[key].refreshMeta(meta);
|
||||
});
|
||||
}
|
||||
|
||||
bindTooltip() {
|
||||
// TODO: could be in tooltip itself, as it is a given functionality for its parent
|
||||
this.chartWrapper.addEventListener('mousemove', (e) => {
|
||||
let o = getOffset(this.chartWrapper);
|
||||
let relX = e.pageX - o.left - this.translateXLeft;
|
||||
let relY = e.pageY - o.top - this.translateY;
|
||||
|
||||
if(relY < this.height + this.translateY * 2) {
|
||||
this.mapTooltipXPosition(relX);
|
||||
} else {
|
||||
this.tip.hide_tip();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mapTooltipXPosition(relX) {
|
||||
let s = this.state;
|
||||
if(!s.yUnitMinimums) return;
|
||||
|
||||
let titles = s.xAxisLabels;
|
||||
if(this.formatTooltipX && this.formatTooltipX(titles[0])) {
|
||||
titles = titles.map(d=>this.formatTooltipX(d));
|
||||
}
|
||||
|
||||
let formatY = this.formatTooltipY && this.formatTooltipY(this.y[0].values[0]);
|
||||
|
||||
for(var i=s.datasetLength - 1; i >= 0 ; i--) {
|
||||
let xVal = s.xAxisPositions[i];
|
||||
// let delta = i === 0 ? s.unitWidth : xVal - s.xAxisPositions[i-1];
|
||||
if(relX > xVal - s.unitWidth/2) {
|
||||
let x = xVal + this.translateXLeft;
|
||||
let y = s.yUnitMinimums[i] + this.translateY;
|
||||
|
||||
let values = s.datasets.map((set, j) => {
|
||||
return {
|
||||
title: set.title,
|
||||
value: formatY ? this.formatTooltipY(set.values[i]) : set.values[i],
|
||||
color: this.colors[j],
|
||||
};
|
||||
});
|
||||
|
||||
this.tip.set_values(x, y, titles[i], '', values);
|
||||
this.tip.show_tip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getDataPoint(index=this.current_index) {
|
||||
// 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.current_index) return;
|
||||
this.current_index = index;
|
||||
$.fire(this.parent, "data-select", this.getDataPoint());
|
||||
}
|
||||
|
||||
// API
|
||||
@ -374,3 +599,7 @@ export default class AxisChart extends BaseChart {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// keep a binding at the end of chart
|
||||
|
||||
|
||||
@ -1,83 +0,0 @@
|
||||
import AxisChart from './AxisChart';
|
||||
|
||||
export default class BarChart extends AxisChart {
|
||||
constructor(args) {
|
||||
super(args);
|
||||
this.type = 'bar';
|
||||
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);
|
||||
// this.state.xOffset = this.state.unitWidth/2;
|
||||
// }
|
||||
|
||||
configUnits() {
|
||||
this.unitArgs = {
|
||||
type: 'bar',
|
||||
args: {
|
||||
spaceWidth: this.state.unitWidth/2,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// makeOverlay() {
|
||||
// // Just make one out of the first element
|
||||
// let index = this.xAxisLabels.length - 1;
|
||||
// let unit = this.y[0].svg_units[index];
|
||||
// this.updateCurrentDataPoint(index);
|
||||
|
||||
// if(this.overlay) {
|
||||
// this.overlay.parentNode.removeChild(this.overlay);
|
||||
// }
|
||||
// this.overlay = unit.cloneNode();
|
||||
// this.overlay.style.fill = '#000000';
|
||||
// this.overlay.style.opacity = '0.4';
|
||||
// this.drawArea.appendChild(this.overlay);
|
||||
// }
|
||||
|
||||
// bindOverlay() {
|
||||
// // on event, update overlay
|
||||
// this.parent.addEventListener('data-select', (e) => {
|
||||
// this.update_overlay(e.svg_unit);
|
||||
// });
|
||||
// }
|
||||
|
||||
// bind_units(units_array) {
|
||||
// units_array.map(unit => {
|
||||
// unit.addEventListener('click', () => {
|
||||
// let index = unit.getAttribute('data-point-index');
|
||||
// this.updateCurrentDataPoint(index);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// update_overlay(unit) {
|
||||
// let attributes = [];
|
||||
// Object.keys(unit.attributes).map(index => {
|
||||
// attributes.push(unit.attributes[index]);
|
||||
// });
|
||||
|
||||
// attributes.filter(attr => attr.specified).map(attr => {
|
||||
// this.overlay.setAttribute(attr.name, attr.nodeValue);
|
||||
// });
|
||||
|
||||
// this.overlay.style.fill = '#000000';
|
||||
// this.overlay.style.opacity = '0.4';
|
||||
// }
|
||||
|
||||
// onLeftArrow() {
|
||||
// this.updateCurrentDataPoint(this.currentIndex - 1);
|
||||
// }
|
||||
|
||||
// onRightArrow() {
|
||||
// this.updateCurrentDataPoint(this.currentIndex + 1);
|
||||
// }
|
||||
|
||||
}
|
||||
@ -40,85 +40,4 @@ export default class LineChart extends AxisChart {
|
||||
this.state.xOffset = 0;
|
||||
}
|
||||
|
||||
getDataUnitsComponents(config) {
|
||||
if(!config.showDots) {
|
||||
return [];
|
||||
}
|
||||
else {
|
||||
return super.getDataUnitsComponents();
|
||||
}
|
||||
}
|
||||
|
||||
getPathComponents() {
|
||||
return this.data.datasets.map((d, index) => {
|
||||
return new ChartComponent({
|
||||
layerClass: 'path dataset-path',
|
||||
make: () => {
|
||||
let d = this.state.datasets[index];
|
||||
let color = this.colors[index];
|
||||
|
||||
return this.getPaths(
|
||||
d.positions,
|
||||
this.state.xAxisPositions,
|
||||
color,
|
||||
this.config.heatline,
|
||||
this.config.regionFill
|
||||
);
|
||||
},
|
||||
animate: (paths) => {
|
||||
let newX = this.state.xAxisPositions;
|
||||
let newY = this.state.datasets[index].positions;
|
||||
|
||||
let oldX = this.oldState.xAxisPositions;
|
||||
let oldY = this.oldState.datasets[index].positions;
|
||||
|
||||
|
||||
let parentNode = paths[0].parentNode;
|
||||
|
||||
[oldX, newX] = equilizeNoOfElements(oldX, newX);
|
||||
[oldY, newY] = equilizeNoOfElements(oldY, newY);
|
||||
|
||||
if(this.oldState.xExtra > 0) {
|
||||
paths = this.getPaths(
|
||||
oldY, oldX, this.colors[index],
|
||||
this.config.heatline,
|
||||
this.config.regionFill
|
||||
);
|
||||
parentNode.textContent = '';
|
||||
paths.map(path => parentNode.appendChild(path));
|
||||
}
|
||||
|
||||
const newPointsList = newY.map((y, i) => (newX[i] + ',' + y));
|
||||
this.elementsToAnimate = this.elementsToAnimate
|
||||
.concat(this.renderer.animatepath(paths, newPointsList.join("L")));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getPaths(yList, xList, color, heatline=false, regionFill=false) {
|
||||
let pointsList = yList.map((y, i) => (xList[i] + ',' + y));
|
||||
let pointsStr = pointsList.join("L");
|
||||
let path = makePath("M"+pointsStr, 'line-graph-path', color);
|
||||
|
||||
// HeatLine
|
||||
if(heatline) {
|
||||
let gradient_id = makeGradient(this.svgDefs, color);
|
||||
path.style.stroke = `url(#${gradient_id})`;
|
||||
}
|
||||
|
||||
let components = [path];
|
||||
|
||||
// Region
|
||||
if(regionFill) {
|
||||
let gradient_id_region = makeGradient(this.svgDefs, color, true);
|
||||
|
||||
let zeroLine = this.state.yAxis.zeroLine;
|
||||
// TODO: use zeroLine OR minimum
|
||||
let pathStr = "M" + `0,${zeroLine}L` + pointsStr + `L${this.width},${zeroLine}`;
|
||||
components.push(makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id_region})`));
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,9 +6,12 @@ import { floatTwo } from '../utils/helpers';
|
||||
export default class MultiAxisChart extends AxisChart {
|
||||
constructor(args) {
|
||||
super(args);
|
||||
// this.unitType = args.unitType || 'line';
|
||||
// this.setup();
|
||||
}
|
||||
|
||||
preSetup() {
|
||||
this.type = 'multiaxis';
|
||||
this.unitType = args.unitType || 'line';
|
||||
this.setup();
|
||||
}
|
||||
|
||||
setHorizontalMargin() {
|
||||
@ -107,7 +110,7 @@ export default class MultiAxisChart extends AxisChart {
|
||||
}
|
||||
|
||||
// TODO remove renderer zeroline from above and below
|
||||
getDataUnitsComponents() {
|
||||
getChartComponents() {
|
||||
return this.data.datasets.map((d, index) => {
|
||||
return new ChartComponent({
|
||||
layerClass: 'dataset-units dataset-' + index,
|
||||
|
||||
215
src/js/objects/AxisChartControllers.js
Normal file
215
src/js/objects/AxisChartControllers.js
Normal file
@ -0,0 +1,215 @@
|
||||
import { getBarHeightAndYAttr } from '../utils/draw-utils';
|
||||
import { createSVG, makePath, makeGradient } from '../utils/draw';
|
||||
import { STD_EASING, UNIT_ANIM_DUR, MARKER_LINE_ANIM_DUR, PATH_ANIM_DUR } from '../utils/animate';
|
||||
|
||||
class AxisChartController {
|
||||
constructor(meta) {
|
||||
// TODO: make configurable passing args
|
||||
this.refreshMeta(meta);
|
||||
this.setupArgs();
|
||||
}
|
||||
|
||||
setupArgs() {}
|
||||
|
||||
refreshMeta(meta) {
|
||||
this.meta = meta || {};
|
||||
}
|
||||
|
||||
draw() {}
|
||||
animate() {}
|
||||
}
|
||||
|
||||
export class AxisController extends AxisChartController {
|
||||
constructor(meta) {
|
||||
super(meta);
|
||||
}
|
||||
|
||||
setupArgs() {}
|
||||
|
||||
draw(x, y, color, index) {
|
||||
return createSVG('circle', {
|
||||
style: `fill: ${color}`,
|
||||
'data-point-index': index,
|
||||
cx: x,
|
||||
cy: y,
|
||||
r: this.args.radius
|
||||
});
|
||||
}
|
||||
|
||||
animate(dot, x, yTop) {
|
||||
return [dot, {cx: x, cy: yTop}, UNIT_ANIM_DUR, STD_EASING];
|
||||
// dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein);
|
||||
}
|
||||
}
|
||||
|
||||
export class BarChartController extends AxisChartController {
|
||||
constructor(meta) {
|
||||
super(meta);
|
||||
}
|
||||
|
||||
setupArgs() {
|
||||
this.args = {
|
||||
spaceRatio: 0.5,
|
||||
};
|
||||
}
|
||||
|
||||
draw(x, yTop, color, index, datasetIndex, noOfDatasets) {
|
||||
let totalWidth = this.meta.unitWidth - this.meta.unitWidth * this.args.spaceRatio;
|
||||
let startX = x - totalWidth/2;
|
||||
|
||||
// temp commented
|
||||
// let width = totalWidth / noOfDatasets;
|
||||
// let currentX = startX + width * datasetIndex;
|
||||
|
||||
// temp
|
||||
let width = totalWidth;
|
||||
let currentX = startX;
|
||||
|
||||
let [height, y] = getBarHeightAndYAttr(yTop, this.meta.zeroLine, this.meta.totalHeight);
|
||||
|
||||
return createSVG('rect', {
|
||||
className: `bar mini`,
|
||||
style: `fill: ${color}`,
|
||||
'data-point-index': index,
|
||||
x: currentX,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
}
|
||||
|
||||
animate(bar, x, yTop, index, noOfDatasets) {
|
||||
let start = x - this.meta.avgUnitWidth/4;
|
||||
let width = (this.meta.avgUnitWidth/2)/noOfDatasets;
|
||||
let [height, y] = getBarHeightAndYAttr(yTop, this.meta.zeroLine, this.meta.totalHeight);
|
||||
|
||||
x = start + (width * index);
|
||||
|
||||
return [bar, {width: width, height: height, x: x, y: y}, UNIT_ANIM_DUR, STD_EASING];
|
||||
// bar.animate({height: args.newHeight, y: yTop}, UNIT_ANIM_DUR, mina.easein);
|
||||
}
|
||||
}
|
||||
|
||||
export class LineChartController extends AxisChartController {
|
||||
constructor(meta) {
|
||||
super(meta);
|
||||
}
|
||||
|
||||
setupArgs() {
|
||||
console.log(this);
|
||||
this.args = {
|
||||
radius: this.meta.dotSize || 4
|
||||
};
|
||||
}
|
||||
|
||||
draw(x, y, color, index) {
|
||||
return createSVG('circle', {
|
||||
style: `fill: ${color}`,
|
||||
'data-point-index': index,
|
||||
cx: x,
|
||||
cy: y,
|
||||
r: this.args.radius
|
||||
});
|
||||
}
|
||||
|
||||
animate(dot, x, yTop) {
|
||||
return [dot, {cx: x, cy: yTop}, UNIT_ANIM_DUR, STD_EASING];
|
||||
// dot.animate({cy: yTop}, UNIT_ANIM_DUR, mina.easein);
|
||||
}
|
||||
}
|
||||
|
||||
export function getPaths(yList, xList, color, heatline=false, regionFill=false) {
|
||||
let pointsList = yList.map((y, i) => (xList[i] + ',' + y));
|
||||
let pointsStr = pointsList.join("L");
|
||||
let path = makePath("M"+pointsStr, 'line-graph-path', color);
|
||||
|
||||
// HeatLine
|
||||
if(heatline) {
|
||||
let gradient_id = makeGradient(this.svgDefs, color);
|
||||
path.style.stroke = `url(#${gradient_id})`;
|
||||
}
|
||||
|
||||
let components = [path];
|
||||
|
||||
// Region
|
||||
if(regionFill) {
|
||||
let gradient_id_region = makeGradient(this.svgDefs, color, true);
|
||||
|
||||
let zeroLine = this.state.yAxis.zeroLine;
|
||||
// TODO: use zeroLine OR minimum
|
||||
let pathStr = "M" + `0,${zeroLine}L` + pointsStr + `L${this.width},${zeroLine}`;
|
||||
components.push(makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id_region})`));
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
// class BarChart extends AxisChart {
|
||||
// constructor(args) {
|
||||
// super(args);
|
||||
// this.type = 'bar';
|
||||
// this.setup();
|
||||
// }
|
||||
|
||||
// configure(args) {
|
||||
// super.configure(args);
|
||||
// this.config.xAxisMode = args.xAxisMode || 'tick';
|
||||
// this.config.yAxisMode = args.yAxisMode || 'span';
|
||||
// }
|
||||
|
||||
// // =================================
|
||||
|
||||
// makeOverlay() {
|
||||
// // Just make one out of the first element
|
||||
// let index = this.xAxisLabels.length - 1;
|
||||
// let unit = this.y[0].svg_units[index];
|
||||
// this.updateCurrentDataPoint(index);
|
||||
|
||||
// if(this.overlay) {
|
||||
// this.overlay.parentNode.removeChild(this.overlay);
|
||||
// }
|
||||
// this.overlay = unit.cloneNode();
|
||||
// this.overlay.style.fill = '#000000';
|
||||
// this.overlay.style.opacity = '0.4';
|
||||
// this.drawArea.appendChild(this.overlay);
|
||||
// }
|
||||
|
||||
// bindOverlay() {
|
||||
// // on event, update overlay
|
||||
// this.parent.addEventListener('data-select', (e) => {
|
||||
// this.update_overlay(e.svg_unit);
|
||||
// });
|
||||
// }
|
||||
|
||||
// bind_units(units_array) {
|
||||
// units_array.map(unit => {
|
||||
// unit.addEventListener('click', () => {
|
||||
// let index = unit.getAttribute('data-point-index');
|
||||
// this.updateCurrentDataPoint(index);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// update_overlay(unit) {
|
||||
// let attributes = [];
|
||||
// Object.keys(unit.attributes).map(index => {
|
||||
// attributes.push(unit.attributes[index]);
|
||||
// });
|
||||
|
||||
// attributes.filter(attr => attr.specified).map(attr => {
|
||||
// this.overlay.setAttribute(attr.name, attr.nodeValue);
|
||||
// });
|
||||
|
||||
// this.overlay.style.fill = '#000000';
|
||||
// this.overlay.style.opacity = '0.4';
|
||||
// }
|
||||
|
||||
// onLeftArrow() {
|
||||
// this.updateCurrentDataPoint(this.currentIndex - 1);
|
||||
// }
|
||||
|
||||
// onRightArrow() {
|
||||
// this.updateCurrentDataPoint(this.currentIndex + 1);
|
||||
// }
|
||||
// }
|
||||
|
||||
@ -44,7 +44,7 @@ export function equilizeNoOfElements(array1, array2,
|
||||
// return values.map((value, i) => {
|
||||
// let space_taken = getStringWidth(value, char_width) + 2;
|
||||
// if(space_taken > allowed_space) {
|
||||
// if(is_series) {
|
||||
// if(isSeries) {
|
||||
// // Skip some axis lines if X axis is a series
|
||||
// let skips = 1;
|
||||
// while((space_taken/skips)*2 > allowed_space) {
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
import { getBarHeightAndYAttr } from './draw-utils';
|
||||
import { getStringWidth } from './helpers';
|
||||
import { STD_EASING, UNIT_ANIM_DUR, MARKER_LINE_ANIM_DUR, PATH_ANIM_DUR } from './animate';
|
||||
|
||||
const AXIS_TICK_LENGTH = 6;
|
||||
const LABEL_MARGIN = 4;
|
||||
const FONT_SIZE = 10;
|
||||
const BASE_LINE_COLOR = '#dadada';
|
||||
const BASE_BG_COLOR = '#F7FAFC';
|
||||
|
||||
function $(expr, con) {
|
||||
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
|
||||
}
|
||||
|
||||
function createSVG(tag, o) {
|
||||
export function createSVG(tag, o) {
|
||||
var element = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
||||
|
||||
for (var i in o) {
|
||||
@ -119,7 +121,7 @@ export function makeHeatSquare(className, x, y, size, fill='none', data={}) {
|
||||
y: y,
|
||||
width: size,
|
||||
height: size,
|
||||
fill: fill
|
||||
fill: 1
|
||||
};
|
||||
|
||||
Object.keys(data).map(key => {
|
||||
@ -140,7 +142,7 @@ export function makeText(className, x, y, content) {
|
||||
});
|
||||
}
|
||||
|
||||
export function makeVertLine(x, label, y1, y2, options={}) {
|
||||
function makeVertLine(x, label, y1, y2, options={}) {
|
||||
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||
let l = createSVG('line', {
|
||||
className: 'line-vertical ' + options.className,
|
||||
@ -172,7 +174,7 @@ export function makeVertLine(x, label, y1, y2, options={}) {
|
||||
return line;
|
||||
}
|
||||
|
||||
export function makeHoriLine(y, label, x1, x2, options={}) {
|
||||
function makeHoriLine(y, label, x1, x2, options={}) {
|
||||
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||
if(!options.lineType) options.lineType = '';
|
||||
let className = 'line-horizontal ' + options.className +
|
||||
@ -182,8 +184,8 @@ export function makeHoriLine(y, label, x1, x2, options={}) {
|
||||
className: className,
|
||||
x1: x1,
|
||||
x2: x2,
|
||||
y1: 0,
|
||||
y2: 0,
|
||||
y1: y,
|
||||
y2: y,
|
||||
styles: {
|
||||
stroke: options.stroke
|
||||
}
|
||||
@ -191,7 +193,7 @@ export function makeHoriLine(y, label, x1, x2, options={}) {
|
||||
|
||||
let text = createSVG('text', {
|
||||
x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN,
|
||||
y: 0,
|
||||
y: y,
|
||||
dy: (FONT_SIZE / 2 - 2) + 'px',
|
||||
'font-size': FONT_SIZE + 'px',
|
||||
'text-anchor': x1 < x2 ? 'end' : 'start',
|
||||
@ -199,7 +201,6 @@ export function makeHoriLine(y, label, x1, x2, options={}) {
|
||||
});
|
||||
|
||||
let line = createSVG('g', {
|
||||
transform: `translate(0, ${ y })`,
|
||||
'stroke-opacity': 1
|
||||
});
|
||||
|
||||
@ -231,46 +232,11 @@ export class AxisChartRenderer {
|
||||
this.zeroLine = zeroLine;
|
||||
}
|
||||
|
||||
bar(x, yTop, args, color, index, datasetIndex, noOfDatasets) {
|
||||
|
||||
let totalWidth = this.unitWidth - args.spaceWidth;
|
||||
let startX = x - totalWidth/2;
|
||||
|
||||
// temp commented
|
||||
// let width = totalWidth / noOfDatasets;
|
||||
// let currentX = startX + width * datasetIndex;
|
||||
|
||||
// temp
|
||||
let width = totalWidth;
|
||||
let currentX = startX;
|
||||
|
||||
let [height, y] = getBarHeightAndYAttr(yTop, this.zeroLine, this.totalHeight);
|
||||
|
||||
return createSVG('rect', {
|
||||
className: `bar mini`,
|
||||
style: `fill: ${color}`,
|
||||
'data-point-index': index,
|
||||
x: currentX,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height
|
||||
});
|
||||
}
|
||||
|
||||
dot(x, y, args, color, index) {
|
||||
return createSVG('circle', {
|
||||
style: `fill: ${color}`,
|
||||
'data-point-index': index,
|
||||
cx: x,
|
||||
cy: y,
|
||||
r: args.radius
|
||||
});
|
||||
}
|
||||
|
||||
xLine(x, label, options={}) {
|
||||
if(!options.pos) options.pos = 'bottom';
|
||||
if(!options.offset) options.offset = 0;
|
||||
if(!options.mode) options.mode = this.xAxisMode;
|
||||
console.log(this.xAxisMode);
|
||||
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||
if(!options.className) options.className = '';
|
||||
|
||||
@ -296,7 +262,8 @@ export class AxisChartRenderer {
|
||||
|
||||
return makeVertLine(x, label, y1, y2, {
|
||||
stroke: options.stroke,
|
||||
className: options.className
|
||||
className: options.className,
|
||||
lineType: options.lineType
|
||||
});
|
||||
}
|
||||
|
||||
@ -322,16 +289,102 @@ export class AxisChartRenderer {
|
||||
|
||||
return makeHoriLine(y, label, x1, x2, {
|
||||
stroke: options.stroke,
|
||||
className: options.className
|
||||
className: options.className,
|
||||
lineType: options.lineType
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
xMarker() {}
|
||||
yMarker() {}
|
||||
yMarker(y, label, options={}) {
|
||||
let labelSvg = createSVG('text', {
|
||||
className: 'chart-label',
|
||||
x: this.totalWidth - getStringWidth(label, 5) - LABEL_MARGIN,
|
||||
y: y - FONT_SIZE - 2,
|
||||
dy: (FONT_SIZE / 2) + 'px',
|
||||
'font-size': FONT_SIZE + 'px',
|
||||
'text-anchor': 'start',
|
||||
innerHTML: label+""
|
||||
});
|
||||
|
||||
xRegion() {}
|
||||
yRegion() {}
|
||||
let line = makeHoriLine(y, '', 0, this.totalWidth, {
|
||||
stroke: options.stroke || BASE_LINE_COLOR,
|
||||
className: options.className || '',
|
||||
lineType: options.lineType
|
||||
});
|
||||
|
||||
line.appendChild(labelSvg);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
xRegion() {
|
||||
return createSVG('rect', {
|
||||
className: `bar mini`, // remove class
|
||||
style: `fill: rgba(228, 234, 239, 0.49)`,
|
||||
// 'data-point-index': index,
|
||||
x: 0,
|
||||
y: y2,
|
||||
width: this.totalWidth,
|
||||
height: y1 - y2
|
||||
});
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
yRegion(y1, y2, label) {
|
||||
// return a group
|
||||
|
||||
let rect = createSVG('rect', {
|
||||
className: `bar mini`, // remove class
|
||||
style: `fill: rgba(228, 234, 239, 0.49)`,
|
||||
// 'data-point-index': index,
|
||||
x: 0,
|
||||
y: y2,
|
||||
width: this.totalWidth,
|
||||
height: y1 - y2
|
||||
});
|
||||
|
||||
let upperBorder = createSVG('line', {
|
||||
className: 'line-horizontal',
|
||||
x1: 0,
|
||||
x2: this.totalWidth,
|
||||
y1: y2,
|
||||
y2: y2,
|
||||
styles: {
|
||||
stroke: BASE_LINE_COLOR
|
||||
}
|
||||
});
|
||||
let lowerBorder = createSVG('line', {
|
||||
className: 'line-horizontal',
|
||||
x1: 0,
|
||||
x2: this.totalWidth,
|
||||
y1: y1,
|
||||
y2: y1,
|
||||
styles: {
|
||||
stroke: BASE_LINE_COLOR
|
||||
}
|
||||
});
|
||||
|
||||
let labelSvg = createSVG('text', {
|
||||
className: 'chart-label',
|
||||
x: this.totalWidth - getStringWidth(label, 4.5) - LABEL_MARGIN,
|
||||
y: y2 - FONT_SIZE - 2,
|
||||
dy: (FONT_SIZE / 2) + 'px',
|
||||
'font-size': FONT_SIZE + 'px',
|
||||
'text-anchor': 'start',
|
||||
innerHTML: label+""
|
||||
});
|
||||
|
||||
let region = createSVG('g', {});
|
||||
|
||||
region.appendChild(rect);
|
||||
region.appendChild(upperBorder);
|
||||
region.appendChild(lowerBorder);
|
||||
region.appendChild(labelSvg);
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
animatebar(bar, x, yTop, index, noOfDatasets) {
|
||||
let start = x - this.avgUnitWidth/4;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user