Workforce Clusters in JavaScript

Using ApexCharts with JavaScript

This Workforce Clusters example uses ApexCharts.js directly in JavaScript, with no wrapper component.

Install it with npm install apexcharts, then mount the chart with new ApexCharts(element, options).render().

JavaScript installation guide
// Shared by the vanilla-js, React and Vue builds. Two views of one 480-person
// company to show off both layouts: the headcount by department (grouped
// clusters) and the whole company split leadership-vs-staff (packed blob with
// the small leadership core nested in the centre). The live control state lives
// in each format's own wiring below.
var DATASETS = {
  departments: {
    values: [200, 120, 90, 70],
    labels: ['Engineering', 'Sales', 'Operations', 'Design'],
    colors: ['#008FFB', '#00B8D9', '#36B37E', '#FFAB00'],
    layout: 'grouped',
  },
  seniority: {
    values: [60, 420],
    labels: ['Leadership', 'Staff'],
    colors: ['#FFAB00', '#7B8794'],
    layout: 'packed',
  },
}

function randomWorkforce() {
  var eng = Math.floor(Math.random() * 110) + 150
  var sales = Math.floor(Math.random() * 70) + 90
  var ops = Math.floor(Math.random() * 60) + 60
  var design = Math.floor(Math.random() * 50) + 45
  return [eng, sales, ops, design]
}

function setActive(id) {
  document
    .querySelectorAll('.actions button[data-dataset]')
    .forEach(function (btn) {
      btn.classList.toggle('active', btn.getAttribute('data-dataset') === id)
    })
}

var options = {
  series: [200, 120, 90, 70],
  chart: {
    id: 'unitChart',
    type: 'unit',
    height: 420,
    animations: {
      enabled: true,
      speed: 900,
    },
  },
  labels: ['Engineering', 'Sales', 'Operations', 'Design'],
  colors: ['#008FFB', '#00B8D9', '#36B37E', '#FFAB00'],
  plotOptions: {
    unit: {
      layout: 'grouped',
      shape: 'circle',
      size: 'auto',
      clusterLabels: {
        show: true,
      },
    },
  },
  legend: {
    position: 'bottom',
  },
}

var chart = new ApexCharts(document.querySelector('#chart'), options)
chart.render()

// DATASETS, randomWorkforce and setActive live in the shared head script; the
// live current-view state stays per-format.
var current = 'departments'
setActive(current)

document
  .querySelectorAll('.actions button[data-dataset]')
  .forEach(function (btn) {
    btn.addEventListener('click', function () {
      var id = btn.getAttribute('data-dataset')
      if (id === current) return
      current = id
      setActive(current)
      var ds = DATASETS[id]
      chart.updateOptions({
        colors: ds.colors,
        labels: ds.labels,
        plotOptions: { unit: { layout: ds.layout } },
        series: ds.values,
      })
    })
  })

document.querySelector('#shuffle').addEventListener('click', function () {
  if (current !== 'departments') return
  chart.updateSeries(randomWorkforce())
})