@classic-homes/charts-core 0.1.36 → 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/dist/{chunk-ZRG25JNX.js → chunk-JM72YHZD.js} +38 -3
- package/dist/index.cjs +299 -6
- package/dist/index.d.cts +171 -4
- package/dist/index.d.ts +171 -4
- package/dist/index.js +243 -7
- package/dist/theme/index.cjs +40 -1
- package/dist/theme/index.d.cts +33 -2
- package/dist/theme/index.d.ts +33 -2
- package/dist/theme/index.js +1 -1
- package/dist/types/index.d.cts +189 -12
- package/dist/types/index.d.ts +189 -12
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export { EChartsTheme, THEME_DARK, THEME_LIGHT, darkTheme, generateDarkTheme, generateLightTheme, lightTheme } from './theme/index.js';
|
|
2
|
-
import { BarChartData, LineChartData, PieSliceData, ScatterChartData } from './types/index.js';
|
|
3
|
-
export { AreaChartData, AreaChartProps, AreaSeriesData, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, CandlestickChartProps, CandlestickData, CandlestickEventParams, CandlestickItem, DataPointEventParams, DonutChartProps, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapData, HeatmapEventParams, LineChartProps, LineSeriesData, PieChartProps, RadarChartData, RadarChartProps, RadarIndicator, RadarSeriesData, SankeyChartProps, SankeyData, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartProps, ScatterSeriesData, SliceEventParams, TreemapChartProps, TreemapEventParams, TreemapLevel, TreemapNode } from './types/index.js';
|
|
1
|
+
export { ChartSemanticColors, EChartsTheme, THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme } from './theme/index.js';
|
|
2
|
+
import { BarChartData, LineChartData, PieSliceData, ScatterChartData, ChartFormatter, LabelFormatterParams, TooltipFormatterParams, AxisConfig, ChartLegendConfig, LegendItem } from './types/index.js';
|
|
3
|
+
export { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, CandlestickChartProps, CandlestickData, CandlestickEventParams, CandlestickItem, ChartLegendPosition, DataPointEventParams, DonutChartProps, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapData, HeatmapEventParams, LabelChartProps, LegendChartProps, LegendRenderProps, LineChartProps, LineSeriesData, PieChartProps, RadarChartData, RadarChartProps, RadarIndicator, RadarSeriesData, SankeyChartProps, SankeyData, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartProps, ScatterSeriesData, SliceEventParams, TreemapChartProps, TreemapEventParams, TreemapLevel, TreemapNode } from './types/index.js';
|
|
4
|
+
import 'echarts';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Formatting utilities for chart values
|
|
@@ -25,6 +26,11 @@ declare function formatDate(date: Date | string, options?: Intl.DateTimeFormatOp
|
|
|
25
26
|
* Abbreviate large numbers (e.g., 1000 -> 1K, 1000000 -> 1M)
|
|
26
27
|
*/
|
|
27
28
|
declare function abbreviateNumber(value: number, decimals?: number): string;
|
|
29
|
+
/**
|
|
30
|
+
* Escape a value for interpolation into HTML. ECharts renders the string a
|
|
31
|
+
* function tooltip formatter returns as innerHTML without encoding it.
|
|
32
|
+
*/
|
|
33
|
+
declare function escapeHtml(value: unknown): string;
|
|
28
34
|
/**
|
|
29
35
|
* Create a tooltip formatter for ECharts
|
|
30
36
|
*/
|
|
@@ -64,4 +70,165 @@ declare function generateScatterChartDescription(title: string, data: ScatterCha
|
|
|
64
70
|
*/
|
|
65
71
|
declare function generateDataTable(headers: string[], rows: (string | number)[][]): string;
|
|
66
72
|
|
|
67
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Helpers for applying a complete ECharts option on top of a previous one.
|
|
75
|
+
*/
|
|
76
|
+
/**
|
|
77
|
+
* Work out the `replaceMerge` list for `chart.setOption(next, { replaceMerge })`
|
|
78
|
+
* when `next` is a complete option that supersedes `prev`.
|
|
79
|
+
*
|
|
80
|
+
* `notMerge: true` would also drop removed series and components, but it
|
|
81
|
+
* rebuilds every component, so legend selection, dataZoom ranges and other
|
|
82
|
+
* interaction state reset on each data update. Instead this replaces only:
|
|
83
|
+
*
|
|
84
|
+
* - `series` and `graphic`, so removed series, mark lines and centre labels
|
|
85
|
+
* disappear;
|
|
86
|
+
* - any component type that `next` has fewer of than `prev` (a hidden legend
|
|
87
|
+
* or tooltip, a dropped volume grid and its axes).
|
|
88
|
+
*
|
|
89
|
+
* Everything else is merged, so its state survives. ECharts rejects types it
|
|
90
|
+
* has no component class for, so only types for which `isComponentType`
|
|
91
|
+
* returns true are listed; pass `echarts.ComponentModel.hasClass`.
|
|
92
|
+
*/
|
|
93
|
+
declare function getReplaceMerge(prev: object | null | undefined, next: object, isComponentType: (mainType: string) => boolean): string[];
|
|
94
|
+
/**
|
|
95
|
+
* Deep-merge a user-supplied ECharts option over a chart's generated option.
|
|
96
|
+
*
|
|
97
|
+
* Plain objects merge key by key. Top-level component arrays (`series`,
|
|
98
|
+
* `xAxis`, `grid`, ...) merge index by index, matching how ECharts merges
|
|
99
|
+
* options, so `{ series: [{ label: { show: true } }] }` patches only the
|
|
100
|
+
* first series. Nested arrays (`data`, `center`, ...) and any other value
|
|
101
|
+
* (functions, class instances such as gradients, primitives) replace the
|
|
102
|
+
* generated one; `undefined` leaves it untouched.
|
|
103
|
+
*/
|
|
104
|
+
declare function mergeChartOption<T extends object>(generated: T, override: object | undefined): T;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Builders for the label, axis, tooltip and legend parts of a chart option.
|
|
108
|
+
*
|
|
109
|
+
* Chart components pass the block they generate by default; with no
|
|
110
|
+
* customisation each builder returns that block unchanged.
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
type AnyObject = Record<string, unknown>;
|
|
114
|
+
/** Looks up the per-point label text for a data point */
|
|
115
|
+
type PointLabelLookup = (seriesIndex: number, dataIndex: number, name: string) => string | undefined;
|
|
116
|
+
interface BuildLegendOptions {
|
|
117
|
+
/** Whether the built-in legend is shown (the `showLegend` prop) */
|
|
118
|
+
show: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* A custom legend replaces the built-in one: it is kept with `show: false`
|
|
121
|
+
* so legend selection actions keep working.
|
|
122
|
+
*/
|
|
123
|
+
custom?: boolean;
|
|
124
|
+
/** Extra keys for the default bottom legend, e.g. `{ orient: 'horizontal' }` */
|
|
125
|
+
defaults?: AnyObject;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build the ECharts `legend` component for a list of item names
|
|
129
|
+
*/
|
|
130
|
+
declare function buildLegend(names: string[], config: ChartLegendConfig | undefined, { show, custom, defaults }: BuildLegendOptions): AnyObject | undefined;
|
|
131
|
+
/**
|
|
132
|
+
* Grid edges to move so a built-in legend and centred axis titles do not
|
|
133
|
+
* overlap the plot or its tick labels (`containLabel` only reserves room for
|
|
134
|
+
* tick labels). Returns a partial `grid` to spread over the default one, or
|
|
135
|
+
* nothing when no room is needed.
|
|
136
|
+
*/
|
|
137
|
+
declare function gridInset(legend: ChartLegendConfig | undefined, showLegend: boolean, xAxis?: AxisConfig, yAxis?: AxisConfig): AnyObject | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Centre for a pie, donut or radar that leaves room for a built-in legend
|
|
140
|
+
*/
|
|
141
|
+
declare function legendCenter(config: ChartLegendConfig | undefined, show: boolean, fallback: [string, string]): [string, string];
|
|
142
|
+
/**
|
|
143
|
+
* Re-anchor a centre graphic group (a donut's centre label) on a pie centre
|
|
144
|
+
* moved by `legendCenter`. `defaultCenter` is where the group's `left: 'center'`
|
|
145
|
+
* / `top` placement assumes the pie sits; the group is returned unchanged
|
|
146
|
+
* when the centre has not moved. A raw bounding positions the group by its
|
|
147
|
+
* origin, so children centred on x = 0 sit on the new centre.
|
|
148
|
+
*/
|
|
149
|
+
declare function anchorCenterGraphic(group: AnyObject, center: [string, string], defaultCenter: [string, string]): AnyObject;
|
|
150
|
+
/**
|
|
151
|
+
* Legend entries with the colour the chart draws each in: an explicit
|
|
152
|
+
* override, else the theme palette by index
|
|
153
|
+
*/
|
|
154
|
+
declare function getLegendItems(names: string[], palette: string[], overrides: (string | undefined)[], visible: Record<string, boolean>): LegendItem[];
|
|
155
|
+
/**
|
|
156
|
+
* Apply an axis customisation to a generated axis
|
|
157
|
+
*/
|
|
158
|
+
declare function buildAxis<T extends AnyObject>(base: T, config: AxisConfig | undefined): T;
|
|
159
|
+
interface BuildDataLabelOptions {
|
|
160
|
+
/** Whether data labels are shown */
|
|
161
|
+
show: boolean;
|
|
162
|
+
/** The `labelFormatter` prop */
|
|
163
|
+
formatter?: ChartFormatter<LabelFormatterParams>;
|
|
164
|
+
/** Per-point label lookup, exposed to formatter functions as `label` */
|
|
165
|
+
getLabel?: PointLabelLookup;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Build a series `label` block. `base` is the block the chart shows by
|
|
169
|
+
* default; `hidden` is what it uses when labels are off.
|
|
170
|
+
*/
|
|
171
|
+
declare function buildDataLabel(base: AnyObject, hidden: AnyObject | undefined, { show, formatter, getLabel }: BuildDataLabelOptions): AnyObject | undefined;
|
|
172
|
+
/**
|
|
173
|
+
* A per-data-item label override, for points with their own label text.
|
|
174
|
+
* When a formatter function is set it receives the text instead, so the
|
|
175
|
+
* item only switches its label on. `base` carries placement (e.g.
|
|
176
|
+
* `{ position: 'top' }`) for when the series' own labels are off.
|
|
177
|
+
*/
|
|
178
|
+
declare function pointLabel(text: string | null | undefined, formatter: ChartFormatter<LabelFormatterParams> | undefined, base?: AnyObject): AnyObject | undefined;
|
|
179
|
+
/**
|
|
180
|
+
* Turn a plain value array into data items, attaching a label override to
|
|
181
|
+
* each point that has label text. Returns the values unchanged when none do.
|
|
182
|
+
*/
|
|
183
|
+
declare function withPointLabels<V>(values: V[], labels: (string | null | undefined)[] | undefined, formatter: ChartFormatter<LabelFormatterParams> | undefined, base?: AnyObject): (V | {
|
|
184
|
+
value: V;
|
|
185
|
+
label: AnyObject;
|
|
186
|
+
})[];
|
|
187
|
+
/**
|
|
188
|
+
* Lookup for series-based per-point labels
|
|
189
|
+
*/
|
|
190
|
+
declare function seriesLabelLookup(series: {
|
|
191
|
+
labels?: (string | null | undefined)[];
|
|
192
|
+
}[]): PointLabelLookup;
|
|
193
|
+
/**
|
|
194
|
+
* Lookup for item-based per-point labels (pie slices, funnel stages)
|
|
195
|
+
*/
|
|
196
|
+
declare function itemLabelLookup(items: {
|
|
197
|
+
label?: string;
|
|
198
|
+
}[]): PointLabelLookup;
|
|
199
|
+
/**
|
|
200
|
+
* Lookup for per-point labels keyed by name, for nodes whose data index does
|
|
201
|
+
* not follow the input order (treemap, sankey). Walks `children` recursively.
|
|
202
|
+
*/
|
|
203
|
+
declare function nameLabelLookup(nodes: {
|
|
204
|
+
name: string;
|
|
205
|
+
label?: string;
|
|
206
|
+
children?: unknown[];
|
|
207
|
+
}[]): PointLabelLookup;
|
|
208
|
+
/**
|
|
209
|
+
* Whether any series has per-point label text
|
|
210
|
+
*/
|
|
211
|
+
declare function hasPointLabels(series: {
|
|
212
|
+
labels?: (string | null | undefined)[];
|
|
213
|
+
}[]): boolean;
|
|
214
|
+
interface BuildTooltipOptions {
|
|
215
|
+
/** Whether the tooltip is shown */
|
|
216
|
+
show: boolean;
|
|
217
|
+
/** The `tooltipFormatter` prop */
|
|
218
|
+
formatter?: ChartFormatter<TooltipFormatterParams[]>;
|
|
219
|
+
/** The `tooltipValueFormatter` prop */
|
|
220
|
+
valueFormatter?: (value: number) => string;
|
|
221
|
+
/** Per-point label lookup, exposed to formatter functions as `label` */
|
|
222
|
+
getLabel?: PointLabelLookup;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Build the `tooltip` component. `base` is the chart's default tooltip.
|
|
226
|
+
*/
|
|
227
|
+
declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel }: BuildTooltipOptions): AnyObject | undefined;
|
|
228
|
+
/**
|
|
229
|
+
* Format a value with `tooltipValueFormatter`, for charts whose default
|
|
230
|
+
* tooltip is custom HTML (where ECharts' `valueFormatter` does not apply)
|
|
231
|
+
*/
|
|
232
|
+
declare function formatTooltipValue(value: unknown, valueFormatter: ((value: number) => string) | undefined): string;
|
|
233
|
+
|
|
234
|
+
export { AxisConfig, BarChartData, type BuildDataLabelOptions, type BuildLegendOptions, type BuildTooltipOptions, ChartFormatter, ChartLegendConfig, LabelFormatterParams, LegendItem, LineChartData, PieSliceData, type PointLabelLookup, ScatterChartData, TooltipFormatterParams, abbreviateNumber, anchorCenterGraphic, buildAxis, buildDataLabel, buildLegend, buildTooltip, createTooltipFormatter, escapeHtml, formatCurrency, formatDate, formatNumber, formatPercent, formatTooltipValue, generateBarChartDescription, generateDataTable, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, getLegendItems, getReplaceMerge, gridInset, hasPointLabels, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, pointLabel, seriesLabelLookup, withPointLabels };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { THEME_DARK, THEME_LIGHT, darkTheme, generateDarkTheme, generateLightTheme, lightTheme } from './chunk-
|
|
1
|
+
export { THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme } from './chunk-JM72YHZD.js';
|
|
2
2
|
|
|
3
3
|
// src/utils/format.ts
|
|
4
4
|
function formatNumber(value, options) {
|
|
@@ -29,7 +29,18 @@ function abbreviateNumber(value, decimals = 1) {
|
|
|
29
29
|
abbreviated /= 1e3;
|
|
30
30
|
suffixIndex++;
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
if (Math.abs(Number(abbreviated.toFixed(decimals))) >= 1e3 && suffixIndex < suffixes.length - 1) {
|
|
33
|
+
abbreviated /= 1e3;
|
|
34
|
+
suffixIndex++;
|
|
35
|
+
}
|
|
36
|
+
let fixed = abbreviated.toFixed(decimals);
|
|
37
|
+
if (fixed.includes(".")) {
|
|
38
|
+
fixed = fixed.replace(/\.?0+$/, "");
|
|
39
|
+
}
|
|
40
|
+
return fixed + suffixes[suffixIndex];
|
|
41
|
+
}
|
|
42
|
+
function escapeHtml(value) {
|
|
43
|
+
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
33
44
|
}
|
|
34
45
|
function createTooltipFormatter(options = {}) {
|
|
35
46
|
const { showSeriesName = true, valueFormatter = formatNumber } = options;
|
|
@@ -44,9 +55,9 @@ function createTooltipFormatter(options = {}) {
|
|
|
44
55
|
const formatted = valueFormatter(value);
|
|
45
56
|
const percent = param.percent ? ` (${param.percent.toFixed(1)}%)` : "";
|
|
46
57
|
if (showSeriesName && param.seriesName) {
|
|
47
|
-
return `${param.seriesName}: ${formatted}${percent}`;
|
|
58
|
+
return `${escapeHtml(param.seriesName)}: ${escapeHtml(formatted)}${percent}`;
|
|
48
59
|
}
|
|
49
|
-
return `${param.name}: ${formatted}${percent}`;
|
|
60
|
+
return `${escapeHtml(param.name)}: ${escapeHtml(formatted)}${percent}`;
|
|
50
61
|
}).join("<br/>");
|
|
51
62
|
};
|
|
52
63
|
}
|
|
@@ -100,6 +111,9 @@ function generatePieChartDescription(title, data, isDonut = false) {
|
|
|
100
111
|
const total = data.reduce((sum, slice) => sum + slice.value, 0);
|
|
101
112
|
const chartType = isDonut ? "Donut" : "Pie";
|
|
102
113
|
let description = `${title}. ${chartType} chart with ${data.length} segments`;
|
|
114
|
+
if (total === 0) {
|
|
115
|
+
return `${description}. All segments have a value of 0`;
|
|
116
|
+
}
|
|
103
117
|
const sorted = [...data].sort((a, b) => b.value - a.value);
|
|
104
118
|
const topSegments = sorted.slice(0, 3);
|
|
105
119
|
const percentages = topSegments.map((segment) => {
|
|
@@ -120,8 +134,8 @@ function generateScatterChartDescription(title, data) {
|
|
|
120
134
|
return description;
|
|
121
135
|
}
|
|
122
136
|
function generateDataTable(headers, rows) {
|
|
123
|
-
const headerRow = headers.map((h) => `<th scope="col">${h}</th>`).join("");
|
|
124
|
-
const bodyRows = rows.map((row) => `<tr>${row.map((cell) => `<td>${cell}</td>`).join("")}</tr>`).join("");
|
|
137
|
+
const headerRow = headers.map((h) => `<th scope="col">${escapeHtml(h)}</th>`).join("");
|
|
138
|
+
const bodyRows = rows.map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`).join("");
|
|
125
139
|
return `
|
|
126
140
|
<table class="sr-only">
|
|
127
141
|
<thead><tr>${headerRow}</tr></thead>
|
|
@@ -130,4 +144,226 @@ function generateDataTable(headers, rows) {
|
|
|
130
144
|
`;
|
|
131
145
|
}
|
|
132
146
|
|
|
133
|
-
|
|
147
|
+
// src/utils/merge.ts
|
|
148
|
+
var ALWAYS_REPLACE = ["series", "graphic"];
|
|
149
|
+
function countComponents(value) {
|
|
150
|
+
if (value == null || value === false) return 0;
|
|
151
|
+
if (Array.isArray(value)) return value.filter((item) => item != null).length;
|
|
152
|
+
return 1;
|
|
153
|
+
}
|
|
154
|
+
function getReplaceMerge(prev, next, isComponentType) {
|
|
155
|
+
const types = new Set(ALWAYS_REPLACE);
|
|
156
|
+
if (prev) {
|
|
157
|
+
const before = prev;
|
|
158
|
+
const after = next;
|
|
159
|
+
for (const key of Object.keys(before)) {
|
|
160
|
+
if (countComponents(before[key]) > countComponents(after[key])) {
|
|
161
|
+
types.add(key);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return [...types].filter((type) => isComponentType(type));
|
|
166
|
+
}
|
|
167
|
+
function isPlainObject(value) {
|
|
168
|
+
if (value === null || typeof value !== "object") return false;
|
|
169
|
+
const proto = Object.getPrototypeOf(value);
|
|
170
|
+
return proto === Object.prototype || proto === null;
|
|
171
|
+
}
|
|
172
|
+
function mergeValue(base, override, topLevel = false) {
|
|
173
|
+
if (override === void 0) return base;
|
|
174
|
+
if (topLevel && (Array.isArray(base) || Array.isArray(override))) {
|
|
175
|
+
const from = Array.isArray(base) ? base : base === void 0 ? [] : [base];
|
|
176
|
+
const to = Array.isArray(override) ? override : [override];
|
|
177
|
+
const length = Math.max(from.length, to.length);
|
|
178
|
+
return Array.from({ length }, (_, index) => mergeValue(from[index], to[index]));
|
|
179
|
+
}
|
|
180
|
+
if (isPlainObject(base) && isPlainObject(override)) {
|
|
181
|
+
const merged = { ...base };
|
|
182
|
+
for (const key of Object.keys(override)) {
|
|
183
|
+
merged[key] = mergeValue(base[key], override[key]);
|
|
184
|
+
}
|
|
185
|
+
return merged;
|
|
186
|
+
}
|
|
187
|
+
return override;
|
|
188
|
+
}
|
|
189
|
+
function mergeChartOption(generated, override) {
|
|
190
|
+
if (!override) return generated;
|
|
191
|
+
const base = generated;
|
|
192
|
+
const merged = { ...base };
|
|
193
|
+
for (const [key, value] of Object.entries(override)) {
|
|
194
|
+
merged[key] = mergeValue(base[key], value, true);
|
|
195
|
+
}
|
|
196
|
+
return merged;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/utils/options.ts
|
|
200
|
+
function toParams(raw, getLabel) {
|
|
201
|
+
const seriesIndex = raw.seriesIndex ?? 0;
|
|
202
|
+
const dataIndex = raw.dataIndex ?? 0;
|
|
203
|
+
const name = raw.name ?? "";
|
|
204
|
+
const value = raw.value;
|
|
205
|
+
return {
|
|
206
|
+
name,
|
|
207
|
+
value: Array.isArray(value) ? value : value,
|
|
208
|
+
seriesName: raw.seriesName ?? "",
|
|
209
|
+
seriesIndex,
|
|
210
|
+
dataIndex,
|
|
211
|
+
percent: raw.percent,
|
|
212
|
+
color: typeof raw.color === "string" ? raw.color : void 0,
|
|
213
|
+
label: getLabel?.(seriesIndex, dataIndex, name)
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function toTooltipParams(raw, getLabel) {
|
|
217
|
+
return {
|
|
218
|
+
...toParams(raw, getLabel),
|
|
219
|
+
marker: typeof raw.marker === "string" ? raw.marker : "",
|
|
220
|
+
axisValue: raw.axisValue
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
var EDGE_ALIGN = { start: "left", center: "center", end: "right" };
|
|
224
|
+
var SIDE_ALIGN = { start: "top", center: "middle", end: "bottom" };
|
|
225
|
+
function buildLegend(names, config, { show, custom = false, defaults = {} }) {
|
|
226
|
+
if (custom) return { show: false, data: names };
|
|
227
|
+
if (!show) return void 0;
|
|
228
|
+
const { position = "bottom", align = "center", orient, icon, formatter, scroll } = config ?? {};
|
|
229
|
+
const legend = { ...defaults, data: names };
|
|
230
|
+
if (position === "top" || position === "bottom") {
|
|
231
|
+
legend[position] = 0;
|
|
232
|
+
if (align !== "center") legend.left = EDGE_ALIGN[align];
|
|
233
|
+
} else {
|
|
234
|
+
legend[position] = 0;
|
|
235
|
+
legend.top = SIDE_ALIGN[align];
|
|
236
|
+
legend.orient = "vertical";
|
|
237
|
+
}
|
|
238
|
+
if (orient) legend.orient = orient;
|
|
239
|
+
if (icon) legend.icon = icon;
|
|
240
|
+
if (formatter) legend.formatter = formatter;
|
|
241
|
+
if (scroll) legend.type = "scroll";
|
|
242
|
+
return legend;
|
|
243
|
+
}
|
|
244
|
+
function gridInset(legend, showLegend, xAxis, yAxis) {
|
|
245
|
+
const inset = {};
|
|
246
|
+
if (showLegend) {
|
|
247
|
+
const position = legend?.position ?? "bottom";
|
|
248
|
+
inset[position] = position === "left" || position === "right" ? 140 : 36;
|
|
249
|
+
}
|
|
250
|
+
if (xAxis?.name !== void 0 && (xAxis.nameLocation ?? "middle") === "middle") {
|
|
251
|
+
inset.bottom = (inset.bottom ?? 12) + 24;
|
|
252
|
+
}
|
|
253
|
+
if (yAxis?.name !== void 0 && (yAxis.nameLocation ?? "middle") === "middle") {
|
|
254
|
+
inset.left = (inset.left ?? 12) + 24;
|
|
255
|
+
}
|
|
256
|
+
return Object.keys(inset).length ? inset : void 0;
|
|
257
|
+
}
|
|
258
|
+
function legendCenter(config, show, fallback) {
|
|
259
|
+
if (!show) return fallback;
|
|
260
|
+
switch (config?.position) {
|
|
261
|
+
case "left":
|
|
262
|
+
return ["60%", "50%"];
|
|
263
|
+
case "right":
|
|
264
|
+
return ["40%", "50%"];
|
|
265
|
+
case "top":
|
|
266
|
+
return ["50%", "55%"];
|
|
267
|
+
default:
|
|
268
|
+
return fallback;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function anchorCenterGraphic(group, center, defaultCenter) {
|
|
272
|
+
if (center[0] === defaultCenter[0] && center[1] === defaultCenter[1]) return group;
|
|
273
|
+
const shiftY = parseFloat(center[1]) - parseFloat(defaultCenter[1]);
|
|
274
|
+
const top = typeof group.top === "string" ? `${parseFloat(group.top) + shiftY}%` : group.top;
|
|
275
|
+
return {
|
|
276
|
+
...group,
|
|
277
|
+
left: center[0],
|
|
278
|
+
top,
|
|
279
|
+
bounding: "raw",
|
|
280
|
+
children: (group.children ?? []).map(
|
|
281
|
+
({ left: _left, top: childTop, ...child }) => ({ ...child, x: 0, y: childTop ?? 0 })
|
|
282
|
+
)
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function getLegendItems(names, palette, overrides, visible) {
|
|
286
|
+
return names.map((name, index) => ({
|
|
287
|
+
name,
|
|
288
|
+
color: overrides[index] || palette[index % palette.length] || "",
|
|
289
|
+
visible: visible[name] !== false
|
|
290
|
+
}));
|
|
291
|
+
}
|
|
292
|
+
function buildAxis(base, config) {
|
|
293
|
+
if (!config) return base;
|
|
294
|
+
const axis = { ...base };
|
|
295
|
+
const { show, name, nameLocation, nameGap, formatter, rotate, min, max, interval } = config;
|
|
296
|
+
if (show !== void 0) axis.show = show;
|
|
297
|
+
if (name !== void 0) {
|
|
298
|
+
axis.name = name;
|
|
299
|
+
axis.nameLocation = nameLocation ?? "middle";
|
|
300
|
+
axis.nameGap = nameGap ?? (axis.nameLocation === "middle" ? 32 : 15);
|
|
301
|
+
}
|
|
302
|
+
if (formatter !== void 0 || rotate !== void 0) {
|
|
303
|
+
const axisLabel = { ...base.axisLabel };
|
|
304
|
+
if (formatter !== void 0) axisLabel.formatter = formatter;
|
|
305
|
+
if (rotate !== void 0) axisLabel.rotate = rotate;
|
|
306
|
+
axis.axisLabel = axisLabel;
|
|
307
|
+
}
|
|
308
|
+
if (min !== void 0) axis.min = min;
|
|
309
|
+
if (max !== void 0) axis.max = max;
|
|
310
|
+
if (interval !== void 0) axis.interval = interval;
|
|
311
|
+
return axis;
|
|
312
|
+
}
|
|
313
|
+
function buildDataLabel(base, hidden, { show, formatter, getLabel }) {
|
|
314
|
+
if (formatter === void 0) return show ? base : hidden;
|
|
315
|
+
const wrapped = typeof formatter === "string" ? formatter : (raw) => formatter(toParams(raw, getLabel));
|
|
316
|
+
if (!show) return { ...hidden, show: false, formatter: wrapped };
|
|
317
|
+
return { ...base, show: true, formatter: wrapped };
|
|
318
|
+
}
|
|
319
|
+
function pointLabel(text, formatter, base = {}) {
|
|
320
|
+
if (text == null || text === "") return void 0;
|
|
321
|
+
return typeof formatter === "function" ? { ...base, show: true } : { ...base, show: true, formatter: text };
|
|
322
|
+
}
|
|
323
|
+
function withPointLabels(values, labels, formatter, base) {
|
|
324
|
+
if (!labels?.some((text) => text != null && text !== "")) return values;
|
|
325
|
+
return values.map((value, index) => {
|
|
326
|
+
const label = pointLabel(labels[index], formatter, base);
|
|
327
|
+
return label ? { value, label } : value;
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
function seriesLabelLookup(series) {
|
|
331
|
+
return (seriesIndex, dataIndex) => series[seriesIndex]?.labels?.[dataIndex] ?? void 0;
|
|
332
|
+
}
|
|
333
|
+
function itemLabelLookup(items) {
|
|
334
|
+
return (_seriesIndex, dataIndex) => items[dataIndex]?.label;
|
|
335
|
+
}
|
|
336
|
+
function nameLabelLookup(nodes) {
|
|
337
|
+
const labels = /* @__PURE__ */ new Map();
|
|
338
|
+
const visit = (list) => {
|
|
339
|
+
for (const node of list) {
|
|
340
|
+
if (node.label != null && !labels.has(node.name)) labels.set(node.name, node.label);
|
|
341
|
+
if (node.children) visit(node.children);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
visit(nodes);
|
|
345
|
+
return (_seriesIndex, _dataIndex, name) => labels.get(name);
|
|
346
|
+
}
|
|
347
|
+
function hasPointLabels(series) {
|
|
348
|
+
return series.some((s) => s.labels?.some((text) => text != null && text !== ""));
|
|
349
|
+
}
|
|
350
|
+
function buildTooltip(base, { show, formatter, valueFormatter, getLabel }) {
|
|
351
|
+
if (!show) return void 0;
|
|
352
|
+
if (formatter === void 0 && valueFormatter === void 0) return base;
|
|
353
|
+
const tooltip = { ...base };
|
|
354
|
+
if (valueFormatter) {
|
|
355
|
+
tooltip.valueFormatter = (value) => typeof value === "number" ? valueFormatter(value) : String(value ?? "");
|
|
356
|
+
}
|
|
357
|
+
if (typeof formatter === "string") {
|
|
358
|
+
tooltip.formatter = formatter;
|
|
359
|
+
} else if (formatter) {
|
|
360
|
+
tooltip.formatter = (raw) => formatter((Array.isArray(raw) ? raw : [raw]).map((p) => toTooltipParams(p, getLabel)));
|
|
361
|
+
}
|
|
362
|
+
return tooltip;
|
|
363
|
+
}
|
|
364
|
+
function formatTooltipValue(value, valueFormatter) {
|
|
365
|
+
if (valueFormatter && typeof value === "number") return valueFormatter(value);
|
|
366
|
+
return String(value ?? "");
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
export { abbreviateNumber, anchorCenterGraphic, buildAxis, buildDataLabel, buildLegend, buildTooltip, createTooltipFormatter, escapeHtml, formatCurrency, formatDate, formatNumber, formatPercent, formatTooltipValue, generateBarChartDescription, generateDataTable, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, getLegendItems, getReplaceMerge, gridInset, hasPointLabels, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, pointLabel, seriesLabelLookup, withPointLabels };
|
package/dist/theme/index.cjs
CHANGED
|
@@ -272,13 +272,52 @@ function generateDarkTheme() {
|
|
|
272
272
|
var lightTheme = generateLightTheme();
|
|
273
273
|
var darkTheme = generateDarkTheme();
|
|
274
274
|
|
|
275
|
-
// src/theme/
|
|
275
|
+
// src/theme/constants.ts
|
|
276
276
|
var THEME_LIGHT = "classic-light";
|
|
277
277
|
var THEME_DARK = "classic-dark";
|
|
278
278
|
|
|
279
|
+
// src/theme/semantic.ts
|
|
280
|
+
var lightChartColors = {
|
|
281
|
+
series: lightTheme.color,
|
|
282
|
+
foreground: themeTokens.semanticColors.light.foreground,
|
|
283
|
+
mutedForeground: themeTokens.semanticColors.light.mutedForeground,
|
|
284
|
+
axisPointerLabel: themeTokens.semanticColors.light.mutedForeground,
|
|
285
|
+
divider: themeTokens.colors.gray[600],
|
|
286
|
+
success: themeTokens.semanticColors.light.success,
|
|
287
|
+
destructive: themeTokens.semanticColors.light.destructive
|
|
288
|
+
};
|
|
289
|
+
var darkChartColors = {
|
|
290
|
+
series: darkTheme.color,
|
|
291
|
+
foreground: themeTokens.semanticColors.dark.foreground,
|
|
292
|
+
mutedForeground: themeTokens.semanticColors.dark.mutedForeground,
|
|
293
|
+
// mutedForeground (gray[400]) is too light under the white label text
|
|
294
|
+
axisPointerLabel: themeTokens.colors.gray[600],
|
|
295
|
+
divider: themeTokens.colors.gray[700],
|
|
296
|
+
success: themeTokens.semanticColors.dark.success,
|
|
297
|
+
destructive: themeTokens.semanticColors.dark.destructive
|
|
298
|
+
};
|
|
299
|
+
function getChartColors(themeName) {
|
|
300
|
+
return themeName === THEME_DARK ? darkChartColors : lightChartColors;
|
|
301
|
+
}
|
|
302
|
+
function colorWithAlpha(color, alpha) {
|
|
303
|
+
let hex = /^#([\da-f]{3}|[\da-f]{6})$/i.exec(color)?.[1];
|
|
304
|
+
if (!hex) return color;
|
|
305
|
+
if (hex.length === 3) {
|
|
306
|
+
hex = hex.split("").map((c) => c + c).join("");
|
|
307
|
+
}
|
|
308
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
309
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
310
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
311
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
312
|
+
}
|
|
313
|
+
|
|
279
314
|
exports.THEME_DARK = THEME_DARK;
|
|
280
315
|
exports.THEME_LIGHT = THEME_LIGHT;
|
|
316
|
+
exports.colorWithAlpha = colorWithAlpha;
|
|
317
|
+
exports.darkChartColors = darkChartColors;
|
|
281
318
|
exports.darkTheme = darkTheme;
|
|
282
319
|
exports.generateDarkTheme = generateDarkTheme;
|
|
283
320
|
exports.generateLightTheme = generateLightTheme;
|
|
321
|
+
exports.getChartColors = getChartColors;
|
|
322
|
+
exports.lightChartColors = lightChartColors;
|
|
284
323
|
exports.lightTheme = lightTheme;
|
package/dist/theme/index.d.cts
CHANGED
|
@@ -133,11 +133,42 @@ declare const lightTheme: EChartsTheme;
|
|
|
133
133
|
declare const darkTheme: EChartsTheme;
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
*
|
|
136
|
+
* Semantic chart colours
|
|
137
|
+
*
|
|
138
|
+
* Colours that chart components set per series or per element (and so cannot
|
|
139
|
+
* come from the registered ECharts theme), derived from the design tokens.
|
|
140
|
+
*/
|
|
141
|
+
interface ChartSemanticColors {
|
|
142
|
+
/** Series palette, in the same order as the ECharts theme `color` array */
|
|
143
|
+
series: string[];
|
|
144
|
+
/** Primary text, e.g. a donut's centre value */
|
|
145
|
+
foreground: string;
|
|
146
|
+
/** Secondary text and marks: labels, gauge ticks, volume bars */
|
|
147
|
+
mutedForeground: string;
|
|
148
|
+
/** Background of axis-pointer labels (rendered with white text) */
|
|
149
|
+
axisPointerLabel: string;
|
|
150
|
+
/** Borders between top-level blocks, e.g. treemap groups */
|
|
151
|
+
divider: string;
|
|
152
|
+
/** Positive values */
|
|
153
|
+
success: string;
|
|
154
|
+
/** Negative values */
|
|
155
|
+
destructive: string;
|
|
156
|
+
}
|
|
157
|
+
declare const lightChartColors: ChartSemanticColors;
|
|
158
|
+
declare const darkChartColors: ChartSemanticColors;
|
|
159
|
+
/**
|
|
160
|
+
* Get the semantic chart colours for a registered theme name
|
|
161
|
+
* (`THEME_LIGHT` / `THEME_DARK`). Unknown names fall back to light.
|
|
162
|
+
*/
|
|
163
|
+
declare function getChartColors(themeName: string): ChartSemanticColors;
|
|
164
|
+
/**
|
|
165
|
+
* Apply an alpha channel to a hex colour (`#rgb` or `#rrggbb`), returning
|
|
166
|
+
* `rgba(...)`. Any other colour string is returned unchanged.
|
|
137
167
|
*/
|
|
168
|
+
declare function colorWithAlpha(color: string, alpha: number): string;
|
|
138
169
|
|
|
139
170
|
/** Theme name constants */
|
|
140
171
|
declare const THEME_LIGHT = "classic-light";
|
|
141
172
|
declare const THEME_DARK = "classic-dark";
|
|
142
173
|
|
|
143
|
-
export { type EChartsTheme, THEME_DARK, THEME_LIGHT, darkTheme, generateDarkTheme, generateLightTheme, lightTheme };
|
|
174
|
+
export { type ChartSemanticColors, type EChartsTheme, THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme };
|
package/dist/theme/index.d.ts
CHANGED
|
@@ -133,11 +133,42 @@ declare const lightTheme: EChartsTheme;
|
|
|
133
133
|
declare const darkTheme: EChartsTheme;
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
*
|
|
136
|
+
* Semantic chart colours
|
|
137
|
+
*
|
|
138
|
+
* Colours that chart components set per series or per element (and so cannot
|
|
139
|
+
* come from the registered ECharts theme), derived from the design tokens.
|
|
140
|
+
*/
|
|
141
|
+
interface ChartSemanticColors {
|
|
142
|
+
/** Series palette, in the same order as the ECharts theme `color` array */
|
|
143
|
+
series: string[];
|
|
144
|
+
/** Primary text, e.g. a donut's centre value */
|
|
145
|
+
foreground: string;
|
|
146
|
+
/** Secondary text and marks: labels, gauge ticks, volume bars */
|
|
147
|
+
mutedForeground: string;
|
|
148
|
+
/** Background of axis-pointer labels (rendered with white text) */
|
|
149
|
+
axisPointerLabel: string;
|
|
150
|
+
/** Borders between top-level blocks, e.g. treemap groups */
|
|
151
|
+
divider: string;
|
|
152
|
+
/** Positive values */
|
|
153
|
+
success: string;
|
|
154
|
+
/** Negative values */
|
|
155
|
+
destructive: string;
|
|
156
|
+
}
|
|
157
|
+
declare const lightChartColors: ChartSemanticColors;
|
|
158
|
+
declare const darkChartColors: ChartSemanticColors;
|
|
159
|
+
/**
|
|
160
|
+
* Get the semantic chart colours for a registered theme name
|
|
161
|
+
* (`THEME_LIGHT` / `THEME_DARK`). Unknown names fall back to light.
|
|
162
|
+
*/
|
|
163
|
+
declare function getChartColors(themeName: string): ChartSemanticColors;
|
|
164
|
+
/**
|
|
165
|
+
* Apply an alpha channel to a hex colour (`#rgb` or `#rrggbb`), returning
|
|
166
|
+
* `rgba(...)`. Any other colour string is returned unchanged.
|
|
137
167
|
*/
|
|
168
|
+
declare function colorWithAlpha(color: string, alpha: number): string;
|
|
138
169
|
|
|
139
170
|
/** Theme name constants */
|
|
140
171
|
declare const THEME_LIGHT = "classic-light";
|
|
141
172
|
declare const THEME_DARK = "classic-dark";
|
|
142
173
|
|
|
143
|
-
export { type EChartsTheme, THEME_DARK, THEME_LIGHT, darkTheme, generateDarkTheme, generateLightTheme, lightTheme };
|
|
174
|
+
export { type ChartSemanticColors, type EChartsTheme, THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme };
|
package/dist/theme/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { THEME_DARK, THEME_LIGHT, darkTheme, generateDarkTheme, generateLightTheme, lightTheme } from '../chunk-
|
|
1
|
+
export { THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme } from '../chunk-JM72YHZD.js';
|