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