apexcharts 8.3.0 → 8.4.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.
Files changed (277) hide show
  1. package/README.md +7 -7
  2. package/dist/apexcharts.common.js +2 -2
  3. package/dist/apexcharts.core.js +20039 -19931
  4. package/dist/apexcharts.core.min.js +2 -2
  5. package/dist/apexcharts.css +3 -8
  6. package/dist/apexcharts.esm.js +950 -574
  7. package/dist/apexcharts.full.js +23401 -22504
  8. package/dist/apexcharts.full.min.js +2 -2
  9. package/dist/apexcharts.js +21723 -21287
  10. package/dist/apexcharts.min.js +2 -2
  11. package/dist/apexcharts.ssr.common.js +2 -2
  12. package/dist/apexcharts.ssr.esm.js +21758 -21242
  13. package/dist/area.common.js +2 -2
  14. package/dist/area.esm.js +118 -65
  15. package/dist/bar.common.js +2 -2
  16. package/dist/bar.esm.js +404 -218
  17. package/dist/bar.js +2 -2
  18. package/dist/boxPlot.common.js +2 -2
  19. package/dist/boxPlot.esm.js +379 -205
  20. package/dist/bubble.common.js +2 -2
  21. package/dist/bubble.esm.js +118 -65
  22. package/dist/candlestick.common.js +2 -2
  23. package/dist/candlestick.esm.js +379 -205
  24. package/dist/candlestick.js +2 -2
  25. package/dist/column.common.js +2 -2
  26. package/dist/column.esm.js +404 -218
  27. package/dist/core.common.js +2 -2
  28. package/dist/core.esm.js +20034 -19926
  29. package/dist/donut.common.js +2 -2
  30. package/dist/donut.esm.js +106 -11
  31. package/dist/dumbbell.common.js +2 -2
  32. package/dist/dumbbell.esm.js +404 -218
  33. package/dist/features/all.common.js +2 -2
  34. package/dist/features/all.esm.js +282 -184
  35. package/dist/features/annotations.common.js +2 -2
  36. package/dist/features/annotations.esm.js +65 -48
  37. package/dist/features/annotations.js +2 -2
  38. package/dist/features/context-menu.common.js +1 -1
  39. package/dist/features/context-menu.esm.js +1 -1
  40. package/dist/features/context-menu.js +1 -1
  41. package/dist/features/drilldown.common.js +2 -2
  42. package/dist/features/drilldown.esm.js +137 -124
  43. package/dist/features/drilldown.js +2 -2
  44. package/dist/features/dumbbell.common.js +1 -1
  45. package/dist/features/dumbbell.esm.js +1 -1
  46. package/dist/features/dumbbell.js +1 -1
  47. package/dist/features/exports.common.js +2 -2
  48. package/dist/features/exports.esm.js +31 -15
  49. package/dist/features/exports.js +2 -2
  50. package/dist/features/facet.common.js +1 -1
  51. package/dist/features/facet.esm.js +1 -1
  52. package/dist/features/facet.js +1 -1
  53. package/dist/features/highlight-filter.common.js +2 -2
  54. package/dist/features/highlight-filter.esm.js +206 -119
  55. package/dist/features/highlight-filter.js +2 -2
  56. package/dist/features/history.common.js +1 -1
  57. package/dist/features/history.esm.js +1 -1
  58. package/dist/features/history.js +1 -1
  59. package/dist/features/ink.common.js +1 -1
  60. package/dist/features/ink.esm.js +1 -1
  61. package/dist/features/ink.js +1 -1
  62. package/dist/features/keyboard.common.js +1 -1
  63. package/dist/features/keyboard.esm.js +1 -1
  64. package/dist/features/keyboard.js +1 -1
  65. package/dist/features/legend.common.js +2 -2
  66. package/dist/features/legend.esm.js +52 -28
  67. package/dist/features/legend.js +2 -2
  68. package/dist/features/link.common.js +1 -1
  69. package/dist/features/link.esm.js +1 -1
  70. package/dist/features/link.js +1 -1
  71. package/dist/features/marks.common.js +1 -1
  72. package/dist/features/marks.esm.js +1 -1
  73. package/dist/features/marks.js +1 -1
  74. package/dist/features/measure.common.js +1 -1
  75. package/dist/features/measure.esm.js +1 -1
  76. package/dist/features/measure.js +1 -1
  77. package/dist/features/morph.common.js +2 -2
  78. package/dist/features/morph.esm.js +27 -14
  79. package/dist/features/morph.js +2 -2
  80. package/dist/features/perspectives.common.js +2 -2
  81. package/dist/features/perspectives.esm.js +4 -1
  82. package/dist/features/perspectives.js +2 -2
  83. package/dist/features/raincloud.common.js +2 -2
  84. package/dist/features/raincloud.esm.js +26 -27
  85. package/dist/features/raincloud.js +2 -2
  86. package/dist/features/renderer-canvas.common.js +2 -2
  87. package/dist/features/renderer-canvas.esm.js +35 -21
  88. package/dist/features/renderer-canvas.js +2 -2
  89. package/dist/features/stats.common.js +2 -2
  90. package/dist/features/stats.esm.js +52 -59
  91. package/dist/features/stats.js +2 -2
  92. package/dist/features/storyboard.common.js +2 -2
  93. package/dist/features/storyboard.esm.js +4 -1
  94. package/dist/features/storyboard.js +2 -2
  95. package/dist/features/streamgraph.common.js +2 -2
  96. package/dist/features/streamgraph.esm.js +160 -55
  97. package/dist/features/streamgraph.js +2 -2
  98. package/dist/features/toolbar.common.js +2 -2
  99. package/dist/features/toolbar.esm.js +78 -28
  100. package/dist/features/toolbar.js +2 -2
  101. package/dist/features/trellis.common.js +2 -2
  102. package/dist/features/trellis.esm.js +876 -543
  103. package/dist/features/trellis.js +2 -2
  104. package/dist/features/waterfall.common.js +2 -2
  105. package/dist/features/waterfall.esm.js +14 -25
  106. package/dist/features/waterfall.js +2 -2
  107. package/dist/features/weave.common.js +2 -2
  108. package/dist/features/weave.esm.js +13 -12
  109. package/dist/features/weave.js +2 -2
  110. package/dist/full.common.js +2 -2
  111. package/dist/full.esm.js +1893 -1342
  112. package/dist/heatmap.common.js +2 -2
  113. package/dist/heatmap.esm.js +38 -54
  114. package/dist/heatmap.js +2 -2
  115. package/dist/histogram.common.js +2 -2
  116. package/dist/histogram.esm.js +455 -276
  117. package/dist/icicle.common.js +2 -2
  118. package/dist/icicle.esm.js +156 -91
  119. package/dist/icicle.js +2 -2
  120. package/dist/line.common.js +2 -2
  121. package/dist/line.esm.js +118 -65
  122. package/dist/line.js +2 -2
  123. package/dist/pictograms.common.js +2 -2
  124. package/dist/pictograms.esm.js +2 -2
  125. package/dist/pictograms.js +2 -2
  126. package/dist/pie.common.js +2 -2
  127. package/dist/pie.esm.js +106 -11
  128. package/dist/pie.js +2 -2
  129. package/dist/polarArea.common.js +2 -2
  130. package/dist/polarArea.esm.js +106 -11
  131. package/dist/radar.common.js +2 -2
  132. package/dist/radar.esm.js +23 -9
  133. package/dist/radar.js +2 -2
  134. package/dist/radialBar.common.js +2 -2
  135. package/dist/radialBar.esm.js +108 -14
  136. package/dist/radialBar.js +2 -2
  137. package/dist/raincloud.common.js +2 -2
  138. package/dist/raincloud.esm.js +39 -31
  139. package/dist/rangeArea.common.js +2 -2
  140. package/dist/rangeArea.esm.js +118 -65
  141. package/dist/rangeBar.common.js +2 -2
  142. package/dist/rangeBar.esm.js +404 -218
  143. package/dist/scatter.common.js +2 -2
  144. package/dist/scatter.esm.js +118 -65
  145. package/dist/streamgraph.common.js +2 -2
  146. package/dist/streamgraph.esm.js +276 -118
  147. package/dist/sunburst.common.js +2 -2
  148. package/dist/sunburst.esm.js +333 -116
  149. package/dist/sunburst.js +2 -2
  150. package/dist/treemap.common.js +2 -2
  151. package/dist/treemap.esm.js +155 -124
  152. package/dist/treemap.js +2 -2
  153. package/dist/unit-shapes.common.js +2 -2
  154. package/dist/unit-shapes.esm.js +2 -2
  155. package/dist/unit-shapes.js +2 -2
  156. package/dist/unit.common.js +2 -2
  157. package/dist/unit.esm.js +29 -27
  158. package/dist/unit.js +2 -2
  159. package/dist/violin.common.js +2 -2
  160. package/dist/violin.esm.js +14 -5
  161. package/dist/violin.js +2 -2
  162. package/dist/waterfall.common.js +2 -2
  163. package/dist/waterfall.esm.js +417 -242
  164. package/package.json +1 -1
  165. package/src/apexcharts.js +37 -60
  166. package/src/assets/apexcharts.css +3 -8
  167. package/src/charts/Bar.js +30 -12
  168. package/src/charts/BarStacked.js +10 -4
  169. package/src/charts/BoxCandleStick.js +8 -2
  170. package/src/charts/Icicle.js +60 -76
  171. package/src/charts/Line.js +58 -22
  172. package/src/charts/Pie.js +136 -14
  173. package/src/charts/Radar.js +6 -9
  174. package/src/charts/Radial.js +2 -6
  175. package/src/charts/RangeBar.js +13 -13
  176. package/src/charts/Scatter.js +5 -7
  177. package/src/charts/Sunburst.js +157 -135
  178. package/src/charts/Treemap.js +100 -56
  179. package/src/charts/common/Breadcrumb.js +37 -16
  180. package/src/charts/common/PreviousPaths.js +45 -0
  181. package/src/charts/common/Stats.js +30 -23
  182. package/src/charts/common/bar/DataLabels.js +85 -77
  183. package/src/charts/common/bar/Helpers.js +144 -56
  184. package/src/charts/common/line/Helpers.js +8 -11
  185. package/src/charts/common/partition/NodeTooltip.js +62 -22
  186. package/src/charts/common/partition/Partition.js +64 -19
  187. package/src/charts/common/partition/Tint.js +6 -3
  188. package/src/charts/common/partition/Transitions.js +108 -0
  189. package/src/charts/common/treemap/ColorScale.js +30 -14
  190. package/src/charts/common/treemap/Helpers.js +47 -68
  191. package/src/charts/common/treemap/Nested.js +2 -3
  192. package/src/features/raincloud.js +16 -10
  193. package/src/features/stats.js +67 -62
  194. package/src/modules/Animations.js +102 -95
  195. package/src/modules/AxisMapping.js +47 -12
  196. package/src/modules/Base.js +12 -14
  197. package/src/modules/CoreUtils.js +26 -46
  198. package/src/modules/Data.js +82 -55
  199. package/src/modules/DataLabels.js +14 -13
  200. package/src/modules/Exports.js +44 -19
  201. package/src/modules/Fill.js +17 -8
  202. package/src/modules/Filters.js +50 -50
  203. package/src/modules/Graphics.js +65 -84
  204. package/src/modules/Markers.js +57 -58
  205. package/src/modules/MorphPieces.js +16 -10
  206. package/src/modules/MorphTypeChange.js +21 -9
  207. package/src/modules/Range.js +32 -55
  208. package/src/modules/RendererController.js +4 -4
  209. package/src/modules/Responsive.js +5 -1
  210. package/src/modules/Scales.js +24 -15
  211. package/src/modules/Series.js +85 -73
  212. package/src/modules/Toolbar.js +3 -2
  213. package/src/modules/UnitLayoutRegistry.js +8 -5
  214. package/src/modules/UnitMarkRegistry.js +7 -10
  215. package/src/modules/ZoomPanSelection.js +4 -0
  216. package/src/modules/animations/AxisTransition.js +11 -4
  217. package/src/modules/animations/DataLabelTransition.js +30 -15
  218. package/src/modules/animations/Easing.js +2 -1
  219. package/src/modules/animations/LayoutTransition.js +43 -32
  220. package/src/modules/animations/LengthTransition.js +176 -84
  221. package/src/modules/annotations/Helpers.js +68 -52
  222. package/src/modules/axes/AxesUtils.js +14 -12
  223. package/src/modules/axes/Grid.js +21 -7
  224. package/src/modules/axes/XAxis.js +4 -4
  225. package/src/modules/axes/YAxis.js +12 -13
  226. package/src/modules/dimensions/Dimensions.js +9 -3
  227. package/src/modules/dimensions/Grid.js +2 -2
  228. package/src/modules/dimensions/XAxis.js +2 -5
  229. package/src/modules/drilldown/Breadcrumb.js +11 -53
  230. package/src/modules/drilldown/Drilldown.js +151 -95
  231. package/src/modules/helpers/InitCtxVariables.js +2 -1
  232. package/src/modules/helpers/UpdateHelpers.js +165 -15
  233. package/src/modules/helpers/XKey.js +56 -0
  234. package/src/modules/highlightFilter/HighlightFilter.js +98 -49
  235. package/src/modules/highlightFilter/PiePart.js +6 -3
  236. package/src/modules/highlightFilter/RadialPart.js +41 -7
  237. package/src/modules/highlightFilter/TreemapPart.js +71 -44
  238. package/src/modules/highlightFilter/util.js +23 -7
  239. package/src/modules/legend/Helpers.js +6 -1
  240. package/src/modules/legend/Legend.js +39 -13
  241. package/src/modules/license/LicenseEnforcer.js +5 -0
  242. package/src/modules/settings/Defaults.js +62 -106
  243. package/src/modules/settings/Globals.js +5 -0
  244. package/src/modules/settings/Options.js +0 -7
  245. package/src/modules/settings/TypeAliases.js +33 -16
  246. package/src/modules/streamgraph/StreamData.js +81 -35
  247. package/src/modules/streamgraph/StreamLabels.js +44 -17
  248. package/src/modules/tooltip/Labels.js +80 -80
  249. package/src/modules/tooltip/Marker.js +25 -21
  250. package/src/modules/tooltip/Position.js +88 -51
  251. package/src/modules/tooltip/Tooltip.js +229 -253
  252. package/src/modules/tooltip/Utils.js +64 -131
  253. package/src/modules/tooltip/constants.js +5 -5
  254. package/src/modules/trellis/Trellis.js +402 -248
  255. package/src/modules/trellis/TrellisChrome.js +110 -44
  256. package/src/modules/trellis/TrellisExports.js +48 -13
  257. package/src/modules/trellis/TrellisFrames.js +11 -15
  258. package/src/modules/trellis/TrellisScales.js +201 -115
  259. package/src/modules/trellis/TrellisSplit.js +72 -36
  260. package/src/modules/trellis/TrellisSync.js +41 -13
  261. package/src/modules/trellis/TrellisTooltip.js +101 -78
  262. package/src/modules/trellis/pivotRows.js +20 -6
  263. package/src/modules/waterfall/WaterfallData.js +14 -30
  264. package/src/modules/weave/WeaveHost.js +18 -12
  265. package/src/pictograms/registry.js +2 -1
  266. package/src/renderers/Renderer.js +41 -37
  267. package/src/renderers/canvas/CanvasCompositor.js +4 -1
  268. package/src/renderers/canvas/CanvasGraphics.js +56 -24
  269. package/src/ssr/RenderShim.js +129 -20
  270. package/src/svg/SVGContainer.js +3 -2
  271. package/src/svg/SVGFilter.js +26 -17
  272. package/src/types/internal.d.ts +8 -6
  273. package/src/unit-shapes/registry.js +3 -1
  274. package/src/utils/DateTime.js +5 -20
  275. package/src/utils/Utils.js +0 -14
  276. package/src/utils/nameAsHtml.js +16 -0
  277. package/types/apexcharts.d.ts +72 -13
@@ -26,6 +26,21 @@ const ICONS = {
26
26
  '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 4v11"/><path d="M7 11l5 5 5-5"/><path d="M4 20h16"/></svg>',
27
27
  }
28
28
 
29
+ /**
30
+ * Paint one legend item from the trellis's state: the marker from the
31
+ * colour map, the inactive look from the hidden set (which every panel
32
+ * mounted from it honours too, so the two always agree).
33
+ * @param {import('./Trellis').default} t
34
+ * @param {any} item
35
+ * @param {string} name
36
+ */
37
+ function paintItem(t, item, name) {
38
+ const hidden = t.sync.isHidden(name)
39
+ item.firstChild.style.background = t.scales ? t.scales.colorOf(name) : '#008FFB'
40
+ item.classList.toggle('apexcharts-inactive-legend', hidden)
41
+ item.setAttribute('data:collapsed', String(hidden))
42
+ }
43
+
29
44
  export default class TrellisChrome {
30
45
  /**
31
46
  * @param {import('./Trellis').default} trellis
@@ -39,6 +54,8 @@ export default class TrellisChrome {
39
54
  /** @type {HTMLElement|null} */
40
55
  this.elTitle = null
41
56
  /** @type {HTMLElement|null} */
57
+ this.elSubtitle = null
58
+ /** @type {HTMLElement|null} */
42
59
  this.elBreadcrumb = null
43
60
  /** @type {HeatmapGradientLegend|null} shared gradient strip (P5, heatmap) */
44
61
  this.gradientLegend = null
@@ -170,21 +187,42 @@ export default class TrellisChrome {
170
187
  }
171
188
 
172
189
  /**
173
- * The trellis-level title, from the host's own `title` config (panels have
174
- * theirs suppressed).
190
+ * The trellis-level title and subtitle, from the host's own `title` and
191
+ * `subtitle` config (panels have theirs suppressed). The title keeps the
192
+ * stylesheet's lighter weight unless the page set `title.style.fontWeight`
193
+ * itself (the merged core default, 900, is heavier than the grid chrome).
175
194
  * @param {HTMLElement} host
176
195
  */
177
196
  buildTitle(host) {
178
- const title = this.trellis.w.config.title
179
- if (!title || !title.text) return
180
- const el = BrowserAPIs.createElement('div')
181
- el.className = 'apexcharts-trellis-title'
182
- el.textContent = title.text
183
- const style = title.style || {}
184
- if (style.fontSize) el.style.fontSize = style.fontSize
185
- if (style.color) el.style.color = style.color
186
- host.appendChild(el)
187
- this.elTitle = el
197
+ const config = this.trellis.w.config
198
+ /** @param {'title'|'subtitle'} k */
199
+ const make = (k) => {
200
+ const cfg = config[k]
201
+ if (!cfg || !cfg.text) return null
202
+ const el = BrowserAPIs.createElement('div')
203
+ el.className = 'apexcharts-trellis-' + k
204
+ el.textContent = cfg.text
205
+ const { fontSize, fontWeight, fontFamily, color } = cfg.style || {}
206
+ const s = el.style
207
+ if (fontSize) s.fontSize = fontSize
208
+ // the title's stylesheet weight stands unless the page set one
209
+ const fw =
210
+ k === 'title'
211
+ ? this.trellis.ctx.opts?.title?.style?.fontWeight
212
+ : fontWeight
213
+ if (fw) s.fontWeight = String(fw)
214
+ if (fontFamily) s.fontFamily = fontFamily
215
+ if (color || k !== 'title') {
216
+ // no stylesheet rule for the subtitle (core CSS stays untouched)
217
+ s.color = color || 'var(--apx-fore, var(--apx-trellis-fore, #373d3f))'
218
+ }
219
+ if (k !== 'title') s.padding = '0 0 6px'
220
+ if (cfg.align && cfg.align !== 'left') s.textAlign = cfg.align
221
+ host.appendChild(el)
222
+ return el
223
+ }
224
+ this.elTitle = make('title')
225
+ this.elSubtitle = make('subtitle')
188
226
  }
189
227
 
190
228
  /**
@@ -194,6 +232,8 @@ export default class TrellisChrome {
194
232
  */
195
233
  buildLegend(host) {
196
234
  const t = this.trellis
235
+ /** @type {string|null} the categorical legend's names, as JSON */
236
+ this._names = null
197
237
  if ((t.cfg.legend || 'shared') !== 'shared') return
198
238
  // A heatmap's legend IS its color scale (P5): one shared gradient strip
199
239
  // for the grid instead of a categorical name list.
@@ -215,11 +255,9 @@ export default class TrellisChrome {
215
255
  item.className = 'apexcharts-legend-series apexcharts-trellis-legend-item'
216
256
  item.setAttribute('role', 'listitem')
217
257
  item.setAttribute('tabindex', '0')
218
- item.setAttribute('data:collapsed', 'false')
219
258
 
220
259
  const marker = BrowserAPIs.createElement('span')
221
260
  marker.className = 'apexcharts-legend-marker'
222
- marker.style.background = t.scales ? t.scales.colorOf(name) : '#008FFB'
223
261
 
224
262
  const text = BrowserAPIs.createElement('span')
225
263
  text.className = 'apexcharts-legend-text'
@@ -227,11 +265,11 @@ export default class TrellisChrome {
227
265
 
228
266
  item.appendChild(marker)
229
267
  item.appendChild(text)
268
+ paintItem(t, item, name)
230
269
 
231
270
  const toggle = () => {
232
- const hidden = t.sync.toggleSeries(name)
233
- item.classList.toggle('apexcharts-inactive-legend', hidden)
234
- item.setAttribute('data:collapsed', String(hidden))
271
+ t.sync.toggleSeries(name)
272
+ paintItem(t, item, name)
235
273
  }
236
274
  item.addEventListener('click', toggle)
237
275
  item.addEventListener('keydown', (/** @type {any} */ e) => {
@@ -245,25 +283,33 @@ export default class TrellisChrome {
245
283
 
246
284
  host.appendChild(wrap)
247
285
  this.elLegend = wrap
286
+ this._names = JSON.stringify(names)
248
287
  }
249
288
 
250
289
  /**
251
- * Repaint the shared legend's markers from the current colour map, without
252
- * rebuilding the legend (which would drop the collapsed state a viewer has
253
- * toggled). Used by the in-place option update, where `colors` may have
254
- * moved but nothing structural has.
290
+ * Bring the shared legend up to date after an in-place update: new data
291
+ * may add or drop names (and a heatmap strip has to span the new colour
292
+ * range); a `colors` change moves the markers. The same names repaint in
293
+ * place, so a streaming grid neither churns the DOM nor takes keyboard
294
+ * focus off an item. Otherwise the legend is rebuilt where it stood.
255
295
  */
256
- refreshLegendColors() {
296
+ rebuildLegend() {
257
297
  const t = this.trellis
258
- const legend = this.elLegend
259
- const scales = t.scales
260
- if (!legend || !scales) return
261
- const items = legend.querySelectorAll('.apexcharts-trellis-legend-item')
262
- items.forEach((/** @type {any} */ item) => {
263
- const name = item.querySelector('.apexcharts-legend-text')?.textContent
264
- const marker = item.querySelector('.apexcharts-legend-marker')
265
- if (name && marker) marker.style.background = scales.colorOf(name)
266
- })
298
+ const old = this.elLegend
299
+ const names = t.split ? t.split.seriesNames : []
300
+ if (old && this._names === JSON.stringify(names)) {
301
+ names.forEach((n, i) =>
302
+ paintItem(t, old.children[i], n),
303
+ )
304
+ return
305
+ }
306
+ this.destroyGradientLegend()
307
+ this.elLegend = null
308
+ if (t.elWrap) this.buildLegend(t.elWrap)
309
+ // (buildLegend set it; the cast undoes the narrowing to null above)
310
+ const el = /** @type {HTMLElement|null} */ (this.elLegend)
311
+ // the new legend takes the old one's place in the wrap
312
+ if (old) el ? old.replaceWith(el) : old.remove()
267
313
  }
268
314
 
269
315
  /**
@@ -328,11 +374,19 @@ export default class TrellisChrome {
328
374
  t.ctx.events.removeEventListener?.('panelMounted', this._gradPending)
329
375
  this._gradPending = null
330
376
  }
331
- // the panels' own buses die with them right after (teardown)
332
377
  t.ctx.events.removeEventListener('panelMounted', this._gradWire)
333
- if (this.gradientLegend) {
378
+ this._gradWire = null
379
+ const legend = this.gradientLegend
380
+ if (legend) {
381
+ // Live panels outlive a strip rebuilt in place (an update), so their
382
+ // hover wiring has to go with it; on teardown they die right after.
383
+ t.panels.forEach((p) => {
384
+ const ev = /** @type {any} */ (p.chart)?.ctx.events
385
+ ev?.removeEventListener('dataPointMouseEnter', legend._onCellEnter)
386
+ ev?.removeEventListener('dataPointMouseLeave', legend._onCellLeave)
387
+ })
334
388
  try {
335
- this.gradientLegend.destroy()
389
+ legend.destroy()
336
390
  } catch (e) {
337
391
  // The carrier panel may already be destroyed; the strip DOM goes
338
392
  // with the trellis wrap either way.
@@ -358,10 +412,13 @@ export default class TrellisChrome {
358
412
  const buttons = {}
359
413
  /** @param {'zoom'|'pan'} tool */
360
414
  const arm = (tool) => {
415
+ // kept on the trellis, so panels mounted later start with this tool
416
+ t._tool = tool
361
417
  t.panels.forEach((p) => {
362
418
  if (!p.chart) return
363
419
  const it = p.chart.w.interact
364
- it.zoomEnabled = tool === 'zoom'
420
+ // never arms a zoom the panel's config turned off
421
+ it.zoomEnabled = tool === 'zoom' && p.chart.w.config.chart.zoom.enabled
365
422
  it.panEnabled = tool === 'pan'
366
423
  it.selectionEnabled = false
367
424
  })
@@ -389,11 +446,17 @@ export default class TrellisChrome {
389
446
  return b
390
447
  }
391
448
 
392
- makeButton('zoom', 'Selection zoom', () => arm('zoom'))
393
- makeButton('pan', 'Pan', () => arm('pan'))
394
- makeButton('reset', 'Reset zoom', () => {
395
- t.sync.resetAll()
396
- })
449
+ // Same rule as the panels' zoom.enabled: with zoom off for the grid there
450
+ // is nothing for zoom, pan or reset to do, so they are left out.
451
+ const zoom =
452
+ t.cfg.zoom !== 'none' && t.ctx.opts?.chart?.zoom?.enabled !== false
453
+ if (zoom) {
454
+ makeButton('zoom', 'Selection zoom', () => arm('zoom'))
455
+ makeButton('pan', 'Pan', () => arm('pan'))
456
+ makeButton('reset', 'Reset zoom', () => {
457
+ t.sync.resetAll()
458
+ })
459
+ }
397
460
 
398
461
  // The download tool (P3): one composed artifact for the whole grid.
399
462
  // Requires the exports feature on the panels; omitted otherwise.
@@ -421,10 +484,13 @@ export default class TrellisChrome {
421
484
  bar.appendChild(menu)
422
485
  }
423
486
 
424
- // Default tool mirrors the per-chart default (toolbar.autoSelected 'zoom').
425
- buttons.zoom.classList.add('apexcharts-selected')
426
- buttons.zoom.setAttribute('aria-pressed', 'true')
427
- buttons.pan.setAttribute('aria-pressed', 'false')
487
+ if (!bar.firstChild) return
488
+ if (zoom) {
489
+ // Default tool mirrors the per-chart default (toolbar.autoSelected 'zoom').
490
+ buttons.zoom.classList.add('apexcharts-selected')
491
+ buttons.zoom.setAttribute('aria-pressed', 'true')
492
+ buttons.pan.setAttribute('aria-pressed', 'false')
493
+ }
428
494
 
429
495
  host.appendChild(bar)
430
496
  this.elToolbar = bar
@@ -49,6 +49,17 @@ export default class TrellisExports {
49
49
  }
50
50
  }
51
51
 
52
+ /**
53
+ * Panels on screen. A promotion parks the rest as display:none, whose
54
+ * rects collapse onto the wrap origin; the export is what the user sees,
55
+ * so a promoted grid exports the promoted panel alone.
56
+ */
57
+ _shown() {
58
+ return this.trellis.panels.filter(
59
+ (p) => !p.cellEl?.classList.contains('apexcharts-trellis-cell-parked'),
60
+ )
61
+ }
62
+
52
63
  /** Export surface size = the wrap's own box. */
53
64
  _size() {
54
65
  const wrap = /** @type {HTMLElement} */ (this.trellis.elWrap)
@@ -67,19 +78,28 @@ export default class TrellisExports {
67
78
  return '#fff'
68
79
  }
69
80
 
70
- /** Text pieces to compose: title + one header per cell (+ legend items). */
81
+ /**
82
+ * Text pieces to compose: title + subtitle + one header per cell (+ legend
83
+ * items). An inline `text-align` (the title's `align`) wins over the
84
+ * piece's default anchor.
85
+ */
71
86
  _chromeTexts() {
72
87
  const t = this.trellis
73
- /** @type {Array<{ text: string, x: number, y: number, font: string, fontSize: number, color: string, align: 'left'|'center' }>} */
88
+ /** @type {Array<{ text: string, x: number, y: number, font: string, fontSize: number, color: string, align: 'left'|'center'|'right' }>} */
74
89
  const texts = []
75
- /** @param {Element|null} el @param {'left'|'center'} align */
76
- const push = (el, align) => {
90
+ /** @param {Element|null} el @param {'left'|'center'} fallback */
91
+ const push = (el, fallback) => {
77
92
  if (!el || !el.textContent) return
78
93
  const rect = this._rectIn(el)
79
94
  const cs = getComputedStyle(/** @type {HTMLElement} */ (el))
95
+ const align = /** @type {'left'|'center'|'right'} */ (
96
+ /** @type {HTMLElement} */ (el).style.textAlign || fallback
97
+ )
80
98
  texts.push({
81
99
  text: el.textContent,
82
- x: align === 'center' ? rect.x + rect.w / 2 : rect.x,
100
+ x:
101
+ rect.x +
102
+ (align === 'left' ? 0 : align === 'right' ? rect.w : rect.w / 2),
83
103
  y: rect.y + rect.h / 2,
84
104
  font: `${cs.fontWeight} ${cs.fontSize} ${cs.fontFamily}`,
85
105
  fontSize: parseFloat(cs.fontSize) || 12,
@@ -88,7 +108,8 @@ export default class TrellisExports {
88
108
  })
89
109
  }
90
110
  push(t.chrome.elTitle, 'left')
91
- t.panels.forEach((p) => {
111
+ push(t.chrome.elSubtitle, 'left')
112
+ this._shown().forEach((p) => {
92
113
  if (!p.cellEl) return
93
114
  push(p.cellEl.querySelector('.apexcharts-trellis-header'), 'center')
94
115
  })
@@ -122,7 +143,6 @@ export default class TrellisExports {
122
143
  * @returns {Promise<{ imgURI: string }>}
123
144
  */
124
145
  async dataURI(options = {}) {
125
- const t = this.trellis
126
146
  const scale = options.scale || 1
127
147
  const size = this._size()
128
148
  const canvas = document.createElement('canvas')
@@ -136,7 +156,7 @@ export default class TrellisExports {
136
156
  ctx.fillRect(0, 0, size.w, size.h)
137
157
 
138
158
  // Panels first (their own export path), skeleton tint for unmounted.
139
- for (const p of t.panels) {
159
+ for (const p of this._shown()) {
140
160
  if (!p.el) continue
141
161
  const rect = this._rectIn(p.el)
142
162
  if (!p.chart) {
@@ -180,7 +200,6 @@ export default class TrellisExports {
180
200
  * @returns {Promise<string>}
181
201
  */
182
202
  async svgString(scale = 1) {
183
- const t = this.trellis
184
203
  const size = this._size()
185
204
  /** @type {string[]} */
186
205
  const parts = []
@@ -190,7 +209,7 @@ export default class TrellisExports {
190
209
  parts.push(
191
210
  `<rect width="${size.w}" height="${size.h}" fill="${this._background()}"/>`,
192
211
  )
193
- for (const p of t.panels) {
212
+ for (const p of this._shown()) {
194
213
  if (!p.el) continue
195
214
  const rect = this._rectIn(p.el)
196
215
  if (!p.chart) {
@@ -205,7 +224,12 @@ export default class TrellisExports {
205
224
  )
206
225
  }
207
226
  for (const piece of this._chromeTexts()) {
208
- const anchor = piece.align === 'center' ? 'middle' : 'start'
227
+ const anchor =
228
+ piece.align === 'left'
229
+ ? 'start'
230
+ : piece.align === 'right'
231
+ ? 'end'
232
+ : 'middle'
209
233
  parts.push(
210
234
  `<text x="${piece.x}" y="${piece.y}" text-anchor="${anchor}" dominant-baseline="central" style="font:${escapeAttr(
211
235
  piece.font,
@@ -248,9 +272,20 @@ export default class TrellisExports {
248
272
  ? `"${s.replace(/"/g, '""')}"`
249
273
  : s
250
274
  }
275
+ // Plain value arrays carry no x: the union holds positions, so write the
276
+ // label the axis shows there (categories, then labels, then 1-based).
277
+ const cats =
278
+ split.xForm === 'plain' &&
279
+ (w.config.xaxis.categories?.length
280
+ ? w.config.xaxis.categories
281
+ : w.config.labels || [])
251
282
  /** @param {any} x */
252
- const xOut = (x) =>
253
- isDatetime && typeof x === 'number' ? new Date(x).toISOString() : x
283
+ const xOut = (x) => {
284
+ if (cats) x = cats[x] ?? x + 1
285
+ // a multi-line category reads as one line, as in a plain chart's CSV
286
+ if (Array.isArray(x)) x = x.join(' ')
287
+ return isDatetime && typeof x === 'number' ? new Date(x).toISOString() : x
288
+ }
254
289
 
255
290
  const names = split.seriesNames
256
291
  const lines = [
@@ -78,13 +78,12 @@ function observationValues(data) {
78
78
 
79
79
  /**
80
80
  * Raw observation ARRAYS of one series' data (the violin/boxPlot raw form:
81
- * `{ x: 'A', y: [obs...] }`), flattened.
81
+ * `{ x: 'A', y: [obs...] }`), flattened onto `out`.
82
82
  * @param {any} data
83
+ * @param {number[]} out appended to in place, so a union grows in one pass
83
84
  * @returns {number[]}
84
85
  */
85
- function rawObservationArrays(data) {
86
- /** @type {number[]} */
87
- const out = []
86
+ function rawObservationArrays(data, out) {
88
87
  if (!Array.isArray(data)) return out
89
88
  data.forEach((/** @type {any} */ d) => {
90
89
  const y = d && typeof d === 'object' ? d.y : null
@@ -146,16 +145,13 @@ export function buildTypeFrames(splitResult, cfg, hostConfig, chartType) {
146
145
  // which pins the edges no matter what each panel's own extent is.
147
146
  if (chartType === 'histogram') {
148
147
  const hcfg = plot.histogram || {}
149
- /** @type {number[]} */
150
- let union = []
151
148
  /** @type {number[][][]} per panel, per series */
152
149
  const panelSeriesVals = panels.map((p) =>
153
- p.series.map((/** @type {any} */ s) => {
154
- const vals = observationValues(s.data)
155
- union = union.concat(vals)
156
- return vals
157
- }),
150
+ p.series.map((/** @type {any} */ s) => observationValues(s.data)),
158
151
  )
152
+ // One flatten: re-concatenating the growing union per series copied it
153
+ // once per series (quadratic in the panel count).
154
+ const union = panelSeriesVals.flat(2)
159
155
  const binning = computeBinning(union, {
160
156
  bins: hcfg.bins,
161
157
  binWidth: hcfg.binWidth,
@@ -203,11 +199,11 @@ export function buildTypeFrames(splitResult, cfg, hostConfig, chartType) {
203
199
  const kde = plot.violin?.kde || {}
204
200
  if (!(typeof kde.bandwidth === 'number' && kde.bandwidth > 0)) {
205
201
  /** @type {number[]} */
206
- let union = []
202
+ const union = []
207
203
  panels.forEach((p) =>
208
- p.series.forEach((/** @type {any} */ s) => {
209
- union = union.concat(rawObservationArrays(s.data))
210
- }),
204
+ p.series.forEach((/** @type {any} */ s) =>
205
+ rawObservationArrays(s.data, union),
206
+ ),
211
207
  )
212
208
  const est = union.length
213
209
  ? kernelDensity(union, { resolution: 8 })