@wil-works/evoke-charts 0.1.1 → 0.2.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/README.md +7 -5
- package/dist/evoke-charts.css +37 -35
- package/dist/index.mjs +323 -76
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
`@wil-works/evoke-charts` — 零依赖 Canvas 自绘图表库(Vue 3)。Evoke 生态的独立图表包,不依赖任何第三方图表引擎,也不依赖 Evoke 其他组件库。
|
|
4
4
|
|
|
5
|
+
**在线文档**:[evoke-charts.wil-works.com](https://evoke-charts.wil-works.com) — 全部图表类型、完整配置项与在线示例。
|
|
6
|
+
|
|
5
7
|
## 特性
|
|
6
8
|
|
|
7
9
|
- **20+ 图表类型**:折线 / 面积 / 柱状 / 堆叠柱 / 条形 / 饼图 / 环形 / 玫瑰图 / 散点(含趋势线)/ 雷达 / 漏斗 / 仪表盘 / 热力图 / K 线 / 子弹图 / 直方图 / 矩形树图 / 旭日图 / 瀑布图 / 箱线图 / 混合图 / 迷你图
|
|
@@ -56,9 +58,9 @@ const options = {
|
|
|
56
58
|
|
|
57
59
|
## 主题接入
|
|
58
60
|
|
|
59
|
-
图表颜色按以下 `--ev-*` 令牌实时读取(`document.documentElement`
|
|
61
|
+
图表颜色按以下 `--ev-*` 令牌实时读取(`document.documentElement` 上取值),未定义的槽位逐槽回落到内置成套色板:
|
|
60
62
|
|
|
61
|
-
- 系列色:`--ev-color-
|
|
63
|
+
- 系列色:`--ev-color-series-1` … `--ev-color-series-8`(数据系列专用色板,与状态语义色解耦;槽 1 未定义时回读 `--ev-color-primary`,主题跟随不受影响)
|
|
62
64
|
- 表面:`--ev-bg-color`、`--ev-bg-color-overlay`、`--ev-app-card-border`
|
|
63
65
|
- 文字:`--ev-text-color-primary / -secondary / -danger`
|
|
64
66
|
- 边线:`--ev-border-color / -light / -dark`
|
|
@@ -67,12 +69,12 @@ const options = {
|
|
|
67
69
|
|
|
68
70
|
暗色跟随约定:在 `html.dark` 选择器下重映射上述令牌(组件内部监听 `<html>` 的 `class` 变化自动重绘)。
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`evoke-business-ui` 依赖本包并以 `EbChart`(模板 `<eb-chart>`)提供同一组件,其样式包内置了 `--eb-*` 令牌到 `--ev-*`(本包读取面)的映射,两库同用时图表自动跟随其主题、暗色与运行时换肤(其换肤时会派发 `ev-theme-change` 事件),无需额外配置。
|
|
72
|
+
图表引擎对宿主保持中立:不为任何组件库做特殊配置;组件库或站点需要个性化时,走通用扩展接口——槽位令牌、`padding` / `theme` / `animation` 配置,以及 `applySeriesPalette` 配色方案工具。
|
|
73
73
|
|
|
74
74
|
## 组件 API(摘要)
|
|
75
75
|
|
|
76
|
+
以下为常用项摘要,各图表类型的独立文档页与完整配置项见[在线文档](https://evoke-charts.wil-works.com/chart)。
|
|
77
|
+
|
|
76
78
|
### Props
|
|
77
79
|
|
|
78
80
|
| 名称 | 类型 | 默认值 | 说明 |
|
package/dist/evoke-charts.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
.ev-chart[data-v-
|
|
2
|
+
.ev-chart[data-v-a07b68e8] {
|
|
3
3
|
position: relative;
|
|
4
4
|
overflow: hidden;
|
|
5
5
|
border-radius: 8px;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/* 关键:canvas 必须能接收鼠标事件 */
|
|
11
|
-
.ev-chart__canvas[data-v-
|
|
11
|
+
.ev-chart__canvas[data-v-a07b68e8] {
|
|
12
12
|
position: absolute;
|
|
13
13
|
top: 0;
|
|
14
14
|
left: 0;
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
/* 触摸:纵向留给页面滚动,横向拖拽由图表接管(dataZoom 平移) */
|
|
19
19
|
touch-action: pan-y;
|
|
20
20
|
}
|
|
21
|
-
.ev-chart__tooltip[data-v-
|
|
21
|
+
.ev-chart__tooltip[data-v-a07b68e8] {
|
|
22
22
|
position: absolute;
|
|
23
23
|
pointer-events: none;
|
|
24
24
|
z-index: 100;
|
|
25
|
-
padding:
|
|
25
|
+
padding: 9px 12px;
|
|
26
26
|
background: var(--ev-bg-color-overlay, #ffffff);
|
|
27
27
|
border: 1px solid var(--ev-border-color, #e2e8f0);
|
|
28
|
-
border-radius:
|
|
29
|
-
box-shadow: 0
|
|
28
|
+
border-radius: 10px;
|
|
29
|
+
box-shadow: 0 6px 20px rgba(15, 23, 42, 0.12);
|
|
30
30
|
font-size: 13px;
|
|
31
31
|
line-height: 1.6;
|
|
32
32
|
white-space: nowrap;
|
|
@@ -39,47 +39,47 @@
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/* tooltip 淡入淡出(仅 opacity,位置由 left/top transition 负责) */
|
|
42
|
-
.ev-chart-tooltip-enter-active[data-v-
|
|
43
|
-
.ev-chart-tooltip-leave-active[data-v-
|
|
42
|
+
.ev-chart-tooltip-enter-active[data-v-a07b68e8],
|
|
43
|
+
.ev-chart-tooltip-leave-active[data-v-a07b68e8] {
|
|
44
44
|
transition: opacity 0.18s ease;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/* 进入期间禁用位置 transition,避免首次出现时从旧位置滑入 */
|
|
48
|
-
.ev-chart-tooltip-enter-active[data-v-
|
|
48
|
+
.ev-chart-tooltip-enter-active[data-v-a07b68e8] {
|
|
49
49
|
transition:
|
|
50
50
|
opacity 0.18s ease,
|
|
51
51
|
left 0s,
|
|
52
52
|
top 0s;
|
|
53
53
|
}
|
|
54
|
-
.ev-chart-tooltip-enter-from[data-v-
|
|
55
|
-
.ev-chart-tooltip-leave-to[data-v-
|
|
54
|
+
.ev-chart-tooltip-enter-from[data-v-a07b68e8],
|
|
55
|
+
.ev-chart-tooltip-leave-to[data-v-a07b68e8] {
|
|
56
56
|
opacity: 0;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/* v-html 注入的内容不带 scoped 属性,需用 :deep() 穿透 */
|
|
60
|
-
[data-v-
|
|
60
|
+
[data-v-a07b68e8] .ev-chart__tooltip-title {
|
|
61
61
|
color: var(--ev-text-color-secondary, #6b7280);
|
|
62
62
|
font-size: 12px;
|
|
63
63
|
margin-bottom: 6px;
|
|
64
64
|
font-weight: 500;
|
|
65
65
|
}
|
|
66
|
-
[data-v-
|
|
66
|
+
[data-v-a07b68e8] .ev-chart__tooltip-item {
|
|
67
67
|
display: flex;
|
|
68
68
|
align-items: center;
|
|
69
69
|
gap: 8px;
|
|
70
70
|
padding: 2px 0;
|
|
71
71
|
}
|
|
72
|
-
[data-v-
|
|
72
|
+
[data-v-a07b68e8] .ev-chart__tooltip-dot {
|
|
73
73
|
width: 8px;
|
|
74
74
|
height: 8px;
|
|
75
75
|
border-radius: 50%;
|
|
76
76
|
flex-shrink: 0;
|
|
77
77
|
display: inline-block;
|
|
78
78
|
}
|
|
79
|
-
[data-v-
|
|
79
|
+
[data-v-a07b68e8] .ev-chart__tooltip-name {
|
|
80
80
|
color: var(--ev-text-color-primary, #111827);
|
|
81
81
|
}
|
|
82
|
-
[data-v-
|
|
82
|
+
[data-v-a07b68e8] .ev-chart__tooltip-value {
|
|
83
83
|
color: var(--ev-text-color-primary, #111827);
|
|
84
84
|
font-weight: 600;
|
|
85
85
|
margin-left: auto;
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/* ══════ 空数据占位 ══════ */
|
|
91
|
-
.ev-chart__empty[data-v-
|
|
91
|
+
.ev-chart__empty[data-v-a07b68e8] {
|
|
92
92
|
position: absolute;
|
|
93
93
|
inset: 0;
|
|
94
94
|
display: flex;
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
gap: 14px;
|
|
101
101
|
user-select: none;
|
|
102
102
|
}
|
|
103
|
-
.ev-chart__empty-icon-wrap[data-v-
|
|
103
|
+
.ev-chart__empty-icon-wrap[data-v-a07b68e8] {
|
|
104
104
|
display: flex;
|
|
105
105
|
align-items: center;
|
|
106
106
|
justify-content: center;
|
|
@@ -109,19 +109,19 @@
|
|
|
109
109
|
border-radius: 16px;
|
|
110
110
|
background: var(--ev-fill-color-light, #f5f7fa);
|
|
111
111
|
}
|
|
112
|
-
.ev-chart__empty-img[data-v-
|
|
112
|
+
.ev-chart__empty-img[data-v-a07b68e8] {
|
|
113
113
|
width: 32px;
|
|
114
114
|
height: 32px;
|
|
115
115
|
opacity: 0.4;
|
|
116
116
|
}
|
|
117
|
-
.ev-chart__empty-text[data-v-
|
|
117
|
+
.ev-chart__empty-text[data-v-a07b68e8] {
|
|
118
118
|
letter-spacing: 0.04em;
|
|
119
119
|
font-weight: 500;
|
|
120
120
|
font-size: 13px;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/* ══════ 加载中骨架 ══════ */
|
|
124
|
-
.ev-chart__loading[data-v-
|
|
124
|
+
.ev-chart__loading[data-v-a07b68e8] {
|
|
125
125
|
position: absolute;
|
|
126
126
|
inset: 0;
|
|
127
127
|
display: flex;
|
|
@@ -129,20 +129,20 @@
|
|
|
129
129
|
justify-content: center;
|
|
130
130
|
background: var(--ev-bg-color, #ffffff);
|
|
131
131
|
}
|
|
132
|
-
.ev-chart__loading-bars[data-v-
|
|
132
|
+
.ev-chart__loading-bars[data-v-a07b68e8] {
|
|
133
133
|
display: flex;
|
|
134
134
|
align-items: flex-end;
|
|
135
135
|
gap: 6px;
|
|
136
136
|
height: 60px;
|
|
137
137
|
}
|
|
138
|
-
.ev-chart__loading-bar[data-v-
|
|
138
|
+
.ev-chart__loading-bar[data-v-a07b68e8] {
|
|
139
139
|
width: 10px;
|
|
140
140
|
height: 40px;
|
|
141
141
|
border-radius: 4px;
|
|
142
142
|
background: var(--ev-border-color, #e2e8f0);
|
|
143
|
-
animation: ev-chart-shimmer-
|
|
143
|
+
animation: ev-chart-shimmer-a07b68e8 1.2s ease-in-out infinite;
|
|
144
144
|
}
|
|
145
|
-
@keyframes ev-chart-shimmer-
|
|
145
|
+
@keyframes ev-chart-shimmer-a07b68e8 {
|
|
146
146
|
0%,
|
|
147
147
|
100% {
|
|
148
148
|
transform: scaleY(0.4);
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/* ══════ 错误边界 ══════ */
|
|
158
|
-
.ev-chart__error[data-v-
|
|
158
|
+
.ev-chart__error[data-v-a07b68e8] {
|
|
159
159
|
position: absolute;
|
|
160
160
|
inset: 0;
|
|
161
161
|
display: flex;
|
|
@@ -168,11 +168,13 @@
|
|
|
168
168
|
padding: 20px;
|
|
169
169
|
text-align: center;
|
|
170
170
|
}
|
|
171
|
-
.ev-chart__error-icon[data-v-
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
.ev-chart__error-icon[data-v-a07b68e8] {
|
|
172
|
+
display: flex;
|
|
173
|
+
align-items: center;
|
|
174
|
+
justify-content: center;
|
|
175
|
+
font-size: 30px;
|
|
174
176
|
}
|
|
175
|
-
.ev-chart__error-text[data-v-
|
|
177
|
+
.ev-chart__error-text[data-v-a07b68e8] {
|
|
176
178
|
font-size: 12px;
|
|
177
179
|
line-height: 1.5;
|
|
178
180
|
max-width: 80%;
|
|
@@ -180,17 +182,17 @@
|
|
|
180
182
|
}
|
|
181
183
|
|
|
182
184
|
/* ══════ 进入/离开淡入淡出 ══════ */
|
|
183
|
-
.ev-chart-fade-enter-active[data-v-
|
|
184
|
-
.ev-chart-fade-leave-active[data-v-
|
|
185
|
+
.ev-chart-fade-enter-active[data-v-a07b68e8],
|
|
186
|
+
.ev-chart-fade-leave-active[data-v-a07b68e8] {
|
|
185
187
|
transition: opacity 0.2s ease;
|
|
186
188
|
}
|
|
187
|
-
.ev-chart-fade-enter-from[data-v-
|
|
188
|
-
.ev-chart-fade-leave-to[data-v-
|
|
189
|
+
.ev-chart-fade-enter-from[data-v-a07b68e8],
|
|
190
|
+
.ev-chart-fade-leave-to[data-v-a07b68e8] {
|
|
189
191
|
opacity: 0;
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
/* ══════ 无障碍:sr-only ══════ */
|
|
193
|
-
.ev-chart__sr-only[data-v-
|
|
195
|
+
.ev-chart__sr-only[data-v-a07b68e8] {
|
|
194
196
|
position: absolute;
|
|
195
197
|
width: 1px;
|
|
196
198
|
height: 1px;
|
package/dist/index.mjs
CHANGED
|
@@ -16,18 +16,20 @@ var DEFAULT_I18N_ZH = {
|
|
|
16
16
|
noData: "暂无数据"
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* 系列色令牌槽位 — 每槽按顺序尝试读取专用数据色令牌(--ev-color-series-N),
|
|
20
|
+
* 槽 1 回退 --ev-color-primary(品牌主色跟随);
|
|
21
|
+
* 任一槽无令牌时该槽回落内置成套色板(与语义状态色解耦的数据色板)。
|
|
20
22
|
* CHART_COLORS 仅作为 SSR / 无 DOM 环境的静态兜底。
|
|
21
23
|
*/
|
|
22
|
-
var
|
|
23
|
-
"--ev-color-primary",
|
|
24
|
-
"--ev-color-
|
|
25
|
-
"--ev-color-
|
|
26
|
-
"--ev-color-
|
|
27
|
-
"--ev-color-
|
|
28
|
-
"--ev-color-
|
|
29
|
-
"--ev-color-
|
|
30
|
-
"--ev-color-
|
|
24
|
+
var SERIES_COLOR_SLOTS = [
|
|
25
|
+
["--ev-color-series-1", "--ev-color-primary"],
|
|
26
|
+
["--ev-color-series-2"],
|
|
27
|
+
["--ev-color-series-3"],
|
|
28
|
+
["--ev-color-series-4"],
|
|
29
|
+
["--ev-color-series-5"],
|
|
30
|
+
["--ev-color-series-6"],
|
|
31
|
+
["--ev-color-series-7"],
|
|
32
|
+
["--ev-color-series-8"]
|
|
31
33
|
];
|
|
32
34
|
function readChartToken(name, fallback) {
|
|
33
35
|
try {
|
|
@@ -38,24 +40,29 @@ function readChartToken(name, fallback) {
|
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
function getSeriesColors(fallbackColors) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
return SERIES_COLOR_SLOTS.map((tokens, i) => {
|
|
44
|
+
for (const t of tokens) {
|
|
45
|
+
const v = readChartToken(t, "");
|
|
46
|
+
if (v) return v;
|
|
47
|
+
}
|
|
48
|
+
return fallbackColors[i];
|
|
49
|
+
});
|
|
43
50
|
}
|
|
44
51
|
var CHART_COLORS = {
|
|
45
52
|
/**
|
|
46
|
-
* 主色板 —
|
|
47
|
-
*
|
|
48
|
-
*
|
|
53
|
+
* 主色板 — 品牌蓝锚定的成套数据色板(与语义状态色解耦):
|
|
54
|
+
* 色相按取色顺序相邻差 ≥ 30°,明度带 44–68,青绿/金黄/天蓝/珊瑚/紫/石板
|
|
55
|
+
* 取材 AntV 经典系并经品牌蓝调协调;暗色板同色相提亮。
|
|
49
56
|
*/
|
|
50
57
|
primary: [
|
|
51
58
|
"#175DFF",
|
|
52
|
-
"#
|
|
53
|
-
"#
|
|
54
|
-
"#
|
|
55
|
-
"#
|
|
56
|
-
"#
|
|
57
|
-
"#
|
|
58
|
-
"#
|
|
59
|
+
"#5AD8A6",
|
|
60
|
+
"#F6BD16",
|
|
61
|
+
"#6DC8EC",
|
|
62
|
+
"#E8684A",
|
|
63
|
+
"#9270CA",
|
|
64
|
+
"#FF9D4D",
|
|
65
|
+
"#5D7092"
|
|
59
66
|
],
|
|
60
67
|
/** 浅色系列 — primary 的 light-9/light-8/light-7 阶梯 */
|
|
61
68
|
light: [
|
|
@@ -63,16 +70,16 @@ var CHART_COLORS = {
|
|
|
63
70
|
"#d0e1ff",
|
|
64
71
|
"#b9d2ff"
|
|
65
72
|
],
|
|
66
|
-
/** 暗色模式系列 —
|
|
73
|
+
/** 暗色模式系列 — 同色相提亮(对齐 evoke 暗色主色) */
|
|
67
74
|
dark: [
|
|
68
75
|
"#4d8bff",
|
|
69
|
-
"#
|
|
70
|
-
"#
|
|
71
|
-
"#
|
|
72
|
-
"#
|
|
73
|
-
"#
|
|
74
|
-
"#
|
|
75
|
-
"#
|
|
76
|
+
"#5ad8a6",
|
|
77
|
+
"#f6bd16",
|
|
78
|
+
"#6dc8ec",
|
|
79
|
+
"#f08568",
|
|
80
|
+
"#a585e8",
|
|
81
|
+
"#ff9d4d",
|
|
82
|
+
"#8da3bf"
|
|
76
83
|
],
|
|
77
84
|
/** 热力图默认色阶(天亮蓝由浅到深) */
|
|
78
85
|
heatmapScale: [
|
|
@@ -292,6 +299,42 @@ function hasLegendContent(options) {
|
|
|
292
299
|
function titleBlockHeight(options) {
|
|
293
300
|
return (options.title ? 26 : 0) + (options.subtitle ? 18 : 0);
|
|
294
301
|
}
|
|
302
|
+
/** options.padding 覆写:数字(四边一致)或 { top, right, bottom, left }(未提供的边回落默认值) */
|
|
303
|
+
function resolveUserPadding(options) {
|
|
304
|
+
const p = options.padding;
|
|
305
|
+
if (!p) return {};
|
|
306
|
+
if (typeof p === "number") return {
|
|
307
|
+
top: p,
|
|
308
|
+
right: p,
|
|
309
|
+
bottom: p,
|
|
310
|
+
left: p
|
|
311
|
+
};
|
|
312
|
+
return p;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* y 轴左侧留白按刻度标签实测宽度自适应(替代固定 65):
|
|
316
|
+
* 取数据范围 → nice 刻度值 → 逐个测宽,留 26px(10 间隙 + 刻度短横 + 余量)。
|
|
317
|
+
* 对数轴/自定义 formatter 场景不可预估时回落 65;结果夹在 [40, 140]。
|
|
318
|
+
*/
|
|
319
|
+
function estimateYAxisLeft(options) {
|
|
320
|
+
const axisConfig = options.yAxis || {};
|
|
321
|
+
if (axisConfig.type === "log") return 65;
|
|
322
|
+
try {
|
|
323
|
+
const range = calculateRange(options, /* @__PURE__ */ new Set(), false);
|
|
324
|
+
const min = axisConfig.min ?? range?.min;
|
|
325
|
+
const max = axisConfig.max ?? range?.max;
|
|
326
|
+
if (!Number.isFinite(min) || !Number.isFinite(max)) return 65;
|
|
327
|
+
const { tickValues } = resolveTickExtendedRange(min, max, axisConfig.ticks || 5);
|
|
328
|
+
const maxW = tickValues.reduce((m, t) => {
|
|
329
|
+
const label = axisConfig.formatter ? axisConfig.formatter(t) : Number.isInteger(t) ? t.toString() : parseFloat(t.toFixed(6)).toString();
|
|
330
|
+
return Math.max(m, estimateTextWidth(String(label), 12));
|
|
331
|
+
}, 0);
|
|
332
|
+
if (!maxW) return 65;
|
|
333
|
+
return Math.max(40, Math.min(140, Math.round(maxW + 26)));
|
|
334
|
+
} catch {
|
|
335
|
+
return 65;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
295
338
|
function getPadding(options, containerWidth = 600) {
|
|
296
339
|
const legend = options.legend || {};
|
|
297
340
|
const legendPosition = legend.show !== false && hasLegendContent(options) ? legend.position || "bottom" : "none";
|
|
@@ -311,12 +354,15 @@ function getPadding(options, containerWidth = 600) {
|
|
|
311
354
|
const zoom = options.dataZoom;
|
|
312
355
|
const hasDataZoom = !!zoom?.enabled && hasXAxis;
|
|
313
356
|
const zoomHeight = (zoom?.height || 40) + 14;
|
|
314
|
-
if (options.type === "sparkline")
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
357
|
+
if (options.type === "sparkline") {
|
|
358
|
+
const sparkPad = resolveUserPadding(options);
|
|
359
|
+
return {
|
|
360
|
+
top: sparkPad.top ?? (options.title ? 32 : 6),
|
|
361
|
+
right: sparkPad.right ?? 6,
|
|
362
|
+
bottom: sparkPad.bottom ?? 6,
|
|
363
|
+
left: sparkPad.left ?? 6
|
|
364
|
+
};
|
|
365
|
+
}
|
|
320
366
|
const legendRowsExtra = (() => {
|
|
321
367
|
if (legendPosition !== "top" && legendPosition !== "bottom") return 0;
|
|
322
368
|
return (estimateLegendRows(options, containerWidth) - 1) * 20;
|
|
@@ -335,13 +381,14 @@ function getPadding(options, containerWidth = 600) {
|
|
|
335
381
|
"bin",
|
|
336
382
|
"sunburst"
|
|
337
383
|
].includes(options.type)) {
|
|
338
|
-
|
|
339
|
-
let
|
|
340
|
-
let
|
|
341
|
-
let
|
|
384
|
+
const pad = resolveUserPadding(options);
|
|
385
|
+
let top2 = (pad.top ?? 18) + titleBlockHeight(options);
|
|
386
|
+
let bottom2 = pad.bottom ?? 18;
|
|
387
|
+
let left2 = pad.left ?? 20;
|
|
388
|
+
let right2 = pad.right ?? 20;
|
|
342
389
|
if (options.type === "heatmap") {
|
|
343
390
|
const maxYLabel = Array.from(new Set((options.heatmapData || []).map((d) => d.y))).reduce((m, s) => Math.max(m, estimateTextWidth(s, 11)), 0);
|
|
344
|
-
left2 = Math.min(130, Math.max(24, maxYLabel + 14));
|
|
391
|
+
left2 = pad.left ?? Math.min(130, Math.max(24, maxYLabel + 14));
|
|
345
392
|
bottom2 = Math.max(bottom2, 26);
|
|
346
393
|
if (options.heatmap?.colorBar === true || options.heatmapColorBar === true) right2 += 44;
|
|
347
394
|
}
|
|
@@ -350,7 +397,7 @@ function getPadding(options, containerWidth = 600) {
|
|
|
350
397
|
(options.series || []).forEach((s) => allValues.push(...s.data.filter((v) => !isMissingValue(v))));
|
|
351
398
|
const maxFreq = allValues.length;
|
|
352
399
|
const tickLabelWidth = String(maxFreq).length * 7;
|
|
353
|
-
left2 = Math.min(90, Math.max(36, tickLabelWidth + 16));
|
|
400
|
+
left2 = pad.left ?? Math.min(90, Math.max(36, tickLabelWidth + 16));
|
|
354
401
|
bottom2 = Math.max(bottom2, 30);
|
|
355
402
|
}
|
|
356
403
|
if (options.type === "bullet") {
|
|
@@ -371,21 +418,43 @@ function getPadding(options, containerWidth = 600) {
|
|
|
371
418
|
left: left2
|
|
372
419
|
};
|
|
373
420
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
let
|
|
377
|
-
let
|
|
421
|
+
const pad = resolveUserPadding(options);
|
|
422
|
+
const xAxisHidden = options.xAxis?.show === false;
|
|
423
|
+
let top = (pad.top ?? 18) + titleBlockHeight(options) + (legendPosition === "top" ? LEGEND_BAND + legendRowsExtra : 0);
|
|
424
|
+
let bottom = (pad.bottom ?? (xAxisHidden ? 12 : 46)) + (legendPosition === "bottom" ? LEGEND_BAND + legendRowsExtra : 0);
|
|
425
|
+
let left = pad.left ?? estimateYAxisLeft(options);
|
|
426
|
+
let right = pad.right ?? (hasYAxisRight ? 65 : 24);
|
|
378
427
|
if (legendPosition === "left") left += 80;
|
|
379
428
|
if (legendPosition === "right") right += 80;
|
|
380
|
-
if (hasXAxis && hasXAxisTitle)
|
|
429
|
+
if (hasXAxis && hasXAxisTitle) return {
|
|
430
|
+
top,
|
|
431
|
+
right,
|
|
432
|
+
bottom: bottom + 15,
|
|
433
|
+
left
|
|
434
|
+
};
|
|
381
435
|
const xRotate = options.xAxis?.rotate || 0;
|
|
382
436
|
if (hasXAxis && xRotate) {
|
|
383
437
|
const maxLabelWidth = (options.labels || []).reduce((m, s) => Math.max(m, estimateTextWidth(String(s), 12)), 0);
|
|
384
|
-
|
|
438
|
+
return {
|
|
439
|
+
top,
|
|
440
|
+
right,
|
|
441
|
+
bottom: bottom + Math.min(90, 12 + maxLabelWidth * Math.sin(Math.min(60, Math.abs(xRotate)) * Math.PI / 180)),
|
|
442
|
+
left
|
|
443
|
+
};
|
|
385
444
|
}
|
|
386
445
|
if (hasDataZoom) {
|
|
387
|
-
if ((zoom?.position || "bottom") === "top")
|
|
388
|
-
|
|
446
|
+
if ((zoom?.position || "bottom") === "top") return {
|
|
447
|
+
top: top + zoomHeight,
|
|
448
|
+
right,
|
|
449
|
+
bottom,
|
|
450
|
+
left
|
|
451
|
+
};
|
|
452
|
+
return {
|
|
453
|
+
top,
|
|
454
|
+
right,
|
|
455
|
+
bottom: bottom + zoomHeight,
|
|
456
|
+
left
|
|
457
|
+
};
|
|
389
458
|
}
|
|
390
459
|
return {
|
|
391
460
|
top,
|
|
@@ -774,6 +843,14 @@ function renderYAxis(ctx, side = "left", dataRange) {
|
|
|
774
843
|
else label = Number.isInteger(tick) ? tick.toString() : parseFloat(tick.toFixed(6)).toString();
|
|
775
844
|
label = truncateLabel(canvasCtx, label, maxLabelWidth);
|
|
776
845
|
canvasCtx.fillText(label, xPos, y);
|
|
846
|
+
if (side === "left") {
|
|
847
|
+
canvasCtx.strokeStyle = theme.gridColor;
|
|
848
|
+
canvasCtx.lineWidth = 1;
|
|
849
|
+
canvasCtx.beginPath();
|
|
850
|
+
canvasCtx.moveTo(plotArea.x - 6, y);
|
|
851
|
+
canvasCtx.lineTo(plotArea.x, y);
|
|
852
|
+
canvasCtx.stroke();
|
|
853
|
+
}
|
|
777
854
|
});
|
|
778
855
|
drawAxisLine(canvasCtx, axisConfig, theme, side === "left" ? plotArea.x : plotArea.x + plotArea.width, plotArea.y, side === "left" ? plotArea.x : plotArea.x + plotArea.width, plotArea.y + plotArea.height);
|
|
779
856
|
if (axisConfig.title) {
|
|
@@ -1158,14 +1235,14 @@ function renderTitle(ctx, paddingTop) {
|
|
|
1158
1235
|
if (!options.title) return;
|
|
1159
1236
|
canvasCtx.save();
|
|
1160
1237
|
canvasCtx.fillStyle = theme.textColor;
|
|
1161
|
-
canvasCtx.font = "
|
|
1162
|
-
canvasCtx.textAlign = "
|
|
1238
|
+
canvasCtx.font = "600 13px Inter, sans-serif";
|
|
1239
|
+
canvasCtx.textAlign = "left";
|
|
1163
1240
|
canvasCtx.textBaseline = "top";
|
|
1164
|
-
canvasCtx.fillText(options.title, plotArea.x
|
|
1241
|
+
canvasCtx.fillText(options.title, plotArea.x, paddingTop);
|
|
1165
1242
|
if (options.subtitle) {
|
|
1166
|
-
canvasCtx.font = "
|
|
1243
|
+
canvasCtx.font = "11px Inter, sans-serif";
|
|
1167
1244
|
canvasCtx.fillStyle = theme.textColorSecondary;
|
|
1168
|
-
canvasCtx.fillText(options.subtitle, plotArea.x
|
|
1245
|
+
canvasCtx.fillText(options.subtitle, plotArea.x, paddingTop + 20);
|
|
1169
1246
|
}
|
|
1170
1247
|
canvasCtx.restore();
|
|
1171
1248
|
}
|
|
@@ -1284,12 +1361,12 @@ function renderLegend(ctx) {
|
|
|
1284
1361
|
canvasCtx.fill();
|
|
1285
1362
|
} else if (icon === "rect") canvasCtx.fillRect(bound.x, bound.y + 5, 12, 12);
|
|
1286
1363
|
else {
|
|
1287
|
-
roundRect(canvasCtx, bound.x, bound.y + 5, 12,
|
|
1364
|
+
roundRect(canvasCtx, bound.x, bound.y + 9.5, 12, 3, 1.5);
|
|
1288
1365
|
canvasCtx.fill();
|
|
1289
1366
|
}
|
|
1290
1367
|
canvasCtx.restore();
|
|
1291
1368
|
canvasCtx.save();
|
|
1292
|
-
canvasCtx.fillStyle =
|
|
1369
|
+
canvasCtx.fillStyle = theme.textColorSecondary;
|
|
1293
1370
|
canvasCtx.globalAlpha = isHidden ? .6 : 1;
|
|
1294
1371
|
canvasCtx.textAlign = "left";
|
|
1295
1372
|
canvasCtx.textBaseline = "middle";
|
|
@@ -1388,6 +1465,32 @@ function computePieMaxRadius(pieData, options, plotArea) {
|
|
|
1388
1465
|
function alphaHex(opacity) {
|
|
1389
1466
|
return Math.round(Math.max(0, Math.min(1, opacity)) * 255).toString(16).padStart(2, "0");
|
|
1390
1467
|
}
|
|
1468
|
+
/** 单调三次插值切线(Fritsch–Carlson 限幅):平滑但不过冲,曲线必过每个数据点 */
|
|
1469
|
+
function monotoneTangents(pts) {
|
|
1470
|
+
const n = pts.length;
|
|
1471
|
+
const m = new Array(n).fill(0);
|
|
1472
|
+
const d = [];
|
|
1473
|
+
for (let i = 0; i < n - 1; i++) d.push((pts[i + 1][1] - pts[i][1]) / (pts[i + 1][0] - pts[i][0]));
|
|
1474
|
+
m[0] = d[0];
|
|
1475
|
+
m[n - 1] = d[n - 2];
|
|
1476
|
+
for (let i = 1; i < n - 1; i++) m[i] = d[i - 1] * d[i] <= 0 ? 0 : (d[i - 1] + d[i]) / 2;
|
|
1477
|
+
for (let i = 0; i < n - 1; i++) {
|
|
1478
|
+
if (d[i] === 0) {
|
|
1479
|
+
m[i] = 0;
|
|
1480
|
+
m[i + 1] = 0;
|
|
1481
|
+
continue;
|
|
1482
|
+
}
|
|
1483
|
+
const a = m[i] / d[i];
|
|
1484
|
+
const b = m[i + 1] / d[i];
|
|
1485
|
+
const s = a * a + b * b;
|
|
1486
|
+
if (s > 9) {
|
|
1487
|
+
const tk = 3 / Math.sqrt(s);
|
|
1488
|
+
m[i] = tk * a * d[i];
|
|
1489
|
+
m[i + 1] = tk * b * d[i];
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return m;
|
|
1493
|
+
}
|
|
1391
1494
|
function renderLineChart(ctx, yRange, side = "left") {
|
|
1392
1495
|
const { ctx: canvasCtx, theme, plotArea, options, progress, hoverIndex, hiddenSeries, valueFormatter } = ctx;
|
|
1393
1496
|
const allPoints = [];
|
|
@@ -1443,16 +1546,25 @@ function renderLineChart(ctx, yRange, side = "left") {
|
|
|
1443
1546
|
});
|
|
1444
1547
|
if (segments.length === 0) return;
|
|
1445
1548
|
const step = series.step || options.step;
|
|
1549
|
+
const smooth = !!(series.smooth ?? options.smooth);
|
|
1446
1550
|
const tracePath = (pts, startMove) => {
|
|
1447
1551
|
startMove(canvasCtx, pts[0]);
|
|
1552
|
+
if (smooth && pts.length > 2) {
|
|
1553
|
+
const m = monotoneTangents(pts);
|
|
1554
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
1555
|
+
const x0 = pts[i][0];
|
|
1556
|
+
const y0 = pts[i][1];
|
|
1557
|
+
const x1 = pts[i + 1][0];
|
|
1558
|
+
const y1 = pts[i + 1][1];
|
|
1559
|
+
const h = (x1 - x0) / 3;
|
|
1560
|
+
canvasCtx.bezierCurveTo(x0 + h, y0 + h * m[i], x1 - h, y1 - h * m[i + 1], x1, y1);
|
|
1561
|
+
}
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1448
1564
|
for (let i = 0; i < pts.length - 1; i++) {
|
|
1449
1565
|
const curr = pts[i];
|
|
1450
1566
|
const next = pts[i + 1];
|
|
1451
|
-
if (
|
|
1452
|
-
const cp1x = curr[0] + (next[0] - curr[0]) / 3;
|
|
1453
|
-
const cp2x = next[0] - (next[0] - curr[0]) / 3;
|
|
1454
|
-
canvasCtx.bezierCurveTo(cp1x, curr[1], cp2x, next[1], next[0], next[1]);
|
|
1455
|
-
} else if (step) {
|
|
1567
|
+
if (step) {
|
|
1456
1568
|
if (step === "start") {
|
|
1457
1569
|
canvasCtx.lineTo(next[0], curr[1]);
|
|
1458
1570
|
canvasCtx.lineTo(next[0], next[1]);
|
|
@@ -1512,7 +1624,7 @@ function renderLineChart(ctx, yRange, side = "left") {
|
|
|
1512
1624
|
}
|
|
1513
1625
|
canvasCtx.beginPath();
|
|
1514
1626
|
canvasCtx.strokeStyle = color;
|
|
1515
|
-
canvasCtx.lineWidth = series.lineWidth ||
|
|
1627
|
+
canvasCtx.lineWidth = series.lineWidth || 1.5;
|
|
1516
1628
|
canvasCtx.lineJoin = "round";
|
|
1517
1629
|
canvasCtx.lineCap = "round";
|
|
1518
1630
|
canvasCtx.setLineDash(resolveLineDash(series));
|
|
@@ -1520,7 +1632,7 @@ function renderLineChart(ctx, yRange, side = "left") {
|
|
|
1520
1632
|
canvasCtx.stroke();
|
|
1521
1633
|
canvasCtx.setLineDash([]);
|
|
1522
1634
|
if (!series.area && options.type !== "area") canvasCtx.restore();
|
|
1523
|
-
if (series.showSymbol
|
|
1635
|
+
if (series.showSymbol === true && series.symbol !== "none") {
|
|
1524
1636
|
const baseR = series.symbolSize ?? 4;
|
|
1525
1637
|
points.forEach((p, i) => {
|
|
1526
1638
|
if (!p) return;
|
|
@@ -1541,6 +1653,15 @@ function renderLineChart(ctx, yRange, side = "left") {
|
|
|
1541
1653
|
canvasCtx.stroke();
|
|
1542
1654
|
}
|
|
1543
1655
|
});
|
|
1656
|
+
} else if (hoverIndex >= 0 && points[hoverIndex]) {
|
|
1657
|
+
const p = points[hoverIndex];
|
|
1658
|
+
canvasCtx.beginPath();
|
|
1659
|
+
canvasCtx.arc(p[0], p[1], 4.5, 0, Math.PI * 2);
|
|
1660
|
+
canvasCtx.fillStyle = theme.backgroundColor;
|
|
1661
|
+
canvasCtx.fill();
|
|
1662
|
+
canvasCtx.strokeStyle = color;
|
|
1663
|
+
canvasCtx.lineWidth = 2;
|
|
1664
|
+
canvasCtx.stroke();
|
|
1544
1665
|
}
|
|
1545
1666
|
if ((series.showValues !== void 0 ? series.showValues : options.showValues) && progress > .9) {
|
|
1546
1667
|
canvasCtx.save();
|
|
@@ -3380,9 +3501,13 @@ function renderDataZoomSlider(ctx, zoom) {
|
|
|
3380
3501
|
}
|
|
3381
3502
|
const { xStart, xEnd } = windowToX(zoom, geo);
|
|
3382
3503
|
const windowFill = theme.crosshairColor.includes("rgba") ? theme.crosshairColor.replace(/[\d.]+\)$/, "0.12)") : theme.crosshairColor + "1f";
|
|
3504
|
+
canvasCtx.save();
|
|
3505
|
+
roundRect(canvasCtx, geo.x, geo.y, geo.width, geo.height, 4);
|
|
3506
|
+
canvasCtx.clip();
|
|
3383
3507
|
canvasCtx.fillStyle = theme.backgroundColor + "aa";
|
|
3384
3508
|
canvasCtx.fillRect(geo.x, geo.y, xStart - geo.x, geo.height);
|
|
3385
3509
|
canvasCtx.fillRect(xEnd, geo.y, geo.x + geo.width - xEnd, geo.height);
|
|
3510
|
+
canvasCtx.restore();
|
|
3386
3511
|
canvasCtx.strokeStyle = theme.gridColor;
|
|
3387
3512
|
canvasCtx.lineWidth = 1;
|
|
3388
3513
|
roundRect(canvasCtx, geo.x, geo.y, geo.width, geo.height, 4);
|
|
@@ -3394,21 +3519,26 @@ function renderDataZoomSlider(ctx, zoom) {
|
|
|
3394
3519
|
canvasCtx.lineWidth = 1;
|
|
3395
3520
|
roundRect(canvasCtx, xStart, geo.y, Math.max(2, xEnd - xStart), geo.height, 4);
|
|
3396
3521
|
canvasCtx.stroke();
|
|
3397
|
-
const handleW =
|
|
3522
|
+
const handleW = 9;
|
|
3398
3523
|
const handleH = 18;
|
|
3399
3524
|
const handleY = geo.y + (geo.height - handleH) / 2;
|
|
3400
3525
|
[xStart - handleW / 2, xEnd - handleW / 2].forEach((hx) => {
|
|
3401
|
-
roundRect(canvasCtx, hx, handleY, handleW, handleH,
|
|
3402
|
-
canvasCtx.fillStyle = theme.
|
|
3526
|
+
roundRect(canvasCtx, hx, handleY, handleW, handleH, 4);
|
|
3527
|
+
canvasCtx.fillStyle = theme.backgroundColor;
|
|
3403
3528
|
canvasCtx.fill();
|
|
3404
|
-
canvasCtx.strokeStyle = theme.
|
|
3405
|
-
canvasCtx.lineWidth = 1;
|
|
3529
|
+
canvasCtx.strokeStyle = theme.crosshairColor;
|
|
3530
|
+
canvasCtx.lineWidth = 1.5;
|
|
3531
|
+
canvasCtx.stroke();
|
|
3532
|
+
canvasCtx.strokeStyle = theme.crosshairColor;
|
|
3533
|
+
canvasCtx.lineWidth = 1.5;
|
|
3534
|
+
canvasCtx.lineCap = "round";
|
|
3406
3535
|
canvasCtx.beginPath();
|
|
3407
|
-
canvasCtx.moveTo(hx + handleW / 2 - 2, handleY +
|
|
3408
|
-
canvasCtx.lineTo(hx + handleW / 2 - 2, handleY + handleH -
|
|
3409
|
-
canvasCtx.moveTo(hx + handleW / 2 + 2, handleY +
|
|
3410
|
-
canvasCtx.lineTo(hx + handleW / 2 + 2, handleY + handleH -
|
|
3536
|
+
canvasCtx.moveTo(hx + handleW / 2 - 2, handleY + 6);
|
|
3537
|
+
canvasCtx.lineTo(hx + handleW / 2 - 2, handleY + handleH - 6);
|
|
3538
|
+
canvasCtx.moveTo(hx + handleW / 2 + 2, handleY + 6);
|
|
3539
|
+
canvasCtx.lineTo(hx + handleW / 2 + 2, handleY + handleH - 6);
|
|
3411
3540
|
canvasCtx.stroke();
|
|
3541
|
+
canvasCtx.lineCap = "butt";
|
|
3412
3542
|
});
|
|
3413
3543
|
canvasCtx.restore();
|
|
3414
3544
|
}
|
|
@@ -5854,7 +5984,35 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
5854
5984
|
_: 1
|
|
5855
5985
|
}),
|
|
5856
5986
|
createVNode(Transition, { name: "ev-chart-fade" }, {
|
|
5857
|
-
default: withCtx(() => [internalError.value ? (openBlock(), createElementBlock("div", _hoisted_7, [_cache[0] || (_cache[0] = createElementVNode("span", { class: "ev-chart__error-icon" },
|
|
5987
|
+
default: withCtx(() => [internalError.value ? (openBlock(), createElementBlock("div", _hoisted_7, [_cache[0] || (_cache[0] = createElementVNode("span", { class: "ev-chart__error-icon" }, [createElementVNode("svg", {
|
|
5988
|
+
viewBox: "0 0 24 24",
|
|
5989
|
+
width: "1em",
|
|
5990
|
+
height: "1em",
|
|
5991
|
+
"aria-hidden": "true",
|
|
5992
|
+
fill: "none",
|
|
5993
|
+
stroke: "currentColor",
|
|
5994
|
+
"stroke-width": "2",
|
|
5995
|
+
"stroke-linecap": "round",
|
|
5996
|
+
"stroke-linejoin": "round"
|
|
5997
|
+
}, [
|
|
5998
|
+
createElementVNode("circle", {
|
|
5999
|
+
cx: "12",
|
|
6000
|
+
cy: "12",
|
|
6001
|
+
r: "9"
|
|
6002
|
+
}),
|
|
6003
|
+
createElementVNode("line", {
|
|
6004
|
+
x1: "12",
|
|
6005
|
+
y1: "7.5",
|
|
6006
|
+
x2: "12",
|
|
6007
|
+
y2: "13"
|
|
6008
|
+
}),
|
|
6009
|
+
createElementVNode("line", {
|
|
6010
|
+
x1: "12",
|
|
6011
|
+
y1: "16.5",
|
|
6012
|
+
x2: "12",
|
|
6013
|
+
y2: "16.51"
|
|
6014
|
+
})
|
|
6015
|
+
])], -1)), createElementVNode("span", _hoisted_8, toDisplayString(internalError.value), 1)])) : createCommentVNode("", true)]),
|
|
5858
6016
|
_: 1
|
|
5859
6017
|
}),
|
|
5860
6018
|
createVNode(Transition, { name: "ev-chart-tooltip" }, {
|
|
@@ -5873,7 +6031,7 @@ var chart_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__
|
|
|
5873
6031
|
], 12, _hoisted_1);
|
|
5874
6032
|
};
|
|
5875
6033
|
}
|
|
5876
|
-
}), [["__scopeId", "data-v-
|
|
6034
|
+
}), [["__scopeId", "data-v-a07b68e8"]]);
|
|
5877
6035
|
//#endregion
|
|
5878
6036
|
//#region src/useChart.js
|
|
5879
6037
|
var USE_CHART_KEY = /* @__PURE__ */ Symbol("ev-chart-context");
|
|
@@ -5937,6 +6095,95 @@ function injectChartContext() {
|
|
|
5937
6095
|
return inject(USE_CHART_KEY, null);
|
|
5938
6096
|
}
|
|
5939
6097
|
//#endregion
|
|
6098
|
+
//#region src/palette.js
|
|
6099
|
+
/**
|
|
6100
|
+
* 配色方案应用工具 — 中性契约的官方实现
|
|
6101
|
+
*
|
|
6102
|
+
* 把一套数据色板写入 --ev-color-series-1..8 槽位令牌并派发 'ev-theme-change',
|
|
6103
|
+
* 页面上的全部图表即时重绘。任何宿主(组件库 / 站点 / 独立页面)都可直接调用,
|
|
6104
|
+
* 不必各自实现令牌写入与事件广播。
|
|
6105
|
+
*
|
|
6106
|
+
* palette 两种形态:
|
|
6107
|
+
* - string[] 单套色值,明暗模式共用;
|
|
6108
|
+
* - { light, dark } 浅/暗两组,随 html.dark 自动换挡(经注入样式表实现)。
|
|
6109
|
+
*
|
|
6110
|
+
* clearSeriesPalette() 移除覆写(内联属性 + 注入样式表),回到内置成套色板。
|
|
6111
|
+
*/
|
|
6112
|
+
var SLOT_COUNT = 8;
|
|
6113
|
+
var PALETTE_EVENT = "ev-theme-change";
|
|
6114
|
+
var STYLE_ID = "data-ev-series-palette";
|
|
6115
|
+
var paletteStyleEl = null;
|
|
6116
|
+
function dispatchPaletteChange(dark) {
|
|
6117
|
+
if (typeof document === "undefined") return;
|
|
6118
|
+
const isDark = typeof dark === "boolean" ? dark : document.documentElement.classList.contains("dark");
|
|
6119
|
+
document.documentElement.dispatchEvent(new CustomEvent(PALETTE_EVENT, {
|
|
6120
|
+
bubbles: true,
|
|
6121
|
+
detail: { dark: isDark }
|
|
6122
|
+
}));
|
|
6123
|
+
}
|
|
6124
|
+
function removeInlineSlots() {
|
|
6125
|
+
if (typeof document === "undefined") return;
|
|
6126
|
+
for (let i = 1; i <= SLOT_COUNT; i++) document.documentElement.style.removeProperty(`--ev-color-series-${i}`);
|
|
6127
|
+
}
|
|
6128
|
+
function ensurePaletteStyleEl() {
|
|
6129
|
+
if (typeof document === "undefined") return null;
|
|
6130
|
+
if (!paletteStyleEl || !paletteStyleEl.isConnected) {
|
|
6131
|
+
paletteStyleEl = document.createElement("style");
|
|
6132
|
+
paletteStyleEl.setAttribute(STYLE_ID, "");
|
|
6133
|
+
document.head.appendChild(paletteStyleEl);
|
|
6134
|
+
}
|
|
6135
|
+
return paletteStyleEl;
|
|
6136
|
+
}
|
|
6137
|
+
function normalizeSlots(colors) {
|
|
6138
|
+
const out = {};
|
|
6139
|
+
if (!Array.isArray(colors)) return out;
|
|
6140
|
+
colors.slice(0, SLOT_COUNT).forEach((color, i) => {
|
|
6141
|
+
if (color) out[`--ev-color-series-${i + 1}`] = color;
|
|
6142
|
+
});
|
|
6143
|
+
return out;
|
|
6144
|
+
}
|
|
6145
|
+
/**
|
|
6146
|
+
* 应用一套数据色板
|
|
6147
|
+
* @param {string[] | { light: string[], dark: string[] }} palette
|
|
6148
|
+
* 数组:写入内联槽位(明暗共用);{ light, dark }:注入样式表,随 html.dark 自动换挡
|
|
6149
|
+
* @param {{ dark?: boolean, target?: HTMLElement }} [options]
|
|
6150
|
+
* dark 手动指定当前暗色态(默认探测 html.dark);target 改写注入目标
|
|
6151
|
+
* @returns {boolean} 是否写入成功
|
|
6152
|
+
*/
|
|
6153
|
+
function applySeriesPalette(palette, options = {}) {
|
|
6154
|
+
if (typeof document === "undefined") return false;
|
|
6155
|
+
const el = options.target ?? document.documentElement;
|
|
6156
|
+
if (!el || !palette) return false;
|
|
6157
|
+
if (Array.isArray(palette)) {
|
|
6158
|
+
paletteStyleEl?.remove();
|
|
6159
|
+
paletteStyleEl = null;
|
|
6160
|
+
for (const [name, value] of Object.entries(normalizeSlots(palette))) el.style.setProperty(name, value);
|
|
6161
|
+
dispatchPaletteChange(options.dark);
|
|
6162
|
+
return true;
|
|
6163
|
+
}
|
|
6164
|
+
const { light, dark } = palette;
|
|
6165
|
+
if (!Array.isArray(light) && !Array.isArray(dark)) return false;
|
|
6166
|
+
removeInlineSlots();
|
|
6167
|
+
const styleEl = ensurePaletteStyleEl();
|
|
6168
|
+
if (styleEl) {
|
|
6169
|
+
const vars = (colors) => colors.map((c, i) => `--ev-color-series-${i + 1}: ${c}`).join(";");
|
|
6170
|
+
styleEl.textContent = `${light ? `:root { ${vars(light)}; }` : ""}${dark ? ` html.dark { ${vars(dark)}; }` : ""}`;
|
|
6171
|
+
}
|
|
6172
|
+
dispatchPaletteChange(options.dark);
|
|
6173
|
+
return true;
|
|
6174
|
+
}
|
|
6175
|
+
/**
|
|
6176
|
+
* 清除配色方案覆写(内联槽位 + 注入样式表),回到内置成套色板
|
|
6177
|
+
*/
|
|
6178
|
+
function clearSeriesPalette(options = {}) {
|
|
6179
|
+
if (typeof document === "undefined") return false;
|
|
6180
|
+
paletteStyleEl?.remove();
|
|
6181
|
+
paletteStyleEl = null;
|
|
6182
|
+
removeInlineSlots();
|
|
6183
|
+
dispatchPaletteChange(options.dark);
|
|
6184
|
+
return true;
|
|
6185
|
+
}
|
|
6186
|
+
//#endregion
|
|
5940
6187
|
//#region src/index.js
|
|
5941
6188
|
var components = { EvChart: chart_default };
|
|
5942
6189
|
function install(app, _options = {}) {
|
|
@@ -5945,4 +6192,4 @@ function install(app, _options = {}) {
|
|
|
5945
6192
|
var EvokeCharts = { install };
|
|
5946
6193
|
var src_default = { install };
|
|
5947
6194
|
//#endregion
|
|
5948
|
-
export { chart_default as EvChart, EvokeCharts, components, src_default as default, injectChartContext, install, provideChartContext, useChart };
|
|
6195
|
+
export { chart_default as EvChart, EvokeCharts, applySeriesPalette, clearSeriesPalette, components, src_default as default, injectChartContext, install, provideChartContext, useChart };
|