vccs 0.4.1 → 0.6.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/cartesian/axis/YAxis.d.ts +5 -5
- package/dist/cartesian/cartesian-axis/CartesianAxis.d.ts +12 -1
- package/dist/cartesian/cartesianPositionToCSSTranslate.d.ts +15 -0
- package/dist/cartesian/getCartesianPosition.d.ts +45 -0
- package/dist/cartesian/index.d.ts +1 -0
- package/dist/cartesian/type.d.ts +8 -0
- package/dist/chart/{RechartsWrapper.d.ts → ChartsWrapper.d.ts} +16 -1
- package/dist/chart/Surface.d.ts +2 -2
- package/dist/chart/generateCategoricalChart.d.ts +4 -0
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/legend/hooks/useLegend.d.ts +3 -0
- package/dist/components/legend/type.d.ts +18 -2
- package/dist/components/legend/utils.d.ts +9 -0
- package/dist/container/Surface.d.ts +3 -3
- package/dist/es/cartesian/axis/XAxis.mjs +11 -5
- package/dist/es/cartesian/axis/YAxis.mjs +62 -9
- package/dist/es/cartesian/cartesian-axis/CartesianAxis.mjs +6 -1
- package/dist/es/cartesian/cartesianPositionToCSSTranslate.mjs +28 -0
- package/dist/es/cartesian/getCartesianPosition.mjs +176 -0
- package/dist/es/chart/{RechartsWrapper.mjs → ChartsWrapper.mjs} +26 -6
- package/dist/es/chart/Sankey.mjs +2 -2
- package/dist/es/chart/SunburstChart.mjs +2 -2
- package/dist/es/chart/Surface.vue_vue_type_script_setup_true_lang.mjs +7 -1
- package/dist/es/chart/Treemap.mjs +2 -2
- package/dist/es/chart/generateCategoricalChart.mjs +53 -49
- package/dist/es/components/legend/Legend.mjs +5 -3
- package/dist/es/components/legend/hooks/useLegend.mjs +50 -9
- package/dist/es/components/legend/type.mjs +9 -1
- package/dist/es/components/legend/utils.mjs +12 -1
- package/dist/es/container/ResponsiveContainer.vue_vue_type_script_setup_true_lang.mjs +2 -11
- package/dist/es/container/Surface.mjs +5 -1
- package/dist/es/hooks/useResponsiveSize.mjs +27 -0
- package/dist/es/hooks/useRoundedSize.mjs +31 -0
- package/dist/es/shape/Sector.mjs +1 -1
- package/dist/es/state/cartesianAxisSlice.mjs +13 -2
- package/dist/es/state/legendSlice.mjs +2 -0
- package/dist/es/state/selectors/axisSelectors.mjs +3 -3
- package/dist/es/state/selectors/barSelectors.mjs +1 -1
- package/dist/es/state/selectors/pieSelectors.mjs +1 -1
- package/dist/es/state/selectors/polarAxisSelectors.mjs +1 -1
- package/dist/es/state/selectors/radialBarSelectors.mjs +1 -1
- package/dist/es/state/selectors/selectChartOffset.mjs +7 -4
- package/dist/es/state/selectors/selectLegendArea.mjs +19 -0
- package/dist/es/state/selectors/tooltipSelectors.mjs +1 -1
- package/dist/es/state/touchEventsMiddleware.mjs +1 -1
- package/dist/es/utils/YAxisUtils.mjs +29 -0
- package/dist/es/utils/chart.mjs +1 -1
- package/dist/es/utils/legend.mjs +23 -1
- package/dist/hooks/useResponsiveSize.d.ts +22 -0
- package/dist/hooks/useRoundedSize.d.ts +22 -0
- package/dist/state/cartesianAxisSlice.d.ts +11 -3
- package/dist/state/legendSlice.d.ts +3 -0
- package/dist/state/selectors/selectLegendArea.d.ts +7 -0
- package/dist/types/axis.d.ts +11 -1
- package/dist/utils/YAxisUtils.d.ts +20 -0
- package/package.json +1 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { isNumber, isPercent } from "../utils/validate.mjs";
|
|
2
|
+
import { getPercentValue } from "../utils/data.mjs";
|
|
3
|
+
//#region src/cartesian/getCartesianPosition.ts
|
|
4
|
+
function cartesianViewBoxToTrapezoid(box) {
|
|
5
|
+
if (!box) return;
|
|
6
|
+
return {
|
|
7
|
+
x: box.x,
|
|
8
|
+
y: box.y,
|
|
9
|
+
upperWidth: "upperWidth" in box ? box.upperWidth : box.width,
|
|
10
|
+
lowerWidth: "lowerWidth" in box ? box.lowerWidth : box.width,
|
|
11
|
+
width: box.width,
|
|
12
|
+
height: box.height
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Calculates the position and alignment for a generic element in a Cartesian coordinate system.
|
|
17
|
+
*
|
|
18
|
+
* @param options - The options including viewBox, position, and offset.
|
|
19
|
+
* @returns The calculated x, y, alignment and size.
|
|
20
|
+
*/
|
|
21
|
+
function getCartesianPosition(options) {
|
|
22
|
+
const { viewBox, position, offset = 0, parentViewBox, clamp } = options;
|
|
23
|
+
const { x, y, height, upperWidth, lowerWidth } = cartesianViewBoxToTrapezoid(viewBox);
|
|
24
|
+
const upperX = x;
|
|
25
|
+
const lowerX = x + (upperWidth - lowerWidth) / 2;
|
|
26
|
+
const middleX = (upperX + lowerX) / 2;
|
|
27
|
+
const midHeightWidth = (upperWidth + lowerWidth) / 2;
|
|
28
|
+
const centerX = upperX + upperWidth / 2;
|
|
29
|
+
const verticalSign = height >= 0 ? 1 : -1;
|
|
30
|
+
const verticalOffset = verticalSign * offset;
|
|
31
|
+
const verticalEnd = verticalSign > 0 ? "end" : "start";
|
|
32
|
+
const verticalStart = verticalSign > 0 ? "start" : "end";
|
|
33
|
+
const horizontalSign = upperWidth >= 0 ? 1 : -1;
|
|
34
|
+
const horizontalOffset = horizontalSign * offset;
|
|
35
|
+
const horizontalEnd = horizontalSign > 0 ? "end" : "start";
|
|
36
|
+
const horizontalStart = horizontalSign > 0 ? "start" : "end";
|
|
37
|
+
if (position === "top") {
|
|
38
|
+
const result = {
|
|
39
|
+
x: upperX + upperWidth / 2,
|
|
40
|
+
y: y - verticalOffset,
|
|
41
|
+
horizontalAnchor: "middle",
|
|
42
|
+
verticalAnchor: verticalEnd
|
|
43
|
+
};
|
|
44
|
+
if (clamp && parentViewBox) {
|
|
45
|
+
result.height = Math.max(y - parentViewBox.y, 0);
|
|
46
|
+
result.width = upperWidth;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
if (position === "bottom") {
|
|
51
|
+
const result = {
|
|
52
|
+
x: lowerX + lowerWidth / 2,
|
|
53
|
+
y: y + height + verticalOffset,
|
|
54
|
+
horizontalAnchor: "middle",
|
|
55
|
+
verticalAnchor: verticalStart
|
|
56
|
+
};
|
|
57
|
+
if (clamp && parentViewBox) {
|
|
58
|
+
result.height = Math.max(parentViewBox.y + parentViewBox.height - (y + height), 0);
|
|
59
|
+
result.width = lowerWidth;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
if (position === "left") {
|
|
64
|
+
const result = {
|
|
65
|
+
x: middleX - horizontalOffset,
|
|
66
|
+
y: y + height / 2,
|
|
67
|
+
horizontalAnchor: horizontalEnd,
|
|
68
|
+
verticalAnchor: "middle"
|
|
69
|
+
};
|
|
70
|
+
if (clamp && parentViewBox) {
|
|
71
|
+
result.width = Math.max(result.x - parentViewBox.x, 0);
|
|
72
|
+
result.height = height;
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
if (position === "right") {
|
|
77
|
+
const result = {
|
|
78
|
+
x: middleX + midHeightWidth + horizontalOffset,
|
|
79
|
+
y: y + height / 2,
|
|
80
|
+
horizontalAnchor: horizontalStart,
|
|
81
|
+
verticalAnchor: "middle"
|
|
82
|
+
};
|
|
83
|
+
if (clamp && parentViewBox) {
|
|
84
|
+
result.width = Math.max(parentViewBox.x + parentViewBox.width - result.x, 0);
|
|
85
|
+
result.height = height;
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
const sizeAttrs = clamp && parentViewBox ? {
|
|
90
|
+
width: midHeightWidth,
|
|
91
|
+
height
|
|
92
|
+
} : {};
|
|
93
|
+
if (position === "insideLeft") return {
|
|
94
|
+
x: middleX + horizontalOffset,
|
|
95
|
+
y: y + height / 2,
|
|
96
|
+
horizontalAnchor: horizontalStart,
|
|
97
|
+
verticalAnchor: "middle",
|
|
98
|
+
...sizeAttrs
|
|
99
|
+
};
|
|
100
|
+
if (position === "insideRight") return {
|
|
101
|
+
x: middleX + midHeightWidth - horizontalOffset,
|
|
102
|
+
y: y + height / 2,
|
|
103
|
+
horizontalAnchor: horizontalEnd,
|
|
104
|
+
verticalAnchor: "middle",
|
|
105
|
+
...sizeAttrs
|
|
106
|
+
};
|
|
107
|
+
if (position === "insideTop") return {
|
|
108
|
+
x: upperX + upperWidth / 2,
|
|
109
|
+
y: y + verticalOffset,
|
|
110
|
+
horizontalAnchor: "middle",
|
|
111
|
+
verticalAnchor: verticalStart,
|
|
112
|
+
...sizeAttrs
|
|
113
|
+
};
|
|
114
|
+
if (position === "insideBottom") return {
|
|
115
|
+
x: lowerX + lowerWidth / 2,
|
|
116
|
+
y: y + height - verticalOffset,
|
|
117
|
+
horizontalAnchor: "middle",
|
|
118
|
+
verticalAnchor: verticalEnd,
|
|
119
|
+
...sizeAttrs
|
|
120
|
+
};
|
|
121
|
+
if (position === "insideTopLeft") return {
|
|
122
|
+
x: upperX + horizontalOffset,
|
|
123
|
+
y: y + verticalOffset,
|
|
124
|
+
horizontalAnchor: horizontalStart,
|
|
125
|
+
verticalAnchor: verticalStart,
|
|
126
|
+
...sizeAttrs
|
|
127
|
+
};
|
|
128
|
+
if (position === "insideTopRight") return {
|
|
129
|
+
x: upperX + upperWidth - horizontalOffset,
|
|
130
|
+
y: y + verticalOffset,
|
|
131
|
+
horizontalAnchor: horizontalEnd,
|
|
132
|
+
verticalAnchor: verticalStart,
|
|
133
|
+
...sizeAttrs
|
|
134
|
+
};
|
|
135
|
+
if (position === "insideBottomLeft") return {
|
|
136
|
+
x: lowerX + horizontalOffset,
|
|
137
|
+
y: y + height - verticalOffset,
|
|
138
|
+
horizontalAnchor: horizontalStart,
|
|
139
|
+
verticalAnchor: verticalEnd,
|
|
140
|
+
...sizeAttrs
|
|
141
|
+
};
|
|
142
|
+
if (position === "insideBottomRight") return {
|
|
143
|
+
x: lowerX + lowerWidth - horizontalOffset,
|
|
144
|
+
y: y + height - verticalOffset,
|
|
145
|
+
horizontalAnchor: horizontalEnd,
|
|
146
|
+
verticalAnchor: verticalEnd,
|
|
147
|
+
...sizeAttrs
|
|
148
|
+
};
|
|
149
|
+
if (!!position && typeof position === "object" && (isNumber(position.x) || isPercent(position.x)) && (isNumber(position.y) || isPercent(position.y))) return {
|
|
150
|
+
x: x + getPercentValue(position.x, midHeightWidth),
|
|
151
|
+
y: y + getPercentValue(position.y, height),
|
|
152
|
+
horizontalAnchor: "end",
|
|
153
|
+
verticalAnchor: "end",
|
|
154
|
+
...sizeAttrs
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
x: centerX,
|
|
158
|
+
y: y + height / 2,
|
|
159
|
+
horizontalAnchor: "middle",
|
|
160
|
+
verticalAnchor: "middle",
|
|
161
|
+
...sizeAttrs
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
var absolutePositions = [
|
|
165
|
+
"top",
|
|
166
|
+
"left",
|
|
167
|
+
"right",
|
|
168
|
+
"bottom"
|
|
169
|
+
];
|
|
170
|
+
function isOutsidePosition(position) {
|
|
171
|
+
if (position == null) return false;
|
|
172
|
+
if (typeof position === "object") return true;
|
|
173
|
+
return absolutePositions.includes(position);
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
export { getCartesianPosition, isOutsidePosition };
|
|
@@ -10,10 +10,11 @@ import { useSynchronisedEventsFromOtherCharts } from "../events/useChartSynchron
|
|
|
10
10
|
import { useReportScale } from "../state/utils/useReportScale.mjs";
|
|
11
11
|
import { providePortalRaw } from "./TooltipPortalContext.mjs";
|
|
12
12
|
import { provideLegendPortalRaw } from "./LegendPortalContext.mjs";
|
|
13
|
-
import { createVNode, defineComponent, ref } from "vue";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
import { createVNode, defineComponent, ref, watch } from "vue";
|
|
14
|
+
import { useResizeObserver } from "@vueuse/core";
|
|
15
|
+
//#region src/chart/ChartsWrapper.tsx
|
|
16
|
+
var ChartsWrapper = /* @__PURE__ */ defineComponent({
|
|
17
|
+
name: "ChartsWrapper",
|
|
17
18
|
props: {
|
|
18
19
|
class: classProp,
|
|
19
20
|
height: {
|
|
@@ -28,9 +29,14 @@ var RechartsWrapper = /* @__PURE__ */ defineComponent({
|
|
|
28
29
|
onMouseLeave: { type: Function },
|
|
29
30
|
onMouseMove: { type: Function },
|
|
30
31
|
onMouseUp: { type: Function },
|
|
32
|
+
onResize: { type: Function },
|
|
31
33
|
onTouchEnd: { type: Function },
|
|
32
34
|
onTouchMove: { type: Function },
|
|
33
35
|
onTouchStart: { type: Function },
|
|
36
|
+
responsive: {
|
|
37
|
+
type: Boolean,
|
|
38
|
+
default: false
|
|
39
|
+
},
|
|
34
40
|
style: { type: [
|
|
35
41
|
String,
|
|
36
42
|
Object,
|
|
@@ -49,11 +55,20 @@ var RechartsWrapper = /* @__PURE__ */ defineComponent({
|
|
|
49
55
|
const legendPortal = ref(null);
|
|
50
56
|
providePortalRaw(tooltipPortal);
|
|
51
57
|
provideLegendPortalRaw(legendPortal);
|
|
58
|
+
const wrapperEl = ref(null);
|
|
52
59
|
const innerRef = (node) => {
|
|
53
60
|
scaleRef.value = node;
|
|
54
61
|
tooltipPortal.value = node;
|
|
55
62
|
legendPortal.value = node;
|
|
63
|
+
wrapperEl.value = node;
|
|
56
64
|
};
|
|
65
|
+
watch(() => props.responsive, (responsive, _, onCleanup) => {
|
|
66
|
+
if (!responsive) return;
|
|
67
|
+
onCleanup(useResizeObserver(wrapperEl, (entries) => {
|
|
68
|
+
const { width, height } = entries[0].contentRect;
|
|
69
|
+
props.onResize?.(width, height);
|
|
70
|
+
}).stop);
|
|
71
|
+
}, { immediate: true });
|
|
57
72
|
const myOnClick = (e) => {
|
|
58
73
|
const chartPointer = getChartPointer(e);
|
|
59
74
|
if (chartPointer) dispatch(mouseClickAction(chartPointer));
|
|
@@ -136,7 +151,12 @@ var RechartsWrapper = /* @__PURE__ */ defineComponent({
|
|
|
136
151
|
};
|
|
137
152
|
return () => createVNode("div", {
|
|
138
153
|
"class": ["v-charts-wrapper", props.class],
|
|
139
|
-
"style": [{
|
|
154
|
+
"style": [props.responsive ? {
|
|
155
|
+
position: "relative",
|
|
156
|
+
cursor: "default",
|
|
157
|
+
width: "100%",
|
|
158
|
+
height: "100%"
|
|
159
|
+
} : {
|
|
140
160
|
position: "relative",
|
|
141
161
|
cursor: "default",
|
|
142
162
|
width: `${props.width}px`,
|
|
@@ -161,4 +181,4 @@ var RechartsWrapper = /* @__PURE__ */ defineComponent({
|
|
|
161
181
|
}
|
|
162
182
|
});
|
|
163
183
|
//#endregion
|
|
164
|
-
export {
|
|
184
|
+
export { ChartsWrapper };
|
package/dist/es/chart/Sankey.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { useAppDispatch } from "../state/hooks.mjs";
|
|
|
3
3
|
import { Layer } from "../container/Layer.mjs";
|
|
4
4
|
import { Animate } from "../animation/Animate.mjs";
|
|
5
5
|
import { createRechartsStore } from "../state/store.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { ChartsWrapper } from "./ChartsWrapper.mjs";
|
|
7
7
|
import Surface from "../container/Surface.mjs";
|
|
8
8
|
import { computeSankeyLayout, linkPathGenerator } from "./sankeyUtils.mjs";
|
|
9
9
|
import { Fragment, computed, createVNode, defineComponent, watchEffect } from "vue";
|
|
@@ -318,7 +318,7 @@ var Sankey = /* @__PURE__ */ defineComponent({
|
|
|
318
318
|
provideStore({ store: createRechartsStore({ options: sankeyOptions }, "Sankey") });
|
|
319
319
|
return () => {
|
|
320
320
|
if (!props.data || !props.data.nodes || props.data.nodes.length === 0) return null;
|
|
321
|
-
return createVNode(
|
|
321
|
+
return createVNode(ChartsWrapper, {
|
|
322
322
|
"width": props.width,
|
|
323
323
|
"height": props.height
|
|
324
324
|
}, { default: () => [createVNode(SankeyInner, props, {
|
|
@@ -3,7 +3,7 @@ import { addTooltipEntrySettings, mouseLeaveItem, removeTooltipEntrySettings, se
|
|
|
3
3
|
import { useAppDispatch } from "../state/hooks.mjs";
|
|
4
4
|
import { Layer } from "../container/Layer.mjs";
|
|
5
5
|
import { createRechartsStore } from "../state/store.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { ChartsWrapper } from "./ChartsWrapper.mjs";
|
|
7
7
|
import Surface from "../container/Surface.mjs";
|
|
8
8
|
import { Sector } from "../shape/Sector.mjs";
|
|
9
9
|
import { computeSunburstLayout } from "./sunburstUtils.mjs";
|
|
@@ -218,7 +218,7 @@ var SunburstChart = /* @__PURE__ */ defineComponent({
|
|
|
218
218
|
provideStore({ store: createRechartsStore({ options: sunburstOptions }, "SunburstChart") });
|
|
219
219
|
return () => {
|
|
220
220
|
if (!props.data?.children || props.data.children.length === 0) return null;
|
|
221
|
-
return createVNode(
|
|
221
|
+
return createVNode(ChartsWrapper, {
|
|
222
222
|
"width": props.width,
|
|
223
223
|
"height": props.height
|
|
224
224
|
}, { default: () => [createVNode(SunburstInner, props, { content: slots.content }), slots.default?.()] });
|
|
@@ -17,7 +17,13 @@ var Surface_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ define
|
|
|
17
17
|
height: {},
|
|
18
18
|
viewBox: {},
|
|
19
19
|
class: {},
|
|
20
|
-
style: {
|
|
20
|
+
style: { type: [
|
|
21
|
+
Boolean,
|
|
22
|
+
null,
|
|
23
|
+
String,
|
|
24
|
+
Object,
|
|
25
|
+
Array
|
|
26
|
+
] },
|
|
21
27
|
title: {},
|
|
22
28
|
desc: {}
|
|
23
29
|
},
|
|
@@ -4,7 +4,7 @@ import { Layer } from "../container/Layer.mjs";
|
|
|
4
4
|
import { getStringSize } from "../utils/attrs.mjs";
|
|
5
5
|
import { Animate } from "../animation/Animate.mjs";
|
|
6
6
|
import { createRechartsStore } from "../state/store.mjs";
|
|
7
|
-
import {
|
|
7
|
+
import { ChartsWrapper } from "./ChartsWrapper.mjs";
|
|
8
8
|
import Surface from "../container/Surface.mjs";
|
|
9
9
|
import { computeTreemapLayout } from "./treemapUtils.mjs";
|
|
10
10
|
import { Fragment, computed, createTextVNode, createVNode, defineComponent, ref, watchEffect } from "vue";
|
|
@@ -368,7 +368,7 @@ var Treemap = /* @__PURE__ */ defineComponent({
|
|
|
368
368
|
provideStore({ store: createRechartsStore({ options: treemapOptions }, "Treemap") });
|
|
369
369
|
return () => {
|
|
370
370
|
if (!props.data || props.data.length === 0) return null;
|
|
371
|
-
return createVNode(
|
|
371
|
+
return createVNode(ChartsWrapper, {
|
|
372
372
|
"width": props.width,
|
|
373
373
|
"height": props.height
|
|
374
374
|
}, { default: () => [createVNode(TreemapInner, props, { content: slots.content }), slots.default?.()] });
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { classProp } from "../types/common.mjs";
|
|
2
|
-
import { validateWidthHeight } from "../utils/validate.mjs";
|
|
3
2
|
import { provideClipPathId } from "./provideClipPathId.mjs";
|
|
4
3
|
import { createRechartsStore } from "../state/store.mjs";
|
|
5
4
|
import Surface_default from "./Surface.mjs";
|
|
6
5
|
import { ChartDataContextProvider } from "../context/ChartDataContextProvider.mjs";
|
|
7
6
|
import ClipPath_default from "../container/ClipPath.mjs";
|
|
8
|
-
import {
|
|
7
|
+
import { ChartsWrapper } from "./ChartsWrapper.mjs";
|
|
9
8
|
import { FULL_WIDTH_AND_HEIGHT } from "./const.mjs";
|
|
10
9
|
import { ReportMainChartProps } from "../state/ReportMainChartProps.mjs";
|
|
11
10
|
import ReportChartProps_default from "../state/ReportChartProps.mjs";
|
|
12
11
|
import { applyDefaultProps } from "../utils/props.mjs";
|
|
13
12
|
import { ReportPolarOptions } from "../state/ReportPolarOptions.mjs";
|
|
13
|
+
import { useResponsiveSize } from "../hooks/useResponsiveSize.mjs";
|
|
14
14
|
import { Fragment, createVNode, defineComponent, mergeProps } from "vue";
|
|
15
15
|
import { provideStore } from "@reduxjs/vue-redux";
|
|
16
16
|
//#region src/chart/generateCategoricalChart.tsx
|
|
@@ -63,6 +63,10 @@ var CategoricalProps = {
|
|
|
63
63
|
},
|
|
64
64
|
maxBarSize: { type: Number },
|
|
65
65
|
outerRadius: { type: [Number, String] },
|
|
66
|
+
responsive: {
|
|
67
|
+
type: Boolean,
|
|
68
|
+
default: false
|
|
69
|
+
},
|
|
66
70
|
reverseStackOrder: {
|
|
67
71
|
type: Boolean,
|
|
68
72
|
default: false
|
|
@@ -102,38 +106,43 @@ function generateCategoricalChart({ chartName, defaultProps = {}, defaultTooltip
|
|
|
102
106
|
eventEmitter: void 0
|
|
103
107
|
} }, props.id ?? chartName) });
|
|
104
108
|
const clipPathId = provideClipPathId(props);
|
|
109
|
+
const { effectiveWidth, effectiveHeight, hasValidSize, handleResize } = useResponsiveSize(props);
|
|
110
|
+
function renderPolarOptions(isPolarChart) {
|
|
111
|
+
if (!isPolarChart) return null;
|
|
112
|
+
return createVNode(ReportPolarOptions, {
|
|
113
|
+
"cx": props.cx ?? "50%",
|
|
114
|
+
"cy": props.cy ?? "50%",
|
|
115
|
+
"startAngle": props.startAngle ?? defaultProps.startAngle ?? 90,
|
|
116
|
+
"endAngle": props.endAngle ?? defaultProps.endAngle ?? -270,
|
|
117
|
+
"innerRadius": props.innerRadius ?? 0,
|
|
118
|
+
"outerRadius": props.outerRadius ?? "80%"
|
|
119
|
+
}, null);
|
|
120
|
+
}
|
|
105
121
|
return () => {
|
|
106
|
-
const { compact, width, height, title, desc, ...rest } = props;
|
|
122
|
+
const { compact, width, height, title, desc, responsive, ...rest } = props;
|
|
107
123
|
const attributes = { ...attrs };
|
|
108
|
-
if (!validateWidthHeight({
|
|
109
|
-
width,
|
|
110
|
-
height
|
|
111
|
-
})) return null;
|
|
112
124
|
const layout = props.layout ?? defaultProps.layout ?? defaultLayout;
|
|
113
125
|
const isPolarChart = layout === "centric" || layout === "radial";
|
|
114
|
-
if (compact)
|
|
115
|
-
|
|
116
|
-
createVNode(
|
|
117
|
-
"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"desc": desc
|
|
135
|
-
}), { default: () => [createVNode(ClipPath_default, { "clipPathId": clipPathId }, null), slots.default?.()] })
|
|
136
|
-
]);
|
|
126
|
+
if (compact) {
|
|
127
|
+
if (!hasValidSize.value) return null;
|
|
128
|
+
return createVNode(Fragment, null, [
|
|
129
|
+
createVNode(ChartDataContextProvider, { "chartData": props.data }, null),
|
|
130
|
+
createVNode(ReportMainChartProps, {
|
|
131
|
+
"width": effectiveWidth.value,
|
|
132
|
+
"height": effectiveHeight.value,
|
|
133
|
+
"layout": layout,
|
|
134
|
+
"margin": props.margin
|
|
135
|
+
}, null),
|
|
136
|
+
renderPolarOptions(isPolarChart),
|
|
137
|
+
createVNode(Surface_default, mergeProps(attrs, rest, {
|
|
138
|
+
"width": effectiveWidth.value,
|
|
139
|
+
"height": effectiveHeight.value,
|
|
140
|
+
"title": title,
|
|
141
|
+
"desc": desc
|
|
142
|
+
}), { default: () => [createVNode(ClipPath_default, { "clipPathId": clipPathId }, null), slots.default?.()] })
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
if (!responsive && !hasValidSize.value) return null;
|
|
137
146
|
if (props.accessibilityLayer) {
|
|
138
147
|
attributes.tabindex = props.tabIndex ?? 0;
|
|
139
148
|
attributes.role = props.role ?? "application";
|
|
@@ -143,34 +152,29 @@ function generateCategoricalChart({ chartName, defaultProps = {}, defaultTooltip
|
|
|
143
152
|
for (const [key, value] of Object.entries(attributes)) if (key.startsWith("on") && typeof value === "function") eventHandlerAttrs[key] = value;
|
|
144
153
|
else svgAttributes[key] = value;
|
|
145
154
|
return createVNode(Fragment, null, [
|
|
146
|
-
createVNode(ChartDataContextProvider, { "chartData": props.data }, null),
|
|
147
|
-
createVNode(ReportMainChartProps, {
|
|
148
|
-
"width":
|
|
149
|
-
"height":
|
|
155
|
+
hasValidSize.value && createVNode(ChartDataContextProvider, { "chartData": props.data }, null),
|
|
156
|
+
hasValidSize.value && createVNode(ReportMainChartProps, {
|
|
157
|
+
"width": effectiveWidth.value,
|
|
158
|
+
"height": effectiveHeight.value,
|
|
150
159
|
"layout": layout,
|
|
151
160
|
"margin": props.margin ?? defaultMargin
|
|
152
161
|
}, null),
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
"endAngle": props.endAngle ?? defaultProps.endAngle ?? -270,
|
|
158
|
-
"innerRadius": props.innerRadius ?? 0,
|
|
159
|
-
"outerRadius": props.outerRadius ?? "80%"
|
|
160
|
-
}, null),
|
|
161
|
-
createVNode(RechartsWrapper, mergeProps({
|
|
162
|
+
hasValidSize.value && renderPolarOptions(isPolarChart),
|
|
163
|
+
createVNode(ChartsWrapper, mergeProps({
|
|
164
|
+
"responsive": responsive,
|
|
165
|
+
"onResize": handleResize,
|
|
162
166
|
"style": props.style,
|
|
163
167
|
"class": props.class,
|
|
164
|
-
"width":
|
|
165
|
-
"height":
|
|
166
|
-
}, eventHandlerAttrs), { default: () => [createVNode(Surface_default, mergeProps(svgAttributes, {
|
|
167
|
-
"width":
|
|
168
|
-
"height":
|
|
168
|
+
"width": effectiveWidth.value,
|
|
169
|
+
"height": effectiveHeight.value
|
|
170
|
+
}, eventHandlerAttrs), { default: () => [hasValidSize.value && createVNode(Surface_default, mergeProps(svgAttributes, {
|
|
171
|
+
"width": effectiveWidth.value,
|
|
172
|
+
"height": effectiveHeight.value,
|
|
169
173
|
"title": title,
|
|
170
174
|
"desc": desc,
|
|
171
175
|
"style": FULL_WIDTH_AND_HEIGHT
|
|
172
176
|
}), { default: () => [createVNode(ClipPath_default, { "clipPathId": clipPathId }, null), slots.default?.()] }), slots.tooltip?.()] }),
|
|
173
|
-
createVNode(ReportChartProps_default, props, null)
|
|
177
|
+
hasValidSize.value && createVNode(ReportChartProps_default, props, null)
|
|
174
178
|
]);
|
|
175
179
|
};
|
|
176
180
|
}
|
|
@@ -12,7 +12,7 @@ var Legend_default = /* @__PURE__ */ defineComponent({
|
|
|
12
12
|
name: "Legend",
|
|
13
13
|
props: LegendVueProps,
|
|
14
14
|
setup(props, { slots }) {
|
|
15
|
-
const { legendRef, processedPayload, outerStyle, legendPortal, syncSettings, syncSize } = useLegend(props);
|
|
15
|
+
const { legendRef, processedPayload, outerStyle, legendPortal, resolvedLayout, positionViewBox, syncSettings, syncSize } = useLegend(props);
|
|
16
16
|
const { getItemStyle, getSvgStyle, formatValue, handleClick, handleMouseEnter, handleMouseLeave } = useLegendContent(props);
|
|
17
17
|
watchEffect(() => {
|
|
18
18
|
syncSettings();
|
|
@@ -20,7 +20,8 @@ var Legend_default = /* @__PURE__ */ defineComponent({
|
|
|
20
20
|
});
|
|
21
21
|
const renderDefaultContent = () => {
|
|
22
22
|
if (!processedPayload.value || processedPayload.value.length === 0) return null;
|
|
23
|
-
const {
|
|
23
|
+
const { align = "center", iconSize = 14 } = props;
|
|
24
|
+
const layout = resolvedLayout.value;
|
|
24
25
|
const finalStyle = {
|
|
25
26
|
padding: 0,
|
|
26
27
|
margin: 0,
|
|
@@ -73,13 +74,14 @@ var Legend_default = /* @__PURE__ */ defineComponent({
|
|
|
73
74
|
if (!processedPayload.value || processedPayload.value.length === 0) return null;
|
|
74
75
|
const contentProps = {
|
|
75
76
|
...props,
|
|
77
|
+
layout: resolvedLayout.value,
|
|
76
78
|
payload: processedPayload.value
|
|
77
79
|
};
|
|
78
80
|
if (slots.content) return slots.content(contentProps);
|
|
79
81
|
return renderDefaultContent();
|
|
80
82
|
};
|
|
81
83
|
return () => {
|
|
82
|
-
if (legendPortal.value == null) return null;
|
|
84
|
+
if (legendPortal.value == null || props.position != null && positionViewBox.value == null) return null;
|
|
83
85
|
const legendElement = createVNode("div", {
|
|
84
86
|
"class": "v-charts-legend-wrapper",
|
|
85
87
|
"style": outerStyle.value,
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { selectLegendPayload } from "../../../state/selectors/legendSelectors.mjs";
|
|
2
|
+
import { getCartesianPosition, isOutsidePosition } from "../../../cartesian/getCartesianPosition.mjs";
|
|
2
3
|
import { useAppDispatch, useAppSelector } from "../../../state/hooks.mjs";
|
|
3
|
-
import { useChartHeight, useChartWidth, useMargin } from "../../../context/chartLayoutContext.mjs";
|
|
4
|
+
import { useChartHeight, useChartWidth, useMargin, useViewBox } from "../../../context/chartLayoutContext.mjs";
|
|
4
5
|
import { setLegendSettings, setLegendSize } from "../../../state/legendSlice.mjs";
|
|
5
6
|
import { useLegendPortal } from "../../../chart/LegendPortalContext.mjs";
|
|
7
|
+
import { selectLegendArea } from "../../../state/selectors/selectLegendArea.mjs";
|
|
6
8
|
import { getUniqPayload } from "../../../utils/payload/getUniqPayload.mjs";
|
|
7
|
-
import {
|
|
9
|
+
import { cartesianPositionToCSSTranslate } from "../../../cartesian/cartesianPositionToCSSTranslate.mjs";
|
|
10
|
+
import { defaultUniqBy, getDefaultPosition, getLayoutForPosition, getOutsidePositionOffset, getWidthOrHeight } from "../utils.mjs";
|
|
8
11
|
import { computed, ref } from "vue";
|
|
9
12
|
import { sortBy } from "es-toolkit/compat";
|
|
10
13
|
import { useElementBounding } from "@vueuse/core";
|
|
@@ -16,14 +19,22 @@ function useLegend(props) {
|
|
|
16
19
|
const margin = useMargin();
|
|
17
20
|
const chartWidth = useChartWidth();
|
|
18
21
|
const chartHeight = useChartHeight();
|
|
22
|
+
const viewBox = useViewBox();
|
|
23
|
+
const legendArea = useAppSelector(selectLegendArea);
|
|
19
24
|
const legendRef = ref();
|
|
20
25
|
const { width: boundingWidth, height: boundingHeight } = useElementBounding(legendRef);
|
|
26
|
+
const resolvedLayout = computed(() => props.layout && props.layout !== "auto" ? props.layout : getLayoutForPosition(props.position));
|
|
21
27
|
const maxWidth = computed(() => chartWidth.value - (margin.value.left || 0) - (margin.value.right || 0));
|
|
22
|
-
const widthOrHeight = computed(() => getWidthOrHeight(
|
|
28
|
+
const widthOrHeight = computed(() => getWidthOrHeight(resolvedLayout.value, props.height, props.width, maxWidth.value));
|
|
23
29
|
const boundingBox = computed(() => ({
|
|
24
30
|
width: boundingWidth.value,
|
|
25
31
|
height: boundingHeight.value
|
|
26
32
|
}));
|
|
33
|
+
const positionViewBox = computed(() => {
|
|
34
|
+
if (props.position == null) return null;
|
|
35
|
+
return isOutsidePosition(props.position) ? legendArea.value : viewBox.value;
|
|
36
|
+
});
|
|
37
|
+
const shouldReportDimensions = computed(() => props.portal == null && (props.position == null || isOutsidePosition(props.position)));
|
|
27
38
|
const processedPayload = computed(() => {
|
|
28
39
|
if (!contextPayload.value || contextPayload.value.length === 0) return [];
|
|
29
40
|
let finalPayload = getUniqPayload(contextPayload.value, props.payloadUniqBy, defaultUniqBy);
|
|
@@ -33,30 +44,58 @@ function useLegend(props) {
|
|
|
33
44
|
}
|
|
34
45
|
return Array.from(finalPayload);
|
|
35
46
|
});
|
|
47
|
+
const positionStyle = computed(() => {
|
|
48
|
+
if (props.position == null || positionViewBox.value == null) return;
|
|
49
|
+
const positionResult = getCartesianPosition({
|
|
50
|
+
viewBox: positionViewBox.value,
|
|
51
|
+
position: props.position,
|
|
52
|
+
offset: props.offset ?? 0
|
|
53
|
+
});
|
|
54
|
+
const outsidePositionOffset = getOutsidePositionOffset(props.position, props.offset ?? 0, boundingBox.value);
|
|
55
|
+
const positionMaxWidth = resolvedLayout.value === "vertical" ? (positionViewBox.value.width ?? 0) / 2 : positionViewBox.value.width ?? 0;
|
|
56
|
+
const positionMaxHeight = resolvedLayout.value === "horizontal" ? (positionViewBox.value.height ?? 0) / 2 : positionViewBox.value.height ?? 0;
|
|
57
|
+
return {
|
|
58
|
+
width: "max-content",
|
|
59
|
+
height: "max-content",
|
|
60
|
+
maxWidth: `${positionMaxWidth}px`,
|
|
61
|
+
maxHeight: `${positionMaxHeight}px`,
|
|
62
|
+
overflowY: "auto",
|
|
63
|
+
top: `${positionResult.y + (outsidePositionOffset.top ?? 0)}px`,
|
|
64
|
+
left: `${positionResult.x + (outsidePositionOffset.left ?? 0)}px`,
|
|
65
|
+
transform: cartesianPositionToCSSTranslate(positionResult.horizontalAnchor, positionResult.verticalAnchor)
|
|
66
|
+
};
|
|
67
|
+
});
|
|
36
68
|
const outerStyle = computed(() => {
|
|
37
69
|
const userStyle = props.wrapperStyle ? { ...props.wrapperStyle } : {};
|
|
38
70
|
if (props.portal) return userStyle;
|
|
39
71
|
const baseStyle = {
|
|
40
72
|
position: "absolute",
|
|
41
|
-
width: widthOrHeight.value?.width
|
|
42
|
-
height: widthOrHeight.value?.height
|
|
73
|
+
width: widthOrHeight.value?.width || props.width || "auto",
|
|
74
|
+
height: widthOrHeight.value?.height || props.height || "auto"
|
|
43
75
|
};
|
|
44
|
-
const
|
|
76
|
+
const calculatedPositionStyle = positionStyle.value ?? getDefaultPosition(userStyle, {
|
|
77
|
+
layout: resolvedLayout.value,
|
|
78
|
+
align: props.align,
|
|
79
|
+
verticalAlign: props.verticalAlign
|
|
80
|
+
}, margin.value, chartWidth.value, chartHeight.value, boundingBox.value);
|
|
45
81
|
return {
|
|
46
82
|
...baseStyle,
|
|
47
|
-
...
|
|
83
|
+
...calculatedPositionStyle,
|
|
48
84
|
...userStyle
|
|
49
85
|
};
|
|
50
86
|
});
|
|
51
87
|
const legendPortal = computed(() => props.portal ?? legendPortalFromContext?.value);
|
|
52
88
|
const syncSettings = () => {
|
|
53
89
|
dispatch(setLegendSettings({
|
|
54
|
-
layout:
|
|
90
|
+
layout: resolvedLayout.value,
|
|
55
91
|
align: props.align,
|
|
56
|
-
verticalAlign: props.verticalAlign
|
|
92
|
+
verticalAlign: props.verticalAlign,
|
|
93
|
+
position: props.position,
|
|
94
|
+
offset: props.offset
|
|
57
95
|
}));
|
|
58
96
|
};
|
|
59
97
|
const syncSize = () => {
|
|
98
|
+
if (!shouldReportDimensions.value) return;
|
|
60
99
|
dispatch(setLegendSize({
|
|
61
100
|
width: boundingBox.value.width,
|
|
62
101
|
height: boundingBox.value.height
|
|
@@ -69,6 +108,8 @@ function useLegend(props) {
|
|
|
69
108
|
processedPayload,
|
|
70
109
|
outerStyle,
|
|
71
110
|
legendPortal,
|
|
111
|
+
resolvedLayout,
|
|
112
|
+
positionViewBox,
|
|
72
113
|
syncSettings,
|
|
73
114
|
syncSize
|
|
74
115
|
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var LegendVueProps = {
|
|
3
3
|
layout: {
|
|
4
4
|
type: String,
|
|
5
|
-
default: "
|
|
5
|
+
default: "auto"
|
|
6
6
|
},
|
|
7
7
|
align: {
|
|
8
8
|
type: String,
|
|
@@ -12,6 +12,14 @@ var LegendVueProps = {
|
|
|
12
12
|
type: String,
|
|
13
13
|
default: "bottom"
|
|
14
14
|
},
|
|
15
|
+
position: {
|
|
16
|
+
type: [String, Object],
|
|
17
|
+
default: void 0
|
|
18
|
+
},
|
|
19
|
+
offset: {
|
|
20
|
+
type: Number,
|
|
21
|
+
default: 0
|
|
22
|
+
},
|
|
15
23
|
width: Number,
|
|
16
24
|
height: Number,
|
|
17
25
|
iconSize: {
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { isNumber } from "../../utils/validate.mjs";
|
|
2
2
|
//#region src/components/legend/utils.ts
|
|
3
|
+
function getLayoutForPosition(position) {
|
|
4
|
+
if (position === "left" || position === "right" || position === "insideLeft" || position === "insideRight") return "vertical";
|
|
5
|
+
return "horizontal";
|
|
6
|
+
}
|
|
7
|
+
function getOutsidePositionOffset(position, offset, box) {
|
|
8
|
+
if (position === "top") return { top: box.height + offset };
|
|
9
|
+
if (position === "bottom") return { top: -box.height - offset };
|
|
10
|
+
if (position === "left") return { left: box.width + offset };
|
|
11
|
+
if (position === "right") return { left: -box.width - offset };
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
3
14
|
function getDefaultPosition(style, props, margin, chartWidth, chartHeight, box) {
|
|
4
15
|
const { layout, align, verticalAlign } = props;
|
|
5
16
|
let hPos = {};
|
|
@@ -22,4 +33,4 @@ function defaultUniqBy(entry) {
|
|
|
22
33
|
return entry.value;
|
|
23
34
|
}
|
|
24
35
|
//#endregion
|
|
25
|
-
export { defaultUniqBy, getDefaultPosition, getWidthOrHeight };
|
|
36
|
+
export { defaultUniqBy, getDefaultPosition, getLayoutForPosition, getOutsidePositionOffset, getWidthOrHeight };
|