apexcharts-skill 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,33 @@
1
1
  # Feature Platform: ApexCharts
2
2
 
3
- ApexCharts ships an opt-in feature platform on top of the chart core. **Every v5 and v6 config keeps working unchanged in v7**, apart from the two v7.0 breaking changes noted below. Each feature is off by default in config terms and ships as its own `apexcharts/features/*` entry.
3
+ ApexCharts ships an opt-in feature platform on top of the chart core. **Every v5 and v6 config keeps working unchanged in v7**, apart from the two v7.0 breaking changes noted below. In 8.0 the breaking change is what the default bundle carries (next section). Each feature is off by default in config terms and ships as its own `apexcharts/features/*` entry.
4
4
 
5
- ## ⚠️ Read first: two bundle tiers (v7.0)
5
+ ## ⚠️ Read first: what is in the default bundle (8.0)
6
6
 
7
- Whether you need the `features/*` import depends on the feature's **tier**, and this changed in v7.0.
7
+ Whether you need an import depends on the feature's or chart type's **tier**. The tiers date from v7.0. **8.0 moved seven items to Tier 2**: the `unit` (with `waffle`), `sunburst` and `violin` chart types, and the `drilldown`, `waterfall`, `dumbbell` and `streamgraph` features. A page that uses one adds its import, or loads the full bundle.
8
8
 
9
- - **Tier 1** is in the default `import ApexCharts from 'apexcharts'` bundle: `exports`, `legend`, `toolbar`, `annotations`, `keyboard`, `morph`, `drilldown`, `weave`, `marks`, `facet`, `stats`, and the v7.1 `waterfall` / `dumbbell` / `streamgraph` chart types. Import these only if you started from `apexcharts/core` or a per-type entry.
10
- - **Tier 2 is not in any bundle.** Nine features (`trellis`, `storyboard`, `perspectives`, `ink`, `renderer-canvas`, `link`, `measure`, `history`, `context-menu`) plus the `raincloud` chart type require an explicit import **whatever entry point you used**, the full bundle included.
9
+ - **Tier 1** is in the default `import ApexCharts from 'apexcharts'` bundle: `exports`, `legend`, `toolbar`, `annotations`, `keyboard`, `morph`, `weave`, `marks`, `facet`, `stats`. Import these only if you started from `apexcharts/core` or a per-type entry.
10
+ - **Tier 2 is not in the default bundle.** Each item needs its own import next to it (or its script tag), or the full bundle:
11
+ - features, `import 'apexcharts/features/<name>'` (script tag `dist/features/<name>.js`): `trellis`, `storyboard`, `perspectives`, `ink`, `renderer-canvas`, `link`, `measure`, `history`, `context-menu`, `highlight-filter` *(7.9)*, and since 8.0 `drilldown`, `waterfall`, `dumbbell` and `streamgraph` (those three are chart types whose renderers, rangeBar and rangeArea, are in the default bundle, so next to it you add only the feature);
12
+ - chart types: `unit` / `waffle` (`apexcharts/unit`, `dist/unit.js`), `sunburst`, `violin`, `icicle` (`apexcharts/<type>`, `dist/<type>.js`) and `raincloud` (`apexcharts/raincloud`; script tags `dist/violin.js`, then `dist/features/raincloud.js`).
13
+ - **The full bundle**, `import ApexCharts from 'apexcharts/full'` (script tag `dist/apexcharts.full.min.js`, new in 8.0), has every chart type and feature and needs no add-on import. Premium items still watermark there.
11
14
 
12
15
  ```js
13
16
  import ApexCharts from 'apexcharts'
14
- import 'apexcharts/features/trellis' // Tier 2: required even here
17
+ import 'apexcharts/features/trellis' // Tier 2: not in the default bundle
15
18
  ```
16
19
  ```html
17
20
  <script src=".../dist/apexcharts.js"></script>
18
21
  <script src=".../dist/features/trellis.js"></script>
19
22
  ```
20
23
 
21
- Nothing fails silently: each Tier 2 module warns in the console when its configuration is present but the module is not, and names both import routes. Where the chart can still draw something sensible it does and says what it did: a trellis renders as a single chart, `renderer: 'canvas'` falls back to SVG. `apexcharts/features/all` is the **Tier 1** set and is not a shortcut past this.
24
+ Nothing fails silently, and since 7.9.1 the minified bundle prints these warnings too. A Tier 2 chart type that is not loaded makes `render()` reject, and the console names the import. A Tier 2 feature warns when its configuration is present but the module is not, naming the import, the script-tag file and `apexcharts.full.min.js`. Where the chart can still draw something sensible it does and says what it did: a trellis renders as a single chart, `renderer: 'canvas'` falls back to SVG, a drilldown chart draws its root level. `waterfall`, `dumbbell` and `streamgraph` without their feature draw an empty plot with one warning. `apexcharts/features/all` is the **Tier 1** set and is not a shortcut past this; the full bundle is.
22
25
 
23
- Why: 24% of the 6.10.0 default bundle was licence-gated premium code that an unlicensed user could only run watermarked, and everybody downloaded it. The default bundle went 291,654 B gzipped (6.10.0) to 252,005 B (7.0.0), and 264,326 B in 7.1.0 with the three new free chart types. See `references/tree-shaking.md` for the full tier table and per-module sizes.
26
+ Why: 24% of the 6.10.0 default bundle was licence-gated premium code that an unlicensed user could only run watermarked, and everybody downloaded it. The default bundle went 291,654 B gzipped (6.10.0) to 252,005 B (7.0.0), and 264,326 B in 7.1.0 with the three new free chart types. At 8.0.0 it is 263,188 B, and the full bundle 381,135 B (the upstream build report's figures, gzip at the default level; they move every release). See `references/tree-shaking.md` for the full tier table and per-module sizes.
24
27
 
25
28
  ## Two v7.0 breaking changes
26
29
 
27
- 1. **`plotOptions.bar.borderRadiusWhenStacked` is removed.** Rounded corners on a stacked bar are no longer a setting: corner ownership follows the **outer edge** of the stack, which is what `'last'` approximated and what `'all'` got wrong on any stack whose last series was empty. An unknown option is ignored, so leaving it in place is harmless but does nothing.
30
+ 1. **`plotOptions.bar.borderRadiusWhenStacked` was dropped in v7.0, and came back working in v7.4.** In v7.0 it had not been read for some time and was removed as dead config, with corner ownership following the stack's **outer edge** automatically. v7.4 made it a real option again: `'all'` (the default) keeps that look with both stack ends rounded, and `'last'` rounds only the far end and leaves the baseline square. A one-point stack no longer rounds its baseline either. Treat it as live, not as legacy.
28
31
  2. **`dataLabels.animate.enabled` now defaults to `true`** (bar and column only). Labels ride to their new position on a data-change update instead of snapping there, so they reflow on the same clock as the bars, markers and axis ticks. Speed and easing follow `chart.animations.dynamicAnimation`. A label that has not moved is a per-label no-op. Set `dataLabels: { animate: { enabled: false } }` for the old behavior.
29
32
 
30
33
  ## Behaviors that are on by default
@@ -32,7 +35,7 @@ Why: 24% of the 6.10.0 default bundle was licence-gated premium code that an unl
32
35
  Carried over from v6, both respecting `prefers-reduced-motion`:
33
36
 
34
37
  1. **Coherent variable-length data transitions**: updates that add/remove data points animate as one coordinated motion instead of popping. Appended bars grow from the baseline, removed bars shrink and fade, line/area fills reshape over the union of old and new points, and markers/bubbles/axis labels ride along. Scatter and bubble now animate on dynamic updates for the first time. Disable per chart with `chart.animations.dynamicAnimation.enabled: false`; auto-skipped above `chart.animations.largeDatasetThreshold` (default 1000) and under `prefers-reduced-motion`.
35
- 2. **Native-feeling mobile gestures**: two-finger pinch-zoom, two-finger pan, and kinetic inertia after a flick, with axis rails so a vertical swipe still scrolls the page. Configure via `chart.zoom.pinch` (default `true`) and `chart.pan.inertia` (default `true`, `friction` default `0.92`).
38
+ 2. **Native-feeling mobile gestures**: two-finger pinch-zoom, two-finger pan, and kinetic inertia after a flick, with axis rails so a vertical swipe still scrolls the page. Configure via `chart.zoom.pinch` (default `'auto'`) and `chart.pan.inertia` (default `true`, `friction` default `0.92`). **`'auto'` is not the same as `true`**: it enables pinch only where the toolbar reset button is present, so a zoom is always undoable. A chart with `toolbar: { show: false }` gets no pinch zoom until `pinch: true` forces it. `chart.zoom.allowMouseWheelZoom` is gated the same way, and `chart.zoom.resetControl` (v7.4) is the other half of that bargain.
36
39
 
37
40
  Also new by default: **`render()` is idempotent**: a repeated `render()` (e.g. a framework double-invoking an effect) returns the same promise instead of building a duplicate chart. `destroy()` clears it so an instance can render fresh.
38
41
 
@@ -45,7 +48,7 @@ Two update behaviors worth knowing (both 6.9/6.10):
45
48
 
46
49
  ## Licensing: premium features and the trial watermark
47
50
 
48
- Introduced in **6.5.0**, tightened in **6.7.0**. Not every feature is free. Eight feature modules plus two chart types are **premium** and run under a lightweight, offline license check. They keep working fully without a key (trial mode), but the chart shows an unobtrusive `APEXCHARTS` watermark until an entitled license is set.
51
+ Introduced in **6.5.0**, tightened in **6.7.0**. Not every feature is free. Nine feature modules plus two chart types are **premium** and run under a lightweight, offline license check. They keep working fully without a key (trial mode), but the chart shows an unobtrusive `APEXCHARTS` watermark until an entitled license is set.
49
52
 
50
53
  **The premium set (watermarked until entitled):**
51
54
 
@@ -57,12 +60,13 @@ Introduced in **6.5.0**, tightened in **6.7.0**. Not every feature is free. Eigh
57
60
  - `context-menu` (`contextMenu`)
58
61
  - `perspectives` (shareable view state)
59
62
  - `history` (undo/redo, Rewind)
63
+ - `highlight-filter` (`highlightFilter`, premium since **7.9.0**; watermarks only while a render draws parts)
60
64
  - the `unit` / `waffle` chart type (premium since **6.6.0**, the first premium chart type)
61
65
  - the `raincloud` chart type (premium since **7.1.0**)
62
66
 
63
- **Everything else is free and never gated**: every other chart type (`sunburst`, and the v7.1 `waterfall` / `dumbbell` / `streamgraph`), and every other module (Weave, Strata / canvas, Marks, Facet / themes, Cadence, drilldown, streaming, exports, legend, toolbar, annotations, keyboard, stats).
67
+ **Everything else is free and never gated**: every other chart type (the Tier 2 `sunburst`, `violin`, `icicle`, `waterfall`, `dumbbell` and `streamgraph` included), and every other module (Weave, Strata / canvas, Marks, Facet / themes, Cadence, drilldown, streaming, exports, legend, toolbar, annotations, keyboard, stats). Free here means never watermarked, not licence-free: the Community licence covers only organisations under $2M in annual revenue, operating budget, funding or equivalent resources, whichever is highest, across parent, affiliates and entities under common control (see LICENSE.md).
64
68
 
65
- **Premium and Tier 2 are independent axes.** Premium is about the watermark; Tier 2 is about the bundle. `renderer-canvas` is Tier 2 and free. `waterfall`, `dumbbell` and `streamgraph` are Tier 1 and free. `unit` / `waffle` is premium but Tier 1 (it is a chart type in the default bundle). `trellis` and `raincloud` are both.
69
+ **Premium and Tier 2 are separate questions.** Premium is about the watermark; Tier 2 is about the bundle. Since 8.0 every premium item is Tier 2, but Tier 2 is not all premium: `renderer-canvas`, `drilldown`, `waterfall`, `dumbbell`, `streamgraph`, `sunburst`, `violin` and `icicle` are Tier 2 and free. The full bundle carries all of them, and the premium ones still watermark there.
66
70
 
67
71
  ### Setting a license
68
72
 
@@ -78,7 +82,7 @@ Both are typed: `ApexCharts.setLicense(key: string)` (static) and `chart.license
78
82
 
79
83
  - **In use, not bundled.** Importing a premium module without actually enabling it does not watermark; only *using* it does.
80
84
  - **Live.** A late `setLicense(validKey)` followed by an update clears an on-screen watermark; no full re-render needed. The watermark is re-evaluated on every render/update.
81
- - **One key across the family.** The key format is shared across the ApexCharts family (apexgantt, apextree, apexsankey, apex-grid-enterprise, apexstock), validated offline with no network call. SSR-safe.
85
+ - **One key across the family.** The key format is shared across every Apex product (apexgantt, apextree, apexsankey, apex-grid-enterprise, apexstock, apexmaps), validated offline with no network call. SSR-safe.
82
86
 
83
87
  ### Plan entitlement (6.7.0 behavior change)
84
88
 
@@ -127,6 +131,7 @@ trellis: { by: 'facet', minPanelWidth: 260 },
127
131
  | `columns` | `'auto'` | `'auto'` fits `minPanelWidth` columns into the container. |
128
132
  | `minPanelWidth` | `220` | Drives `'auto'` columns and the responsive collapse. |
129
133
  | `gap` / `aspectRatio` / `panelHeight` | `12` / `1.6` / none | `panelHeight` wins over `aspectRatio` and `chart.height`. |
134
+ | `minPanelHeight` | `80` | *(7.7.0)* Floor for a height-derived panel, in px. A grid that cannot fit its container overflows and warns by how much instead of shrinking panels past this. Lower it when fitting a short container matters more than legibility. |
130
135
  | `order` | `'first-seen'` | Also `'asc'`, `'desc'`, an explicit `string[]`, or a comparator. |
131
136
  | `limit` | none | Render only the first N panels (warns about the rest). |
132
137
  | `virtualize` | `'auto'` | Mounts only panels intersecting the viewport (plus one row) once the grid exceeds **64 panels**. An unmounted cell keeps its header and a fixed-height skeleton so page height never shifts; a remount restores its zoom window. **`getPanel(key)` returns `null` for an unmounted panel.** |
@@ -136,13 +141,15 @@ trellis: { by: 'facet', minPanelWidth: 260 },
136
141
  | `legend` / `toolbar` | `'shared'` | One legend (toggles a series name in every panel) and one zoom/pan/reset toolbar. `'none'` to drop. |
137
142
  | `tooltip` | `'panel'` | Card in the hovered panel while the crosshair sweeps all. `'sync'` = a card per panel; `'grid'` = one card near the cursor with a row per panel, composed from the panels' own tooltips so every formatter is honored. |
138
143
  | `zoom` | `'sync'` | A zoom in any panel moves every panel. |
139
- | `promote` | `true` | Clicking a header expands that panel to full width with an "All panels" breadcrumb. |
144
+ | `promote` | `false` | *(7.7.0 BREAKING: was `true`)* Set `true` to make a header click expand that panel to full width with an "All panels" breadcrumb. `chart.promotePanel(key)` / `chart.restorePanels()` work either way. |
140
145
  | `radiusByTotal` | `false` | Pie/donut/polarArea only: scale each panel's radius so its **area** is proportional to the panel's total. Equal-size pies cannot encode magnitude, so set this on a pie trellis. |
141
146
  | `targetTicks` | `3` | Tick-interval target for the shared nice y scale, so a small panel wears few labels. |
142
147
  | `header` | shown | `{ show, formatter(key, { dimension, index, count }), style }`. |
143
148
  | `panel` | none | `(key, { index, seriesNames }) => ApexOptions`, a per-panel override applied last. |
144
149
 
145
- **API.** `chart.getPanels()` returns the panels in grid order; `chart.getPanel(key)` returns one panel's own ApexCharts instance (the escape hatch to every per-chart API the trellis does not re-expose); `chart.promotePanel(key)` / `chart.restorePanels()` drive the promotion from code. `ApexCharts.trellis(el, options)` is the imperative constructor and throws if the feature is not imported.
150
+ **API.** `chart.getPanels()` returns the panels in grid order; `chart.getPanel(key)` returns one panel's own ApexCharts instance (the escape hatch to every per-chart API the trellis does not re-expose); `chart.promotePanel(key)` / `chart.restorePanels()` drive the promotion from code. `ApexCharts.trellis(el, options)` is the imperative constructor: it creates the host and starts rendering it (a later `render()` settles with the same mount). It returns `null`, with a console warning, when the trellis feature is not loaded, so check the return before calling panel methods.
151
+
152
+ **Events.** `panelMounted`, `trellisMounted`, `panelPromoted` and `panelRestored` fire through `chart.events` (7.7.0) as well as `addEventListener`, with `(chart, payload)`; they are not in the shipped types. On scatter and bubble panels `dataPointIndex` indexes the panel's own data. To turn a trellis back into one chart, pass `trellis: null` to `updateOptions` (a cast in TypeScript); leaving the key out keeps the grid.
146
153
 
147
154
  **Annotations** take `scope`: absent or `'trellis'` draws in every panel through each panel's own scale, a key or array of keys restricts it.
148
155
 
@@ -181,13 +188,117 @@ The `setup(api)` facade:
181
188
 
182
189
  - `api.on(hook, fn)` / `api.off(hook, fn)`: hooks: `'afterParse' | 'afterScales' | 'draw' | 'afterUpdate' | 'destroy'`.
183
190
  - `api.layer(opts?)`: a plugin-owned drawing surface (`path`, `line`, `rect`, `circle`, `text`, `clear`); call it inside draw handlers only, layers are wiped each pass.
184
- - `api.scales` (readonly): `x(v)`, `y(v, axis?)`, `domainX`, `domainY(axis?)`, `gridWidth`, `gridHeight`, `ratios`.
185
- - `api.data` (readonly): `[{ name?, hidden, color?, points: [{ x, y }] }]`.
191
+ - `api.scales` (readonly): `x(v)`, `y(v, axis?)`, `domainX`, `domainY(axis?)`, `gridWidth`, `gridHeight`, `ratios`. **`null` on a non-axis chart**, so it is a fact about the chart rather than about the host and is deliberately not a capability.
192
+ - `api.data` (readonly): `[{ name?, hidden, color?, points: [{ x, y }], raw }]`.
186
193
  - `api.theme`: `{ mode, foreColor, seriesColor(i), token(name) }`.
187
194
  - `api.store`: per-instance mutable state bag.
188
195
  - `api.emit(name, detail?)`: dispatches `plugin:<name>:<event>` on the chart; `api.chart`, `api.el`, `api.name`, `api.version`, `api.options` round out the facade.
189
196
 
190
- `apiVersion` gates the contract so raw internals can keep changing safely.
197
+ ### Declare the lowest version you can run on, then feature-detect (v2+)
198
+
199
+ `apiVersion` gates the contract so raw internals can keep changing safely, but it is **not** how you ask for a newer member. A plugin declaring a version **newer than the host is skipped outright**, so declaring the latest is how one build stops working on older hosts. Declare the lowest version your plugin can actually run on, and discover anything above that:
200
+
201
+ ```js
202
+ // v6 hosts: ask by name.
203
+ if (api.can('claim')) { /* ... */ }
204
+ api.capabilities // the whole frozen list, for logging and support
205
+
206
+ // Older hosts, or a plugin that must run on both: probe the member.
207
+ if (typeof api.pointer === 'function') { /* ... */ }
208
+ ```
209
+
210
+ Each capability name is probed off the facade that was actually built, so a member that is ever made conditional drops out of the list instead of being advertised and then missing. Names are permanent once published.
211
+
212
+ The version gate was an exact-match check before v2, which would have disabled every v1 plugin on the first additive change. It is forward-compatible now: an older plugin is served, and only one needing a newer host is skipped.
213
+
214
+ ### v2: know what you are attached to
215
+
216
+ ```js
217
+ api.info // { type, axisChart, datetimeX, horizontalBars,
218
+ // dataLabels: { enabled, enabledOnSeries },
219
+ // stroke: { dashArray }, // v5
220
+ // title } // v6, '' when untitled
221
+ api.categories // display labels per x position, resolved config-first
222
+ api.data[0].raw // the caller's own data array for that series, unparsed
223
+ api.markDerived(['Revenue (forecast)']) // these series are mine, not the caller's
224
+ ```
225
+
226
+ - **`info` is how a plugin decides whether it applies at all**, instead of adding a series and letting the core warn at the user.
227
+ - **Read `categories`, never `globals.categoryLabels`.** The globals are populated after a mount and empty after an `updateSeries()`, so reading them directly gives real labels on first paint and ordinals after any update.
228
+ - **`raw` is fine to read and wrong to copy**: the three accepted data shapes are not interchangeable, so the normalised `points` are what you compute from.
229
+ - **`markDerived` is not optional if the plugin adds series.** The core cannot tell a plugin's series from the caller's, and the host uses this to keep them out of the initial-series snapshot. Without it, `resetSeries()` hands the user the plugin's computed output as if it were their own data. It is idempotent; pass `[]` when the plugin's series are gone. It is also what puts the plugin's name on those series in `drawn()`.
230
+
231
+ Also fixed in v2: every Weave plugin silently did nothing on a pie, donut or radialBar, because those hold one number per entry and the snapshot called `.map` on it. Each slice is presented as a one-point series now.
232
+
233
+ ### v3: reserve room for the plugin's own UI
234
+
235
+ ```js
236
+ api.reserve({ right: 120 }) // px; pass null (or all zeros) to give it back
237
+ ```
238
+
239
+ A plugin rendering beside the chart cannot make room for itself: the chart sizes from the element the caller handed it, so a sibling inserted there does not narrow it. Reservations are per plugin and summed, so two plugins each asking for a right-hand gutter get one each, and the total is clamped so the chart keeps at least half the container on each axis. An unchanged box does nothing, so calling it on every render is free.
240
+
241
+ ### v4: what the viewer is pointing at
242
+
243
+ ```js
244
+ const off = api.pointer((e) => {
245
+ // e.type 'enter' | 'leave' | 'select'
246
+ // e.seriesIndex / e.dataPointIndex
247
+ // e.category resolved display label ('Mar'), not an index
248
+ // e.seriesName undefined on a pie
249
+ // e.selected on 'select' only: is the point now in or out
250
+ // e.modifiers { shift, ctrl, alt, meta } (v6)
251
+ })
252
+ off() // pointer() returns its unsubscribe
253
+ ```
254
+
255
+ Forwards the chart's own `dataPointMouseEnter`, `dataPointMouseLeave` and `dataPointSelection` as one payload, so a plugin does not hit-test the SVG and then disagree with the tooltip on the same pixel. **Nothing here can intercept or cancel**: the tooltip, the selection state and the caller's own `dataPoint*` events are unaffected, and a handler that throws is contained.
256
+
257
+ `category` is the resolved label because a plugin coordinating two charts keys on the label; an index means something different on every chart. `modifiers` is always all four booleans, never partial and never undefined (all false where there was no DOM event), because a plugin reads `e.modifiers.shift` inside a viewer's click and a sometimes-missing key is how that becomes a crash.
258
+
259
+ ### v6: set a positional option for your own series (`claim`)
260
+
261
+ `stroke.dashArray` and `dataLabels.enabledOnSeries` are indexed by series position with no per-series form, so a plugin wanting one value for its own series used to write the array covering every series and put the caller's back afterwards.
262
+
263
+ ```js
264
+ const claim = api.claim('stroke.dashArray', [
265
+ { series: 'Revenue (forecast)', value: 6 },
266
+ ])
267
+ claim.update([{ series: 'Revenue (forecast)', value: 4 }])
268
+ claim.release()
269
+ ```
270
+
271
+ The host resolves a claim **where the option is read**, so nothing is written: there is nothing to restore, releasing is a deletion, and the caller's own `updateOptions` composes with the claim instead of reverting it. Claims resolve in order, so two plugins on one series produce a defined winner rather than whichever ran last.
272
+
273
+ - **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.
274
+ - **Claimable options are an allowlist** (`stroke.dashArray`, `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. Check the return.
275
+ - 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.
276
+
277
+ `api.info.stroke` (v5) exists for the older read-and-restore pattern and reports what is configured, a scalar or one entry per series, copied on the way out. Prefer `claim()` where the host has it: the restore is stale the moment the caller calls `updateOptions`, it leaks if the plugin throws in between, and two plugins doing it at once fight.
278
+
279
+ ### v6: an inventory of what is on the chart (`drawn` / `declare`)
280
+
281
+ ```js
282
+ api.drawn() // [{ id, kind: 'series' | 'annotation' | 'overlay', label, owner, visible }, ...]
283
+
284
+ api.on('draw', () => {
285
+ api.declare({ id: 'trend-1', label: 'Trend' }) // declare from the draw handler
286
+ })
287
+ ```
288
+
289
+ `drawn()` reports the series, the caller's annotations and whatever plugins have declared. An ink stroke a viewer drew is an annotation too, but reports `owner: 'ink'` rather than `'core'` (fixed in v7.6.1): crediting a viewer's note to the caller is the one case the `owner` field exists to get right. **Every entry names its `owner`**, and that is the point rather than decoration: the list is only ever as complete as the features that opted in, so a layers panel or an export summary can say what its inventory covers instead of presenting a partial list as everything.
290
+
291
+ - **Read only, deliberately.** Removing or hiding another feature's output would mean one plugin reaching into another's state with no way for the owner to refuse.
292
+ - **Declare from the draw handler.** Declarations are cleared with the layers at the start of every draw, so an inventory cannot outlive the drawing it describes. Declaring the same id twice replaces it rather than growing a duplicate row.
293
+ - **Annotations carry no 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.
294
+
295
+ ### The types a plugin author works with
296
+
297
+ Declared in the shipped `apexcharts.d.ts`: `ApexPluginAPI` (the `setup(api)` facade), `ApexPluginSeries`, `ApexPluginScales`, `ApexPluginLayer`, `ApexPluginHook`, `ApexPluginPayload`, and for the v6 additions `ApexPluginChartInfo` (`api.info`), `ApexPluginPointerEvent` (the `pointer` payload), `ApexPluginClaim` (`{ release(), update(entries) }`) and `ApexDrawnItem` (a `drawn()` row).
298
+
299
+ ### Typing gap (still present at 8.0.0)
300
+
301
+ `api.categories` and `data[].raw` are real at runtime (both shipped in v2) but are **not declared** in the shipped `apexcharts.d.ts`: `ApexPluginAPI` has no `categories` member and `ApexPluginSeries` is typed as `{ name?, hidden, color?, points }`. TypeScript rejects both. Cast the facade where you need them, and do not read the absence as "removed".
191
302
 
192
303
  ---
193
304
 
@@ -204,7 +315,7 @@ import 'apexcharts/features/renderer-canvas'
204
315
  const options = {
205
316
  chart: {
206
317
  renderer: 'auto', // 'svg' | 'canvas' | 'auto'
207
- rendererThreshold: 5000, // point count above which 'auto' picks canvas
318
+ rendererThreshold: 8000, // point count above which 'auto' picks canvas (default 8000)
208
319
  },
209
320
  }
210
321
  // chart.getActiveRenderer() // => 'svg' | 'canvas'
@@ -261,7 +372,7 @@ Generic Ctrl-Z over a command journal. Zooms, series toggles, option changes, an
261
372
 
262
373
  ```js
263
374
  const options = {
264
- chart: { history: { enabled: true, maxDepth: 50, coalesceMs: 300, keyboard: true } },
375
+ chart: { history: { enabled: true, maxDepth: 100, coalesceMs: 250, keyboard: true } }, // those are the defaults
265
376
  }
266
377
 
267
378
  chart.history.undo(animate?) // and .redo(animate?)
@@ -299,6 +410,8 @@ ApexCharts.perspectives.decode(str)
299
410
  const options = { chart: { perspectives: { serializeOptions: ['colors', 'title'] } } }
300
411
  ```
301
412
 
413
+ Calling the static `decode` / `fromURL` counts as using Perspectives for every chart on the page that has the feature loaded, so an unlicensed page shows the watermark on those charts (a script-tag add-on included, since 7.9.1).
414
+
302
415
  ---
303
416
 
304
417
  ## Facet: design tokens and OS-aware themes
@@ -396,6 +509,55 @@ const b = {
396
509
 
397
510
  ---
398
511
 
512
+ ## Highlight filter
513
+
514
+ `import 'apexcharts/features/highlight-filter'` *(7.9)*: **Tier 2** (not in the default bundle; script tag `dist/features/highlight-filter.js`, or the full bundle) and **premium**. It watermarks only while a render draws parts: loading the feature, or drawing a chart with no parts, never does.
515
+
516
+ Each value is drawn faded at its whole, with a part of it solid in front, from the same baseline and in the same slot: what a dashboard shows when a pick elsewhere leaves part of every value behind. The part travels with the data, as `highlightData` on a series (parallel to `data`, `null` where a datum has none) or `highlight` on a point (`{ x, y, highlight }`, which wins), so a pick and a clear are ordinary updates. A part may exceed its whole (an average) or have the other sign. Parts alone switch it on; every option is optional.
517
+
518
+ ```js
519
+ import ApexCharts from 'apexcharts'
520
+ import 'apexcharts/features/highlight-filter'
521
+
522
+ const chart = new ApexCharts(el, {
523
+ chart: { type: 'bar' },
524
+ series: [{ name: 'Units', data: [34, 43, 66], highlightData: [26, null, 50] }],
525
+ xaxis: { categories: ['2015', '2016', '2017'] },
526
+ })
527
+ await chart.render()
528
+
529
+ // A new pick is an updateSeries() with new parts, or the helpers:
530
+ await chart.highlightFilter.set([[8, 14, 16]]) // one row per series
531
+ await chart.highlightFilter.clear() // the plain chart again
532
+ ```
533
+
534
+ A pie, donut, polarArea, radialBar or gauge has no per-datum slot for a part, so give one per slice or ring in `highlightFilter.data` (a `highlight` on a slice's own `{ x, y }` point wins). A nested treemap takes `highlight` on each leaf and ignores `highlightData` with a warning.
535
+
536
+ | Option | Default | Notes |
537
+ |---|---|---|
538
+ | `enabled` | `true` | `false` keeps the parts in the data but draws the chart as usual. |
539
+ | `data` | none | Circles only: one part per slice or ring. Ignored, with a warning, while its length differs from the slice count. |
540
+ | `fadeOpacity` | `0.2` | Opacity of the faded whole. |
541
+ | `outline` | `{ width: 1, opacity: 1 }` | Keeps a faded whole readable; `width: 0` for none. |
542
+ | `enter` | `'whole'` | First pick: each part starts as its whole and drains (or grows) to the part. `'baseline'` rises from the baseline. |
543
+ | `axis` | `'extend'` | A part past the value axis stretches the axis; `'clamp'` keeps the axis the wholes set. |
544
+ | `dataLabels` | `{ value: 'part', total: 'part' }` | Each `'part'` or `'whole'`. `total` is the stack total and the donut's centre total. |
545
+ | `tooltip` | `{ show: true, share: false }` | Each row reads "part / whole"; `share` appends the part's percentage; `formatter(part, whole, opts)` returns the row's value text instead (plain text or HTML); the series name stays. |
546
+
547
+ Per type:
548
+
549
+ - `line.dashArray` (`4`): a line chart draws the whole dashed at full strength, with the part solid over it.
550
+ - `pie.encoding` (`'radius'`): how a part's share sets its radius on pie and donut; `'area'` makes the part's area its share. polarArea follows its own value scale.
551
+ - `radialBar.indicator`: `'arc'` when unset (the part as a solid arc in front of its faded whole), except `'needle'` on a gauge with `plotOptions.radialBar.shape: 'needle'`. `'lanes'` splits each ring into an outer whole lane and an inner part lane, for a part that can exceed its whole (`lanes.opacity`, default 0.45; the word on the whole lane's tooltip row is the locale's `options.highlightFilter.part`). `needle: { color, length, baseWidth, tipWidth, offsetY }` falls back to `plotOptions.radialBar.needle`.
552
+
553
+ **Where it draws.** Column and bar (grouped, horizontal, distributed, negative values, stacked including 100% and groups), funnel and pyramid, line and area (every curve, stacked area too), pie, donut, polarArea, radialBar and gauge, and treemap (flat and nested). Any other type, mixed-type charts, range series, histograms, and stacked charts over more than one y axis or a logarithmic one warn once and draw as usual. `highlightData` cannot follow `chart.dataReducer` or `chart.streaming` and warns there: put the part on each point instead.
554
+
555
+ **API and event.** `chart.highlightFilter` is `{ isActive(), valueAt(seriesIndex, dataPointIndex?), set(parts, opts?), clear(opts?) }`, or `null` without the feature (parts are then ignored with one console warning naming the import). `set` takes one row per series, one value per slice or ring on a circle, or a function of the datum, and resolves when the update is drawn. `chart.events.highlightFilterChanged(chart, { active, phase })` fires once that render is drawn, with `phase` `'enter'`, `'update'` or `'clear'`. Slice and tile label formatters (pie, donut, polarArea, treemap) and the donut and radialBar centre value and total formatters receive `opts.highlight` (`{ value, total, share, overflow }`). In `tooltip.custom`, read the part with `opts.ctx.highlightFilter.valueAt(opts.seriesIndex, opts.dataPointIndex)`.
556
+
557
+ **Drilldown.** A drill, either way, drops the parts from the level it shows, including any written into a `drilldown.series` entry, and reports `'clear'` if parts were on screen. Send the pick for the new level with `chart.highlightFilter.set()`.
558
+
559
+ ---
560
+
399
561
  ## Ink: direct-manipulation annotation authoring
400
562
 
401
563
  `import 'apexcharts/features/ink'`: **Tier 2** (not in the default bundle) and **premium**.
@@ -537,13 +699,14 @@ const options = { chart: { streaming: { enabled: true, maxPoints: 100000 } } }
537
699
 
538
700
  ## Drilldown: hierarchical drill-down
539
701
 
540
- `import 'apexcharts/features/drilldown'` (add `import 'apexcharts/features/morph'` for animated cross-type transitions): both Tier 1 (already in the default bundle).
702
+ `import 'apexcharts/features/drilldown'`: **Tier 2 since 8.0** (no longer in the default bundle; script tag `dist/features/drilldown.js`, or the full bundle). Free. `apexcharts/features/morph`, for animated cross-type transitions, is Tier 1 and already in the default bundle. Without the drilldown import, a chart with `drilldown.enabled` warns once and draws its root level, which never navigates, and `chart.drillDown()` / `drillUp()` / `drillToRoot()` / `clearDrilldownCache()` throw.
541
703
 
542
704
  Data points reference a child level via a `drilldown` id; clicking drills in, with an optional breadcrumb. Since **v6.9** drilldown also works on **line and area** charts, and levels can be resolved **async** against a backend.
543
705
 
544
706
  ```js
545
- import 'apexcharts/features/drilldown'
546
- import 'apexcharts/features/morph' // optional, animated type morphs
707
+ import ApexCharts from 'apexcharts'
708
+ import 'apexcharts/features/drilldown' // Tier 2 since 8.0
709
+ import 'apexcharts/features/morph' // animated type morphs; already in the default bundle
547
710
 
548
711
  const options = {
549
712
  series: [{ data: [
@@ -563,7 +726,8 @@ const options = {
563
726
  // false disables it. role="status", aria-live="polite",
564
727
  // spinner flattens to a pulse under prefers-reduced-motion
565
728
  cache: true, // (v6.9) cache onDrillDown results by id (default true);
566
- // clear with the drilldown module's clearCache()
729
+ // clear with chart.clearDrilldownCache(id?), the public
730
+ // typed method (it delegates to the module's clearCache())
567
731
  // (v6.9) line/area only: the dot marking a drillable point when the chart
568
732
  // draws no markers. Only drillable points get one, so it reads as "these open".
569
733
  marker: { show: true, size: 6, shape: 'circle', fillColor: undefined, strokeColor: '#fff' },
@@ -585,6 +749,8 @@ chart.drillToRoot()
585
749
 
586
750
  **Async failure semantics (v6.9):** a failed fetch never strands the view. On a throw, a rejection, or a resolver returning something without a `data` array, the chart stays where it was, the breadcrumb is untouched, nothing is cached, and `drillDownError` fires. A second click while one resolve is in flight is ignored rather than starting a second request.
587
751
 
752
+ *(8.0)* A child level naming a Tier 2 chart type whose renderer the page has not loaded (`unit` / `waffle`, `sunburst`, `icicle`, `violin`, and `raincloud`, which draws through violin) is refused too: the update stops before anything moves, the level comes back off the stack, `drillDownError` fires with the error, and the `chart.drillDown(id)` promise rejects. A `waterfall`, `dumbbell` or `streamgraph` level without its feature is not refused: it navigates, then draws blank with one console warning. Load each level's chart type and feature as well as the drilldown feature.
753
+
588
754
  ---
589
755
 
590
756
  ## Stats: raw-sample statistics (6.9.0)
@@ -595,9 +761,9 @@ Free. The statistics behind three things, kept out of core so they cost nothing
595
761
 
596
762
  - **Histogram binning**: `chart.type: 'histogram'` bins raw observations (the `apexcharts/histogram` entry includes this feature automatically).
597
763
  - **Raw samples for boxPlot and violin**: a datum supplying `points: [number]` instead of a summary `y` gets its quartiles (R type 7) or KDE density computed.
598
- - **`chart.rowSeries(opts?)`**: returns the rows behind the chart's marks as a unit-chart series (one cluster per mark), or `null` when the type has no row source. `chart.updateOptions({ chart: { type: 'unit' }, series: chart.rowSeries() })` opens a summary into its observations; with the `morph` feature loaded each dot leaves from the part of the mark that stood for it.
764
+ - **`chart.rowSeries(opts?)`**: returns the rows behind the chart's marks as a unit-chart series (one cluster per mark), or `null` when the type has no row source. `chart.updateOptions({ chart: { type: 'unit' }, series: chart.rowSeries() })` opens a summary into its observations; with the `morph` feature loaded each dot leaves from the part of the mark that stood for it. Since 8.0 `unit` is Tier 2, so add `import 'apexcharts/unit'` (or `dist/unit.js`, or the full bundle) first. Without it the update is refused with a console error and the chart stays as it was.
599
765
 
600
- Full data shapes and options are in `references/financial-charts.md`. The full `apexcharts` bundle includes it.
766
+ Full data shapes and options are in `references/financial-charts.md`. The default `apexcharts` bundle includes it.
601
767
 
602
768
  ---
603
769
 
@@ -631,9 +797,9 @@ const options = {
631
797
 
632
798
  ## Common Pitfalls
633
799
 
634
- 1. **Assuming the full bundle still carries everything *(the #1 v6 → v7 mistake)*.** In v6, `import ApexCharts from 'apexcharts'` was enough for every feature. In v7 the nine Tier 2 features and the `raincloud` type need their own import on top of it. Read the console: each one names both routes when its config is present without it.
800
+ 1. **Assuming the default bundle carries everything *(the #1 upgrade mistake, v6 to v7 and again 7 to 8)*.** In v6, `import ApexCharts from 'apexcharts'` was enough for every feature. At 8.0 each of these needs its own import on top of it: the features `trellis`, `storyboard`, `perspectives`, `ink`, `renderer-canvas`, `link`, `measure`, `history`, `context-menu`, `highlight-filter`, `drilldown`, `waterfall`, `dumbbell` and `streamgraph`, and the `unit` / `waffle`, `sunburst`, `violin`, `icicle` and `raincloud` chart types. Read the console: each names the import, the script-tag file and `apexcharts.full.min.js`.
635
801
  2. **Forgetting the feature import on a tree-shaken build**: a Tier 1 feature is in the default bundle but not in `apexcharts/core` or a per-type entry, so those builds still need `import 'apexcharts/features/<name>'`.
636
- 3. **Reaching for `apexcharts/features/all` to fix either of the above**: it is the Tier 1 set only, by design (`tests/unit/feature-tier-budget.spec.js` upstream fails the build if a Tier 2 feature reappears in it).
802
+ 3. **Reaching for `apexcharts/features/all` to fix either of the above**: it is the Tier 1 set only, by design (`tests/unit/feature-tier-budget.spec.js` upstream fails the build if a Tier 2 feature reappears in it). The entry that does have everything is `apexcharts/full`.
637
803
  4. **Using `api.zeroY` in a custom mark**: it does not exist. Use `ctx.scales.y(0)` for the baseline.
638
804
  5. **Expecting `mode: 'filter'` on `chart.link` without records**: highlight mode needs `chart.selection.enabled: true`; the crossfilter engine needs `ApexCharts.crossfilter({ id, records })` or a `dimension` extractor.
639
805
  6. **Two different `largeDatasetThreshold` options.** `chart.animations.largeDatasetThreshold` (default 1000) skips update animations; `markers.largeDatasetThreshold` *(v7.0, default 0 = off)* batches a series' markers into one path. Neither lives on `chart` directly.
@@ -4,7 +4,7 @@
4
4
 
5
5
  - **Candlestick** (`'candlestick'`) — OHLC (Open, High, Low, Close) financial chart
6
6
  - **Box Plot** (`'boxPlot'`) — Statistical distribution chart showing min, Q1, median, Q3, max
7
- - **Violin** (`'violin'`, **new in v6**): Statistical distribution chart showing a density curve per category, optionally with the raw sample points overlaid as jitter
7
+ - **Violin** (`'violin'`, **new in v6, Tier 2 since v8.0**): Statistical distribution chart showing a density curve per category, optionally with the raw sample points overlaid as jitter
8
8
  - **Histogram** (`'histogram'`, **new in v6.9**): Bins raw observations into columns; the series carries the sample, the chart does the aggregating
9
9
  - **Raincloud** (`'raincloud'`, **new in v7.1, premium, Tier 2**): A distribution shown three ways at once: a half-violin for the shape, a box for the summary, and the observations themselves as "rain" underneath
10
10
 
@@ -15,14 +15,19 @@ import ApexCharts from 'apexcharts/candlestick'
15
15
  // Registers: candlestick, boxPlot
16
16
  // Alias: apexcharts/boxPlot
17
17
 
18
- import ApexCharts from 'apexcharts/violin'
19
- // Registers: violin (separate entry point, new in v6)
20
-
21
18
  import ApexCharts from 'apexcharts/histogram'
22
19
  // Registers: histogram (v6.9), the bar engine plus the stats feature
23
20
 
24
- import ApexCharts from 'apexcharts/raincloud'
25
- // Registers: raincloud (v7.1), the violin engine plus the raincloud feature
21
+ // violin is Tier 2 since v8.0 and raincloud since v7.1: neither is in the default bundle.
22
+ // Add the entry next to the default bundle:
23
+ import ApexCharts from 'apexcharts'
24
+ import 'apexcharts/violin' // Registers: violin (new in v6)
25
+ import 'apexcharts/raincloud' // Registers: raincloud (v7.1), the violin engine plus the raincloud feature
26
+
27
+ // Tree-shaking: violin and raincloud draw on the bar renderer without
28
+ // bundling one, so pair them with apexcharts/bar
29
+ import ApexCharts from 'apexcharts/bar'
30
+ import 'apexcharts/violin' // or 'apexcharts/raincloud'
26
31
 
27
32
  // The statistics (histogram binning, boxPlot/violin raw-sample summaries,
28
33
  // rowSeries()) live behind one optional feature. apexcharts/histogram already
@@ -30,9 +35,11 @@ import ApexCharts from 'apexcharts/raincloud'
30
35
  import 'apexcharts/features/stats'
31
36
  ```
32
37
 
33
- **⚠️ Raincloud is Tier 2 (v7.1).** Unlike every other chart type, it is **not in the default `apexcharts` bundle**. On the full bundle add `import 'apexcharts/raincloud'` (or `import 'apexcharts/features/raincloud'`); from a script tag add `dist/features/raincloud.js` after `apexcharts.js`. It is also premium, so it renders an `APEXCHARTS` watermark until an entitled license is set.
38
+ **⚠️ Violin is Tier 2 since v8.0.** `import ApexCharts from 'apexcharts'` no longer registers it, so a `chart.type: 'violin'` chart draws nothing and the console names the import. Add `import 'apexcharts/violin'` (script tag: `dist/violin.js`), or use the full bundle (`apexcharts/full`, `dist/apexcharts.full.min.js`). Plain violins stay free.
39
+
40
+ **⚠️ Raincloud is Tier 2 (v7.1).** Since v8.0 the default bundle carries neither the raincloud feature nor the violin renderer it draws through. Next to `import ApexCharts from 'apexcharts'` add `import 'apexcharts/raincloud'`, which brings both; `apexcharts/features/raincloud` alone is enough only where violin is already registered. Script tag: load `dist/violin.js`, then `dist/features/raincloud.js`. Or use the full bundle (`apexcharts/full`, `dist/apexcharts.full.min.js`), which has both. On the lean core also import `apexcharts/bar`. It is also premium, so it renders an `APEXCHARTS` watermark until an entitled license is set.
34
41
 
35
- **Raw samples (v6.9):** boxPlot, violin, and histogram can all take the raw observations and compute the statistics themselves (quartiles for boxPlot, KDE density for violin, bin counts for histogram). Precomputed summaries keep working exactly as before. With the full `apexcharts` bundle the stats feature is always present; with tree-shaking entries it must be imported (without it, a raw-sample boxPlot/violin datum has nothing to summarize and a histogram warns and draws nothing).
42
+ **Raw samples (v6.9):** boxPlot, violin, and histogram can all take the raw observations and compute the statistics themselves (quartiles for boxPlot, KDE density for violin, bin counts for histogram). Precomputed summaries keep working exactly as before. The default `apexcharts` bundle (and the full bundle) always has the stats feature; with tree-shaking entries it must be imported (without it, a raw-sample boxPlot/violin draws nothing, with a console warning naming the import since v8.0, and a histogram warns and draws nothing).
36
43
 
37
44
  ---
38
45
 
@@ -125,6 +132,8 @@ Each point is `{ x, y: { density, points } }`:
125
132
  - `points` (optional): a flat array of raw observations, rendered as jitter dots.
126
133
 
127
134
  ```js
135
+ import 'apexcharts/violin' // Tier 2 since v8.0: not in the default bundle
136
+
128
137
  {
129
138
  chart: { type: 'violin', height: 420 },
130
139
  series: [{
@@ -161,6 +170,8 @@ Each point is `{ x, y: { density, points } }`:
161
170
  A datum may instead supply only the raw observations (in `points`, no `y`) and the library derives the density itself via kernel density estimation. Requires `apexcharts/features/stats` when tree-shaking. `plotOptions.violin.kde` tunes the estimation; it is ignored when a precomputed `density` profile is given.
162
171
 
163
172
  ```js
173
+ import 'apexcharts/violin' // Tier 2 since v8.0: not in the default bundle
174
+
164
175
  {
165
176
  chart: { type: 'violin', height: 420 },
166
177
  series: [{
@@ -196,7 +207,7 @@ plotOptions: {
196
207
 
197
208
  ### Histogram (v6.9)
198
209
 
199
- The series carries **raw observations** (a flat number array, or `{ y }` objects), one number per event; the chart chooses the bin width and counts them. It renders through the bar pathway (like funnel, pyramid, gauge, and waffle), so stacking, zoom, export, and animation all work on the bins. Import from `apexcharts/histogram`, or add `import 'apexcharts/features/stats'` alongside `apexcharts/bar`; the full `apexcharts` bundle already includes it. Without the stats feature the chart warns and draws nothing.
210
+ The series carries **raw observations** (a flat number array, or `{ y }` objects), one number per event; the chart chooses the bin width and counts them. It renders through the bar pathway (like funnel and pyramid), so stacking, zoom, export, and animation all work on the bins. Import from `apexcharts/histogram`, or add `import 'apexcharts/features/stats'` alongside `apexcharts/bar`; the default `apexcharts` bundle already includes it. Without the stats feature the chart warns and draws nothing.
200
211
 
201
212
  ```js
202
213
  {
@@ -228,7 +239,7 @@ The series carries **raw observations** (a flat number array, or `{ y }` objects
228
239
 
229
240
  ```js
230
241
  import ApexCharts from 'apexcharts'
231
- import 'apexcharts/raincloud' // Tier 2: required even on the full bundle
242
+ import 'apexcharts/raincloud' // Tier 2: not in the default bundle (apexcharts/full already has it)
232
243
 
233
244
  new ApexCharts(el, {
234
245
  chart: { type: 'raincloud', height: 350 },
@@ -286,9 +297,12 @@ The rain **is** the outlier display, so the box draws no outlier dots of its own
286
297
  A histogram bin, a box, and a violin all stand for rows the chart is already holding. `chart.rowSeries()` returns them as a unit-chart series (one cluster per mark, one datum per row), so a summary can be opened into its observations:
287
298
 
288
299
  ```js
300
+ import 'apexcharts/unit' // Tier 2 since v8.0: the target type must be loaded
289
301
  chart.updateOptions({ chart: { type: 'unit' }, series: chart.rowSeries() })
290
302
  ```
291
303
 
304
+ Since v8.0 the unit type is not in the default bundle, so load it first (`apexcharts/unit`, `dist/unit.js`, or the full bundle). Without it the update is refused (the promise rejects and the console names the import) and the chart stays as it was.
305
+
292
306
  Returns `null` when the current type has no row source or the stats feature is not loaded. `chart.rowSeries({ maxRows })` caps the dots produced (default 3000); past the cap every cluster is thinned by one shared stride so relative sizes survive. With the `morph` feature loaded, each dot animates out of the part of the mark that stood for it.
293
307
 
294
308
  ---
@@ -324,6 +338,8 @@ plotOptions: {
324
338
  }
325
339
  ```
326
340
 
341
+ **Updates animate (v7.9):** on `updateSeries()` each box morphs from its previous shape. Set `chart: { animations: { dynamicAnimation: { enabled: false } } }` to snap instead.
342
+
327
343
  ---
328
344
 
329
345
  ## Complete Working Example — Candlestick Chart
@@ -382,7 +398,7 @@ plotOptions: {
382
398
  7. **Violin orientation set on the wrong namespace**: horizontal violins and per-violin colors come from `plotOptions.bar` (`horizontal`, `distributed`), while `bandwidthScale`, `kde`, `normalize`, and `points` live under `plotOptions.violin`.
383
399
  8. **Feeding a histogram pre-binned counts**: the series must carry raw observations. If you already have counts per bucket, use a `bar` chart; a histogram would bin your counts as if they were measurements.
384
400
  9. **`whiskers: 'tukey'` on a precomputed boxPlot summary**: `plotOptions.boxPlot.whiskers` only applies when the summary is derived from `points`. A five-number `y` is drawn exactly as given.
385
- 10. **Raw samples with tree-shaking but no stats feature**: `apexcharts/candlestick` and `apexcharts/violin` alone cannot summarize `points`. Add `import 'apexcharts/features/stats'` (the `apexcharts/histogram` entry and the full bundle already include it).
386
- 11. **Expecting `raincloud` to work on the full bundle** *(v7.1)*: it is the one chart type that is Tier 2. `import ApexCharts from 'apexcharts'` alone gives you a console warning and no raincloud. Add `import 'apexcharts/raincloud'`.
401
+ 10. **Raw samples with tree-shaking but no stats feature**: `apexcharts/candlestick`, and `apexcharts/bar` plus `apexcharts/violin`, cannot summarize `points`. Add `import 'apexcharts/features/stats'` (the `apexcharts/histogram` entry and the default bundle already include it). Since v8.0 the console names the missing import.
402
+ 11. **Expecting `violin` or `raincloud` on the default bundle** *(v8.0)*: `import ApexCharts from 'apexcharts'` registers neither, so the chart draws nothing and the console reports `chart type "violin" is not registered` (for a raincloud, `chart type "raincloud" is not registered (it draws through "violin")`) with the import to add. That is the same failure an unregistered `icicle` gives. Add `import 'apexcharts/violin'` for a plain violin or `import 'apexcharts/raincloud'` for a raincloud, or use the full bundle (`apexcharts/full`).
387
403
  12. **Looking for `plotOptions.raincloud`**: there is none. Raincloud is a preset over the violin engine, so configure it through `plotOptions.violin` (`side`, `box`, `points`, `kde`, `normalize`).
388
404
  13. **Giving a raincloud a `y`**: it takes the raw sample per category: `data: [{ x, points: [number] }]`. A precomputed `y.summary` is honored for the box, but the density and the rain need the observations.
@@ -10,6 +10,30 @@
10
10
 
11
11
  **Important:** All wrappers require `apexcharts` as a peer dependency. Install both packages.
12
12
 
13
+ **With apexcharts 8**, use ng-apexcharts 3.2.0 or later (Angular 20+): 3.1.x accepts apexcharts 6 or 7 only, so npm reports a peer conflict. react-apexcharts (2.1.1) and vue3-apexcharts (1.11.2) accept any apexcharts from 5.10 up, and vue-apexcharts (Vue 2) any from 4.0.
14
+
15
+ ---
16
+
17
+ ## Opt-in chart types and features (apexcharts 8)
18
+
19
+ The wrappers' standard components load the default ApexCharts bundle, which since 8.0 leaves out some chart types and features (see `references/tree-shaking.md`). Until the app imports the add-on, a wrapper chart of type `unit`, `waffle`, `sunburst`, `violin`, `icicle` or `raincloud` fails to render, and `waterfall`, `dumbbell` or `streamgraph` draws an empty plot. Import it once anywhere in the app (`main.ts`, or the component file): it registers on the ApexCharts the wrapper uses.
20
+
21
+ | You use | Add once |
22
+ |---|---|
23
+ | `unit`/`waffle`, `sunburst`, `violin`, `icicle` | `import 'apexcharts/unit'` (or `/sunburst`, `/violin`, `/icicle`) |
24
+ | `raincloud` | `import 'apexcharts/raincloud'` |
25
+ | `waterfall`, `dumbbell`, `streamgraph`, drilldown or another Tier 2 feature | `import 'apexcharts/features/<name>'` (`highlightFilter` is `features/highlight-filter`) |
26
+ | Everything | `import 'apexcharts/full'`, which reuses the default bundle the wrapper already loads |
27
+
28
+ ```jsx
29
+ import Chart from 'react-apexcharts'
30
+ import 'apexcharts/sunburst' // once per app
31
+
32
+ export default function Breakdown({ options, series }) {
33
+ return <Chart type="sunburst" options={options} series={series} height={350} />
34
+ }
35
+ ```
36
+
13
37
  ---
14
38
 
15
39
  ## React
@@ -218,7 +242,7 @@ updateChart() {
218
242
  <div id="chart"></div>
219
243
 
220
244
  <!-- CDN -->
221
- <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
245
+ <script src="https://cdn.jsdelivr.net/npm/apexcharts@8/dist/apexcharts.min.js"></script>
222
246
 
223
247
  <script>
224
248
  const options = {
@@ -234,18 +258,24 @@ updateChart() {
234
258
 
235
259
  ### CDN Links
236
260
 
261
+ ```html
262
+ <!-- default bundle -->
263
+ <script src="https://cdn.jsdelivr.net/npm/apexcharts@8/dist/apexcharts.min.js"></script>
264
+ <!-- one add-on next to it: an opt-in chart type -->
265
+ <script src="https://cdn.jsdelivr.net/npm/apexcharts@8/dist/sunburst.js"></script>
266
+
267
+ <!-- or the full bundle: every chart type and feature, no add-ons -->
268
+ <script src="https://cdn.jsdelivr.net/npm/apexcharts@8/dist/apexcharts.full.min.js"></script>
237
269
  ```
238
- https://cdn.jsdelivr.net/npm/apexcharts
239
- https://cdn.jsdelivr.net/npm/apexcharts@5/dist/apexcharts.min.js
240
- https://unpkg.com/apexcharts
241
- ```
270
+
271
+ Pin the major: the unversioned URLs (`https://cdn.jsdelivr.net/npm/apexcharts`, `https://unpkg.com/apexcharts`) moved to 8.0 on its release. Use `apexcharts@7` to stay on 7. The other add-ons are `dist/unit.js`, `dist/violin.js`, `dist/icicle.js` and `dist/features/<name>.js`, and may load before or after the main script.
242
272
 
243
273
  ---
244
274
 
245
275
  ## Common Framework Pitfalls
246
276
 
247
277
  1. **Missing peer dependency** — `react-apexcharts`, `vue3-apexcharts`, and `ng-apexcharts` all require `apexcharts` as a separate dependency.
248
- 2. **SSR with wrapper components** — most wrappers access `window`/`document` and crash in SSR. Use dynamic imports with `{ ssr: false }` or the native SSR API.
278
+ 2. **SSR with wrapper components**: the standard components access `window`/`document` and can crash in SSR. Use dynamic imports with `{ ssr: false }`, the native SSR API, or the wrapper's SSR components: `react-apexcharts/server`, vue3-apexcharts `<apexchart-server>` and `<apexchart-hydrate>` (1.11.2 or later, which render with your installed apexcharts), ng-apexcharts `<apx-chart-ssr>` and `<apx-chart-hydrate>`. They load `apexcharts/ssr`, so with apexcharts 8 a Tier 2 type needs its opt-in import on the server too. For React hydration call `apexcharts/ssr` directly: `react-apexcharts/hydrate` (2.1.1) loads `apexcharts/client`, which has no `hydrate()`.
249
279
  3. **Not destroying on unmount (vanilla API in React/Vue)** — causes memory leaks and duplicate charts. The official wrappers handle this automatically.
250
280
  4. **Mutating config objects in place (Vue)** — deep mutations may not trigger reactivity. Replace the entire object or use `ref` at the right level.
251
281
  5. **Rendering in hidden containers** — charts rendered in hidden tabs/modals have zero dimensions. Call `chart.updateOptions({})` or trigger a resize after showing the container.