import React from 'react'
import ReactApexChart from 'react-apexcharts'
import './styles.css'
// A VERTICAL beeswarm: value runs up the Y axis, each species is a column, and
// dots are anti-overlap "swarm" packed so the column's WIDTH reads as density.
// One dot per penguin. Figures are an illustrative sample around the real
// Palmer Archipelago body-mass distributions (Gentoo run visibly heavier).
// Shared by the vanilla-js / React / Vue builds.
var SPECIES = [
{ name: 'Adelie', mean: 3700, sd: 460, n: 44 },
{ name: 'Chinstrap', mean: 3733, sd: 385, n: 34 },
{ name: 'Gentoo', mean: 5076, sd: 505, n: 40 },
]
// Deterministic normal-ish sample (Irwin-Hall of 6 uniforms via an LCG): stable
// across re-renders and SSR, no Math.random. Rounded to the nearest 25 g.
function swarm(mean, sd, n, seed) {
var out = []
var s = seed >>> 0
function u() {
s = (s * 1664525 + 1013904223) >>> 0
return s / 4294967296
}
for (var i = 0; i < n; i++) {
var g = 0
for (var k = 0; k < 6; k++) g += u()
var z = (g - 3) / Math.sqrt(0.5)
out.push(Math.round((mean + z * sd) / 25) * 25)
}
return out
}
var swarmSeries = SPECIES.map(function (sp, i) {
return {
name: sp.name,
data: swarm(sp.mean, sp.sd, sp.n, 97 + i * 613).map(function (v, j) {
return { name: sp.name + ' ' + (j + 1), value: v }
}),
}
})
const ApexChart = () => {
const [state, setState] = React.useState({
series: swarmSeries,
options: {
chart: {
id: 'bodyMassSwarm',
type: 'unit',
height: 460,
fontFamily: 'inherit',
animations: {
enabled: true,
speed: 700,
},
},
colors: ['#eb6834', '#4a3aa7', '#1baf7a'],
legend: {
position: 'bottom',
markers: { radius: 12 },
},
plotOptions: {
unit: {
layout: 'scatter',
size: 4.4,
scatter: {
orientation: 'vertical',
spread: 'swarm',
xMin: 2000,
xMax: 6000,
tickAmount: 5,
xTitle: 'Body mass',
xFormatter: function (v) {
return (v / 1000).toFixed(1) + ' kg'
},
},
},
},
tooltip: {
enabled: true,
},
},
})
return (
<div>
<div className="wrap">
<h1>How much does a penguin weigh?</h1>
<p className="lead">
One dot per penguin, stacked into a column per species. The swarm
packs dots side by side with no overlap, so each column's width is its
density, you read the spread and the clusters, not just an average.
</p>
<div className="card">
<div id="chart">
<ReactApexChart
options={state.options}
series={state.series}
type="unit"
height={460}
/>
</div>
</div>
<div className="note">
A vertical beeswarm is <code>plotOptions.unit.scatter</code> with
<code>orientation: 'vertical'</code>: the value sits on the Y axis and
each category becomes a column across X. <code>spread: 'swarm'</code>{' '}
packs equal and near-equal values off the centre line so nothing
overlaps, turning each column into a density silhouette. The unit
chart is a premium type, without a license it renders with a trial
watermark.
</div>
</div>
</div>
)
}
export default ApexChart