import React from 'react'
import ReactApexChart from 'react-apexcharts'
import ApexCharts from 'apexcharts'
import './styles.css'
// A wandering line (numeric x, ~45 points) so there is room to zoom, pan,
// annotate and measure. Randomize regenerates it into a fresh checkpoint.
function randomSeries() {
var d = []
var v = 45 + Math.random() * 25
for (var i = 0; i < 45; i++) {
v += (Math.random() - 0.5) * 22
if (v < 8) v = 8
if (v > 100) v = 100
d.push([i + 1, Math.round(v)])
}
return d
}
const ApexChart = () => {
const [state, setState] = React.useState({
series: [{ name: 'Value', data: randomSeries() }],
options: {
chart: {
height: 400,
type: 'line',
id: 'rewind-demo',
fontFamily: 'Helvetica, Arial, sans-serif',
animations: { enabled: true },
// Toolbar on so zoom / pan / reset (and the measure tool) are one click away.
toolbar: { show: true, autoSelected: 'zoom' },
zoom: { enabled: true, type: 'x' },
history: {
enabled: true,
maxDepth: 50,
coalesceMs: 250,
keyboard: true,
},
// Right-click actions: drop a note, a dashed marker line, or seed the ruler.
contextMenu: {
enabled: true,
line: { text: 'Marker', strokeDashArray: 5, color: '#0EA5E9' },
items: ['annotate', 'xline', 'yline', 'measure'],
},
// Measure ruler + editable / draggable annotations. Both commit checkpoints,
// so Undo / Redo steps through them alongside data and zoom changes.
measure: { enabled: true },
ink: { enabled: true, snap: true },
},
colors: ['#0EA5E9'],
stroke: { width: 2.5, curve: 'smooth' },
markers: { size: 0, hover: { size: 5 } },
dataLabels: { enabled: false },
title: {
text: 'Undo / redo over data, zoom, annotations, and measurements',
align: 'left',
},
xaxis: { type: 'numeric', title: { text: 'Session' }, tickAmount: 10 },
yaxis: { decimalsInFloat: 0 },
grid: { borderColor: '#eceff5' },
},
})
React.useEffect(() => {
// The react-apexcharts wrapper owns the render, so reach the live instance by
// its chart.id, then wire the same buttons the vanilla build does.
// (randomSeries lives in the shared head script.) History drives the chart
// instance directly, so no React state changes and the chart is never
// re-rendered under it.
let chart
const timer = window.setInterval(() => {
chart = ApexCharts.getChartByID('rewind-demo')
if (!chart) return
window.clearInterval(timer)
const undoBtn = document.getElementById('undo')
const redoBtn = document.getElementById('redo')
const readout = document.getElementById('readout')
function syncButtons(state) {
if (!state) state = chart.history.state()
undoBtn.disabled = !state.canUndo
redoBtn.disabled = !state.canRedo
readout.innerHTML =
'history: <b>' +
(state.index + 1) +
' / ' +
state.length +
'</b> checkpoints'
}
chart.addEventListener('historyChange', function (c, state) {
syncButtons(state && state.length != null ? state : undefined)
})
undoBtn.addEventListener('click', function () {
chart.history.undo()
})
redoBtn.addEventListener('click', function () {
chart.history.redo()
})
document
.getElementById('randomize')
.addEventListener('click', function () {
chart.updateSeries([{ name: 'Value', data: randomSeries() }])
})
document.getElementById('reset').addEventListener('click', function () {
chart.history.clear()
})
// initial state once the baseline checkpoint lands
window.setTimeout(syncButtons, 300)
}, 50)
return () => {
window.clearInterval(timer)
}
}, [])
return (
<div>
<div className="panel">
<div className="controls">
<button id="undo" className="primary" disabled>
Undo
</button>
<button id="redo" className="primary" disabled>
Redo
</button>
<button id="randomize">Randomize data</button>
<button id="reset">Clear history</button>
<span className="readout" id="readout">
history: empty
</span>
</div>
</div>
<div id="chart">
<ReactApexChart
options={state.options}
series={state.series}
type="line"
height={400}
/>
</div>
<div className="panel">
<div className="note">
History is enabled with{' '}
<code>chart: { history: { enabled: true } }</code>
. Every data update, zoom, pan, annotation and measurement commits a
checkpoint. <b>Right-click</b> the plot to add a note, drop a dashed
marker line, or start a measurement (context menu); <b>drag-select</b>{' '}
or use the toolbar to zoom and pan; hold <code>m</code> and drag to
measure; press Randomize to replace the data. Then step back with Undo
/ Redo (or press
<code>Cmd/Ctrl + Z</code> and <code>Shift + Cmd/Ctrl + Z</code>). The
buttons enable from the <code>historyChange</code> event via
<code>chart.history.state()</code>.
</div>
</div>
</div>
)
}
export default ApexChart