You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

11 KiB

Charts

Concept uses Chart.js v4.5.0 with a custom component wrapper that provides consistent styling and color palette across all charts.

Concept Chart Configuration

Chart Component

// From Concept's charts.js
import Chart from 'chart.js/auto';

// Concept's color palette
const colors = {
  primary: '#5969ff',
  secondary: '#6c757d',
  success: '#28a745',
  info: '#17a2b8',
  warning: '#ffc107',
  danger: '#dc3545',
  light: '#f8f9fa',
  dark: '#23272b',
  purple: '#7b1fa2',
  pink: '#ff407b',
  orange: '#fd7e14',
  teal: '#20c997',
  cyan: '#17a2b8',
  gray: '#6c757d',
  grayDark: '#343a40'
};

// Default Concept chart options
const defaultOptions = {
  responsive: true,
  maintainAspectRatio: false,
  plugins: {
    legend: {
      display: true,
      position: 'bottom',
      labels: {
        padding: 20,
        usePointStyle: true,
        font: {
          size: 12
        }
      }
    },
    tooltip: {
      backgroundColor: 'rgba(0, 0, 0, 0.8)',
      padding: 12,
      cornerRadius: 4,
      titleFont: {
        size: 14
      },
      bodyFont: {
        size: 13
      }
    }
  }
};

Chart Types

Line Chart

new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    datasets: [{
      label: 'This Week',
      data: [65, 59, 80, 81, 56, 55, 40],
      borderColor: '#5969ff',
      backgroundColor: 'rgba(89, 105, 255, 0.1)',
      tension: 0.4
    }, {
      label: 'Last Week',
      data: [45, 39, 60, 71, 46, 35, 30],
      borderColor: '#ff407b',
      backgroundColor: 'rgba(255, 64, 123, 0.1)',
      tension: 0.4
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: {
        position: 'top',
      }
    },
    scales: {
      y: {
        beginAtZero: true
      }
    }
  }
});

Bar Chart

new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['Q1', 'Q2', 'Q3', 'Q4'],
    datasets: [{
      label: '2023',
      data: [12000, 19000, 15000, 25000],
      backgroundColor: '#5969ff'
    }, {
      label: '2024',
      data: [15000, 23000, 18000, 29000],
      backgroundColor: '#ff407b'
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: {
        position: 'top',
      }
    },
    scales: {
      y: {
        beginAtZero: true,
        ticks: {
          callback: function(value) {
            return '$' + value.toLocaleString();
          }
        }
      }
    }
  }
});

Pie/Doughnut Chart

new Chart(ctx, {
  type: 'doughnut',
  data: {
    labels: ['Desktop', 'Mobile', 'Tablet'],
    datasets: [{
      data: [55, 30, 15],
      backgroundColor: [
        '#5969ff',
        '#ff407b',
        '#25d5f2'
      ],
      borderWidth: 0
    }]
  },
  options: {
    responsive: true,
    plugins: {
      legend: {
        position: 'bottom'
      },
      tooltip: {
        callbacks: {
          label: function(context) {
            return context.label + ': ' + context.parsed + '%';
          }
        }
      }
    }
  }
});

Area Chart

new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    datasets: [{
      label: 'Revenue',
      data: [10, 25, 20, 30, 45, 40],
      borderColor: '#5969ff',
      backgroundColor: 'rgba(89, 105, 255, 0.2)',
      fill: true,
      tension: 0.4
    }]
  }
});

Dashboard Charts

Revenue Chart

function createRevenueChart(canvasId) {
  const ctx = document.getElementById(canvasId).getContext('2d');
  
  return new Chart(ctx, {
    type: 'line',
    data: {
      labels: getLast12Months(),
      datasets: [{
        label: 'Revenue',
        data: [45000, 52000, 48000, 58000, 63000, 72000, 
               78000, 82000, 79000, 85000, 91000, 95000],
        borderColor: '#5969ff',
        backgroundColor: 'rgba(89, 105, 255, 0.05)',
        borderWidth: 3,
        fill: true,
        tension: 0.4,
        pointRadius: 0,
        pointHoverRadius: 6
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: {
          display: false
        },
        tooltip: {
          backgroundColor: 'rgba(0, 0, 0, 0.8)',
          titleColor: '#fff',
          bodyColor: '#fff',
          borderColor: '#ddd',
          borderWidth: 1,
          padding: 10,
          displayColors: false,
          callbacks: {
            label: function(context) {
              return 'Revenue: $' + context.parsed.y.toLocaleString();
            }
          }
        }
      },
      scales: {
        x: {
          grid: {
            display: false
          }
        },
        y: {
          beginAtZero: false,
          ticks: {
            callback: function(value) {
              return '$' + (value / 1000) + 'k';
            }
          }
        }
      }
    }
  });
}

Real-time Chart

class RealtimeChart {
  constructor(canvasId) {
    this.chart = new Chart(document.getElementById(canvasId), {
      type: 'line',
      data: {
        labels: [],
        datasets: [{
          label: 'Active Users',
          data: [],
          borderColor: '#5969ff',
          backgroundColor: 'rgba(89, 105, 255, 0.1)',
          borderWidth: 2,
          fill: true
        }]
      },
      options: {
        responsive: true,
        scales: {
          x: {
            type: 'realtime',
            realtime: {
              duration: 20000,
              refresh: 1000,
              delay: 2000,
              onRefresh: chart => {
                chart.data.datasets.forEach(dataset => {
                  dataset.data.push({
                    x: Date.now(),
                    y: Math.random() * 100
                  });
                });
              }
            }
          },
          y: {
            beginAtZero: true
          }
        }
      }
    });
  }
  
  start() {
    this.chart.options.scales.x.realtime.pause = false;
    this.chart.update('none');
  }
  
  stop() {
    this.chart.options.scales.x.realtime.pause = true;
    this.chart.update('none');
  }
}

Advanced Features

Mixed Chart Types

new Chart(ctx, {
  type: 'bar',
  data: {
    labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
    datasets: [{
      type: 'bar',
      label: 'Sales',
      data: [12, 19, 3, 5, 2, 3],
      backgroundColor: '#5969ff'
    }, {
      type: 'line',
      label: 'Profit',
      data: [5, 8, 2, 3, 1, 2],
      borderColor: '#ff407b',
      backgroundColor: 'transparent',
      yAxisID: 'y1'
    }]
  },
  options: {
    scales: {
      y: {
        beginAtZero: true,
        position: 'left'
      },
      y1: {
        beginAtZero: true,
        position: 'right',
        grid: {
          drawOnChartArea: false
        }
      }
    }
  }
});

Dynamic Updates

// Update chart data
function updateChartData(chart, newData) {
  chart.data.datasets[0].data = newData;
  chart.update();
}

// Add data point
function addDataPoint(chart, label, value) {
  chart.data.labels.push(label);
  chart.data.datasets.forEach((dataset) => {
    dataset.data.push(value);
  });
  chart.update();
}

// Remove data point
function removeDataPoint(chart) {
  chart.data.labels.shift();
  chart.data.datasets.forEach((dataset) => {
    dataset.data.shift();
  });
  chart.update();
}

Chart Interactions

new Chart(ctx, {
  type: 'bar',
  data: chartData,
  options: {
    onClick: (event, activeElements) => {
      if (activeElements.length > 0) {
        const dataIndex = activeElements[0].index;
        const label = chart.data.labels[dataIndex];
        console.log('Clicked on:', label);
        // Handle click
      }
    },
    onHover: (event, activeElements) => {
      ctx.canvas.style.cursor = activeElements.length > 0 ? 'pointer' : 'default';
    }
  }
});

Responsive Charts

Container-based Sizing

<div class="chart-container" style="position: relative; height:400px;">
  <canvas id="responsiveChart"></canvas>
</div>
new Chart(document.getElementById('responsiveChart'), {
  type: 'line',
  data: data,
  options: {
    responsive: true,
    maintainAspectRatio: false
  }
});

Responsive Options

new Chart(ctx, {
  type: 'bar',
  data: data,
  options: {
    responsive: true,
    aspectRatio: 2,
    resizeDelay: 200,
    plugins: {
      legend: {
        display: window.innerWidth > 768,
        position: window.innerWidth > 768 ? 'right' : 'top'
      }
    }
  }
});

Custom Styling

Theme Colors

// Define theme colors
const chartColors = {
  primary: '#5969ff',
  secondary: '#6c757d',
  success: '#28a745',
  danger: '#dc3545',
  warning: '#ffc107',
  info: '#17a2b8'
};

// Use in charts
new Chart(ctx, {
  type: 'bar',
  data: {
    datasets: [{
      backgroundColor: [
        chartColors.primary,
        chartColors.success,
        chartColors.warning,
        chartColors.danger
      ]
    }]
  }
});

Gradient Fills

// Create gradient
const gradient = ctx.createLinearGradient(0, 0, 0, 400);
gradient.addColorStop(0, 'rgba(89, 105, 255, 0.5)');
gradient.addColorStop(1, 'rgba(89, 105, 255, 0)');

new Chart(ctx, {
  type: 'line',
  data: {
    datasets: [{
      backgroundColor: gradient,
      fill: true
    }]
  }
});

Chart Utilities

Export Chart

function exportChart(chart, filename = 'chart.png') {
  const link = document.createElement('a');
  link.download = filename;
  link.href = chart.toBase64Image();
  link.click();
}

// Usage
const exportBtn = document.getElementById('exportBtn');
exportBtn.addEventListener('click', () => {
  exportChart(myChart, 'sales-report.png');
});

Chart Loading State

function showChartLoading(canvasId) {
  const canvas = document.getElementById(canvasId);
  const parent = canvas.parentElement;
  
  parent.innerHTML = `
    <div class="d-flex justify-content-center align-items-center" style="height: 400px;">
      <div class="spinner-border text-primary" role="status">
        <span class="visually-hidden">Loading...</span>
      </div>
    </div>
  `;
}

async function loadChartData(url, canvasId) {
  showChartLoading(canvasId);
  
  try {
    const response = await fetch(url);
    const data = await response.json();
    
    // Recreate canvas
    const parent = document.querySelector(`#${canvasId}`).parentElement;
    parent.innerHTML = `<canvas id="${canvasId}"></canvas>`;
    
    // Create chart
    new Chart(document.getElementById(canvasId), data);
  } catch (error) {
    console.error('Failed to load chart data:', error);
  }
}

Performance Tips

Data Decimation

new Chart(ctx, {
  type: 'line',
  data: largeDataset,
  options: {
    parsing: false,
    normalized: true,
    animation: false,
    plugins: {
      decimation: {
        enabled: true,
        algorithm: 'lttb',
        samples: 500
      }
    }
  }
});

Disable Animations

// Globally
Chart.defaults.animation = false;

// Per chart
new Chart(ctx, {
  options: {
    animation: {
      duration: 0
    }
  }
});

Best Practices

DO:

  • Choose appropriate chart types for data
  • Keep charts simple and focused
  • Use consistent color schemes
  • Provide context with labels and legends
  • Make charts responsive
  • Test on different screen sizes

DON'T:

  • Don't overload charts with data
  • Don't use 3D effects unnecessarily
  • Don't forget accessibility
  • Don't use too many colors
  • Don't ignore mobile users