| |
| |
| |
|
|
|
|
|
|
|
|
| export class ChartComponent {
|
| constructor(canvasId, type = 'line', options = {}) {
|
| this.canvasId = canvasId;
|
| this.canvas = document.getElementById(canvasId);
|
| this.type = type;
|
| this.options = options;
|
| this.chart = null;
|
|
|
| if (!this.canvas) {
|
| console.error(`[Chart] Canvas not found: ${canvasId}`);
|
| }
|
| }
|
|
|
| |
| |
|
|
| async create(data, customOptions = {}) {
|
| if (!this.canvas) return;
|
|
|
|
|
| if (typeof Chart === 'undefined') {
|
| console.error('[Chart] Chart.js not loaded');
|
| return;
|
| }
|
|
|
|
|
| this.destroy();
|
|
|
| const config = {
|
| type: this.type,
|
| data: data,
|
| options: {
|
| responsive: true,
|
| maintainAspectRatio: false,
|
| ...this.getDefaultOptions(this.type),
|
| ...this.options,
|
| ...customOptions,
|
| },
|
| };
|
|
|
| this.chart = new Chart(this.canvas, config);
|
| }
|
|
|
| |
| |
|
|
| update(data) {
|
| if (!this.chart) {
|
| console.warn('[Chart] Chart not initialized');
|
| return;
|
| }
|
|
|
| this.chart.data = data;
|
| this.chart.update();
|
| }
|
|
|
| |
| |
|
|
| destroy() {
|
| if (this.chart) {
|
| this.chart.destroy();
|
| this.chart = null;
|
| }
|
| }
|
|
|
| |
| |
|
|
| getDefaultOptions(type) {
|
| const common = {
|
| plugins: {
|
| legend: {
|
| display: true,
|
| position: 'top',
|
| labels: {
|
| color: 'var(--text-normal)',
|
| font: {
|
| family: 'var(--font-family-base)',
|
| },
|
| },
|
| },
|
| tooltip: {
|
| backgroundColor: 'var(--surface-glass)',
|
| titleColor: 'var(--text-strong)',
|
| bodyColor: 'var(--text-normal)',
|
| borderColor: 'var(--border-default)',
|
| borderWidth: 1,
|
| },
|
| },
|
| };
|
|
|
| const typeDefaults = {
|
| line: {
|
| scales: {
|
| x: {
|
| grid: {
|
| color: 'var(--border-subtle)',
|
| },
|
| ticks: {
|
| color: 'var(--text-soft)',
|
| },
|
| },
|
| y: {
|
| grid: {
|
| color: 'var(--border-subtle)',
|
| },
|
| ticks: {
|
| color: 'var(--text-soft)',
|
| },
|
| },
|
| },
|
| },
|
| bar: {
|
| scales: {
|
| x: {
|
| grid: {
|
| display: false,
|
| },
|
| ticks: {
|
| color: 'var(--text-soft)',
|
| },
|
| },
|
| y: {
|
| grid: {
|
| color: 'var(--border-subtle)',
|
| },
|
| ticks: {
|
| color: 'var(--text-soft)',
|
| },
|
| },
|
| },
|
| },
|
| doughnut: {
|
| plugins: {
|
| legend: {
|
| position: 'right',
|
| },
|
| },
|
| },
|
| };
|
|
|
| return {
|
| ...common,
|
| ...(typeDefaults[type] || {}),
|
| };
|
| }
|
| }
|
|
|
| |
| |
|
|
| export async function loadChartJS() {
|
| if (typeof Chart !== 'undefined') {
|
| return Promise.resolve();
|
| }
|
|
|
| return new Promise((resolve, reject) => {
|
| const script = document.createElement('script');
|
| script.src = 'https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js';
|
| script.onload = () => {
|
| console.log('[Chart] Chart.js loaded from CDN');
|
| resolve();
|
| };
|
| script.onerror = () => {
|
| console.error('[Chart] Failed to load Chart.js');
|
| reject(new Error('Failed to load Chart.js'));
|
| };
|
| document.head.appendChild(script);
|
| });
|
| }
|
|
|
| export default ChartComponent;
|
|
|