BBnepal-Accounts/hostel/assets-hms/js/charty.js
Sampanna Rimal 9cd05ef3cb commitall
2024-07-10 18:28:19 +05:45

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