Feat: Adding multi y-Axis support and configurable labes
This commit is contained in:
parent
10de973608
commit
539bc50883
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
File diff suppressed because one or more lines are too long
@ -31,14 +31,31 @@ export default class AxisChart extends BaseChart {
|
|||||||
|
|
||||||
configure(options) {
|
configure(options) {
|
||||||
super.configure(options);
|
super.configure(options);
|
||||||
|
const { axisOptions = {} } = options;
|
||||||
|
const { xAxis, yAxis } = axisOptions || {};
|
||||||
|
|
||||||
options.axisOptions = options.axisOptions || {};
|
|
||||||
options.tooltipOptions = options.tooltipOptions || {};
|
options.tooltipOptions = options.tooltipOptions || {};
|
||||||
|
|
||||||
this.config.xAxisMode = options.axisOptions.xAxisMode || 'span';
|
this.config.xAxisMode = xAxis ? xAxis.xAxisMode : axisOptions.xAxisMode || 'span';
|
||||||
this.config.yAxisMode = options.axisOptions.yAxisMode || 'span';
|
|
||||||
this.config.xIsSeries = options.axisOptions.xIsSeries || 0;
|
// this will pass an array
|
||||||
this.config.shortenYAxisNumbers = options.axisOptions.shortenYAxisNumbers || 0;
|
// lets determine if we need two yAxis based on if there is length
|
||||||
|
// to the yAxis array
|
||||||
|
if (yAxis && yAxis.length) {
|
||||||
|
this.config.yAxisConfig = yAxis.map((item) => {
|
||||||
|
return {
|
||||||
|
yAxisMode: item.yAxisMode,
|
||||||
|
id: item.id,
|
||||||
|
position: item.position,
|
||||||
|
title: item.title
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.config.yAxisMode = axisOptions.yAxisMode || 'span';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.config.xIsSeries = axisOptions.xIsSeries || 0;
|
||||||
|
this.config.shortenYAxisNumbers = axisOptions.shortenYAxisNumbers || 0;
|
||||||
|
|
||||||
this.config.formatTooltipX = options.tooltipOptions.formatTooltipX;
|
this.config.formatTooltipX = options.tooltipOptions.formatTooltipX;
|
||||||
this.config.formatTooltipY = options.tooltipOptions.formatTooltipY;
|
this.config.formatTooltipY = options.tooltipOptions.formatTooltipY;
|
||||||
@ -83,18 +100,61 @@ export default class AxisChart extends BaseChart {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
calcYAxisParameters(dataValues, withMinimum = 'false') {
|
|
||||||
const yPts = calcChartIntervals(dataValues, withMinimum);
|
|
||||||
const scaleMultiplier = this.height / getValueRange(yPts);
|
|
||||||
const intervalHeight = getIntervalSize(yPts) * scaleMultiplier;
|
|
||||||
const zeroLine = this.height - (getZeroIndex(yPts) * intervalHeight);
|
|
||||||
|
|
||||||
|
calcYAxisParameters(dataValues, withMinimum = 'false') {
|
||||||
|
let yPts, scaleMultiplier, intervalHeight, zeroLine, positions;
|
||||||
|
|
||||||
|
// if we have an object we have multiple yAxisParameters.
|
||||||
|
if (dataValues instanceof Array) {
|
||||||
|
yPts = calcChartIntervals(dataValues, withMinimum);
|
||||||
|
scaleMultiplier = this.height / getValueRange(yPts);
|
||||||
|
intervalHeight = getIntervalSize(yPts) * scaleMultiplier;
|
||||||
|
zeroLine = this.height - getZeroIndex(yPts) * intervalHeight;
|
||||||
this.state.yAxis = {
|
this.state.yAxis = {
|
||||||
labels: yPts,
|
labels: yPts,
|
||||||
positions: yPts.map(d => zeroLine - d * scaleMultiplier),
|
positions: yPts.map((d) => zeroLine - d * scaleMultiplier),
|
||||||
scaleMultiplier: scaleMultiplier,
|
scaleMultiplier: scaleMultiplier,
|
||||||
zeroLine: zeroLine,
|
zeroLine: zeroLine
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
this.state.yAxis = [];
|
||||||
|
for (let key in dataValues) {
|
||||||
|
const dataValue = dataValues[key];
|
||||||
|
yPts = calcChartIntervals(dataValue, withMinimum);
|
||||||
|
scaleMultiplier = this.height / getValueRange(yPts);
|
||||||
|
intervalHeight = getIntervalSize(yPts) * scaleMultiplier;
|
||||||
|
zeroLine = this.height - getZeroIndex(yPts) * intervalHeight;
|
||||||
|
positions = yPts.map((d) => zeroLine - d * scaleMultiplier);
|
||||||
|
|
||||||
|
const yAxisConfigObject =
|
||||||
|
this.config.yAxisConfig.find((item) => key === item.id) || [];
|
||||||
|
const yAxisAlignment = yAxisConfigObject
|
||||||
|
? yAxisConfigObject.position
|
||||||
|
: 'right';
|
||||||
|
|
||||||
|
if (this.state.yAxis.length) {
|
||||||
|
const yPtsArray = [];
|
||||||
|
const firstArr = this.state.yAxis[0];
|
||||||
|
// we need to loop through original positions.
|
||||||
|
firstArr.positions.forEach((pos) => {
|
||||||
|
yPtsArray.push(Math.ceil(pos / scaleMultiplier));
|
||||||
|
});
|
||||||
|
yPts = yPtsArray.reverse();
|
||||||
|
zeroLine = this.height - getZeroIndex(yPts) * intervalHeight;
|
||||||
|
positions = firstArr.positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state.yAxis.push({
|
||||||
|
axisID: key || 'left-axis',
|
||||||
|
labels: yPts,
|
||||||
|
title: yAxisConfigObject.title,
|
||||||
|
pos: yAxisAlignment,
|
||||||
|
scaleMultiplier,
|
||||||
|
zeroLine,
|
||||||
|
positions
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dependent if above changes
|
// Dependent if above changes
|
||||||
this.calcDatasetPoints();
|
this.calcDatasetPoints();
|
||||||
@ -104,21 +164,39 @@ export default class AxisChart extends BaseChart {
|
|||||||
|
|
||||||
calcDatasetPoints() {
|
calcDatasetPoints() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
let scaleAll = values => values.map(val => scale(val, s.yAxis));
|
let scaleAll = (values, id) => {
|
||||||
|
return values.map((val) => {
|
||||||
|
let { yAxis } = s;
|
||||||
|
|
||||||
|
if (yAxis instanceof Array) {
|
||||||
|
yAxis = yAxis.length > 1 ? yAxis.find((axis) => id === axis.axisID) : s.yAxis[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return scale(val, yAxis);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
s.barChartIndex = 1;
|
||||||
s.datasets = this.data.datasets.map((d, i) => {
|
s.datasets = this.data.datasets.map((d, i) => {
|
||||||
let values = d.values;
|
let values = d.values;
|
||||||
let cumulativeYs = d.cumulativeYs || [];
|
let cumulativeYs = d.cumulativeYs || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: d.name && d.name.replace(/<|>|&/g, (char) => char == '&' ? '&' : char == '<' ? '<' : '>'),
|
name:
|
||||||
|
d.name &&
|
||||||
|
d.name.replace(/<|>|&/g, (char) =>
|
||||||
|
char == '&' ? '&' : char == '<' ? '<' : '>'
|
||||||
|
),
|
||||||
index: i,
|
index: i,
|
||||||
|
barIndex: d.chartType === 'bar' ? s.barChartIndex++ : s.barChartIndex,
|
||||||
chartType: d.chartType,
|
chartType: d.chartType,
|
||||||
|
|
||||||
values: values,
|
values: values,
|
||||||
yPositions: scaleAll(values),
|
yPositions: scaleAll(values, d.axisID),
|
||||||
|
id: d.axisID,
|
||||||
|
|
||||||
cumulativeYs: cumulativeYs,
|
cumulativeYs: cumulativeYs,
|
||||||
cumulativeYPos: scaleAll(cumulativeYs),
|
cumulativeYPos: scaleAll(cumulativeYs, d.axisID)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -163,44 +241,71 @@ export default class AxisChart extends BaseChart {
|
|||||||
|
|
||||||
getAllYValues() {
|
getAllYValues() {
|
||||||
let key = 'values';
|
let key = 'values';
|
||||||
|
let multiAxis = this.config.yAxisConfig ? true : false;
|
||||||
|
let allValueLists = multiAxis ? {} : [];
|
||||||
|
|
||||||
if(this.barOptions.stacked) {
|
let groupBy = (arr, property) => {
|
||||||
key = 'cumulativeYs';
|
return arr.reduce((acc, cur) => {
|
||||||
|
acc[cur[property]] = [...(acc[cur[property]] || []), cur];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
let generateCumulative = (arr) => {
|
||||||
let cumulative = new Array(this.state.datasetLength).fill(0);
|
let cumulative = new Array(this.state.datasetLength).fill(0);
|
||||||
this.data.datasets.map((d, i) => {
|
arr.forEach((d, i) => {
|
||||||
let values = this.data.datasets[i].values;
|
let values = arr[i].values;
|
||||||
d[key] = cumulative = cumulative.map((c, i) => c + values[i]);
|
d[key] = cumulative = cumulative.map((c, i) => {
|
||||||
|
return c + values[i];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.barOptions.stacked) {
|
||||||
|
key = 'cumulativeYs';
|
||||||
|
// we need to filter out the different yAxis ID's here.
|
||||||
|
if (multiAxis) {
|
||||||
|
const groupedDataSets = groupBy(this.data.datasets, 'axisID');
|
||||||
|
// const dataSetsByAxis = this.data.dd
|
||||||
|
for (var axisID in groupedDataSets) {
|
||||||
|
generateCumulative(groupedDataSets[axisID]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generateCumulative(this.data.datasets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is the trouble maker, we don't want to merge all
|
||||||
|
// datasets since we are trying to run two yAxis.
|
||||||
|
if (multiAxis) {
|
||||||
|
this.data.datasets.forEach((d) => {
|
||||||
|
// if the array exists already just push more data into it.
|
||||||
|
// otherwise create a new array into the object.
|
||||||
|
allValueLists[d.axisID || key]
|
||||||
|
? allValueLists[d.axisID || key].push(...d[key])
|
||||||
|
: (allValueLists[d.axisID || key] = [...d[key]]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
allValueLists = this.data.datasets.map((d) => {
|
||||||
|
return d[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let allValueLists = this.data.datasets.map(d => d[key]);
|
if (this.data.yMarkers && !multiAxis) {
|
||||||
if(this.data.yMarkers) {
|
allValueLists.push(this.data.yMarkers.map((d) => d.value));
|
||||||
allValueLists.push(this.data.yMarkers.map(d => d.value));
|
|
||||||
}
|
}
|
||||||
if(this.data.yRegions) {
|
|
||||||
this.data.yRegions.map(d => {
|
if (this.data.yRegions && !multiAxis) {
|
||||||
|
this.data.yRegions.map((d) => {
|
||||||
allValueLists.push([d.end, d.start]);
|
allValueLists.push([d.end, d.start]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return [].concat(...allValueLists);
|
return multiAxis ? allValueLists : [].concat(...allValueLists);
|
||||||
}
|
}
|
||||||
|
|
||||||
setupComponents() {
|
setupComponents() {
|
||||||
let componentConfigs = [
|
let componentConfigs = [
|
||||||
[
|
|
||||||
'yAxis',
|
|
||||||
{
|
|
||||||
mode: this.config.yAxisMode,
|
|
||||||
width: this.width,
|
|
||||||
shortenNumbers: this.config.shortenYAxisNumbers
|
|
||||||
// pos: 'right'
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
return this.state.yAxis;
|
|
||||||
}.bind(this)
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
[
|
||||||
'xAxis',
|
'xAxis',
|
||||||
{
|
{
|
||||||
@ -229,11 +334,43 @@ export default class AxisChart extends BaseChart {
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// if we have multiple yAxisConfigs we need to update the yAxisDefault
|
||||||
|
// components to multiple yAxis components.
|
||||||
|
if (this.config.yAxisConfig && this.config.yAxisConfig.length) {
|
||||||
|
this.config.yAxisConfig.forEach((yAxis) => {
|
||||||
|
componentConfigs.push([
|
||||||
|
'yAxis',
|
||||||
|
{
|
||||||
|
mode: this.config.yAxisMode,
|
||||||
|
width: this.width,
|
||||||
|
shortenNumbers: this.config.shortenYAxisNumbers,
|
||||||
|
pos: yAxis.position || 'left'
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
return this.state.yAxis;
|
||||||
|
}.bind(this)
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
componentConfigs.push([
|
||||||
|
'yAxis',
|
||||||
|
{
|
||||||
|
mode: this.config.yAxisMode,
|
||||||
|
width: this.width,
|
||||||
|
shortenNumbers: this.config.shortenYAxisNumbers
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
return this.state.yAxis;
|
||||||
|
}.bind(this)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
let barDatasets = this.state.datasets.filter(d => d.chartType === 'bar');
|
let barDatasets = this.state.datasets.filter(d => d.chartType === 'bar');
|
||||||
let lineDatasets = this.state.datasets.filter(d => d.chartType === 'line');
|
let lineDatasets = this.state.datasets.filter(d => d.chartType === 'line');
|
||||||
|
|
||||||
let barsConfigs = barDatasets.map(d => {
|
let barsConfigs = barDatasets.map(d => {
|
||||||
let index = d.index;
|
let index = d.index;
|
||||||
|
let barIndex = d.barIndex || index;
|
||||||
return [
|
return [
|
||||||
'barGraph' + '-' + d.index,
|
'barGraph' + '-' + d.index,
|
||||||
{
|
{
|
||||||
@ -247,29 +384,41 @@ export default class AxisChart extends BaseChart {
|
|||||||
},
|
},
|
||||||
function() {
|
function() {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
|
let { yAxis } = s;
|
||||||
let d = s.datasets[index];
|
let d = s.datasets[index];
|
||||||
|
let { id = 'left-axis' } = d;
|
||||||
let stacked = this.barOptions.stacked;
|
let stacked = this.barOptions.stacked;
|
||||||
|
|
||||||
let spaceRatio = this.barOptions.spaceRatio || BAR_CHART_SPACE_RATIO;
|
let spaceRatio = this.barOptions.spaceRatio || BAR_CHART_SPACE_RATIO;
|
||||||
let barsWidth = s.unitWidth * (1 - spaceRatio);
|
let barsWidth = s.unitWidth * (1 - spaceRatio);
|
||||||
let barWidth = barsWidth/(stacked ? 1 : barDatasets.length);
|
let barWidth = barsWidth / (stacked ? 1 : barDatasets.length);
|
||||||
|
|
||||||
let xPositions = s.xAxis.positions.map(x => x - barsWidth/2);
|
// if there are multiple yAxis we need to return the yAxis with the
|
||||||
if(!stacked) {
|
// proper ID.
|
||||||
xPositions = xPositions.map(p => p + barWidth * index);
|
if (yAxis instanceof Array) {
|
||||||
|
// if the person only configured one yAxis in the array return the first.
|
||||||
|
yAxis = yAxis.length > 1 ? yAxis.find((axis) => id === axis.axisID) : s.yAxis[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let xPositions = s.xAxis.positions.map((x) => x - barsWidth / 2);
|
||||||
|
|
||||||
|
if (!stacked) {
|
||||||
|
xPositions = xPositions.map((p) => {
|
||||||
|
return p + barWidth * barIndex - barWidth;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let labels = new Array(s.datasetLength).fill('');
|
let labels = new Array(s.datasetLength).fill('');
|
||||||
if(this.config.valuesOverPoints) {
|
if (this.config.valuesOverPoints) {
|
||||||
if(stacked && d.index === s.datasets.length - 1) {
|
if (stacked && d.index === s.datasets.length - 1) {
|
||||||
labels = d.cumulativeYs;
|
labels = d.cumulativeYs;
|
||||||
} else {
|
} else {
|
||||||
labels = d.values;
|
labels = d.values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let offsets = new Array(s.datasetLength).fill(0);
|
let offsets = new Array(s.datasetLength).fill(0);
|
||||||
if(stacked) {
|
if (stacked) {
|
||||||
offsets = d.yPositions.map((y, j) => y - d.cumulativeYPos[j]);
|
offsets = d.yPositions.map((y, j) => y - d.cumulativeYPos[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,7 +429,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
// values: d.values,
|
// values: d.values,
|
||||||
labels: labels,
|
labels: labels,
|
||||||
|
|
||||||
zeroLine: s.yAxis.zeroLine,
|
zeroLine: yAxis.zeroLine,
|
||||||
barsWidth: barsWidth,
|
barsWidth: barsWidth,
|
||||||
barWidth: barWidth,
|
barWidth: barWidth,
|
||||||
};
|
};
|
||||||
@ -288,7 +437,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
let lineConfigs = lineDatasets.map(d => {
|
let lineConfigs = lineDatasets.map((d) => {
|
||||||
let index = d.index;
|
let index = d.index;
|
||||||
return [
|
return [
|
||||||
'lineGraph' + '-' + d.index,
|
'lineGraph' + '-' + d.index,
|
||||||
@ -303,13 +452,21 @@ export default class AxisChart extends BaseChart {
|
|||||||
hideLine: this.lineOptions.hideLine,
|
hideLine: this.lineOptions.hideLine,
|
||||||
|
|
||||||
// same for all datasets
|
// same for all datasets
|
||||||
valuesOverPoints: this.config.valuesOverPoints,
|
valuesOverPoints: this.config.valuesOverPoints
|
||||||
},
|
},
|
||||||
function() {
|
function () {
|
||||||
let s = this.state;
|
let s = this.state;
|
||||||
let d = s.datasets[index];
|
let d = s.datasets[index];
|
||||||
let minLine = s.yAxis.positions[0] < s.yAxis.zeroLine
|
|
||||||
? s.yAxis.positions[0] : s.yAxis.zeroLine;
|
// if we have more than one yindex lets map the values
|
||||||
|
const yAxis = s.yAxis.length
|
||||||
|
? s.yAxis.find((axis) => d.id === axis.axisID) || s.yAxis[0]
|
||||||
|
: s.yAxis;
|
||||||
|
|
||||||
|
let minLine =
|
||||||
|
yAxis.positions[0] < yAxis.zeroLine
|
||||||
|
? yAxis.positions[0]
|
||||||
|
: yAxis.zeroLine;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
xPositions: s.xAxis.positions,
|
xPositions: s.xAxis.positions,
|
||||||
@ -318,7 +475,7 @@ export default class AxisChart extends BaseChart {
|
|||||||
values: d.values,
|
values: d.values,
|
||||||
|
|
||||||
zeroLine: minLine,
|
zeroLine: minLine,
|
||||||
radius: this.lineOptions.dotSize || LINE_CHART_DOT_SIZE,
|
radius: this.lineOptions.dotSize || LINE_CHART_DOT_SIZE
|
||||||
};
|
};
|
||||||
}.bind(this)
|
}.bind(this)
|
||||||
];
|
];
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { makeSVGGroup } from '../utils/draw';
|
import { makeSVGGroup } from '../utils/draw';
|
||||||
import { makeText, makePath, xLine, yLine, yMarker, yRegion, datasetBar, datasetDot, percentageBar, getPaths, heatSquare } from '../utils/draw';
|
import { makeText, generateAxisLabel, makePath, xLine, yLine, yMarker, yRegion, datasetBar, datasetDot, percentageBar, getPaths, heatSquare } from '../utils/draw';
|
||||||
import { equilizeNoOfElements } from '../utils/draw-utils';
|
import { equilizeNoOfElements } from '../utils/draw-utils';
|
||||||
import { translateHoriLine, translateVertLine, animateRegion, animateBar,
|
import { translateHoriLine, translateVertLine, animateRegion, animateBar,
|
||||||
animateDot, animatePath, animatePathStr } from '../utils/animate';
|
animateDot, animatePath, animatePathStr } from '../utils/animate';
|
||||||
@ -50,8 +50,12 @@ class ChartComponent {
|
|||||||
this.store = this.makeElements(data);
|
this.store = this.makeElements(data);
|
||||||
|
|
||||||
this.layer.textContent = '';
|
this.layer.textContent = '';
|
||||||
this.store.forEach(element => {
|
this.store.forEach((element) => {
|
||||||
this.layer.appendChild(element);
|
element.length
|
||||||
|
? element.forEach((el) => {
|
||||||
|
this.layer.appendChild(el);
|
||||||
|
})
|
||||||
|
: this.layer.appendChild(element);
|
||||||
});
|
});
|
||||||
this.labels.forEach(element => {
|
this.labels.forEach(element => {
|
||||||
this.layer.appendChild(element);
|
this.layer.appendChild(element);
|
||||||
@ -117,13 +121,72 @@ let componentConfigs = {
|
|||||||
yAxis: {
|
yAxis: {
|
||||||
layerClass: 'y axis',
|
layerClass: 'y axis',
|
||||||
makeElements(data) {
|
makeElements(data) {
|
||||||
return data.positions.map((position, i) =>
|
let elements = [];
|
||||||
yLine(position, data.labels[i], this.constants.width,
|
|
||||||
{mode: this.constants.mode, pos: this.constants.pos, shortenNumbers: this.constants.shortenNumbers})
|
if (data.length) {
|
||||||
|
data.forEach((item, i) => {
|
||||||
|
item.positions.map((position, i) => {
|
||||||
|
elements.push(
|
||||||
|
yLine(position, item.labels[i], this.constants.width, {
|
||||||
|
mode: this.constants.mode,
|
||||||
|
pos: item.pos || this.constants.pos,
|
||||||
|
shortenNumbers: this.constants.shortenNumbers
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
// we need to make yAxis titles if they are defined
|
||||||
|
if (item.title) {
|
||||||
|
elements.push(
|
||||||
|
generateAxisLabel({
|
||||||
|
title: item.title,
|
||||||
|
position: item.pos,
|
||||||
|
height: item.zeroLine,
|
||||||
|
width: this.constants.width
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.positions.map((position, i) => {
|
||||||
|
return yLine(position, data.labels[i], this.constants.width, {
|
||||||
|
mode: this.constants.mode,
|
||||||
|
pos: this.constants.pos,
|
||||||
|
shortenNumbers: this.constants.shortenNumbers
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
animateElements(newData) {
|
animateElements(newData) {
|
||||||
|
const animateMultipleElements = (oldData, newData) => {
|
||||||
|
let newPos = newData.positions;
|
||||||
|
let newLabels = newData.labels;
|
||||||
|
let oldPos = oldData.positions;
|
||||||
|
let oldLabels = oldData.labels;
|
||||||
|
|
||||||
|
[oldPos, newPos] = equilizeNoOfElements(oldPos, newPos);
|
||||||
|
[oldLabels, newLabels] = equilizeNoOfElements(oldLabels, newLabels);
|
||||||
|
|
||||||
|
this.render({
|
||||||
|
positions: oldPos,
|
||||||
|
labels: newLabels
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.store.map((line, i) => {
|
||||||
|
return translateHoriLine(line, newPos[i], oldPos[i]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// we will need to animate both axis if we have more than one.
|
||||||
|
// so check if the oldData is an array of values.
|
||||||
|
if (this.oldData instanceof Array) {
|
||||||
|
return this.oldData.forEach((old, i) => {
|
||||||
|
animateMultipleElements(old, newData[i]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let newPos = newData.positions;
|
let newPos = newData.positions;
|
||||||
let newLabels = newData.labels;
|
let newLabels = newData.labels;
|
||||||
let oldPos = this.oldData.positions;
|
let oldPos = this.oldData.positions;
|
||||||
@ -138,9 +201,7 @@ let componentConfigs = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return this.store.map((line, i) => {
|
return this.store.map((line, i) => {
|
||||||
return translateHoriLine(
|
return translateHoriLine(line, newPos[i], oldPos[i]);
|
||||||
line, newPos[i], oldPos[i]
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -63,13 +63,15 @@ export function zeroDataPrep(realData) {
|
|||||||
|
|
||||||
let zeroData = {
|
let zeroData = {
|
||||||
labels: realData.labels.slice(0, -1),
|
labels: realData.labels.slice(0, -1),
|
||||||
datasets: realData.datasets.map(d => {
|
datasets: realData.datasets.map((d) => {
|
||||||
|
const { axisID } = d;
|
||||||
return {
|
return {
|
||||||
|
axisID,
|
||||||
name: '',
|
name: '',
|
||||||
values: zeroArray.slice(0, -1),
|
values: zeroArray.slice(0, -1),
|
||||||
chartType: d.chartType
|
chartType: d.chartType
|
||||||
};
|
};
|
||||||
}),
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
if(realData.yMarkers) {
|
if(realData.yMarkers) {
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
import { getBarHeightAndYAttr, truncateString, shortenLargeNumber, getSplineCurvePointsStr } from './draw-utils';
|
import {
|
||||||
|
getBarHeightAndYAttr,
|
||||||
|
truncateString,
|
||||||
|
shortenLargeNumber,
|
||||||
|
getSplineCurvePointsStr
|
||||||
|
} from './draw-utils';
|
||||||
import { getStringWidth, isValidNumber } from './helpers';
|
import { getStringWidth, isValidNumber } from './helpers';
|
||||||
import { DOT_OVERLAY_SIZE_INCR, PERCENTAGE_BAR_DEFAULT_DEPTH } from './constants';
|
import { DOT_OVERLAY_SIZE_INCR, PERCENTAGE_BAR_DEFAULT_DEPTH } from './constants';
|
||||||
import { lightenDarkenColor } from './colors';
|
import { lightenDarkenColor } from './colors';
|
||||||
@ -11,32 +16,32 @@ const BASE_LINE_COLOR = '#dadada';
|
|||||||
const FONT_FILL = '#555b51';
|
const FONT_FILL = '#555b51';
|
||||||
|
|
||||||
function $(expr, con) {
|
function $(expr, con) {
|
||||||
return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
|
return typeof expr === 'string' ? (con || document).querySelector(expr) : expr || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createSVG(tag, o) {
|
export function createSVG(tag, o) {
|
||||||
var element = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
var element = document.createElementNS('http://www.w3.org/2000/svg', tag);
|
||||||
|
|
||||||
for (var i in o) {
|
for (var i in o) {
|
||||||
var val = o[i];
|
var val = o[i];
|
||||||
|
|
||||||
if (i === "inside") {
|
if (i === 'inside') {
|
||||||
$(val).appendChild(element);
|
$(val).appendChild(element);
|
||||||
}
|
} else if (i === 'around') {
|
||||||
else if (i === "around") {
|
|
||||||
var ref = $(val);
|
var ref = $(val);
|
||||||
ref.parentNode.insertBefore(element, ref);
|
ref.parentNode.insertBefore(element, ref);
|
||||||
element.appendChild(ref);
|
element.appendChild(ref);
|
||||||
|
} else if (i === 'styles') {
|
||||||
} else if (i === "styles") {
|
if (typeof val === 'object') {
|
||||||
if(typeof val === "object") {
|
Object.keys(val).map((prop) => {
|
||||||
Object.keys(val).map(prop => {
|
|
||||||
element.style[prop] = val[prop];
|
element.style[prop] = val[prop];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(i === "className") { i = "class"; }
|
if (i === 'className') {
|
||||||
if(i === "innerHTML") {
|
i = 'class';
|
||||||
|
}
|
||||||
|
if (i === 'innerHTML') {
|
||||||
element['textContent'] = val;
|
element['textContent'] = val;
|
||||||
} else {
|
} else {
|
||||||
element.setAttribute(i, val);
|
element.setAttribute(i, val);
|
||||||
@ -60,9 +65,9 @@ function renderVerticalGradient(svgDefElem, gradientId) {
|
|||||||
|
|
||||||
function setGradientStop(gradElem, offset, color, opacity) {
|
function setGradientStop(gradElem, offset, color, opacity) {
|
||||||
return createSVG('stop', {
|
return createSVG('stop', {
|
||||||
'inside': gradElem,
|
inside: gradElem,
|
||||||
'style': `stop-color: ${color}`,
|
style: `stop-color: ${color}`,
|
||||||
'offset': offset,
|
offset: offset,
|
||||||
'stop-opacity': opacity
|
'stop-opacity': opacity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -78,28 +83,34 @@ export function makeSVGContainer(parent, className, width, height) {
|
|||||||
|
|
||||||
export function makeSVGDefs(svgContainer) {
|
export function makeSVGDefs(svgContainer) {
|
||||||
return createSVG('defs', {
|
return createSVG('defs', {
|
||||||
inside: svgContainer,
|
inside: svgContainer
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeSVGGroup(className, transform='', parent=undefined) {
|
export function makeSVGGroup(className, transform = '', parent = undefined) {
|
||||||
let args = {
|
let args = {
|
||||||
className: className,
|
className: className,
|
||||||
transform: transform
|
transform: transform
|
||||||
};
|
};
|
||||||
if(parent) args.inside = parent;
|
if (parent) args.inside = parent;
|
||||||
return createSVG('g', args);
|
return createSVG('g', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapInSVGGroup(elements, className='') {
|
export function wrapInSVGGroup(elements, className = '') {
|
||||||
let g = createSVG('g', {
|
let g = createSVG('g', {
|
||||||
className: className
|
className: className
|
||||||
});
|
});
|
||||||
elements.forEach(e => g.appendChild(e));
|
elements.forEach((e) => g.appendChild(e));
|
||||||
return g;
|
return g;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makePath(pathStr, className='', stroke='none', fill='none', strokeWidth=2) {
|
export function makePath(
|
||||||
|
pathStr,
|
||||||
|
className = '',
|
||||||
|
stroke = 'none',
|
||||||
|
fill = 'none',
|
||||||
|
strokeWidth = 2
|
||||||
|
) {
|
||||||
return createSVG('path', {
|
return createSVG('path', {
|
||||||
className: className,
|
className: className,
|
||||||
d: pathStr,
|
d: pathStr,
|
||||||
@ -111,7 +122,14 @@ export function makePath(pathStr, className='', stroke='none', fill='none', stro
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeArcPathStr(startPosition, endPosition, center, radius, clockWise=1, largeArc=0){
|
export function makeArcPathStr(
|
||||||
|
startPosition,
|
||||||
|
endPosition,
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
clockWise = 1,
|
||||||
|
largeArc = 0
|
||||||
|
) {
|
||||||
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
||||||
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
||||||
return `M${center.x} ${center.y}
|
return `M${center.x} ${center.y}
|
||||||
@ -120,9 +138,20 @@ export function makeArcPathStr(startPosition, endPosition, center, radius, clock
|
|||||||
${arcEndX} ${arcEndY} z`;
|
${arcEndX} ${arcEndY} z`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeCircleStr(startPosition, endPosition, center, radius, clockWise=1, largeArc=0){
|
export function makeCircleStr(
|
||||||
|
startPosition,
|
||||||
|
endPosition,
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
clockWise = 1,
|
||||||
|
largeArc = 0
|
||||||
|
) {
|
||||||
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
||||||
let [arcEndX, midArc, arcEndY] = [center.x + endPosition.x, center.y * 2, center.y + endPosition.y];
|
let [arcEndX, midArc, arcEndY] = [
|
||||||
|
center.x + endPosition.x,
|
||||||
|
center.y * 2,
|
||||||
|
center.y + endPosition.y
|
||||||
|
];
|
||||||
return `M${center.x} ${center.y}
|
return `M${center.x} ${center.y}
|
||||||
L${arcStartX} ${arcStartY}
|
L${arcStartX} ${arcStartY}
|
||||||
A ${radius} ${radius} 0 ${largeArc} ${clockWise ? 1 : 0}
|
A ${radius} ${radius} 0 ${largeArc} ${clockWise ? 1 : 0}
|
||||||
@ -132,7 +161,14 @@ export function makeCircleStr(startPosition, endPosition, center, radius, clockW
|
|||||||
${arcEndX} ${arcEndY} z`;
|
${arcEndX} ${arcEndY} z`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeArcStrokePathStr(startPosition, endPosition, center, radius, clockWise=1, largeArc=0){
|
export function makeArcStrokePathStr(
|
||||||
|
startPosition,
|
||||||
|
endPosition,
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
clockWise = 1,
|
||||||
|
largeArc = 0
|
||||||
|
) {
|
||||||
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
||||||
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
let [arcEndX, arcEndY] = [center.x + endPosition.x, center.y + endPosition.y];
|
||||||
|
|
||||||
@ -141,9 +177,20 @@ export function makeArcStrokePathStr(startPosition, endPosition, center, radius,
|
|||||||
${arcEndX} ${arcEndY}`;
|
${arcEndX} ${arcEndY}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeStrokeCircleStr(startPosition, endPosition, center, radius, clockWise=1, largeArc=0){
|
export function makeStrokeCircleStr(
|
||||||
|
startPosition,
|
||||||
|
endPosition,
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
clockWise = 1,
|
||||||
|
largeArc = 0
|
||||||
|
) {
|
||||||
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
let [arcStartX, arcStartY] = [center.x + startPosition.x, center.y + startPosition.y];
|
||||||
let [arcEndX, midArc, arcEndY] = [center.x + endPosition.x, radius * 2 + arcStartY, center.y + startPosition.y];
|
let [arcEndX, midArc, arcEndY] = [
|
||||||
|
center.x + endPosition.x,
|
||||||
|
radius * 2 + arcStartY,
|
||||||
|
center.y + startPosition.y
|
||||||
|
];
|
||||||
|
|
||||||
return `M${arcStartX} ${arcStartY}
|
return `M${arcStartX} ${arcStartY}
|
||||||
A ${radius} ${radius} 0 ${largeArc} ${clockWise ? 1 : 0}
|
A ${radius} ${radius} 0 ${largeArc} ${clockWise ? 1 : 0}
|
||||||
@ -154,23 +201,29 @@ export function makeStrokeCircleStr(startPosition, endPosition, center, radius,
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function makeGradient(svgDefElem, color, lighter = false) {
|
export function makeGradient(svgDefElem, color, lighter = false) {
|
||||||
let gradientId ='path-fill-gradient' + '-' + color + '-' +(lighter ? 'lighter' : 'default');
|
let gradientId =
|
||||||
|
'path-fill-gradient' + '-' + color + '-' + (lighter ? 'lighter' : 'default');
|
||||||
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
let gradientDef = renderVerticalGradient(svgDefElem, gradientId);
|
||||||
let opacities = [1, 0.6, 0.2];
|
let opacities = [1, 0.6, 0.2];
|
||||||
if(lighter) {
|
if (lighter) {
|
||||||
opacities = [0.4, 0.2, 0];
|
opacities = [0.4, 0.2, 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
setGradientStop(gradientDef, "0%", color, opacities[0]);
|
setGradientStop(gradientDef, '0%', color, opacities[0]);
|
||||||
setGradientStop(gradientDef, "50%", color, opacities[1]);
|
setGradientStop(gradientDef, '50%', color, opacities[1]);
|
||||||
setGradientStop(gradientDef, "100%", color, opacities[2]);
|
setGradientStop(gradientDef, '100%', color, opacities[2]);
|
||||||
|
|
||||||
return gradientId;
|
return gradientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function percentageBar(x, y, width, height,
|
export function percentageBar(
|
||||||
depth=PERCENTAGE_BAR_DEFAULT_DEPTH, fill='none') {
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth = PERCENTAGE_BAR_DEFAULT_DEPTH,
|
||||||
|
fill = 'none'
|
||||||
|
) {
|
||||||
let args = {
|
let args = {
|
||||||
className: 'percentage-bar',
|
className: 'percentage-bar',
|
||||||
x: x,
|
x: x,
|
||||||
@ -179,18 +232,18 @@ export function percentageBar(x, y, width, height,
|
|||||||
height: height,
|
height: height,
|
||||||
fill: fill,
|
fill: fill,
|
||||||
styles: {
|
styles: {
|
||||||
'stroke': lightenDarkenColor(fill, -25),
|
stroke: lightenDarkenColor(fill, -25),
|
||||||
// Diabolically good: https://stackoverflow.com/a/9000859
|
// Diabolically good: https://stackoverflow.com/a/9000859
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
|
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
|
||||||
'stroke-dasharray': `0, ${height + width}, ${width}, ${height}`,
|
'stroke-dasharray': `0, ${height + width}, ${width}, ${height}`,
|
||||||
'stroke-width': depth
|
'stroke-width': depth
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return createSVG("rect", args);
|
return createSVG('rect', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function heatSquare(className, x, y, size, radius, fill='none', data={}) {
|
export function heatSquare(className, x, y, size, radius, fill = 'none', data = {}) {
|
||||||
let args = {
|
let args = {
|
||||||
className: className,
|
className: className,
|
||||||
x: x,
|
x: x,
|
||||||
@ -201,14 +254,14 @@ export function heatSquare(className, x, y, size, radius, fill='none', data={})
|
|||||||
fill: fill
|
fill: fill
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(data).map(key => {
|
Object.keys(data).map((key) => {
|
||||||
args[key] = data[key];
|
args[key] = data[key];
|
||||||
});
|
});
|
||||||
|
|
||||||
return createSVG("rect", args);
|
return createSVG('rect', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function legendBar(x, y, size, fill='none', label, truncate=false) {
|
export function legendBar(x, y, size, fill = 'none', label, truncate = false) {
|
||||||
label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label;
|
label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label;
|
||||||
|
|
||||||
let args = {
|
let args = {
|
||||||
@ -223,8 +276,8 @@ export function legendBar(x, y, size, fill='none', label, truncate=false) {
|
|||||||
className: 'legend-dataset-text',
|
className: 'legend-dataset-text',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
dy: (FONT_SIZE * 2) + 'px',
|
dy: FONT_SIZE * 2 + 'px',
|
||||||
'font-size': (FONT_SIZE * 1.2) + 'px',
|
'font-size': FONT_SIZE * 1.2 + 'px',
|
||||||
'text-anchor': 'start',
|
'text-anchor': 'start',
|
||||||
fill: FONT_FILL,
|
fill: FONT_FILL,
|
||||||
innerHTML: label
|
innerHTML: label
|
||||||
@ -233,13 +286,13 @@ export function legendBar(x, y, size, fill='none', label, truncate=false) {
|
|||||||
let group = createSVG('g', {
|
let group = createSVG('g', {
|
||||||
transform: `translate(${x}, ${y})`
|
transform: `translate(${x}, ${y})`
|
||||||
});
|
});
|
||||||
group.appendChild(createSVG("rect", args));
|
group.appendChild(createSVG('rect', args));
|
||||||
group.appendChild(text);
|
group.appendChild(text);
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function legendDot(x, y, size, fill='none', label, truncate=false) {
|
export function legendDot(x, y, size, fill = 'none', label, truncate = false) {
|
||||||
label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label;
|
label = truncate ? truncateString(label, LABEL_MAX_CHARS) : label;
|
||||||
|
|
||||||
let args = {
|
let args = {
|
||||||
@ -253,9 +306,9 @@ export function legendDot(x, y, size, fill='none', label, truncate=false) {
|
|||||||
className: 'legend-dataset-text',
|
className: 'legend-dataset-text',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
dx: (FONT_SIZE) + 'px',
|
dx: FONT_SIZE + 'px',
|
||||||
dy: (FONT_SIZE/3) + 'px',
|
dy: FONT_SIZE / 3 + 'px',
|
||||||
'font-size': (FONT_SIZE * 1.2) + 'px',
|
'font-size': FONT_SIZE * 1.2 + 'px',
|
||||||
'text-anchor': 'start',
|
'text-anchor': 'start',
|
||||||
fill: FONT_FILL,
|
fill: FONT_FILL,
|
||||||
innerHTML: label
|
innerHTML: label
|
||||||
@ -264,7 +317,7 @@ export function legendDot(x, y, size, fill='none', label, truncate=false) {
|
|||||||
let group = createSVG('g', {
|
let group = createSVG('g', {
|
||||||
transform: `translate(${x}, ${y})`
|
transform: `translate(${x}, ${y})`
|
||||||
});
|
});
|
||||||
group.appendChild(createSVG("circle", args));
|
group.appendChild(createSVG('circle', args));
|
||||||
group.appendChild(text);
|
group.appendChild(text);
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
@ -272,7 +325,7 @@ export function legendDot(x, y, size, fill='none', label, truncate=false) {
|
|||||||
|
|
||||||
export function makeText(className, x, y, content, options = {}) {
|
export function makeText(className, x, y, content, options = {}) {
|
||||||
let fontSize = options.fontSize || FONT_SIZE;
|
let fontSize = options.fontSize || FONT_SIZE;
|
||||||
let dy = options.dy !== undefined ? options.dy : (fontSize / 2);
|
let dy = options.dy !== undefined ? options.dy : fontSize / 2;
|
||||||
let fill = options.fill || FONT_FILL;
|
let fill = options.fill || FONT_FILL;
|
||||||
let textAnchor = options.textAnchor || 'start';
|
let textAnchor = options.textAnchor || 'start';
|
||||||
return createSVG('text', {
|
return createSVG('text', {
|
||||||
@ -287,8 +340,8 @@ export function makeText(className, x, y, content, options = {}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeVertLine(x, label, y1, y2, options={}) {
|
function makeVertLine(x, label, y1, y2, options = {}) {
|
||||||
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
if (!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
let l = createSVG('line', {
|
let l = createSVG('line', {
|
||||||
className: 'line-vertical ' + options.className,
|
className: 'line-vertical ' + options.className,
|
||||||
x1: 0,
|
x1: 0,
|
||||||
@ -306,11 +359,11 @@ function makeVertLine(x, label, y1, y2, options={}) {
|
|||||||
dy: FONT_SIZE + 'px',
|
dy: FONT_SIZE + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': 'middle',
|
'text-anchor': 'middle',
|
||||||
innerHTML: label + ""
|
innerHTML: label + ''
|
||||||
});
|
});
|
||||||
|
|
||||||
let line = createSVG('g', {
|
let line = createSVG('g', {
|
||||||
transform: `translate(${ x }, 0)`
|
transform: `translate(${x}, 0)`
|
||||||
});
|
});
|
||||||
|
|
||||||
line.appendChild(l);
|
line.appendChild(l);
|
||||||
@ -319,13 +372,16 @@ function makeVertLine(x, label, y1, y2, options={}) {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeHoriLine(y, label, x1, x2, options={}) {
|
function makeHoriLine(y, label, x1, x2, options = {}) {
|
||||||
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
if (!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
if(!options.lineType) options.lineType = '';
|
if (!options.lineType) options.lineType = '';
|
||||||
|
if (!options.alignment) options.alignment = 'left';
|
||||||
if (options.shortenNumbers) label = shortenLargeNumber(label);
|
if (options.shortenNumbers) label = shortenLargeNumber(label);
|
||||||
|
|
||||||
let className = 'line-horizontal ' + options.className +
|
let className =
|
||||||
(options.lineType === "dashed" ? "dashed": "");
|
'line-horizontal ' +
|
||||||
|
options.className +
|
||||||
|
(options.lineType === 'dashed' ? 'dashed' : '');
|
||||||
|
|
||||||
let l = createSVG('line', {
|
let l = createSVG('line', {
|
||||||
className: className,
|
className: className,
|
||||||
@ -339,12 +395,12 @@ function makeHoriLine(y, label, x1, x2, options={}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let text = createSVG('text', {
|
let text = createSVG('text', {
|
||||||
x: x1 < x2 ? x1 - LABEL_MARGIN : x1 + LABEL_MARGIN,
|
x: options.alignment === 'left' ? x1 - LABEL_MARGIN : x2 + LABEL_MARGIN * 4,
|
||||||
y: 0,
|
y: 0,
|
||||||
dy: (FONT_SIZE / 2 - 2) + 'px',
|
dy: FONT_SIZE / 2 - 2 + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': x1 < x2 ? 'end' : 'start',
|
'text-anchor': x1 < x2 ? 'end' : 'start',
|
||||||
innerHTML: label+""
|
innerHTML: label + ''
|
||||||
});
|
});
|
||||||
|
|
||||||
let line = createSVG('g', {
|
let line = createSVG('g', {
|
||||||
@ -352,8 +408,8 @@ function makeHoriLine(y, label, x1, x2, options={}) {
|
|||||||
'stroke-opacity': 1
|
'stroke-opacity': 1
|
||||||
});
|
});
|
||||||
|
|
||||||
if(text === 0 || text === '0') {
|
if (text === 0 || text === '0') {
|
||||||
line.style.stroke = "rgba(27, 31, 35, 0.6)";
|
line.style.stroke = 'rgba(27, 31, 35, 0.6)';
|
||||||
}
|
}
|
||||||
|
|
||||||
line.appendChild(l);
|
line.appendChild(l);
|
||||||
@ -362,44 +418,69 @@ function makeHoriLine(y, label, x1, x2, options={}) {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function yLine(y, label, width, options={}) {
|
export function generateAxisLabel(options) {
|
||||||
|
if (!options.title) return;
|
||||||
|
|
||||||
|
const x = options.position === 'left' ? LABEL_MARGIN : options.width;
|
||||||
|
// - getStringWidth(options.title, 5);
|
||||||
|
const rotation =
|
||||||
|
options.position === 'right'
|
||||||
|
? `rotate(90, ${options.width}, ${options.height / 2})`
|
||||||
|
: `rotate(270, 0, ${options.height / 2})`;
|
||||||
|
|
||||||
|
const labelSvg = createSVG('text', {
|
||||||
|
className: 'chart-label',
|
||||||
|
x: x - getStringWidth(options.title, 5) / 2,
|
||||||
|
y: options.height / 2 - LABEL_MARGIN,
|
||||||
|
dy: FONT_SIZE / -2 + 'px',
|
||||||
|
'font-size': FONT_SIZE + 'px',
|
||||||
|
'text-anchor': 'start',
|
||||||
|
transform: rotation,
|
||||||
|
innerHTML: options.title + ''
|
||||||
|
});
|
||||||
|
|
||||||
|
return labelSvg;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function yLine(y, label, width, options = {}) {
|
||||||
if (!isValidNumber(y)) y = 0;
|
if (!isValidNumber(y)) y = 0;
|
||||||
|
|
||||||
if(!options.pos) options.pos = 'left';
|
if (!options.pos) options.pos = 'left';
|
||||||
if(!options.offset) options.offset = 0;
|
if (!options.offset) options.offset = 0;
|
||||||
if(!options.mode) options.mode = 'span';
|
if (!options.mode) options.mode = 'span';
|
||||||
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
if (!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
if(!options.className) options.className = '';
|
if (!options.className) options.className = '';
|
||||||
|
|
||||||
let x1 = -1 * AXIS_TICK_LENGTH;
|
let x1 = -1 * AXIS_TICK_LENGTH;
|
||||||
let x2 = options.mode === 'span' ? width + AXIS_TICK_LENGTH : 0;
|
let x2 = options.mode === 'span' ? width + AXIS_TICK_LENGTH : 0;
|
||||||
|
|
||||||
if(options.mode === 'tick' && options.pos === 'right') {
|
if (options.mode === 'tick' && options.pos === 'right') {
|
||||||
x1 = width + AXIS_TICK_LENGTH;
|
x1 = width + AXIS_TICK_LENGTH;
|
||||||
x2 = width;
|
x2 = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
// let offset = options.pos === 'left' ? -1 * options.offset : options.offset;
|
let offset = options.pos === 'left' ? -1 * options.offset : options.offset;
|
||||||
|
|
||||||
x1 += options.offset;
|
x1 += offset;
|
||||||
x2 += options.offset;
|
x2 += offset;
|
||||||
|
|
||||||
return makeHoriLine(y, label, x1, x2, {
|
return makeHoriLine(y, label, x1, x2, {
|
||||||
stroke: options.stroke,
|
stroke: options.stroke,
|
||||||
className: options.className,
|
className: options.className,
|
||||||
lineType: options.lineType,
|
lineType: options.lineType,
|
||||||
|
alignment: options.pos,
|
||||||
shortenNumbers: options.shortenNumbers
|
shortenNumbers: options.shortenNumbers
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function xLine(x, label, height, options={}) {
|
export function xLine(x, label, height, options = {}) {
|
||||||
if (!isValidNumber(x)) x = 0;
|
if (!isValidNumber(x)) x = 0;
|
||||||
|
|
||||||
if(!options.pos) options.pos = 'bottom';
|
if (!options.pos) options.pos = 'bottom';
|
||||||
if(!options.offset) options.offset = 0;
|
if (!options.offset) options.offset = 0;
|
||||||
if(!options.mode) options.mode = 'span';
|
if (!options.mode) options.mode = 'span';
|
||||||
if(!options.stroke) options.stroke = BASE_LINE_COLOR;
|
if (!options.stroke) options.stroke = BASE_LINE_COLOR;
|
||||||
if(!options.className) options.className = '';
|
if (!options.className) options.className = '';
|
||||||
|
|
||||||
// Draw X axis line in span/tick mode with optional label
|
// Draw X axis line in span/tick mode with optional label
|
||||||
// y2(span)
|
// y2(span)
|
||||||
@ -415,7 +496,7 @@ export function xLine(x, label, height, options={}) {
|
|||||||
let y1 = height + AXIS_TICK_LENGTH;
|
let y1 = height + AXIS_TICK_LENGTH;
|
||||||
let y2 = options.mode === 'span' ? -1 * AXIS_TICK_LENGTH : height;
|
let y2 = options.mode === 'span' ? -1 * AXIS_TICK_LENGTH : height;
|
||||||
|
|
||||||
if(options.mode === 'tick' && options.pos === 'top') {
|
if (options.mode === 'tick' && options.pos === 'top') {
|
||||||
// top axis ticks
|
// top axis ticks
|
||||||
y1 = -1 * AXIS_TICK_LENGTH;
|
y1 = -1 * AXIS_TICK_LENGTH;
|
||||||
y2 = 0;
|
y2 = 0;
|
||||||
@ -428,19 +509,21 @@ export function xLine(x, label, height, options={}) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function yMarker(y, label, width, options={}) {
|
export function yMarker(y, label, width, options = {}) {
|
||||||
if(!options.labelPos) options.labelPos = 'right';
|
if (!options.labelPos) options.labelPos = 'right';
|
||||||
let x = options.labelPos === 'left' ? LABEL_MARGIN
|
let x =
|
||||||
|
options.labelPos === 'left'
|
||||||
|
? LABEL_MARGIN
|
||||||
: width - getStringWidth(label, 5) - LABEL_MARGIN;
|
: width - getStringWidth(label, 5) - LABEL_MARGIN;
|
||||||
|
|
||||||
let labelSvg = createSVG('text', {
|
let labelSvg = createSVG('text', {
|
||||||
className: 'chart-label',
|
className: 'chart-label',
|
||||||
x: x,
|
x: x,
|
||||||
y: 0,
|
y: 0,
|
||||||
dy: (FONT_SIZE / -2) + 'px',
|
dy: FONT_SIZE / -2 + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': 'start',
|
'text-anchor': 'start',
|
||||||
innerHTML: label+""
|
innerHTML: label + ''
|
||||||
});
|
});
|
||||||
|
|
||||||
let line = makeHoriLine(y, '', 0, width, {
|
let line = makeHoriLine(y, '', 0, width, {
|
||||||
@ -454,7 +537,7 @@ export function yMarker(y, label, width, options={}) {
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function yRegion(y1, y2, width, label, options={}) {
|
export function yRegion(y1, y2, width, label, options = {}) {
|
||||||
// return a group
|
// return a group
|
||||||
let height = y1 - y2;
|
let height = y1 - y2;
|
||||||
|
|
||||||
@ -472,18 +555,20 @@ export function yRegion(y1, y2, width, label, options={}) {
|
|||||||
height: height
|
height: height
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!options.labelPos) options.labelPos = 'right';
|
if (!options.labelPos) options.labelPos = 'right';
|
||||||
let x = options.labelPos === 'left' ? LABEL_MARGIN
|
let x =
|
||||||
: width - getStringWidth(label+"", 4.5) - LABEL_MARGIN;
|
options.labelPos === 'left'
|
||||||
|
? LABEL_MARGIN
|
||||||
|
: width - getStringWidth(label + '', 4.5) - LABEL_MARGIN;
|
||||||
|
|
||||||
let labelSvg = createSVG('text', {
|
let labelSvg = createSVG('text', {
|
||||||
className: 'chart-label',
|
className: 'chart-label',
|
||||||
x: x,
|
x: x,
|
||||||
y: 0,
|
y: 0,
|
||||||
dy: (FONT_SIZE / -2) + 'px',
|
dy: FONT_SIZE / -2 + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': 'start',
|
'text-anchor': 'start',
|
||||||
innerHTML: label+""
|
innerHTML: label + ''
|
||||||
});
|
});
|
||||||
|
|
||||||
let region = createSVG('g', {
|
let region = createSVG('g', {
|
||||||
@ -496,11 +581,20 @@ export function yRegion(y1, y2, width, label, options={}) {
|
|||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, meta={}) {
|
export function datasetBar(
|
||||||
|
x,
|
||||||
|
yTop,
|
||||||
|
width,
|
||||||
|
color,
|
||||||
|
label = '',
|
||||||
|
index = 0,
|
||||||
|
offset = 0,
|
||||||
|
meta = {}
|
||||||
|
) {
|
||||||
let [height, y] = getBarHeightAndYAttr(yTop, meta.zeroLine);
|
let [height, y] = getBarHeightAndYAttr(yTop, meta.zeroLine);
|
||||||
y -= offset;
|
y -= offset;
|
||||||
|
|
||||||
if(height === 0) {
|
if (height === 0) {
|
||||||
height = meta.minHeight;
|
height = meta.minHeight;
|
||||||
y -= meta.minHeight;
|
y -= meta.minHeight;
|
||||||
}
|
}
|
||||||
@ -521,18 +615,18 @@ export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, m
|
|||||||
height: height
|
height: height
|
||||||
});
|
});
|
||||||
|
|
||||||
label += "";
|
label += '';
|
||||||
|
|
||||||
if(!label && !label.length) {
|
if (!label && !label.length) {
|
||||||
return rect;
|
return rect;
|
||||||
} else {
|
} else {
|
||||||
rect.setAttribute('y', 0);
|
rect.setAttribute('y', 0);
|
||||||
rect.setAttribute('x', 0);
|
rect.setAttribute('x', 0);
|
||||||
let text = createSVG('text', {
|
let text = createSVG('text', {
|
||||||
className: 'data-point-value',
|
className: 'data-point-value',
|
||||||
x: width/2,
|
x: width / 2,
|
||||||
y: 0,
|
y: 0,
|
||||||
dy: (FONT_SIZE / 2 * -1) + 'px',
|
dy: (FONT_SIZE / 2) * -1 + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': 'middle',
|
'text-anchor': 'middle',
|
||||||
innerHTML: label
|
innerHTML: label
|
||||||
@ -549,7 +643,7 @@ export function datasetBar(x, yTop, width, color, label='', index=0, offset=0, m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function datasetDot(x, y, radius, color, label='', index=0) {
|
export function datasetDot(x, y, radius, color, label = '', index = 0) {
|
||||||
let dot = createSVG('circle', {
|
let dot = createSVG('circle', {
|
||||||
style: `fill: ${color}`,
|
style: `fill: ${color}`,
|
||||||
'data-point-index': index,
|
'data-point-index': index,
|
||||||
@ -558,9 +652,9 @@ export function datasetDot(x, y, radius, color, label='', index=0) {
|
|||||||
r: radius
|
r: radius
|
||||||
});
|
});
|
||||||
|
|
||||||
label += "";
|
label += '';
|
||||||
|
|
||||||
if(!label && !label.length) {
|
if (!label && !label.length) {
|
||||||
return dot;
|
return dot;
|
||||||
} else {
|
} else {
|
||||||
dot.setAttribute('cy', 0);
|
dot.setAttribute('cy', 0);
|
||||||
@ -570,7 +664,7 @@ export function datasetDot(x, y, radius, color, label='', index=0) {
|
|||||||
className: 'data-point-value',
|
className: 'data-point-value',
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
dy: (FONT_SIZE / 2 * -1 - radius) + 'px',
|
dy: (FONT_SIZE / 2) * -1 - radius + 'px',
|
||||||
'font-size': FONT_SIZE + 'px',
|
'font-size': FONT_SIZE + 'px',
|
||||||
'text-anchor': 'middle',
|
'text-anchor': 'middle',
|
||||||
innerHTML: label
|
innerHTML: label
|
||||||
@ -587,18 +681,17 @@ export function datasetDot(x, y, radius, color, label='', index=0) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPaths(xList, yList, color, options={}, meta={}) {
|
export function getPaths(xList, yList, color, options = {}, meta = {}) {
|
||||||
let pointsList = yList.map((y, i) => (xList[i] + ',' + y));
|
let pointsList = yList.map((y, i) => xList[i] + ',' + y);
|
||||||
let pointsStr = pointsList.join("L");
|
let pointsStr = pointsList.join('L');
|
||||||
|
|
||||||
// Spline
|
// Spline
|
||||||
if (options.spline)
|
if (options.spline) pointsStr = getSplineCurvePointsStr(xList, yList);
|
||||||
pointsStr = getSplineCurvePointsStr(xList, yList);
|
|
||||||
|
|
||||||
let path = makePath("M"+pointsStr, 'line-graph-path', color);
|
let path = makePath('M' + pointsStr, 'line-graph-path', color);
|
||||||
|
|
||||||
// HeatLine
|
// HeatLine
|
||||||
if(options.heatline) {
|
if (options.heatline) {
|
||||||
let gradient_id = makeGradient(meta.svgDefs, color);
|
let gradient_id = makeGradient(meta.svgDefs, color);
|
||||||
path.style.stroke = `url(#${gradient_id})`;
|
path.style.stroke = `url(#${gradient_id})`;
|
||||||
}
|
}
|
||||||
@ -608,10 +701,14 @@ export function getPaths(xList, yList, color, options={}, meta={}) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Region
|
// Region
|
||||||
if(options.regionFill) {
|
if (options.regionFill) {
|
||||||
let gradient_id_region = makeGradient(meta.svgDefs, color, true);
|
let gradient_id_region = makeGradient(meta.svgDefs, color, true);
|
||||||
|
|
||||||
let pathStr = "M" + `${xList[0]},${meta.zeroLine}L` + pointsStr + `L${xList.slice(-1)[0]},${meta.zeroLine}`;
|
let pathStr =
|
||||||
|
'M' +
|
||||||
|
`${xList[0]},${meta.zeroLine}L` +
|
||||||
|
pointsStr +
|
||||||
|
`L${xList.slice(-1)[0]},${meta.zeroLine}`;
|
||||||
paths.region = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id_region})`);
|
paths.region = makePath(pathStr, `region-fill`, 'none', `url(#${gradient_id_region})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,9 +716,9 @@ export function getPaths(xList, yList, color, options={}, meta={}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export let makeOverlay = {
|
export let makeOverlay = {
|
||||||
'bar': (unit) => {
|
bar: (unit) => {
|
||||||
let transformValue;
|
let transformValue;
|
||||||
if(unit.nodeName !== 'rect') {
|
if (unit.nodeName !== 'rect') {
|
||||||
transformValue = unit.getAttribute('transform');
|
transformValue = unit.getAttribute('transform');
|
||||||
unit = unit.childNodes[0];
|
unit = unit.childNodes[0];
|
||||||
}
|
}
|
||||||
@ -629,15 +726,15 @@ export let makeOverlay = {
|
|||||||
overlay.style.fill = '#000000';
|
overlay.style.fill = '#000000';
|
||||||
overlay.style.opacity = '0.4';
|
overlay.style.opacity = '0.4';
|
||||||
|
|
||||||
if(transformValue) {
|
if (transformValue) {
|
||||||
overlay.setAttribute('transform', transformValue);
|
overlay.setAttribute('transform', transformValue);
|
||||||
}
|
}
|
||||||
return overlay;
|
return overlay;
|
||||||
},
|
},
|
||||||
|
|
||||||
'dot': (unit) => {
|
dot: (unit) => {
|
||||||
let transformValue;
|
let transformValue;
|
||||||
if(unit.nodeName !== 'circle') {
|
if (unit.nodeName !== 'circle') {
|
||||||
transformValue = unit.getAttribute('transform');
|
transformValue = unit.getAttribute('transform');
|
||||||
unit = unit.childNodes[0];
|
unit = unit.childNodes[0];
|
||||||
}
|
}
|
||||||
@ -648,15 +745,15 @@ export let makeOverlay = {
|
|||||||
overlay.setAttribute('fill', fill);
|
overlay.setAttribute('fill', fill);
|
||||||
overlay.style.opacity = '0.6';
|
overlay.style.opacity = '0.6';
|
||||||
|
|
||||||
if(transformValue) {
|
if (transformValue) {
|
||||||
overlay.setAttribute('transform', transformValue);
|
overlay.setAttribute('transform', transformValue);
|
||||||
}
|
}
|
||||||
return overlay;
|
return overlay;
|
||||||
},
|
},
|
||||||
|
|
||||||
'heat_square': (unit) => {
|
heat_square: (unit) => {
|
||||||
let transformValue;
|
let transformValue;
|
||||||
if(unit.nodeName !== 'circle') {
|
if (unit.nodeName !== 'circle') {
|
||||||
transformValue = unit.getAttribute('transform');
|
transformValue = unit.getAttribute('transform');
|
||||||
unit = unit.childNodes[0];
|
unit = unit.childNodes[0];
|
||||||
}
|
}
|
||||||
@ -667,7 +764,7 @@ export let makeOverlay = {
|
|||||||
overlay.setAttribute('fill', fill);
|
overlay.setAttribute('fill', fill);
|
||||||
overlay.style.opacity = '0.6';
|
overlay.style.opacity = '0.6';
|
||||||
|
|
||||||
if(transformValue) {
|
if (transformValue) {
|
||||||
overlay.setAttribute('transform', transformValue);
|
overlay.setAttribute('transform', transformValue);
|
||||||
}
|
}
|
||||||
return overlay;
|
return overlay;
|
||||||
@ -675,57 +772,57 @@ export let makeOverlay = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export let updateOverlay = {
|
export let updateOverlay = {
|
||||||
'bar': (unit, overlay) => {
|
bar: (unit, overlay) => {
|
||||||
let transformValue;
|
let transformValue;
|
||||||
if(unit.nodeName !== 'rect') {
|
if (unit.nodeName !== 'rect') {
|
||||||
transformValue = unit.getAttribute('transform');
|
transformValue = unit.getAttribute('transform');
|
||||||
unit = unit.childNodes[0];
|
unit = unit.childNodes[0];
|
||||||
}
|
}
|
||||||
let attributes = ['x', 'y', 'width', 'height'];
|
let attributes = ['x', 'y', 'width', 'height'];
|
||||||
Object.values(unit.attributes)
|
Object.values(unit.attributes)
|
||||||
.filter(attr => attributes.includes(attr.name) && attr.specified)
|
.filter((attr) => attributes.includes(attr.name) && attr.specified)
|
||||||
.map(attr => {
|
.map((attr) => {
|
||||||
overlay.setAttribute(attr.name, attr.nodeValue);
|
overlay.setAttribute(attr.name, attr.nodeValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(transformValue) {
|
if (transformValue) {
|
||||||
overlay.setAttribute('transform', transformValue);
|
overlay.setAttribute('transform', transformValue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'dot': (unit, overlay) => {
|
dot: (unit, overlay) => {
|
||||||
let transformValue;
|
let transformValue;
|
||||||
if(unit.nodeName !== 'circle') {
|
if (unit.nodeName !== 'circle') {
|
||||||
transformValue = unit.getAttribute('transform');
|
transformValue = unit.getAttribute('transform');
|
||||||
unit = unit.childNodes[0];
|
unit = unit.childNodes[0];
|
||||||
}
|
}
|
||||||
let attributes = ['cx', 'cy'];
|
let attributes = ['cx', 'cy'];
|
||||||
Object.values(unit.attributes)
|
Object.values(unit.attributes)
|
||||||
.filter(attr => attributes.includes(attr.name) && attr.specified)
|
.filter((attr) => attributes.includes(attr.name) && attr.specified)
|
||||||
.map(attr => {
|
.map((attr) => {
|
||||||
overlay.setAttribute(attr.name, attr.nodeValue);
|
overlay.setAttribute(attr.name, attr.nodeValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(transformValue) {
|
if (transformValue) {
|
||||||
overlay.setAttribute('transform', transformValue);
|
overlay.setAttribute('transform', transformValue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
'heat_square': (unit, overlay) => {
|
heat_square: (unit, overlay) => {
|
||||||
let transformValue;
|
let transformValue;
|
||||||
if(unit.nodeName !== 'circle') {
|
if (unit.nodeName !== 'circle') {
|
||||||
transformValue = unit.getAttribute('transform');
|
transformValue = unit.getAttribute('transform');
|
||||||
unit = unit.childNodes[0];
|
unit = unit.childNodes[0];
|
||||||
}
|
}
|
||||||
let attributes = ['cx', 'cy'];
|
let attributes = ['cx', 'cy'];
|
||||||
Object.values(unit.attributes)
|
Object.values(unit.attributes)
|
||||||
.filter(attr => attributes.includes(attr.name) && attr.specified)
|
.filter((attr) => attributes.includes(attr.name) && attr.specified)
|
||||||
.map(attr => {
|
.map((attr) => {
|
||||||
overlay.setAttribute(attr.name, attr.nodeValue);
|
overlay.setAttribute(attr.name, attr.nodeValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(transformValue) {
|
if (transformValue) {
|
||||||
overlay.setAttribute('transform', transformValue);
|
overlay.setAttribute('transform', transformValue);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user