181 lines
5.5 KiB
JavaScript
181 lines
5.5 KiB
JavaScript
/**
|
|
* Charty a circle & pie chart small library.
|
|
* @author: Mario Abreu
|
|
*/
|
|
|
|
/** @class Charty representing a chart*/
|
|
class Charty{
|
|
/**
|
|
* Creates an instance of Charty.
|
|
*
|
|
*
|
|
*
|
|
* @constructor
|
|
* @param {string} title The title of the chart
|
|
* @param {string} chartType The type of chart circle|pie
|
|
* @param {array} data An array of objects in the pair format: name/vale {name: value}
|
|
* @param {boolean} precision Determines wheter the chart have decimal precision or not
|
|
* @param {string} selector The html element where the chart will be insert, must be a valid css selector
|
|
*/
|
|
constructor({title = 'New Chart', chartType = 'circle', data = [{Test: 5}], precision = false, selector = 'body' }) {
|
|
this.chartType = chartType;
|
|
this.title = title;
|
|
this.precision = precision;
|
|
this.selector = selector;
|
|
this.id = `${Math.floor(Math.random()*10000)}`;
|
|
this.data = [...data];
|
|
this.percents = [];
|
|
this.angles = [];
|
|
|
|
this.calculatePercents(this.data);
|
|
this.draw();
|
|
}
|
|
|
|
/**
|
|
* Insert new data in a chart
|
|
*
|
|
* @param {string} name The name of the data to insert
|
|
* @param {int} value The value of the data to insert
|
|
*/
|
|
addData({ name, value }) {
|
|
const newData = Object.fromEntries([[name, value]]);
|
|
this.data = [...this.data, newData];
|
|
this.calculatePercents(this.data);
|
|
this.draw();
|
|
};
|
|
|
|
/**
|
|
* Calculate the individual percentage of every data
|
|
*
|
|
* @param {array} data An array of the initial values for a chart
|
|
*/
|
|
calculatePercents(data) {
|
|
const totalItems = data.length;
|
|
const totalCount = data.reduce((acc, obj) => acc + +Object.values(obj), 0);
|
|
|
|
this.percents = [
|
|
...this.data.map(obj =>{
|
|
if(!this.precision){
|
|
return Math.round((+Object.values(obj) * 100) / totalCount);
|
|
}
|
|
else{
|
|
return ((+Object.values(obj) * 100) / totalCount).toFixed(2);
|
|
}
|
|
})
|
|
];
|
|
|
|
this.calculateAngles(this.percents);
|
|
};
|
|
|
|
/**
|
|
* Calculate the angles of the gradient for every data
|
|
*
|
|
* @param {array} data An array of the data in the chart
|
|
*/
|
|
calculateAngles(data) {
|
|
this.angles = [...data.map(value => Math.floor((value * 360) / 100))];
|
|
for (let i = 1; i < this.angles.length; ++i) {
|
|
this.angles[i] += this.angles[i - 1];
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Return a color for a piece of the chart
|
|
*
|
|
* @param {int} index A integer to get a color
|
|
* @return {string} Th color in the array to return
|
|
*/
|
|
getColor(index) {
|
|
const colors = [
|
|
'#032DA1',
|
|
'#215AF9',
|
|
'#4A78F9',
|
|
'#A4BCFF'
|
|
];
|
|
return colors[index];
|
|
};
|
|
|
|
/**
|
|
* Draw a chart and insert it in a element of the DOM
|
|
*
|
|
*/
|
|
draw() {
|
|
const selector = this.selector;
|
|
let gradient;
|
|
if (this.angles.length !== 1) {
|
|
gradient = this.angles.reduce(
|
|
(acc, curr, index) =>
|
|
acc +
|
|
`${this.getColor(index)} ${curr}deg, ${this.getColor(
|
|
index + 1
|
|
)} ${curr}deg, `,
|
|
''
|
|
);
|
|
let tempGradient = gradient.split(', ');
|
|
tempGradient = tempGradient.slice(0, tempGradient.length - 2);
|
|
gradient = tempGradient.join(', ');
|
|
}
|
|
else {
|
|
gradient = `${this.getColor(0)} 0deg, ${this.getColor(0)} 360deg`;
|
|
}
|
|
|
|
const chart = document.createElement('DIV');
|
|
const title = document.createElement('H2');
|
|
const chartGraph = document.createElement('DIV');
|
|
const chartData = document.createElement('DIV');
|
|
const chartPercents = document.createElement('P');
|
|
|
|
chart.setAttribute('class', 'chart');
|
|
title.setAttribute('class', 'chart__title');
|
|
title.textContent = this.title;
|
|
chartGraph.setAttribute('class', `chart__graph chart__${this.chartType}`);
|
|
chartGraph.style.backgroundImage = `conic-gradient(${gradient})`;
|
|
chartData.setAttribute('class', 'chart__data');
|
|
chartPercents.setAttribute('class', 'chart__percents');
|
|
|
|
this.percents.forEach((percent, index) => {
|
|
const chartPercent = document.createElement('SPAN');
|
|
const chartPiece = document.createElement('I');
|
|
chartPercent.textContent = `${percent}%`;
|
|
chartPercent.setAttribute('class', 'chart__percent');
|
|
chartPiece.setAttribute('class', 'chart__piece');
|
|
chartPiece.style.backgroundColor = this.getColor(index);
|
|
chartPercent.appendChild(chartPiece);
|
|
chartPercents.appendChild(chartPercent);
|
|
});
|
|
chartData.appendChild(chartPercents);
|
|
|
|
this.data.forEach((data, index) => {
|
|
const chartDataRow = document.createElement('P');
|
|
const chartDataName = document.createElement('SPAN');
|
|
const chartDataValue = document.createElement('SPAN');
|
|
const chartPiece = document.createElement('I');
|
|
chartDataRow.setAttribute('class', 'chart__dataRow');
|
|
chartDataName.setAttribute('class', 'chart__dataName');
|
|
chartDataValue.setAttribute('class', 'chart__dataValue');
|
|
chartDataName.textContent = Object.keys(data)[0];
|
|
chartDataValue.textContent = Object.values(data)[0];
|
|
chartPiece.setAttribute('class', 'chart__piece');
|
|
chartPiece.style.backgroundColor = this.getColor(index);
|
|
chartDataValue.appendChild(chartPiece);
|
|
chartDataRow.appendChild(chartDataName);
|
|
chartDataRow.appendChild(chartDataValue);
|
|
chartData.appendChild(chartDataRow);
|
|
});
|
|
|
|
chart.appendChild(title);
|
|
chart.appendChild(chartGraph);
|
|
chart.appendChild(chartData);
|
|
chart.dataset.chartId = this.id;
|
|
|
|
const existId = document.querySelector(`[data-chart-id="${this.id}"]`);
|
|
if(existId){
|
|
existId.parentNode.replaceChild(chart, existId);
|
|
}
|
|
else
|
|
{
|
|
document.querySelector(selector).appendChild(chart);
|
|
}
|
|
};
|
|
}
|