import React from 'react'
import ReactApexChart from 'react-apexcharts'
import './styles.css'
// A BUBBLE beeswarm: one bubble per game, laned by genre on a critic-score axis,
// with each bubble sized (by area) to its sales. So position reads quality and
// area reads reach, the biggest sellers pop out without leaving the swarm.
// Each datum carries value (score, X), z (copies sold, drives the radius) and a
// title (name, shown on hover). Figures are illustrative.
// Shared by the vanilla-js / React / Vue builds.
function g(name, score, sales) {
return { name: name, value: score, z: sales }
}
var gameSeries = [
{
name: 'Action-Adventure',
data: [
g('Red Dead Redemption 2', 97, 55),
g('God of War', 94, 23),
g('Horizon Zero Dawn', 89, 24),
g('Marvel’s Spider-Man', 87, 20),
g('Uncharted 4', 93, 16),
g('Ghost of Tsushima', 83, 13),
g('The Last of Us Part II', 93, 10),
g('Assassin’s Creed Odyssey', 83, 12),
],
},
{
name: 'RPG',
data: [
g('Skyrim', 94, 60),
g('The Witcher 3', 93, 50),
g('Cyberpunk 2077', 86, 30),
g('Elden Ring', 96, 25),
g('Baldur’s Gate 3', 96, 15),
g('Dark Souls III', 89, 10),
g('Persona 5', 93, 9),
g('Final Fantasy VII Remake', 87, 7),
],
},
{
name: 'Racing',
data: [
g('Mario Kart 8 Deluxe', 92, 62),
g('Forza Horizon 5', 92, 45),
g('Forza Horizon 4', 92, 24),
g('Gran Turismo 7', 87, 12),
g('Burnout Paradise', 88, 8),
g('F1 23', 82, 5),
g('Need for Speed Heat', 72, 4),
g('Dirt Rally 2.0', 84, 3),
],
},
]
const ApexChart = () => {
const [state, setState] = React.useState({
series: gameSeries,
options: {
chart: {
id: 'gameBubbleSwarm',
type: 'unit',
height: 460,
fontFamily: 'inherit',
animations: {
enabled: true,
speed: 700,
},
},
colors: ['#2a78d6', '#eb6834', '#1baf7a'],
legend: {
position: 'bottom',
markers: { radius: 12 },
},
plotOptions: {
unit: {
layout: 'scatter',
scatter: {
orientation: 'horizontal',
spread: 'swarm',
sizeField: 'z',
sizeRange: [5, 26],
xMin: 70,
xMax: 100,
tickAmount: 6,
laneLabelWidth: 128,
xTitle: 'Metacritic score',
},
},
},
tooltip: {
enabled: true,
},
},
})
return (
<div>
<div className="wrap">
<h1>Critic scores vs sales, by genre</h1>
<p className="lead">
One bubble per game, laned by genre on a critic-score axis. Position
is the score, bubble area is copies sold, so the crowd-pleasers that
also review well sit large and far to the right. Hover any bubble for
the title.
</p>
<div className="card">
<div id="chart">
<ReactApexChart
options={state.options}
series={state.series}
type="unit"
height={460}
/>
</div>
</div>
<div className="note">
Adding <code>scatter.sizeRange: [min, max]</code> turns the beeswarm
dots into area-scaled bubbles, read from each datum's{' '}
<code>sizeField</code> (here
<code>z</code>). The swarm still packs with no overlap, sized for the
largest bubble, so a third measure rides on the distribution for free.
The unit chart is a premium type, without a license it renders with a
trial watermark.
</div>
</div>
</div>
)
}
export default ApexChart