<template>
  <div>
    <div class="wrap">
      <h1>Two views of one partition</h1>
      <p>
        A treemap tile and a sunburst arc are both exactly one mark per row, so
        switching between them is a shape change rather than a redraw: each
        rectangle unrolls into the arc that holds the same team, and back. Every
        team keeps its colour, so you can pick one and follow it across. The
        sunburst adds the level a treemap cannot show, the parent rings, which
        sweep in behind the leaves.
      </p>

      <div class="actions">
        <button data-view="treemap" class="on">Treemap</button>
        <button data-view="sunburst">Sunburst</button>
      </div>

      <div class="chart-wrap">
        <div id="chart">
          <apexchart
            type="treemap"
            height="460"
            width="700"
            :options="chartOptions"
            :series="series"
          ></apexchart>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import VueApexCharts from 'vue-apexcharts'
import ApexCharts from 'apexcharts'

// Shared by the vanilla-js, React and Vue builds.
//
// One dataset, two partitions of it. The sunburst reads the hierarchy; the
// treemap reads the leaves, which are the level the two views have in common.
//
// Colour carries the identity across the switch: a team keeps its colour, so
// the tile you were looking at is the arc you end up looking at. Each branch
// owns a hue and its teams are progressively lighter shades of it.
var TREE = [
  {
    data: [
      {
        x: 'Engineering',
        y: 88,
        color: '#008FFB',
        children: [
          { x: 'Platform', y: 34, color: '#008FFB' },
          { x: 'Product', y: 30, color: '#29A1FC' },
          { x: 'Data', y: 24, color: '#52B3FC' }
        ]
      },
      {
        x: 'Design',
        y: 34,
        color: '#00E396',
        children: [
          { x: 'Research', y: 12, color: '#00E396' },
          { x: 'Brand', y: 10, color: '#29E7A7' },
          { x: 'Systems', y: 12, color: '#52ECB8' }
        ]
      },
      {
        x: 'Go to market',
        y: 52,
        color: '#FEB019',
        children: [
          { x: 'Sales', y: 26, color: '#FEB019' },
          { x: 'Marketing', y: 15, color: '#FEBD3E' },
          { x: 'Support', y: 11, color: '#FEC963' }
        ]
      }
    ]
  }
]

// The leaves, flattened: a treemap has no hierarchy, so it draws the level the
// two charts share. That is also the level the morph pairs up, tile for arc.
var LEAVES = [
  {
    data: TREE[0].data.reduce(function (acc, parent) {
      return acc.concat(
        parent.children.map(function (c) {
          return { x: c.x, y: c.y }
        })
      )
    }, [])
  }
]

// A sunburst colours per node, so its colours travel with the data above. A
// treemap colours from the palette instead, one entry per tile once
// `distributed` is on, so hand it the same colours in the same order.
var LEAF_COLORS = TREE[0].data.reduce(function (acc, parent) {
  return acc.concat(
    parent.children.map(function (c) {
      return c.color
    })
  )
}, [])

function wirePartitionToggle(chart) {
  var buttons = [].slice.call(document.querySelectorAll('[data-view]'))

  function apply(view) {
    buttons.forEach(function (b) {
      b.className = b.getAttribute('data-view') === view ? 'on' : ''
    })
    chart.updateOptions({
      chart: { type: view === 'sunburst' ? 'sunburst' : 'treemap' },
      series: view === 'sunburst' ? TREE : LEAVES
    })
  }

  buttons.forEach(function (b) {
    b.addEventListener('click', function () {
      apply(b.getAttribute('data-view'))
    })
  })
}

export default {
components: {
apexchart: VueApexCharts,
},
data: function () {
return {
series: LEAVES,
chartOptions: {
chart: {
  id: 'partition',
  type: 'treemap',
  width: 700,
  height: 460,
  toolbar: {
    show: false
  },
  animations: {
    enabled: true,
    chartTypeMorph: {
      enabled: true,
      speed: 900,
    },
  },
},
colors: LEAF_COLORS,
legend: {
  show: false,
},
dataLabels: {
  enabled: true,
  style: {
    fontSize: '12px',
  },
},
plotOptions: {
  treemap: {
    // One palette entry per tile, taken straight from the data above, so a
    // tile and the arc it becomes are the same colour. Shading is off because
    // it would recolour tiles by value and break that pairing.
    distributed: true,
    enableShades: false,
  },
  sunburst: {
    innerSize: '22%',
    borderRadius: 2,
    spacing: 1,
  },
},
},
partitionTimer: null,
}
},
mounted: function () {
  // The vue-apexcharts wrapper owns the render, so reach the live instance by
  // its chart.id before wiring the toggle.
  var me = this
  me.partitionTimer = window.setInterval(function () {
    var chart = ApexCharts.getChartByID('partition')
    if (!chart) return
    window.clearInterval(me.partitionTimer)
    wirePartitionToggle(chart)
  }, 50)
},
beforeDestroy: function () {
  window.clearInterval(this.partitionTimer)
},,
}
</script>

<style>
body {
  font-family:
    -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: #fafbfc;
  color: #2c3e50;
  margin: 0;
  padding: 32px 16px;
}
.wrap {
  max-width: 780px;
  margin: 0 auto;
}
h1 {
  font-size: 22px;
  margin: 0 0 8px;
}
p {
  color: #5b6b78;
  line-height: 1.5;
  margin: 0 0 24px;
}
.chart-wrap {
  background: #fff;
  border-radius: 8px;
  padding: 16px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
}
.actions {
  margin: 20px auto 12px;
  display: flex;
  gap: 8px;
  justify-content: center;
}
.actions button {
  color: #5b6b78;
  background: #fff;
  border: 1px solid #dfe6ec;
  padding: 8px 18px;
  font-weight: 600;
  font-size: 13px;
  border-radius: 6px;
  cursor: pointer;
}
.actions button.on {
  color: #fff;
  background: #008ffb;
  border-color: #008ffb;
}
</style>
Treemap / Sunburst Morph - Vue Treemap Charts | ApexCharts.js | ApexCharts.js