import React from 'react'
import ReactApexChart from 'react-apexcharts'
import ApexCharts from 'apexcharts'
import './styles.css'

// Small-multiple waffles: ONE mini-waffle per country, each a 10x10 grid where
// every square is 1% of that country's population living in a city. Reading
// down the trellis you compare the fill height across countries at a glance;
// toggling the year fills each tile cell-by-cell IN PLACE, so you watch the
// whole world urbanise between 1980 and 2020.
//
// This is the unit chart's `grid` layout in `split` mode: `grid.split: true`
// draws one tile per category, `grid.total: 100` gives each tile 100 cells and
// `grid.max: 100` maps the value straight to filled cells (a value of 92 fills
// 92 of 100), so each tile reads as a true percentage. The empty cells show as
// a faint `trackColor` backdrop. Figures are World-Bank-style illustrative
// urban-population shares. Shared by the vanilla-js/React/Vue builds.
var COUNTRIES = [
  'Japan',
  'United States',
  'Germany',
  'Brazil',
  'South Korea',
  'China',
  'Nigeria',
  'India',
  'Kenya',
]

// One accent for every filled cell: the metric is the same everywhere, so the
// eye compares fill height, not colour. The per-tile label carries the country.
var ACCENT = '#0EA5E9'
var COLORS = COUNTRIES.map(function () {
  return ACCENT
})

var YEARS = {
  2020: {
    label: '2020',
    caption:
      'By 2020 most of the world lives in a city. The tiles that were nearly empty in 1980, China and Nigeria among them, have filled in fast.',
    // urban population, % of total (illustrative, ~2020)
    values: [92, 83, 77, 87, 81, 61, 52, 35, 28],
  },
  1980: {
    label: '1980',
    caption:
      'In 1980 cities were still the exception across much of Asia and Africa: barely a fifth of China lived in one. Each square is one percent of the population.',
    // urban population, % of total (illustrative, ~1980)
    values: [76, 74, 73, 66, 57, 19, 22, 23, 16],
  },
}

const ApexChart = () => {
  const [state, setState] = React.useState({
    series: YEARS['2020'].values,
    options: {
      chart: {
        id: 'urbanWaffle',
        type: 'waffle',
        height: 560,
        animations: {
          enabled: true,
          speed: 650,
        },
      },
      labels: COUNTRIES,
      colors: COLORS,
      legend: {
        show: false,
      },
      plotOptions: {
        unit: {
          grid: {
            split: true,
            columns: 10,
            total: 100,
            max: 100,
            trackColor: '#E8EDF2',
          },
          spacing: 1.2,
          clusterLabels: {
            show: true,
            position: 'bottom',
            fontSize: '12px',
            fontWeight: 600,
            color: '#334155',
            formatter: function (name, o) {
              return name + ' · ' + Math.round(o.value) + '%'
            },
          },
        },
      },
    },
  })

  React.useEffect(() => {
    // Reach the live instance by its chart.id, then wire the buttons. (Data lives
    // in the shared head script.) updateSeries drives the instance directly, so no
    // React state changes under it.
    let chart
    const timer = window.setInterval(() => {
      chart = ApexCharts.getChartByID('urbanWaffle')
      if (!chart) return
      window.clearInterval(timer)

      const buttons = document.querySelectorAll('#year-nav button')
      const caption = document.getElementById('caption')

      const showYear = (key) => {
        buttons.forEach((b) =>
          b.classList.toggle('active', b.getAttribute('data-year') === key),
        )
        caption.textContent = YEARS[key].caption
        chart.updateSeries(YEARS[key].values)
      }

      buttons.forEach((btn) => {
        btn.addEventListener('click', () => {
          showYear(btn.getAttribute('data-year'))
        })
      })

      caption.textContent = YEARS['2020'].caption
    }, 50)

    return () => window.clearInterval(timer)
  }, [])

  return (
    <div>
      <div className="wrap">
        <h1>The century of the city</h1>
        <p className="lead">
          One mini-waffle per country: each square is 1% of its population
          living in a city.
        </p>

        <div className="chart-wrap">
          <div id="chart">
            <ReactApexChart
              options={state.options}
              series={state.series}
              type="waffle"
              height={560}
            />
          </div>
        </div>

        <div className="actions" id="year-nav">
          <button data-year="1980">1980</button>
          <button data-year="2020" className="active">
            2020
          </button>
        </div>

        <p className="caption" id="caption"></p>

        <div className="note">
          Small multiples are the unit chart's <code>grid</code> layout in split
          mode:
          <code>plotOptions.unit.grid.split: true</code> draws one waffle per
          category,
          <code>grid.total: 100</code> gives each tile 100 cells and
          <code>grid.max: 100</code> fills value-of-100 of them, so every tile
          reads as a percentage against its faint <code>trackColor</code>{' '}
          backdrop. Switching the year is a plain{' '}
          <code>chart.updateSeries()</code>; each tile keeps its cells in place
          and only the fill boundary moves. The unit / waffle chart is a premium
          type; without a license it renders with a trial watermark.
        </div>
      </div>
    </div>
  )
}

export default ApexChart
Urban Small Multiples - React Waffle Charts | ApexCharts.js | ApexCharts.js