apexcharts 7.3.0 → 7.5.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 (177) hide show
  1. package/LICENSE +39 -11
  2. package/dist/apexcharts.common.js +2 -2
  3. package/dist/apexcharts.core.js +579 -467
  4. package/dist/apexcharts.core.min.js +2 -2
  5. package/dist/apexcharts.esm.js +1347 -810
  6. package/dist/apexcharts.js +896 -267
  7. package/dist/apexcharts.min.js +2 -2
  8. package/dist/apexcharts.ssr.common.js +2 -2
  9. package/dist/apexcharts.ssr.esm.js +896 -267
  10. package/dist/area.common.js +1 -1
  11. package/dist/area.esm.js +1 -1
  12. package/dist/bar.common.js +2 -2
  13. package/dist/bar.esm.js +20 -10
  14. package/dist/bar.js +2 -2
  15. package/dist/boxPlot.common.js +2 -2
  16. package/dist/boxPlot.esm.js +20 -10
  17. package/dist/bubble.common.js +1 -1
  18. package/dist/bubble.esm.js +1 -1
  19. package/dist/candlestick.common.js +2 -2
  20. package/dist/candlestick.esm.js +20 -10
  21. package/dist/candlestick.js +2 -2
  22. package/dist/column.common.js +2 -2
  23. package/dist/column.esm.js +20 -10
  24. package/dist/core.common.js +2 -2
  25. package/dist/core.esm.js +579 -467
  26. package/dist/donut.common.js +1 -1
  27. package/dist/donut.esm.js +1 -1
  28. package/dist/dumbbell.common.js +2 -2
  29. package/dist/dumbbell.esm.js +20 -10
  30. package/dist/features/all.common.js +2 -2
  31. package/dist/features/all.esm.js +536 -34
  32. package/dist/features/annotations.common.js +1 -1
  33. package/dist/features/annotations.esm.js +1 -1
  34. package/dist/features/annotations.js +1 -1
  35. package/dist/features/context-menu.common.js +1 -1
  36. package/dist/features/context-menu.esm.js +1 -1
  37. package/dist/features/context-menu.js +1 -1
  38. package/dist/features/drilldown.common.js +1 -1
  39. package/dist/features/drilldown.esm.js +1 -1
  40. package/dist/features/drilldown.js +1 -1
  41. package/dist/features/dumbbell.common.js +1 -1
  42. package/dist/features/dumbbell.esm.js +1 -1
  43. package/dist/features/dumbbell.js +1 -1
  44. package/dist/features/exports.common.js +2 -2
  45. package/dist/features/exports.esm.js +12 -14
  46. package/dist/features/exports.js +2 -2
  47. package/dist/features/facet.common.js +1 -1
  48. package/dist/features/facet.esm.js +1 -1
  49. package/dist/features/facet.js +1 -1
  50. package/dist/features/history.common.js +1 -1
  51. package/dist/features/history.esm.js +1 -1
  52. package/dist/features/history.js +1 -1
  53. package/dist/features/ink.common.js +1 -1
  54. package/dist/features/ink.esm.js +1 -1
  55. package/dist/features/ink.js +1 -1
  56. package/dist/features/keyboard.common.js +2 -2
  57. package/dist/features/keyboard.esm.js +14 -1
  58. package/dist/features/keyboard.js +2 -2
  59. package/dist/features/legend.common.js +1 -1
  60. package/dist/features/legend.esm.js +1 -1
  61. package/dist/features/legend.js +1 -1
  62. package/dist/features/link.common.js +1 -1
  63. package/dist/features/link.esm.js +38 -38
  64. package/dist/features/link.js +1 -1
  65. package/dist/features/marks.common.js +1 -1
  66. package/dist/features/marks.esm.js +1 -1
  67. package/dist/features/marks.js +1 -1
  68. package/dist/features/measure.common.js +1 -1
  69. package/dist/features/measure.esm.js +1 -1
  70. package/dist/features/measure.js +1 -1
  71. package/dist/features/morph.common.js +1 -1
  72. package/dist/features/morph.esm.js +1 -1
  73. package/dist/features/morph.js +1 -1
  74. package/dist/features/perspectives.common.js +1 -1
  75. package/dist/features/perspectives.esm.js +11 -11
  76. package/dist/features/perspectives.js +1 -1
  77. package/dist/features/raincloud.common.js +1 -1
  78. package/dist/features/raincloud.esm.js +1 -1
  79. package/dist/features/raincloud.js +1 -1
  80. package/dist/features/renderer-canvas.common.js +1 -1
  81. package/dist/features/renderer-canvas.esm.js +1 -1
  82. package/dist/features/renderer-canvas.js +1 -1
  83. package/dist/features/stats.common.js +1 -1
  84. package/dist/features/stats.esm.js +1 -1
  85. package/dist/features/stats.js +1 -1
  86. package/dist/features/storyboard.common.js +1 -1
  87. package/dist/features/storyboard.esm.js +11 -11
  88. package/dist/features/storyboard.js +1 -1
  89. package/dist/features/streamgraph.common.js +1 -1
  90. package/dist/features/streamgraph.esm.js +1 -1
  91. package/dist/features/streamgraph.js +1 -1
  92. package/dist/features/toolbar.common.js +2 -2
  93. package/dist/features/toolbar.esm.js +172 -25
  94. package/dist/features/toolbar.js +2 -2
  95. package/dist/features/trellis.common.js +1 -1
  96. package/dist/features/trellis.esm.js +1 -1
  97. package/dist/features/trellis.js +1 -1
  98. package/dist/features/waterfall.common.js +1 -1
  99. package/dist/features/waterfall.esm.js +1 -1
  100. package/dist/features/waterfall.js +1 -1
  101. package/dist/features/weave.common.js +2 -2
  102. package/dist/features/weave.esm.js +352 -10
  103. package/dist/features/weave.js +2 -2
  104. package/dist/heatmap.common.js +1 -1
  105. package/dist/heatmap.esm.js +1 -1
  106. package/dist/heatmap.js +1 -1
  107. package/dist/histogram.common.js +2 -2
  108. package/dist/histogram.esm.js +20 -10
  109. package/dist/line.common.js +1 -1
  110. package/dist/line.esm.js +1 -1
  111. package/dist/line.js +1 -1
  112. package/dist/pictograms.common.js +1 -1
  113. package/dist/pictograms.esm.js +1 -1
  114. package/dist/pictograms.js +1 -1
  115. package/dist/pie.common.js +1 -1
  116. package/dist/pie.esm.js +1 -1
  117. package/dist/pie.js +1 -1
  118. package/dist/polarArea.common.js +1 -1
  119. package/dist/polarArea.esm.js +1 -1
  120. package/dist/radar.common.js +1 -1
  121. package/dist/radar.esm.js +1 -1
  122. package/dist/radar.js +1 -1
  123. package/dist/radialBar.common.js +2 -2
  124. package/dist/radialBar.esm.js +27 -2
  125. package/dist/radialBar.js +2 -2
  126. package/dist/raincloud.common.js +2 -2
  127. package/dist/raincloud.esm.js +20 -10
  128. package/dist/rangeArea.common.js +1 -1
  129. package/dist/rangeArea.esm.js +1 -1
  130. package/dist/rangeBar.common.js +2 -2
  131. package/dist/rangeBar.esm.js +20 -10
  132. package/dist/scatter.common.js +1 -1
  133. package/dist/scatter.esm.js +1 -1
  134. package/dist/streamgraph.common.js +1 -1
  135. package/dist/streamgraph.esm.js +1 -1
  136. package/dist/sunburst.common.js +1 -1
  137. package/dist/sunburst.esm.js +1 -1
  138. package/dist/sunburst.js +1 -1
  139. package/dist/treemap.common.js +1 -1
  140. package/dist/treemap.esm.js +1 -1
  141. package/dist/treemap.js +1 -1
  142. package/dist/unit-shapes.common.js +1 -1
  143. package/dist/unit-shapes.esm.js +1 -1
  144. package/dist/unit-shapes.js +1 -1
  145. package/dist/unit.common.js +1 -1
  146. package/dist/unit.esm.js +79 -79
  147. package/dist/unit.js +1 -1
  148. package/dist/violin.common.js +2 -2
  149. package/dist/violin.esm.js +20 -10
  150. package/dist/violin.js +2 -2
  151. package/dist/waterfall.common.js +2 -2
  152. package/dist/waterfall.esm.js +20 -10
  153. package/package.json +2 -2
  154. package/src/apexcharts.js +39 -15
  155. package/src/charts/Radial.js +12 -3
  156. package/src/charts/common/bar/Helpers.js +31 -12
  157. package/src/modules/Data.js +8 -2
  158. package/src/modules/DataLabels.js +14 -4
  159. package/src/modules/Exports.js +14 -12
  160. package/src/modules/Formatters.js +24 -1
  161. package/src/modules/Graphics.js +10 -0
  162. package/src/modules/Range.js +28 -1
  163. package/src/modules/Toolbar.js +112 -2
  164. package/src/modules/ZoomPanSelection.js +85 -7
  165. package/src/modules/accessibility/KeyboardNavigation.js +14 -0
  166. package/src/modules/axes/YAxis.js +12 -15
  167. package/src/modules/helpers/UpdateHelpers.js +25 -8
  168. package/src/modules/settings/Globals.js +5 -10
  169. package/src/modules/settings/Options.js +15 -0
  170. package/src/modules/weave/Claims.js +231 -0
  171. package/src/modules/weave/Drawn.js +169 -0
  172. package/src/modules/weave/PluginAPI.js +195 -4
  173. package/src/modules/weave/WeaveHost.js +76 -3
  174. package/src/ssr/DOMShim.js +50 -0
  175. package/src/types/internal.d.ts +1 -1
  176. package/src/utils/Utils.js +6 -9
  177. package/types/apexcharts.d.ts +92 -0
@@ -1,4 +1,7 @@
1
1
  // @ts-check
2
+ import { addClaim, normaliseEntries, releaseClaim } from './Claims'
3
+ import { collectDrawn } from './Drawn'
4
+
2
5
  /**
3
6
  * The frozen facade handed to each Weave plugin. Plugins NEVER receive raw `w`,
4
7
  * internal module instances, or the `__apex_*` internals: only this stable,
@@ -25,12 +28,56 @@
25
28
  * tooltip and its `dataPoint*` events; before this a plugin either re-derived
26
29
  * it from raw pixels or did without.
27
30
  *
31
+ * v5 added `api.info.stroke`, the caller's own `stroke.dashArray`. That option
32
+ * is indexed by series position, so a plugin that wants its own derived series
33
+ * dashed must write the whole array; reporting the current value is what lets
34
+ * it put back what it found instead of flattening the caller's dashed lines.
35
+ *
36
+ * v6 also added `api.info.title` (the name the page already gave this chart, so
37
+ * a readout naming several charts has something better than a container id) and
38
+ * `modifiers` on the pointer payload (so a gesture can be shift-click).
39
+ *
40
+ * v6 added three larger pieces, each closing a class rather than one incident.
41
+ * `api.capabilities` / `api.can()` replace the typeof-sniffing below with
42
+ * something a plugin can ask. `api.claim()` generalises what v5 only reported,
43
+ * so a plugin sets a positional option for ITS OWN series and the host resolves
44
+ * it where the option is read, writing nothing. `api.drawn()` / `api.declare()`
45
+ * publish what is on the chart, including other features' output, which is the
46
+ * question a layers panel and an export summary both ask. See
47
+ * `plans/26-weave-capabilities.md`.
48
+ *
28
49
  * Feature-detect rather than bumping `apiVersion` unless the plugin genuinely
29
50
  * cannot work without the new surface: a plugin declaring v3 is SKIPPED
30
- * outright on a v2 host, so `apiVersion: 2` plus `typeof api.reserve ===
31
- * 'function'` keeps one build working on both.
51
+ * outright on a v2 host, so `apiVersion: 2` plus a capability check keeps one
52
+ * build working on both.
32
53
  */
33
- export const WEAVE_API_VERSION = 4
54
+ export const WEAVE_API_VERSION = 6
55
+
56
+ /**
57
+ * What this host can do, for plugins that must run against older ones too.
58
+ *
59
+ * The version integer cannot answer this. A plugin declaring a version newer
60
+ * than the host is skipped outright, so a plugin supporting several hosts
61
+ * declares the LOWEST version it can run on and then has to discover anything
62
+ * above that. Until now it did so by sniffing the facade for function members.
63
+ *
64
+ * Named for the capability rather than the version that introduced it, and
65
+ * permanent once published: removing a name is a breaking change on the same
66
+ * terms as removing a member.
67
+ *
68
+ * `scales` is deliberately absent. It is null for non-axis charts, which is a
69
+ * property of the chart rather than of the host, and advertising it here would
70
+ * tell a plugin on a pie chart that projection is available.
71
+ */
72
+ export const WEAVE_CAPABILITIES = Object.freeze([
73
+ 'layer',
74
+ 'derived',
75
+ 'reserve',
76
+ 'pointer',
77
+ 'stroke-info',
78
+ 'claim',
79
+ 'drawn',
80
+ ])
34
81
 
35
82
  /**
36
83
  * Public chart methods safe to expose to plugins (each bound to ctx). Excludes
@@ -307,6 +354,31 @@ export function buildPluginAPI(host, record) {
307
354
  ? w.config.dataLabels.enabledOnSeries.slice()
308
355
  : null,
309
356
  }),
357
+ // The caller's own dashing, reported for the same reason and against
358
+ // the same trap (v5). `stroke.dashArray` is indexed by series position
359
+ // with no per-series escape hatch, so a plugin that wants ITS OWN
360
+ // computed series dashed has to write the whole array, and writing one
361
+ // without knowing what was there discards the caller's dashed lines
362
+ // with nothing to restore them from.
363
+ //
364
+ // A scalar applies to every series and an array is per series. There is
365
+ // no "unset" to report: the option defaults to 0, and 0 already means
366
+ // no dashing, so restoring it restores exactly what was there.
367
+ stroke: Object.freeze({
368
+ dashArray: Array.isArray(w.config.stroke && w.config.stroke.dashArray)
369
+ ? w.config.stroke.dashArray.slice()
370
+ : (w.config.stroke && w.config.stroke.dashArray) || 0,
371
+ }),
372
+ // What the chart calls itself, where the caller titled it (v6).
373
+ //
374
+ // For a plugin that has to NAME this chart to somebody: a page-level
375
+ // readout listing several charts otherwise has only the container's id
376
+ // to head each row with, which is a string written for a stylesheet.
377
+ // The title is the name the page already chose and put on screen.
378
+ //
379
+ // Empty string rather than undefined for an untitled chart, so a
380
+ // caller can use it directly in a template; falsy either way.
381
+ title: String((w.config.title && w.config.title.text) || ''),
310
382
  })
311
383
  },
312
384
 
@@ -377,6 +449,88 @@ export function buildPluginAPI(host, record) {
377
449
  return api
378
450
  },
379
451
 
452
+ /**
453
+ * Set a positional option for your own series, without writing the
454
+ * caller's config.
455
+ *
456
+ * Some options are indexed by series position with no per-series escape
457
+ * hatch, so setting one for a single series has always meant writing the
458
+ * array that covers all of them, then putting the caller's value back. A
459
+ * claim says what this plugin wants instead, and the host answers with it
460
+ * where the option is READ. Nothing is written, so releasing is a deletion
461
+ * rather than a restore, and a caller's own `updateOptions` composes with
462
+ * the claim instead of being reverted by it.
463
+ *
464
+ * const claim = api.claim('stroke.dashArray', [
465
+ * { series: 'Revenue (forecast)', value: 6 },
466
+ * ])
467
+ * claim.release()
468
+ *
469
+ * Name the series rather than its position where you can: a name is
470
+ * resolved each time the option is read, so the claim follows the series
471
+ * through the caller adding, removing or reordering others.
472
+ *
473
+ * Claimable options are an allowlist (see `CLAIMABLE`). An option that is
474
+ * not on it returns null rather than throwing, so a plugin written against
475
+ * a newer host degrades. Every claim is released on teardown, on destroy,
476
+ * and if the host disables this plugin after repeated failures.
477
+ *
478
+ * @param {string} option
479
+ * @param {Array<{series: string|number, value: any}>} entries
480
+ * @returns {{release: () => void, update: (entries: Array<{series: string|number, value: any}>) => void}|null}
481
+ * @since Weave v6
482
+ */
483
+ claim(option, entries) {
484
+ const claim = addClaim(w, record.def.name, option, entries)
485
+ if (!claim) return null
486
+ return Object.freeze({
487
+ release() {
488
+ releaseClaim(w, claim)
489
+ },
490
+ update(next) {
491
+ claim.entries = normaliseEntries(option, next)
492
+ },
493
+ })
494
+ },
495
+
496
+ /**
497
+ * Everything drawn on this chart, including what other features drew.
498
+ *
499
+ * The chart's series, the caller's annotations (ink strokes among them,
500
+ * since an ink stroke is an annotation), and whatever plugins have
501
+ * declared. Each entry names its `owner`, because the list is only as
502
+ * complete as the features that opted into it: a reader can say what it
503
+ * covers instead of assuming it is everything.
504
+ *
505
+ * Read only. Removing or hiding another feature's output is a much larger
506
+ * promise than this platform makes, and is deliberately not here.
507
+ *
508
+ * Rebuilt per call: it is a projection of live state, and a remembered
509
+ * inventory is a list of what WAS drawn.
510
+ *
511
+ * @returns {ReadonlyArray<{id: string, kind: 'series'|'annotation'|'overlay', label: string, owner: string, visible: boolean}>}
512
+ * @since Weave v6
513
+ */
514
+ drawn() {
515
+ return Object.freeze(collectDrawn(w, host).map((i) => Object.freeze(i)))
516
+ },
517
+
518
+ /**
519
+ * Say what this plugin has drawn, so it appears in `api.drawn()`.
520
+ *
521
+ * Declare from your draw handler, on the same terms as the drawing itself:
522
+ * declarations are cleared with the layers at the start of every draw, so
523
+ * an inventory cannot outlive what it describes. Declaring the same id
524
+ * twice replaces it rather than adding a second row.
525
+ *
526
+ * @param {{id: string, label?: string, visible?: boolean}} item
527
+ * @since Weave v6
528
+ */
529
+ declare(item) {
530
+ host._declare(record.def.name, item)
531
+ return api
532
+ },
533
+
380
534
  /**
381
535
  * Subscribe to the data point a viewer is pointing at.
382
536
  *
@@ -400,7 +554,12 @@ export function buildPluginAPI(host, record) {
400
554
  * and a handler that throws is contained rather than allowed to break the
401
555
  * interaction it was watching.
402
556
  *
403
- * @param {(e: {type: 'enter'|'leave'|'select', seriesIndex: number, dataPointIndex: number, category: string|undefined, seriesName: string|undefined, selected: boolean|undefined}) => void} fn
557
+ * `modifiers` (v6) reports the keys held during the interaction, for the
558
+ * gestures that need them: shift-click to add to a selection is the one
559
+ * page-level coordination wants. All four are false when the interaction
560
+ * came from somewhere with no DOM event, such as the keyboard.
561
+ *
562
+ * @param {(e: {type: 'enter'|'leave'|'select', seriesIndex: number, dataPointIndex: number, category: string|undefined, seriesName: string|undefined, selected: boolean|undefined, modifiers: {shift: boolean, ctrl: boolean, alt: boolean, meta: boolean}}) => void} fn
404
563
  * @returns {() => void} unsubscribe
405
564
  * @since Weave v4
406
565
  */
@@ -428,5 +587,37 @@ export function buildPluginAPI(host, record) {
428
587
  },
429
588
  }
430
589
 
590
+ // Probed off the facade that was actually built rather than copied from the
591
+ // constant, so a member that is ever made conditional drops out instead of
592
+ // being advertised and then missing.
593
+ //
594
+ // An ARRAY rather than a Set, because a frozen Set is not actually read-only:
595
+ // Object.freeze does not touch internal slots, so `add()` and `delete()` keep
596
+ // working and one plugin could edit what every later one is told. The Set
597
+ // stays in the closure and `can()` is the lookup.
598
+ const probes = /** @type {Record<string, (a: any) => boolean>} */ (WIRED)
599
+ const granted = WEAVE_CAPABILITIES.filter((name) => probes[name](api))
600
+ const grantedSet = new Set(granted)
601
+ const extras = /** @type {any} */ (api)
602
+ extras.capabilities = Object.freeze(granted)
603
+ /** Whether this host supports a named capability. @since Weave v6 */
604
+ extras.can = (/** @type {string} */ name) => grantedSet.has(name)
605
+
431
606
  return Object.freeze(api)
432
607
  }
608
+
609
+ /**
610
+ * How each capability name is confirmed present. One entry per member of
611
+ * {@link WEAVE_CAPABILITIES}; a name with no probe would always be advertised,
612
+ * which is the failure this table exists to prevent.
613
+ */
614
+ const WIRED = {
615
+ layer: (/** @type {any} */ a) => typeof a.layer === 'function',
616
+ derived: (/** @type {any} */ a) => typeof a.markDerived === 'function',
617
+ reserve: (/** @type {any} */ a) => typeof a.reserve === 'function',
618
+ pointer: (/** @type {any} */ a) => typeof a.pointer === 'function',
619
+ 'stroke-info': (/** @type {any} */ a) => !!(a.info && a.info.stroke),
620
+ claim: (/** @type {any} */ a) => typeof a.claim === 'function',
621
+ drawn: (/** @type {any} */ a) =>
622
+ typeof a.drawn === 'function' && typeof a.declare === 'function',
623
+ }
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
  import { getPlugin } from './PluginRegistry'
3
3
  import { buildPluginAPI, makeLayerHandle, WEAVE_API_VERSION } from './PluginAPI'
4
+ import { releaseOwner } from './Claims'
4
5
 
5
6
  export { WEAVE_API_VERSION }
6
7
 
@@ -52,6 +53,8 @@ export default class WeaveHost {
52
53
  this._reserveTimer = null
53
54
  /** @type {Map<string, Function[]>|null} plugin name -> pointer handlers */
54
55
  this._pointerSubs = null
56
+ /** @type {Map<string, any[]>|null} plugin name -> what it says it drew */
57
+ this._declared = null
55
58
  /** @type {Array<[string, Function]>|null} chart listeners to remove on teardown */
56
59
  this._pointerWired = null
57
60
 
@@ -185,6 +188,11 @@ export default class WeaveHost {
185
188
  record.failures = (record.failures || 0) + 1
186
189
  if (record.failures >= 3) {
187
190
  record.disabled = true
191
+ // Its claims go with it. A disabled plugin has stopped running, so a
192
+ // claim it left behind would go on answering for an option nobody is
193
+ // maintaining, which is the permanent-leak case claims exist to rule
194
+ // out (see weave/Claims).
195
+ releaseOwner(this.w, record.def.name)
188
196
  console.error(
189
197
  `[apexcharts] plugin "${record.def.name}" disabled after repeated errors.`,
190
198
  )
@@ -431,8 +439,11 @@ export default class WeaveHost {
431
439
  ]
432
440
  this._pointerWired = []
433
441
  for (const [type, name] of map) {
434
- const handler = (/** @type {any} */ _e, /** @type {any} */ _ctx, /** @type {any} */ opts) => {
435
- this._emitPointer(type, opts)
442
+ // The DOM event is carried through rather than dropped: it is the only
443
+ // place the modifier keys exist, and a gesture like shift-click to add to
444
+ // a selection cannot be expressed without them. See `_emitPointer`.
445
+ const handler = (/** @type {any} */ e, /** @type {any} */ _ctx, /** @type {any} */ opts) => {
446
+ this._emitPointer(type, opts, e)
436
447
  }
437
448
  this.ctx.addEventListener(name, handler)
438
449
  this._pointerWired.push([name, handler])
@@ -449,8 +460,9 @@ export default class WeaveHost {
449
460
  *
450
461
  * @param {'enter'|'leave'|'select'} type
451
462
  * @param {any} opts
463
+ * @param {any} [e] the DOM event, where the interaction came from one
452
464
  */
453
- _emitPointer(type, opts) {
465
+ _emitPointer(type, opts, e) {
454
466
  if (!this._pointerSubs || !this._pointerSubs.size) return
455
467
  const seriesIndex = opts && typeof opts.seriesIndex === 'number' ? opts.seriesIndex : -1
456
468
  const dataPointIndex =
@@ -475,6 +487,14 @@ export default class WeaveHost {
475
487
  // set, and what a plugin wants to know is whether THIS point is now in
476
488
  // it, so a second click reads as a deselect rather than another select.
477
489
  selected: type === 'select' ? WeaveHost._isSelected(opts, seriesIndex, dataPointIndex) : undefined,
490
+ // The modifier keys held during the interaction (v6), for the gestures
491
+ // that need them: shift-click to add to a selection is the one page mode
492
+ // wants, and a plugin cannot invent it from anything else here.
493
+ //
494
+ // All false when the interaction came from somewhere with no DOM event
495
+ // (the keyboard, a programmatic selection), which is the honest answer:
496
+ // no key was held.
497
+ modifiers: WeaveHost._modifiers(e),
478
498
  }
479
499
  for (const [name, handlers] of this._pointerSubs) {
480
500
  for (const fn of handlers.slice()) {
@@ -526,6 +546,24 @@ export default class WeaveHost {
526
546
  return mine.indexOf(dataPointIndex) > -1
527
547
  }
528
548
 
549
+ /**
550
+ * Which modifier keys were held, read off the DOM event.
551
+ *
552
+ * Always the same four booleans, never undefined and never a partial object:
553
+ * a plugin writes `if (e.modifiers.shift)` without a guard, and a shape that
554
+ * sometimes lacks a key is how that becomes a crash inside a viewer's click.
555
+ *
556
+ * @param {any} e
557
+ */
558
+ static _modifiers(e) {
559
+ return Object.freeze({
560
+ shift: !!(e && e.shiftKey),
561
+ ctrl: !!(e && e.ctrlKey),
562
+ alt: !!(e && e.altKey),
563
+ meta: !!(e && e.metaKey),
564
+ })
565
+ }
566
+
529
567
  /**
530
568
  * Record a plugin's container reservation and re-render if it changed.
531
569
  *
@@ -730,6 +768,39 @@ export default class WeaveHost {
730
768
  Array.prototype.forEach.call(groups, (/** @type {any} */ n) => n.remove())
731
769
  }
732
770
  this._layers.clear()
771
+ // Declarations go with the drawing they describe. A plugin repaints its
772
+ // overlays from state on every draw, and it declares them the same way, so
773
+ // an inventory can never outlive what it is an inventory OF.
774
+ this._declared = null
775
+ }
776
+
777
+ /**
778
+ * Record one thing a plugin has drawn, for `api.drawn()`.
779
+ *
780
+ * Replaced by id rather than appended, so a plugin declaring the same overlay
781
+ * on every draw (which is the pattern this expects) produces one entry.
782
+ *
783
+ * @param {string} name plugin
784
+ * @param {{id: string, label?: string, visible?: boolean}} item
785
+ */
786
+ _declare(name, item) {
787
+ if (!item || typeof item.id !== 'string' || !item.id) {
788
+ console.warn(
789
+ `[apexcharts] plugin "${name}" declared something with no id; ignored.`,
790
+ )
791
+ return
792
+ }
793
+ if (!this._declared) this._declared = new Map()
794
+ const mine = this._declared.get(name) || []
795
+ const entry = {
796
+ id: item.id,
797
+ label: item.label ? String(item.label) : item.id,
798
+ visible: item.visible !== false,
799
+ }
800
+ const at = mine.findIndex((/** @type {any} */ d) => d.id === entry.id)
801
+ if (at > -1) mine[at] = entry
802
+ else mine.push(entry)
803
+ this._declared.set(name, mine)
733
804
  }
734
805
 
735
806
  // ─── Config-change reconciliation ───────────────────────────────────────
@@ -795,10 +866,12 @@ export default class WeaveHost {
795
866
  for (const record of this.active) {
796
867
  this._guard(record, 'destroy', () => record.def.destroy && record.def.destroy(record.api))
797
868
  }
869
+ for (const record of this.active) releaseOwner(this.w, record.def.name)
798
870
  this.active = []
799
871
  this._derived = null
800
872
  this._reserved = null
801
873
  this._pointerSubs = null
874
+ this._declared = null
802
875
  if (this._pointerWired) {
803
876
  for (const [name, handler] of this._pointerWired) {
804
877
  this.ctx.removeEventListener && this.ctx.removeEventListener(name, handler)
@@ -4,6 +4,14 @@
4
4
  * Provides just enough SVG element emulation to create chart structure without full DOM
5
5
  */
6
6
 
7
+ // Approximate sans-serif metrics, in em, used to estimate SVG text bounds when
8
+ // no font engine is available to measure the glyphs.
9
+ const CHAR_WIDTH_EM = 0.55
10
+ const ASCENT_EM = 0.917
11
+ const DESCENT_EM = 0.25
12
+ const LINE_HEIGHT_EM = 1.1
13
+ const DEFAULT_FONT_SIZE = 11
14
+
7
15
  /**
8
16
  * Mock SVG element for SSR environment
9
17
  */
@@ -148,6 +156,48 @@ class SSRElement {
148
156
  }
149
157
  }
150
158
 
159
+ getBBox() {
160
+ if (this.nodeName !== 'text' && this.nodeName !== 'tspan') {
161
+ return { x: 0, y: 0, width: 0, height: 0 }
162
+ }
163
+
164
+ const lines = this._textLines()
165
+ const longest = lines.reduce((acc, line) => Math.max(acc, line.length), 0)
166
+ if (!longest) {
167
+ return { x: 0, y: 0, width: 0, height: 0 }
168
+ }
169
+
170
+ const fontSize =
171
+ parseFloat(this.attributes.get('font-size')) || DEFAULT_FONT_SIZE
172
+ const width = longest * fontSize * CHAR_WIDTH_EM
173
+ const height =
174
+ fontSize * (ASCENT_EM + DESCENT_EM) +
175
+ (lines.length - 1) * fontSize * LINE_HEIGHT_EM
176
+
177
+ const anchor = this.attributes.get('text-anchor')
178
+ const x = parseFloat(this.attributes.get('x')) || 0
179
+ const y = parseFloat(this.attributes.get('y')) || 0
180
+
181
+ return {
182
+ x: anchor === 'middle' ? x - width / 2 : anchor === 'end' ? x - width : x,
183
+ y: y - fontSize * ASCENT_EM,
184
+ width,
185
+ height,
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Text of this element, one entry per rendered line
191
+ * @returns {string[]}
192
+ */
193
+ _textLines() {
194
+ const tspans = this.children.filter((child) => child.nodeName === 'tspan')
195
+ if (tspans.length) {
196
+ return tspans.map((tspan) => String(tspan.textContent ?? ''))
197
+ }
198
+ return [String(this.textContent ?? '')]
199
+ }
200
+
151
201
  getRootNode() {
152
202
  /** @type {SSRElement} */
153
203
  let root = this
@@ -802,7 +802,7 @@ export interface ChartContext {
802
802
  eventList: string[]
803
803
 
804
804
  // Public methods on the ApexCharts instance
805
- update(options?: any): Promise<any>
805
+ update(options?: any, overwriteInitialSeries?: boolean): Promise<any>
806
806
  getSyncedCharts(): any[]
807
807
  getGroupedCharts(): any[]
808
808
  fastUpdate(animate?: boolean, prevAxisScaleSig?: string): Promise<any>
@@ -75,15 +75,12 @@ class Utils {
75
75
  }
76
76
 
77
77
  // A per-series shallow copy: the series OBJECTS are copied, their data arrays
78
- // are shared. Almost every internal "mutation" of a series' data is a
79
- // property REPLACEMENT (`series[i].data = []` on legend collapse,
80
- // `series[i] = 0` for non-axis charts), and those cannot reach a copy made
81
- // this way because the series object was copied at capture time. The
82
- // exception is appendData(), whose push loop grows the shared data array in
83
- // place, so a copy taken before it does see the appended points; see the
84
- // note above defineLazyInitialSeries(). This is the same cheap shape
85
- // `globals.initialSeries` captures, so snapshotting a config stays O(n)
86
- // instead of deep-cloning every point.
78
+ // are shared. Every internal "mutation" of a series' data is a property
79
+ // REPLACEMENT (`series[i].data = []` on legend collapse, `series[i] = 0` for
80
+ // non-axis charts, appendData's concat), and those cannot reach a copy made
81
+ // this way because the series object was copied at capture time. This is the
82
+ // same cheap shape `globals.initialSeries` captures, so snapshotting a config
83
+ // stays O(n) instead of deep-cloning every point.
87
84
  /**
88
85
  * @param {any} series
89
86
  */
@@ -850,6 +850,81 @@ interface ApexPluginAPI {
850
850
  */
851
851
  emit(name: string, detail?: any): void
852
852
  readonly el: Element
853
+ /**
854
+ * What this host supports, for a plugin that has to run against older ones
855
+ * too. A plugin targeting several hosts declares the lowest `apiVersion` it
856
+ * can run on (a newer one is skipped outright) and asks this for the rest.
857
+ *
858
+ * @since Weave v6
859
+ */
860
+ can(name: string): boolean
861
+ /** The same list, for logging and support. Frozen. @since Weave v6 */
862
+ readonly capabilities: readonly string[]
863
+ /**
864
+ * Set a positional option for your own series without writing the caller's
865
+ * config.
866
+ *
867
+ * Options like `stroke.dashArray` are indexed by series position with no
868
+ * per-series alternative, so setting one for a single series has meant
869
+ * writing the whole array and restoring the caller's afterwards. A claim is
870
+ * resolved where the option is READ instead: nothing is written, releasing is
871
+ * a deletion rather than a restore, and a caller's own `updateOptions`
872
+ * composes with the claim instead of being reverted by it.
873
+ *
874
+ * Name the series rather than its position where you can: the name is
875
+ * resolved each time the option is read, so the claim follows that series
876
+ * when others are added, removed or reordered.
877
+ *
878
+ * Returns null for an option that is not claimable, so a plugin written
879
+ * against a newer host degrades instead of throwing. Every claim is released
880
+ * on teardown, on destroy, and if the host disables the plugin.
881
+ *
882
+ * @since Weave v6
883
+ */
884
+ claim(
885
+ option: 'stroke.dashArray' | 'dataLabels.enabledOnSeries' | (string & {}),
886
+ entries: Array<{ series: string | number; value: any }>
887
+ ): ApexPluginClaim | null
888
+ /**
889
+ * Everything drawn on this chart, including other features' output: the
890
+ * series, the caller's annotations (ink strokes among them), and whatever
891
+ * plugins have declared.
892
+ *
893
+ * Read only. Every entry names its `owner`, because the list is only as
894
+ * complete as the features that opted in, and a reader should be able to say
895
+ * what it covers rather than assume it is exhaustive.
896
+ *
897
+ * @since Weave v6
898
+ */
899
+ drawn(): ReadonlyArray<ApexDrawnItem>
900
+ /**
901
+ * Say what this plugin has drawn, so it appears in `drawn()`. Declare from
902
+ * your draw handler: declarations are cleared with the layers at the start of
903
+ * every draw, so an inventory cannot outlive what it describes.
904
+ *
905
+ * @since Weave v6
906
+ */
907
+ declare(item: { id: string; label?: string; visible?: boolean }): ApexPluginAPI
908
+ }
909
+
910
+ /** One thing drawn on a chart. @since Weave v6 */
911
+ interface ApexDrawnItem {
912
+ /** Stable within this chart, opaque to readers. */
913
+ readonly id: string
914
+ readonly kind: 'series' | 'annotation' | 'overlay'
915
+ /** A human name where one exists. */
916
+ readonly label: string
917
+ /** 'core', or the plugin that put it there. */
918
+ readonly owner: string
919
+ readonly visible: boolean
920
+ }
921
+
922
+ /** A live claim over one positional option. @since Weave v6 */
923
+ interface ApexPluginClaim {
924
+ /** Drop the claim. Idempotent. */
925
+ release(): void
926
+ /** Replace this claim's entries, keeping its place in the resolution order. */
927
+ update(entries: Array<{ series: string | number; value: any }>): void
853
928
  }
854
929
 
855
930
  interface ApexPlugin {
@@ -1878,6 +1953,17 @@ type ApexChart = {
1878
1953
  * @default 'auto'
1879
1954
  */
1880
1955
  pinch?: boolean | 'auto'
1956
+ /**
1957
+ * The way back out of a zoom on a chart whose toolbar is hidden. While the
1958
+ * chart is zoomed and nothing else on screen can reset it, one reset
1959
+ * control is drawn where the toolbar would have been, and it goes when the
1960
+ * range does; `Escape` resets the zoom as well. `'auto'` supplies both only
1961
+ * where there is no reset button already, `true` also forces the control on
1962
+ * where `toolbar.tools.reset` is off, and `false` turns both off for a page
1963
+ * that supplies its own reset. Requires `enabled: true`.
1964
+ * @default 'auto'
1965
+ */
1966
+ resetControl?: boolean | 'auto'
1881
1967
  zoomedArea?: {
1882
1968
  fill?: {
1883
1969
  color?: string
@@ -2656,6 +2742,12 @@ type ApexPlotOptions = {
2656
2742
  distributed?: boolean
2657
2743
  borderRadius?: number;
2658
2744
  borderRadiusApplication?: 'around' | 'end';
2745
+ /**
2746
+ * Which segments of a stack round. 'all' (default) caps both ends of the
2747
+ * stack, the baseline and the far end; 'last' caps only the far end so
2748
+ * the stack sits square on the baseline. Stacked bar/column only.
2749
+ */
2750
+ borderRadiusWhenStacked?: 'all' | 'last';
2659
2751
  hideZeroBarsWhenGrouped?: boolean
2660
2752
  rangeBarOverlap?: boolean
2661
2753
  rangeBarGroupRows?: boolean