@zerodep-dev/vue-tsx-pro-ui 0.3.2 → 0.4.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/charts/AreaChart.d.ts +1 -1
- package/dist/charts/AreaChart.js +6 -48
- package/dist/charts/AreaChart.js.map +1 -1
- package/dist/charts/BarChart.js +6 -49
- package/dist/charts/BarChart.js.map +1 -1
- package/dist/charts/ChordChart.d.ts +7 -2
- package/dist/charts/ChordChart.js +151 -60
- package/dist/charts/ChordChart.js.map +1 -1
- package/dist/charts/ComboChart.d.ts +2 -1
- package/dist/charts/ComboChart.js +17 -93
- package/dist/charts/ComboChart.js.map +1 -1
- package/dist/charts/DonutChart.d.ts +6 -1
- package/dist/charts/DonutChart.js +134 -51
- package/dist/charts/DonutChart.js.map +1 -1
- package/dist/charts/FunnelChart.js.map +1 -1
- package/dist/charts/GraphChart.d.ts +22 -10
- package/dist/charts/GraphChart.js +415 -175
- package/dist/charts/GraphChart.js.map +1 -1
- package/dist/charts/LineChart.d.ts +1 -29
- package/dist/charts/LineChart.js +6 -54
- package/dist/charts/LineChart.js.map +1 -1
- package/dist/charts/MapChart.d.ts +21 -48
- package/dist/charts/MapChart.js +426 -353
- package/dist/charts/MapChart.js.map +1 -1
- package/dist/charts/RadarChart.js.map +1 -1
- package/dist/charts/SankeyChart.d.ts +7 -2
- package/dist/charts/SankeyChart.js +164 -76
- package/dist/charts/SankeyChart.js.map +1 -1
- package/dist/charts/ScatterChart.d.ts +7 -1
- package/dist/charts/ScatterChart.js +45 -30
- package/dist/charts/ScatterChart.js.map +1 -1
- package/dist/charts/TimelineChart.d.ts +8 -1
- package/dist/charts/TimelineChart.js +141 -89
- package/dist/charts/TimelineChart.js.map +1 -1
- package/dist/charts/TreemapChart.d.ts +6 -0
- package/dist/charts/TreemapChart.js +174 -58
- package/dist/charts/TreemapChart.js.map +1 -1
- package/dist/charts/WaterfallChart.d.ts +6 -7
- package/dist/charts/WaterfallChart.js +46 -51
- package/dist/charts/WaterfallChart.js.map +1 -1
- package/dist/charts/XYChart.d.ts +44 -35
- package/dist/charts/XYChart.js +493 -281
- package/dist/charts/XYChart.js.map +1 -1
- package/dist/charts/axis-ticks.d.ts +1 -1
- package/dist/charts/axis-ticks.js +4 -5
- package/dist/charts/axis-ticks.js.map +1 -1
- package/dist/charts/flow.d.ts +4 -11
- package/dist/charts/flow.js +4 -4
- package/dist/charts/flow.js.map +1 -1
- package/dist/charts/frame.d.ts +6 -30
- package/dist/charts/frame.js +6 -99
- package/dist/charts/frame.js.map +1 -1
- package/dist/charts/geo/projection.d.ts +45 -0
- package/dist/charts/geo/projection.js +201 -0
- package/dist/charts/geo/projection.js.map +1 -0
- package/dist/charts/geo/topojson.d.ts +50 -0
- package/dist/charts/geo/topojson.js +119 -0
- package/dist/charts/geo/topojson.js.map +1 -0
- package/dist/charts/index.d.ts +21 -9
- package/dist/charts/index.js +19 -8
- package/dist/charts/label-layout.js.map +1 -1
- package/dist/charts/layout/chord.d.ts +44 -0
- package/dist/charts/layout/chord.js +45 -0
- package/dist/charts/layout/chord.js.map +1 -0
- package/dist/charts/layout/graph.d.ts +49 -0
- package/dist/charts/layout/graph.js +268 -0
- package/dist/charts/layout/graph.js.map +1 -0
- package/dist/charts/layout/sankey.d.ts +79 -0
- package/dist/charts/layout/sankey.js +200 -0
- package/dist/charts/layout/sankey.js.map +1 -0
- package/dist/charts/layout/treemap.d.ts +46 -0
- package/dist/charts/layout/treemap.js +152 -0
- package/dist/charts/layout/treemap.js.map +1 -0
- package/dist/charts/marks.d.ts +79 -0
- package/dist/charts/marks.js +231 -0
- package/dist/charts/marks.js.map +1 -0
- package/dist/charts/plot.d.ts +107 -0
- package/dist/charts/plot.js +231 -0
- package/dist/charts/plot.js.map +1 -0
- package/dist/charts/scale.d.ts +29 -0
- package/dist/charts/scale.js +244 -0
- package/dist/charts/scale.js.map +1 -0
- package/dist/charts/shape.d.ts +46 -0
- package/dist/charts/shape.js +131 -0
- package/dist/charts/shape.js.map +1 -0
- package/dist/charts/stack.d.ts +17 -0
- package/dist/charts/stack.js +35 -0
- package/dist/charts/stack.js.map +1 -0
- package/dist/charts/text-size.d.ts +12 -0
- package/dist/charts/text-size.js +32 -0
- package/dist/charts/text-size.js.map +1 -0
- package/dist/display/VirtualList.d.ts +1 -2
- package/dist/display/VirtualList.js +11 -21
- package/dist/display/VirtualList.js.map +1 -1
- package/dist/input/Combobox.d.ts +1 -1
- package/dist/input/Combobox.js.map +1 -1
- package/dist/input/ListPopover.d.ts +1 -1
- package/dist/input/ListPopover.js.map +1 -1
- package/dist/input/Listbox.d.ts +1 -2
- package/dist/input/Listbox.js +7 -19
- package/dist/input/Listbox.js.map +1 -1
- package/dist/input/Select.d.ts +1 -1
- package/dist/input/Select.js.map +1 -1
- package/dist/maps/china-provinces.js +35680 -0
- package/dist/maps/china-provinces.js.map +1 -0
- package/dist/maps/china.d.ts +13 -0
- package/dist/maps/china.js +8 -0
- package/dist/maps/china.js.map +1 -0
- package/dist/maps/index.d.ts +9 -0
- package/dist/maps/index.js +3 -0
- package/dist/maps/world-countries.js +22273 -0
- package/dist/maps/world-countries.js.map +1 -0
- package/dist/maps/world.d.ts +13 -0
- package/dist/maps/world.js +8 -0
- package/dist/maps/world.js.map +1 -0
- package/dist/overlay/CommandPalette.d.ts +1 -1
- package/dist/overlay/CommandPalette.js.map +1 -1
- package/dist/overlay/detached.js +14 -1
- package/dist/overlay/detached.js.map +1 -1
- package/dist/patterns/QueryTable.js +0 -2
- package/dist/patterns/QueryTable.js.map +1 -1
- package/dist/table/DataTable.js +1 -1
- package/dist/table/DataTable.js.map +1 -1
- package/dist/table/DataTableView.d.ts +0 -3
- package/dist/table/DataTableView.js +32 -26
- package/dist/table/DataTableView.js.map +1 -1
- package/dist/table/table-styles.d.ts +1 -1
- package/dist/table/table-styles.js +0 -13
- package/dist/table/table-styles.js.map +1 -1
- package/package.json +7 -8
- package/dist/charts/single.d.ts +0 -41
- package/dist/charts/single.js +0 -66
- package/dist/charts/single.js.map +0 -1
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { svgNumber } from "../utils/data/number.js";
|
|
2
|
+
import { t } from "../config/theme/tokens.js";
|
|
2
3
|
import { useLang } from "../config/context.js";
|
|
3
4
|
import { useFormat } from "../config/lang/format.js";
|
|
4
5
|
import { Skeleton } from "../feedback/Skeleton.js";
|
|
6
|
+
import { ChartTooltipBox, chartSvgStyles, pointerIn, usePlotArea } from "./plot.js";
|
|
7
|
+
import { horizontalLinkPath } from "./shape.js";
|
|
5
8
|
import { ChartFrame, chartTooltip, useChartValueText } from "./frame.js";
|
|
6
9
|
import { flowData, resolveFlowNodes } from "./flow.js";
|
|
7
|
-
import {
|
|
10
|
+
import { sankeyLayout } from "./layout/sankey.js";
|
|
8
11
|
import { computed, createVNode, shallowRef } from "vue";
|
|
9
12
|
import { _component, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
10
13
|
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
11
|
-
import { FitMode, Sankey, SankeyNodeAlign } from "@unovis/ts";
|
|
12
14
|
//#region src/charts/SankeyChart.tsx
|
|
13
|
-
/**
|
|
14
|
-
* 桑基图:节点之间的流量画成粗细不同的带子,看量从哪来、到哪去、在哪流失(转化漏斗、预算分配、能源流向)。
|
|
15
|
-
*
|
|
16
|
-
* <SankeyChart aria-label="注册转化" nodes={steps} links={flows} unit="人" />
|
|
17
|
-
*
|
|
18
|
-
* 节点是 { id, label?, color? },流向是 { source, target, value }(value 可以是 Decimal、bigint,要大于 0;不能成环)。
|
|
19
|
-
* 带子取起点节点的颜色;悬停带子出“起点 → 终点”的流量,悬停节点出它的总量。节点旁写名字与总量。
|
|
20
|
-
* 单位、加载与空状态、给读屏的数据表(每条流向一行)同其他图表(见 ChartFrame)。
|
|
21
|
-
*/
|
|
22
15
|
/** 节点怎样排:justify 两端对齐(默认),left、right 靠向一侧,center 居中 */
|
|
23
16
|
/** 未声明的属性落到外层 div(role="figure")上,aria-label 是图表的名称 */
|
|
24
|
-
/** SankeyChart
|
|
17
|
+
/** SankeyChart 的样式:节点、带子、名字、骨架 */
|
|
25
18
|
var sankeyChartStyles = css.create({
|
|
26
|
-
root: {
|
|
27
|
-
"--vis-sankey-node-label-color": colors.text,
|
|
28
|
-
"--vis-sankey-node-label-font-size": fontSizes.xs,
|
|
29
|
-
"--vis-sankey-node-label-font-weight": "500",
|
|
30
|
-
"--vis-sankey-node-label-background-fill-color": colors.surface,
|
|
31
|
-
"--vis-sankey-node-label-background-stroke-color": colors.borderMuted,
|
|
32
|
-
"--vis-sankey-node-sublabel-font-size": fontSizes.xs,
|
|
33
|
-
"--vis-sankey-link-opacity": "0.35",
|
|
34
|
-
"--vis-sankey-link-hover-opacity": "0.6"
|
|
35
|
-
},
|
|
36
19
|
skeleton: {
|
|
37
20
|
display: "block",
|
|
38
21
|
width: "100%",
|
|
39
22
|
height: "100%"
|
|
23
|
+
},
|
|
24
|
+
node: {
|
|
25
|
+
stroke: "_surface",
|
|
26
|
+
strokeWidth: 1
|
|
27
|
+
},
|
|
28
|
+
link: {
|
|
29
|
+
fill: "none",
|
|
30
|
+
strokeOpacity: .35,
|
|
31
|
+
transition: `stroke-opacity ${t.durations.sm} ${t.easings.standard}`
|
|
32
|
+
},
|
|
33
|
+
linkActive: { strokeOpacity: .6 },
|
|
34
|
+
linkFaded: { strokeOpacity: .12 },
|
|
35
|
+
name: {
|
|
36
|
+
fill: "_text",
|
|
37
|
+
fontSize: "_xs",
|
|
38
|
+
fontWeight: "_medium"
|
|
39
|
+
},
|
|
40
|
+
value: {
|
|
41
|
+
fill: "_textMuted",
|
|
42
|
+
fontSize: "_xs"
|
|
40
43
|
}
|
|
41
44
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
};
|
|
48
|
-
/** 交给 Unovis 的节点、流向 */
|
|
49
|
-
/** Unovis 布局后:流向的起点、终点换成了节点对象,节点带上了总量 value */
|
|
45
|
+
/** 节点的宽、同一列里节点的间距、上下留的空(px) */
|
|
46
|
+
var NODE_WIDTH = 12;
|
|
47
|
+
var NODE_PADDING = 16;
|
|
48
|
+
var MARGIN = 8;
|
|
49
|
+
/** 悬停的是节点还是带子 */
|
|
50
50
|
/** 桑基图 */
|
|
51
51
|
var SankeyChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
52
52
|
const _cache = [];
|
|
@@ -59,7 +59,6 @@ var SankeyChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
59
59
|
const tooltip = readonlyProp(_props, "tooltip", true);
|
|
60
60
|
const loading = readonlyProp(_props, "loading", false);
|
|
61
61
|
const empty = readonlyProp(_props, "empty");
|
|
62
|
-
const sx = readonlyProp(_props, "sx");
|
|
63
62
|
const rest = restProps(_props, _ctx, true);
|
|
64
63
|
const lang = useLang();
|
|
65
64
|
const format = useFormat();
|
|
@@ -69,53 +68,138 @@ var SankeyChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
69
68
|
unit: unit.value
|
|
70
69
|
}));
|
|
71
70
|
const labelOf = (id) => resolved.value.get(id)?.label ?? id;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
71
|
+
const area = usePlotArea();
|
|
72
|
+
let hovered = shallowRef(null);
|
|
73
|
+
const layout = computed(() => {
|
|
74
|
+
const data = flowData(resolved.value, links.value);
|
|
75
|
+
return {
|
|
76
|
+
...sankeyLayout(data.nodes.map((node) => node.id), data.links.map((link) => ({
|
|
77
|
+
source: link.source,
|
|
78
|
+
target: link.target,
|
|
79
|
+
value: link.amount
|
|
80
|
+
})), {
|
|
81
|
+
width: area.width,
|
|
82
|
+
height: Math.max(0, area.height - 16),
|
|
83
|
+
nodeWidth: NODE_WIDTH,
|
|
84
|
+
nodePadding: NODE_PADDING,
|
|
85
|
+
align: align.value
|
|
86
|
+
}),
|
|
87
|
+
flows: data.links
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
function onPointermove(event) {
|
|
91
|
+
const target = event.target.closest?.("[data-node],[data-link]");
|
|
92
|
+
if (!target) {
|
|
93
|
+
hovered.value = null;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const { x, y } = pointerIn(event, event.currentTarget);
|
|
97
|
+
const node = target.getAttribute("data-node");
|
|
98
|
+
hovered.value = node === null ? {
|
|
99
|
+
kind: "link",
|
|
100
|
+
index: Number(target.getAttribute("data-link")),
|
|
101
|
+
x,
|
|
102
|
+
y
|
|
103
|
+
} : {
|
|
104
|
+
kind: "node",
|
|
105
|
+
index: Number(node),
|
|
106
|
+
x,
|
|
107
|
+
y
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** 带子与悬停的节点相连(悬停带子时只有它自己):true 突出、false 变淡、undefined 没有悬停 */
|
|
111
|
+
const linkState = (index) => {
|
|
112
|
+
const current = hovered.value;
|
|
113
|
+
if (!current) return void 0;
|
|
114
|
+
if (current.kind === "link") return current.index === index;
|
|
115
|
+
const link = layout.value.links[index];
|
|
116
|
+
const node = layout.value.nodes[current.index];
|
|
117
|
+
return link.source === node || link.target === node;
|
|
118
|
+
};
|
|
119
|
+
function renderPlot() {
|
|
120
|
+
const { width } = area;
|
|
121
|
+
return createVNode("svg", mergeJsxProps({
|
|
122
|
+
"width": width,
|
|
123
|
+
"height": area.height
|
|
124
|
+
}, css.attrs(chartSvgStyles.svg), {
|
|
125
|
+
"onPointermove": onPointermove,
|
|
126
|
+
"onPointerleave": _cache[0] || (_cache[0] = () => hovered.value = null)
|
|
127
|
+
}), [createVNode("g", mergeJsxProps({ "transform": `translate(0,${MARGIN})` }, css.attrs(chartSvgStyles.fadeIn)), [createVNode("g", { "data-part": "links" }, [layout.value.links.map((link, index) => {
|
|
128
|
+
const state = linkState(index);
|
|
129
|
+
return createVNode("path", mergeJsxProps({
|
|
130
|
+
"key": index,
|
|
131
|
+
"data-part": "link",
|
|
132
|
+
"data-link": index,
|
|
133
|
+
"d": horizontalLinkPath(link.source.x1, link.y0, link.target.x0, link.y1),
|
|
134
|
+
"stroke-width": svgNumber(Math.max(1, link.width))
|
|
135
|
+
}, css.attrs(sankeyChartStyles.link, chartSvgStyles.stroke(resolved.value.get(link.source.id).color), state === true && sankeyChartStyles.linkActive, state === false && sankeyChartStyles.linkFaded)), null);
|
|
136
|
+
})]), layout.value.nodes.map((node, index) => {
|
|
137
|
+
const info = resolved.value.get(node.id);
|
|
138
|
+
const right = node.x0 < width / 2;
|
|
139
|
+
const middle = (node.y0 + node.y1) / 2;
|
|
140
|
+
const x = right ? node.x1 + 6 : node.x0 - 6;
|
|
141
|
+
const anchor = right ? "start" : "end";
|
|
142
|
+
return createVNode("g", { "key": node.id }, [
|
|
143
|
+
createVNode("rect", mergeJsxProps({
|
|
144
|
+
"data-part": "node",
|
|
145
|
+
"data-node": index,
|
|
146
|
+
"x": svgNumber(node.x0),
|
|
147
|
+
"y": svgNumber(node.y0),
|
|
148
|
+
"width": NODE_WIDTH,
|
|
149
|
+
"height": svgNumber(Math.max(1, node.y1 - node.y0)),
|
|
150
|
+
"rx": 2
|
|
151
|
+
}, css.attrs(sankeyChartStyles.node, chartSvgStyles.fill(info.color))), null),
|
|
152
|
+
createVNode("text", mergeJsxProps({
|
|
153
|
+
"x": x,
|
|
154
|
+
"y": svgNumber(middle - 7),
|
|
155
|
+
"text-anchor": anchor,
|
|
156
|
+
"dominant-baseline": "central"
|
|
157
|
+
}, css.attrs(sankeyChartStyles.name, chartSvgStyles.halo, chartSvgStyles.noPointer)), [info.label]),
|
|
158
|
+
createVNode("text", mergeJsxProps({
|
|
159
|
+
"x": x,
|
|
160
|
+
"y": svgNumber(middle + 8),
|
|
161
|
+
"text-anchor": anchor,
|
|
162
|
+
"dominant-baseline": "central"
|
|
163
|
+
}, css.attrs(sankeyChartStyles.value, chartSvgStyles.halo, chartSvgStyles.noPointer)), [valueText(node.value)])
|
|
164
|
+
]);
|
|
165
|
+
})])]);
|
|
166
|
+
}
|
|
167
|
+
const tip = () => {
|
|
168
|
+
const current = hovered.value;
|
|
169
|
+
if (!tooltip.value || !current) return null;
|
|
170
|
+
if (current.kind === "node") {
|
|
171
|
+
const node = layout.value.nodes[current.index];
|
|
172
|
+
const info = node && resolved.value.get(node.id);
|
|
173
|
+
if (!node || !info) return null;
|
|
94
174
|
return {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}])),
|
|
103
|
-
[Sankey.selectors.node]: (node) => chart.tooltip(chartTooltip(node.label, [{
|
|
104
|
-
label: lang.chartTotal,
|
|
105
|
-
color: node.color,
|
|
106
|
-
value: valueText(node.value)
|
|
107
|
-
}]))
|
|
108
|
-
}
|
|
175
|
+
x: current.x,
|
|
176
|
+
y: current.y,
|
|
177
|
+
content: chartTooltip(info.label, [{
|
|
178
|
+
label: lang.chartTotal,
|
|
179
|
+
color: info.color,
|
|
180
|
+
value: valueText(node.value)
|
|
181
|
+
}])
|
|
109
182
|
};
|
|
110
183
|
}
|
|
111
|
-
|
|
184
|
+
const link = layout.value.links[current.index];
|
|
185
|
+
if (!link) return null;
|
|
186
|
+
const source = resolved.value.get(link.source.id);
|
|
187
|
+
return {
|
|
188
|
+
x: current.x,
|
|
189
|
+
y: current.y,
|
|
190
|
+
content: chartTooltip(lang.chartFlowLabel(source.label, labelOf(link.target.id)), [{
|
|
191
|
+
label: lang.chartValue,
|
|
192
|
+
color: source.color,
|
|
193
|
+
value: valueText(layout.value.flows[link.index].value)
|
|
194
|
+
}])
|
|
195
|
+
};
|
|
196
|
+
};
|
|
112
197
|
return () => createVNode(ChartFrame, mergeJsxProps(rest, {
|
|
113
|
-
"sx": [sankeyChartStyles.root, sx.value],
|
|
114
198
|
"unit": unit.value,
|
|
115
199
|
"height": height.value,
|
|
116
|
-
"legend": _cache[
|
|
117
|
-
"hiddenKeys": _cache[
|
|
118
|
-
"onLegendToggle": _cache[
|
|
200
|
+
"legend": _cache[1] || (_cache[1] = []),
|
|
201
|
+
"hiddenKeys": _cache[2] || (_cache[2] = []),
|
|
202
|
+
"onLegendToggle": _cache[3] || (_cache[3] = () => {}),
|
|
119
203
|
"loading": loading.value,
|
|
120
204
|
"skeleton": createVNode(Skeleton, { "sx": sankeyChartStyles.skeleton }, null),
|
|
121
205
|
"isEmpty": nodes.value.length === 0,
|
|
@@ -127,7 +211,12 @@ var SankeyChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
127
211
|
cells: [format.number(link.value, valueFormat.value)]
|
|
128
212
|
}))
|
|
129
213
|
},
|
|
130
|
-
"plotRef":
|
|
214
|
+
"plotRef": area.ref,
|
|
215
|
+
"plot": area.ready ? [renderPlot(), createVNode(ChartTooltipBox, {
|
|
216
|
+
"key": "tooltip",
|
|
217
|
+
"tip": tip(),
|
|
218
|
+
"width": area.width
|
|
219
|
+
}, null)] : null
|
|
131
220
|
}), null);
|
|
132
221
|
}, {
|
|
133
222
|
name: "SankeyChart",
|
|
@@ -140,8 +229,7 @@ var SankeyChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
140
229
|
"align",
|
|
141
230
|
"tooltip",
|
|
142
231
|
"loading",
|
|
143
|
-
"empty"
|
|
144
|
-
"sx"
|
|
232
|
+
"empty"
|
|
145
233
|
]
|
|
146
234
|
});
|
|
147
235
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SankeyChart.js","names":["_vtp_readonlyProp","_vtp_restProps","_vue_computed","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/charts/SankeyChart.tsx"],"sourcesContent":["/**\n * 桑基图:节点之间的流量画成粗细不同的带子,看量从哪来、到哪去、在哪流失(转化漏斗、预算分配、能源流向)。\n *\n * <SankeyChart aria-label=\"注册转化\" nodes={steps} links={flows} unit=\"人\" />\n *\n * 节点是 { id, label?, color? },流向是 { source, target, value }(value 可以是 Decimal、bigint,要大于 0;不能成环)。\n * 带子取起点节点的颜色;悬停带子出“起点 → 终点”的流量,悬停节点出它的总量。节点旁写名字与总量。\n * 单位、加载与空状态、给读屏的数据表(每条流向一行)同其他图表(见 ChartFrame)。\n */\nimport { FitMode, Sankey, SankeyNodeAlign } from '@unovis/ts';\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { useLang } from '../config/context';\nimport { useFormat, type NumericFormat } from '../config/lang/format';\nimport { colors, fontSizes } from '../config/theme/tokens';\nimport { Skeleton } from '../feedback/Skeleton';\nimport type { PartProps } from '../utils/props';\nimport { flowData, resolveFlowNodes, type FlowLink, type FlowNode } from './flow';\nimport { ChartFrame, chartTooltip, useChartValueText, type ChartFrameParts } from './frame';\nimport { useSingleChart, type SingleChart } from './single';\n\n/** 节点怎样排:justify 两端对齐(默认),left、right 靠向一侧,center 居中 */\nexport interface SankeyAligns {\n /** 两端对齐(默认):按离起点的远近分列,没有流出的节点都排到最右一列 */\n justify: unknown;\n /** 靠左:每个节点排在离起点最近的一列 */\n left: unknown;\n /** 靠右:每个节点排在离终点最近的一列 */\n right: unknown;\n /** 居中:同靠左,但没有流入的节点往右挪,贴着它流向的节点 */\n center: unknown;\n}\n\n/** 未声明的属性落到外层 div(role=\"figure\")上,aria-label 是图表的名称 */\nexport interface SankeyChartProps extends PartProps<'div', 'role' | 'aria-busy'>, ChartFrameParts {\n /** 节点 */\n nodes: readonly FlowNode[];\n /** 流向 */\n links: readonly FlowLink[];\n /** 数值的格式(可缩放,如以万元为单位):节点旁的总量、提示框、数据表共用 */\n valueFormat?: NumericFormat;\n /** 单位:显示在左上方,数值后面带上 */\n unit?: string;\n /**\n * 绘图区的高度;数字按 px\n * @default 320\n */\n height?: number | string;\n /**\n * 节点的排法\n * @default 'justify'\n */\n align?: keyof SankeyAligns;\n /**\n * 悬停时的提示框\n * @default true\n */\n tooltip?: boolean;\n /**\n * 加载中:绘图区显示骨架,带 aria-busy\n * @default false\n */\n loading?: boolean;\n /** 没有数据时显示的内容,默认是“暂无数据” */\n empty?: VNodeChild;\n}\n\n/** SankeyChart 的样式:root(--vis-sankey-* 主题变量)、skeleton */\nexport const sankeyChartStyles = css.create({\n root: {\n '--vis-sankey-node-label-color': colors.text,\n '--vis-sankey-node-label-font-size': fontSizes.xs,\n '--vis-sankey-node-label-font-weight': '500',\n '--vis-sankey-node-label-background-fill-color': colors.surface,\n '--vis-sankey-node-label-background-stroke-color': colors.borderMuted,\n '--vis-sankey-node-sublabel-font-size': fontSizes.xs,\n '--vis-sankey-link-opacity': '0.35',\n '--vis-sankey-link-hover-opacity': '0.6',\n },\n skeleton: { display: 'block', width: '100%', height: '100%' },\n});\n\nconst ALIGNS: Record<keyof SankeyAligns, SankeyNodeAlign> = {\n justify: SankeyNodeAlign.Justify,\n left: SankeyNodeAlign.Left,\n right: SankeyNodeAlign.Right,\n center: SankeyNodeAlign.Center,\n};\n\n/** 交给 Unovis 的节点、流向 */\ntype Data = ReturnType<typeof flowData>;\ntype Node = Data['nodes'][number];\ntype Link = Data['links'][number];\n/** Unovis 布局后:流向的起点、终点换成了节点对象,节点带上了总量 value */\ntype LaidOutNode = Node & { value?: number };\ntype LaidOutLink = Omit<Link, 'source' | 'target'> & { source: LaidOutNode; target: LaidOutNode };\n\n/** 桑基图 */\nexport const SankeyChart = _component(\n ({\n nodes,\n links,\n valueFormat,\n unit,\n height = 320,\n align = 'justify',\n tooltip = true,\n loading = false,\n empty,\n sx,\n ...rest\n }: SankeyChartProps) => {\n const lang = useLang();\n const format = useFormat();\n const resolved = _derived(resolveFlowNodes(nodes));\n const valueText = useChartValueText(() => ({ valueFormat, unit }));\n const labelOf = (id: string) => resolved.get(id)?.label ?? id;\n\n let plot = _state.raw<HTMLElement | null>(null);\n const chart: SingleChart = useSingleChart<{ nodes: Node[]; links?: Link[] }>(() => ({\n element: plot,\n ready: !loading && nodes.length > 0,\n tooltip,\n build: (instance) => {\n const sankey = instance(\n 'sankey',\n () => new Sankey<Node, Link>({ linkValue: (link) => link.amount }),\n );\n sankey.setConfig({\n linkValue: (link) => link.amount,\n nodeColor: (node) => node.color,\n linkColor: (link) => link.source.color,\n nodeAlign: ALIGNS[align],\n label: (node) => node.label,\n subLabel: (node) => (node.value === undefined ? '' : valueText(node.value)),\n nodePadding: 16,\n nodeWidth: 12,\n labelMaxWidth: 120,\n labelMaxWidthTakeAvailableSpace: true,\n labelFit: FitMode.Wrap,\n labelBackground: true,\n enableZoom: false,\n });\n return {\n component: sankey,\n data: flowData(resolved, links),\n triggers: {\n [Sankey.selectors.link]: (link: LaidOutLink) =>\n chart.tooltip(\n chartTooltip(lang.chartFlowLabel(link.source.label, link.target.label), [\n {\n label: lang.chartValue,\n color: link.source.color,\n value: valueText(link.value),\n },\n ]),\n ),\n [Sankey.selectors.node]: (node: LaidOutNode) =>\n chart.tooltip(\n chartTooltip(node.label, [\n { label: lang.chartTotal, color: node.color, value: valueText(node.value) },\n ]),\n ),\n },\n };\n },\n }));\n\n return () => (\n <ChartFrame\n {...rest}\n sx={[sankeyChartStyles.root, sx]}\n unit={unit}\n height={height}\n legend={[]}\n hiddenKeys={[]}\n onLegendToggle={() => {}}\n loading={loading}\n skeleton={<Skeleton sx={sankeyChartStyles.skeleton} />}\n isEmpty={nodes.length === 0}\n empty={empty}\n table={{\n columns: [lang.chartFlow, unit ? `${lang.chartValue}(${unit})` : lang.chartValue],\n rows: links.map((link) => ({\n header: lang.chartFlowLabel(labelOf(link.source), labelOf(link.target)),\n cells: [format.number(link.value, valueFormat)],\n })),\n }}\n plotRef={(element) => (plot = element)}\n />\n );\n },\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SankeyChart.js","names":["_vtp_readonlyProp","_vtp_restProps","_vue_computed","_vue_shallowRef","_createVNode","_mergeProps"],"sources":["../../src/charts/SankeyChart.tsx"],"sourcesContent":["/**\n * 桑基图:节点之间的流量画成粗细不同的带子,看量从哪来、到哪去、在哪流失(转化漏斗、预算分配、能源流向)。\n *\n * <SankeyChart aria-label=\"注册转化\" nodes={steps} links={flows} unit=\"人\" />\n *\n * 节点是 { id, label?, color? },流向是 { source, target, value }(value 可以是 Decimal、bigint,要大于 0;不能成环)。\n * 带子取起点节点的颜色;悬停带子出“起点 → 终点”的流量,悬停节点出它的总量并突出与它相连的带子。\n * 节点旁写名字与总量(左半边的写在右侧,右半边的写在左侧)。布局见 sankeyLayout。\n * 单位、加载与空状态、给读屏的数据表(每条流向一行)同其他图表(见 ChartFrame)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport type { VNodeChild } from 'vue';\nimport { useLang } from '../config/context';\nimport { useFormat, type NumericFormat } from '../config/lang/format';\nimport { t } from '../config/theme/tokens';\nimport { Skeleton } from '../feedback/Skeleton';\nimport type { PartProps } from '../utils/props';\nimport { flowData, resolveFlowNodes, type FlowLink, type FlowNode } from './flow';\nimport { svgNumber } from '../utils/data/number';\nimport { ChartFrame, chartTooltip, useChartValueText, type ChartFrameParts } from './frame';\nimport { sankeyLayout } from './layout/sankey';\nimport { ChartTooltipBox, chartSvgStyles, pointerIn, usePlotArea, type ChartTip } from './plot';\nimport { horizontalLinkPath } from './shape';\n\n/** 节点怎样排:justify 两端对齐(默认),left、right 靠向一侧,center 居中 */\nexport interface SankeyAligns {\n /** 两端对齐(默认):按离起点的远近分列,没有流出的节点都排到最右一列 */\n justify: unknown;\n /** 靠左:每个节点排在离起点最近的一列 */\n left: unknown;\n /** 靠右:每个节点排在离终点最近的一列 */\n right: unknown;\n /** 居中:同靠左,但没有流入的节点往右挪,贴着它流向的节点 */\n center: unknown;\n}\n\n/** 未声明的属性落到外层 div(role=\"figure\")上,aria-label 是图表的名称 */\nexport interface SankeyChartProps extends PartProps<'div', 'role' | 'aria-busy'>, ChartFrameParts {\n /** 节点 */\n nodes: readonly FlowNode[];\n /** 流向 */\n links: readonly FlowLink[];\n /** 数值的格式(可缩放,如以万元为单位):节点旁的总量、提示框、数据表共用 */\n valueFormat?: NumericFormat;\n /** 单位:显示在左上方,数值后面带上 */\n unit?: string;\n /**\n * 绘图区的高度;数字按 px\n * @default 320\n */\n height?: number | string;\n /**\n * 节点的排法\n * @default 'justify'\n */\n align?: keyof SankeyAligns;\n /**\n * 悬停时的提示框\n * @default true\n */\n tooltip?: boolean;\n /**\n * 加载中:绘图区显示骨架,带 aria-busy\n * @default false\n */\n loading?: boolean;\n /** 没有数据时显示的内容,默认是“暂无数据” */\n empty?: VNodeChild;\n}\n\n/** SankeyChart 的样式:节点、带子、名字、骨架 */\nexport const sankeyChartStyles = css.create({\n skeleton: { display: 'block', width: '100%', height: '100%' },\n node: { stroke: '_surface', strokeWidth: 1 },\n link: {\n fill: 'none',\n strokeOpacity: 0.35,\n transition: `stroke-opacity ${t.durations.sm} ${t.easings.standard}`,\n },\n linkActive: { strokeOpacity: 0.6 },\n linkFaded: { strokeOpacity: 0.12 },\n name: { fill: '_text', fontSize: '_xs', fontWeight: '_medium' },\n value: { fill: '_textMuted', fontSize: '_xs' },\n});\n\n/** 节点的宽、同一列里节点的间距、上下留的空(px) */\nconst NODE_WIDTH = 12;\nconst NODE_PADDING = 16;\nconst MARGIN = 8;\n\n/** 悬停的是节点还是带子 */\ninterface SankeyHover {\n kind: 'node' | 'link';\n index: number;\n x: number;\n y: number;\n}\n\n/** 桑基图 */\nexport const SankeyChart = _component(\n ({\n nodes,\n links,\n valueFormat,\n unit,\n height = 320,\n align = 'justify',\n tooltip = true,\n loading = false,\n empty,\n ...rest\n }: SankeyChartProps) => {\n const lang = useLang();\n const format = useFormat();\n const resolved = _derived(resolveFlowNodes(nodes));\n const valueText = useChartValueText(() => ({ valueFormat, unit }));\n const labelOf = (id: string) => resolved.get(id)?.label ?? id;\n const area = usePlotArea();\n let hovered = _state.raw<SankeyHover | null>(null);\n\n const layout = _derived.by(() => {\n const data = flowData(resolved, links);\n const laid = sankeyLayout(\n data.nodes.map((node) => node.id),\n data.links.map((link) => ({\n source: link.source,\n target: link.target,\n value: link.amount,\n })),\n {\n width: area.width,\n height: Math.max(0, area.height - MARGIN * 2),\n nodeWidth: NODE_WIDTH,\n nodePadding: NODE_PADDING,\n align,\n },\n );\n return { ...laid, flows: data.links };\n });\n\n function onPointermove(event: PointerEvent) {\n const target = (event.target as Element).closest?.('[data-node],[data-link]');\n if (!target) {\n hovered = null;\n return;\n }\n const { x, y } = pointerIn(event, event.currentTarget as Element);\n const node = target.getAttribute('data-node');\n hovered =\n node === null\n ? { kind: 'link', index: Number(target.getAttribute('data-link')), x, y }\n : { kind: 'node', index: Number(node), x, y };\n }\n\n /** 带子与悬停的节点相连(悬停带子时只有它自己):true 突出、false 变淡、undefined 没有悬停 */\n const linkState = (index: number) => {\n const current = hovered;\n if (!current) return undefined;\n if (current.kind === 'link') return current.index === index;\n const link = layout.links[index]!;\n const node = layout.nodes[current.index]!;\n return link.source === node || link.target === node;\n };\n\n function renderPlot() {\n const { width } = area;\n return (\n <svg\n width={width}\n height={area.height}\n {...css.attrs(chartSvgStyles.svg)}\n onPointermove={onPointermove}\n onPointerleave={() => (hovered = null)}\n >\n <g transform={`translate(0,${MARGIN})`} {...css.attrs(chartSvgStyles.fadeIn)}>\n <g data-part=\"links\">\n {layout.links.map((link, index) => {\n const state = linkState(index);\n return (\n <path\n key={index}\n data-part=\"link\"\n data-link={index}\n d={horizontalLinkPath(link.source.x1, link.y0, link.target.x0, link.y1)}\n stroke-width={svgNumber(Math.max(1, link.width))}\n {...css.attrs(\n sankeyChartStyles.link,\n chartSvgStyles.stroke(resolved.get(link.source.id)!.color),\n state === true && sankeyChartStyles.linkActive,\n state === false && sankeyChartStyles.linkFaded,\n )}\n />\n );\n })}\n </g>\n {layout.nodes.map((node, index) => {\n const info = resolved.get(node.id)!;\n // 左半边的节点名字写在右侧,右半边的写在左侧:都朝着图的中间\n const right = node.x0 < width / 2;\n const middle = (node.y0 + node.y1) / 2;\n const x = right ? node.x1 + 6 : node.x0 - 6;\n const anchor = right ? 'start' : 'end';\n return (\n <g key={node.id}>\n <rect\n data-part=\"node\"\n data-node={index}\n x={svgNumber(node.x0)}\n y={svgNumber(node.y0)}\n width={NODE_WIDTH}\n height={svgNumber(Math.max(1, node.y1 - node.y0))}\n rx={2}\n {...css.attrs(sankeyChartStyles.node, chartSvgStyles.fill(info.color))}\n />\n <text\n x={x}\n y={svgNumber(middle - 7)}\n text-anchor={anchor}\n dominant-baseline=\"central\"\n {...css.attrs(\n sankeyChartStyles.name,\n chartSvgStyles.halo,\n chartSvgStyles.noPointer,\n )}\n >\n {info.label}\n </text>\n <text\n x={x}\n y={svgNumber(middle + 8)}\n text-anchor={anchor}\n dominant-baseline=\"central\"\n {...css.attrs(\n sankeyChartStyles.value,\n chartSvgStyles.halo,\n chartSvgStyles.noPointer,\n )}\n >\n {valueText(node.value)}\n </text>\n </g>\n );\n })}\n </g>\n </svg>\n );\n }\n\n const tip = (): ChartTip | null => {\n const current = hovered;\n if (!tooltip || !current) return null;\n if (current.kind === 'node') {\n const node = layout.nodes[current.index];\n const info = node && resolved.get(node.id);\n if (!node || !info) return null;\n return {\n x: current.x,\n y: current.y,\n content: chartTooltip(info.label, [\n { label: lang.chartTotal, color: info.color, value: valueText(node.value) },\n ]),\n };\n }\n const link = layout.links[current.index];\n if (!link) return null;\n const source = resolved.get(link.source.id)!;\n return {\n x: current.x,\n y: current.y,\n content: chartTooltip(lang.chartFlowLabel(source.label, labelOf(link.target.id)), [\n {\n label: lang.chartValue,\n color: source.color,\n value: valueText(layout.flows[link.index]!.value),\n },\n ]),\n };\n };\n\n return () => (\n <ChartFrame\n {...rest}\n unit={unit}\n height={height}\n legend={[]}\n hiddenKeys={[]}\n onLegendToggle={() => {}}\n loading={loading}\n skeleton={<Skeleton sx={sankeyChartStyles.skeleton} />}\n isEmpty={nodes.length === 0}\n empty={empty}\n table={{\n columns: [lang.chartFlow, unit ? `${lang.chartValue}(${unit})` : lang.chartValue],\n rows: links.map((link) => ({\n header: lang.chartFlowLabel(labelOf(link.source), labelOf(link.target)),\n cells: [format.number(link.value, valueFormat)],\n })),\n }}\n plotRef={area.ref}\n plot={\n area.ready\n ? [renderPlot(), <ChartTooltipBox key=\"tooltip\" tip={tip()} width={area.width} />]\n : null\n }\n />\n );\n },\n);\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
1
2
|
import { type NumericKey } from './series';
|
|
2
3
|
import { type XYChartProps } from './XYChart';
|
|
3
4
|
/** 未声明的属性落到外层 div(role="figure")上 */
|
|
@@ -9,10 +10,15 @@ export interface ScatterChartProps<T extends object> extends XYChartProps<T> {
|
|
|
9
10
|
/** 横轴数值的格式(Intl.NumberFormatOptions) */
|
|
10
11
|
xFormat?: Intl.NumberFormatOptions;
|
|
11
12
|
/**
|
|
12
|
-
* 点的直径(px
|
|
13
|
+
* 点的直径(px),或者一个数值字段(气泡图:面积按它在 6–32px 的直径之间缩放)
|
|
13
14
|
* @default 9
|
|
14
15
|
*/
|
|
15
16
|
size?: number | NumericKey<T>;
|
|
16
17
|
}
|
|
18
|
+
/** ScatterChart 的样式 */
|
|
19
|
+
export declare const scatterChartStyles: Readonly<{
|
|
20
|
+
active: css.CompiledStyles;
|
|
21
|
+
point: css.CompiledStyles;
|
|
22
|
+
}>;
|
|
17
23
|
/** 散点图 */
|
|
18
24
|
export declare const ScatterChart: <T extends object>(props: ScatterChartProps<T>) => import("@zerodep-dev/vue-tsx-pro-tsx").TsxNode;
|
|
@@ -1,23 +1,30 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { svgNumber } from "../utils/data/number.js";
|
|
2
|
+
import { t } from "../config/theme/tokens.js";
|
|
2
3
|
import { useFormat } from "../config/lang/format.js";
|
|
4
|
+
import { chartSvgStyles } from "./plot.js";
|
|
5
|
+
import { chartMarkStyles } from "./marks.js";
|
|
3
6
|
import { seriesNumber } from "./series.js";
|
|
4
7
|
import { XYChart } from "./XYChart.js";
|
|
5
8
|
import { computed, createVNode } from "vue";
|
|
6
9
|
import { _component, bindable, mergeJsxProps, readonlyProp, restProps } from "@zerodep-dev/vue-tsx-pro-tsx";
|
|
7
|
-
import {
|
|
10
|
+
import { css } from "@zerodep-dev/vue-tsx-pro-tsx/styles";
|
|
8
11
|
//#region src/charts/ScatterChart.tsx
|
|
9
|
-
/**
|
|
10
|
-
* 散点图:横轴、纵轴都是数值,每一行在每个系列上画一个点;size 给成字段时点的大小随它变(气泡图)。
|
|
11
|
-
* 悬停一个点,提示框列出这一行的名称、横轴的值与各系列的值。
|
|
12
|
-
*
|
|
13
|
-
* <ScatterChart data={cities} index="name" x="gdp" xLabel="人均 GDP" series={[{ key: 'life', label: '预期寿命' }]} />
|
|
14
|
-
* <ScatterChart data={products} index="name" x="price" series={[{ key: 'rating', label: '评分' }]} size="sales" />
|
|
15
|
-
*
|
|
16
|
-
* 纵轴不强制从 0 起(按数据取);图例、单位、加载与空状态、给读屏的数据表同其他图表(见 XYChart)。
|
|
17
|
-
*/
|
|
18
12
|
/** 未声明的属性落到外层 div(role="figure")上 */
|
|
19
|
-
/**
|
|
13
|
+
/** 气泡图的直径范围(px) */
|
|
20
14
|
var BUBBLE_RANGE = [6, 32];
|
|
15
|
+
/** ScatterChart 的样式 */
|
|
16
|
+
var scatterChartStyles = css.create({
|
|
17
|
+
point: {
|
|
18
|
+
stroke: "_surface",
|
|
19
|
+
strokeWidth: 1,
|
|
20
|
+
fillOpacity: .85,
|
|
21
|
+
transition: `stroke-width ${t.durations.sm} ${t.easings.standard}`
|
|
22
|
+
},
|
|
23
|
+
active: {
|
|
24
|
+
strokeWidth: 2.5,
|
|
25
|
+
fillOpacity: 1
|
|
26
|
+
}
|
|
27
|
+
});
|
|
21
28
|
/** 散点图 */
|
|
22
29
|
var ScatterChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
23
30
|
const _cache = [];
|
|
@@ -28,9 +35,17 @@ var ScatterChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
28
35
|
const hiddenSeries = bindable(_props, "_hiddenSeries", () => [], _ctx.emit);
|
|
29
36
|
const rest = restProps(_props, _ctx, true);
|
|
30
37
|
const format = useFormat();
|
|
38
|
+
/** 点的半径:固定直径,或按字段的值让面积成比例 */
|
|
39
|
+
const radiusOf = (row, sizes) => {
|
|
40
|
+
if (typeof size.value === "number") return size.value / 2;
|
|
41
|
+
const value = seriesNumber(row, size.value) ?? 0;
|
|
42
|
+
const [low, high] = [Math.min(...sizes), Math.max(...sizes)];
|
|
43
|
+
const ratio = high === low ? 1 : Math.sqrt((value - low) / (high - low));
|
|
44
|
+
return (BUBBLE_RANGE[0] + ratio * (BUBBLE_RANGE[1] - BUBBLE_RANGE[0])) / 2;
|
|
45
|
+
};
|
|
31
46
|
const kind = computed(() => ({
|
|
32
47
|
zeroBaseline: false,
|
|
33
|
-
pointTooltip:
|
|
48
|
+
pointTooltip: true,
|
|
34
49
|
padding: {
|
|
35
50
|
left: 18,
|
|
36
51
|
right: 18,
|
|
@@ -43,22 +58,22 @@ var ScatterChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
43
58
|
label: xLabel.value ?? xKey.value,
|
|
44
59
|
title: xLabel.value
|
|
45
60
|
},
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
61
|
+
render: (context) => {
|
|
62
|
+
const sizes = typeof size.value === "number" ? [] : context.rows.map((row) => seriesNumber(row, size.value) ?? 0);
|
|
63
|
+
return createVNode("g", { "data-part": "points" }, [context.series.flatMap((item) => context.rows.map((row, index) => {
|
|
64
|
+
const value = context.valueOf(item, row);
|
|
65
|
+
const cx = context.category(row, index);
|
|
66
|
+
if (value === void 0 || Number.isNaN(cx)) return null;
|
|
67
|
+
return createVNode("circle", mergeJsxProps({
|
|
68
|
+
"key": `${item.key}:${index}`,
|
|
69
|
+
"data-part": "point",
|
|
70
|
+
"data-index": index,
|
|
71
|
+
"cx": svgNumber(cx),
|
|
72
|
+
"cy": svgNumber(context.value(value)),
|
|
73
|
+
"r": radiusOf(row, sizes)
|
|
74
|
+
}, css.attrs(chartMarkStyles.dot, scatterChartStyles.point, chartSvgStyles.fill(item.color), context.hovered === index && scatterChartStyles.active)), null);
|
|
75
|
+
}))]);
|
|
76
|
+
}
|
|
62
77
|
}));
|
|
63
78
|
return () => createVNode(XYChart, mergeJsxProps(rest, {
|
|
64
79
|
"_hiddenSeries": hiddenSeries.value,
|
|
@@ -77,6 +92,6 @@ var ScatterChart = /*#__PURE__*/ _component((_props, _ctx) => {
|
|
|
77
92
|
]
|
|
78
93
|
});
|
|
79
94
|
//#endregion
|
|
80
|
-
export { ScatterChart };
|
|
95
|
+
export { ScatterChart, scatterChartStyles };
|
|
81
96
|
|
|
82
97
|
//# sourceMappingURL=ScatterChart.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScatterChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/charts/ScatterChart.tsx"],"sourcesContent":["/**\n * 散点图:横轴、纵轴都是数值,每一行在每个系列上画一个点;size 给成字段时点的大小随它变(气泡图)。\n *
|
|
1
|
+
{"version":3,"file":"ScatterChart.js","names":["_vtp_readonlyProp","_vtp_bindable","_vtp_restProps","_vue_computed","_createVNode","_mergeProps"],"sources":["../../src/charts/ScatterChart.tsx"],"sourcesContent":["/**\n * 散点图:横轴、纵轴都是数值,每一行在每个系列上画一个点;size 给成字段时点的大小随它变(气泡图)。\n * 悬停离指针最近的点,提示框列出这一行的名称、横轴的值与各系列的值。\n *\n * <ScatterChart data={cities} index=\"name\" x=\"gdp\" xLabel=\"人均 GDP\" series={[{ key: 'life', label: '预期寿命' }]} />\n * <ScatterChart data={products} index=\"name\" x=\"price\" series={[{ key: 'rating', label: '评分' }]} size=\"sales\" />\n *\n * 纵轴不强制从 0 起(按数据取);图例、单位、加载与空状态、给读屏的数据表同其他图表(见 XYChart)。\n */\nimport { _component } from '@zerodep-dev/vue-tsx-pro-tsx';\nimport { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';\nimport { t } from '../config/theme/tokens';\nimport { useFormat } from '../config/lang/format';\nimport { svgNumber } from '../utils/data/number';\nimport { chartMarkStyles } from './marks';\nimport { chartSvgStyles } from './plot';\nimport { seriesNumber, type NumericKey } from './series';\nimport { XYChart, type XYChartKind, type XYChartProps } from './XYChart';\n\n/** 未声明的属性落到外层 div(role=\"figure\")上 */\nexport interface ScatterChartProps<T extends object> extends XYChartProps<T> {\n /** 横轴的数值字段 */\n x: NumericKey<T>;\n /** 横轴的名字:显示在横轴下方,提示框与数据表里也用;不写时轴下不显示,提示框与数据表用字段名 */\n xLabel?: string;\n /** 横轴数值的格式(Intl.NumberFormatOptions) */\n xFormat?: Intl.NumberFormatOptions;\n /**\n * 点的直径(px),或者一个数值字段(气泡图:面积按它在 6–32px 的直径之间缩放)\n * @default 9\n */\n size?: number | NumericKey<T>;\n}\n\n/** 气泡图的直径范围(px) */\nconst BUBBLE_RANGE: [number, number] = [6, 32];\n\n/** ScatterChart 的样式 */\nexport const scatterChartStyles = css.create({\n // 气泡互相重叠时用表面色描一圈边分开;悬停的点描粗一点\n point: {\n stroke: '_surface',\n strokeWidth: 1,\n fillOpacity: 0.85,\n transition: `stroke-width ${t.durations.sm} ${t.easings.standard}`,\n },\n active: { strokeWidth: 2.5, fillOpacity: 1 },\n});\n\n/** 散点图 */\nexport const ScatterChart = _component(\n <T extends object>({\n x: xKey,\n xLabel,\n xFormat,\n size = 9,\n _hiddenSeries: hiddenSeries = _bindable<string[]>([]),\n ...rest\n }: ScatterChartProps<T>) => {\n const format = useFormat();\n /** 点的半径:固定直径,或按字段的值让面积成比例 */\n const radiusOf = (row: T, sizes: readonly number[]) => {\n if (typeof size === 'number') return size / 2;\n const value = seriesNumber(row, size) ?? 0;\n const [low, high] = [Math.min(...sizes), Math.max(...sizes)];\n const ratio = high === low ? 1 : Math.sqrt((value - low) / (high - low));\n return (BUBBLE_RANGE[0] + ratio * (BUBBLE_RANGE[1] - BUBBLE_RANGE[0])) / 2;\n };\n const kind = _derived<XYChartKind<T>>({\n zeroBaseline: false,\n pointTooltip: true,\n // 留出半个最大气泡的边:贴边的点不被裁掉\n padding: { left: 18, right: 18, top: 18, bottom: 8 },\n continuousX: {\n value: (row) => seriesNumber(row, xKey),\n format: (value) => format.number(value, xFormat),\n label: xLabel ?? xKey,\n title: xLabel,\n },\n render: (context) => {\n const sizes =\n typeof size === 'number' ? [] : context.rows.map((row) => seriesNumber(row, size) ?? 0);\n return (\n <g data-part=\"points\">\n {context.series.flatMap((item) =>\n context.rows.map((row, index) => {\n const value = context.valueOf(item, row);\n const cx = context.category(row, index);\n if (value === undefined || Number.isNaN(cx)) return null;\n return (\n <circle\n key={`${item.key}:${index}`}\n data-part=\"point\"\n data-index={index}\n cx={svgNumber(cx)}\n cy={svgNumber(context.value(value))}\n r={radiusOf(row, sizes)}\n {...css.attrs(\n chartMarkStyles.dot,\n scatterChartStyles.point,\n chartSvgStyles.fill(item.color),\n context.hovered === index && scatterChartStyles.active,\n )}\n />\n );\n }),\n )}\n </g>\n );\n },\n });\n return () => <XYChart {...rest} _hiddenSeries={hiddenSeries} kind={kind} />;\n },\n);\n"],"mappings":";;;;;;;;;;;;;AAmCA,IAAM,eAAiC,CAAC,GAAG,EAAE;;AAG7C,IAAa,qBAAqB,IAAI,OAAO;CAE3C,OAAO;EACL,QAAQ;EACR,aAAa;EACb,aAAa;EACb,YAAY,gBAAgB,EAAE,UAAU,GAAE,GAAI,EAAE,QAAQ;CAC1D;CACA,QAAQ;EAAE,aAAa;EAAK,aAAa;CAAE;AAC7C,CAAC;;AAGD,IAAa,eAAe,0BAC1B,QAAA,SAO4B;CAAA,MAAA,SAAA,CAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,GAAA;CAAA,MAAA,SAAAA,aAAA,QAAA,QAAA;CAAA,MAAA,UAAAA,aAAA,QAAA,SAAA;CAAA,MAAA,OAAAA,aAAA,QAAA,QAHnB,CAAC;CAAA,MAAA,eAAAC,SAAA,QAAA,uBAC0C,CAAA,GAAE,KAAA,IAAA;CAAA,MACjD,OAAIC,UAAA,QAAA,MAAA,IAAA;CAEP,MAAM,SAAS,UAAU;;CAEzB,MAAM,YAAY,KAAQ,UAA6B;EACrD,IAAI,OAAA,KAAA,UAAgB,UAAU,OAAO,KAAA,QAAO;EAC5C,MAAM,QAAQ,aAAa,KAAG,KAAA,KAAM,KAAK;EACzC,MAAM,CAAC,KAAK,QAAQ,CAAC,KAAK,IAAI,GAAG,KAAK,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;EAC3D,MAAM,QAAQ,SAAS,MAAM,IAAI,KAAK,MAAM,QAAQ,QAAQ,OAAO,IAAI;EACvE,QAAQ,aAAa,KAAK,SAAS,aAAa,KAAK,aAAa,OAAO;CAC3E;CACA,MAAM,OAAIC,gBAA4B;EACpC,cAAc;EACd,cAAc;EAEd,SAAS;GAAE,MAAM;GAAI,OAAO;GAAI,KAAK;GAAI,QAAQ;EAAE;EACnD,aAAa;GACX,QAAQ,QAAQ,aAAa,KAAG,KAAA,KAAM;GACtC,SAAS,UAAU,OAAO,OAAO,OAAK,QAAA,KAAS;GAC/C,OAAO,OAAA,SAAA,KAAA;GACP,OAAK,OAAA;EACP;EACA,SAAS,YAAY;GACnB,MAAM,QACJ,OAAA,KAAA,UAAgB,WAAW,CAAA,IAAK,QAAQ,KAAK,KAAK,QAAQ,aAAa,KAAG,KAAA,KAAM,KAAK,CAAC;GACxF,OAAAC,YAAA,KAAA,EAAA,aAAA,SAAA,GAAA,CAEK,QAAQ,OAAO,SAAS,SACvB,QAAQ,KAAK,KAAK,KAAK,UAAU;IAC/B,MAAM,QAAQ,QAAQ,QAAQ,MAAM,GAAG;IACvC,MAAM,KAAK,QAAQ,SAAS,KAAK,KAAK;IACtC,IAAI,UAAU,KAAA,KAAa,OAAO,MAAM,EAAE,GAAG,OAAO;IACpD,OAAAA,YAAA,UAAAC,cAAA;KAAA,OAES,GAAG,KAAK,IAAG,GAAI;KAAO,aAAA;KAAA,cAEf;KAAK,MACb,UAAU,EAAE;KAAC,MACb,UAAU,QAAQ,MAAM,KAAK,CAAC;KAAC,KAChC,SAAS,KAAK,KAAK;IAAC,GACnB,IAAI,MACN,gBAAgB,KAChB,mBAAmB,OACnB,eAAe,KAAK,KAAK,KAAK,GAC9B,QAAQ,YAAY,SAAS,mBAAmB,MAClD,CAAC,GAAA,IAAA;GAGP,CAAC,CACH,CAAC,CAAA;EAGP;CACF,EAAC;CACD,aAAOD,YAAA,SAAAC,cAAmB,MAAI;EAAA,iBAAA,aAAA;EAAA,0BAAA,OAAA,OAAA,OAAA,MAAA,WAAA,aAAA,QAAA;EAAA,QAAA,KAAA;CAAA,CAAA,GAAA,IAAA;AAChC,GAAC;CAAA,MAAA;CAAA,OAAA;EAAA;EAAA;EAAA;EAAA;EAAA;EAAA;CAAA;AAAA,CACH"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { css } from '@zerodep-dev/vue-tsx-pro-tsx/styles';
|
|
2
|
+
import type { VNodeChild } from 'vue';
|
|
2
3
|
import type { PartProps } from '../utils/props';
|
|
3
4
|
import { type ChartFrameParts } from './frame';
|
|
4
5
|
import { type ChartColor } from './series';
|
|
@@ -67,5 +68,11 @@ export interface TimelineChartProps extends PartProps<'div', 'role' | 'aria-busy
|
|
|
67
68
|
/** 悬停时的提示框,默认 true */
|
|
68
69
|
tooltip?: boolean;
|
|
69
70
|
}
|
|
71
|
+
/** TimelineChart 的样式:隔行的底色、时间条 */
|
|
72
|
+
export declare const timelineChartStyles: Readonly<{
|
|
73
|
+
segment: css.CompiledStyles;
|
|
74
|
+
segmentActive: css.CompiledStyles;
|
|
75
|
+
stripe: css.CompiledStyles;
|
|
76
|
+
}>;
|
|
70
77
|
/** 时间条图 */
|
|
71
78
|
export declare const TimelineChart: import("@zerodep-dev/vue-tsx-pro-tsx").TsxComponent<TimelineChartProps>;
|