apexcharts-skill 2.1.0 → 3.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,22 +1,55 @@
1
- # v6 Feature Platform: ApexCharts
1
+ # Feature Platform: ApexCharts
2
2
 
3
- ApexCharts v6 adds an opt-in, tree-shakeable feature platform on top of the v5 core. **Every v5 config keeps working unchanged.** Each feature below is off by default and ships as its own `apexcharts/features/*` entry, so it only enters your bundle when you import it. When using the full `apexcharts` bundle, all features are already present.
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
- Two behaviors changed *on by default* (both respect `prefers-reduced-motion`):
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` chart type 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. 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` 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.
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
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`).
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
 
39
+ Two update behaviors worth knowing (both 6.9/6.10):
40
+
41
+ - **Function-valued options are compared by identity (6.10)**: `update()` skips a redundant render by comparing the incoming options with the previous ones. Passing a *different* function (a new `dataLabels.formatter`, custom tooltip, `plotOptions.unit.positions`) gets the render it asked for; passing the *same* function twice still skips. Building a fresh closure on every update means a render every time.
42
+ - **A type change re-chooses type defaults (6.9)**: `updateOptions({ chart: { type } })` now re-picks the defaults that decide what a chart reads, says, hit-tests, or offers as interaction (e.g. a boxPlot-to-violin switch gets the right tooltip formatter). Deliberate paint choices and anything you set yourself are never re-chosen.
43
+
12
44
  ---
13
45
 
14
46
  ## Licensing: premium features and the trial watermark
15
47
 
16
- Introduced in **6.5.0**, tightened in **6.7.0**. Not every v6 feature is free. Seven feature modules plus the `unit` / `waffle` chart type 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.
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.
17
49
 
18
50
  **The premium set (watermarked until entitled):**
19
51
 
52
+ - `trellis` (small multiples, premium since **7.0.0**)
20
53
  - `storyboard` (scrollytelling)
21
54
  - `link` (crossfilter / linked views)
22
55
  - `ink` (annotation authoring)
@@ -25,8 +58,11 @@ Introduced in **6.5.0**, tightened in **6.7.0**. Not every v6 feature is free. S
25
58
  - `perspectives` (shareable view state)
26
59
  - `history` (undo/redo, Rewind)
27
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**)
28
62
 
29
- **Everything else is free and never gated**: every other chart type (including `sunburst`), and every other module (Weave, Strata / canvas, Marks, Facet / themes, Cadence, drilldown, streaming, exports, legend, toolbar, annotations, keyboard).
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).
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.
30
66
 
31
67
  ### Setting a license
32
68
 
@@ -50,9 +86,73 @@ As of **6.7.0** the premium features clear the watermark only on a `premium` or
50
86
 
51
87
  ---
52
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
+
53
153
  ## Weave: public plugin platform
54
154
 
55
- `import 'apexcharts/features/weave'`
155
+ `import 'apexcharts/features/weave'`: Tier 1 (already in the default bundle).
56
156
 
57
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.
58
158
 
@@ -93,7 +193,7 @@ The `setup(api)` facade:
93
193
 
94
194
  ## Strata: hybrid SVG + canvas renderer
95
195
 
96
- `import 'apexcharts/features/renderer-canvas'`
196
+ `import 'apexcharts/features/renderer-canvas'`: **Tier 2** (not in the default bundle). Free.
97
197
 
98
198
  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.
99
199
 
@@ -116,7 +216,7 @@ Canvas is live for line, area, bar, column, scatter, and candlestick, with share
116
216
 
117
217
  ## Marks: composable custom series types
118
218
 
119
- `import 'apexcharts/features/marks'`
219
+ `import 'apexcharts/features/marks'`: Tier 1 (already in the default bundle).
120
220
 
121
221
  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.
122
222
 
@@ -153,7 +253,7 @@ Dumbbell uses `dataType: 'rangeXY'` (datum `y` is `[start, end]`, both bounds fo
153
253
 
154
254
  ## Rewind: history and undo/redo
155
255
 
156
- `import 'apexcharts/features/history'`
256
+ `import 'apexcharts/features/history'`: **Tier 2** (not in the default bundle) and **premium**.
157
257
 
158
258
  **Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
159
259
 
@@ -176,7 +276,7 @@ chart.history.entries() // [{ id, label, at }]
176
276
 
177
277
  ## Perspectives: shareable view state
178
278
 
179
- `import 'apexcharts/features/perspectives'`
279
+ `import 'apexcharts/features/perspectives'`: **Tier 2** (not in the default bundle) and **premium**.
180
280
 
181
281
  **Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
182
282
 
@@ -203,7 +303,7 @@ const options = { chart: { perspectives: { serializeOptions: ['colors', 'title']
203
303
 
204
304
  ## Facet: design tokens and OS-aware themes
205
305
 
206
- `import 'apexcharts/features/facet'`
306
+ `import 'apexcharts/features/facet'`: Tier 1 (already in the default bundle).
207
307
 
208
308
  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.
209
309
 
@@ -260,7 +360,7 @@ Built-in curve names: `linear`, `easeInSine`, `easeOutSine`, `easeInOutSine`, `e
260
360
 
261
361
  ## Link: crossfilter and cross-chart coordination
262
362
 
263
- `import 'apexcharts/features/link'`
363
+ `import 'apexcharts/features/link'`: **Tier 2** (not in the default bundle) and **premium**.
264
364
 
265
365
  **Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
266
366
 
@@ -298,7 +398,7 @@ const b = {
298
398
 
299
399
  ## Ink: direct-manipulation annotation authoring
300
400
 
301
- `import 'apexcharts/features/ink'`
401
+ `import 'apexcharts/features/ink'`: **Tier 2** (not in the default bundle) and **premium**.
302
402
 
303
403
  **Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
304
404
 
@@ -322,7 +422,7 @@ const options = {
322
422
 
323
423
  ## Measure: delta ruler
324
424
 
325
- `import 'apexcharts/features/measure'`
425
+ `import 'apexcharts/features/measure'`: **Tier 2** (not in the default bundle) and **premium**.
326
426
 
327
427
  **Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
328
428
 
@@ -371,7 +471,7 @@ const options = {
371
471
 
372
472
  ## Context menu: Radial Actions
373
473
 
374
- `import 'apexcharts/features/context-menu'`
474
+ `import 'apexcharts/features/context-menu'`: **Tier 2** (not in the default bundle) and **premium**.
375
475
 
376
476
  **Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
377
477
 
@@ -399,7 +499,7 @@ Built-in `annotate` / `xline` / `yline` items are ink-managed when the ink featu
399
499
 
400
500
  ## Storyboard: scroll-driven choreography (scrollytelling)
401
501
 
402
- `import 'apexcharts/features/storyboard'` (includes Perspectives)
502
+ `import 'apexcharts/features/storyboard'` (registers Perspectives too): **Tier 2** (not in the default bundle) and **premium**.
403
503
 
404
504
  **Premium** (watermarked in trial mode until an entitled license is set, see Licensing above).
405
505
 
@@ -437,9 +537,9 @@ const options = { chart: { streaming: { enabled: true, maxPoints: 100000 } } }
437
537
 
438
538
  ## Drilldown: hierarchical drill-down
439
539
 
440
- `import 'apexcharts/features/drilldown'` (add `import 'apexcharts/features/morph'` for animated cross-type transitions)
540
+ `import 'apexcharts/features/drilldown'` (add `import 'apexcharts/features/morph'` for animated cross-type transitions): both Tier 1 (already in the default bundle).
441
541
 
442
- Data points reference a child level via a `drilldown` id; clicking drills in, with an optional breadcrumb.
542
+ 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.
443
543
 
444
544
  ```js
445
545
  import 'apexcharts/features/drilldown'
@@ -457,14 +557,23 @@ const options = {
457
557
  series: [
458
558
  { id: '2021', name: '2021 by Channel', data: [/* ... */], chart: { type: 'bar' } },
459
559
  ],
460
- // or resolve lazily:
461
- onDrillDown: (ctx) => ({ id: ctx.point.drilldown, name: '...', data: [] }),
560
+ // or resolve lazily; may return a Promise (v6.9):
561
+ onDrillDown: async (ctx) => ({ id: ctx.point.drilldown, name: '...', data: await fetchLevel(ctx) }),
562
+ loading: { show: true, text: 'Loading' }, // (v6.9) overlay while an async level resolves;
563
+ // false disables it. role="status", aria-live="polite",
564
+ // spinner flattens to a pulse under prefers-reduced-motion
565
+ cache: true, // (v6.9) cache onDrillDown results by id (default true);
566
+ // clear with the drilldown module's clearCache()
567
+ // (v6.9) line/area only: the dot marking a drillable point when the chart
568
+ // draws no markers. Only drillable points get one, so it reads as "these open".
569
+ marker: { show: true, size: 6, shape: 'circle', fillColor: undefined, strokeColor: '#fff' },
462
570
  },
463
571
  chart: {
464
572
  events: {
465
573
  drillDownStart: (info, chart, options) => {},
466
574
  drillDownEnd: (info, chart, options) => {},
467
575
  drillUp: (info, chart, options) => {},
576
+ drillDownError: ({ id, error }, chart, options) => {}, // (v6.9) async level failed
468
577
  },
469
578
  },
470
579
  }
@@ -474,6 +583,22 @@ chart.drillUp()
474
583
  chart.drillToRoot()
475
584
  ```
476
585
 
586
+ **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
+
588
+ ---
589
+
590
+ ## Stats: raw-sample statistics (6.9.0)
591
+
592
+ `import 'apexcharts/features/stats'`: Tier 1 (already in the default bundle).
593
+
594
+ Free. The statistics behind three things, kept out of core so they cost nothing unless used:
595
+
596
+ - **Histogram binning**: `chart.type: 'histogram'` bins raw observations (the `apexcharts/histogram` entry includes this feature automatically).
597
+ - **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.
599
+
600
+ Full data shapes and options are in `references/financial-charts.md`. The full `apexcharts` bundle includes it.
601
+
477
602
  ---
478
603
 
479
604
  ## Point annotation tooltips (6.7.0)
@@ -506,8 +631,11 @@ const options = {
506
631
 
507
632
  ## Common Pitfalls
508
633
 
509
- 1. **Forgetting the feature import**: every v6 feature is off until you `import 'apexcharts/features/<name>'` (or use the full `apexcharts` bundle). Enabling the config alone does nothing if the feature isn't in the bundle.
510
- 2. **Using `api.zeroY` in a custom mark**: it does not exist. Use `ctx.scales.y(0)` for the baseline.
511
- 3. **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.
512
- 4. **`largeDatasetThreshold` is under `chart.animations`**, not `chart` directly.
513
- 5. **Assuming the canvas renderer supports everything**: pattern/image fills and per-point selection visuals fall back to (or stay) SVG.
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.
635
+ 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).
637
+ 4. **Using `api.zeroY` in a custom mark**: it does not exist. Use `ctx.scales.y(0)` for the baseline.
638
+ 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
+ 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.
640
+ 7. **Assuming the canvas renderer supports everything**: pattern/image fills and per-point selection visuals fall back to (or stay) SVG.
641
+ 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).
@@ -5,6 +5,8 @@
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
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
+ - **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
8
10
 
9
11
  ## Tree-Shakeable Import
10
12
 
@@ -15,8 +17,23 @@ import ApexCharts from 'apexcharts/candlestick'
15
17
 
16
18
  import ApexCharts from 'apexcharts/violin'
17
19
  // Registers: violin (separate entry point, new in v6)
20
+
21
+ import ApexCharts from 'apexcharts/histogram'
22
+ // Registers: histogram (v6.9), the bar engine plus the stats feature
23
+
24
+ import ApexCharts from 'apexcharts/raincloud'
25
+ // Registers: raincloud (v7.1), the violin engine plus the raincloud feature
26
+
27
+ // The statistics (histogram binning, boxPlot/violin raw-sample summaries,
28
+ // rowSeries()) live behind one optional feature. apexcharts/histogram already
29
+ // includes it; boxPlot/violin raw samples need it explicitly when tree-shaking:
30
+ import 'apexcharts/features/stats'
18
31
  ```
19
32
 
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.
34
+
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).
36
+
20
37
  ---
21
38
 
22
39
  ## Data Formats
@@ -72,11 +89,39 @@ The `y` value must be an array of exactly 5 numbers: `[min, Q1, median, Q3, max]
72
89
  }
73
90
  ```
74
91
 
92
+ ### Box Plot: Raw Sample (v6.9)
93
+
94
+ Instead of a five-number `y`, a datum may supply the raw observations in `points` and the library computes the summary (quartiles interpolate between ranks, R type 7). Requires `apexcharts/features/stats` when tree-shaking. `points` is the same field used for the jitter-dot overlay, so the sample lives in one place whether you summarize it or the library does.
95
+
96
+ ```js
97
+ {
98
+ chart: { type: 'boxPlot', height: 350 },
99
+ series: [{
100
+ data: [
101
+ { x: 'Phone', points: [1.2, 1.9, 3.4, 2.2, 2.8, 4.1, 1.7] },
102
+ { x: 'Tablet', points: [2.4, 3.1, 3.9, 2.9, 4.6, 3.3] }
103
+ ]
104
+ }],
105
+ plotOptions: {
106
+ boxPlot: {
107
+ whiskers: 'minmax', // 'minmax' (default): whiskers reach the extremes, nothing hidden
108
+ // 'tukey': stop at the last observation within 1.5 × IQR of each quartile
109
+ points: { show: true } // overlay the observations as jitter dots
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ - `whiskers` only applies when the summary is **derived** from `points`; a precomputed five-number `y` is drawn exactly as given.
116
+ - With `whiskers: 'tukey'`, observations beyond the fence fall outside the whisker, so pair it with `points: { show: true }` or they become invisible.
117
+ - `plotOptions.boxPlot.points` also takes `shape` (`'circle'` | `'square'`), `size`, `jitter` (0..1 fraction of the box half-width), `maxPoints` (cap per box, stride-thinned beyond), `opacity`, `fillColor` (`'series-dark'` default | `'series'` | any color), `strokeColor`, `strokeWidth`, and a value-driven `colorScale: { colors, min, max, steps }`.
118
+ - A datum with both `y` and `points` draws the given summary and overlays the points as jitter.
119
+
75
120
  ### Violin: Density Profile (v6)
76
121
 
77
122
  Each point is `{ x, y: { density, points } }`:
78
123
 
79
- - `density` (**required**): an array of `[value, weight]` pairs describing the precomputed density/KDE profile.
124
+ - `density`: an array of `[value, weight]` pairs describing the precomputed density/KDE profile. Drawn exactly as given.
80
125
  - `points` (optional): a flat array of raw observations, rendered as jitter dots.
81
126
 
82
127
  ```js
@@ -111,6 +156,32 @@ Each point is `{ x, y: { density, points } }`:
111
156
  }
112
157
  ```
113
158
 
159
+ ### Violin: Raw Sample (v6.9)
160
+
161
+ 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
+
163
+ ```js
164
+ {
165
+ chart: { type: 'violin', height: 420 },
166
+ series: [{
167
+ name: 'Session duration',
168
+ data: [
169
+ { x: 'Direct', points: [22, 31, 38, 41, 47, 52, 58, 33, 40] },
170
+ { x: 'Referral', points: [27, 34, 44, 48, 53, 61, 39] }
171
+ ]
172
+ }],
173
+ plotOptions: {
174
+ violin: {
175
+ kde: {
176
+ bandwidth: undefined, // kernel width in value units; unset = Silverman's rule of thumb
177
+ resolution: 64 // density samples per violin (default 64)
178
+ },
179
+ points: { show: true } // the same sample doubles as the jitter overlay
180
+ }
181
+ }
182
+ }
183
+ ```
184
+
114
185
  **Orientation and per-category color come from `plotOptions.bar`** (violin reuses the bar renderer):
115
186
 
116
187
  ```js
@@ -123,6 +194,103 @@ plotOptions: {
123
194
  }
124
195
  ```
125
196
 
197
+ ### Histogram (v6.9)
198
+
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.
200
+
201
+ ```js
202
+ {
203
+ chart: { type: 'histogram', height: 350 },
204
+ series: [{ name: 'Latency', data: [102, 87, 143, 91, 118, 95, 132, 88, 107] }],
205
+ plotOptions: {
206
+ histogram: {
207
+ bins: 'auto', // 'auto' | 'fd' | 'sturges' | 'scott' | 'rice' | 'sqrt' | number (fixed count)
208
+ // 'auto' takes the narrower of Freedman-Diaconis and Sturges
209
+ binWidth: undefined, // explicit bin width in value units; wins over `bins` when the
210
+ // boundaries carry meaning (decades, 5-minute buckets)
211
+ range: undefined, // [min, max] to bin over instead of the data's own extent
212
+ normalize: 'count', // 'count' | 'relative' (percent of series total) | 'density' (total area = 1)
213
+ cumulative: false, // running total across bins (a CDF)
214
+ overlap: true // multi-series: draw each series across the full bin (overlay).
215
+ // false = side-by-side grouped bars per bin
216
+ }
217
+ }
218
+ }
219
+ ```
220
+
221
+ - **All series share one set of bin edges** derived from their combined extent, so two distributions stay comparable instead of putting different bars at the same x.
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.
223
+ - Do NOT pass pre-aggregated counts; that is a plain `bar` chart. The histogram's job is the binning.
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
+
284
+ ### `rowSeries()`: the rows behind a summary mark (v6.9)
285
+
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:
287
+
288
+ ```js
289
+ chart.updateOptions({ chart: { type: 'unit' }, series: chart.rowSeries() })
290
+ ```
291
+
292
+ 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
+
126
294
  ---
127
295
 
128
296
  ## Key plotOptions
@@ -206,9 +374,15 @@ plotOptions: {
206
374
  ## Family-Specific Pitfalls
207
375
 
208
376
  1. **Wrong OHLC order** — must be `[Open, High, Low, Close]`, not `[High, Low, Open, Close]` or any other order.
209
- 2. **Box Plot with wrong array length** — must be exactly 5 values `[min, Q1, median, Q3, max]`. Fewer or more values cause rendering errors.
210
- 3. **Using simple numeric array** — `data: [10, 20, 30]` does NOT work for candlestick/boxPlot. Each point requires an array in `y`.
377
+ 2. **Box Plot with wrong array length** — a summary `y` must be exactly 5 values `[min, Q1, median, Q3, max]`. Fewer or more values cause rendering errors. (Since v6.9 the alternative is no `y` at all: put the raw sample in `points` and the summary is computed.)
378
+ 3. **Using simple numeric array** — `data: [10, 20, 30]` does NOT work for candlestick/boxPlot. Each point requires an array in `y` (or, for boxPlot since v6.9, a raw sample in `points`). For a distribution of a flat number array, use `chart.type: 'histogram'` instead.
211
379
  4. **Missing `xaxis.type: 'datetime'`** — candlestick charts with date x-values need `xaxis: { type: 'datetime' }` to format dates correctly.
212
380
  5. **Confusing candlestick with boxPlot** — candlestick is 4 values (OHLC), boxPlot is 5 values (five-number summary). They share the same entry point but have different data shapes.
213
- 6. **Violin with a plain numeric `y`**: violin points need `y: { density: [[value, weight], ...] }`, not a number. The `density` profile is precomputed (ApexCharts does not run a KDE for you); `points` for the raw-sample jitter overlay is optional.
214
- 7. **Violin orientation set on the wrong namespace**: horizontal violins and per-violin colors come from `plotOptions.bar` (`horizontal`, `distributed`), while `bandwidthScale`, `normalize`, and `points` live under `plotOptions.violin`.
381
+ 6. **Violin with a plain numeric `y`**: violin points need `y: { density: [[value, weight], ...] }` or (v6.9) raw observations in `points`, not a single number. With a precomputed `density` profile it is drawn exactly as given; with raw `points` the KDE runs for you (stats feature required when tree-shaking).
382
+ 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
+ 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
+ 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'`.
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.
@@ -133,6 +133,55 @@ Flat list of `{ x, y }` where `x` is the label and `y` is the value (determines
133
133
  }
134
134
  ```
135
135
 
136
+ ### Nested Treemap (v6.9)
137
+
138
+ A datum may carry `children` to any depth; every branch is drawn as a real container with a header strip and its children inset below it, and a container's area is exactly the sum of its children. A branch normally omits `y` and takes the sum of its children; a leaf supplies one. Flat inputs are untouched and render identically to before.
139
+
140
+ ```js
141
+ {
142
+ chart: { type: 'treemap', height: 400 },
143
+ series: [{
144
+ data: [
145
+ { x: 'Technology', children: [
146
+ { x: 'Software', children: [
147
+ { x: 'Acme Corp', y: 120 },
148
+ { x: 'Initech', y: 80 }
149
+ ] },
150
+ { x: 'Hardware', y: 95 }
151
+ ] },
152
+ { x: 'Energy', children: [
153
+ { x: 'Solar Co', y: 60 },
154
+ { x: 'Wind Ltd', y: 45 }
155
+ ] }
156
+ ]
157
+ }]
158
+ }
159
+ ```
160
+
161
+ Nested-specific options under `plotOptions.treemap`:
162
+
163
+ ```js
164
+ plotOptions: {
165
+ treemap: {
166
+ nested: {
167
+ enabled: true, // 'auto' behavior: parents appear as soon as the data is nested;
168
+ // false forces the flat two-level layout
169
+ drilldownAsLevels: false // read `drilldown: '<id>'` ids on data points as extra levels
170
+ // instead of as click targets for the drilldown feature.
171
+ // Default false ("descend on click" is the historical meaning)
172
+ },
173
+ parents: { // how branch containers are drawn once the data is nested
174
+ show: 'auto' // 'auto' (on when the data carries children) | true | false
175
+ // plus header/label styling and a tooltip.formatter receiving
176
+ // { name, value, depth, leafCount, percentOfParent, percentOfTotal, node, w }
177
+ },
178
+ levels: [ /* per-depth overrides of `parents`, indexed from the outermost drawn group */ ]
179
+ }
180
+ }
181
+ ```
182
+
183
+ The hierarchy resolver is shared with sunburst, including the `drilldown: '<id>'` adapter (opted into via `nested.drilldownAsLevels`).
184
+
136
185
  ---
137
186
 
138
187
  ## Key plotOptions
@@ -239,3 +288,4 @@ plotOptions: {
239
288
  2. **Inconsistent x-values across heatmap series** — all series should have the same set of x-values to form a proper grid. Missing cells show as gaps.
240
289
  3. **Treemap with negative values** — treemap `y` values must be positive (they represent area). Negative values cause rendering issues.
241
290
  4. **Confusing heatmap `y` with position** — in heatmap data `{ x, y }`, the `y` is the VALUE (color intensity), NOT the y-axis position. The series `name` determines the row.
291
+ 5. **Nested treemap branch carrying its own `y` (v6.9)**: a branch node normally omits `y`; its area is the sum of its children. Only leaves supply values. Flattening a hierarchy by hand is no longer necessary; pass `children` instead.