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