@c2n/chart 0.0.7 → 0.0.8
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 +4 -3
- package/dist/candlestick-chart.js +139 -0
- package/dist/{chart-base-C4-1TyKq.js → chart-base-BwPwRgHt.js} +89 -10
- 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-tooltip-J-oC2DKZ.js +130 -0
- package/dist/chart-tooltip.js +2 -0
- package/dist/chart.js +10 -5
- package/dist/decorate-BkO8ArQg.js +9 -0
- package/dist/{echarts-chart-base-LogCQN10.js → echarts-chart-base-6JsZbB05.js} +2 -4
- package/dist/echarts-chart-base.js +1 -1
- package/dist/gauge-chart.js +103 -0
- package/dist/line-chart.js +4 -3
- package/dist/pie-chart.js +11 -2
- package/dist/scatter-chart.js +40 -0
- package/dist/sparkline.js +4 -4
- package/dist/{uplot-chart-base-BWr0zJE2.js → uplot-chart-base-CDHzOLwa.js} +21 -5
- package/dist/uplot-chart-base.js +1 -1
- package/dist/{uplot-paths-ChKohvIX.js → uplot-paths-BRtF33Wh.js} +1 -1
- package/package.json +26 -3
- package/react.d.ts +10 -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 +31 -1
- 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-tooltip.d.ts +42 -0
- package/types/src/chart-tooltip.d.ts.map +1 -0
- package/types/src/chart.d.ts +11 -1
- package/types/src/chart.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/gauge-chart.d.ts +52 -0
- package/types/src/gauge-chart.d.ts.map +1 -0
- package/types/src/pie-chart.d.ts +3 -0
- package/types/src/pie-chart.d.ts.map +1 -1
- package/types/src/scatter-chart.d.ts +41 -0
- package/types/src/scatter-chart.d.ts.map +1 -0
- package/vue.d.ts +81 -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-BRtF33Wh.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-CDHzOLwa.js";
|
|
4
|
+
import { t as barPaths } from "./uplot-paths-BRtF33Wh.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
|
|
@@ -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-6JsZbB05.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,6 +1,7 @@
|
|
|
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";
|
|
@@ -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
|
*
|
|
@@ -163,6 +168,8 @@ var ChartBase = class extends LitElement {
|
|
|
163
168
|
this.hiddenSeries = /* @__PURE__ */ new Set();
|
|
164
169
|
this.hoverContext = null;
|
|
165
170
|
this.engineFailed = false;
|
|
171
|
+
this.hasLinkedLegend = false;
|
|
172
|
+
this.hasLinkedTooltip = false;
|
|
166
173
|
this.themeController = new ChartThemeController(this, () => this.handleThemeChange());
|
|
167
174
|
this.frameBuilder = new ChartFrameBuilder();
|
|
168
175
|
this.#creating = false;
|
|
@@ -175,6 +182,9 @@ var ChartBase = class extends LitElement {
|
|
|
175
182
|
height: 0
|
|
176
183
|
};
|
|
177
184
|
this.#visible = false;
|
|
185
|
+
this.#tooltipContext = null;
|
|
186
|
+
this.#legendPresenters = /* @__PURE__ */ new Set();
|
|
187
|
+
this.#tooltipPresenters = /* @__PURE__ */ new Set();
|
|
178
188
|
this.#handleSeriesChange = (event) => {
|
|
179
189
|
event.stopPropagation();
|
|
180
190
|
this.#collectSeries();
|
|
@@ -194,6 +204,17 @@ var ChartBase = class extends LitElement {
|
|
|
194
204
|
#intersectionObserver;
|
|
195
205
|
#visible;
|
|
196
206
|
#unsubscribe;
|
|
207
|
+
#tooltipContext;
|
|
208
|
+
#legendPresenters;
|
|
209
|
+
#tooltipPresenters;
|
|
210
|
+
/** Restores interaction state after an engine instance is created or replaced. */
|
|
211
|
+
restoreVisibility(adapter) {
|
|
212
|
+
for (const index of this.hiddenSeries) adapter.setSeriesVisibility(index, false);
|
|
213
|
+
}
|
|
214
|
+
/** Whether changing chart data can change the legend model. Pie charts use row labels as entries. */
|
|
215
|
+
get legendDependsOnData() {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
197
218
|
connectedCallback() {
|
|
198
219
|
super.connectedCallback();
|
|
199
220
|
this.addEventListener(SERIES_CHANGE_EVENT, this.#handleSeriesChange);
|
|
@@ -251,6 +272,12 @@ var ChartBase = class extends LitElement {
|
|
|
251
272
|
super.updated(changed);
|
|
252
273
|
if (isServer) return;
|
|
253
274
|
this.#sync();
|
|
275
|
+
if (changed.has("tooltip") && this.tooltip === "none") {
|
|
276
|
+
this.#tooltipContext = null;
|
|
277
|
+
this.#setHover(null);
|
|
278
|
+
this.dispatchEvent(new CustomEvent("tooltip-change", { detail: null }));
|
|
279
|
+
}
|
|
280
|
+
if ([...changed.keys()].some((key) => key !== "hoverContext")) this.notifyLegendChange();
|
|
254
281
|
}
|
|
255
282
|
/**
|
|
256
283
|
* Replaces the data without going through the update queue. Equivalent to assigning `data`, but skips
|
|
@@ -281,6 +308,7 @@ var ChartBase = class extends LitElement {
|
|
|
281
308
|
else next.add(index);
|
|
282
309
|
this.hiddenSeries = next;
|
|
283
310
|
this.adapter?.setSeriesVisibility(index, visible);
|
|
311
|
+
this.notifyLegendChange();
|
|
284
312
|
const series = this.resolvedSeries[index];
|
|
285
313
|
if (!series) return;
|
|
286
314
|
this.dispatchEvent(new CustomEvent("series-toggle", { detail: {
|
|
@@ -290,6 +318,42 @@ var ChartBase = class extends LitElement {
|
|
|
290
318
|
hidden: [...next]
|
|
291
319
|
} }));
|
|
292
320
|
}
|
|
321
|
+
/** Current entries for a linked `c2-chart-legend`. */
|
|
322
|
+
getLegendItems() {
|
|
323
|
+
return this.legendItems();
|
|
324
|
+
}
|
|
325
|
+
/** Current hover model for a linked `c2-chart-tooltip`. */
|
|
326
|
+
getTooltipContext() {
|
|
327
|
+
return this.#tooltipContext;
|
|
328
|
+
}
|
|
329
|
+
/** Plot bounds used to place a linked floating tooltip in viewport coordinates. */
|
|
330
|
+
getPlotBounds() {
|
|
331
|
+
return (this.plotElement ?? this).getBoundingClientRect();
|
|
332
|
+
}
|
|
333
|
+
/** Registers an external legend and suppresses the built-in legend until it disconnects. */
|
|
334
|
+
registerLegendPresenter(presenter) {
|
|
335
|
+
this.#legendPresenters.add(presenter);
|
|
336
|
+
this.hasLinkedLegend = this.#legendPresenters.size > 0;
|
|
337
|
+
}
|
|
338
|
+
/** Removes an external legend registration. */
|
|
339
|
+
unregisterLegendPresenter(presenter) {
|
|
340
|
+
this.#legendPresenters.delete(presenter);
|
|
341
|
+
this.hasLinkedLegend = this.#legendPresenters.size > 0;
|
|
342
|
+
}
|
|
343
|
+
/** Registers an external tooltip and suppresses the built-in tooltip until it disconnects. */
|
|
344
|
+
registerTooltipPresenter(presenter) {
|
|
345
|
+
this.#tooltipPresenters.add(presenter);
|
|
346
|
+
this.hasLinkedTooltip = this.#tooltipPresenters.size > 0;
|
|
347
|
+
}
|
|
348
|
+
/** Removes an external tooltip registration. */
|
|
349
|
+
unregisterTooltipPresenter(presenter) {
|
|
350
|
+
this.#tooltipPresenters.delete(presenter);
|
|
351
|
+
this.hasLinkedTooltip = this.#tooltipPresenters.size > 0;
|
|
352
|
+
}
|
|
353
|
+
/** Notifies linked legends after a subclass changes a data-shaped legend such as pie slices. */
|
|
354
|
+
notifyLegendChange() {
|
|
355
|
+
this.dispatchEvent(new CustomEvent("legend-change", { detail: { items: this.getLegendItems() } }));
|
|
356
|
+
}
|
|
293
357
|
/** The series in draw order: `c2-chart-series` children when present, otherwise the `series` property. */
|
|
294
358
|
get resolvedSeries() {
|
|
295
359
|
if (this.seriesElements.length > 0) return this.seriesElements.filter((element) => !element.hidden).map((element) => element.toConfig());
|
|
@@ -359,13 +423,17 @@ var ChartBase = class extends LitElement {
|
|
|
359
423
|
await this.#createChart();
|
|
360
424
|
return;
|
|
361
425
|
}
|
|
426
|
+
let restoreAfterData = false;
|
|
362
427
|
if (this.#optionsDirty) {
|
|
363
428
|
this.#optionsDirty = false;
|
|
364
|
-
|
|
429
|
+
const mode = this.#optionsMode;
|
|
430
|
+
this.adapter.setOptions(this.buildOptions(this.buildContext()), mode);
|
|
365
431
|
this.#optionsMode = "merge";
|
|
432
|
+
restoreAfterData = mode === "replace";
|
|
366
433
|
this.#dataDirty = true;
|
|
367
434
|
}
|
|
368
435
|
if (this.#dataDirty) this.#flushData();
|
|
436
|
+
if (restoreAfterData) this.restoreVisibility(this.adapter);
|
|
369
437
|
}
|
|
370
438
|
async #createChart() {
|
|
371
439
|
if (this.#creating || !this.plotElement || !this.frame) return;
|
|
@@ -385,7 +453,7 @@ var ChartBase = class extends LitElement {
|
|
|
385
453
|
this.adapter = adapter;
|
|
386
454
|
this.#optionsDirty = false;
|
|
387
455
|
this.#dataDirty = false;
|
|
388
|
-
|
|
456
|
+
this.restoreVisibility(adapter);
|
|
389
457
|
this.setAttribute("data-chart-ready", "true");
|
|
390
458
|
this.dispatchEvent(new CustomEvent("chart-ready", { detail: { engine: this.engineName } }));
|
|
391
459
|
} catch (error) {
|
|
@@ -402,6 +470,7 @@ var ChartBase = class extends LitElement {
|
|
|
402
470
|
if (this.#appended > 0 && this.adapter.appendData) this.adapter.appendData(projected, this.#appended);
|
|
403
471
|
else this.adapter.setData(projected);
|
|
404
472
|
this.#appended = 0;
|
|
473
|
+
if (this.legendDependsOnData) this.notifyLegendChange();
|
|
405
474
|
}
|
|
406
475
|
#handleResize(entries) {
|
|
407
476
|
const rect = entries[entries.length - 1]?.contentRect;
|
|
@@ -451,12 +520,18 @@ var ChartBase = class extends LitElement {
|
|
|
451
520
|
} else this.#unsubscribe = () => controller.abort();
|
|
452
521
|
}
|
|
453
522
|
#handleHover(detail) {
|
|
454
|
-
if (!detail || !this.frame
|
|
523
|
+
if (!detail || !this.frame) {
|
|
524
|
+
this.#tooltipContext = null;
|
|
455
525
|
this.#setHover(null);
|
|
526
|
+
this.dispatchEvent(new CustomEvent("tooltip-change", { detail: null }));
|
|
527
|
+
this.dispatchEvent(new CustomEvent("point-hover", { detail: null }));
|
|
456
528
|
return;
|
|
457
529
|
}
|
|
458
530
|
const context = this.#tooltipContextAt(detail);
|
|
459
|
-
this
|
|
531
|
+
const tooltipContext = this.tooltip === "none" ? null : context;
|
|
532
|
+
this.#tooltipContext = tooltipContext;
|
|
533
|
+
this.#setHover(this.hasLinkedTooltip ? null : tooltipContext);
|
|
534
|
+
this.dispatchEvent(new CustomEvent("tooltip-change", { detail: tooltipContext }));
|
|
460
535
|
const point = this.#pointAt(detail.index, detail.seriesIndex);
|
|
461
536
|
if (point) this.dispatchEvent(new CustomEvent("point-hover", { detail: point }));
|
|
462
537
|
}
|
|
@@ -468,7 +543,7 @@ var ChartBase = class extends LitElement {
|
|
|
468
543
|
#setHover(context) {
|
|
469
544
|
const previous = this.hoverContext;
|
|
470
545
|
if (context && this.tooltipElement) this.tooltipElement.style.transform = `translate(${context.px}px, ${context.py}px)`;
|
|
471
|
-
if (previous && context && previous.index === context.index) return;
|
|
546
|
+
if (previous && context && previous.index === context.index && (this.tooltip !== "item" || previous.entries[0]?.seriesIndex === context.entries[0]?.seriesIndex)) return;
|
|
472
547
|
this.hoverContext = context;
|
|
473
548
|
}
|
|
474
549
|
#tooltipContextAt(detail) {
|
|
@@ -541,8 +616,8 @@ var ChartBase = class extends LitElement {
|
|
|
541
616
|
return new Intl.NumberFormat(this.locale, { maximumFractionDigits: 2 }).format(value);
|
|
542
617
|
}
|
|
543
618
|
render() {
|
|
544
|
-
const legendBefore = this.legend === "top" || this.legend === "start";
|
|
545
|
-
const legendAfter = this.legend === "bottom" || this.legend === "end";
|
|
619
|
+
const legendBefore = !this.hasLinkedLegend && (this.legend === "top" || this.legend === "start");
|
|
620
|
+
const legendAfter = !this.hasLinkedLegend && (this.legend === "bottom" || this.legend === "end");
|
|
546
621
|
const side = this.legend === "start" || this.legend === "end";
|
|
547
622
|
return html`
|
|
548
623
|
<div class="frame ${side ? "frame--side" : ""}" part="frame">
|
|
@@ -551,7 +626,9 @@ var ChartBase = class extends LitElement {
|
|
|
551
626
|
<!-- No bindings inside: Lit never patches this node, so the engine's canvas survives updates. -->
|
|
552
627
|
<div class="plot" part="plot"></div>
|
|
553
628
|
<div class="overlay" part="overlay">
|
|
554
|
-
<div class="tooltip" part="tooltip" role="tooltip" ?hidden=${!this.hoverContext || this.tooltip === "none"
|
|
629
|
+
<div class="tooltip" part="tooltip" role="tooltip" ?hidden=${!this.hoverContext || this.tooltip === "none" || this.hasLinkedTooltip}>
|
|
630
|
+
${this.renderTooltipBody()}
|
|
631
|
+
</div>
|
|
555
632
|
<div class="actions" part="actions"><slot name="actions"></slot></div>
|
|
556
633
|
</div>
|
|
557
634
|
${this.renderState()}
|
|
@@ -662,5 +739,7 @@ __decorate([state()], ChartBase.prototype, "seriesElements", void 0);
|
|
|
662
739
|
__decorate([state()], ChartBase.prototype, "hiddenSeries", void 0);
|
|
663
740
|
__decorate([state()], ChartBase.prototype, "hoverContext", void 0);
|
|
664
741
|
__decorate([state()], ChartBase.prototype, "engineFailed", void 0);
|
|
742
|
+
__decorate([state()], ChartBase.prototype, "hasLinkedLegend", void 0);
|
|
743
|
+
__decorate([state()], ChartBase.prototype, "hasLinkedTooltip", void 0);
|
|
665
744
|
//#endregion
|
|
666
745
|
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-BwPwRgHt.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 };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { t as __decorate } from "./decorate-BkO8ArQg.js";
|
|
2
|
+
import { property } from "lit/decorators.js";
|
|
3
|
+
import { LitElement, isServer } from "lit";
|
|
4
|
+
//#region src/chart-link.ts
|
|
5
|
+
/** Resolves and follows a chart by id within the companion's document or shadow root. */
|
|
6
|
+
var ChartLinkedElement = class ChartLinkedElement extends LitElement {
|
|
7
|
+
constructor(..._args) {
|
|
8
|
+
super(..._args);
|
|
9
|
+
this.for = "";
|
|
10
|
+
}
|
|
11
|
+
static #watchers = /* @__PURE__ */ new WeakMap();
|
|
12
|
+
#watchedRoot;
|
|
13
|
+
connectedCallback() {
|
|
14
|
+
super.connectedCallback();
|
|
15
|
+
if (isServer) return;
|
|
16
|
+
queueMicrotask(() => this.#resolveChart());
|
|
17
|
+
const root = this.getRootNode();
|
|
18
|
+
const observed = root instanceof Document ? root.documentElement : root;
|
|
19
|
+
let watcher = ChartLinkedElement.#watchers.get(root);
|
|
20
|
+
if (!watcher) {
|
|
21
|
+
const elements = /* @__PURE__ */ new Set();
|
|
22
|
+
const observer = new MutationObserver(() => elements.forEach((element) => element.#resolveChart()));
|
|
23
|
+
observer.observe(observed, {
|
|
24
|
+
childList: true,
|
|
25
|
+
subtree: true,
|
|
26
|
+
attributes: true,
|
|
27
|
+
attributeFilter: ["id"]
|
|
28
|
+
});
|
|
29
|
+
watcher = {
|
|
30
|
+
elements,
|
|
31
|
+
observer
|
|
32
|
+
};
|
|
33
|
+
ChartLinkedElement.#watchers.set(root, watcher);
|
|
34
|
+
}
|
|
35
|
+
watcher.elements.add(this);
|
|
36
|
+
this.#watchedRoot = root;
|
|
37
|
+
}
|
|
38
|
+
disconnectedCallback() {
|
|
39
|
+
super.disconnectedCallback();
|
|
40
|
+
if (this.#watchedRoot) {
|
|
41
|
+
const watcher = ChartLinkedElement.#watchers.get(this.#watchedRoot);
|
|
42
|
+
watcher?.elements.delete(this);
|
|
43
|
+
if (watcher?.elements.size === 0) {
|
|
44
|
+
watcher.observer.disconnect();
|
|
45
|
+
ChartLinkedElement.#watchers.delete(this.#watchedRoot);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
this.#watchedRoot = void 0;
|
|
49
|
+
this.#setChart(void 0);
|
|
50
|
+
}
|
|
51
|
+
willUpdate(changed) {
|
|
52
|
+
if (changed.has("for") && !isServer) queueMicrotask(() => this.#resolveChart());
|
|
53
|
+
}
|
|
54
|
+
chartConnected(_chart) {}
|
|
55
|
+
chartDisconnected(_chart) {}
|
|
56
|
+
#resolveChart() {
|
|
57
|
+
const root = this.getRootNode();
|
|
58
|
+
const candidate = this.for && (root instanceof Document || root instanceof ShadowRoot) ? root.getElementById(this.for) : null;
|
|
59
|
+
const chart = candidate && "getLegendItems" in candidate && "getTooltipContext" in candidate && "getPlotBounds" in candidate && "registerLegendPresenter" in candidate && "unregisterLegendPresenter" in candidate && "registerTooltipPresenter" in candidate && "unregisterTooltipPresenter" in candidate ? candidate : void 0;
|
|
60
|
+
this.#setChart(chart);
|
|
61
|
+
}
|
|
62
|
+
#setChart(chart) {
|
|
63
|
+
if (chart === this.chart) return;
|
|
64
|
+
if (this.chart) this.chartDisconnected(this.chart);
|
|
65
|
+
this.chart = chart;
|
|
66
|
+
if (chart) this.chartConnected(chart);
|
|
67
|
+
this.requestUpdate();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
__decorate([property({
|
|
71
|
+
type: String,
|
|
72
|
+
reflect: true
|
|
73
|
+
})], ChartLinkedElement.prototype, "for", void 0);
|
|
74
|
+
//#endregion
|
|
75
|
+
export { ChartLinkedElement as t };
|