vccs 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -0
- package/dist/animation/Animate.d.ts +1 -1
- package/dist/cartesian/area/type.d.ts +3 -3
- package/dist/cartesian/bar/type.d.ts +1 -13
- package/dist/cartesian/brush/components/Slide.d.ts +1 -1
- package/dist/cartesian/brush/components/TravellerLayer.d.ts +1 -1
- package/dist/cartesian/brush/type.d.ts +2 -1
- package/dist/cartesian/cartesian-axis/CartesianAxis.d.ts +2 -1
- package/dist/cartesian/funnel/type.d.ts +4 -5
- package/dist/cartesian/index.d.ts +1 -0
- package/dist/cartesian/line/Line.d.ts +7 -13
- package/dist/cartesian/line/type.d.ts +4 -17
- package/dist/cartesian/reference-area/ReferenceArea.d.ts +16 -16
- package/dist/cartesian/reference-dot/ReferenceDot.d.ts +143 -0
- package/dist/cartesian/reference-dot/index.d.ts +2 -0
- package/dist/cartesian/reference-line/ReferenceLine.d.ts +8 -8
- package/dist/cartesian/scatter/Scatter.d.ts +1 -1
- package/dist/chart/RechartsWrapper.d.ts +7 -3
- package/dist/chart/Surface.d.ts +2 -1
- package/dist/chart/Treemap.d.ts +237 -0
- package/dist/chart/generateCategoricalChart.d.ts +2 -1
- package/dist/chart/index.d.ts +1 -0
- package/dist/chart/treemapUtils.d.ts +22 -0
- package/dist/components/Customized.d.ts +23 -0
- package/dist/components/Text.d.ts +4 -4
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/label/Label.d.ts +3 -3
- package/dist/components/label/types.d.ts +1 -1
- package/dist/container/Surface.d.ts +3 -2
- package/dist/es/cartesian/area/type.mjs +2 -4
- package/dist/es/cartesian/bar/Bar.mjs +2 -17
- package/dist/es/cartesian/bar/type.mjs +2 -16
- package/dist/es/cartesian/cartesian-grid/const.mjs +1 -1
- package/dist/es/cartesian/funnel/Funnel.mjs +3 -18
- package/dist/es/cartesian/funnel/type.mjs +3 -5
- package/dist/es/cartesian/line/type.mjs +2 -7
- package/dist/es/cartesian/reference-area/ReferenceArea.mjs +6 -22
- package/dist/es/cartesian/reference-dot/ReferenceDot.mjs +134 -0
- package/dist/es/cartesian/reference-line/ReferenceLine.mjs +6 -19
- package/dist/es/chart/RechartsWrapper.mjs +2 -1
- package/dist/es/chart/Treemap.mjs +379 -0
- package/dist/es/chart/generateCategoricalChart.mjs +3 -2
- package/dist/es/chart/treemapUtils.mjs +32 -0
- package/dist/es/components/Customized.mjs +43 -0
- package/dist/es/components/label/types.mjs +2 -1
- package/dist/es/components/legend/Legend.mjs +1 -1
- package/dist/es/components/legend/hooks/useLegend.mjs +8 -3
- package/dist/es/container/Surface.mjs +2 -4
- package/dist/es/hooks/publicHooks.mjs +207 -0
- package/dist/es/index.mjs +9 -1
- package/dist/es/polar/pie/Pie.mjs +1 -1
- package/dist/es/polar/pie/type.mjs +2 -4
- package/dist/es/shape/Curve.mjs +2 -1
- package/dist/es/shape/Polygon.mjs +19 -0
- package/dist/es/shape/Rectangle.mjs +0 -4
- package/dist/es/shape/Sector.mjs +4 -6
- package/dist/es/shape/Symbols.mjs +1 -1
- package/dist/es/shape/Trapezoid.mjs +5 -7
- package/dist/es/state/ReportChartProps.mjs +2 -4
- package/dist/es/state/hooks.mjs +21 -1
- package/dist/es/state/referenceElementsSlice.mjs +1 -1
- package/dist/es/state/selectors/axisSelectors.mjs +30 -1
- package/dist/es/state/selectors/tooltipSelectors.mjs +2 -2
- package/dist/es/types/common.mjs +12 -0
- package/dist/es/utils/VueUtils.mjs +1 -1
- package/dist/es/utils/cell.mjs +28 -0
- package/dist/es/utils/createCategoricalInverse.mjs +46 -0
- package/dist/es/utils/scale.mjs +46 -0
- package/dist/hooks/publicHooks.d.ts +143 -0
- package/dist/index.d.ts +1 -0
- package/dist/polar/pie/type.d.ts +2 -2
- package/dist/polar/radar/Radar.d.ts +4 -4
- package/dist/shape/Curve.d.ts +2 -1
- package/dist/shape/Polygon.d.ts +9 -0
- package/dist/shape/Rectangle.d.ts +0 -4
- package/dist/shape/Sector.d.ts +2 -2
- package/dist/shape/Symbols.d.ts +2 -1
- package/dist/shape/Trapezoid.d.ts +9 -10
- package/dist/shape/index.d.ts +1 -0
- package/dist/shape/types.d.ts +4 -5
- package/dist/state/ReportChartProps.d.ts +3 -3
- package/dist/state/rootPropsSlice.d.ts +2 -2
- package/dist/state/selectors/axisSelectors.d.ts +5 -0
- package/dist/types/axis.d.ts +2 -2
- package/dist/types/bar.d.ts +2 -6
- package/dist/types/categorical.d.ts +2 -2
- package/dist/types/common.d.ts +7 -1
- package/dist/types/tooltip.d.ts +2 -2
- package/dist/utils/cell.d.ts +11 -0
- package/dist/utils/createCategoricalInverse.d.ts +16 -0
- package/dist/utils/scale.d.ts +23 -0
- package/package.json +2 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { classProp } from "../../types/common.mjs";
|
|
2
|
+
import { isNumOrStr } from "../../utils/validate.mjs";
|
|
2
3
|
import { useIsPanorama } from "../../context/PanoramaContextProvider.mjs";
|
|
3
4
|
import { selectAxisScale } from "../../state/selectors/axisSelectors.mjs";
|
|
4
5
|
import { useAppDispatch, useAppSelector } from "../../state/hooks.mjs";
|
|
@@ -7,22 +8,8 @@ import { Label } from "../../components/label/Label.mjs";
|
|
|
7
8
|
import { Rectangle } from "../../shape/Rectangle.mjs";
|
|
8
9
|
import { addArea, removeArea } from "../../state/referenceElementsSlice.mjs";
|
|
9
10
|
import { useClipPathId } from "../../chart/provideClipPathId.mjs";
|
|
11
|
+
import { rangeMax, rangeMin, scaleValue } from "../../utils/scale.mjs";
|
|
10
12
|
import { computed, createVNode, defineComponent, mergeProps, onMounted, onUnmounted, reactive } from "vue";
|
|
11
|
-
//#region src/cartesian/reference-area/ReferenceArea.tsx
|
|
12
|
-
function scaleValue(scale, value, position) {
|
|
13
|
-
const coord = scale(value);
|
|
14
|
-
if (!isWellBehavedNumber(coord)) return void 0;
|
|
15
|
-
const bandwidth = scale.bandwidth?.() ?? 0;
|
|
16
|
-
return position === "start" ? coord : coord + bandwidth;
|
|
17
|
-
}
|
|
18
|
-
function rangeMin(scale) {
|
|
19
|
-
const r = scale.range();
|
|
20
|
-
return Math.min(r[0], r[1]);
|
|
21
|
-
}
|
|
22
|
-
function rangeMax(scale) {
|
|
23
|
-
const r = scale.range();
|
|
24
|
-
return Math.max(r[0], r[1]);
|
|
25
|
-
}
|
|
26
13
|
var ReferenceArea = /* @__PURE__ */ defineComponent({
|
|
27
14
|
name: "ReferenceArea",
|
|
28
15
|
props: {
|
|
@@ -79,14 +66,11 @@ var ReferenceArea = /* @__PURE__ */ defineComponent({
|
|
|
79
66
|
type: String,
|
|
80
67
|
default: "discard"
|
|
81
68
|
},
|
|
82
|
-
className: {
|
|
83
|
-
type: String,
|
|
84
|
-
default: void 0
|
|
85
|
-
},
|
|
86
69
|
radius: {
|
|
87
70
|
type: [Number, Array],
|
|
88
71
|
default: 0
|
|
89
|
-
}
|
|
72
|
+
},
|
|
73
|
+
class: classProp
|
|
90
74
|
},
|
|
91
75
|
inheritAttrs: false,
|
|
92
76
|
setup(props, { attrs }) {
|
|
@@ -144,7 +128,7 @@ var ReferenceArea = /* @__PURE__ */ defineComponent({
|
|
|
144
128
|
};
|
|
145
129
|
const labelValue = props.label;
|
|
146
130
|
const labelProps = typeof labelValue === "object" && labelValue !== null ? labelValue : {};
|
|
147
|
-
return createVNode(Layer, { "class": ["v-charts-reference-area", props.
|
|
131
|
+
return createVNode(Layer, { "class": ["v-charts-reference-area", props.class] }, { default: () => [createVNode(Rectangle, mergeProps(svgAttrs, {
|
|
148
132
|
"clip-path": clipPath,
|
|
149
133
|
"x": r.x,
|
|
150
134
|
"y": r.y,
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { isNumOrStr } from "../../utils/validate.mjs";
|
|
2
|
+
import { useIsPanorama } from "../../context/PanoramaContextProvider.mjs";
|
|
3
|
+
import { selectAxisScale } from "../../state/selectors/axisSelectors.mjs";
|
|
4
|
+
import { useAppDispatch, useAppSelector } from "../../state/hooks.mjs";
|
|
5
|
+
import { Layer } from "../../container/Layer.mjs";
|
|
6
|
+
import { Dot } from "../../shape/Dot.mjs";
|
|
7
|
+
import { Label } from "../../components/label/Label.mjs";
|
|
8
|
+
import { addDot, removeDot } from "../../state/referenceElementsSlice.mjs";
|
|
9
|
+
import { useClipPathId } from "../../chart/provideClipPathId.mjs";
|
|
10
|
+
import { isInRange, scaleCoord } from "../../utils/scale.mjs";
|
|
11
|
+
import { computed, createVNode, defineComponent, mergeProps, onMounted, onUnmounted, reactive } from "vue";
|
|
12
|
+
var ReferenceDot = /* @__PURE__ */ defineComponent({
|
|
13
|
+
name: "ReferenceDot",
|
|
14
|
+
props: {
|
|
15
|
+
x: {
|
|
16
|
+
type: [Number, String],
|
|
17
|
+
default: void 0
|
|
18
|
+
},
|
|
19
|
+
y: {
|
|
20
|
+
type: [Number, String],
|
|
21
|
+
default: void 0
|
|
22
|
+
},
|
|
23
|
+
r: {
|
|
24
|
+
type: Number,
|
|
25
|
+
default: 10
|
|
26
|
+
},
|
|
27
|
+
xAxisId: {
|
|
28
|
+
type: [Number, String],
|
|
29
|
+
default: 0
|
|
30
|
+
},
|
|
31
|
+
yAxisId: {
|
|
32
|
+
type: [Number, String],
|
|
33
|
+
default: 0
|
|
34
|
+
},
|
|
35
|
+
fill: {
|
|
36
|
+
type: String,
|
|
37
|
+
default: "#fff"
|
|
38
|
+
},
|
|
39
|
+
stroke: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: "#ccc"
|
|
42
|
+
},
|
|
43
|
+
label: {
|
|
44
|
+
type: [
|
|
45
|
+
String,
|
|
46
|
+
Number,
|
|
47
|
+
Boolean,
|
|
48
|
+
Object
|
|
49
|
+
],
|
|
50
|
+
default: void 0
|
|
51
|
+
},
|
|
52
|
+
ifOverflow: {
|
|
53
|
+
type: String,
|
|
54
|
+
default: "discard"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
inheritAttrs: false,
|
|
58
|
+
slots: Object,
|
|
59
|
+
setup(props, { attrs, slots }) {
|
|
60
|
+
const dispatch = useAppDispatch();
|
|
61
|
+
const isPanorama = useIsPanorama();
|
|
62
|
+
const clipPathId = useClipPathId();
|
|
63
|
+
const settings = reactive({
|
|
64
|
+
xAxisId: props.xAxisId,
|
|
65
|
+
yAxisId: props.yAxisId,
|
|
66
|
+
ifOverflow: props.ifOverflow,
|
|
67
|
+
x: props.x,
|
|
68
|
+
y: props.y,
|
|
69
|
+
r: props.r
|
|
70
|
+
});
|
|
71
|
+
onMounted(() => {
|
|
72
|
+
dispatch(addDot(settings));
|
|
73
|
+
});
|
|
74
|
+
onUnmounted(() => {
|
|
75
|
+
dispatch(removeDot(settings));
|
|
76
|
+
});
|
|
77
|
+
const xAxisScale = useAppSelector((state) => selectAxisScale(state, "xAxis", props.xAxisId, isPanorama));
|
|
78
|
+
const yAxisScale = useAppSelector((state) => selectAxisScale(state, "yAxis", props.yAxisId, isPanorama));
|
|
79
|
+
const dotCoord = computed(() => {
|
|
80
|
+
const xScale = xAxisScale.value;
|
|
81
|
+
const yScale = yAxisScale.value;
|
|
82
|
+
if (!xScale || !yScale) return null;
|
|
83
|
+
if (!isNumOrStr(props.x) || !isNumOrStr(props.y)) return null;
|
|
84
|
+
const cx = scaleCoord(xScale, props.x);
|
|
85
|
+
const cy = scaleCoord(yScale, props.y);
|
|
86
|
+
if (cx == null || cy == null) return null;
|
|
87
|
+
if (props.ifOverflow === "discard") {
|
|
88
|
+
if (!isInRange(xScale, cx) || !isInRange(yScale, cy)) return null;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
cx,
|
|
92
|
+
cy
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
return () => {
|
|
96
|
+
const coord = dotCoord.value;
|
|
97
|
+
if (!coord) return null;
|
|
98
|
+
const { cx, cy } = coord;
|
|
99
|
+
const clipPath = props.ifOverflow === "hidden" ? `url(#${clipPathId})` : void 0;
|
|
100
|
+
const { class: userClass, ...restAttrs } = attrs;
|
|
101
|
+
const svgAttrs = {
|
|
102
|
+
...restAttrs,
|
|
103
|
+
fill: props.fill,
|
|
104
|
+
stroke: props.stroke
|
|
105
|
+
};
|
|
106
|
+
const labelViewBox = {
|
|
107
|
+
x: cx - props.r,
|
|
108
|
+
y: cy - props.r,
|
|
109
|
+
width: props.r * 2,
|
|
110
|
+
height: props.r * 2
|
|
111
|
+
};
|
|
112
|
+
const labelValue = props.label;
|
|
113
|
+
const labelProps = typeof labelValue === "object" && labelValue !== null ? labelValue : {};
|
|
114
|
+
const shapeProps = {
|
|
115
|
+
...svgAttrs,
|
|
116
|
+
cx,
|
|
117
|
+
cy,
|
|
118
|
+
r: props.r,
|
|
119
|
+
clipPath
|
|
120
|
+
};
|
|
121
|
+
return createVNode(Layer, { "class": ["v-charts-reference-dot", userClass] }, { default: () => [slots.shape ? slots.shape(shapeProps) : createVNode(Dot, mergeProps(svgAttrs, {
|
|
122
|
+
"cx": cx,
|
|
123
|
+
"cy": cy,
|
|
124
|
+
"r": props.r,
|
|
125
|
+
"clip-path": clipPath
|
|
126
|
+
}), null), labelValue != null && labelValue !== false && createVNode(Label, mergeProps({
|
|
127
|
+
"viewBox": labelViewBox,
|
|
128
|
+
"value": typeof labelValue === "string" || typeof labelValue === "number" ? labelValue : void 0
|
|
129
|
+
}, labelProps), null)] });
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
//#endregion
|
|
134
|
+
export { ReferenceDot };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { classProp } from "../../types/common.mjs";
|
|
1
2
|
import { isNumOrStr, isWellBehavedNumber } from "../../utils/validate.mjs";
|
|
2
3
|
import { useIsPanorama } from "../../context/PanoramaContextProvider.mjs";
|
|
3
4
|
import { selectAxisScale, selectXAxisSettings, selectYAxisSettings } from "../../state/selectors/axisSelectors.mjs";
|
|
@@ -7,19 +8,8 @@ import { Layer } from "../../container/Layer.mjs";
|
|
|
7
8
|
import { Label } from "../../components/label/Label.mjs";
|
|
8
9
|
import { addLine, removeLine } from "../../state/referenceElementsSlice.mjs";
|
|
9
10
|
import { useClipPathId } from "../../chart/provideClipPathId.mjs";
|
|
11
|
+
import { isInRange, scaleCoord } from "../../utils/scale.mjs";
|
|
10
12
|
import { computed, createVNode, defineComponent, mergeProps, onMounted, onUnmounted, reactive } from "vue";
|
|
11
|
-
//#region src/cartesian/reference-line/ReferenceLine.tsx
|
|
12
|
-
function scaleCoord(scale, value) {
|
|
13
|
-
const coord = scale(value);
|
|
14
|
-
if (!isWellBehavedNumber(coord)) return;
|
|
15
|
-
return coord + (scale.bandwidth?.() ?? 0) / 2;
|
|
16
|
-
}
|
|
17
|
-
function isInRange(scale, coord) {
|
|
18
|
-
const r = scale.range();
|
|
19
|
-
const min = Math.min(r[0], r[1]);
|
|
20
|
-
const max = Math.max(r[0], r[1]);
|
|
21
|
-
return coord >= min && coord <= max;
|
|
22
|
-
}
|
|
23
13
|
var ReferenceLine = /* @__PURE__ */ defineComponent({
|
|
24
14
|
name: "ReferenceLine",
|
|
25
15
|
props: {
|
|
@@ -64,10 +54,7 @@ var ReferenceLine = /* @__PURE__ */ defineComponent({
|
|
|
64
54
|
type: String,
|
|
65
55
|
default: "discard"
|
|
66
56
|
},
|
|
67
|
-
|
|
68
|
-
type: String,
|
|
69
|
-
default: void 0
|
|
70
|
-
}
|
|
57
|
+
class: classProp
|
|
71
58
|
},
|
|
72
59
|
inheritAttrs: false,
|
|
73
60
|
setup(props, { attrs }) {
|
|
@@ -135,9 +122,9 @@ var ReferenceLine = /* @__PURE__ */ defineComponent({
|
|
|
135
122
|
const clipPath = props.ifOverflow === "hidden" ? `url(#${clipPathId})` : void 0;
|
|
136
123
|
const svgAttrs = {
|
|
137
124
|
...attrs,
|
|
138
|
-
stroke: props.stroke,
|
|
125
|
+
"stroke": props.stroke,
|
|
139
126
|
"stroke-width": props.strokeWidth,
|
|
140
|
-
fill: props.fill
|
|
127
|
+
"fill": props.fill
|
|
141
128
|
};
|
|
142
129
|
const labelViewBox = {
|
|
143
130
|
x: Math.min(p1.x, p2.x),
|
|
@@ -147,7 +134,7 @@ var ReferenceLine = /* @__PURE__ */ defineComponent({
|
|
|
147
134
|
};
|
|
148
135
|
const labelValue = props.label;
|
|
149
136
|
const labelProps = typeof labelValue === "object" && labelValue !== null ? labelValue : {};
|
|
150
|
-
return createVNode(Layer, { "class": ["v-charts-reference-line", props.
|
|
137
|
+
return createVNode(Layer, { "class": ["v-charts-reference-line", props.class] }, { default: () => [createVNode("line", mergeProps(svgAttrs, {
|
|
151
138
|
"clip-path": clipPath,
|
|
152
139
|
"x1": p1.x,
|
|
153
140
|
"y1": p1.y,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { classProp } from "../types/common.mjs";
|
|
1
2
|
import { getChartPointer } from "../utils/chart.mjs";
|
|
2
3
|
import { mouseLeaveChart } from "../state/tooltipSlice.mjs";
|
|
3
4
|
import { useAppDispatch } from "../state/hooks.mjs";
|
|
@@ -14,7 +15,7 @@ import { createVNode, defineComponent, ref } from "vue";
|
|
|
14
15
|
var RechartsWrapper = /* @__PURE__ */ defineComponent({
|
|
15
16
|
name: "RechartsWrapper",
|
|
16
17
|
props: {
|
|
17
|
-
class:
|
|
18
|
+
class: classProp,
|
|
18
19
|
height: {
|
|
19
20
|
type: Number,
|
|
20
21
|
required: true
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
import { addTooltipEntrySettings, mouseLeaveItem, removeTooltipEntrySettings, setActiveClickItemIndex, setActiveMouseOverItemIndex } from "../state/tooltipSlice.mjs";
|
|
2
|
+
import { useAppDispatch } from "../state/hooks.mjs";
|
|
3
|
+
import { Layer } from "../container/Layer.mjs";
|
|
4
|
+
import { getStringSize } from "../utils/attrs.mjs";
|
|
5
|
+
import { Animate } from "../animation/Animate.mjs";
|
|
6
|
+
import { createRechartsStore } from "../state/store.mjs";
|
|
7
|
+
import { RechartsWrapper } from "./RechartsWrapper.mjs";
|
|
8
|
+
import Surface from "../container/Surface.mjs";
|
|
9
|
+
import { computeTreemapLayout } from "./treemapUtils.mjs";
|
|
10
|
+
import { Fragment, computed, createTextVNode, createVNode, defineComponent, ref, watchEffect } from "vue";
|
|
11
|
+
import { provideStore } from "@reduxjs/vue-redux";
|
|
12
|
+
import { get } from "lodash-es";
|
|
13
|
+
//#region src/chart/Treemap.tsx
|
|
14
|
+
var DEFAULT_COLORS = [
|
|
15
|
+
"#8889DD",
|
|
16
|
+
"#9597E4",
|
|
17
|
+
"#8DC77B",
|
|
18
|
+
"#A5D297",
|
|
19
|
+
"#E2CF45",
|
|
20
|
+
"#F8C12D",
|
|
21
|
+
"#F89C24",
|
|
22
|
+
"#F56E1A"
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Recursively sum all descendant values for a given dataKey.
|
|
26
|
+
*/
|
|
27
|
+
function sumValues(item, dataKey) {
|
|
28
|
+
if (item.children && item.children.length > 0) return item.children.reduce((sum, child) => sum + sumValues(child, dataKey), 0);
|
|
29
|
+
const val = item[dataKey];
|
|
30
|
+
return typeof val === "number" && val > 0 ? val : 0;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Tooltip payload searcher for Treemap — navigates nested node structure
|
|
34
|
+
* using a path string like 'children[0].children[1]'.
|
|
35
|
+
*/
|
|
36
|
+
var treemapPayloadSearcher = (data, activeIndex) => {
|
|
37
|
+
if (!data || !activeIndex) return void 0;
|
|
38
|
+
return get(data, activeIndex);
|
|
39
|
+
};
|
|
40
|
+
var treemapOptions = {
|
|
41
|
+
chartName: "Treemap",
|
|
42
|
+
defaultTooltipEventType: "item",
|
|
43
|
+
validateTooltipEventTypes: ["item"],
|
|
44
|
+
tooltipPayloadSearcher: treemapPayloadSearcher,
|
|
45
|
+
eventEmitter: void 0
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Build a hierarchical node structure with tooltipIndex paths for tooltip lookup.
|
|
49
|
+
*/
|
|
50
|
+
function buildNodeTree(data, dataKey, nameKey, parentIndex = "") {
|
|
51
|
+
return {
|
|
52
|
+
children: data.map((item, i) => {
|
|
53
|
+
const tooltipIndex = `${parentIndex}children[${i}]`;
|
|
54
|
+
if (item.children && item.children.length > 0) {
|
|
55
|
+
const childTree = buildNodeTree(item.children, dataKey, nameKey, `${tooltipIndex}.`);
|
|
56
|
+
return {
|
|
57
|
+
...item,
|
|
58
|
+
tooltipIndex,
|
|
59
|
+
...childTree
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...item,
|
|
64
|
+
tooltipIndex,
|
|
65
|
+
value: sumValues(item, dataKey)
|
|
66
|
+
};
|
|
67
|
+
}),
|
|
68
|
+
name: "root",
|
|
69
|
+
tooltipIndex: parentIndex
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
var TreemapVueProps = {
|
|
73
|
+
data: {
|
|
74
|
+
type: Array,
|
|
75
|
+
required: true
|
|
76
|
+
},
|
|
77
|
+
dataKey: {
|
|
78
|
+
type: String,
|
|
79
|
+
default: "value"
|
|
80
|
+
},
|
|
81
|
+
nameKey: {
|
|
82
|
+
type: String,
|
|
83
|
+
default: "name"
|
|
84
|
+
},
|
|
85
|
+
width: {
|
|
86
|
+
type: Number,
|
|
87
|
+
required: true
|
|
88
|
+
},
|
|
89
|
+
height: {
|
|
90
|
+
type: Number,
|
|
91
|
+
required: true
|
|
92
|
+
},
|
|
93
|
+
aspectRatio: {
|
|
94
|
+
type: Number,
|
|
95
|
+
default: 4 / 3
|
|
96
|
+
},
|
|
97
|
+
fill: {
|
|
98
|
+
type: String,
|
|
99
|
+
default: "#808080"
|
|
100
|
+
},
|
|
101
|
+
stroke: {
|
|
102
|
+
type: String,
|
|
103
|
+
default: "#fff"
|
|
104
|
+
},
|
|
105
|
+
type: {
|
|
106
|
+
type: String,
|
|
107
|
+
default: "flat"
|
|
108
|
+
},
|
|
109
|
+
colorPanel: {
|
|
110
|
+
type: Array,
|
|
111
|
+
default: void 0
|
|
112
|
+
},
|
|
113
|
+
isAnimationActive: {
|
|
114
|
+
type: Boolean,
|
|
115
|
+
default: true
|
|
116
|
+
},
|
|
117
|
+
transition: {
|
|
118
|
+
type: Object,
|
|
119
|
+
default: () => ({
|
|
120
|
+
duration: .8,
|
|
121
|
+
ease: "easeOut"
|
|
122
|
+
})
|
|
123
|
+
},
|
|
124
|
+
onClick: {
|
|
125
|
+
type: Function,
|
|
126
|
+
default: void 0
|
|
127
|
+
},
|
|
128
|
+
onMouseEnter: {
|
|
129
|
+
type: Function,
|
|
130
|
+
default: void 0
|
|
131
|
+
},
|
|
132
|
+
onMouseLeave: {
|
|
133
|
+
type: Function,
|
|
134
|
+
default: void 0
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Inner component that has access to the Redux store (provided by Treemap wrapper).
|
|
139
|
+
*/
|
|
140
|
+
var TreemapInner = /* @__PURE__ */ defineComponent({
|
|
141
|
+
name: "TreemapInner",
|
|
142
|
+
props: TreemapVueProps,
|
|
143
|
+
slots: Object,
|
|
144
|
+
setup(props, { slots }) {
|
|
145
|
+
const dispatch = useAppDispatch();
|
|
146
|
+
const colors = computed(() => props.colorPanel ?? DEFAULT_COLORS);
|
|
147
|
+
const breadcrumbTrail = ref([]);
|
|
148
|
+
const currentData = ref(null);
|
|
149
|
+
const animationKey = ref(0);
|
|
150
|
+
const isNestMode = computed(() => props.type === "nest");
|
|
151
|
+
const nestCurrentData = computed(() => {
|
|
152
|
+
if (!isNestMode.value) return null;
|
|
153
|
+
return currentData.value ?? props.data;
|
|
154
|
+
});
|
|
155
|
+
function computeNestLevelData(data) {
|
|
156
|
+
return data.map((item) => {
|
|
157
|
+
const aggregatedValue = sumValues(item, props.dataKey);
|
|
158
|
+
const { children: _, ...rest } = item;
|
|
159
|
+
return {
|
|
160
|
+
...rest,
|
|
161
|
+
[props.dataKey]: aggregatedValue
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const nodes = computed(() => {
|
|
166
|
+
return computeTreemapLayout({
|
|
167
|
+
data: isNestMode.value ? computeNestLevelData(nestCurrentData.value ?? props.data) : props.data,
|
|
168
|
+
width: props.width,
|
|
169
|
+
height: props.height,
|
|
170
|
+
dataKey: props.dataKey,
|
|
171
|
+
nameKey: props.nameKey,
|
|
172
|
+
aspectRatio: props.aspectRatio,
|
|
173
|
+
colorPanel: colors.value
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
const nodeTree = computed(() => {
|
|
177
|
+
return buildNodeTree(isNestMode.value ? nestCurrentData.value ?? props.data : props.data, props.dataKey, props.nameKey);
|
|
178
|
+
});
|
|
179
|
+
watchEffect((onCleanup) => {
|
|
180
|
+
const tooltipEntrySettings = {
|
|
181
|
+
dataDefinedOnItem: nodeTree.value,
|
|
182
|
+
positions: void 0,
|
|
183
|
+
settings: {
|
|
184
|
+
stroke: props.stroke,
|
|
185
|
+
strokeWidth: void 0,
|
|
186
|
+
fill: props.fill,
|
|
187
|
+
dataKey: props.dataKey,
|
|
188
|
+
nameKey: props.nameKey,
|
|
189
|
+
name: void 0,
|
|
190
|
+
hide: false,
|
|
191
|
+
type: void 0,
|
|
192
|
+
color: props.fill,
|
|
193
|
+
unit: ""
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
dispatch(addTooltipEntrySettings(tooltipEntrySettings));
|
|
197
|
+
onCleanup(() => {
|
|
198
|
+
dispatch(removeTooltipEntrySettings(tooltipEntrySettings));
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
function getTooltipIndex(node) {
|
|
202
|
+
const data = isNestMode.value ? nestCurrentData.value ?? props.data : props.data;
|
|
203
|
+
const idx = data.findIndex((item) => item[props.nameKey] === node.name);
|
|
204
|
+
if (idx >= 0) return `children[${idx}]`;
|
|
205
|
+
for (let i = 0; i < data.length; i++) if (data[i].children) {
|
|
206
|
+
const childIdx = data[i].children.findIndex((c) => c[props.nameKey] === node.name);
|
|
207
|
+
if (childIdx >= 0) return `children[${i}].children[${childIdx}]`;
|
|
208
|
+
}
|
|
209
|
+
return `children[0]`;
|
|
210
|
+
}
|
|
211
|
+
function handleNestClick(node, e) {
|
|
212
|
+
const sourceData = nestCurrentData.value ?? props.data;
|
|
213
|
+
const clickedItem = sourceData.find((item) => item[props.nameKey] === node.name);
|
|
214
|
+
if (clickedItem?.children && clickedItem.children.length > 0) {
|
|
215
|
+
breadcrumbTrail.value = [...breadcrumbTrail.value, {
|
|
216
|
+
name: clickedItem[props.nameKey] ?? clickedItem.name,
|
|
217
|
+
data: sourceData
|
|
218
|
+
}];
|
|
219
|
+
currentData.value = clickedItem.children;
|
|
220
|
+
animationKey.value++;
|
|
221
|
+
}
|
|
222
|
+
props.onClick?.(node, e);
|
|
223
|
+
}
|
|
224
|
+
function navigateToBreadcrumb(index) {
|
|
225
|
+
if (index < 0) {
|
|
226
|
+
currentData.value = null;
|
|
227
|
+
breadcrumbTrail.value = [];
|
|
228
|
+
} else {
|
|
229
|
+
currentData.value = breadcrumbTrail.value[index].data;
|
|
230
|
+
breadcrumbTrail.value = breadcrumbTrail.value.slice(0, index);
|
|
231
|
+
}
|
|
232
|
+
animationKey.value++;
|
|
233
|
+
}
|
|
234
|
+
function getNodeFill(node) {
|
|
235
|
+
return node.color ?? props.fill;
|
|
236
|
+
}
|
|
237
|
+
function handleNodeMouseEnter(node, e) {
|
|
238
|
+
const tooltipIndex = getTooltipIndex(node);
|
|
239
|
+
const activeCoordinate = {
|
|
240
|
+
x: node.x + node.width / 2,
|
|
241
|
+
y: node.y + node.height / 2
|
|
242
|
+
};
|
|
243
|
+
dispatch(setActiveMouseOverItemIndex({
|
|
244
|
+
activeIndex: tooltipIndex,
|
|
245
|
+
activeDataKey: props.dataKey,
|
|
246
|
+
activeCoordinate
|
|
247
|
+
}));
|
|
248
|
+
props.onMouseEnter?.(node, e);
|
|
249
|
+
}
|
|
250
|
+
function handleNodeMouseLeave(node, e) {
|
|
251
|
+
dispatch(mouseLeaveItem());
|
|
252
|
+
props.onMouseLeave?.(node, e);
|
|
253
|
+
}
|
|
254
|
+
function handleNodeClick(node, e) {
|
|
255
|
+
if (isNestMode.value) handleNestClick(node, e);
|
|
256
|
+
else {
|
|
257
|
+
const tooltipIndex = getTooltipIndex(node);
|
|
258
|
+
const activeCoordinate = {
|
|
259
|
+
x: node.x + node.width / 2,
|
|
260
|
+
y: node.y + node.height / 2
|
|
261
|
+
};
|
|
262
|
+
dispatch(setActiveClickItemIndex({
|
|
263
|
+
activeIndex: tooltipIndex,
|
|
264
|
+
activeDataKey: props.dataKey,
|
|
265
|
+
activeCoordinate
|
|
266
|
+
}));
|
|
267
|
+
props.onClick?.(node, e);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function renderNodeAtProgress(node, index, progress) {
|
|
271
|
+
const nodeFill = getNodeFill(node);
|
|
272
|
+
const translateX = (-node.x - node.width) * (1 - progress);
|
|
273
|
+
const transform = progress < 1 ? `translate(${translateX}, 0)` : void 0;
|
|
274
|
+
const nodeProps = {
|
|
275
|
+
...node,
|
|
276
|
+
index,
|
|
277
|
+
fill: nodeFill,
|
|
278
|
+
stroke: props.stroke
|
|
279
|
+
};
|
|
280
|
+
if (slots.content) return createVNode("g", {
|
|
281
|
+
"key": `node-${index}`,
|
|
282
|
+
"class": "v-charts-treemap-node",
|
|
283
|
+
"style": { transformOrigin: `${node.x}px ${node.y}px` },
|
|
284
|
+
"transform": transform,
|
|
285
|
+
"onClick": (e) => handleNodeClick(node, e),
|
|
286
|
+
"onMouseenter": (e) => handleNodeMouseEnter(node, e),
|
|
287
|
+
"onMouseleave": (e) => handleNodeMouseLeave(node, e)
|
|
288
|
+
}, [slots.content(nodeProps)]);
|
|
289
|
+
const arrow = isNestMode.value && (() => {
|
|
290
|
+
const item = (nestCurrentData.value ?? props.data).find((d) => d[props.nameKey] === node.name);
|
|
291
|
+
return item?.children && item.children.length > 0;
|
|
292
|
+
})() && node.width > 10 && node.height > 10 ? createVNode("polygon", {
|
|
293
|
+
"points": `${node.x + 2},${node.y + node.height / 2} ${node.x + 6},${node.y + node.height / 2 + 3} ${node.x + 2},${node.y + node.height / 2 + 6}`,
|
|
294
|
+
"fill": "#fff"
|
|
295
|
+
}, null) : null;
|
|
296
|
+
const nameSize = node.width > 20 && node.height > 20 ? getStringSize(node.name, { fontSize: "14px" }) : {
|
|
297
|
+
width: Infinity,
|
|
298
|
+
height: Infinity
|
|
299
|
+
};
|
|
300
|
+
const text = node.width > 20 && node.height > 20 && nameSize.width < node.width && nameSize.height < node.height ? createVNode("text", {
|
|
301
|
+
"x": node.x + 8,
|
|
302
|
+
"y": node.y + node.height / 2 + 7,
|
|
303
|
+
"fill": "#fff",
|
|
304
|
+
"font-size": 14
|
|
305
|
+
}, [node.name]) : null;
|
|
306
|
+
return createVNode("g", {
|
|
307
|
+
"key": `node-${index}`,
|
|
308
|
+
"class": "v-charts-treemap-node",
|
|
309
|
+
"style": { transformOrigin: `${node.x}px ${node.y}px` },
|
|
310
|
+
"transform": transform,
|
|
311
|
+
"onClick": (e) => handleNodeClick(node, e),
|
|
312
|
+
"onMouseenter": (e) => handleNodeMouseEnter(node, e),
|
|
313
|
+
"onMouseleave": (e) => handleNodeMouseLeave(node, e)
|
|
314
|
+
}, [
|
|
315
|
+
createVNode("rect", {
|
|
316
|
+
"x": node.x,
|
|
317
|
+
"y": node.y,
|
|
318
|
+
"width": node.width,
|
|
319
|
+
"height": node.height,
|
|
320
|
+
"fill": nodeFill,
|
|
321
|
+
"stroke": props.stroke
|
|
322
|
+
}, null),
|
|
323
|
+
arrow,
|
|
324
|
+
text
|
|
325
|
+
]);
|
|
326
|
+
}
|
|
327
|
+
function renderBreadcrumb() {
|
|
328
|
+
if (!isNestMode.value || breadcrumbTrail.value.length === 0) return null;
|
|
329
|
+
return createVNode("div", { "class": "v-charts-treemap-breadcrumb" }, [createVNode("span", {
|
|
330
|
+
"class": "v-charts-treemap-breadcrumb-item",
|
|
331
|
+
"style": { cursor: "pointer" },
|
|
332
|
+
"onClick": () => navigateToBreadcrumb(-1)
|
|
333
|
+
}, [createTextVNode("Root")]), breadcrumbTrail.value.map((entry, i) => createVNode("span", { "key": i }, [createVNode("span", { "style": { margin: "0 4px" } }, [createTextVNode("/")]), createVNode("span", {
|
|
334
|
+
"class": "v-charts-treemap-breadcrumb-item",
|
|
335
|
+
"style": { cursor: "pointer" },
|
|
336
|
+
"onClick": () => navigateToBreadcrumb(i)
|
|
337
|
+
}, [entry.name])]))]);
|
|
338
|
+
}
|
|
339
|
+
return () => createVNode(Fragment, null, [renderBreadcrumb(), createVNode(Surface, {
|
|
340
|
+
"width": props.width,
|
|
341
|
+
"height": props.height
|
|
342
|
+
}, { default: () => [createVNode(Layer, { "class": "v-charts-treemap" }, { default: () => [createVNode(Animate, {
|
|
343
|
+
"key": animationKey.value,
|
|
344
|
+
"isActive": props.isAnimationActive,
|
|
345
|
+
"from": 0,
|
|
346
|
+
"to": 1,
|
|
347
|
+
"transition": props.transition
|
|
348
|
+
}, { default: (progress) => nodes.value.map((node, index) => renderNodeAtProgress(node, index, progress)) })] })] })]);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
/**
|
|
352
|
+
* Treemap chart — hierarchical data visualization using nested rectangles.
|
|
353
|
+
*
|
|
354
|
+
* Supports `<Tooltip>` as a child component (same pattern as BarChart, LineChart, etc.).
|
|
355
|
+
*
|
|
356
|
+
* Usage:
|
|
357
|
+
* ```vue
|
|
358
|
+
* <Treemap :data="data" data-key="value" :width="600" :height="400">
|
|
359
|
+
* <Tooltip />
|
|
360
|
+
* </Treemap>
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
var Treemap = /* @__PURE__ */ defineComponent({
|
|
364
|
+
name: "Treemap",
|
|
365
|
+
props: TreemapVueProps,
|
|
366
|
+
slots: Object,
|
|
367
|
+
setup(props, { slots }) {
|
|
368
|
+
provideStore({ store: createRechartsStore({ options: treemapOptions }, "Treemap") });
|
|
369
|
+
return () => {
|
|
370
|
+
if (!props.data || props.data.length === 0) return null;
|
|
371
|
+
return createVNode(RechartsWrapper, {
|
|
372
|
+
"width": props.width,
|
|
373
|
+
"height": props.height
|
|
374
|
+
}, { default: () => [createVNode(TreemapInner, props, { content: slots.content }), slots.default?.()] });
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
//#endregion
|
|
379
|
+
export { Treemap, TreemapVueProps, treemapPayloadSearcher };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { classProp } from "../types/common.mjs";
|
|
1
2
|
import { validateWidthHeight } from "../utils/validate.mjs";
|
|
2
3
|
import { provideClipPathId } from "./provideClipPathId.mjs";
|
|
3
4
|
import { createRechartsStore } from "../state/store.mjs";
|
|
@@ -34,7 +35,7 @@ var CategoricalProps = {
|
|
|
34
35
|
default: 4
|
|
35
36
|
},
|
|
36
37
|
barSize: { type: [Number, String] },
|
|
37
|
-
class:
|
|
38
|
+
class: classProp,
|
|
38
39
|
compact: { type: Boolean },
|
|
39
40
|
cx: { type: [Number, String] },
|
|
40
41
|
cy: { type: [Number, String] },
|
|
@@ -130,7 +131,7 @@ function generateCategoricalChart({ chartName, defaultProps = {}, defaultTooltip
|
|
|
130
131
|
}), { default: () => [createVNode(ClipPath_default, { "clipPathId": clipPathId }, null), slots.default?.()] })
|
|
131
132
|
]);
|
|
132
133
|
if (props.accessibilityLayer) {
|
|
133
|
-
attributes.
|
|
134
|
+
attributes.tabindex = props.tabIndex ?? 0;
|
|
134
135
|
attributes.role = props.role ?? "application";
|
|
135
136
|
}
|
|
136
137
|
const eventHandlerAttrs = {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { hierarchy, treemap, treemapSquarify } from "d3-hierarchy";
|
|
2
|
+
//#region src/chart/treemapUtils.ts
|
|
3
|
+
function computeTreemapLayout(options) {
|
|
4
|
+
const { data, width, height, dataKey, aspectRatio = 4 / 3, nameKey = "name", colorPanel } = options;
|
|
5
|
+
if (!data || data.length === 0 || width <= 0 || height <= 0) return [];
|
|
6
|
+
const root = hierarchy({ children: data }).sum((d) => {
|
|
7
|
+
if (d.children && d.children.length > 0) return 0;
|
|
8
|
+
const val = d[dataKey];
|
|
9
|
+
return typeof val === "number" && val > 0 ? val : 0;
|
|
10
|
+
}).sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
|
|
11
|
+
treemap().size([width, height]).tile(treemapSquarify.ratio(aspectRatio)).round(true)(root);
|
|
12
|
+
return root.leaves().map((leaf) => {
|
|
13
|
+
const d = leaf.data;
|
|
14
|
+
let ancestor = leaf;
|
|
15
|
+
while (ancestor.depth > 1 && ancestor.parent) ancestor = ancestor.parent;
|
|
16
|
+
const rootIndex = ancestor.parent ? ancestor.parent.children.indexOf(ancestor) : 0;
|
|
17
|
+
return {
|
|
18
|
+
x: leaf.x0,
|
|
19
|
+
y: leaf.y0,
|
|
20
|
+
width: leaf.x1 - leaf.x0,
|
|
21
|
+
height: leaf.y1 - leaf.y0,
|
|
22
|
+
depth: leaf.depth,
|
|
23
|
+
name: d[nameKey] ?? "",
|
|
24
|
+
value: leaf.value ?? 0,
|
|
25
|
+
payload: d,
|
|
26
|
+
root: rootIndex,
|
|
27
|
+
color: colorPanel ? colorPanel[rootIndex % colorPanel.length] : void 0
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { computeTreemapLayout };
|