apexcharts-skill 2.2.0 → 3.1.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.
- package/.cursorrules +57 -19
- package/README.md +14 -10
- package/SKILL.md +165 -66
- package/index.js +1 -1
- package/package.json +1 -1
- package/references/bar-charts.md +130 -5
- package/references/cartesian-charts.md +96 -1
- package/references/circular-charts.md +52 -1
- package/references/{v6-features.md → feature-platform.md} +233 -30
- package/references/financial-charts.md +68 -0
- package/references/framework-wrappers.md +1 -1
- package/references/grid-charts.md +91 -1
- package/references/ssr.md +18 -9
- package/references/tree-shaking.md +119 -28
|
@@ -1,11 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Feature Platform: ApexCharts
|
|
2
2
|
|
|
3
|
-
ApexCharts
|
|
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.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## ⚠️ Read first: two bundle tiers (v7.0)
|
|
6
|
+
|
|
7
|
+
Whether you need the `features/*` import depends on the feature's **tier**, and this changed in v7.0.
|
|
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` (premium) and `icicle` (free) chart types require an explicit import **whatever entry point you used**, the full bundle included.
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import ApexCharts from 'apexcharts'
|
|
14
|
+
import 'apexcharts/features/trellis' // Tier 2: required even here
|
|
15
|
+
```
|
|
16
|
+
```html
|
|
17
|
+
<script src=".../dist/apexcharts.js"></script>
|
|
18
|
+
<script src=".../dist/features/trellis.js"></script>
|
|
19
|
+
```
|
|
20
|
+
|
|
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.
|
|
22
|
+
|
|
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. It is about 270 KB at 7.6.1 (measured on the shipped dist with gzip -9; the upstream build report gzips at a different level and prints a few hundred bytes more). See `references/tree-shaking.md` for the full tier table and per-module sizes.
|
|
24
|
+
|
|
25
|
+
## Two v7.0 breaking changes
|
|
26
|
+
|
|
27
|
+
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
|
+
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
|
+
|
|
30
|
+
## Behaviors that are on by default
|
|
31
|
+
|
|
32
|
+
Carried over from v6, both respecting `prefers-reduced-motion`:
|
|
6
33
|
|
|
7
34
|
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`.
|
|
8
|
-
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 `
|
|
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 `'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.
|
|
9
36
|
|
|
10
37
|
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.
|
|
11
38
|
|
|
@@ -18,10 +45,11 @@ Two update behaviors worth knowing (both 6.9/6.10):
|
|
|
18
45
|
|
|
19
46
|
## Licensing: premium features and the trial watermark
|
|
20
47
|
|
|
21
|
-
Introduced in **6.5.0**, tightened in **6.7.0**. Not every
|
|
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.
|
|
22
49
|
|
|
23
50
|
**The premium set (watermarked until entitled):**
|
|
24
51
|
|
|
52
|
+
- `trellis` (small multiples, premium since **7.0.0**)
|
|
25
53
|
- `storyboard` (scrollytelling)
|
|
26
54
|
- `link` (crossfilter / linked views)
|
|
27
55
|
- `ink` (annotation authoring)
|
|
@@ -30,8 +58,11 @@ Introduced in **6.5.0**, tightened in **6.7.0**. Not every v6 feature is free. S
|
|
|
30
58
|
- `perspectives` (shareable view state)
|
|
31
59
|
- `history` (undo/redo, Rewind)
|
|
32
60
|
- the `unit` / `waffle` chart type (premium since **6.6.0**, the first premium chart type)
|
|
61
|
+
- the `raincloud` chart type (premium since **7.1.0**)
|
|
33
62
|
|
|
34
|
-
**Everything else is free and never gated**: every other chart type (
|
|
63
|
+
**Everything else is free and never gated**: every other chart type (`sunburst`, the v7.1 `waterfall` / `dumbbell` / `streamgraph`, and the v7.6 `icicle`, which is an opt-in import but free), and every other module (Weave, Strata / canvas, Marks, Facet / themes, Cadence, drilldown, streaming, exports, legend, toolbar, annotations, keyboard, stats).
|
|
64
|
+
|
|
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.
|
|
35
66
|
|
|
36
67
|
### Setting a license
|
|
37
68
|
|
|
@@ -55,9 +86,73 @@ As of **6.7.0** the premium features clear the watermark only on a `premium` or
|
|
|
55
86
|
|
|
56
87
|
---
|
|
57
88
|
|
|
89
|
+
## Trellis: small multiples that agree to the pixel (7.0)
|
|
90
|
+
|
|
91
|
+
`import 'apexcharts/features/trellis'`: **Tier 2** (not in the default bundle) and **premium**.
|
|
92
|
+
|
|
93
|
+
One dataset split into a grid of real charts that share a scale, a legend, a toolbar and a crosshair. Setting `trellis.by` makes the chart a trellis **host**: the series array is split into one panel per facet-key value, every panel is a real chart of the host's `chart.type`, and the trellis owns everything shared.
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
import ApexCharts from 'apexcharts'
|
|
97
|
+
import 'apexcharts/features/trellis'
|
|
98
|
+
|
|
99
|
+
new ApexCharts(el, {
|
|
100
|
+
chart: { type: 'line' },
|
|
101
|
+
series: [
|
|
102
|
+
{ name: 'Revenue', region: 'North', data: north },
|
|
103
|
+
{ name: 'Revenue', region: 'South', data: south },
|
|
104
|
+
],
|
|
105
|
+
trellis: { by: 'region', minPanelWidth: 260 },
|
|
106
|
+
}).render()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`trellis.by` is the whole configuration. The grid owns the y domain (the union across panels), the x window, and a column count computed from `minPanelWidth` alone. **A series carrying no facet key repeats in every panel**, which is how you get a reference line.
|
|
110
|
+
|
|
111
|
+
**In TypeScript**, use the typed `facet` field rather than an arbitrary key, or the series objects will not typecheck:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
series: [
|
|
115
|
+
{ name: 'Revenue', facet: 'North', data: north },
|
|
116
|
+
{ name: 'Revenue', facet: 'South', data: south },
|
|
117
|
+
],
|
|
118
|
+
trellis: { by: 'facet', minPanelWidth: 260 },
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Facet accessors.** `by` is a key name on each series object (`facet` is the blessed typed field; any other key name works from plain JS) or a function `(series, index) => key`, which works from either. For a 2-D grid use `row` × `column` instead of `by`: every (row, column) combination in row-major order at a fixed column count, column labels once across the top, row labels once down the left. A series carrying only the row key repeats across that row.
|
|
122
|
+
|
|
123
|
+
**Tidy-row input.** `trellis.data` takes a row table instead of `series`, pivoted by the `by` / `x` / `y` / `seriesBy` **column names**. Rows win over `series` when both are given; duplicate (panel, series, x) rows keep the last and warn, so aggregate first.
|
|
124
|
+
|
|
125
|
+
| Option | Default | Notes |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| `columns` | `'auto'` | `'auto'` fits `minPanelWidth` columns into the container. |
|
|
128
|
+
| `minPanelWidth` | `220` | Drives `'auto'` columns and the responsive collapse. |
|
|
129
|
+
| `gap` / `aspectRatio` / `panelHeight` | `12` / `1.6` / none | `panelHeight` wins over `aspectRatio` and `chart.height`. |
|
|
130
|
+
| `order` | `'first-seen'` | Also `'asc'`, `'desc'`, an explicit `string[]`, or a comparator. |
|
|
131
|
+
| `limit` | none | Render only the first N panels (warns about the rest). |
|
|
132
|
+
| `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.** |
|
|
133
|
+
| `scales` | `{ x: 'shared', y: 'shared' }` | `y` also takes `'independent-row'` / `'independent-column'` in a 2-D grid. Non-shared `y` still renders pixel-aligned panels. |
|
|
134
|
+
| `emptyPanels` | `'placeholder'` | Missing (row, column) combos: a real empty panel with `noData.text`, or `'skip'` (tinted blank) or `'hide'`. |
|
|
135
|
+
| `axes.labels` | `'edges'` | y labels on the first column, x labels on each column's bottom panel. Label *space* is always reserved everywhere so panels stay aligned. Also `'all'` / `'none'`. |
|
|
136
|
+
| `legend` / `toolbar` | `'shared'` | One legend (toggles a series name in every panel) and one zoom/pan/reset toolbar. `'none'` to drop. |
|
|
137
|
+
| `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
|
+
| `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. |
|
|
140
|
+
| `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
|
+
| `targetTicks` | `3` | Tick-interval target for the shared nice y scale, so a small panel wears few labels. |
|
|
142
|
+
| `header` | shown | `{ show, formatter(key, { dimension, index, count }), style }`. |
|
|
143
|
+
| `panel` | none | `(key, { index, seriesNames }) => ApexOptions`, a per-panel override applied last. |
|
|
144
|
+
|
|
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.
|
|
146
|
+
|
|
147
|
+
**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
|
+
|
|
149
|
+
**Per-type guardrails** keep a shared frame honest: histograms share a bin frame, violins a bandwidth, heatmaps one colour scale and a single gradient legend.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
58
153
|
## Weave: public plugin platform
|
|
59
154
|
|
|
60
|
-
`import 'apexcharts/features/weave'
|
|
155
|
+
`import 'apexcharts/features/weave'`: Tier 1 (already in the default bundle).
|
|
61
156
|
|
|
62
157
|
Publish reusable chart plugins to npm against a stable, versioned API. A plugin draws into its own sandboxed layer and subscribes to lifecycle hooks; it never touches raw internal state.
|
|
63
158
|
|
|
@@ -86,19 +181,123 @@ The `setup(api)` facade:
|
|
|
86
181
|
|
|
87
182
|
- `api.on(hook, fn)` / `api.off(hook, fn)`: hooks: `'afterParse' | 'afterScales' | 'draw' | 'afterUpdate' | 'destroy'`.
|
|
88
183
|
- `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.
|
|
89
|
-
- `api.scales` (readonly): `x(v)`, `y(v, axis?)`, `domainX`, `domainY(axis?)`, `gridWidth`, `gridHeight`, `ratios`.
|
|
90
|
-
- `api.data` (readonly): `[{ name?, hidden, color?, points: [{ x, y }] }]`.
|
|
184
|
+
- `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.
|
|
185
|
+
- `api.data` (readonly): `[{ name?, hidden, color?, points: [{ x, y }], raw }]`.
|
|
91
186
|
- `api.theme`: `{ mode, foreColor, seriesColor(i), token(name) }`.
|
|
92
187
|
- `api.store`: per-instance mutable state bag.
|
|
93
188
|
- `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.
|
|
94
189
|
|
|
95
|
-
|
|
190
|
+
### Declare the lowest version you can run on, then feature-detect (v2+)
|
|
191
|
+
|
|
192
|
+
`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:
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
// v6 hosts: ask by name.
|
|
196
|
+
if (api.can('claim')) { /* ... */ }
|
|
197
|
+
api.capabilities // the whole frozen list, for logging and support
|
|
198
|
+
|
|
199
|
+
// Older hosts, or a plugin that must run on both: probe the member.
|
|
200
|
+
if (typeof api.pointer === 'function') { /* ... */ }
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
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.
|
|
204
|
+
|
|
205
|
+
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.
|
|
206
|
+
|
|
207
|
+
### v2: know what you are attached to
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
api.info // { type, axisChart, datetimeX, horizontalBars,
|
|
211
|
+
// dataLabels: { enabled, enabledOnSeries },
|
|
212
|
+
// stroke: { dashArray }, // v5
|
|
213
|
+
// title } // v6, '' when untitled
|
|
214
|
+
api.categories // display labels per x position, resolved config-first
|
|
215
|
+
api.data[0].raw // the caller's own data array for that series, unparsed
|
|
216
|
+
api.markDerived(['Revenue (forecast)']) // these series are mine, not the caller's
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- **`info` is how a plugin decides whether it applies at all**, instead of adding a series and letting the core warn at the user.
|
|
220
|
+
- **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.
|
|
221
|
+
- **`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.
|
|
222
|
+
- **`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()`.
|
|
223
|
+
|
|
224
|
+
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.
|
|
225
|
+
|
|
226
|
+
### v3: reserve room for the plugin's own UI
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
api.reserve({ right: 120 }) // px; pass null (or all zeros) to give it back
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
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.
|
|
233
|
+
|
|
234
|
+
### v4: what the viewer is pointing at
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
const off = api.pointer((e) => {
|
|
238
|
+
// e.type 'enter' | 'leave' | 'select'
|
|
239
|
+
// e.seriesIndex / e.dataPointIndex
|
|
240
|
+
// e.category resolved display label ('Mar'), not an index
|
|
241
|
+
// e.seriesName undefined on a pie
|
|
242
|
+
// e.selected on 'select' only: is the point now in or out
|
|
243
|
+
// e.modifiers { shift, ctrl, alt, meta } (v6)
|
|
244
|
+
})
|
|
245
|
+
off() // pointer() returns its unsubscribe
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
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.
|
|
249
|
+
|
|
250
|
+
`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.
|
|
251
|
+
|
|
252
|
+
### v6: set a positional option for your own series (`claim`)
|
|
253
|
+
|
|
254
|
+
`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.
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
const claim = api.claim('stroke.dashArray', [
|
|
258
|
+
{ series: 'Revenue (forecast)', value: 6 },
|
|
259
|
+
])
|
|
260
|
+
claim.update([{ series: 'Revenue (forecast)', value: 4 }])
|
|
261
|
+
claim.release()
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
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.
|
|
265
|
+
|
|
266
|
+
- **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.
|
|
267
|
+
- **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.
|
|
268
|
+
- 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.
|
|
269
|
+
|
|
270
|
+
`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.
|
|
271
|
+
|
|
272
|
+
### v6: an inventory of what is on the chart (`drawn` / `declare`)
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
api.drawn() // [{ id, kind: 'series' | 'annotation' | 'overlay', label, owner, visible }, ...]
|
|
276
|
+
|
|
277
|
+
api.on('draw', () => {
|
|
278
|
+
api.declare({ id: 'trend-1', label: 'Trend' }) // declare from the draw handler
|
|
279
|
+
})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`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.
|
|
283
|
+
|
|
284
|
+
- **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.
|
|
285
|
+
- **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.
|
|
286
|
+
- **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.
|
|
287
|
+
|
|
288
|
+
### The types a plugin author works with
|
|
289
|
+
|
|
290
|
+
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).
|
|
291
|
+
|
|
292
|
+
### Typing gap at 7.6.1
|
|
293
|
+
|
|
294
|
+
`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".
|
|
96
295
|
|
|
97
296
|
---
|
|
98
297
|
|
|
99
298
|
## Strata: hybrid SVG + canvas renderer
|
|
100
299
|
|
|
101
|
-
`import 'apexcharts/features/renderer-canvas'
|
|
300
|
+
`import 'apexcharts/features/renderer-canvas'`: **Tier 2** (not in the default bundle). Free.
|
|
102
301
|
|
|
103
302
|
Break the SVG node ceiling without leaving SVG behind. Below a threshold the output is identical SVG; above it, only the series layer becomes a `<canvas>` while axes, grid, tooltips, annotations, and exports stay SVG.
|
|
104
303
|
|
|
@@ -109,7 +308,7 @@ import 'apexcharts/features/renderer-canvas'
|
|
|
109
308
|
const options = {
|
|
110
309
|
chart: {
|
|
111
310
|
renderer: 'auto', // 'svg' | 'canvas' | 'auto'
|
|
112
|
-
rendererThreshold:
|
|
311
|
+
rendererThreshold: 8000, // point count above which 'auto' picks canvas (default 8000)
|
|
113
312
|
},
|
|
114
313
|
}
|
|
115
314
|
// chart.getActiveRenderer() // => 'svg' | 'canvas'
|
|
@@ -121,7 +320,7 @@ Canvas is live for line, area, bar, column, scatter, and candlestick, with share
|
|
|
121
320
|
|
|
122
321
|
## Marks: composable custom series types
|
|
123
322
|
|
|
124
|
-
`import 'apexcharts/features/marks'
|
|
323
|
+
`import 'apexcharts/features/marks'`: Tier 1 (already in the default bundle).
|
|
125
324
|
|
|
126
325
|
Register a `renderItem(ctx)` function and get a first-class series: events, shared tooltip, legend, and keyboard navigation all work with no extra wiring. Dumbbell, lollipop, and bullet ship as samples.
|
|
127
326
|
|
|
@@ -158,7 +357,7 @@ Dumbbell uses `dataType: 'rangeXY'` (datum `y` is `[start, end]`, both bounds fo
|
|
|
158
357
|
|
|
159
358
|
## Rewind: history and undo/redo
|
|
160
359
|
|
|
161
|
-
`import 'apexcharts/features/history'
|
|
360
|
+
`import 'apexcharts/features/history'`: **Tier 2** (not in the default bundle) and **premium**.
|
|
162
361
|
|
|
163
362
|
**Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
|
|
164
363
|
|
|
@@ -166,7 +365,7 @@ Generic Ctrl-Z over a command journal. Zooms, series toggles, option changes, an
|
|
|
166
365
|
|
|
167
366
|
```js
|
|
168
367
|
const options = {
|
|
169
|
-
chart: { history: { enabled: true, maxDepth:
|
|
368
|
+
chart: { history: { enabled: true, maxDepth: 100, coalesceMs: 250, keyboard: true } }, // those are the defaults
|
|
170
369
|
}
|
|
171
370
|
|
|
172
371
|
chart.history.undo(animate?) // and .redo(animate?)
|
|
@@ -181,7 +380,7 @@ chart.history.entries() // [{ id, label, at }]
|
|
|
181
380
|
|
|
182
381
|
## Perspectives: shareable view state
|
|
183
382
|
|
|
184
|
-
`import 'apexcharts/features/perspectives'
|
|
383
|
+
`import 'apexcharts/features/perspectives'`: **Tier 2** (not in the default bundle) and **premium**.
|
|
185
384
|
|
|
186
385
|
**Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
|
|
187
386
|
|
|
@@ -208,7 +407,7 @@ const options = { chart: { perspectives: { serializeOptions: ['colors', 'title']
|
|
|
208
407
|
|
|
209
408
|
## Facet: design tokens and OS-aware themes
|
|
210
409
|
|
|
211
|
-
`import 'apexcharts/features/facet'
|
|
410
|
+
`import 'apexcharts/features/facet'`: Tier 1 (already in the default bundle).
|
|
212
411
|
|
|
213
412
|
Charts read `--apx-*` CSS custom properties from the cascade, follow the OS light/dark and contrast preferences with no JS, and can reference named brand themes.
|
|
214
413
|
|
|
@@ -265,7 +464,7 @@ Built-in curve names: `linear`, `easeInSine`, `easeOutSine`, `easeInOutSine`, `e
|
|
|
265
464
|
|
|
266
465
|
## Link: crossfilter and cross-chart coordination
|
|
267
466
|
|
|
268
|
-
`import 'apexcharts/features/link'
|
|
467
|
+
`import 'apexcharts/features/link'`: **Tier 2** (not in the default bundle) and **premium**.
|
|
269
468
|
|
|
270
469
|
**Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
|
|
271
470
|
|
|
@@ -303,7 +502,7 @@ const b = {
|
|
|
303
502
|
|
|
304
503
|
## Ink: direct-manipulation annotation authoring
|
|
305
504
|
|
|
306
|
-
`import 'apexcharts/features/ink'
|
|
505
|
+
`import 'apexcharts/features/ink'`: **Tier 2** (not in the default bundle) and **premium**.
|
|
307
506
|
|
|
308
507
|
**Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
|
|
309
508
|
|
|
@@ -327,7 +526,7 @@ const options = {
|
|
|
327
526
|
|
|
328
527
|
## Measure: delta ruler
|
|
329
528
|
|
|
330
|
-
`import 'apexcharts/features/measure'
|
|
529
|
+
`import 'apexcharts/features/measure'`: **Tier 2** (not in the default bundle) and **premium**.
|
|
331
530
|
|
|
332
531
|
**Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
|
|
333
532
|
|
|
@@ -376,7 +575,7 @@ const options = {
|
|
|
376
575
|
|
|
377
576
|
## Context menu: Radial Actions
|
|
378
577
|
|
|
379
|
-
`import 'apexcharts/features/context-menu'
|
|
578
|
+
`import 'apexcharts/features/context-menu'`: **Tier 2** (not in the default bundle) and **premium**.
|
|
380
579
|
|
|
381
580
|
**Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
|
|
382
581
|
|
|
@@ -404,7 +603,7 @@ Built-in `annotate` / `xline` / `yline` items are ink-managed when the ink featu
|
|
|
404
603
|
|
|
405
604
|
## Storyboard: scroll-driven choreography (scrollytelling)
|
|
406
605
|
|
|
407
|
-
`import 'apexcharts/features/storyboard'` (
|
|
606
|
+
`import 'apexcharts/features/storyboard'` (registers Perspectives too): **Tier 2** (not in the default bundle) and **premium**.
|
|
408
607
|
|
|
409
608
|
**Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
|
|
410
609
|
|
|
@@ -442,7 +641,7 @@ const options = { chart: { streaming: { enabled: true, maxPoints: 100000 } } }
|
|
|
442
641
|
|
|
443
642
|
## Drilldown: hierarchical drill-down
|
|
444
643
|
|
|
445
|
-
`import 'apexcharts/features/drilldown'` (add `import 'apexcharts/features/morph'` for animated cross-type transitions)
|
|
644
|
+
`import 'apexcharts/features/drilldown'` (add `import 'apexcharts/features/morph'` for animated cross-type transitions): both Tier 1 (already in the default bundle).
|
|
446
645
|
|
|
447
646
|
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.
|
|
448
647
|
|
|
@@ -468,7 +667,8 @@ const options = {
|
|
|
468
667
|
// false disables it. role="status", aria-live="polite",
|
|
469
668
|
// spinner flattens to a pulse under prefers-reduced-motion
|
|
470
669
|
cache: true, // (v6.9) cache onDrillDown results by id (default true);
|
|
471
|
-
// clear with the
|
|
670
|
+
// clear with chart.clearDrilldownCache(id?), the public
|
|
671
|
+
// typed method (it delegates to the module's clearCache())
|
|
472
672
|
// (v6.9) line/area only: the dot marking a drillable point when the chart
|
|
473
673
|
// draws no markers. Only drillable points get one, so it reads as "these open".
|
|
474
674
|
marker: { show: true, size: 6, shape: 'circle', fillColor: undefined, strokeColor: '#fff' },
|
|
@@ -494,7 +694,7 @@ chart.drillToRoot()
|
|
|
494
694
|
|
|
495
695
|
## Stats: raw-sample statistics (6.9.0)
|
|
496
696
|
|
|
497
|
-
`import 'apexcharts/features/stats'
|
|
697
|
+
`import 'apexcharts/features/stats'`: Tier 1 (already in the default bundle).
|
|
498
698
|
|
|
499
699
|
Free. The statistics behind three things, kept out of core so they cost nothing unless used:
|
|
500
700
|
|
|
@@ -536,8 +736,11 @@ const options = {
|
|
|
536
736
|
|
|
537
737
|
## Common Pitfalls
|
|
538
738
|
|
|
539
|
-
1. **
|
|
540
|
-
2. **
|
|
541
|
-
3. **
|
|
542
|
-
4.
|
|
543
|
-
5. **
|
|
739
|
+
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` and `icicle` types need their own import on top of it. Read the console: each one names both routes when its config is present without it.
|
|
740
|
+
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>'`.
|
|
741
|
+
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).
|
|
742
|
+
4. **Using `api.zeroY` in a custom mark**: it does not exist. Use `ctx.scales.y(0)` for the baseline.
|
|
743
|
+
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.
|
|
744
|
+
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.
|
|
745
|
+
7. **Assuming the canvas renderer supports everything**: pattern/image fills and per-point selection visuals fall back to (or stay) SVG.
|
|
746
|
+
8. **Calling `getPanel(key)` on a virtualized trellis**: it returns `null` for a panel that is not currently mounted (grids over 64 panels virtualize by default).
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
- **Box Plot** (`'boxPlot'`) — Statistical distribution chart showing min, Q1, median, Q3, max
|
|
7
7
|
- **Violin** (`'violin'`, **new in v6**): 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
|
+
- **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
|
|
9
10
|
|
|
10
11
|
## Tree-Shakeable Import
|
|
11
12
|
|
|
@@ -20,12 +21,17 @@ import ApexCharts from 'apexcharts/violin'
|
|
|
20
21
|
import ApexCharts from 'apexcharts/histogram'
|
|
21
22
|
// Registers: histogram (v6.9), the bar engine plus the stats feature
|
|
22
23
|
|
|
24
|
+
import ApexCharts from 'apexcharts/raincloud'
|
|
25
|
+
// Registers: raincloud (v7.1), the violin engine plus the raincloud feature
|
|
26
|
+
|
|
23
27
|
// The statistics (histogram binning, boxPlot/violin raw-sample summaries,
|
|
24
28
|
// rowSeries()) live behind one optional feature. apexcharts/histogram already
|
|
25
29
|
// includes it; boxPlot/violin raw samples need it explicitly when tree-shaking:
|
|
26
30
|
import 'apexcharts/features/stats'
|
|
27
31
|
```
|
|
28
32
|
|
|
33
|
+
**⚠️ Raincloud is Tier 2 (v7.1).** It is **not in the default `apexcharts` bundle**, one of only two chart types that are not (the other is `icicle`, v7.6, which is free). 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.
|
|
34
|
+
|
|
29
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).
|
|
30
36
|
|
|
31
37
|
---
|
|
@@ -216,6 +222,65 @@ The series carries **raw observations** (a flat number array, or `{ y }` objects
|
|
|
216
222
|
- `overlap: true` (the default with multiple series) also softens the fill and drops the bin separator stroke so the overlapping region reads; both remain overridable. A single series is unaffected either way.
|
|
217
223
|
- Do NOT pass pre-aggregated counts; that is a plain `bar` chart. The histogram's job is the binning.
|
|
218
224
|
|
|
225
|
+
### Raincloud (v7.1, premium, Tier 2)
|
|
226
|
+
|
|
227
|
+
`chart.type: 'raincloud'` shows a distribution three ways at once: the **half-violin** is the shape, the **box** is the summary, and the **rain** underneath is the observations themselves. You hand it the raw values and it does the rest.
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
import ApexCharts from 'apexcharts'
|
|
231
|
+
import 'apexcharts/raincloud' // Tier 2: required even on the full bundle
|
|
232
|
+
|
|
233
|
+
new ApexCharts(el, {
|
|
234
|
+
chart: { type: 'raincloud', height: 350 },
|
|
235
|
+
series: [{
|
|
236
|
+
name: 'Weight gain',
|
|
237
|
+
data: [
|
|
238
|
+
{ x: 'Control', points: [3.1, 4.7, 2.9, 5.2, 3.8, 4.1] },
|
|
239
|
+
{ x: 'Treatment', points: [6.4, 7.1, 5.8, 6.9, 7.6, 6.2] },
|
|
240
|
+
],
|
|
241
|
+
}],
|
|
242
|
+
}).render()
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Raincloud is a **preset over the violin engine**, so it is configured through `plotOptions.violin`. The type sets three of those defaults for you:
|
|
246
|
+
|
|
247
|
+
| `plotOptions.violin` key | Raincloud preset | Plain violin default |
|
|
248
|
+
|---|---|---|
|
|
249
|
+
| `side` | `'right'` (or `'top'` when horizontal), a half-violin | `'both'` |
|
|
250
|
+
| `box.show` | `true` (the "umbrella") | `false` |
|
|
251
|
+
| `box.whiskers` | `'tukey'` (1.5×IQR fences clamped to the data) | `'minmax'` |
|
|
252
|
+
| `points.position` | `'left'` (or `'bottom'` when horizontal), the "rain" gets its own lane | `'center'` |
|
|
253
|
+
|
|
254
|
+
Everything else is the violin surface, so override it there:
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
plotOptions: {
|
|
258
|
+
violin: {
|
|
259
|
+
box: {
|
|
260
|
+
width: '15%', // fraction of the category slot reserved for the box lane
|
|
261
|
+
capWidth: 0.5, // whisker cap length, 0..1 of the box lane width
|
|
262
|
+
fillColor: undefined,// defaults to the series colour
|
|
263
|
+
strokeWidth: 1,
|
|
264
|
+
},
|
|
265
|
+
points: {
|
|
266
|
+
laneWidth: '40%', // fraction of the category slot for the off-centre lane
|
|
267
|
+
jitter: 0.5, // 0..1 of the half-width to scatter within
|
|
268
|
+
size: 3,
|
|
269
|
+
maxPoints: undefined,// cap per violin; observations beyond it are stride-thinned
|
|
270
|
+
fillColor: 'series-dark', // or 'series', or any literal colour
|
|
271
|
+
strokeColor: '#fff',
|
|
272
|
+
strokeWidth: 1,
|
|
273
|
+
},
|
|
274
|
+
kde: { bandwidth: undefined, resolution: 64 },
|
|
275
|
+
normalize: 'individual', // or 'group' to keep widths proportional across categories
|
|
276
|
+
},
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
The rain **is** the outlier display, so the box draws no outlier dots of its own. Off-centre dots ignore `constrainToViolin`. A `y.summary` supplied by hand as `[whiskerLow, q1, median, q3, whiskerHigh]` is drawn exactly as given rather than derived.
|
|
281
|
+
|
|
282
|
+
**The alias rewrites `chart.type` to `'violin'` at runtime.** Code that reads the live config (a formatter reading `w.config.chart.type`, a `chart.getState()` consumer, a plugin) sees `'violin'`, not `'raincloud'`; the requested type is kept on `chart.requestedType`. Branch on `requestedType` if you need to tell a raincloud from a plain violin. This is also how licensing distinguishes them: plain violins are free and never watermark.
|
|
283
|
+
|
|
219
284
|
### `rowSeries()`: the rows behind a summary mark (v6.9)
|
|
220
285
|
|
|
221
286
|
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:
|
|
@@ -318,3 +383,6 @@ plotOptions: {
|
|
|
318
383
|
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.
|
|
319
384
|
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.
|
|
320
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 one of two chart types outside the default bundle (the other is `icicle`, v7.6). `import ApexCharts from 'apexcharts'` alone gives you a console warning and no raincloud, because `raincloud` aliases the violin engine and degrades; an unregistered `icicle` throws instead. Add `import 'apexcharts/raincloud'`.
|
|
387
|
+
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
|
+
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.
|
|
@@ -236,7 +236,7 @@ updateChart() {
|
|
|
236
236
|
|
|
237
237
|
```
|
|
238
238
|
https://cdn.jsdelivr.net/npm/apexcharts
|
|
239
|
-
https://cdn.jsdelivr.net/npm/apexcharts@
|
|
239
|
+
https://cdn.jsdelivr.net/npm/apexcharts@7/dist/apexcharts.min.js
|
|
240
240
|
https://unpkg.com/apexcharts
|
|
241
241
|
```
|
|
242
242
|
|