apexcharts 7.7.0 → 7.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{LICENSE → LICENSE.md} +1 -1
- package/README.md +1 -0
- package/dist/apexcharts.common.js +2 -2
- package/dist/apexcharts.core.js +4414 -722
- package/dist/apexcharts.core.min.js +2 -2
- package/dist/apexcharts.css +22 -5
- package/dist/apexcharts.esm.js +2141 -598
- package/dist/apexcharts.js +6568 -1263
- package/dist/apexcharts.min.js +2 -2
- package/dist/apexcharts.ssr.common.js +2 -2
- package/dist/apexcharts.ssr.esm.js +6567 -1262
- package/dist/area.common.js +2 -2
- package/dist/area.esm.js +297 -194
- package/dist/bar.common.js +2 -2
- package/dist/bar.esm.js +357 -143
- package/dist/bar.js +2 -2
- package/dist/boxPlot.common.js +2 -2
- package/dist/boxPlot.esm.js +417 -152
- package/dist/bubble.common.js +2 -2
- package/dist/bubble.esm.js +297 -194
- package/dist/candlestick.common.js +2 -2
- package/dist/candlestick.esm.js +417 -152
- package/dist/candlestick.js +2 -2
- package/dist/column.common.js +2 -2
- package/dist/column.esm.js +357 -143
- package/dist/core.common.js +2 -2
- package/dist/core.esm.js +4414 -722
- package/dist/donut.common.js +2 -2
- package/dist/donut.esm.js +49 -17
- package/dist/dumbbell.common.js +2 -2
- package/dist/dumbbell.esm.js +357 -143
- package/dist/features/all.common.js +2 -2
- package/dist/features/all.esm.js +647 -133
- package/dist/features/annotations.common.js +2 -2
- package/dist/features/annotations.esm.js +2 -2
- package/dist/features/annotations.js +2 -2
- package/dist/features/context-menu.common.js +2 -2
- package/dist/features/context-menu.esm.js +17 -8
- package/dist/features/context-menu.js +2 -2
- package/dist/features/drilldown.common.js +2 -2
- package/dist/features/drilldown.esm.js +3 -1
- package/dist/features/drilldown.js +2 -2
- package/dist/features/dumbbell.common.js +1 -1
- package/dist/features/dumbbell.esm.js +1 -1
- package/dist/features/dumbbell.js +1 -1
- package/dist/features/exports.common.js +2 -2
- package/dist/features/exports.esm.js +12 -6
- package/dist/features/exports.js +2 -2
- package/dist/features/facet.common.js +1 -1
- package/dist/features/facet.esm.js +1 -1
- package/dist/features/facet.js +1 -1
- package/dist/features/highlight-filter.common.js +5 -0
- package/dist/features/highlight-filter.esm.js +4485 -0
- package/dist/features/highlight-filter.js +5 -0
- package/dist/features/history.common.js +1 -1
- package/dist/features/history.esm.js +1 -1
- package/dist/features/history.js +1 -1
- package/dist/features/ink.common.js +2 -2
- package/dist/features/ink.esm.js +6 -4
- package/dist/features/ink.js +2 -2
- package/dist/features/keyboard.common.js +2 -2
- package/dist/features/keyboard.esm.js +482 -54
- package/dist/features/keyboard.js +2 -2
- package/dist/features/legend.common.js +2 -2
- package/dist/features/legend.esm.js +24 -8
- package/dist/features/legend.js +2 -2
- package/dist/features/link.common.js +1 -1
- package/dist/features/link.esm.js +1 -1
- package/dist/features/link.js +1 -1
- package/dist/features/marks.common.js +1 -1
- package/dist/features/marks.esm.js +1 -1
- package/dist/features/marks.js +1 -1
- package/dist/features/measure.common.js +2 -2
- package/dist/features/measure.esm.js +17 -7
- package/dist/features/measure.js +2 -2
- package/dist/features/morph.common.js +2 -2
- package/dist/features/morph.esm.js +74 -25
- package/dist/features/morph.js +2 -2
- package/dist/features/perspectives.common.js +2 -2
- package/dist/features/perspectives.esm.js +3 -1
- package/dist/features/perspectives.js +2 -2
- package/dist/features/raincloud.common.js +1 -1
- package/dist/features/raincloud.esm.js +1 -1
- package/dist/features/raincloud.js +1 -1
- package/dist/features/renderer-canvas.common.js +2 -2
- package/dist/features/renderer-canvas.esm.js +464 -28
- package/dist/features/renderer-canvas.js +2 -2
- package/dist/features/stats.common.js +1 -1
- package/dist/features/stats.esm.js +1 -1
- package/dist/features/stats.js +1 -1
- package/dist/features/storyboard.common.js +2 -2
- package/dist/features/storyboard.esm.js +3 -1
- package/dist/features/storyboard.js +2 -2
- package/dist/features/streamgraph.common.js +1 -1
- package/dist/features/streamgraph.esm.js +1 -1
- package/dist/features/streamgraph.js +1 -1
- package/dist/features/toolbar.common.js +2 -2
- package/dist/features/toolbar.esm.js +69 -48
- package/dist/features/toolbar.js +2 -2
- package/dist/features/trellis.common.js +2 -2
- package/dist/features/trellis.esm.js +131 -46
- package/dist/features/trellis.js +2 -2
- package/dist/features/waterfall.common.js +1 -1
- package/dist/features/waterfall.esm.js +1 -1
- package/dist/features/waterfall.js +1 -1
- package/dist/features/weave.common.js +1 -1
- package/dist/features/weave.esm.js +1 -1
- package/dist/features/weave.js +1 -1
- package/dist/heatmap.common.js +2 -2
- package/dist/heatmap.esm.js +74 -19
- package/dist/heatmap.js +2 -2
- package/dist/histogram.common.js +2 -2
- package/dist/histogram.esm.js +357 -143
- package/dist/icicle.common.js +1 -1
- package/dist/icicle.esm.js +1 -1
- package/dist/icicle.js +1 -1
- package/dist/line.common.js +2 -2
- package/dist/line.esm.js +297 -194
- package/dist/line.js +2 -2
- package/dist/locales/eu.json +56 -0
- package/dist/locales/gl.json +4 -3
- package/dist/pictograms.common.js +1 -1
- package/dist/pictograms.esm.js +1 -1
- package/dist/pictograms.js +1 -1
- package/dist/pie.common.js +2 -2
- package/dist/pie.esm.js +49 -17
- package/dist/pie.js +2 -2
- package/dist/polarArea.common.js +2 -2
- package/dist/polarArea.esm.js +49 -17
- package/dist/radar.common.js +2 -2
- package/dist/radar.esm.js +30 -4
- package/dist/radar.js +2 -2
- package/dist/radialBar.common.js +2 -2
- package/dist/radialBar.esm.js +149 -60
- package/dist/radialBar.js +2 -2
- package/dist/raincloud.common.js +2 -2
- package/dist/raincloud.esm.js +325 -143
- package/dist/rangeArea.common.js +2 -2
- package/dist/rangeArea.esm.js +297 -194
- package/dist/rangeBar.common.js +2 -2
- package/dist/rangeBar.esm.js +357 -143
- package/dist/scatter.common.js +2 -2
- package/dist/scatter.esm.js +297 -194
- package/dist/streamgraph.common.js +2 -2
- package/dist/streamgraph.esm.js +297 -194
- package/dist/sunburst.common.js +1 -1
- package/dist/sunburst.esm.js +1 -1
- package/dist/sunburst.js +1 -1
- package/dist/treemap.common.js +2 -2
- package/dist/treemap.esm.js +522 -40
- package/dist/treemap.js +2 -2
- package/dist/unit-shapes.common.js +1 -1
- package/dist/unit-shapes.esm.js +1 -1
- package/dist/unit-shapes.js +1 -1
- package/dist/unit.common.js +1 -1
- package/dist/unit.esm.js +1 -1
- package/dist/unit.js +1 -1
- package/dist/violin.common.js +2 -2
- package/dist/violin.esm.js +325 -143
- package/dist/violin.js +2 -2
- package/dist/waterfall.common.js +2 -2
- package/dist/waterfall.esm.js +357 -143
- package/package.json +7 -9
- package/src/apexcharts.js +43 -3
- package/src/assets/apexcharts.css +22 -5
- package/src/charts/Bar.js +268 -116
- package/src/charts/BarStacked.js +30 -5
- package/src/charts/BoxCandleStick.js +155 -16
- package/src/charts/HeatMap.js +77 -24
- package/src/charts/Line.js +303 -148
- package/src/charts/Pie.js +66 -15
- package/src/charts/Radar.js +70 -2
- package/src/charts/Radial.js +151 -82
- package/src/charts/RangeBar.js +24 -0
- package/src/charts/Treemap.js +676 -39
- package/src/charts/Violin.js +26 -0
- package/src/charts/common/bar/DataLabels.js +39 -16
- package/src/charts/common/bar/Helpers.js +134 -112
- package/src/charts/common/line/Helpers.js +27 -0
- package/src/entries/core.js +7 -0
- package/src/features/highlight-filter.js +27 -0
- package/src/locales/eu.json +56 -0
- package/src/locales/gl.json +4 -3
- package/src/modules/Animations.js +86 -6
- package/src/modules/Base.js +7 -0
- package/src/modules/Core.js +39 -42
- package/src/modules/CoreUtils.js +15 -7
- package/src/modules/Data.js +132 -17
- package/src/modules/DataLabels.js +289 -31
- package/src/modules/Exports.js +18 -6
- package/src/modules/Graphics.js +44 -23
- package/src/modules/Markers.js +11 -2
- package/src/modules/MorphTypeChange.js +89 -32
- package/src/modules/Range.js +62 -28
- package/src/modules/RendererController.js +36 -10
- package/src/modules/Series.js +341 -9
- package/src/modules/Theme.js +4 -10
- package/src/modules/ZoomPanSelection.js +68 -51
- package/src/modules/accessibility/KeyboardNavigation.js +674 -99
- package/src/modules/animations/AxisTransition.js +100 -9
- package/src/modules/animations/CircleTransition.js +479 -0
- package/src/modules/animations/DataLabelTransition.js +275 -2
- package/src/modules/animations/LayoutTransition.js +472 -0
- package/src/modules/animations/LengthTransition.js +122 -0
- package/src/modules/annotations/Annotations.js +10 -1
- package/src/modules/axes/Grid.js +68 -43
- package/src/modules/contextMenu/ContextMenu.js +23 -7
- package/src/modules/drilldown/Drilldown.js +3 -0
- package/src/modules/helpers/DataLabelOverlap.js +222 -0
- package/src/modules/helpers/Destroy.js +2 -0
- package/src/modules/helpers/InitCtxVariables.js +15 -0
- package/src/modules/helpers/UpdateHelpers.js +95 -3
- package/src/modules/highlightFilter/HighlightFilter.js +2858 -0
- package/src/modules/highlightFilter/PiePart.js +653 -0
- package/src/modules/highlightFilter/RadialPart.js +1337 -0
- package/src/modules/highlightFilter/TreemapPart.js +499 -0
- package/src/modules/highlightFilter/util.js +306 -0
- package/src/modules/ink/InkLayer.js +15 -3
- package/src/modules/legend/HeatmapGradientLegend.js +26 -7
- package/src/modules/legend/Helpers.js +14 -1
- package/src/modules/license/LicenseEnforcer.js +8 -5
- package/src/modules/measure/Measure.js +17 -7
- package/src/modules/settings/Config.js +322 -2
- package/src/modules/settings/Defaults.js +48 -49
- package/src/modules/settings/Globals.js +49 -1
- package/src/modules/settings/Options.js +44 -0
- package/src/modules/settings/TypeAliases.js +83 -0
- package/src/modules/tooltip/AxesTooltip.js +5 -1
- package/src/modules/tooltip/Intersect.js +663 -197
- package/src/modules/tooltip/Labels.js +89 -28
- package/src/modules/tooltip/Marker.js +209 -20
- package/src/modules/tooltip/Position.js +829 -138
- package/src/modules/tooltip/Tooltip.js +493 -73
- package/src/modules/tooltip/Utils.js +173 -3
- package/src/modules/tooltip/constants.js +20 -0
- package/src/modules/tooltip/placement.js +569 -0
- package/src/modules/trellis/Trellis.js +14 -2
- package/src/modules/trellis/TrellisScales.js +58 -17
- package/src/modules/trellis/TrellisSplit.js +19 -3
- package/src/modules/trellis/TrellisSync.js +7 -1
- package/src/modules/trellis/TrellisTooltip.js +27 -15
- package/src/renderers/Renderer.js +2 -0
- package/src/renderers/canvas/CanvasCompositor.js +113 -17
- package/src/renderers/canvas/CanvasGraphics.js +173 -3
- package/src/renderers/canvas/CanvasRenderer.js +275 -14
- package/src/types/internal.d.ts +132 -2
- package/src/utils/Utils.js +61 -0
- package/types/apexcharts.d.ts +365 -16
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/**
|
|
3
|
+
* Highlight filter on treemaps. A tile keeps its box, faded; its part is the
|
|
4
|
+
* bottom share of the tile, solid, as tall as the part's share of the tile's
|
|
5
|
+
* value. No element is added: the part is drawn inside the tile's own fill, a
|
|
6
|
+
* vertical gradient with a hard stop (solid below the share, faded above),
|
|
7
|
+
* in the tile's own box units. So everything that moves or hit-tests a tile
|
|
8
|
+
* (its tweens, a zoom's camera and ghosts, the pointer, a cross-type morph)
|
|
9
|
+
* carries the part with no work here, and the tile is hovered wherever the
|
|
10
|
+
* pointer is on it, solid or faded.
|
|
11
|
+
*
|
|
12
|
+
* Treemap reaches this through one dispatch per render, after the layout,
|
|
13
|
+
* which returns the painter each tile hands its fill to. Nested parents are
|
|
14
|
+
* frames: they never fade, and know their leaves' parts through
|
|
15
|
+
* `node.highlight` (header and tooltip formatters already receive the node).
|
|
16
|
+
*
|
|
17
|
+
* @module modules/highlightFilter/TreemapPart
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import Graphics from '../Graphics'
|
|
21
|
+
import { finite, mix, info, isPoint, stripSeries } from './util'
|
|
22
|
+
|
|
23
|
+
const TILE = 't:'
|
|
24
|
+
const CLASS = 'apexcharts-highlight-tile'
|
|
25
|
+
const OVER = 'apexcharts-highlight-overflow'
|
|
26
|
+
// A fill with no colour to cut a share from: a gradient, pattern or image.
|
|
27
|
+
const URL = /^url\(/i
|
|
28
|
+
|
|
29
|
+
/** @typedef {{s: number, f: number, c: string}} TileLook */
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse side, before the parts are read: whether this is a nested treemap
|
|
33
|
+
* (its parts sit on its leaves), warning once about parts that cannot be
|
|
34
|
+
* placed there.
|
|
35
|
+
* @param {any} hf @param {string} type
|
|
36
|
+
*/
|
|
37
|
+
export function treeParse(hf, type) {
|
|
38
|
+
const gl = hf.w.globals
|
|
39
|
+
if (type !== 'treemap' || !gl.treemapRoots) return false
|
|
40
|
+
const raw = /** @type {any[]} */ (gl.treemapRawSeries || [])
|
|
41
|
+
if (raw.some((s) => Array.isArray(s?.highlightData))) {
|
|
42
|
+
hf._warn(
|
|
43
|
+
'nested',
|
|
44
|
+
'on a nested treemap put the part on each leaf as { x, y, highlight }; highlightData is ignored.',
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
/** @param {any} d @returns {boolean} */
|
|
48
|
+
const onParent = (d) =>
|
|
49
|
+
isPoint(d) &&
|
|
50
|
+
Array.isArray(d.children) &&
|
|
51
|
+
d.children.length > 0 &&
|
|
52
|
+
('highlight' in d || d.children.some(onParent))
|
|
53
|
+
if (raw.some((s) => (s?.data || []).some(onParent))) {
|
|
54
|
+
hf._warn(
|
|
55
|
+
'parent',
|
|
56
|
+
"a parent's highlight is ignored: parents add up their leaves' parts.",
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
return true
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* set() on a nested treemap: the series as handed in with each leaf's part
|
|
64
|
+
* on the leaf itself, by its index among its series' leaves (depth first,
|
|
65
|
+
* as the chart numbers them). `parts` is one row per series, or a function
|
|
66
|
+
* of the leaf.
|
|
67
|
+
* @param {any} hf @param {any[]} base the raw nested series
|
|
68
|
+
* @param {any} parts
|
|
69
|
+
*/
|
|
70
|
+
export function treeSeries(hf, base, parts) {
|
|
71
|
+
const w = hf.w
|
|
72
|
+
return base.map((s, i) => {
|
|
73
|
+
const out = stripSeries(s)
|
|
74
|
+
const row = Array.isArray(parts?.[i]) ? parts[i] : null
|
|
75
|
+
if (typeof parts !== 'function' && !row) return out
|
|
76
|
+
let k = 0
|
|
77
|
+
/** @param {any} d @returns {any} */
|
|
78
|
+
const leaf = (d) => {
|
|
79
|
+
if (isPoint(d) && Array.isArray(d.children) && d.children.length) {
|
|
80
|
+
return { ...d, children: d.children.map(leaf) }
|
|
81
|
+
}
|
|
82
|
+
const j = k++
|
|
83
|
+
const pt = isPoint(d) ? { ...d } : { x: '', y: d }
|
|
84
|
+
pt.highlight =
|
|
85
|
+
typeof parts === 'function'
|
|
86
|
+
? parts({
|
|
87
|
+
seriesIndex: i,
|
|
88
|
+
dataPointIndex: j,
|
|
89
|
+
seriesName: s.name,
|
|
90
|
+
x: pt.x ?? pt.name,
|
|
91
|
+
value:
|
|
92
|
+
typeof (pt.y ?? pt.value) === 'number'
|
|
93
|
+
? (pt.y ?? pt.value)
|
|
94
|
+
: null,
|
|
95
|
+
datum: d,
|
|
96
|
+
w,
|
|
97
|
+
})
|
|
98
|
+
: row?.[j]
|
|
99
|
+
return pt
|
|
100
|
+
}
|
|
101
|
+
if (Array.isArray(out.data)) out.data = out.data.map(leaf)
|
|
102
|
+
return out
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Treemap.draw, once, after the layout and the zoom camera, before any
|
|
108
|
+
* parent or tile is drawn. Returns this render's tile painter, or undefined
|
|
109
|
+
* when nothing is highlighted now or was a frame ago.
|
|
110
|
+
* @param {any} hf the HighlightFilter
|
|
111
|
+
* @param {any} tm the Treemap renderer
|
|
112
|
+
*/
|
|
113
|
+
export function treemapPass(hf, tm) {
|
|
114
|
+
const w = hf.w
|
|
115
|
+
const gl = w.globals
|
|
116
|
+
const hd = w.highlightData
|
|
117
|
+
if (!hd) return undefined
|
|
118
|
+
hf._beginRender(tm, TILE)
|
|
119
|
+
// An update on the fast path keeps <defs>: the last render's gradients go.
|
|
120
|
+
hf._tmDefs.forEach((/** @type {any} */ n) => n.parentNode?.removeChild(n))
|
|
121
|
+
hf._tmDefs = []
|
|
122
|
+
const tiles = (hf._tiles = new Map())
|
|
123
|
+
const active = hd.active
|
|
124
|
+
const vals = w.seriesData.series
|
|
125
|
+
// Every render, highlighted or not: a nested tree outlives a resize or a
|
|
126
|
+
// zoom, so a roll-up from an earlier pick would stay on it.
|
|
127
|
+
rollUp(tm, active ? hd.parts : null, vals)
|
|
128
|
+
const prevMap = hf._prev()
|
|
129
|
+
if (!active && !prevMap?.size) return undefined
|
|
130
|
+
// The same tiles by series and index: a series hidden from the legend lays
|
|
131
|
+
// out as one tile of nothing under a key of its own, which closes from the
|
|
132
|
+
// tile that stood first in that series.
|
|
133
|
+
/** @type {Map<string, any>} */
|
|
134
|
+
const byIJ = new Map()
|
|
135
|
+
prevMap?.forEach((/** @type {any} */ r) => byIJ.set(r.ij, r))
|
|
136
|
+
|
|
137
|
+
// A zoom's ghosts are what the last render drew: no longer tiles.
|
|
138
|
+
gl.prevTreemapView
|
|
139
|
+
?.querySelectorAll(`.${CLASS}`)
|
|
140
|
+
.forEach((/** @type {any} */ n) => {
|
|
141
|
+
n.classList.remove(CLASS, OVER)
|
|
142
|
+
n.removeAttribute('data:hl-share')
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
const cfg = hf.cfg
|
|
146
|
+
const fadeTo = active ? (cfg.fadeOpacity ?? 0.2) : 1
|
|
147
|
+
const morph = hf.ctx.morphTypeChange?.isActive() === true
|
|
148
|
+
const dyn = tm.dynamicAnim
|
|
149
|
+
// The renderer's own rule for easing a tile (or a cross-type morph, whose
|
|
150
|
+
// tiles unroll on the morph's clock); the renderer kind plays no part,
|
|
151
|
+
// treemaps are always drawn in SVG.
|
|
152
|
+
const anim = morph || !!(gl.dataChanged && dyn.enabled && gl.shouldAnimate)
|
|
153
|
+
const speed = morph ? hf.ctx.morphTypeChange.getSpeed() : dyn.speed
|
|
154
|
+
const fore = w.config.chart.foreColor
|
|
155
|
+
let warned = false
|
|
156
|
+
|
|
157
|
+
if (active) wrapParentTip(hf, tm)
|
|
158
|
+
// A tile's label is drawn after its tile: it takes its tone from the share
|
|
159
|
+
// drawn under it from the start, and follows it as it moves. It reads as
|
|
160
|
+
// on the solid share once the share's edge has passed the top of its text
|
|
161
|
+
// (a label turned to fit, the top of its run).
|
|
162
|
+
/** @type {any} */
|
|
163
|
+
let bound = null
|
|
164
|
+
const cl = tm.helpers.calculateDataLabels
|
|
165
|
+
tm.helpers.calculateDataLabels = (/** @type {any} */ o) => {
|
|
166
|
+
const g = cl.call(tm.helpers, o)
|
|
167
|
+
const rec = (bound = tiles.get(o.i + '|' + o.j))
|
|
168
|
+
if (g && rec) {
|
|
169
|
+
const ts = texts(g.node)
|
|
170
|
+
rec.lb = ts.filter((t) =>
|
|
171
|
+
/^(#fff|#ffffff|white)$/i.test(t.getAttribute('fill') || ''),
|
|
172
|
+
)
|
|
173
|
+
const t = ts[0]
|
|
174
|
+
const [, y1, , y2] = rec.r
|
|
175
|
+
if (t && y2 > y1) {
|
|
176
|
+
const fs = parseFloat(t.getAttribute('font-size')) || 12
|
|
177
|
+
rec.need = (y2 - parseFloat(t.getAttribute('y')) + 0.9 * fs) / (y2 - y1)
|
|
178
|
+
}
|
|
179
|
+
tone(rec, fore)
|
|
180
|
+
}
|
|
181
|
+
return g
|
|
182
|
+
}
|
|
183
|
+
const rf = tm.rotateToFitLabel
|
|
184
|
+
tm.rotateToFitLabel = (/** @type {any[]} */ ...a) => {
|
|
185
|
+
rf.apply(tm, a)
|
|
186
|
+
const rec = bound
|
|
187
|
+
bound = null
|
|
188
|
+
if (rec?.lb && /rotate/.test(a[0].node.getAttribute('transform') || '')) {
|
|
189
|
+
const run = new Graphics(w).getTextRects(a[2], String(a[1])).width
|
|
190
|
+
rec.need = 0.5 + run / 2 / (a[6] - a[4])
|
|
191
|
+
rec.tone = null
|
|
192
|
+
tone(rec, fore)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @param {any} el the tile, not yet given its fill
|
|
198
|
+
* @param {number} i @param {number} j
|
|
199
|
+
* @param {string} pathFill its fill as the renderer worked it out
|
|
200
|
+
* @param {any} leaf its layout node
|
|
201
|
+
*/
|
|
202
|
+
return (el, i, j, pathFill, leaf) => {
|
|
203
|
+
// A render that is no data change (a mount, a resize) draws each tile
|
|
204
|
+
// where it rests, whatever an older render left; so does an update
|
|
205
|
+
// sent without animation, which captured nothing.
|
|
206
|
+
const key = TILE + leaf._key
|
|
207
|
+
const ij = i + '|' + j
|
|
208
|
+
const whole = vals[i]?.[j]
|
|
209
|
+
const prev = anim
|
|
210
|
+
? prevMap?.get(key) || (whole ? undefined : byIJ.get(ij))
|
|
211
|
+
: undefined
|
|
212
|
+
if (!active && !prev) return undefined
|
|
213
|
+
if (URL.test(pathFill)) {
|
|
214
|
+
if (active && !warned) {
|
|
215
|
+
warned = true
|
|
216
|
+
hf._warn(
|
|
217
|
+
'fill',
|
|
218
|
+
'treemap tiles with a gradient, pattern or image fill are drawn without their parts.',
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
return undefined
|
|
222
|
+
}
|
|
223
|
+
const p = active ? hd.parts[i]?.[j] : null
|
|
224
|
+
// A tile of nothing has no share to draw.
|
|
225
|
+
if (!whole && !prev) return undefined
|
|
226
|
+
const share = finite(p) && whole ? p / whole : null
|
|
227
|
+
|
|
228
|
+
/** @type {TileLook} */
|
|
229
|
+
let to = active
|
|
230
|
+
? {
|
|
231
|
+
s: share == null || share < 0 ? 0 : Math.min(share, 1),
|
|
232
|
+
f: fadeTo,
|
|
233
|
+
c: pathFill,
|
|
234
|
+
}
|
|
235
|
+
: // A clear grows the part back over its tile, the faded rest at 0
|
|
236
|
+
// under it, and lands on the plain tile.
|
|
237
|
+
{ s: 1, f: 0, c: pathFill }
|
|
238
|
+
/** @type {TileLook} */
|
|
239
|
+
let from = to
|
|
240
|
+
if (prev) {
|
|
241
|
+
from = { s: prev.s, f: prev.f, c: prev.col }
|
|
242
|
+
// A tile with nothing left (a series hidden from the legend) keeps
|
|
243
|
+
// the look it has while it closes.
|
|
244
|
+
if (!whole && active) to = { ...from }
|
|
245
|
+
} else if (anim) {
|
|
246
|
+
const was = /** @type {any} */ (gl.previousPaths[i])?.[j]
|
|
247
|
+
// A tile on screen until now, plain: the pick starts as that tile
|
|
248
|
+
// (the part covers it, the faded rest at 0), or rises from its base.
|
|
249
|
+
if (morph || was?.rect?.width > 0) {
|
|
250
|
+
from =
|
|
251
|
+
cfg.enter === 'baseline'
|
|
252
|
+
? { s: 0, f: 1, c: to.c }
|
|
253
|
+
: { s: 1, f: 0, c: to.c }
|
|
254
|
+
if (!morph && was.fill && !URL.test(was.fill)) from.c = was.fill
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const moves = from.s !== to.s || from.f !== to.f || from.c !== to.c
|
|
258
|
+
// A clear with nothing to move is the plain tile.
|
|
259
|
+
if (!active && !moves) return undefined
|
|
260
|
+
|
|
261
|
+
const g = w.dom.Paper.gradient('linear', (/** @type {any} */ add) => {
|
|
262
|
+
for (let k = 0; k < 4; k++) add.stop(0, pathFill, 1)
|
|
263
|
+
})
|
|
264
|
+
// Bottom to top, in the tile's own box: it rides every resize of it.
|
|
265
|
+
g.from(0, 1).to(0, 0)
|
|
266
|
+
hf._tmDefs.push(g.node)
|
|
267
|
+
const stops = Array.from(g.node.childNodes)
|
|
268
|
+
const node = el.node
|
|
269
|
+
if (active) {
|
|
270
|
+
node.setAttribute(
|
|
271
|
+
'class',
|
|
272
|
+
`${node.getAttribute('class') || ''} ${CLASS}${share != null && share > 1 ? ' ' + OVER : ''}`.trim(),
|
|
273
|
+
)
|
|
274
|
+
node.setAttribute('data:hl-share', share == null ? '' : String(share))
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** What this tile draws, for the next update to start from. */
|
|
278
|
+
/** @type {any} */
|
|
279
|
+
const rec = {
|
|
280
|
+
s: to.s,
|
|
281
|
+
f: to.f,
|
|
282
|
+
col: to.c,
|
|
283
|
+
c: !active,
|
|
284
|
+
ij,
|
|
285
|
+
run: false,
|
|
286
|
+
base: pathFill,
|
|
287
|
+
r: leaf.rect,
|
|
288
|
+
lb: null,
|
|
289
|
+
need: 0.5,
|
|
290
|
+
tone: null,
|
|
291
|
+
}
|
|
292
|
+
hf._targets.set(key, rec)
|
|
293
|
+
tiles.set(ij, rec)
|
|
294
|
+
const ease = colourEase(from.c, to.c)
|
|
295
|
+
/** @param {TileLook} L */
|
|
296
|
+
const write = (L) => {
|
|
297
|
+
rec.ls = L.s
|
|
298
|
+
rec.lf = L.f
|
|
299
|
+
rec.lc = L.c
|
|
300
|
+
stops.forEach((/** @type {any} */ n, /** @type {number} */ k) => {
|
|
301
|
+
n.setAttribute('offset', String(k === 0 ? 0 : k === 3 ? 1 : L.s))
|
|
302
|
+
n.setAttribute('stop-color', L.c)
|
|
303
|
+
n.setAttribute('stop-opacity', String(k < 2 ? 1 : L.f))
|
|
304
|
+
})
|
|
305
|
+
tone(rec, fore)
|
|
306
|
+
}
|
|
307
|
+
const land = () => {
|
|
308
|
+
rec.run = false
|
|
309
|
+
write(to)
|
|
310
|
+
if (!active && node.isConnected) {
|
|
311
|
+
// The plain tile: its own fill, and the gradient gone.
|
|
312
|
+
node.setAttribute('fill', pathFill)
|
|
313
|
+
g.node.parentNode?.removeChild(g.node)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (anim && moves) {
|
|
317
|
+
rec.run = true
|
|
318
|
+
write(from)
|
|
319
|
+
// An export lands the fade only: the share is geometry and moves on
|
|
320
|
+
// with its tile. A clear has nothing else to show, so it lands.
|
|
321
|
+
/** @type {number | null} */
|
|
322
|
+
let f = null
|
|
323
|
+
let done = false
|
|
324
|
+
const job = {
|
|
325
|
+
finish() {
|
|
326
|
+
hf._live.delete(job)
|
|
327
|
+
if (active) write({ s: rec.ls, f: (f = to.f), c: rec.lc })
|
|
328
|
+
else {
|
|
329
|
+
done = true
|
|
330
|
+
land()
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
}
|
|
334
|
+
hf._hold(job)
|
|
335
|
+
// On a runner of its own, started in the same flush as the tile's and
|
|
336
|
+
// on the same curve, so the share and the box move as one.
|
|
337
|
+
g.animate(speed, 0)
|
|
338
|
+
.during(
|
|
339
|
+
(/** @type {number} */ t) =>
|
|
340
|
+
done ||
|
|
341
|
+
write({
|
|
342
|
+
s: mix(from.s, to.s, t),
|
|
343
|
+
f: f ?? mix(from.f, to.f, t),
|
|
344
|
+
c: ease(t),
|
|
345
|
+
}),
|
|
346
|
+
)
|
|
347
|
+
.after(() => {
|
|
348
|
+
hf._live.delete(job)
|
|
349
|
+
done || land()
|
|
350
|
+
})
|
|
351
|
+
} else write(to)
|
|
352
|
+
return g.url()
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Each node's part: a leaf's own, and a parent's the sum of its leaves'
|
|
358
|
+
* (each as far as it agrees in sign with its leaf) against the parent's
|
|
359
|
+
* area, the layout's own sum of the leaves' sizes. Written on every node of
|
|
360
|
+
* the tree before anything is drawn, or cleared when nothing is highlighted.
|
|
361
|
+
* @param {any} tm @param {any[] | null} parts @param {any[]} vals
|
|
362
|
+
*/
|
|
363
|
+
function rollUp(tm, parts, vals) {
|
|
364
|
+
/** @param {any} n @returns {any} */
|
|
365
|
+
const walk = (n) => {
|
|
366
|
+
const kids = n.children
|
|
367
|
+
if (!kids || !kids.length) {
|
|
368
|
+
const p = parts?.[n._si]?.[n._di]
|
|
369
|
+
n.highlight = parts
|
|
370
|
+
? info(finite(p) ? p : null, vals[n._si]?.[n._di])
|
|
371
|
+
: undefined
|
|
372
|
+
return n.highlight
|
|
373
|
+
}
|
|
374
|
+
/** @type {number | null} */
|
|
375
|
+
let v = null
|
|
376
|
+
kids.forEach((/** @type {any} */ c) => {
|
|
377
|
+
const h = walk(c)
|
|
378
|
+
if (h && h.value != null && !(h.value * (h.total || 0) < 0)) {
|
|
379
|
+
v = (v || 0) + Math.abs(h.value)
|
|
380
|
+
}
|
|
381
|
+
})
|
|
382
|
+
if (!parts) return (n.highlight = undefined)
|
|
383
|
+
const total = tm._subtreeArea(n)
|
|
384
|
+
const share = v != null && total ? v / total : null
|
|
385
|
+
n.highlight = {
|
|
386
|
+
value: v,
|
|
387
|
+
total,
|
|
388
|
+
share,
|
|
389
|
+
overflow: share != null && share > 1,
|
|
390
|
+
}
|
|
391
|
+
return n.highlight
|
|
392
|
+
}
|
|
393
|
+
;(tm.roots || []).forEach(walk)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* A parent's default tooltip row states its leaves' parts against its total,
|
|
398
|
+
* as a tile's row does. A custom formatter is handed `node.highlight`.
|
|
399
|
+
* @param {any} hf @param {any} tm
|
|
400
|
+
*/
|
|
401
|
+
function wrapParentTip(hf, tm) {
|
|
402
|
+
const show = tm._showParentTooltip
|
|
403
|
+
tm._showParentTooltip = (/** @type {any} */ e, /** @type {any} */ node) => {
|
|
404
|
+
show.call(tm, e, node)
|
|
405
|
+
const h = node.highlight
|
|
406
|
+
const tt = hf.cfg.tooltip || {}
|
|
407
|
+
if (
|
|
408
|
+
h?.value == null ||
|
|
409
|
+
tt.show === false ||
|
|
410
|
+
typeof hf.w.config.plotOptions.treemap.parents?.tooltip?.formatter ===
|
|
411
|
+
'function'
|
|
412
|
+
) {
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
const v = tm._tip()?.querySelector('.apexcharts-tooltip-text-y-value')
|
|
416
|
+
if (!v) return
|
|
417
|
+
let rest = `/ ${tm._formatValue(h.total)}`
|
|
418
|
+
if (tt.share && h.total) rest += ` · ${Math.round(h.share * 100)}%`
|
|
419
|
+
v.innerHTML =
|
|
420
|
+
`${tm._formatValue(h.value)} ` +
|
|
421
|
+
`<span class="apexcharts-highlight-total" style="font-weight: 400; opacity: 0.6">${rest}</span>`
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* A white label reads on the solid share but not on the faded tint: until
|
|
427
|
+
* the share drawn under it reaches the top of its text, it takes the chart's
|
|
428
|
+
* text colour.
|
|
429
|
+
* @param {any} rec @param {string} fore
|
|
430
|
+
*/
|
|
431
|
+
function tone(rec, fore) {
|
|
432
|
+
const on = (rec.ls ?? rec.s) >= rec.need - 1e-9
|
|
433
|
+
if (!rec.lb || rec.tone === on) return
|
|
434
|
+
rec.tone = on
|
|
435
|
+
rec.lb.forEach((/** @type {any} */ t) => {
|
|
436
|
+
if (t._apxFill == null) t._apxFill = t.getAttribute('fill')
|
|
437
|
+
t.setAttribute('fill', on ? t._apxFill : fore)
|
|
438
|
+
})
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Every text element under a node (walked by hand: the server renderer's
|
|
443
|
+
* elements answer no selectors).
|
|
444
|
+
* @param {any} n
|
|
445
|
+
* @returns {any[]}
|
|
446
|
+
*/
|
|
447
|
+
function texts(n) {
|
|
448
|
+
/** @type {any[]} */
|
|
449
|
+
const out = []
|
|
450
|
+
;(function walk(/** @type {any} */ x) {
|
|
451
|
+
if (String(x.tagName || x.nodeName).toLowerCase() === 'text') out.push(x)
|
|
452
|
+
Array.from(x.childNodes || []).forEach(walk)
|
|
453
|
+
})(n)
|
|
454
|
+
return out
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* A colour from `a` to `b` at progress t. Colours it cannot read switch on
|
|
459
|
+
* landing.
|
|
460
|
+
* @param {string} a @param {string} b
|
|
461
|
+
* @returns {(t: number) => string}
|
|
462
|
+
*/
|
|
463
|
+
function colourEase(a, b) {
|
|
464
|
+
const x = rgba(a)
|
|
465
|
+
const y = rgba(b)
|
|
466
|
+
if (a === b || !x || !y) return (t) => (t >= 1 ? b : a)
|
|
467
|
+
return (t) =>
|
|
468
|
+
t >= 1
|
|
469
|
+
? b
|
|
470
|
+
: `rgba(${[0, 1, 2].map((k) => Math.round(mix(x[k], y[k], t))).join(',')},${+mix(x[3], y[3], t).toFixed(3)})`
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* `#rgb`, `#rrggbb` (with or without alpha) and `rgb()`/`rgba()` as
|
|
475
|
+
* [r, g, b, a], or null.
|
|
476
|
+
* @param {string} c
|
|
477
|
+
* @returns {number[] | null}
|
|
478
|
+
*/
|
|
479
|
+
function rgba(c) {
|
|
480
|
+
const s = String(c).trim()
|
|
481
|
+
let m = /^#([\da-f]+)$/i.exec(s)
|
|
482
|
+
if (m) {
|
|
483
|
+
let h = m[1]
|
|
484
|
+
if (h.length < 5) h = h.replace(/./g, '$&$&')
|
|
485
|
+
if (h.length !== 6 && h.length !== 8) return null
|
|
486
|
+
return [0, 2, 4, 6].map((k) =>
|
|
487
|
+
k < h.length ? parseInt(h.slice(k, k + 2), 16) / (k === 6 ? 255 : 1) : 1,
|
|
488
|
+
)
|
|
489
|
+
}
|
|
490
|
+
m = /^rgba?\(([^)]*)\)$/i.exec(s)
|
|
491
|
+
if (!m) return null
|
|
492
|
+
const v = m[1]
|
|
493
|
+
.split(/[\s,/]+/)
|
|
494
|
+
.filter(Boolean)
|
|
495
|
+
.map(parseFloat)
|
|
496
|
+
return v.length >= 3 && v.every(isFinite)
|
|
497
|
+
? [v[0], v[1], v[2], v[3] ?? 1]
|
|
498
|
+
: null
|
|
499
|
+
}
|