A minor release of two additions and twelve fixes, and every one of those fixes came from an outside contributor. They land on the parts of the library that only show their seams in real use: what resetSeries() restores from, CSV export, an x axis whose type was never declared, a hidden y axis, stacked corners, and server-side rendering.
Also here, and worth knowing about even though nothing visible changes: apex-commons moves from ^0.5.0 to ^0.8.0. A caret on a 0.x version pins the MINOR, so every published build since 0.5.0 resolved a commons three minors old, silently. Nothing breaks and nothing warns when a caret cannot reach a newer minor. The bundle carries commons rather than importing it at runtime, so this is the release that actually moves it.
| gzip | |
|---|---|
| 7.3.0 default bundle | 267,241 B |
| 7.4.0 default bundle | 268,389 B |
No API breaking changes. Upgrading is npm install apexcharts@7.4.0.
โจ New
Weave v5: api.info.stroke
stroke.dashArray is one option indexed by series position, with no per-series escape hatch. So a plugin that adds a computed series and wants that series dashed, to say its points were derived rather than measured, has to write an array covering every series on the chart including the caller's.
Doing that blind flattens whatever dashing the caller had into solid lines, and leaves nothing to restore them from when the tool is switched off. The option was therefore unusable by a plugin that respects the chart it is attached to, which meant a derived series could only be told apart by its name and its colour.
const dashes = api.info.stroke.dashArray // scalar, or one entry per seriesIt reports what is actually configured, so the array can be rebuilt around the caller's own values and put back exactly as found. A scalar covers every series and an array is per series, both passed through as they are, since a plugin restoring what it was told has to restore the shape too. The array is copied on the way out, so a plugin cannot restyle the chart by mutating what it was handed. This is the same reason dataLabels.enabledOnSeries is already reported: an option a plugin must narrow, and cannot narrow safely without knowing the current value.
Additive, so no plugin has to declare apiVersion: 5 to keep working. Feature-detect it the way reserve and pointer are detected.
A way back out of a zoom when the toolbar is hidden
Drag-to-zoom is a deliberate gesture, so unlike the wheel and the pinch it is not withheld when the toolbar is hidden. The reset button that undoes it is, which leaves a viewer who drags across a bare chart in a window with nothing on screen that puts the range back, and no key anyone would guess.
chart: {
toolbar: { show: false },
zoom: { resetControl: 'auto' }, // the default
}So while the chart IS zoomed and nothing else can reset it, one reset control is drawn where the toolbar would have been, and it goes when the range does. A chart nobody zooms is untouched: toolbar: { show: false } still means an empty chart for everyone who does not zoom. Escape resets under the same gate, deferring to keyboard navigation and leaving the event alone so a page listening for it still hears it.
'auto' resolves the way allowMouseWheelZoom already does: a toolbar showing its reset tool counts as a way back, anything else does not. false for a page with its own control, which turns off the key too. true also forces the control on where toolbar.tools.reset is off, which is the same dead end as a hidden toolbar.
The gates on the wheel and the pinch are deliberately left where they are. This control arrives after the fact, while what an incidental wheel zoom takes first is the page scroll it swallowed, which no button hands back.
One side effect worth having: a completed drag-zoom now focuses the chart, so the +, - and 0 keys are within reach for the first time after a mouse zoom.
๐ Fixes
resetSeries() could not bring back a series the viewer had hidden
globals.initialSeries is what resetSeries() and the toolbar's reset-home button restore from, but parseData() re-captured it from the live series on every parse. Any re-render that ran after the series was mutated baked the mutation into the baseline. A legend collapse replaces series[i].data with [] and re-renders, so by the time the viewer hits reset the baseline is the emptied series: the reset clears the collapsed indices and then restores [] onto [], and the hidden series never comes back.
parseData now owns the capture, gated on overwriteInitialSeries, which is the line the codebase already draws between a caller redefining the input and the library handing back the same data. Three more paths reached the same baseline and are closed with it:
appendData()on the raw-stash types (histogram, waterfall, dumbbell, streamgraph) appends into the stash and re-renders throughupdate(), so the baseline stayed on the pre-append observations.update()now carries the flag through to the parse._updateSeriesreassignedinitialSeriestoconfig.seriesright after that parse. On the derived types that is the binned counts, the accumulated pairs or the downsampled window, so a correct baseline was replaced with rows this library derived from it.- The legend-collapse reconcile runs before
parseDataand empties each collapsed row on its copy, so the capture forgot the data of exactly the series the viewer had switched off. Both snapshots now come off the array the caller handed in, and only when a reconcile actually ran.
chart.dataReducer charts also appended into config.series while every later parse re-reduced from the raw stash, and the two disagreeing is what made an appended point disappear on the next re-render. Fixes #5283, thanks @lazerg (#5284).
CSV export repeated rows, and a category could reach Object.prototype
handleUnequalXValues grouped rows in a plain unvalidated object keyed by the category. A category named toString threw values is not iterable, while one named __proto__ wrote through to Object.prototype, leaving every object on the page with an 0 property. It is now keyed in a Map.
The same function kept a Set of raw categories beside the map of row values. Two categories printing Date objects for the same instant held two Set entries pointing at one row, and the export repeated that row. The Set goes, since the map's keys are already the categories, while the raw string is kept for the category formatter. Thanks @81reap (#5308, #5309).
An inferred numeric x axis labelled its ticks as categories
Numeric XY parsing sets isXNumeric, but an omitted xaxis.type stays configured as category on a vertical bar chart, and the two halves of the axis then disagree. Range skipped the under-30 numeric tick heuristic while Formatters rounded the generated fractional coordinates as category labels, so on a two-series x = 1..12 chart integer-looking labels landed between the bar groups they named. Inferred numeric axes now carry their own semantics through both, and inferred category labels are preserved rather than regenerated. Fixes #5086, thanks @lovasoa (#5304).
A hidden y axis with labels.align threw
drawYaxis() always creates the .apexcharts-yaxis group, keeping the rel index aligned with w.config.yaxis, but returns before adding the texts group when the axis is hidden. setYAxisTextAlignments() read the bounding box of that missing group, so the render threw on null.
It also iterated the DOM collection, which is shorter than config.yaxis whenever an axis is hidden or collapsed, so every visible axis after the hidden one was skipped. It now iterates the config and returns early where there is no texts group, mirroring the guard yAxisTitleRotate() already had. Thanks @tarzan77cz (#5305).
borderRadiusWhenStacked did nothing, and a one-point stack rounded its baseline
plotOptions.bar.borderRadiusWhenStacked is a working option again. 'all' is the default and keeps the current look, both stack ends rounded, so existing charts do not change. 'last' rounds only the far end and leaves the baseline square, which is the shape asked for in the report. The option of the same name had not been read for some time and was removed as dead config.
Separately, a stack of a single data point no longer hands its baseline segment the 'top' treatment, which on a horizontal 100% stack with one category rounded the first segment on its inner edge. Closes #4845, thanks @gioboa (#5306).
Server-side rendering measured every label as 0x0
The SSR DOM shim has no getBBox(), so bbox() fell back to a zero rect and every getTextRects() call in SSR reported a 0x0 label. Horizontal bar data labels add half the label height to their offset, so they sat above the bar centre instead of on it, and the axis label sizes that set the plot area collapsed the same way.
SSRElement now estimates the box for text and tspan nodes from the font size and the content, using average sans-serif metrics. Other elements keep returning an empty rect. Fixes #5299, thanks @lazerg (#5300).
๐งน Housekeeping
- The LICENSE is the canonical text now, generated from one template shared across the organisation rather than maintained by hand here. It drops a SKU that was written as one rung when it is two, and a long-standing typo.
- Release candidates now publish under a dist-tag naming their own line,
rc-7.4rather thannext. A prerelease published undernextis correct for about a day: the moment the stable ships,latestmoves andnextis left pointing at something older, which happened twice in the 7.2 and 7.3 cycles.npm i apexcharts@nextnow fails with "No matching version" rather than quietly installing the wrong thing, and release notes name the exact tag for anyone who wants the candidate.