|
- import { makeSVGGroup } from '../utils/draw';
- import { makeText, makePath, xLine, yLine, yMarker, yRegion, datasetBar, datasetDot, percentageBar, getPaths, heatSquare } from '../utils/draw';
- import { equilizeNoOfElements } from '../utils/draw-utils';
- import { translateHoriLine, translateVertLine, animateRegion, animateBar,
- animateDot, animatePath, animatePathStr } from '../utils/animate';
- import { getMonthName } from '../utils/date-utils';
-
- class ChartComponent {
- constructor({
- layerClass = '',
- layerTransform = '',
- constants,
-
- getData,
- makeElements,
- animateElements
- }) {
- this.layerTransform = layerTransform;
- this.constants = constants;
-
- this.makeElements = makeElements;
- this.getData = getData;
-
- this.animateElements = animateElements;
-
- this.store = [];
- this.labels = [];
-
- this.layerClass = layerClass;
- this.layerClass = typeof(this.layerClass) === 'function'
- ? this.layerClass() : this.layerClass;
-
- this.refresh();
- }
-
- refresh(data) {
- this.data = data || this.getData();
- }
-
- setup(parent) {
- this.layer = makeSVGGroup(this.layerClass, this.layerTransform, parent);
- }
-
- make() {
- this.render(this.data);
- this.oldData = this.data;
- }
-
- render(data) {
- this.store = this.makeElements(data);
-
- this.layer.textContent = '';
- this.store.forEach(element => {
- this.layer.appendChild(element);
- });
- this.labels.forEach(element => {
- this.layer.appendChild(element);
- });
- }
-
- update(animate = true) {
- this.refresh();
- let animateElements = [];
- if(animate) {
- animateElements = this.animateElements(this.data) || [];
- }
- return animateElements;
- }
- }
-
- let componentConfigs = {
- donutSlices: {
- layerClass: 'donut-slices',
- makeElements(data) {
- return data.sliceStrings.map((s, i) => {
- let slice = makePath(s, 'donut-path', data.colors[i], 'none', data.strokeWidth);
- slice.style.transition = 'transform .3s;';
- return slice;
- });
- },
-
- animateElements(newData) {
- return this.store.map((slice, i) => animatePathStr(slice, newData.sliceStrings[i]));
- },
- },
- pieSlices: {
- layerClass: 'pie-slices',
- makeElements(data) {
- return data.sliceStrings.map((s, i) =>{
- let slice = makePath(s, 'pie-path', 'none', data.colors[i]);
- slice.style.transition = 'transform .3s;';
- return slice;
- });
- },
-
- animateElements(newData) {
- return this.store.map((slice, i) =>
- animatePathStr(slice, newData.sliceStrings[i])
- );
- }
- },
- percentageBars: {
- layerClass: 'percentage-bars',
- makeElements(data) {
- return data.xPositions.map((x, i) =>{
- let y = 0;
- let bar = percentageBar(x, y, data.widths[i],
- this.constants.barHeight, this.constants.barDepth, data.colors[i]);
- return bar;
- });
- },
-
- animateElements(newData) {
- if(newData) return [];
- }
- },
- yAxis: {
- layerClass: 'y axis',
- makeElements(data) {
- return data.positions.map((position, i) =>
- yLine(position, data.labels[i], this.constants.width,
- {mode: this.constants.mode, pos: this.constants.pos, shortenNumbers: this.constants.shortenNumbers})
- );
- },
-
- animateElements(newData) {
- let newPos = newData.positions;
- let newLabels = newData.labels;
- let oldPos = this.oldData.positions;
- let oldLabels = this.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]
- );
- });
- }
- },
-
- xAxis: {
- layerClass: 'x axis',
- makeElements(data) {
- return data.positions.map((position, i) =>
- xLine(position, data.calcLabels[i], this.constants.height,
- {mode: this.constants.mode, pos: this.constants.pos})
- );
- },
-
- animateElements(newData) {
- let newPos = newData.positions;
- let newLabels = newData.calcLabels;
- let oldPos = this.oldData.positions;
- let oldLabels = this.oldData.calcLabels;
-
- [oldPos, newPos] = equilizeNoOfElements(oldPos, newPos);
- [oldLabels, newLabels] = equilizeNoOfElements(oldLabels, newLabels);
-
- this.render({
- positions: oldPos,
- calcLabels: newLabels
- });
-
- return this.store.map((line, i) => {
- return translateVertLine(
- line, newPos[i], oldPos[i]
- );
- });
- }
- },
-
- yMarkers: {
- layerClass: 'y-markers',
- makeElements(data) {
- return data.map(m =>
- yMarker(m.position, m.label, this.constants.width,
- {labelPos: m.options.labelPos, mode: 'span', lineType: 'dashed'})
- );
- },
- animateElements(newData) {
- [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
-
- let newPos = newData.map(d => d.position);
- let newLabels = newData.map(d => d.label);
- let newOptions = newData.map(d => d.options);
-
- let oldPos = this.oldData.map(d => d.position);
-
- this.render(oldPos.map((pos, i) => {
- return {
- position: oldPos[i],
- label: newLabels[i],
- options: newOptions[i]
- };
- }));
-
- return this.store.map((line, i) => {
- return translateHoriLine(
- line, newPos[i], oldPos[i]
- );
- });
- }
- },
-
- yRegions: {
- layerClass: 'y-regions',
- makeElements(data) {
- return data.map(r =>
- yRegion(r.startPos, r.endPos, this.constants.width,
- r.label, {labelPos: r.options.labelPos})
- );
- },
- animateElements(newData) {
- [this.oldData, newData] = equilizeNoOfElements(this.oldData, newData);
-
- let newPos = newData.map(d => d.endPos);
- let newLabels = newData.map(d => d.label);
- let newStarts = newData.map(d => d.startPos);
- let newOptions = newData.map(d => d.options);
-
- let oldPos = this.oldData.map(d => d.endPos);
- let oldStarts = this.oldData.map(d => d.startPos);
-
- this.render(oldPos.map((pos, i) => {
- return {
- startPos: oldStarts[i],
- endPos: oldPos[i],
- label: newLabels[i],
- options: newOptions[i]
- };
- }));
-
- let animateElements = [];
-
- this.store.map((rectGroup, i) => {
- animateElements = animateElements.concat(animateRegion(
- rectGroup, newStarts[i], newPos[i], oldPos[i]
- ));
- });
-
- return animateElements;
- }
- },
-
- heatDomain: {
- layerClass: function() { return 'heat-domain domain-' + this.constants.index; },
- makeElements(data) {
- let {index, colWidth, rowHeight, squareSize, radius, xTranslate} = this.constants;
- let monthNameHeight = -12;
- let x = xTranslate, y = 0;
-
- this.serializedSubDomains = [];
-
- data.cols.map((week, weekNo) => {
- if(weekNo === 1) {
- this.labels.push(
- makeText('domain-name', x, monthNameHeight, getMonthName(index, true).toUpperCase(),
- {
- fontSize: 9
- }
- )
- );
- }
- week.map((day, i) => {
- if(day.fill) {
- let data = {
- 'data-date': day.yyyyMmDd,
- 'data-value': day.dataValue,
- 'data-day': i
- };
- let square = heatSquare('day', x, y, squareSize, radius, day.fill, data);
- this.serializedSubDomains.push(square);
- }
- y += rowHeight;
- });
- y = 0;
- x += colWidth;
- });
-
- return this.serializedSubDomains;
- },
-
- animateElements(newData) {
- if(newData) return [];
- }
- },
-
- barGraph: {
- layerClass: function() { return 'dataset-units dataset-bars dataset-' + this.constants.index; },
- makeElements(data) {
- let c = this.constants;
- this.unitType = 'bar';
- this.units = data.yPositions.map((y, j) => {
- return datasetBar(
- data.xPositions[j],
- y,
- data.barWidth,
- c.color,
- data.labels[j],
- j,
- data.offsets[j],
- {
- zeroLine: data.zeroLine,
- barsWidth: data.barsWidth,
- minHeight: c.minHeight
- }
- );
- });
- return this.units;
- },
- animateElements(newData) {
- let newXPos = newData.xPositions;
- let newYPos = newData.yPositions;
- let newOffsets = newData.offsets;
- let newLabels = newData.labels;
-
- let oldXPos = this.oldData.xPositions;
- let oldYPos = this.oldData.yPositions;
- let oldOffsets = this.oldData.offsets;
- let oldLabels = this.oldData.labels;
-
- [oldXPos, newXPos] = equilizeNoOfElements(oldXPos, newXPos);
- [oldYPos, newYPos] = equilizeNoOfElements(oldYPos, newYPos);
- [oldOffsets, newOffsets] = equilizeNoOfElements(oldOffsets, newOffsets);
- [oldLabels, newLabels] = equilizeNoOfElements(oldLabels, newLabels);
-
- this.render({
- xPositions: oldXPos,
- yPositions: oldYPos,
- offsets: oldOffsets,
- labels: newLabels,
-
- zeroLine: this.oldData.zeroLine,
- barsWidth: this.oldData.barsWidth,
- barWidth: this.oldData.barWidth,
- });
-
- let animateElements = [];
-
- this.store.map((bar, i) => {
- animateElements = animateElements.concat(animateBar(
- bar, newXPos[i], newYPos[i], newData.barWidth, newOffsets[i],
- {zeroLine: newData.zeroLine}
- ));
- });
-
- return animateElements;
- }
- },
-
- lineGraph: {
- layerClass: function() { return 'dataset-units dataset-line dataset-' + this.constants.index; },
- makeElements(data) {
- let c = this.constants;
- this.unitType = 'dot';
- this.paths = {};
- if(!c.hideLine) {
- this.paths = getPaths(
- data.xPositions,
- data.yPositions,
- c.color,
- {
- heatline: c.heatline,
- regionFill: c.regionFill,
- spline: c.spline
- },
- {
- svgDefs: c.svgDefs,
- zeroLine: data.zeroLine
- }
- );
- }
-
- this.units = [];
- if(!c.hideDots) {
- this.units = data.yPositions.map((y, j) => {
- return datasetDot(
- data.xPositions[j],
- y,
- data.radius,
- c.color,
- (c.valuesOverPoints ? data.values[j] : ''),
- j
- );
- });
- }
-
- return Object.values(this.paths).concat(this.units);
- },
- animateElements(newData) {
- let newXPos = newData.xPositions;
- let newYPos = newData.yPositions;
- let newValues = newData.values;
-
- let oldXPos = this.oldData.xPositions;
- let oldYPos = this.oldData.yPositions;
- let oldValues = this.oldData.values;
-
- [oldXPos, newXPos] = equilizeNoOfElements(oldXPos, newXPos);
- [oldYPos, newYPos] = equilizeNoOfElements(oldYPos, newYPos);
- [oldValues, newValues] = equilizeNoOfElements(oldValues, newValues);
-
- this.render({
- xPositions: oldXPos,
- yPositions: oldYPos,
- values: newValues,
-
- zeroLine: this.oldData.zeroLine,
- radius: this.oldData.radius,
- });
-
- let animateElements = [];
-
- if(Object.keys(this.paths).length) {
- animateElements = animateElements.concat(animatePath(
- this.paths, newXPos, newYPos, newData.zeroLine, this.constants.spline));
- }
-
- if(this.units.length) {
- this.units.map((dot, i) => {
- animateElements = animateElements.concat(animateDot(
- dot, newXPos[i], newYPos[i]));
- });
- }
-
- return animateElements;
- }
- }
- };
-
- export function getComponent(name, constants, getData) {
- let keys = Object.keys(componentConfigs).filter(k => name.includes(k));
- let config = componentConfigs[keys[0]];
- Object.assign(config, {
- constants: constants,
- getData: getData
- });
- return new ChartComponent(config);
- }
|