@flux-ui/statistics 3.0.0-next.66 → 3.0.0-next.68
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 +1 -1
- package/dist/component/FluxStatisticsAreaChart.vue.d.ts +13 -5
- package/dist/component/FluxStatisticsBarChart.vue.d.ts +13 -5
- package/dist/component/FluxStatisticsBase.vue.d.ts +8 -15
- package/dist/component/FluxStatisticsBoxPlotChart.vue.d.ts +15 -0
- package/dist/component/FluxStatisticsBubbleChart.vue.d.ts +14 -0
- package/dist/component/FluxStatisticsCandlestickChart.vue.d.ts +15 -0
- package/dist/component/FluxStatisticsChange.vue.d.ts +2 -1
- package/dist/component/FluxStatisticsChart.vue.d.ts +8 -19
- package/dist/component/FluxStatisticsChartPane.vue.d.ts +9 -17
- package/dist/component/FluxStatisticsComparison.vue.d.ts +16 -0
- package/dist/component/FluxStatisticsDetailsTable.vue.d.ts +6 -13
- package/dist/component/FluxStatisticsDetailsTableRow.vue.d.ts +2 -1
- package/dist/component/FluxStatisticsDonutChart.vue.d.ts +10 -5
- package/dist/component/FluxStatisticsEmpty.vue.d.ts +19 -0
- package/dist/component/FluxStatisticsGrid.vue.d.ts +7 -11
- package/dist/component/FluxStatisticsHeatmapChart.vue.d.ts +15 -0
- package/dist/component/FluxStatisticsKpi.vue.d.ts +2 -1
- package/dist/component/FluxStatisticsLegend.vue.d.ts +7 -11
- package/dist/component/FluxStatisticsLegendItem.vue.d.ts +3 -1
- package/dist/component/FluxStatisticsLineChart.vue.d.ts +13 -5
- package/dist/component/FluxStatisticsMeter.vue.d.ts +2 -1
- package/dist/component/FluxStatisticsMetric.vue.d.ts +6 -13
- package/dist/component/FluxStatisticsMixedChart.vue.d.ts +17 -0
- package/dist/component/FluxStatisticsPieChart.vue.d.ts +10 -5
- package/dist/component/FluxStatisticsPolarAreaChart.vue.d.ts +14 -0
- package/dist/component/FluxStatisticsRadarChart.vue.d.ts +12 -0
- package/dist/component/FluxStatisticsRadialBar.vue.d.ts +11 -0
- package/dist/component/FluxStatisticsScatterChart.vue.d.ts +14 -0
- package/dist/component/FluxStatisticsSparkline.vue.d.ts +13 -0
- package/dist/component/FluxStatisticsTreemapChart.vue.d.ts +11 -0
- package/dist/component/index.d.ts +13 -0
- package/dist/composable/index.d.ts +10 -0
- package/dist/composable/useChartHoverSync.d.ts +9 -0
- package/dist/composable/useChartLegend.d.ts +14 -0
- package/dist/composable/useChartSeriesSetup.d.ts +23 -0
- package/dist/composable/useECharts.d.ts +9 -0
- package/dist/composable/usePieSlicesSetup.d.ts +14 -0
- package/dist/echarts.d.ts +1 -0
- package/dist/index.css +230 -37
- package/dist/index.d.ts +5 -2
- package/dist/index.js +10924 -9047
- package/dist/index.js.map +1 -1
- package/dist/util/baseOptions.d.ts +15 -0
- package/dist/util/colors.d.ts +4 -0
- package/dist/util/convert.d.ts +22 -0
- package/dist/util/defaultOptions.d.ts +76 -0
- package/dist/util/iconSvg.d.ts +2 -0
- package/dist/util/index.d.ts +7 -0
- package/dist/util/seriesDefaults.d.ts +15 -0
- package/dist/util/sparklineOptions.d.ts +7 -0
- package/package.json +14 -15
- package/src/component/FluxStatisticsAreaChart.vue +38 -41
- package/src/component/FluxStatisticsBarChart.vue +38 -33
- package/src/component/FluxStatisticsBase.vue +14 -1
- package/src/component/FluxStatisticsBoxPlotChart.vue +69 -0
- package/src/component/FluxStatisticsBubbleChart.vue +56 -0
- package/src/component/FluxStatisticsCandlestickChart.vue +81 -0
- package/src/component/FluxStatisticsChart.vue +19 -169
- package/src/component/FluxStatisticsChartPane.vue +23 -11
- package/src/component/FluxStatisticsComparison.vue +113 -0
- package/src/component/FluxStatisticsDonutChart.vue +39 -18
- package/src/component/FluxStatisticsEmpty.vue +44 -0
- package/src/component/FluxStatisticsHeatmapChart.vue +80 -0
- package/src/component/FluxStatisticsLegend.vue +33 -1
- package/src/component/FluxStatisticsLegendItem.vue +3 -1
- package/src/component/FluxStatisticsLineChart.vue +38 -41
- package/src/component/FluxStatisticsMixedChart.vue +55 -0
- package/src/component/FluxStatisticsPieChart.vue +39 -18
- package/src/component/FluxStatisticsPolarAreaChart.vue +53 -0
- package/src/component/FluxStatisticsRadarChart.vue +108 -0
- package/src/component/FluxStatisticsRadialBar.vue +48 -0
- package/src/component/FluxStatisticsScatterChart.vue +56 -0
- package/src/component/FluxStatisticsSparkline.vue +67 -0
- package/src/component/FluxStatisticsTreemapChart.vue +39 -0
- package/src/component/index.ts +13 -0
- package/src/composable/index.ts +10 -0
- package/src/composable/useChartHoverSync.ts +92 -0
- package/src/composable/useChartLegend.ts +23 -0
- package/src/composable/useChartSeriesSetup.ts +75 -0
- package/src/composable/useECharts.ts +55 -0
- package/src/composable/usePieSlicesSetup.ts +58 -0
- package/src/css/Base.module.scss +28 -1
- package/src/css/Chart.module.scss +66 -32
- package/src/css/ChartPane.module.scss +24 -9
- package/src/css/Comparison.module.scss +52 -0
- package/src/css/Empty.module.scss +39 -0
- package/src/css/Grid.module.scss +1 -0
- package/src/css/Legend.module.scss +11 -1
- package/src/css/Metric.module.scss +6 -0
- package/src/css/Sparkline.module.scss +13 -0
- package/src/echarts.ts +47 -0
- package/src/index.ts +7 -3
- package/src/util/baseOptions.ts +74 -0
- package/src/util/colors.ts +86 -0
- package/src/util/convert.ts +360 -0
- package/src/util/defaultOptions.ts +398 -0
- package/src/util/iconSvg.ts +20 -0
- package/src/util/index.ts +7 -0
- package/src/util/seriesDefaults.ts +210 -0
- package/src/util/sparklineOptions.ts +67 -0
package/src/echarts.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BarChart,
|
|
3
|
+
BoxplotChart,
|
|
4
|
+
CandlestickChart,
|
|
5
|
+
GaugeChart,
|
|
6
|
+
HeatmapChart,
|
|
7
|
+
LineChart,
|
|
8
|
+
PieChart,
|
|
9
|
+
RadarChart,
|
|
10
|
+
ScatterChart,
|
|
11
|
+
TreemapChart
|
|
12
|
+
} from 'echarts/charts';
|
|
13
|
+
import {
|
|
14
|
+
AxisPointerComponent,
|
|
15
|
+
GridComponent,
|
|
16
|
+
LegendComponent,
|
|
17
|
+
RadarComponent,
|
|
18
|
+
TitleComponent,
|
|
19
|
+
TooltipComponent,
|
|
20
|
+
VisualMapComponent
|
|
21
|
+
} from 'echarts/components';
|
|
22
|
+
import { use } from 'echarts/core';
|
|
23
|
+
import { LabelLayout, LegacyGridContainLabel } from 'echarts/features';
|
|
24
|
+
import { CanvasRenderer } from 'echarts/renderers';
|
|
25
|
+
|
|
26
|
+
use([
|
|
27
|
+
BarChart,
|
|
28
|
+
BoxplotChart,
|
|
29
|
+
CandlestickChart,
|
|
30
|
+
GaugeChart,
|
|
31
|
+
HeatmapChart,
|
|
32
|
+
LineChart,
|
|
33
|
+
PieChart,
|
|
34
|
+
RadarChart,
|
|
35
|
+
ScatterChart,
|
|
36
|
+
TreemapChart,
|
|
37
|
+
AxisPointerComponent,
|
|
38
|
+
GridComponent,
|
|
39
|
+
LegendComponent,
|
|
40
|
+
RadarComponent,
|
|
41
|
+
TitleComponent,
|
|
42
|
+
TooltipComponent,
|
|
43
|
+
VisualMapComponent,
|
|
44
|
+
LabelLayout,
|
|
45
|
+
LegacyGridContainLabel,
|
|
46
|
+
CanvasRenderer
|
|
47
|
+
]);
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { amber500, blue500, cyan500, emerald500, fuchsia500, green500, indigo500, lime500, orange500, pink500, purple500, red500, rose50, sky500, teal500, violet500, yellow500 } from '@flux-ui/internals';
|
|
2
|
+
import type { FluxStatisticsChartColor } from '@flux-ui/types';
|
|
2
3
|
|
|
3
4
|
export * from './component';
|
|
5
|
+
export * from './composable';
|
|
4
6
|
|
|
5
|
-
export
|
|
7
|
+
export type { ChartTooltipValueFormatter, SharedTooltipItem, Translator } from './util';
|
|
8
|
+
|
|
9
|
+
export const CHART_COLORS: readonly FluxStatisticsChartColor[] = [
|
|
6
10
|
'var(--chart-1)',
|
|
7
11
|
'var(--chart-2)',
|
|
8
12
|
'var(--chart-3)',
|
|
9
13
|
'var(--chart-4)'
|
|
10
14
|
];
|
|
11
15
|
|
|
12
|
-
export const CHART_COLORFUL_COLORS = [
|
|
16
|
+
export const CHART_COLORFUL_COLORS: readonly FluxStatisticsChartColor[] = [
|
|
13
17
|
red500,
|
|
14
18
|
orange500,
|
|
15
19
|
amber500,
|
|
@@ -27,4 +31,4 @@ export const CHART_COLORFUL_COLORS = [
|
|
|
27
31
|
fuchsia500,
|
|
28
32
|
pink500,
|
|
29
33
|
rose50
|
|
30
|
-
];
|
|
34
|
+
] as FluxStatisticsChartColor[];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { EChartsOption } from '~flux/statistics/composable';
|
|
2
|
+
|
|
3
|
+
const FOREGROUND_LABEL = { show: true, color: 'var(--foreground-secondary)' } as const;
|
|
4
|
+
const HIDDEN_AXIS = { show: false } as const;
|
|
5
|
+
const DASHED_SPLIT_LINE = { show: true, lineStyle: { type: 'dashed' as const, color: 'var(--gray-200)' } } as const;
|
|
6
|
+
|
|
7
|
+
interface CartesianBaseConfig {
|
|
8
|
+
readonly xAxisType?: 'category' | 'value';
|
|
9
|
+
readonly yAxisType?: 'value' | 'category';
|
|
10
|
+
readonly scale?: boolean;
|
|
11
|
+
readonly tooltipTrigger?: 'axis' | 'item';
|
|
12
|
+
readonly xAxisLabels?: boolean;
|
|
13
|
+
readonly yAxisLabels?: boolean;
|
|
14
|
+
readonly splitLines?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function buildCartesianGrid(xAxisLabels: boolean, yAxisLabels: boolean): EChartsOption['grid'] {
|
|
18
|
+
if (!xAxisLabels && !yAxisLabels) {
|
|
19
|
+
return { left: 0, right: 0, top: 0, bottom: 0, containLabel: false };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
left: yAxisLabels ? 9 : 0,
|
|
24
|
+
right: 9,
|
|
25
|
+
top: 9,
|
|
26
|
+
bottom: xAxisLabels ? 9 : 0,
|
|
27
|
+
containLabel: true
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function buildCartesianBaseOptions(config: CartesianBaseConfig = {}): EChartsOption {
|
|
32
|
+
const xAxisLabels = config.xAxisLabels ?? false;
|
|
33
|
+
const yAxisLabels = config.yAxisLabels ?? false;
|
|
34
|
+
const showSplitLines = config.splitLines ?? false;
|
|
35
|
+
|
|
36
|
+
const xSplitLine = showSplitLines && config.xAxisType === 'value' ? DASHED_SPLIT_LINE : HIDDEN_AXIS;
|
|
37
|
+
const ySplitLine = showSplitLines && config.yAxisType !== 'category' ? DASHED_SPLIT_LINE : HIDDEN_AXIS;
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
grid: buildCartesianGrid(xAxisLabels, yAxisLabels),
|
|
41
|
+
tooltip: { trigger: config.tooltipTrigger ?? 'item' },
|
|
42
|
+
xAxis: {
|
|
43
|
+
type: config.xAxisType ?? 'category',
|
|
44
|
+
show: true,
|
|
45
|
+
scale: config.scale && config.xAxisType === 'value',
|
|
46
|
+
splitLine: xSplitLine,
|
|
47
|
+
axisLabel: xAxisLabels ? FOREGROUND_LABEL : HIDDEN_AXIS,
|
|
48
|
+
axisLine: HIDDEN_AXIS,
|
|
49
|
+
axisTick: HIDDEN_AXIS
|
|
50
|
+
},
|
|
51
|
+
yAxis: {
|
|
52
|
+
type: config.yAxisType ?? 'value',
|
|
53
|
+
show: true,
|
|
54
|
+
scale: config.scale && config.yAxisType !== 'category',
|
|
55
|
+
splitLine: ySplitLine,
|
|
56
|
+
axisLabel: yAxisLabels ? FOREGROUND_LABEL : HIDDEN_AXIS,
|
|
57
|
+
axisLine: HIDDEN_AXIS,
|
|
58
|
+
axisTick: HIDDEN_AXIS
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const POLAR_BASE_OPTIONS: EChartsOption = {
|
|
64
|
+
tooltip: { show: false },
|
|
65
|
+
xAxis: { show: false },
|
|
66
|
+
yAxis: { show: false },
|
|
67
|
+
grid: { show: false }
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const SPARKLINE_AXIS_BASE: EChartsOption = {
|
|
71
|
+
tooltip: { trigger: 'axis' },
|
|
72
|
+
xAxis: { show: false, boundaryGap: false },
|
|
73
|
+
yAxis: { show: false, min: 'dataMin', max: 'dataMax' }
|
|
74
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
const CSS_VAR_PATTERN = /var\((--[^,)]+)(?:,\s*([^)]+))?\)/g;
|
|
4
|
+
|
|
5
|
+
const themeVersion = ref(0);
|
|
6
|
+
|
|
7
|
+
if (typeof document !== 'undefined') {
|
|
8
|
+
new MutationObserver(() => themeVersion.value++).observe(
|
|
9
|
+
document.documentElement,
|
|
10
|
+
{ attributes: true, attributeFilter: ['class', 'data-theme'] }
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useCssVarVersion() {
|
|
15
|
+
return themeVersion;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveCssVar(input: string, root?: HTMLElement): string {
|
|
19
|
+
if (typeof document === 'undefined' || !input.includes('var(')) {
|
|
20
|
+
return input;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const target = root ?? document.documentElement;
|
|
24
|
+
|
|
25
|
+
return input.replace(CSS_VAR_PATTERN, (match, name: string, fallback?: string) => {
|
|
26
|
+
const value = getComputedStyle(target).getPropertyValue(name).trim();
|
|
27
|
+
|
|
28
|
+
if (value) {
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return fallback ? resolveCssVar(fallback.trim(), target) : match;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
37
|
+
if (value === null || typeof value !== 'object') {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const proto = Object.getPrototypeOf(value);
|
|
42
|
+
return proto === Object.prototype || proto === null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function deepResolveCssVars<T>(value: T, root?: HTMLElement): T {
|
|
46
|
+
if (typeof value === 'string') {
|
|
47
|
+
return (value.includes('var(') ? resolveCssVar(value, root) : value) as T;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (Array.isArray(value)) {
|
|
51
|
+
let changed = false;
|
|
52
|
+
const out: unknown[] = new Array(value.length);
|
|
53
|
+
|
|
54
|
+
for (let i = 0; i < value.length; i++) {
|
|
55
|
+
const resolved = deepResolveCssVars(value[i], root);
|
|
56
|
+
|
|
57
|
+
if (resolved !== value[i]) {
|
|
58
|
+
changed = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
out[i] = resolved;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return (changed ? out : value) as T;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!isPlainObject(value)) {
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let changed = false;
|
|
72
|
+
const out: Record<string, unknown> = {};
|
|
73
|
+
|
|
74
|
+
for (const key of Object.keys(value)) {
|
|
75
|
+
const original = value[key];
|
|
76
|
+
const resolved = deepResolveCssVars(original, root);
|
|
77
|
+
|
|
78
|
+
if (resolved !== original) {
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
out[key] = resolved;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (changed ? out : value) as T;
|
|
86
|
+
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FluxStatisticsChartAreaSeries,
|
|
3
|
+
FluxStatisticsChartBarSeries,
|
|
4
|
+
FluxStatisticsChartBoxPlotSeries,
|
|
5
|
+
FluxStatisticsChartBubbleSeries,
|
|
6
|
+
FluxStatisticsChartCandlestickSeries,
|
|
7
|
+
FluxStatisticsChartCartesianSeries,
|
|
8
|
+
FluxStatisticsChartCategoryPoint,
|
|
9
|
+
FluxStatisticsChartColor,
|
|
10
|
+
FluxStatisticsChartGaugeSeries,
|
|
11
|
+
FluxStatisticsChartHeatmapSeries,
|
|
12
|
+
FluxStatisticsChartLineSeries,
|
|
13
|
+
FluxStatisticsChartMixedSeries,
|
|
14
|
+
FluxStatisticsChartPieSlice,
|
|
15
|
+
FluxStatisticsChartRadarSeries,
|
|
16
|
+
FluxStatisticsChartScatterSeries,
|
|
17
|
+
FluxStatisticsChartTreemapNode
|
|
18
|
+
} from '@flux-ui/types';
|
|
19
|
+
import type {
|
|
20
|
+
BarSeriesOption,
|
|
21
|
+
BoxplotSeriesOption,
|
|
22
|
+
CandlestickSeriesOption,
|
|
23
|
+
GaugeSeriesOption,
|
|
24
|
+
HeatmapSeriesOption,
|
|
25
|
+
LineSeriesOption,
|
|
26
|
+
PieSeriesOption,
|
|
27
|
+
RadarSeriesOption,
|
|
28
|
+
ScatterSeriesOption,
|
|
29
|
+
TreemapSeriesOption
|
|
30
|
+
} from 'echarts/charts';
|
|
31
|
+
import {
|
|
32
|
+
AREA_SERIES_DEFAULTS,
|
|
33
|
+
BAR_SERIES_DEFAULTS,
|
|
34
|
+
BOXPLOT_SERIES_DEFAULTS,
|
|
35
|
+
BUBBLE_SERIES_DEFAULTS,
|
|
36
|
+
CANDLESTICK_SERIES_DEFAULTS,
|
|
37
|
+
DONUT_SERIES_DEFAULTS,
|
|
38
|
+
GAUGE_SERIES_DEFAULTS,
|
|
39
|
+
HEATMAP_SERIES_DEFAULTS,
|
|
40
|
+
LINE_SERIES_DEFAULTS,
|
|
41
|
+
PIE_SERIES_DEFAULTS,
|
|
42
|
+
POLAR_AREA_SERIES_DEFAULTS,
|
|
43
|
+
RADAR_SERIES_DEFAULTS,
|
|
44
|
+
SCATTER_SERIES_DEFAULTS,
|
|
45
|
+
TREEMAP_SERIES_DEFAULTS
|
|
46
|
+
} from './seriesDefaults';
|
|
47
|
+
|
|
48
|
+
export function resolveChartColor(color?: FluxStatisticsChartColor): string | undefined {
|
|
49
|
+
if (!color) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (color.startsWith('#') || color.startsWith('var(')) {
|
|
54
|
+
return color;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return `var(--${color}-600)`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function extractLabels(
|
|
61
|
+
series: readonly { readonly data: readonly (number | FluxStatisticsChartCategoryPoint)[] }[]
|
|
62
|
+
): readonly string[] | undefined {
|
|
63
|
+
for (const s of series) {
|
|
64
|
+
const labels: string[] = [];
|
|
65
|
+
let hasAny = false;
|
|
66
|
+
|
|
67
|
+
for (const point of s.data) {
|
|
68
|
+
if (typeof point === 'object' && point !== null && typeof point.label === 'string') {
|
|
69
|
+
labels.push(point.label);
|
|
70
|
+
hasAny = true;
|
|
71
|
+
} else {
|
|
72
|
+
labels.push('');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (hasAny) {
|
|
77
|
+
return labels;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function extractValues(data: readonly (number | FluxStatisticsChartCategoryPoint)[]): number[] {
|
|
85
|
+
return data.map(point => typeof point === 'number' ? point : point.value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function toLineSeries(s: FluxStatisticsChartLineSeries, fallbackColor: string): LineSeriesOption {
|
|
89
|
+
return {
|
|
90
|
+
...LINE_SERIES_DEFAULTS,
|
|
91
|
+
name: s.name,
|
|
92
|
+
data: extractValues(s.data),
|
|
93
|
+
color: resolveChartColor(s.color) ?? fallbackColor
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function toAreaSeries(s: FluxStatisticsChartAreaSeries, fallbackColor: string): LineSeriesOption {
|
|
98
|
+
return {
|
|
99
|
+
...AREA_SERIES_DEFAULTS,
|
|
100
|
+
name: s.name,
|
|
101
|
+
data: extractValues(s.data),
|
|
102
|
+
color: resolveChartColor(s.color) ?? fallbackColor
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function toBarSeries(s: FluxStatisticsChartBarSeries, fallbackColor: string): BarSeriesOption {
|
|
107
|
+
return {
|
|
108
|
+
...BAR_SERIES_DEFAULTS,
|
|
109
|
+
name: s.name,
|
|
110
|
+
data: extractValues(s.data),
|
|
111
|
+
color: resolveChartColor(s.color) ?? fallbackColor
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function toMixedSeries(s: FluxStatisticsChartMixedSeries, fallbackColor: string): LineSeriesOption | BarSeriesOption {
|
|
116
|
+
const color = resolveChartColor(s.color) ?? fallbackColor;
|
|
117
|
+
const values = extractValues(s.data);
|
|
118
|
+
|
|
119
|
+
if (s.type === 'bar') {
|
|
120
|
+
return {
|
|
121
|
+
...BAR_SERIES_DEFAULTS,
|
|
122
|
+
barCategoryGap: '55%',
|
|
123
|
+
name: s.name,
|
|
124
|
+
data: values,
|
|
125
|
+
color
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (s.type === 'area') {
|
|
130
|
+
return {
|
|
131
|
+
...AREA_SERIES_DEFAULTS,
|
|
132
|
+
name: s.name,
|
|
133
|
+
data: values,
|
|
134
|
+
color
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
...LINE_SERIES_DEFAULTS,
|
|
140
|
+
lineStyle: { width: 2 },
|
|
141
|
+
name: s.name,
|
|
142
|
+
data: values,
|
|
143
|
+
color
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface PieDataItem {
|
|
148
|
+
readonly name: string;
|
|
149
|
+
readonly value: number;
|
|
150
|
+
readonly itemStyle?: { readonly color: string };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function buildPieData(slices: readonly FluxStatisticsChartPieSlice[], palette: readonly string[]): PieDataItem[] {
|
|
154
|
+
return slices.map((slice, idx) => {
|
|
155
|
+
const color = resolveChartColor(slice.color) ?? palette[idx % palette.length];
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
name: slice.label,
|
|
159
|
+
value: slice.value,
|
|
160
|
+
itemStyle: { color }
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function toPieSeries(slices: readonly FluxStatisticsChartPieSlice[], palette: readonly string[]): PieSeriesOption {
|
|
166
|
+
return {
|
|
167
|
+
...PIE_SERIES_DEFAULTS,
|
|
168
|
+
data: buildPieData(slices, palette)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function toDonutSeries(slices: readonly FluxStatisticsChartPieSlice[], palette: readonly string[]): PieSeriesOption {
|
|
173
|
+
return {
|
|
174
|
+
...DONUT_SERIES_DEFAULTS,
|
|
175
|
+
data: buildPieData(slices, palette)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function toPolarAreaSeries(slices: readonly FluxStatisticsChartPieSlice[], palette: readonly string[]): PieSeriesOption {
|
|
180
|
+
return {
|
|
181
|
+
...POLAR_AREA_SERIES_DEFAULTS,
|
|
182
|
+
data: buildPieData(slices, palette)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
interface RadarRing {
|
|
187
|
+
readonly value: readonly number[];
|
|
188
|
+
readonly name?: string;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function toRadarSeries(series: readonly FluxStatisticsChartRadarSeries[], palette: readonly string[]): RadarSeriesOption {
|
|
192
|
+
const data: RadarRing[] = series.map(ring => ({
|
|
193
|
+
value: ring.values,
|
|
194
|
+
name: ring.name
|
|
195
|
+
}));
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
...RADAR_SERIES_DEFAULTS,
|
|
199
|
+
data,
|
|
200
|
+
color: palette as string[]
|
|
201
|
+
} as RadarSeriesOption;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function toScatterSeries(s: FluxStatisticsChartScatterSeries, fallbackColor: string): ScatterSeriesOption {
|
|
205
|
+
return {
|
|
206
|
+
...SCATTER_SERIES_DEFAULTS,
|
|
207
|
+
name: s.name,
|
|
208
|
+
data: s.data.map(point => [point.x, point.y]),
|
|
209
|
+
color: resolveChartColor(s.color) ?? fallbackColor
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function toBubbleSeries(s: FluxStatisticsChartBubbleSeries, fallbackColor: string): ScatterSeriesOption {
|
|
214
|
+
const sizes = s.data.map(p => p.size);
|
|
215
|
+
const maxSize = Math.max(1, ...sizes);
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
...BUBBLE_SERIES_DEFAULTS,
|
|
219
|
+
name: s.name,
|
|
220
|
+
data: s.data.map(point => [point.x, point.y, point.size]),
|
|
221
|
+
symbolSize: (val: unknown) => {
|
|
222
|
+
const size = Array.isArray(val) ? (val[2] as number) : 0;
|
|
223
|
+
return 14 + (size / maxSize) * 30;
|
|
224
|
+
},
|
|
225
|
+
color: resolveChartColor(s.color) ?? fallbackColor
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function toCandlestickSeries(s: FluxStatisticsChartCandlestickSeries): CandlestickSeriesOption {
|
|
230
|
+
const positive = resolveChartColor(s.positiveColor) ?? 'var(--success-500)';
|
|
231
|
+
const negative = resolveChartColor(s.negativeColor) ?? 'var(--danger-500)';
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
...CANDLESTICK_SERIES_DEFAULTS,
|
|
235
|
+
name: s.name,
|
|
236
|
+
data: s.data.map(point => [point.open, point.close, point.low, point.high]),
|
|
237
|
+
itemStyle: {
|
|
238
|
+
color: positive,
|
|
239
|
+
color0: negative,
|
|
240
|
+
borderColor: positive,
|
|
241
|
+
borderColor0: negative
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function toBoxPlotSeries(s: FluxStatisticsChartBoxPlotSeries, fallbackColor: string): BoxplotSeriesOption {
|
|
247
|
+
const color = resolveChartColor(s.color) ?? fallbackColor;
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
...BOXPLOT_SERIES_DEFAULTS,
|
|
251
|
+
name: s.name,
|
|
252
|
+
data: s.data.map(point => [point.min, point.q1, point.median, point.q3, point.max]),
|
|
253
|
+
itemStyle: {
|
|
254
|
+
color,
|
|
255
|
+
borderColor: 'var(--foreground-prominent)',
|
|
256
|
+
borderWidth: 1
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function toHeatmapSeries(
|
|
262
|
+
s: FluxStatisticsChartHeatmapSeries,
|
|
263
|
+
xLabels: readonly (string | number)[],
|
|
264
|
+
yLabels: readonly (string | number)[]
|
|
265
|
+
): HeatmapSeriesOption {
|
|
266
|
+
const data = s.data.map(point => {
|
|
267
|
+
const xIdx = typeof point.x === 'number' ? point.x : xLabels.indexOf(point.x);
|
|
268
|
+
const yIdx = typeof point.y === 'number' ? point.y : yLabels.indexOf(point.y);
|
|
269
|
+
|
|
270
|
+
return [xIdx, yIdx, point.value];
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
...HEATMAP_SERIES_DEFAULTS,
|
|
275
|
+
name: s.name,
|
|
276
|
+
data
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
interface TreemapDataItem {
|
|
281
|
+
readonly name: string;
|
|
282
|
+
readonly value?: number;
|
|
283
|
+
readonly itemStyle?: { readonly color: string };
|
|
284
|
+
readonly children?: TreemapDataItem[];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function mapTreemapNode(node: FluxStatisticsChartTreemapNode, palette: readonly string[], index: number): TreemapDataItem {
|
|
288
|
+
const resolved = resolveChartColor(node.color);
|
|
289
|
+
const color = resolved ?? palette[index % palette.length];
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
name: node.name,
|
|
293
|
+
value: node.value,
|
|
294
|
+
itemStyle: { color },
|
|
295
|
+
children: node.children?.map((child, idx) => mapTreemapNode(child, palette, idx))
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function toTreemapSeries(
|
|
300
|
+
nodes: readonly FluxStatisticsChartTreemapNode[],
|
|
301
|
+
palette: readonly string[]
|
|
302
|
+
): TreemapSeriesOption {
|
|
303
|
+
return {
|
|
304
|
+
...TREEMAP_SERIES_DEFAULTS,
|
|
305
|
+
data: nodes.map((node, idx) => mapTreemapNode(node, palette, idx)),
|
|
306
|
+
levels: [{
|
|
307
|
+
itemStyle: { borderColor: 'var(--surface)', borderWidth: 3, gapWidth: 0 }
|
|
308
|
+
}, {
|
|
309
|
+
itemStyle: { gapWidth: 1 }
|
|
310
|
+
}, {
|
|
311
|
+
itemStyle: { gapWidth: 1 }
|
|
312
|
+
}]
|
|
313
|
+
} as TreemapSeriesOption;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function toGaugeSeries(
|
|
317
|
+
s: FluxStatisticsChartGaugeSeries,
|
|
318
|
+
fallbackColor: string,
|
|
319
|
+
index: number,
|
|
320
|
+
total: number
|
|
321
|
+
): GaugeSeriesOption {
|
|
322
|
+
const color = resolveChartColor(s.color) ?? fallbackColor;
|
|
323
|
+
const ringStep = total > 1 ? 30 : 0;
|
|
324
|
+
const baseRadius = s.radius ?? (90 - index * ringStep);
|
|
325
|
+
const labelOffset = (total - 1 - index) * 22 - 30;
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
...GAUGE_SERIES_DEFAULTS,
|
|
329
|
+
radius: `${baseRadius}%`,
|
|
330
|
+
data: [{ value: s.value, name: s.name, itemStyle: { color } }],
|
|
331
|
+
progress: {
|
|
332
|
+
show: true,
|
|
333
|
+
width: 14,
|
|
334
|
+
roundCap: true,
|
|
335
|
+
itemStyle: { color }
|
|
336
|
+
},
|
|
337
|
+
title: {
|
|
338
|
+
show: true,
|
|
339
|
+
color: 'var(--foreground-secondary)',
|
|
340
|
+
fontSize: 12,
|
|
341
|
+
fontWeight: 500,
|
|
342
|
+
fontFamily: 'inherit',
|
|
343
|
+
offsetCenter: [0, `${labelOffset}%`]
|
|
344
|
+
},
|
|
345
|
+
detail: {
|
|
346
|
+
show: total === 1,
|
|
347
|
+
color: 'var(--foreground-prominent)',
|
|
348
|
+
fontSize: 28,
|
|
349
|
+
fontWeight: 800,
|
|
350
|
+
fontFamily: 'inherit',
|
|
351
|
+
offsetCenter: [0, '10%'],
|
|
352
|
+
formatter: '{value}%'
|
|
353
|
+
}
|
|
354
|
+
} as GaugeSeriesOption;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export function cartesianFallbackLabels(series: readonly FluxStatisticsChartCartesianSeries[]): string[] {
|
|
358
|
+
const longest = series.reduce((max, s) => Math.max(max, s.data.length), 0);
|
|
359
|
+
return Array.from({ length: longest }, (_, i) => String(i + 1));
|
|
360
|
+
}
|