A minor release for plugin authors. Weave goes to API v6 with four additions, and the theme running through all of them is the same: the chart knew something a plugin had no way to ask for, so the plugin either guessed, or reached for the caller's config, or did without.
The largest of the four ends a pattern this library had been quietly asking plugins to use. Options indexed by series position have no per-series escape hatch, so a plugin wanting one value for its own series had to write the array covering every series and put the caller's back afterwards. api.claim() replaces that with something the host resolves at the point it reads the option, so nothing is written at all.
Everything here is additive. No plugin needs to declare apiVersion: 6 to keep working, and a plugin that wants the new surface while still running on older hosts can now ask for it by name.
| gzip | |
|---|---|
| 7.4.0 default bundle | 268,612 B |
| 7.5.0 default bundle | 269,856 B |
Both measured the same way, gzip at its default level over dist/apexcharts.min.js, so they are comparable with each other. Earlier release notes quoted a figure from the CI build, which lands a few hundred bytes apart from the committed bundle.
No API breaking changes. Upgrading is npm install apexcharts@7.5.0.
โจ New
Weave v6: api.capabilities and api.can()
The version integer never answered the question a plugin actually has, which is "does this host have X". A plugin declaring a version newer than the host is skipped outright, so one that supports several hosts declares the lowest version it can run on and then has to discover anything above that. Until now it did so by sniffing the facade for function members, which means every plugin reimplements the same guesswork against a surface it is deliberately not supposed to know the shape of.
if (api.can('claim')) {
// ...
}api.capabilities is the whole list, for logging and support. Names describe the capability rather than the version that introduced it, and are permanent once published: removing one is a breaking change on the same terms as removing a member.
Each name is probed off the facade that was actually built rather than copied from a constant, so a member that is ever made conditional drops out of the list instead of being advertised and then missing. The list is an array with the lookup behind a closure rather than a frozen Set, because Object.freeze does not touch a Set's internal slots: add() and delete() would go on working, and one plugin could quietly edit what every later plugin is told.
scales is deliberately not a capability. It is null for non-axis charts, which is a fact about the chart rather than about the host, and advertising it would tell a plugin on a pie chart that projection is available.
api.claim(): set an option for your own series, without writing the caller's config
stroke.dashArray and dataLabels.enabledOnSeries are indexed by series position. There is no per-series form of either, so a plugin that adds a computed series and wants it dashed, or wants the chart to stop printing labels over it, has had to write the array covering every series including the caller's, then restore what it found.
7.4.0 added api.info.stroke so a plugin could at least see what to restore, which made the pattern survivable rather than sound. Four things are wrong with it. The restore is stale the moment the caller calls updateOptions. It leaks if the plugin throws in between. A caller who wrote a single number gets it flattened into an array. And two plugins doing it at once fight, with the winner decided by execution order.
const claim = api.claim('stroke.dashArray', [
{ series: 'Revenue (forecast)', value: 6 },
])
claim.release()A claim says what the plugin wants, and the host answers with it where the option is READ. Nothing is written, so there is nothing to restore, releasing is a deletion, and a caller's own updateOptions composes with the claim rather than reverting it. Claims resolve in order, so two plugins on one series produce a defined winner instead of whichever happened to run last.
Name the series rather than its position where you can. A name is resolved each time the option is read, so the claim follows that series when the caller adds, removes or reorders others.
Claimable options are an allowlist, stroke.dashArray and dataLabels.enabledOnSeries to begin with, each declaring its value type so a wrong one is dropped rather than drawn. An option that is not on the list returns null rather than throwing, so a plugin built against a newer host degrades instead of breaking. Every claim is released on teardown, on destroy, and if the host disables the plugin after repeated errors, so none can outlive the plugin holding it.
api.drawn() and api.declare(): what is on the chart
A plugin that wants to list what a chart is showing had no way to learn it. It knows its own overlays and can read the series off api.data, but the caller's annotations, another plugin's overlays and the ink strokes a viewer drew were all invisible to it. A layers panel and an export summary are the same question, and both were unanswerable.
api.drawn() // [{ id, kind, label, owner, visible }, ...]
api.declare({ id: 'trend-1', label: 'Trend' })drawn() reports the series, the caller's annotations, and whatever plugins have declared, each entry naming its owner. The owner is the point rather than decoration: the list is only ever as complete as the features that opted into it, so a reader can say what its inventory covers instead of presenting a partial list as everything.
declare() is how a plugin joins, called from its draw handler. Declarations are cleared with the layers at the start of every draw, so an inventory can never outlive the drawing it describes, and declaring the same id twice replaces it rather than growing a duplicate row.
Read only, deliberately. Removing or hiding another feature's output is a much larger promise than this platform makes: it would mean one plugin reaching into another's state with no way for the owner to refuse.
One limitation worth knowing. Annotations do not carry a caller id. The id on a point annotation is the key of a deferred-execution entry rather than a handle on the drawing, so entries fall back to annotation:<type>:<index> and take their label from label.text where the caller wrote one.
api.info.title, and the modifier keys on a pointer event
Two small reads.
api.info.title is what the chart calls itself. A plugin that has to name this chart to somebody, a page-level readout listing several of them, otherwise heads each row with the container's id, which is a string written for a stylesheet rather than for a reader. The title is the name the page already chose and already shows. It is an empty string rather than undefined for an untitled chart, so it drops into a template without a guard.
api.info.title // 'Revenue by region', or ''modifiers on the pointer payload reports the keys held during the interaction. Shift-click to add to a selection is the gesture that page-level coordination wants and could not express, because the keys exist only on the DOM event, which the pointer handler had been discarding.
api.pointer((e) => {
if (e.modifiers.shift) { /* add to the selection */ }
})Always the same four booleans, shift, ctrl, alt and meta, never partial and never undefined, because a plugin writes e.modifiers.shift inside a viewer's click and a sometimes-missing key is how that becomes a crash. All four are false where there was no DOM event, which is the honest answer for a keyboard or programmatic selection.
๐ Fixes
The LICENSE branches on which plan the product needs
Two changes, both generated from one template shared across the organisation.
Products with no free tier no longer print the Community sections. Their files told a reader under $2M in revenue that they could use the product for free, and said it in five separate places: the dual-license opening, the Community section itself, the non-profit budget branch, the line about work built only from free features, and the acceptance list. The runtime never agreed: those products check the licence over the whole product, so no key means a watermark whatever the customer earns.
Products that do have a free tier now say what is in it. The Community section described who qualifies, on revenue, non-profit status or educational use, and never what you actually get. ApexCharts and the others with a free tier now list which features are free and which need Premium, taken from the same lists the pricing page renders.
๐งน Housekeeping
The Weave feature module is now 4.48 KB gzipped on top of core, up about 1.2 KB in this release, against a Tier-1 budget of roughly 5 KB. That was measured by building the default bundle with and without it, which the build reports as 263.53 KB and 259.05 KB gzipped, because the budget test checks Tier-1 membership rather than bytes. Worth measuring again before the next addition rather than assuming the headroom is still there.