@c2n/chart 0.0.7 → 0.0.9
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/README.md +44 -14
- package/dist/area-chart.js +2 -2
- package/dist/bar-chart.js +5 -4
- package/dist/candlestick-chart.js +139 -0
- package/dist/{chart-base-C4-1TyKq.js → chart-base-CEZvMKwW.js} +106 -13
- package/dist/chart-base.js +1 -1
- package/dist/chart-legend-DJE3HO7C.js +49 -0
- package/dist/chart-legend.js +2 -0
- package/dist/chart-link-C9vN9Y6c.js +75 -0
- package/dist/chart-series.js +75 -1
- package/dist/chart-theme.js +4 -2
- package/dist/chart-tooltip-J-oC2DKZ.js +130 -0
- package/dist/chart-tooltip.js +2 -0
- package/dist/chart.js +11 -5
- package/dist/decorate-BkO8ArQg.js +9 -0
- package/dist/{echarts-chart-base-LogCQN10.js → echarts-chart-base-CImXMzVk.js} +8 -5
- package/dist/echarts-chart-base.js +1 -1
- package/dist/gauge-chart.js +111 -0
- package/dist/line-chart.js +4 -3
- package/dist/pie-chart.js +26 -3
- package/dist/radar-chart.js +109 -0
- package/dist/scatter-chart.js +40 -0
- package/dist/sparkline.js +4 -4
- package/dist/{uplot-chart-base-BWr0zJE2.js → uplot-chart-base-CLhgOeM_.js} +21 -5
- package/dist/uplot-chart-base.js +1 -1
- package/dist/{uplot-paths-ChKohvIX.js → uplot-paths-D5HCEKRV.js} +9 -4
- package/package.json +31 -3
- package/react.d.ts +12 -0
- package/types/src/candlestick-chart.d.ts +52 -0
- package/types/src/candlestick-chart.d.ts.map +1 -0
- package/types/src/chart-base.d.ts +41 -4
- package/types/src/chart-base.d.ts.map +1 -1
- package/types/src/chart-legend.d.ts +37 -0
- package/types/src/chart-legend.d.ts.map +1 -0
- package/types/src/chart-link.d.ts +26 -0
- package/types/src/chart-link.d.ts.map +1 -0
- package/types/src/chart-theme.d.ts +2 -0
- package/types/src/chart-theme.d.ts.map +1 -1
- package/types/src/chart-tooltip.d.ts +42 -0
- package/types/src/chart-tooltip.d.ts.map +1 -0
- package/types/src/chart.d.ts +13 -1
- package/types/src/chart.d.ts.map +1 -1
- package/types/src/echarts-chart-base.d.ts.map +1 -1
- package/types/src/engines/echarts-loader.d.ts +1 -1
- package/types/src/engines/echarts-loader.d.ts.map +1 -1
- package/types/src/engines/uplot-adapter.d.ts.map +1 -1
- package/types/src/engines/uplot-paths.d.ts +1 -1
- package/types/src/engines/uplot-paths.d.ts.map +1 -1
- package/types/src/gauge-chart.d.ts +54 -0
- package/types/src/gauge-chart.d.ts.map +1 -0
- package/types/src/pie-chart.d.ts +6 -0
- package/types/src/pie-chart.d.ts.map +1 -1
- package/types/src/radar-chart.d.ts +55 -0
- package/types/src/radar-chart.d.ts.map +1 -0
- package/types/src/scatter-chart.d.ts +41 -0
- package/types/src/scatter-chart.d.ts.map +1 -0
- package/vue.d.ts +104 -0
- package/dist/chart-series-BnpHO3lm.js +0 -83
package/README.md
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# @c2n/chart
|
|
2
2
|
|
|
3
|
-
Line, area, bar, sparkline and
|
|
3
|
+
Line, area, bar, sparkline, pie, gauge, scatter and candlestick charts as custom elements, from one package.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install @c2n/chart uplot # line, area, bar, sparkline
|
|
7
|
-
npm install @c2n/chart echarts # pie
|
|
7
|
+
npm install @c2n/chart echarts # pie, gauge, scatter, candlestick
|
|
8
8
|
```
|
|
9
9
|
|
|
10
10
|
```html
|
|
11
|
-
<c2-line-chart
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
<c2-line-chart
|
|
12
|
+
x-field="year"
|
|
13
|
+
data='[{"year":2022,"nuclear":294.73,"wind":38.2,"solar":19.34},{"year":2023,"nuclear":338.2,"wind":50.48,"solar":22.59},{"year":2024,"nuclear":380.45,"wind":45.43,"solar":24.87}]'
|
|
14
|
+
>
|
|
15
|
+
<c2-chart-series field="nuclear" label="Nuclear"></c2-chart-series>
|
|
16
|
+
<c2-chart-series field="wind" label="Wind"></c2-chart-series>
|
|
17
|
+
<c2-chart-series field="solar" label="Solar"></c2-chart-series>
|
|
14
18
|
</c2-line-chart>
|
|
15
19
|
```
|
|
16
20
|
|
|
@@ -21,14 +25,34 @@ import '@c2n/chart' // all of them
|
|
|
21
25
|
|
|
22
26
|
## Elements
|
|
23
27
|
|
|
24
|
-
| Tag
|
|
25
|
-
|
|
|
26
|
-
| `c2-line-chart`
|
|
27
|
-
| `c2-area-chart`
|
|
28
|
-
| `c2-bar-chart`
|
|
29
|
-
| `c2-sparkline`
|
|
30
|
-
| `c2-pie-chart`
|
|
31
|
-
| `c2-chart
|
|
28
|
+
| Tag | Engine | For |
|
|
29
|
+
| ---------------------- | ------- | ----------------------------------------------- |
|
|
30
|
+
| `c2-line-chart` | uPlot | Time or numeric trends |
|
|
31
|
+
| `c2-area-chart` | uPlot | The same, with the region under the line filled |
|
|
32
|
+
| `c2-bar-chart` | uPlot | Categorical or time-bucketed values |
|
|
33
|
+
| `c2-sparkline` | uPlot | A chromeless trend for a table cell or KPI row |
|
|
34
|
+
| `c2-pie-chart` | ECharts | Parts of a whole, with a donut mode |
|
|
35
|
+
| `c2-gauge-chart` | ECharts | A bounded current value or target |
|
|
36
|
+
| `c2-scatter-chart` | ECharts | Relationships, clusters and outliers |
|
|
37
|
+
| `c2-candlestick-chart` | ECharts | Open-high-low-close financial sessions |
|
|
38
|
+
| `c2-chart-series` | — | A series definition; renders nothing |
|
|
39
|
+
| `c2-chart-legend` | — | A linked legend placed anywhere in the layout |
|
|
40
|
+
| `c2-chart-tooltip` | — | A linked floating or inline tooltip |
|
|
41
|
+
|
|
42
|
+
## Independent legend and tooltip
|
|
43
|
+
|
|
44
|
+
The chart renders built-in chrome by default. To own its layout, link companion elements to the chart by id;
|
|
45
|
+
each companion automatically replaces its corresponding built-in display:
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<c2-line-chart id="sales" data="..."></c2-line-chart>
|
|
49
|
+
<c2-chart-legend for="sales"></c2-chart-legend>
|
|
50
|
+
<c2-chart-tooltip for="sales"></c2-chart-tooltip>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The legend occupies its normal HTML position. The tooltip floats beside the hovered point by default and
|
|
54
|
+
flips at viewport edges; `position="inline"` makes it occupy its authored position. Assign `renderLegend`,
|
|
55
|
+
`renderLegendItem` or `renderTooltip` for data-driven custom Lit templates.
|
|
32
56
|
|
|
33
57
|
## Engines
|
|
34
58
|
|
|
@@ -42,6 +66,11 @@ whichever engines your charts need.
|
|
|
42
66
|
Internally everything becomes one columnar frame of `Float64Array`s, which is the shape uPlot draws
|
|
43
67
|
natively and what makes appending O(1).
|
|
44
68
|
|
|
69
|
+
The documentation examples share a France 2015–2024 extract from the public
|
|
70
|
+
[Our World in Data Energy dataset](https://github.com/owid/energy-data), measured in terawatt-hours. Line,
|
|
71
|
+
area, bar, scatter and sparkline charts read the annual rows directly; radial charts derive a focused
|
|
72
|
+
metric or one slice per energy source from a selected year. Candlesticks use public OHLC observations.
|
|
73
|
+
|
|
45
74
|
```js
|
|
46
75
|
chart.data = rows // full replace
|
|
47
76
|
chart.revision++ // after mutating the array in place
|
|
@@ -75,7 +104,8 @@ messages) is ordinary DOM and does expose parts.
|
|
|
75
104
|
|
|
76
105
|
## Events
|
|
77
106
|
|
|
78
|
-
`chart-ready`, `chart-error`, `point-click`, `point-hover`, `range-change
|
|
107
|
+
`chart-ready`, `chart-error`, `point-click`, `point-hover`, `range-change`, `series-toggle`, `legend-change`
|
|
108
|
+
and `tooltip-change`. The
|
|
79
109
|
semantic ones do not bubble: several components fire similarly named events, so a listener belongs on the
|
|
80
110
|
element itself. The host also carries `data-chart-ready` once the first frame is drawn, which is the
|
|
81
111
|
signal to wait on in a test.
|
package/dist/area-chart.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as withAlpha } from "./uplot-paths-
|
|
1
|
+
import { t as __decorate } from "./decorate-BkO8ArQg.js";
|
|
2
|
+
import { r as withAlpha } from "./uplot-paths-D5HCEKRV.js";
|
|
3
3
|
import { LineChart } from "./line-chart.js";
|
|
4
4
|
import { property } from "lit/decorators.js";
|
|
5
5
|
import { customElement } from "@c2n/core/element-helper.js";
|
package/dist/bar-chart.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { t as
|
|
1
|
+
import { t as __decorate } from "./decorate-BkO8ArQg.js";
|
|
2
|
+
import "./chart-series.js";
|
|
3
|
+
import { t as UplotChartBase } from "./uplot-chart-base-CLhgOeM_.js";
|
|
4
|
+
import { t as barPaths } from "./uplot-paths-D5HCEKRV.js";
|
|
4
5
|
import { property } from "lit/decorators.js";
|
|
5
6
|
import { customElement } from "@c2n/core/element-helper.js";
|
|
6
7
|
//#region src/bar-chart.ts
|
|
@@ -16,7 +17,7 @@ var BarChart = class BarChart extends UplotChartBase {
|
|
|
16
17
|
stroke: color,
|
|
17
18
|
fill: color,
|
|
18
19
|
width: 0,
|
|
19
|
-
paths: barPaths(this.barWidth, this.barGap, index, context.series.length),
|
|
20
|
+
paths: barPaths(this.barWidth, this.barGap, index, context.series.length, context.theme.barRadius),
|
|
20
21
|
points: { show: false }
|
|
21
22
|
};
|
|
22
23
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { columnValue } from "./chart-data.js";
|
|
2
|
+
import { t as __decorate } from "./decorate-BkO8ArQg.js";
|
|
3
|
+
import "./chart-series.js";
|
|
4
|
+
import { t as EchartsChartBase } from "./echarts-chart-base-CImXMzVk.js";
|
|
5
|
+
import { property } from "lit/decorators.js";
|
|
6
|
+
import { customElement } from "@c2n/core/element-helper.js";
|
|
7
|
+
//#region src/candlestick-chart.ts
|
|
8
|
+
var CandlestickChart = class CandlestickChart extends EchartsChartBase {
|
|
9
|
+
constructor() {
|
|
10
|
+
super();
|
|
11
|
+
this.features = ["candlestick", "grid"];
|
|
12
|
+
this.openField = "open";
|
|
13
|
+
this.closeField = "close";
|
|
14
|
+
this.lowField = "low";
|
|
15
|
+
this.highField = "high";
|
|
16
|
+
this.name = "Price";
|
|
17
|
+
this.xType = "category";
|
|
18
|
+
this.tooltip = "axis";
|
|
19
|
+
this.legend = "none";
|
|
20
|
+
}
|
|
21
|
+
inferredSeries() {
|
|
22
|
+
return [
|
|
23
|
+
{
|
|
24
|
+
field: this.openField,
|
|
25
|
+
label: "Open"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
field: this.closeField,
|
|
29
|
+
label: "Close"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
field: this.lowField,
|
|
33
|
+
label: "Low"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
field: this.highField,
|
|
37
|
+
label: "High"
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
coordinateSystem(context) {
|
|
42
|
+
const axisLine = { lineStyle: { color: context.theme.gridColor } };
|
|
43
|
+
const axisLabel = {
|
|
44
|
+
color: context.theme.axisColor,
|
|
45
|
+
fontSize: context.theme.fontSize
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
grid: {
|
|
49
|
+
left: 48,
|
|
50
|
+
right: 16,
|
|
51
|
+
top: 16,
|
|
52
|
+
bottom: 32,
|
|
53
|
+
containLabel: false
|
|
54
|
+
},
|
|
55
|
+
xAxis: {
|
|
56
|
+
type: "category",
|
|
57
|
+
data: this.xLabels(),
|
|
58
|
+
boundaryGap: true,
|
|
59
|
+
axisLine,
|
|
60
|
+
axisLabel
|
|
61
|
+
},
|
|
62
|
+
yAxis: {
|
|
63
|
+
type: "value",
|
|
64
|
+
scale: true,
|
|
65
|
+
axisLine,
|
|
66
|
+
axisLabel,
|
|
67
|
+
splitLine: { lineStyle: { color: context.theme.gridColor } }
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
projectData(frame) {
|
|
72
|
+
const [open, close, low, high] = frame.columns;
|
|
73
|
+
return [Array.from({ length: frame.length }, (_item, index) => [
|
|
74
|
+
open ? columnValue(open, index) : null,
|
|
75
|
+
close ? columnValue(close, index) : null,
|
|
76
|
+
low ? columnValue(low, index) : null,
|
|
77
|
+
high ? columnValue(high, index) : null
|
|
78
|
+
])];
|
|
79
|
+
}
|
|
80
|
+
buildOptions(context) {
|
|
81
|
+
return {
|
|
82
|
+
...super.buildOptions(context),
|
|
83
|
+
series: [{
|
|
84
|
+
name: this.name,
|
|
85
|
+
...this.seriesOption(0, context)
|
|
86
|
+
}]
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
seriesOption(_index, context) {
|
|
90
|
+
return {
|
|
91
|
+
type: "candlestick",
|
|
92
|
+
itemStyle: {
|
|
93
|
+
color: context.theme.positive,
|
|
94
|
+
color0: context.theme.negative,
|
|
95
|
+
borderColor: context.theme.positive,
|
|
96
|
+
borderColor0: context.theme.negative
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
legendItems() {
|
|
101
|
+
const visible = !this.hiddenSeries.has(0);
|
|
102
|
+
return [{
|
|
103
|
+
label: this.name,
|
|
104
|
+
color: this.themeController.theme.positive,
|
|
105
|
+
visible,
|
|
106
|
+
toggle: () => this.setSeriesVisible(0, !visible),
|
|
107
|
+
series: {
|
|
108
|
+
field: this.openField,
|
|
109
|
+
label: this.name
|
|
110
|
+
},
|
|
111
|
+
index: 0
|
|
112
|
+
}];
|
|
113
|
+
}
|
|
114
|
+
xLabels() {
|
|
115
|
+
const frame = this.frame;
|
|
116
|
+
if (!frame) return [];
|
|
117
|
+
return Array.from({ length: frame.length }, (_item, index) => frame.labels?.[index] ?? this.formatX(columnValue(frame.x, index) ?? index));
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
__decorate([property({
|
|
121
|
+
type: String,
|
|
122
|
+
attribute: "open-field"
|
|
123
|
+
})], CandlestickChart.prototype, "openField", void 0);
|
|
124
|
+
__decorate([property({
|
|
125
|
+
type: String,
|
|
126
|
+
attribute: "close-field"
|
|
127
|
+
})], CandlestickChart.prototype, "closeField", void 0);
|
|
128
|
+
__decorate([property({
|
|
129
|
+
type: String,
|
|
130
|
+
attribute: "low-field"
|
|
131
|
+
})], CandlestickChart.prototype, "lowField", void 0);
|
|
132
|
+
__decorate([property({
|
|
133
|
+
type: String,
|
|
134
|
+
attribute: "high-field"
|
|
135
|
+
})], CandlestickChart.prototype, "highField", void 0);
|
|
136
|
+
__decorate([property({ type: String })], CandlestickChart.prototype, "name", void 0);
|
|
137
|
+
CandlestickChart = __decorate([customElement("c2-candlestick-chart")], CandlestickChart);
|
|
138
|
+
//#endregion
|
|
139
|
+
export { CandlestickChart };
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ChartFrameBuilder, columnValue } from "./chart-data.js";
|
|
2
2
|
import { ChartThemeController, PROBE_COLORS } from "./chart-theme.js";
|
|
3
|
-
import {
|
|
3
|
+
import { t as __decorate } from "./decorate-BkO8ArQg.js";
|
|
4
|
+
import { SERIES_CHANGE_EVENT, SERIES_TAG } from "./chart-series.js";
|
|
4
5
|
import { property, query, state } from "lit/decorators.js";
|
|
5
6
|
import { LitElement, html, isServer, nothing, unsafeCSS } from "lit";
|
|
6
7
|
import { jsonPropertyConverter } from "@c2n/core/lit-helper.js";
|
|
7
8
|
//#region src/chart.scss?inline
|
|
8
|
-
var chart_default = "@charset \"UTF-8\";\n/* ex : var((width: 24px), width, c2-checkbox) returns var(--c2-checkbox-width, 24px) */\n:host {\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: var(--c2-chart--width,100%);\n height: var(--c2-chart--height,320px);\n /*\n * The plot is absolutely positioned, so nothing inside the host contributes intrinsic width. As a flex\n * item — which is what every example frame in the docs site makes it — the host would otherwise shrink\n * to its legend, and to nothing at all when there is no legend. `min-width: 0` keeps it able to shrink\n * below its content when a layout genuinely needs that.\n */\n min-width: 0;\n padding: var(--c2-chart--padding,12px);\n background: var(--c2-chart--background,#ffffff);\n color: var(--c2-chart--color,#18181b);\n font-family: var(--c2-chart--font-family,inherit);\n font-size: var(--c2-chart--font-size,12px);\n border-top: var(--c2-chart--border-top,1px solid #e4e4e7);\n border-right: var(--c2-chart--border-right,1px solid #e4e4e7);\n border-bottom: var(--c2-chart--border-bottom,1px solid #e4e4e7);\n border-left: var(--c2-chart--border-left,1px solid #e4e4e7);\n border-top-left-radius: var(--c2-chart--border-top-left-radius,8px);\n border-top-right-radius: var(--c2-chart--border-top-right-radius,8px);\n border-bottom-left-radius: var(--c2-chart--border-bottom-left-radius,8px);\n border-bottom-right-radius: var(--c2-chart--border-bottom-right-radius,8px);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n.frame {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/*\n * The plot area is the engine's territory. `contain: strict` plus absolute positioning means the canvas\n * the engine sizes inside it can never feed its own size back into the ResizeObserver watching this box,\n * which is what makes the resize loop structurally impossible rather than merely unlikely.\n */\n.plot-area {\n position: relative;\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n}\n\n.plot {\n position: absolute;\n inset: 0;\n contain: strict;\n}\n\n.plot canvas {\n display: block;\n position: absolute;\n inset: 0;\n}\n\n.overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n\n.tooltip {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2;\n pointer-events: none;\n will-change: transform;\n display: flex;\n flex-direction: column;\n gap: var(--c2-chart__tooltip--gap,6px);\n padding: var(--c2-chart__tooltip--padding,8px 10px);\n border-radius: var(--c2-chart__tooltip--border-radius,6px);\n background: var(--c2-chart__tooltip--background-color,#18181b);\n color: var(--c2-chart__tooltip--color,#fafafa);\n font-size: var(--c2-chart__tooltip--font-size,12px);\n box-shadow: var(--c2-chart__tooltip--box-shadow,0 8px 24px rgba(24, 24, 27, 0.08));\n white-space: nowrap;\n}\n\n.tooltip[hidden] {\n display: none;\n}\n\n.tooltip-row {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.tooltip-marker {\n width: var(--c2-chart__legend-marker--size,10px);\n height: var(--c2-chart__legend-marker--size,10px);\n border-radius: var(--c2-chart__legend-marker--border-radius,999px);\n flex: none;\n}\n\n.tooltip-label {\n flex: 1 1 auto;\n}\n\n.tooltip-value {\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n}\n\n.actions {\n position: absolute;\n top: 0;\n right: 0;\n pointer-events: auto;\n display: flex;\n gap: 4px;\n}\n\n.state {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--c2-chart__state--color,#71717a);\n font-size: var(--c2-chart__state--font-size,14px);\n background: var(--c2-chart--background,#ffffff);\n}\n\n.legend {\n display: flex;\n flex-wrap: wrap;\n gap: var(--c2-chart__legend--gap,12px);\n padding: var(--c2-chart__legend--padding,8px 0 0);\n color: var(--c2-chart__legend--color,#71717a);\n font-size: var(--c2-chart__legend--font-size,12px);\n}\n\n.legend--start,\n.legend--end {\n flex-direction: column;\n align-content: flex-start;\n flex: 0 0 auto;\n align-self: center;\n max-height: 100%;\n overflow: auto;\n}\n\n/* The legend is on an inline edge, so the frame runs as a row. */\n.frame--side {\n flex-direction: row;\n align-items: stretch;\n}\n\n.legend-item {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n\n.legend-item[aria-pressed=false] {\n opacity: var(--c2-chart__legend__disabled--opacity,0.38);\n}\n\n.legend-marker {\n width: var(--c2-chart__legend-marker--size,10px);\n height: var(--c2-chart__legend-marker--size,10px);\n border-radius: var(--c2-chart__legend-marker--border-radius,999px);\n flex: none;\n}\n\n.definitions {\n display: none;\n}\n\n/*\n * Colour probes. `getComputedStyle().getPropertyValue()` hands back the author's text for a `var()` or\n * `color-mix()` chain, which no canvas engine can parse; the computed `color` of an element always\n * resolves to `rgb(…)`. `display: none` keeps them free.\n */\n.theme-probe {\n display: none;\n}\n.theme-probe i:nth-child(1) {\n color: var(--c2-chart__series-1--color,#0265dc);\n}\n.theme-probe i:nth-child(2) {\n color: var(--c2-chart__series-2--color,#ea580c);\n}\n.theme-probe i:nth-child(3) {\n color: var(--c2-chart__series-3--color,#0f766e);\n}\n.theme-probe i:nth-child(4) {\n color: var(--c2-chart__series-4--color,#db2777);\n}\n.theme-probe i:nth-child(5) {\n color: var(--c2-chart__series-5--color,#a16207);\n}\n.theme-probe i:nth-child(6) {\n color: var(--c2-chart__series-6--color,#7c3aed);\n}\n.theme-probe i:nth-child(7) {\n color: var(--c2-chart__series-7--color,#0891b2);\n}\n.theme-probe i:nth-child(8) {\n color: var(--c2-chart__series-8--color,#52525b);\n}\n.theme-probe i:nth-child(9) {\n color: var(--c2-chart__axis--color,#71717a);\n}\n.theme-probe i:nth-child(10) {\n color: var(--c2-chart__grid--color,#e4e4e7);\n}\n.theme-probe i:nth-child(11) {\n color: var(--c2-chart--color,#18181b);\n}\n.theme-probe i:nth-child(12) {\n color: var(--c2-chart__muted--color,#71717a);\n}\n.theme-probe i:nth-child(13) {\n color: var(--c2-chart__tooltip--background-color,#18181b);\n}\n.theme-probe i:nth-child(14) {\n color: var(--c2-chart__tooltip--color,#fafafa);\n}\n.theme-probe i:nth-child(15) {\n color: var(--c2-chart__crosshair--color,#a1a1aa);\n}\n.theme-probe i:nth-child(16) {\n color: var(--c2-chart__positive--color,#16a34a);\n}\n.theme-probe i:nth-child(17) {\n color: var(--c2-chart__negative--color,#dc2626);\n}\n.theme-probe i:nth-child(18) {\n color: var(--c2-chart__surface--color,#ffffff);\n}\n\n/*\n * uPlot's structural CSS, hand-ported.\n *\n * `uplot/dist/uPlot.min.css` is a global stylesheet and a shadow root cannot see it, so the rules the\n * engine's DOM depends on live here instead. Only the structural half is kept: the legend and title rules\n * are dropped because this component renders its own. Version-coupled to the `uplot` peer range — re-check\n * after an upgrade.\n */\n.uplot,\n.uplot *,\n.uplot *::before,\n.uplot *::after {\n box-sizing: border-box;\n}\n\n.uplot {\n font-family: var(--c2-chart--font-family,inherit);\n line-height: 1.5;\n width: min-content;\n}\n\n.u-wrap {\n position: relative;\n user-select: none;\n}\n\n.u-over,\n.u-under {\n position: absolute;\n}\n\n.u-under {\n overflow: hidden;\n}\n\n.uplot canvas {\n display: block;\n position: relative;\n width: 100%;\n height: 100%;\n}\n\n.u-axis {\n position: absolute;\n}\n\n.u-select {\n position: absolute;\n pointer-events: none;\n background: rgba(0, 0, 0, 0.07);\n}\n\n.u-cursor-x,\n.u-cursor-y {\n position: absolute;\n left: 0;\n top: 0;\n pointer-events: none;\n will-change: transform;\n}\n\n.u-hz .u-cursor-x,\n.u-vt .u-cursor-y {\n height: 100%;\n border-right: var(--c2-chart__crosshair--width,1px) dashed var(--c2-chart__crosshair--color,#a1a1aa);\n}\n\n.u-hz .u-cursor-y,\n.u-vt .u-cursor-x {\n width: 100%;\n border-bottom: var(--c2-chart__crosshair--width,1px) dashed var(--c2-chart__crosshair--color,#a1a1aa);\n}\n\n.u-cursor-pt {\n position: absolute;\n top: 0;\n left: 0;\n border-radius: 50%;\n border: 0 solid;\n pointer-events: none;\n will-change: transform;\n /* Must stay !important: uPlot sets the `background` shorthand inline. */\n background-clip: padding-box !important;\n}\n\n.u-axis.u-off,\n.u-select.u-off,\n.u-cursor-x.u-off,\n.u-cursor-y.u-off,\n.u-cursor-pt.u-off {\n display: none;\n}";
|
|
9
|
+
var chart_default = "@charset \"UTF-8\";\n/* ex : var((width: 24px), width, c2-checkbox) returns var(--c2-checkbox-width, 24px) */\n:host {\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n width: var(--c2-chart--width,100%);\n height: var(--c2-chart--height,320px);\n /*\n * The plot is absolutely positioned, so nothing inside the host contributes intrinsic width. As a flex\n * item — which is what every example frame in the docs site makes it — the host would otherwise shrink\n * to its legend, and to nothing at all when there is no legend. `min-width: 0` keeps it able to shrink\n * below its content when a layout genuinely needs that.\n */\n min-width: 0;\n padding: var(--c2-chart--padding,12px);\n background: var(--c2-chart--background,#ffffff);\n color: var(--c2-chart--color,#18181b);\n font-family: var(--c2-chart--font-family,inherit);\n font-size: var(--c2-chart--font-size,12px);\n border-top: var(--c2-chart--border-top,1px solid #e4e4e7);\n border-right: var(--c2-chart--border-right,1px solid #e4e4e7);\n border-bottom: var(--c2-chart--border-bottom,1px solid #e4e4e7);\n border-left: var(--c2-chart--border-left,1px solid #e4e4e7);\n border-top-left-radius: var(--c2-chart--border-top-left-radius,8px);\n border-top-right-radius: var(--c2-chart--border-top-right-radius,8px);\n border-bottom-left-radius: var(--c2-chart--border-bottom-left-radius,8px);\n border-bottom-right-radius: var(--c2-chart--border-bottom-right-radius,8px);\n}\n\n:host([hidden]) {\n display: none;\n}\n\n.frame {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 0;\n}\n\n/*\n * The plot area is the engine's territory. `contain: strict` plus absolute positioning means the canvas\n * the engine sizes inside it can never feed its own size back into the ResizeObserver watching this box,\n * which is what makes the resize loop structurally impossible rather than merely unlikely.\n */\n.plot-area {\n position: relative;\n flex: 1 1 auto;\n min-height: 0;\n overflow: hidden;\n}\n\n.plot {\n position: absolute;\n inset: 0;\n contain: strict;\n}\n\n/* uPlot has no entry animation. Reveal its already-rendered canvas once, when the adapter becomes ready. */\n:host([data-chart-engine=uplot][animation=auto]) .plot .u-under {\n animation: c2-chart-plot-reveal 500ms cubic-bezier(0.22, 1, 0.36, 1) both;\n}\n\n/* Bars enter from their baseline instead of using the line/area chart's horizontal reveal. */\n:host(c2-bar-chart[data-chart-engine=uplot][animation=auto]) .plot .u-under {\n animation-name: c2-chart-bar-grow;\n animation-duration: 600ms;\n}\n\n@keyframes c2-chart-plot-reveal {\n from {\n clip-path: inset(0 100% 0 0);\n opacity: 0.65;\n }\n to {\n clip-path: inset(0);\n opacity: 1;\n }\n}\n@keyframes c2-chart-bar-grow {\n from {\n clip-path: inset(100% 0 0);\n opacity: 0.65;\n }\n to {\n clip-path: inset(0);\n opacity: 1;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n :host([data-chart-engine=uplot][animation=auto]) .plot .u-under,\n :host(c2-bar-chart[data-chart-engine=uplot][animation=auto]) .plot .u-under {\n animation: none;\n }\n}\n.plot canvas {\n display: block;\n position: absolute;\n inset: 0;\n}\n\n.overlay {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n\n.tooltip {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2;\n pointer-events: none;\n will-change: transform;\n display: flex;\n flex-direction: column;\n gap: var(--c2-chart__tooltip--gap,6px);\n padding: var(--c2-chart__tooltip--padding,8px 10px);\n border-radius: var(--c2-chart__tooltip--border-radius,6px);\n background: var(--c2-chart__tooltip--background-color,#18181b);\n color: var(--c2-chart__tooltip--color,#fafafa);\n font-size: var(--c2-chart__tooltip--font-size,12px);\n box-shadow: var(--c2-chart__tooltip--box-shadow,0 8px 24px rgba(24, 24, 27, 0.08));\n white-space: nowrap;\n}\n\n.tooltip[hidden] {\n display: none;\n}\n\n.tooltip-row {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.tooltip-marker {\n width: var(--c2-chart__legend-marker--size,10px);\n height: var(--c2-chart__legend-marker--size,10px);\n border-radius: var(--c2-chart__legend-marker--border-radius,999px);\n flex: none;\n}\n\n.tooltip-label {\n flex: 1 1 auto;\n}\n\n.tooltip-value {\n font-variant-numeric: tabular-nums;\n font-weight: 600;\n}\n\n.actions {\n position: absolute;\n top: 0;\n right: 0;\n pointer-events: auto;\n display: flex;\n gap: 4px;\n}\n\n.state {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--c2-chart__state--color,#71717a);\n font-size: var(--c2-chart__state--font-size,14px);\n background: var(--c2-chart--background,#ffffff);\n}\n\n.legend {\n display: flex;\n flex-wrap: wrap;\n gap: var(--c2-chart__legend--gap,12px);\n padding: var(--c2-chart__legend--padding,8px 0 0);\n color: var(--c2-chart__legend--color,#71717a);\n font-size: var(--c2-chart__legend--font-size,12px);\n}\n\n.legend--start,\n.legend--end {\n flex-direction: column;\n align-content: flex-start;\n flex: 0 0 auto;\n align-self: center;\n max-height: 100%;\n overflow: auto;\n}\n\n/* The legend is on an inline edge, so the frame runs as a row. */\n.frame--side {\n flex-direction: row;\n align-items: stretch;\n}\n\n.legend-item {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n\n.legend-item[aria-pressed=false] {\n opacity: var(--c2-chart__legend__disabled--opacity,0.38);\n}\n\n.legend-marker {\n width: var(--c2-chart__legend-marker--size,10px);\n height: var(--c2-chart__legend-marker--size,10px);\n border-radius: var(--c2-chart__legend-marker--border-radius,999px);\n flex: none;\n}\n\n.definitions {\n display: none;\n}\n\n/*\n * Colour probes. `getComputedStyle().getPropertyValue()` hands back the author's text for a `var()` or\n * `color-mix()` chain, which no canvas engine can parse; the computed `color` of an element always\n * resolves to `rgb(…)`. `display: none` keeps them free.\n */\n.theme-probe {\n display: none;\n}\n.theme-probe i:nth-child(1) {\n color: var(--c2-chart__series-1--color,#0265dc);\n}\n.theme-probe i:nth-child(2) {\n color: var(--c2-chart__series-2--color,#ea580c);\n}\n.theme-probe i:nth-child(3) {\n color: var(--c2-chart__series-3--color,#0f766e);\n}\n.theme-probe i:nth-child(4) {\n color: var(--c2-chart__series-4--color,#db2777);\n}\n.theme-probe i:nth-child(5) {\n color: var(--c2-chart__series-5--color,#a16207);\n}\n.theme-probe i:nth-child(6) {\n color: var(--c2-chart__series-6--color,#7c3aed);\n}\n.theme-probe i:nth-child(7) {\n color: var(--c2-chart__series-7--color,#0891b2);\n}\n.theme-probe i:nth-child(8) {\n color: var(--c2-chart__series-8--color,#52525b);\n}\n.theme-probe i:nth-child(9) {\n color: var(--c2-chart__axis--color,#71717a);\n}\n.theme-probe i:nth-child(10) {\n color: var(--c2-chart__grid--color,#e4e4e7);\n}\n.theme-probe i:nth-child(11) {\n color: var(--c2-chart--color,#18181b);\n}\n.theme-probe i:nth-child(12) {\n color: var(--c2-chart__muted--color,#71717a);\n}\n.theme-probe i:nth-child(13) {\n color: var(--c2-chart__tooltip--background-color,#18181b);\n}\n.theme-probe i:nth-child(14) {\n color: var(--c2-chart__tooltip--color,#fafafa);\n}\n.theme-probe i:nth-child(15) {\n color: var(--c2-chart__crosshair--color,#a1a1aa);\n}\n.theme-probe i:nth-child(16) {\n color: var(--c2-chart__positive--color,#16a34a);\n}\n.theme-probe i:nth-child(17) {\n color: var(--c2-chart__negative--color,#dc2626);\n}\n.theme-probe i:nth-child(18) {\n color: var(--c2-chart__surface--color,#ffffff);\n}\n\n/*\n * uPlot's structural CSS, hand-ported.\n *\n * `uplot/dist/uPlot.min.css` is a global stylesheet and a shadow root cannot see it, so the rules the\n * engine's DOM depends on live here instead. Only the structural half is kept: the legend and title rules\n * are dropped because this component renders its own. Version-coupled to the `uplot` peer range — re-check\n * after an upgrade.\n */\n.uplot,\n.uplot *,\n.uplot *::before,\n.uplot *::after {\n box-sizing: border-box;\n}\n\n.uplot {\n font-family: var(--c2-chart--font-family,inherit);\n line-height: 1.5;\n width: min-content;\n}\n\n.u-wrap {\n position: relative;\n user-select: none;\n}\n\n.u-over,\n.u-under {\n position: absolute;\n}\n\n.u-under {\n overflow: hidden;\n}\n\n.uplot canvas {\n display: block;\n position: relative;\n width: 100%;\n height: 100%;\n}\n\n.u-axis {\n position: absolute;\n}\n\n.u-select {\n position: absolute;\n pointer-events: none;\n background: rgba(0, 0, 0, 0.07);\n}\n\n.u-cursor-x,\n.u-cursor-y {\n position: absolute;\n left: 0;\n top: 0;\n pointer-events: none;\n will-change: transform;\n}\n\n.u-hz .u-cursor-x,\n.u-vt .u-cursor-y {\n height: 100%;\n border-right: var(--c2-chart__crosshair--width,1px) dashed var(--c2-chart__crosshair--color,#a1a1aa);\n}\n\n.u-hz .u-cursor-y,\n.u-vt .u-cursor-x {\n width: 100%;\n border-bottom: var(--c2-chart__crosshair--width,1px) dashed var(--c2-chart__crosshair--color,#a1a1aa);\n}\n\n.u-cursor-pt {\n position: absolute;\n top: 0;\n left: 0;\n border-radius: 50%;\n border: 0 solid;\n pointer-events: none;\n will-change: transform;\n /* Must stay !important: uPlot sets the `background` shorthand inline. */\n background-clip: padding-box !important;\n}\n\n.u-axis.u-off,\n.u-select.u-off,\n.u-cursor-x.u-off,\n.u-cursor-y.u-off,\n.u-cursor-pt.u-off {\n display: none;\n}";
|
|
9
10
|
//#endregion
|
|
10
11
|
//#region src/chart-base.ts
|
|
11
12
|
/** Properties that only ever move data. A change limited to these takes the fast path. */
|
|
@@ -31,6 +32,8 @@ var VIEW_ONLY_KEYS = /* @__PURE__ */ new Set([
|
|
|
31
32
|
"lazyRender",
|
|
32
33
|
"renderTooltip",
|
|
33
34
|
"renderLegendItem",
|
|
35
|
+
"hasLinkedLegend",
|
|
36
|
+
"hasLinkedTooltip",
|
|
34
37
|
"dataSource"
|
|
35
38
|
]);
|
|
36
39
|
/** `stats.cpu` -> `Cpu`, `unit_price` -> `Unit price`: a readable label for an inferred field. */
|
|
@@ -68,7 +71,9 @@ var RESIZE_EPSILON = 1;
|
|
|
68
71
|
* @event {CustomEvent<{ engine: string }>} chart-ready - Fired after the engine has loaded and drawn for the first time. The host also gains `data-chart-ready`, which is what a test should wait on.
|
|
69
72
|
* @event {CustomEvent<{ error: unknown }>} chart-error - Fired when the engine fails to load or to draw. `detail.error` is the underlying failure.
|
|
70
73
|
* @event {CustomEvent<ChartPointEventDetail>} point-click - Fired when a datum is clicked. Does not bubble: several components fire point events, so a listener belongs on the element itself.
|
|
71
|
-
* @event {CustomEvent<ChartPointEventDetail>} point-hover - Fired as the pointer moves between data points, and with a `null` detail when it leaves the plot. Does not bubble.
|
|
74
|
+
* @event {CustomEvent<ChartPointEventDetail | null>} point-hover - Fired as the pointer moves between data points, and with a `null` detail when it leaves the plot. Does not bubble.
|
|
75
|
+
* @event {CustomEvent<ChartTooltipContext | null>} tooltip-change - Fired with the complete tooltip model as the pointer moves, and with `null` when it leaves. Used by `c2-chart-tooltip`.
|
|
76
|
+
* @event {CustomEvent<ChartLegendChangeEventDetail>} legend-change - Fired when legend entries or visibility change. Used by `c2-chart-legend`.
|
|
72
77
|
* @event {CustomEvent<ChartRangeEventDetail>} range-change - Fired after the user zooms or brushes. `detail.min` and `detail.max` are the new x bounds. Does not bubble.
|
|
73
78
|
* @event {CustomEvent<ChartSeriesToggleEventDetail>} series-toggle - Fired when a legend entry is toggled. Does not bubble.
|
|
74
79
|
*
|
|
@@ -112,6 +117,7 @@ var RESIZE_EPSILON = 1;
|
|
|
112
117
|
* @cssproperty {pixel} [--c2-chart__line--width=2px] - Stroke width of a line series.
|
|
113
118
|
* @cssproperty {pixel} [--c2-chart__point--radius=2.5px] - Radius of a data point marker.
|
|
114
119
|
* @cssproperty {opacity} [--c2-chart__area--opacity=0.15] - Opacity of the fill under an area series.
|
|
120
|
+
* @cssproperty {number} [--c2-chart__bar--border-radius=0] - Roundedness of a bar's value end, from 0 (square) to 0.5 (fully rounded).
|
|
115
121
|
*
|
|
116
122
|
* @cssproperty {color} [--c2-chart__axis--color=#71717a] - Colour of the axis lines and tick labels.
|
|
117
123
|
* @cssproperty {font-size} [--c2-chart__axis--font-size=12px] - Font size of the tick labels.
|
|
@@ -153,7 +159,7 @@ var ChartBase = class extends LitElement {
|
|
|
153
159
|
this.xType = "linear";
|
|
154
160
|
this.legend = "bottom";
|
|
155
161
|
this.tooltip = "axis";
|
|
156
|
-
this.animation = "
|
|
162
|
+
this.animation = "auto";
|
|
157
163
|
this.loading = false;
|
|
158
164
|
this.error = "";
|
|
159
165
|
this.emptyMessage = "No data";
|
|
@@ -163,6 +169,8 @@ var ChartBase = class extends LitElement {
|
|
|
163
169
|
this.hiddenSeries = /* @__PURE__ */ new Set();
|
|
164
170
|
this.hoverContext = null;
|
|
165
171
|
this.engineFailed = false;
|
|
172
|
+
this.hasLinkedLegend = false;
|
|
173
|
+
this.hasLinkedTooltip = false;
|
|
166
174
|
this.themeController = new ChartThemeController(this, () => this.handleThemeChange());
|
|
167
175
|
this.frameBuilder = new ChartFrameBuilder();
|
|
168
176
|
this.#creating = false;
|
|
@@ -175,6 +183,9 @@ var ChartBase = class extends LitElement {
|
|
|
175
183
|
height: 0
|
|
176
184
|
};
|
|
177
185
|
this.#visible = false;
|
|
186
|
+
this.#tooltipContext = null;
|
|
187
|
+
this.#legendPresenters = /* @__PURE__ */ new Set();
|
|
188
|
+
this.#tooltipPresenters = /* @__PURE__ */ new Set();
|
|
178
189
|
this.#handleSeriesChange = (event) => {
|
|
179
190
|
event.stopPropagation();
|
|
180
191
|
this.#collectSeries();
|
|
@@ -194,6 +205,17 @@ var ChartBase = class extends LitElement {
|
|
|
194
205
|
#intersectionObserver;
|
|
195
206
|
#visible;
|
|
196
207
|
#unsubscribe;
|
|
208
|
+
#tooltipContext;
|
|
209
|
+
#legendPresenters;
|
|
210
|
+
#tooltipPresenters;
|
|
211
|
+
/** Restores interaction state after an engine instance is created or replaced. */
|
|
212
|
+
restoreVisibility(adapter) {
|
|
213
|
+
for (const index of this.hiddenSeries) adapter.setSeriesVisibility(index, false);
|
|
214
|
+
}
|
|
215
|
+
/** Whether changing chart data can change the legend model. Pie charts use row labels as entries. */
|
|
216
|
+
get legendDependsOnData() {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
197
219
|
connectedCallback() {
|
|
198
220
|
super.connectedCallback();
|
|
199
221
|
this.addEventListener(SERIES_CHANGE_EVENT, this.#handleSeriesChange);
|
|
@@ -210,6 +232,14 @@ var ChartBase = class extends LitElement {
|
|
|
210
232
|
this.#intersectionObserver.observe(this);
|
|
211
233
|
} else this.#visible = true;
|
|
212
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Client-rendered charts connect before Lit creates their plot node. Observe it after the first render as
|
|
237
|
+
* well, so a chart whose initial layout is zero-sized still draws when its container becomes measurable.
|
|
238
|
+
*/
|
|
239
|
+
firstUpdated() {
|
|
240
|
+
if (this.plotElement) this.#resizeObserver?.observe(this.plotElement);
|
|
241
|
+
this.themeController.invalidate();
|
|
242
|
+
}
|
|
213
243
|
disconnectedCallback() {
|
|
214
244
|
super.disconnectedCallback();
|
|
215
245
|
this.removeEventListener(SERIES_CHANGE_EVENT, this.#handleSeriesChange);
|
|
@@ -219,6 +249,7 @@ var ChartBase = class extends LitElement {
|
|
|
219
249
|
this.#unsubscribe = void 0;
|
|
220
250
|
this.adapter?.destroy();
|
|
221
251
|
this.adapter = void 0;
|
|
252
|
+
this.removeAttribute("data-chart-engine");
|
|
222
253
|
this.removeAttribute("data-chart-ready");
|
|
223
254
|
}
|
|
224
255
|
/**
|
|
@@ -251,6 +282,12 @@ var ChartBase = class extends LitElement {
|
|
|
251
282
|
super.updated(changed);
|
|
252
283
|
if (isServer) return;
|
|
253
284
|
this.#sync();
|
|
285
|
+
if (changed.has("tooltip") && this.tooltip === "none") {
|
|
286
|
+
this.#tooltipContext = null;
|
|
287
|
+
this.#setHover(null);
|
|
288
|
+
this.dispatchEvent(new CustomEvent("tooltip-change", { detail: null }));
|
|
289
|
+
}
|
|
290
|
+
if ([...changed.keys()].some((key) => key !== "hoverContext")) this.notifyLegendChange();
|
|
254
291
|
}
|
|
255
292
|
/**
|
|
256
293
|
* Replaces the data without going through the update queue. Equivalent to assigning `data`, but skips
|
|
@@ -281,6 +318,7 @@ var ChartBase = class extends LitElement {
|
|
|
281
318
|
else next.add(index);
|
|
282
319
|
this.hiddenSeries = next;
|
|
283
320
|
this.adapter?.setSeriesVisibility(index, visible);
|
|
321
|
+
this.notifyLegendChange();
|
|
284
322
|
const series = this.resolvedSeries[index];
|
|
285
323
|
if (!series) return;
|
|
286
324
|
this.dispatchEvent(new CustomEvent("series-toggle", { detail: {
|
|
@@ -290,6 +328,42 @@ var ChartBase = class extends LitElement {
|
|
|
290
328
|
hidden: [...next]
|
|
291
329
|
} }));
|
|
292
330
|
}
|
|
331
|
+
/** Current entries for a linked `c2-chart-legend`. */
|
|
332
|
+
getLegendItems() {
|
|
333
|
+
return this.legendItems();
|
|
334
|
+
}
|
|
335
|
+
/** Current hover model for a linked `c2-chart-tooltip`. */
|
|
336
|
+
getTooltipContext() {
|
|
337
|
+
return this.#tooltipContext;
|
|
338
|
+
}
|
|
339
|
+
/** Plot bounds used to place a linked floating tooltip in viewport coordinates. */
|
|
340
|
+
getPlotBounds() {
|
|
341
|
+
return (this.plotElement ?? this).getBoundingClientRect();
|
|
342
|
+
}
|
|
343
|
+
/** Registers an external legend and suppresses the built-in legend until it disconnects. */
|
|
344
|
+
registerLegendPresenter(presenter) {
|
|
345
|
+
this.#legendPresenters.add(presenter);
|
|
346
|
+
this.hasLinkedLegend = this.#legendPresenters.size > 0;
|
|
347
|
+
}
|
|
348
|
+
/** Removes an external legend registration. */
|
|
349
|
+
unregisterLegendPresenter(presenter) {
|
|
350
|
+
this.#legendPresenters.delete(presenter);
|
|
351
|
+
this.hasLinkedLegend = this.#legendPresenters.size > 0;
|
|
352
|
+
}
|
|
353
|
+
/** Registers an external tooltip and suppresses the built-in tooltip until it disconnects. */
|
|
354
|
+
registerTooltipPresenter(presenter) {
|
|
355
|
+
this.#tooltipPresenters.add(presenter);
|
|
356
|
+
this.hasLinkedTooltip = this.#tooltipPresenters.size > 0;
|
|
357
|
+
}
|
|
358
|
+
/** Removes an external tooltip registration. */
|
|
359
|
+
unregisterTooltipPresenter(presenter) {
|
|
360
|
+
this.#tooltipPresenters.delete(presenter);
|
|
361
|
+
this.hasLinkedTooltip = this.#tooltipPresenters.size > 0;
|
|
362
|
+
}
|
|
363
|
+
/** Notifies linked legends after a subclass changes a data-shaped legend such as pie slices. */
|
|
364
|
+
notifyLegendChange() {
|
|
365
|
+
this.dispatchEvent(new CustomEvent("legend-change", { detail: { items: this.getLegendItems() } }));
|
|
366
|
+
}
|
|
293
367
|
/** The series in draw order: `c2-chart-series` children when present, otherwise the `series` property. */
|
|
294
368
|
get resolvedSeries() {
|
|
295
369
|
if (this.seriesElements.length > 0) return this.seriesElements.filter((element) => !element.hidden).map((element) => element.toConfig());
|
|
@@ -359,13 +433,17 @@ var ChartBase = class extends LitElement {
|
|
|
359
433
|
await this.#createChart();
|
|
360
434
|
return;
|
|
361
435
|
}
|
|
436
|
+
let restoreAfterData = false;
|
|
362
437
|
if (this.#optionsDirty) {
|
|
363
438
|
this.#optionsDirty = false;
|
|
364
|
-
|
|
439
|
+
const mode = this.#optionsMode;
|
|
440
|
+
this.adapter.setOptions(this.buildOptions(this.buildContext()), mode);
|
|
365
441
|
this.#optionsMode = "merge";
|
|
442
|
+
restoreAfterData = mode === "replace";
|
|
366
443
|
this.#dataDirty = true;
|
|
367
444
|
}
|
|
368
445
|
if (this.#dataDirty) this.#flushData();
|
|
446
|
+
if (restoreAfterData) this.restoreVisibility(this.adapter);
|
|
369
447
|
}
|
|
370
448
|
async #createChart() {
|
|
371
449
|
if (this.#creating || !this.plotElement || !this.frame) return;
|
|
@@ -385,7 +463,8 @@ var ChartBase = class extends LitElement {
|
|
|
385
463
|
this.adapter = adapter;
|
|
386
464
|
this.#optionsDirty = false;
|
|
387
465
|
this.#dataDirty = false;
|
|
388
|
-
|
|
466
|
+
this.restoreVisibility(adapter);
|
|
467
|
+
this.setAttribute("data-chart-engine", this.engineName);
|
|
389
468
|
this.setAttribute("data-chart-ready", "true");
|
|
390
469
|
this.dispatchEvent(new CustomEvent("chart-ready", { detail: { engine: this.engineName } }));
|
|
391
470
|
} catch (error) {
|
|
@@ -402,6 +481,7 @@ var ChartBase = class extends LitElement {
|
|
|
402
481
|
if (this.#appended > 0 && this.adapter.appendData) this.adapter.appendData(projected, this.#appended);
|
|
403
482
|
else this.adapter.setData(projected);
|
|
404
483
|
this.#appended = 0;
|
|
484
|
+
if (this.legendDependsOnData) this.notifyLegendChange();
|
|
405
485
|
}
|
|
406
486
|
#handleResize(entries) {
|
|
407
487
|
const rect = entries[entries.length - 1]?.contentRect;
|
|
@@ -451,12 +531,18 @@ var ChartBase = class extends LitElement {
|
|
|
451
531
|
} else this.#unsubscribe = () => controller.abort();
|
|
452
532
|
}
|
|
453
533
|
#handleHover(detail) {
|
|
454
|
-
if (!detail || !this.frame
|
|
534
|
+
if (!detail || !this.frame) {
|
|
535
|
+
this.#tooltipContext = null;
|
|
455
536
|
this.#setHover(null);
|
|
537
|
+
this.dispatchEvent(new CustomEvent("tooltip-change", { detail: null }));
|
|
538
|
+
this.dispatchEvent(new CustomEvent("point-hover", { detail: null }));
|
|
456
539
|
return;
|
|
457
540
|
}
|
|
458
541
|
const context = this.#tooltipContextAt(detail);
|
|
459
|
-
this
|
|
542
|
+
const tooltipContext = this.tooltip === "none" ? null : context;
|
|
543
|
+
this.#tooltipContext = tooltipContext;
|
|
544
|
+
this.#setHover(this.hasLinkedTooltip ? null : tooltipContext);
|
|
545
|
+
this.dispatchEvent(new CustomEvent("tooltip-change", { detail: tooltipContext }));
|
|
460
546
|
const point = this.#pointAt(detail.index, detail.seriesIndex);
|
|
461
547
|
if (point) this.dispatchEvent(new CustomEvent("point-hover", { detail: point }));
|
|
462
548
|
}
|
|
@@ -468,7 +554,7 @@ var ChartBase = class extends LitElement {
|
|
|
468
554
|
#setHover(context) {
|
|
469
555
|
const previous = this.hoverContext;
|
|
470
556
|
if (context && this.tooltipElement) this.tooltipElement.style.transform = `translate(${context.px}px, ${context.py}px)`;
|
|
471
|
-
if (previous && context && previous.index === context.index) return;
|
|
557
|
+
if (previous && context && previous.index === context.index && (this.tooltip !== "item" || previous.entries[0]?.seriesIndex === context.entries[0]?.seriesIndex)) return;
|
|
472
558
|
this.hoverContext = context;
|
|
473
559
|
}
|
|
474
560
|
#tooltipContextAt(detail) {
|
|
@@ -541,8 +627,8 @@ var ChartBase = class extends LitElement {
|
|
|
541
627
|
return new Intl.NumberFormat(this.locale, { maximumFractionDigits: 2 }).format(value);
|
|
542
628
|
}
|
|
543
629
|
render() {
|
|
544
|
-
const legendBefore = this.legend === "top" || this.legend === "start";
|
|
545
|
-
const legendAfter = this.legend === "bottom" || this.legend === "end";
|
|
630
|
+
const legendBefore = !this.hasLinkedLegend && (this.legend === "top" || this.legend === "start");
|
|
631
|
+
const legendAfter = !this.hasLinkedLegend && (this.legend === "bottom" || this.legend === "end");
|
|
546
632
|
const side = this.legend === "start" || this.legend === "end";
|
|
547
633
|
return html`
|
|
548
634
|
<div class="frame ${side ? "frame--side" : ""}" part="frame">
|
|
@@ -551,7 +637,9 @@ var ChartBase = class extends LitElement {
|
|
|
551
637
|
<!-- No bindings inside: Lit never patches this node, so the engine's canvas survives updates. -->
|
|
552
638
|
<div class="plot" part="plot"></div>
|
|
553
639
|
<div class="overlay" part="overlay">
|
|
554
|
-
<div class="tooltip" part="tooltip" role="tooltip" ?hidden=${!this.hoverContext || this.tooltip === "none"
|
|
640
|
+
<div class="tooltip" part="tooltip" role="tooltip" ?hidden=${!this.hoverContext || this.tooltip === "none" || this.hasLinkedTooltip}>
|
|
641
|
+
${this.renderTooltipBody()}
|
|
642
|
+
</div>
|
|
555
643
|
<div class="actions" part="actions"><slot name="actions"></slot></div>
|
|
556
644
|
</div>
|
|
557
645
|
${this.renderState()}
|
|
@@ -637,7 +725,10 @@ __decorate([property({
|
|
|
637
725
|
})], ChartBase.prototype, "xType", void 0);
|
|
638
726
|
__decorate([property({ type: String })], ChartBase.prototype, "legend", void 0);
|
|
639
727
|
__decorate([property({ type: String })], ChartBase.prototype, "tooltip", void 0);
|
|
640
|
-
__decorate([property({
|
|
728
|
+
__decorate([property({
|
|
729
|
+
type: String,
|
|
730
|
+
reflect: true
|
|
731
|
+
})], ChartBase.prototype, "animation", void 0);
|
|
641
732
|
__decorate([property({ type: String })], ChartBase.prototype, "locale", void 0);
|
|
642
733
|
__decorate([property({
|
|
643
734
|
type: Boolean,
|
|
@@ -662,5 +753,7 @@ __decorate([state()], ChartBase.prototype, "seriesElements", void 0);
|
|
|
662
753
|
__decorate([state()], ChartBase.prototype, "hiddenSeries", void 0);
|
|
663
754
|
__decorate([state()], ChartBase.prototype, "hoverContext", void 0);
|
|
664
755
|
__decorate([state()], ChartBase.prototype, "engineFailed", void 0);
|
|
756
|
+
__decorate([state()], ChartBase.prototype, "hasLinkedLegend", void 0);
|
|
757
|
+
__decorate([state()], ChartBase.prototype, "hasLinkedTooltip", void 0);
|
|
665
758
|
//#endregion
|
|
666
759
|
export { chart_default as n, ChartBase as t };
|
package/dist/chart-base.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as ChartBase } from "./chart-base-
|
|
1
|
+
import { t as ChartBase } from "./chart-base-CEZvMKwW.js";
|
|
2
2
|
export { ChartBase };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { t as __decorate } from "./decorate-BkO8ArQg.js";
|
|
2
|
+
import { t as ChartLinkedElement } from "./chart-link-C9vN9Y6c.js";
|
|
3
|
+
import { property, state } from "lit/decorators.js";
|
|
4
|
+
import { customElement } from "@c2n/core/element-helper.js";
|
|
5
|
+
import { html, unsafeCSS } from "lit";
|
|
6
|
+
//#region src/chart-legend.scss?inline
|
|
7
|
+
var chart_legend_default = "/* ex : var((width: 24px), width, c2-checkbox) returns var(--c2-checkbox-width, 24px) */\n:host {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--c2-chart__legend--gap,12px);\n padding: var(--c2-chart__legend--padding,0);\n color: var(--c2-chart__legend--color,#71717a);\n font-size: var(--c2-chart__legend--font-size,12px);\n}\n\n.item {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n\n.item[aria-pressed=false] {\n opacity: var(--c2-chart__legend__disabled--opacity,0.38);\n}\n\n.marker {\n width: var(--c2-chart__legend-marker--size,10px);\n height: var(--c2-chart__legend-marker--size,10px);\n border-radius: var(--c2-chart__legend-marker--border-radius,999px);\n flex: none;\n}";
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/chart-legend.ts
|
|
10
|
+
var ChartLegend = class ChartLegend extends ChartLinkedElement {
|
|
11
|
+
constructor(..._args) {
|
|
12
|
+
super(..._args);
|
|
13
|
+
this.items = [];
|
|
14
|
+
this.#handleLegendChange = (event) => {
|
|
15
|
+
this.items = event.detail.items;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
static {
|
|
19
|
+
this.styles = unsafeCSS(chart_legend_default);
|
|
20
|
+
}
|
|
21
|
+
#handleLegendChange;
|
|
22
|
+
chartConnected(chart) {
|
|
23
|
+
chart.registerLegendPresenter(this);
|
|
24
|
+
chart.addEventListener("legend-change", this.#handleLegendChange);
|
|
25
|
+
this.items = chart.getLegendItems();
|
|
26
|
+
}
|
|
27
|
+
chartDisconnected(chart) {
|
|
28
|
+
chart.removeEventListener("legend-change", this.#handleLegendChange);
|
|
29
|
+
chart.unregisterLegendPresenter(this);
|
|
30
|
+
this.items = [];
|
|
31
|
+
}
|
|
32
|
+
render() {
|
|
33
|
+
const content = this.renderLegend?.(this.items) ?? this.items.map((item) => html`
|
|
34
|
+
<button class="item" part="item" type="button" aria-pressed=${item.visible ? "true" : "false"} @click=${() => item.toggle()}>
|
|
35
|
+
${this.renderLegendItem?.(item) ?? html`
|
|
36
|
+
<span class="marker" part="marker" style="background:${item.color}"></span>
|
|
37
|
+
<span>${item.label}</span>
|
|
38
|
+
`}
|
|
39
|
+
</button>
|
|
40
|
+
`);
|
|
41
|
+
return html`<slot>${content}</slot>`;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
__decorate([property({ attribute: false })], ChartLegend.prototype, "renderLegend", void 0);
|
|
45
|
+
__decorate([property({ attribute: false })], ChartLegend.prototype, "renderLegendItem", void 0);
|
|
46
|
+
__decorate([state()], ChartLegend.prototype, "items", void 0);
|
|
47
|
+
ChartLegend = __decorate([customElement("c2-chart-legend")], ChartLegend);
|
|
48
|
+
//#endregion
|
|
49
|
+
export { ChartLegend as t };
|