Chart.js

Chart.js integration, Bootstrap theming aware. In order to limit the scope of the matestack-ui-bootstrap gem, Chart.js components are not part of this gem. The below shown example should enable you to easily integrate Chart.js by copy&paste (or any other chart library) and optionally adjust according to your needs!

Example and docs below are meant to support Chart.js v2, not Chart.js v3

Ruby component

app/matestack/components/chart_js.rb

class Components::ChartJs < Matestack::Ui::VueJsComponent
  vue_name "chart-js-component"

  requires :type
  requires :datasets
  optional :labels
  optional :height
  optional :width
  optional :display_legend
  optional :display_x_axes
  optional :display_y_axes
  optional :cutout_percentage
  optional class: { as: :bs_class }

  # injected into vue.js components
  def vue_props
    {}.tap do |props|
      props[:type] = context.type
      props[:datasets] = context.datasets
      props[:labels] = context.labels
      props[:display_legend] = !display_legend.nil? ? context.display_legend : false
      props[:display_x_axes] = !display_x_axes.nil? ? context.display_x_axes : true
      props[:display_y_axes] = !display_y_axes.nil? ? context.display_y_axes : true
      props[:display_y_axes] = !display_y_axes.nil? ? context.display_y_axes : true
      props[:cutout_percentage] = !context.cutout_percentage.nil? ? context.cutout_percentage : 70
    end
  end

  def response
    div class: "chart-container #{context.bs_class}",  style: "width: 100%; height: 100%;" do
      plain "<canvas matestack-ui-vuejs-ref=#{matestack_ui_vuejs_ref('chart')}></canvas>".html_safe
    end
  end

end

Vue.js component

If not already installed, do:

yarn add chart.js@2.9.4

Do not forget to import the following file into you application pack

app/matestack/components/chart_js.js

import Chart from 'chart.js';
import MatestackUiVueJs from 'matestack-ui-vuejs'

const chartJsComponent = {
  mixins: [MatestackUiVueJs.componentMixin],
  template: MatestackUiVueJs.componentHelpers.inlineTemplate,

  data() {
    return {
      chartJsInstance: undefined,
      defaultColor: undefined,
      fontColor: undefined,
      fontFamily: undefined
    };
  },

  methods: {
    getThemeColor: function(key){
      const style = getComputedStyle(document.body);
      return style.getPropertyValue('--bs-'+key);
    },
    getThemeColorArray: function(keysArray){
      let result = []
      const style = getComputedStyle(document.body);
      keysArray.forEach(function(key){
        result.push(style.getPropertyValue('--bs-'+key))
      })
      return result;
    },
    drawBarChart: function(chartElement){
      const self = this;
      this.props["datasets"].forEach(function(item){
        if (item["backgroundColor"] === undefined){
          item["backgroundColor"] = self.getThemeColor("primary")
        }else{
          if(Array.isArray(item["backgroundColor"])){
            item["backgroundColor"] = self.getThemeColorArray(item["backgroundColor"])
          }else{
            item["backgroundColor"] = self.getThemeColor(item["backgroundColor"])
          }
        }
        item["hoverBackgroundColor"] = "rgba(0, 0, 0, 0.1)"
        if (item["barThickness"] === undefined){
          item["barThickness"] = 10;
        }
      })
      this.chartJsInstance = new Chart(chartElement, {
        type: 'bar',
        data: {
            labels: this.props["labels"],
            datasets: this.props["datasets"]
        },
        options: {
            legend: {
              display: this.props["display_legend"],
            },
            scales: {
                yAxes: [{
                    display: this.props["display_y_axes"],
                    gridLines: {
                      display: false,
                    },
                    ticks: {
                        beginAtZero: true
                    }
                }],
                xAxes: [{
                    display: this.props["display_x_axes"],
                    gridLines: {
                      display: false,
                    },
                    ticks: {
                        beginAtZero: true
                    }
                }]
            }
        }
      });
    },
    drawLineChart: function(chartElement){
      const self = this;
      this.props["datasets"].forEach(function(item){
        if (item["borderColor"] === undefined){
          item["borderColor"] = self.getThemeColor("primary")
        }else{
          item["borderColor"] = self.getThemeColor(item["borderColor"])
        }
        item["hoverBackgroundColor"] = "rgba(0, 0, 0, 0.1)"
        if (item["fill"] === undefined){
          item["fill"] = false;
        }
      })
      this.chartJsInstance = new Chart(chartElement, {
        type: 'line',
        data: {
            labels: this.props["labels"],
            datasets: this.props["datasets"]
        },
        options: {
            legend: {
              display: this.props["display_legend"],
            },
            scales: {
                yAxes: [{
                    display: this.props["display_y_axes"],
                    gridLines: {
                      display: false,
                    },
                    ticks: {
                        beginAtZero: true
                    }
                }],
                xAxes: [{
                    display: this.props["display_x_axes"],
                    gridLines: {
                      display: false,
                    },
                    ticks: {
                        beginAtZero: true
                    }
                }]
            }
        }
      });
    },
    drawDoughnutChart: function(chartElement){
      const self = this;
      this.props["datasets"].forEach(function(item){
        if (item["backgroundColor"] === undefined){
          item["backgroundColor"] = self.getThemeColor("primary")
        }else{
          if(Array.isArray(item["backgroundColor"])){
            item["backgroundColor"] = self.getThemeColorArray(item["backgroundColor"])
          }else{
            item["backgroundColor"] = self.getThemeColor(item["backgroundColor"])
          }
        }
        item["hoverBackgroundColor"] = "rgba(0, 0, 0, 0.1)"
        if (item["borderColor"] === undefined){
          item["borderColor"] = self.getThemeColor("white")
        }else{
          item["borderColor"] = self.getThemeColor(item["borderColor"])
        }
        if (item["hoverBorderColor"] === undefined){
          item["hoverBorderColor"] = self.getThemeColor("white")
        }else{
          item["hoverBorderColor"] = self.getThemeColor(item["hoverBorderColor"])
        }
        if (item["borderWidth"] === undefined){
          item["borderWidth"] = 10
        }
        if (item["weight"] === undefined){
          item["weight"] = 1
        }
      })
      this.chartJsInstance = new Chart(chartElement, {
        type: 'doughnut',
        data: {
            labels: this.props["labels"],
            datasets: this.props["datasets"]
        },
        options: {
            legend: {
              display: this.props["display_legend"],
            },
            cutoutPercentage: this.props["cutout_percentage"]
        }
      });
    },
    drawPieChart: function(chartElement){
      const self = this;
      this.props["datasets"].forEach(function(item){
        if (item["backgroundColor"] === undefined){
          item["backgroundColor"] = self.getThemeColor("primary")
        }else{
          if(Array.isArray(item["backgroundColor"])){
            item["backgroundColor"] = self.getThemeColorArray(item["backgroundColor"])
          }else{
            item["backgroundColor"] = self.getThemeColor(item["backgroundColor"])
          }
        }
        item["hoverBackgroundColor"] = "rgba(0, 0, 0, 0.1)"
      })
      this.chartJsInstance = new Chart(chartElement, {
        type: 'pie',
        data: {
            labels: this.props["labels"],
            datasets: this.props["datasets"]
        },
        options: {
            legend: {
              display: this.props["display_legend"],
            }
        }
      });
    },
  },

  mounted: function() {
    const style = getComputedStyle(document.body);

    this.defaultColor = style.getPropertyValue('--bs-primary');
    this.fontColor = style.getPropertyValue('--bs-secondary');
    this.fontFamily = style.getPropertyValue('--bs-font-sans-serif');

    Chart.defaults.global.defaultFontColor = this.fontColor
    Chart.defaults.global.defaultFontFamily = this.fontFamily
    Chart.defaults.global.defaultColor = this.fontFamily

    const chartElement = this.$refs.chart

    if(this.props["type"] == "bar"){
      this.drawBarChart(chartElement);
    }
    if(this.props["type"] == "line"){
      this.drawLineChart(chartElement);
    }
    if(this.props["type"] == "doughnut"){
      this.drawDoughnutChart(chartElement);
    }
    if(this.props["type"] == "pie"){
      this.drawPieChart(chartElement);
    }

  }
}

export default chartJsComponent

and then in your application.js file:

import chartJsComponent from '../../matestack/components/chart_js.js'// import component definition from source

const appInstance = createApp({})

appInstance.component('chart-js-component', chartJsComponent) // register at appInstance

MatestackUiBootstrap.registerComponents(appInstance)

document.addEventListener('DOMContentLoaded', () => {
  MatestackUiVueJs.mount(appInstance)
})

Usage

Example 1: Bar chart

Components::ChartJs.call(class: "w-50", type: :bar, datasets: [
  {
    label: "€",
    data: [x, y, z],
    backgroundColor: :primary
  },
], labels: ["x", "y", "z"])

Example 2: Doughnut chart

Components::ChartJs.call(type: :doughnut, datasets: [
  {
    label: "€",
    data: [x, y, z],
    backgroundColor: [:orange, :secondary, :primary]
  },
], labels: ["x", "y", "z"])

Example 3: Line chart

Components::ChartJs.call(type: :line, datasets: [
  {
    label: "€",
    data: [x, y, z],
    borderColor: :primary,
    pointRadius: 0
  },
  {
    label: "€",
    data: [last_7_days, total, last_30_days],
    borderColor: :danger,
    fill: false
  },
], labels: ["x", "y", "z"], display_x_axes: false, display_y_axes: false)

Example 4: Pie chart

Components::ChartJs.call(type: :pie, datasets: [
  {
    label: "€",
    data: [x, y, z],
    backgroundColor: [:orange, :secondary, :primary]
  },
], labels: ["x", "y", "z"])

Last updated