@gravity-ui/charts 1.16.0 → 1.17.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/{esm/components/Axis → cjs/components/AxisX}/AxisX.js +1 -15
- package/dist/{esm/components/Axis → cjs/components/AxisY}/AxisY.d.ts +2 -10
- package/dist/cjs/components/AxisY/AxisY.js +173 -0
- package/dist/cjs/components/AxisY/prepare-axis-data.d.ts +9 -0
- package/dist/cjs/components/AxisY/prepare-axis-data.js +306 -0
- package/dist/cjs/components/AxisY/styles.css +15 -0
- package/dist/cjs/components/AxisY/types.d.ts +81 -0
- package/dist/cjs/components/AxisY/types.js +1 -0
- package/dist/cjs/components/AxisY/utils.d.ts +12 -0
- package/dist/cjs/components/AxisY/utils.js +71 -0
- package/dist/cjs/components/ChartInner/index.js +31 -3
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +13 -1
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.js +5 -2
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/utils.d.ts +7 -1
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/utils.js +12 -7
- package/dist/cjs/constants/date.d.ts +1 -0
- package/dist/cjs/constants/date.js +1 -0
- package/dist/cjs/constants/index.d.ts +1 -0
- package/dist/cjs/constants/index.js +1 -0
- package/dist/cjs/hooks/useChartOptions/types.d.ts +7 -1
- package/dist/cjs/hooks/useChartOptions/x-axis.js +5 -5
- package/dist/cjs/hooks/useChartOptions/y-axis.d.ts +3 -1
- package/dist/cjs/hooks/useChartOptions/y-axis.js +20 -20
- package/dist/cjs/hooks/useCrosshair/index.js +2 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/types/chart/axis.d.ts +11 -1
- package/dist/cjs/types/chart/base.d.ts +1 -0
- package/dist/cjs/utils/chart/axis-generators/bottom.js +3 -3
- package/dist/cjs/utils/chart/axis.d.ts +14 -8
- package/dist/cjs/utils/chart/axis.js +34 -9
- package/dist/cjs/utils/chart/format.js +2 -1
- package/dist/cjs/utils/chart/index.d.ts +0 -7
- package/dist/cjs/utils/chart/index.js +0 -17
- package/dist/cjs/utils/chart/text.d.ts +2 -1
- package/dist/cjs/utils/chart/text.js +3 -10
- package/dist/{cjs/components/Axis → esm/components/AxisX}/AxisX.js +1 -15
- package/dist/{cjs/components/Axis → esm/components/AxisY}/AxisY.d.ts +2 -10
- package/dist/esm/components/AxisY/AxisY.js +173 -0
- package/dist/esm/components/AxisY/prepare-axis-data.d.ts +9 -0
- package/dist/esm/components/AxisY/prepare-axis-data.js +306 -0
- package/dist/esm/components/AxisY/styles.css +15 -0
- package/dist/esm/components/AxisY/types.d.ts +81 -0
- package/dist/esm/components/AxisY/types.js +1 -0
- package/dist/esm/components/AxisY/utils.d.ts +12 -0
- package/dist/esm/components/AxisY/utils.js +71 -0
- package/dist/esm/components/ChartInner/index.js +31 -3
- package/dist/esm/components/ChartInner/useChartInnerProps.js +13 -1
- package/dist/esm/components/Tooltip/DefaultTooltipContent/index.js +5 -2
- package/dist/esm/components/Tooltip/DefaultTooltipContent/utils.d.ts +7 -1
- package/dist/esm/components/Tooltip/DefaultTooltipContent/utils.js +12 -7
- package/dist/esm/constants/date.d.ts +1 -0
- package/dist/esm/constants/date.js +1 -0
- package/dist/esm/constants/index.d.ts +1 -0
- package/dist/esm/constants/index.js +1 -0
- package/dist/esm/hooks/useChartOptions/types.d.ts +7 -1
- package/dist/esm/hooks/useChartOptions/x-axis.js +5 -5
- package/dist/esm/hooks/useChartOptions/y-axis.d.ts +3 -1
- package/dist/esm/hooks/useChartOptions/y-axis.js +20 -20
- package/dist/esm/hooks/useCrosshair/index.js +2 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/types/chart/axis.d.ts +11 -1
- package/dist/esm/types/chart/base.d.ts +1 -0
- package/dist/esm/utils/chart/axis-generators/bottom.js +3 -3
- package/dist/esm/utils/chart/axis.d.ts +14 -8
- package/dist/esm/utils/chart/axis.js +34 -9
- package/dist/esm/utils/chart/format.js +2 -1
- package/dist/esm/utils/chart/index.d.ts +0 -7
- package/dist/esm/utils/chart/index.js +0 -17
- package/dist/esm/utils/chart/text.d.ts +2 -1
- package/dist/esm/utils/chart/text.js +3 -10
- package/package.json +1 -1
- package/dist/cjs/components/Axis/AxisY.js +0 -416
- package/dist/cjs/components/Axis/index.d.ts +0 -2
- package/dist/cjs/components/Axis/index.js +0 -2
- package/dist/cjs/components/Tooltip/utils.d.ts +0 -30
- package/dist/cjs/components/Tooltip/utils.js +0 -126
- package/dist/esm/components/Axis/AxisY.js +0 -416
- package/dist/esm/components/Axis/index.d.ts +0 -2
- package/dist/esm/components/Axis/index.js +0 -2
- package/dist/esm/components/Tooltip/utils.d.ts +0 -30
- package/dist/esm/components/Tooltip/utils.js +0 -126
- /package/dist/cjs/components/{Axis → AxisX}/AxisX.d.ts +0 -0
- /package/dist/cjs/components/{Axis → AxisX}/styles.css +0 -0
- /package/dist/esm/components/{Axis → AxisX}/AxisX.d.ts +0 -0
- /package/dist/esm/components/{Axis → AxisX}/styles.css +0 -0
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { line, select } from 'd3';
|
|
3
|
-
import { block,
|
|
3
|
+
import { block, getAxisTitleRows, getBandsPosition, getLabelFormatter, getLineDashArray, getMaxTickCount, getTicksCount, handleOverflowingText, } from '../../utils';
|
|
4
4
|
import { axisBottom } from '../../utils/chart/axis-generators';
|
|
5
5
|
import './styles.css';
|
|
6
6
|
const b = block('axis');
|
|
7
|
-
function getLabelFormatter({ axis, scale }) {
|
|
8
|
-
const ticks = getScaleTicks(scale);
|
|
9
|
-
const tickStep = getClosestPointsRange(axis, ticks);
|
|
10
|
-
return (value) => {
|
|
11
|
-
if (!axis.labels.enabled) {
|
|
12
|
-
return '';
|
|
13
|
-
}
|
|
14
|
-
return formatAxisTickLabel({
|
|
15
|
-
axis,
|
|
16
|
-
value,
|
|
17
|
-
step: tickStep,
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
7
|
export function getTitlePosition(args) {
|
|
22
8
|
const { axis, width, rowCount } = args;
|
|
23
9
|
if (rowCount < 1) {
|
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AxisYData } from './types';
|
|
3
3
|
import './styles.css';
|
|
4
4
|
interface Props {
|
|
5
|
-
|
|
6
|
-
boundsOffsetTop: number;
|
|
7
|
-
boundsOffsetLeft: number;
|
|
8
|
-
scale: ChartScale[];
|
|
9
|
-
width: number;
|
|
10
|
-
height: number;
|
|
5
|
+
preparedAxisData: AxisYData;
|
|
11
6
|
htmlLayout: HTMLElement | null;
|
|
12
|
-
split: PreparedSplit;
|
|
13
7
|
plotBeforeRef?: React.MutableRefObject<SVGGElement | null>;
|
|
14
8
|
plotAfterRef?: React.MutableRefObject<SVGGElement | null>;
|
|
15
|
-
bottomLimit?: number;
|
|
16
|
-
topLimit?: number;
|
|
17
9
|
}
|
|
18
10
|
export declare const AxisY: (props: Props) => React.JSX.Element;
|
|
19
11
|
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { line, select } from 'd3';
|
|
3
|
+
import { HtmlLayer } from '../../hooks/useShapes/HtmlLayer';
|
|
4
|
+
import { block, getLineDashArray } from '../../utils';
|
|
5
|
+
import './styles.css';
|
|
6
|
+
const b = block('y-axis');
|
|
7
|
+
export const AxisY = (props) => {
|
|
8
|
+
const { htmlLayout, plotBeforeRef, plotAfterRef, preparedAxisData } = props;
|
|
9
|
+
const ref = React.useRef(null);
|
|
10
|
+
const lineGenerator = line();
|
|
11
|
+
const htmlLabels = preparedAxisData.ticks.map((d) => d.htmlLabel).filter(Boolean);
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
if (!ref.current) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const svgElement = select(ref.current);
|
|
17
|
+
svgElement.selectAll('*').remove();
|
|
18
|
+
let plotBeforeContainer = null;
|
|
19
|
+
let plotAfterContainer = null;
|
|
20
|
+
const plotDataAttr = 'data-plot-y';
|
|
21
|
+
if (plotBeforeRef === null || plotBeforeRef === void 0 ? void 0 : plotBeforeRef.current) {
|
|
22
|
+
plotBeforeContainer = select(plotBeforeRef.current);
|
|
23
|
+
plotBeforeContainer.selectAll(`[${plotDataAttr}]`).remove();
|
|
24
|
+
}
|
|
25
|
+
if (plotAfterRef === null || plotAfterRef === void 0 ? void 0 : plotAfterRef.current) {
|
|
26
|
+
plotAfterContainer = select(plotAfterRef.current);
|
|
27
|
+
plotAfterContainer.selectAll(`[${plotDataAttr}]`).remove();
|
|
28
|
+
}
|
|
29
|
+
if (preparedAxisData.title) {
|
|
30
|
+
svgElement
|
|
31
|
+
.append('g')
|
|
32
|
+
.attr('class', b('title'))
|
|
33
|
+
.append('text')
|
|
34
|
+
.attr('text-anchor', 'start')
|
|
35
|
+
.style('dominant-baseline', 'text-after-edge')
|
|
36
|
+
.style('transform', `translate(${preparedAxisData.title.x}px, ${preparedAxisData.title.y}px) rotate(${preparedAxisData.title.rotate}deg) translate(0px, ${preparedAxisData.title.offset}px)`)
|
|
37
|
+
.attr('font-size', preparedAxisData.title.style.fontSize)
|
|
38
|
+
.selectAll('tspan')
|
|
39
|
+
.data(preparedAxisData.title.content)
|
|
40
|
+
.join('tspan')
|
|
41
|
+
.html((d) => d.text)
|
|
42
|
+
.attr('x', (d) => d.x)
|
|
43
|
+
.attr('y', (d) => d.y)
|
|
44
|
+
.attr('text-anchor', 'start');
|
|
45
|
+
}
|
|
46
|
+
svgElement
|
|
47
|
+
.append('path')
|
|
48
|
+
.attr('class', b('domain'))
|
|
49
|
+
.attr('d', lineGenerator([preparedAxisData.domain.start, preparedAxisData.domain.end]))
|
|
50
|
+
.style('stroke', preparedAxisData.domain.lineColor);
|
|
51
|
+
const tickClassName = b('tick');
|
|
52
|
+
const ticks = svgElement
|
|
53
|
+
.selectAll(`.${tickClassName}`)
|
|
54
|
+
.remove()
|
|
55
|
+
.data(preparedAxisData.ticks)
|
|
56
|
+
.join('g')
|
|
57
|
+
.attr('class', tickClassName);
|
|
58
|
+
const labelClassName = b('label');
|
|
59
|
+
ticks.each(function () {
|
|
60
|
+
var _a;
|
|
61
|
+
const tickSelection = select(this);
|
|
62
|
+
const tickData = tickSelection.datum();
|
|
63
|
+
if (tickData.line) {
|
|
64
|
+
tickSelection.append('path').attr('d', lineGenerator(tickData.line.points));
|
|
65
|
+
}
|
|
66
|
+
if (tickData.svgLabel) {
|
|
67
|
+
const label = tickData.svgLabel;
|
|
68
|
+
const textSelection = tickSelection.append('text');
|
|
69
|
+
if (label.title) {
|
|
70
|
+
textSelection.append('title').html(label.title);
|
|
71
|
+
}
|
|
72
|
+
textSelection
|
|
73
|
+
.selectAll('tspan')
|
|
74
|
+
.data(label.content)
|
|
75
|
+
.join('tspan')
|
|
76
|
+
.html((d) => d.text)
|
|
77
|
+
.attr('x', (d) => d.x)
|
|
78
|
+
.attr('y', (d) => d.y)
|
|
79
|
+
.attr('text-anchor', 'start')
|
|
80
|
+
.attr('class', labelClassName)
|
|
81
|
+
.style('dominant-baseline', 'text-before-edge')
|
|
82
|
+
.style('font-size', label.style.fontSize)
|
|
83
|
+
.style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '');
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
if (preparedAxisData.plotBands.length > 0) {
|
|
87
|
+
const plotBandDataAttr = `data-plot-y-band-${preparedAxisData.id}`;
|
|
88
|
+
const setPlotBands = (plotContainer, plotBands) => {
|
|
89
|
+
if (!plotContainer || !plotBands.length) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const plotBandsSelection = plotContainer
|
|
93
|
+
.selectAll(`[${plotBandDataAttr}]`)
|
|
94
|
+
.remove()
|
|
95
|
+
.data(plotBands)
|
|
96
|
+
.join('g')
|
|
97
|
+
.attr(plotDataAttr, 1)
|
|
98
|
+
.attr(plotBandDataAttr, 1)
|
|
99
|
+
.style('transform', (d) => `translate(${d.x}px, ${d.y}px)`);
|
|
100
|
+
plotBandsSelection
|
|
101
|
+
.append('rect')
|
|
102
|
+
.attr('width', (d) => d.width)
|
|
103
|
+
.attr('height', (d) => d.height)
|
|
104
|
+
.attr('fill', (d) => d.color)
|
|
105
|
+
.attr('opacity', (d) => d.opacity);
|
|
106
|
+
plotBandsSelection.each(function () {
|
|
107
|
+
var _a, _b;
|
|
108
|
+
const plotBandSelection = select(this);
|
|
109
|
+
const band = plotBandSelection.datum();
|
|
110
|
+
const label = band.label;
|
|
111
|
+
if (label) {
|
|
112
|
+
plotBandSelection
|
|
113
|
+
.append('text')
|
|
114
|
+
.html(label.text)
|
|
115
|
+
.style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '')
|
|
116
|
+
.style('font-size', label.style.fontSize)
|
|
117
|
+
.style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
|
|
118
|
+
.style('dominant-baseline', 'text-before-edge')
|
|
119
|
+
.attr('x', label.x)
|
|
120
|
+
.attr('y', label.y);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
setPlotBands(plotBeforeContainer, preparedAxisData.plotBands.filter((item) => item.layerPlacement === 'before'));
|
|
125
|
+
setPlotBands(plotAfterContainer, preparedAxisData.plotBands.filter((item) => item.layerPlacement === 'after'));
|
|
126
|
+
}
|
|
127
|
+
if (preparedAxisData.plotLines.length > 0) {
|
|
128
|
+
const plotLineDataAttr = `data-plot-y-line-${preparedAxisData.id}`;
|
|
129
|
+
const setPlotLines = (plotContainer, plotLines) => {
|
|
130
|
+
if (!plotContainer || !plotLines.length) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const plotLinesSelection = plotContainer
|
|
134
|
+
.selectAll(`[${plotLineDataAttr}]`)
|
|
135
|
+
.remove()
|
|
136
|
+
.data(plotLines)
|
|
137
|
+
.join('g')
|
|
138
|
+
.attr(plotDataAttr, 1)
|
|
139
|
+
.attr(plotLineDataAttr, 1)
|
|
140
|
+
.style('transform', (d) => `translate(${d.x}px, ${d.y}px)`);
|
|
141
|
+
plotLinesSelection
|
|
142
|
+
.append('path')
|
|
143
|
+
.attr('d', (d) => lineGenerator(d.points))
|
|
144
|
+
.attr('stroke', (d) => d.color)
|
|
145
|
+
.attr('stroke-width', (d) => d.lineWidth)
|
|
146
|
+
.attr('stroke-dasharray', (d) => getLineDashArray(d.dashStyle, d.lineWidth))
|
|
147
|
+
.attr('opacity', (d) => d.opacity);
|
|
148
|
+
plotLinesSelection.each(function () {
|
|
149
|
+
var _a, _b;
|
|
150
|
+
const itemSelection = select(this);
|
|
151
|
+
const plotLine = itemSelection.datum();
|
|
152
|
+
const label = plotLine.label;
|
|
153
|
+
if (label) {
|
|
154
|
+
itemSelection
|
|
155
|
+
.append('text')
|
|
156
|
+
.text(label.text)
|
|
157
|
+
.style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '')
|
|
158
|
+
.style('font-size', label.style.fontSize)
|
|
159
|
+
.style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
|
|
160
|
+
.style('dominant-baseline', 'text-before-edge')
|
|
161
|
+
.attr('x', label.x)
|
|
162
|
+
.attr('y', label.y);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
};
|
|
166
|
+
setPlotLines(plotBeforeContainer, preparedAxisData.plotLines.filter((item) => item.layerPlacement === 'before'));
|
|
167
|
+
setPlotLines(plotAfterContainer, preparedAxisData.plotLines.filter((item) => item.layerPlacement === 'after'));
|
|
168
|
+
}
|
|
169
|
+
}, [lineGenerator, plotAfterRef, plotBeforeRef, preparedAxisData]);
|
|
170
|
+
return (React.createElement(React.Fragment, null,
|
|
171
|
+
React.createElement(HtmlLayer, { preparedData: { htmlElements: htmlLabels }, htmlLayout: htmlLayout }),
|
|
172
|
+
React.createElement("g", { ref: ref, className: b() })));
|
|
173
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ChartScale, PreparedAxis, PreparedSplit } from '../../hooks';
|
|
2
|
+
import type { AxisYData } from './types';
|
|
3
|
+
export declare function prepareAxisData({ axis, split, scale, width, height, }: {
|
|
4
|
+
axis: PreparedAxis;
|
|
5
|
+
split: PreparedSplit;
|
|
6
|
+
scale: ChartScale;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
}): Promise<AxisYData>;
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { getUniqId } from '@gravity-ui/uikit';
|
|
2
|
+
import { calculateCos, calculateSin, getBandsPosition, getLabelFormatter, getLabelsSize, getTextSizeFn, getTextWithElipsis, wrapText, } from '../../utils';
|
|
3
|
+
import { getTickValues } from './utils';
|
|
4
|
+
async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHeight, }) {
|
|
5
|
+
var _a;
|
|
6
|
+
const originalTextSize = await getTextSize(text);
|
|
7
|
+
// Currently, a preliminary label calculation is used to build the chart - we cannot exceed it here.
|
|
8
|
+
// Therefore, we rely on a pre-calculated number instead of the current maximum label width.
|
|
9
|
+
const labelMaxWidth = axis.labels.width; //axis.labels.maxWidth;
|
|
10
|
+
const size = originalTextSize;
|
|
11
|
+
const content = [];
|
|
12
|
+
// Warp label text only for categories - it will look strange for numbers or dates.
|
|
13
|
+
if (originalTextSize.width > labelMaxWidth && axis.type === 'category') {
|
|
14
|
+
const textRows = await wrapText({
|
|
15
|
+
text,
|
|
16
|
+
style: axis.labels.style,
|
|
17
|
+
width: labelMaxWidth,
|
|
18
|
+
getTextSize,
|
|
19
|
+
});
|
|
20
|
+
let topOffset = top;
|
|
21
|
+
let newLabelWidth = 0;
|
|
22
|
+
let newLabelHeight = 0;
|
|
23
|
+
for (let textRowIndex = 0; textRowIndex < textRows.length; textRowIndex++) {
|
|
24
|
+
const textRow = textRows[textRowIndex];
|
|
25
|
+
let textSize = await getTextSize(textRow.text);
|
|
26
|
+
if (newLabelHeight + textSize.height <= labelMaxHeight) {
|
|
27
|
+
newLabelWidth = Math.max(newLabelWidth, textSize.width);
|
|
28
|
+
newLabelHeight += textSize.height;
|
|
29
|
+
let rowText = textRow.text.trim();
|
|
30
|
+
if (textRowIndex < textRows.length - 1) {
|
|
31
|
+
const nextTextRow = textRows[textRowIndex + 1];
|
|
32
|
+
if (newLabelHeight + (await getTextSize(nextTextRow.text)).height >
|
|
33
|
+
labelMaxHeight) {
|
|
34
|
+
rowText = textRow.text + nextTextRow.text;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
textSize = await getTextSize(rowText);
|
|
38
|
+
if (textSize.width > labelMaxWidth) {
|
|
39
|
+
rowText = await getTextWithElipsis({
|
|
40
|
+
text: rowText,
|
|
41
|
+
getTextWidth: async (str) => (await getTextSize(str)).width,
|
|
42
|
+
maxWidth: labelMaxWidth,
|
|
43
|
+
});
|
|
44
|
+
textSize = await getTextSize(rowText);
|
|
45
|
+
}
|
|
46
|
+
const x = axis.position === 'left'
|
|
47
|
+
? left - textSize.width - axis.labels.margin
|
|
48
|
+
: left + axis.labels.margin;
|
|
49
|
+
content.push({
|
|
50
|
+
text: rowText,
|
|
51
|
+
x,
|
|
52
|
+
y: topOffset,
|
|
53
|
+
size: textSize,
|
|
54
|
+
});
|
|
55
|
+
topOffset += textSize.height;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
content.forEach((row) => {
|
|
59
|
+
row.y -= newLabelHeight / 2;
|
|
60
|
+
});
|
|
61
|
+
size.width = newLabelWidth;
|
|
62
|
+
size.height = newLabelHeight;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
const x = axis.position === 'left'
|
|
66
|
+
? left - size.width - axis.labels.margin
|
|
67
|
+
: left + axis.labels.margin;
|
|
68
|
+
content.push({
|
|
69
|
+
text,
|
|
70
|
+
x,
|
|
71
|
+
y: Math.max(0, top - size.height / 2),
|
|
72
|
+
size,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const svgLabel = {
|
|
76
|
+
title: content.length > 1 || ((_a = content[0]) === null || _a === void 0 ? void 0 : _a.text) !== text ? text : undefined,
|
|
77
|
+
content: content,
|
|
78
|
+
style: axis.labels.style,
|
|
79
|
+
size: size,
|
|
80
|
+
};
|
|
81
|
+
return svgLabel;
|
|
82
|
+
}
|
|
83
|
+
// eslint-disable-next-line complexity
|
|
84
|
+
export async function prepareAxisData({ axis, split, scale, width, height, }) {
|
|
85
|
+
var _a, _b, _c;
|
|
86
|
+
const axisPlotTopPosition = ((_a = split.plots[axis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
87
|
+
const axisHeight = ((_b = split.plots[axis.plotIndex]) === null || _b === void 0 ? void 0 : _b.height) || height;
|
|
88
|
+
const domainX = axis.position === 'left' ? 0 : width;
|
|
89
|
+
const domain = {
|
|
90
|
+
start: [domainX, axisPlotTopPosition],
|
|
91
|
+
end: [domainX, axisPlotTopPosition + axisHeight],
|
|
92
|
+
lineColor: (_c = axis.lineColor) !== null && _c !== void 0 ? _c : '',
|
|
93
|
+
};
|
|
94
|
+
const ticks = [];
|
|
95
|
+
const getTextSize = getTextSizeFn({ style: axis.labels.style });
|
|
96
|
+
const labelLineHeight = (await getTextSize('Tmp')).height;
|
|
97
|
+
const values = getTickValues({ scale, axis, labelLineHeight });
|
|
98
|
+
const labelMaxHeight = values.length > 1 ? values[0].y - values[1].y - axis.labels.padding * 2 : axisHeight;
|
|
99
|
+
const labelFormatter = getLabelFormatter({ axis, scale });
|
|
100
|
+
for (let i = 0; i < values.length; i++) {
|
|
101
|
+
const tickValue = values[i];
|
|
102
|
+
const y = axisPlotTopPosition + tickValue.y;
|
|
103
|
+
let svgLabel = null;
|
|
104
|
+
let htmlLabel = null;
|
|
105
|
+
if (axis.labels.enabled) {
|
|
106
|
+
if (axis.labels.html) {
|
|
107
|
+
const content = String(tickValue.value);
|
|
108
|
+
const labelSize = await getLabelsSize({
|
|
109
|
+
labels: [content],
|
|
110
|
+
html: true,
|
|
111
|
+
style: axis.labels.style,
|
|
112
|
+
});
|
|
113
|
+
const size = { width: labelSize.maxWidth, height: labelSize.maxHeight };
|
|
114
|
+
const left = domainX;
|
|
115
|
+
const top = y;
|
|
116
|
+
const x = axis.position === 'left'
|
|
117
|
+
? left - size.width - axis.labels.margin
|
|
118
|
+
: left + axis.labels.margin;
|
|
119
|
+
htmlLabel = {
|
|
120
|
+
content,
|
|
121
|
+
x,
|
|
122
|
+
y: top - size.height / 2,
|
|
123
|
+
size,
|
|
124
|
+
style: axis.labels.style,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const text = labelFormatter(tickValue.value);
|
|
129
|
+
svgLabel = await getSvgAxisLabel({
|
|
130
|
+
getTextSize,
|
|
131
|
+
text,
|
|
132
|
+
axis,
|
|
133
|
+
top: y,
|
|
134
|
+
left: domainX,
|
|
135
|
+
labelMaxHeight,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const tickLine = axis.grid.enabled
|
|
140
|
+
? {
|
|
141
|
+
points: [
|
|
142
|
+
[0, y],
|
|
143
|
+
[width, y],
|
|
144
|
+
],
|
|
145
|
+
}
|
|
146
|
+
: null;
|
|
147
|
+
ticks.push({
|
|
148
|
+
line: tickLine,
|
|
149
|
+
svgLabel,
|
|
150
|
+
htmlLabel,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
let labelsWidth = ticks.reduce((acc, item) => { var _a, _b, _c, _d; return Math.max(acc, (_d = (_b = (_a = item.svgLabel) === null || _a === void 0 ? void 0 : _a.size.width) !== null && _b !== void 0 ? _b : (_c = item.htmlLabel) === null || _c === void 0 ? void 0 : _c.size.width) !== null && _d !== void 0 ? _d : 0); }, 0);
|
|
154
|
+
labelsWidth = Math.min(axis.labels.width, labelsWidth);
|
|
155
|
+
let title = null;
|
|
156
|
+
if (axis.title.text) {
|
|
157
|
+
const getTitleTextSize = getTextSizeFn({ style: axis.title.style });
|
|
158
|
+
const rotateAngle = axis.position === 'left' ? -90 : 90;
|
|
159
|
+
const sin = Math.abs(calculateSin(rotateAngle));
|
|
160
|
+
const cos = Math.abs(calculateCos(rotateAngle));
|
|
161
|
+
const titleContent = [];
|
|
162
|
+
const titleMaxWidth = sin * axisHeight;
|
|
163
|
+
if (axis.title.maxRowCount > 1) {
|
|
164
|
+
const titleTextRows = await wrapText({
|
|
165
|
+
text: axis.title.text,
|
|
166
|
+
style: axis.title.style,
|
|
167
|
+
width: titleMaxWidth,
|
|
168
|
+
getTextSize: getTitleTextSize,
|
|
169
|
+
});
|
|
170
|
+
for (let i = 0; i < axis.title.maxRowCount && i < titleTextRows.length; i++) {
|
|
171
|
+
const textRow = titleTextRows[i];
|
|
172
|
+
const textRowContent = textRow.text.trim();
|
|
173
|
+
const textRowSize = await getTitleTextSize(textRowContent);
|
|
174
|
+
titleContent.push({
|
|
175
|
+
text: textRowContent,
|
|
176
|
+
x: 0,
|
|
177
|
+
y: textRow.y,
|
|
178
|
+
size: textRowSize,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const text = await getTextWithElipsis({
|
|
184
|
+
text: axis.title.text,
|
|
185
|
+
maxWidth: titleMaxWidth,
|
|
186
|
+
getTextWidth: async (s) => (await getTitleTextSize(s)).width,
|
|
187
|
+
});
|
|
188
|
+
titleContent.push({
|
|
189
|
+
text,
|
|
190
|
+
x: 0,
|
|
191
|
+
y: 0,
|
|
192
|
+
size: await getTitleTextSize(text),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const originalTextSize = titleContent.reduce((acc, item) => {
|
|
196
|
+
acc.width = Math.max(acc.width, item.size.width);
|
|
197
|
+
acc.height += item.size.height;
|
|
198
|
+
return acc;
|
|
199
|
+
}, { width: 0, height: 0 });
|
|
200
|
+
const rotatedTitleSize = {
|
|
201
|
+
width: sin * originalTextSize.height + cos * originalTextSize.width,
|
|
202
|
+
height: sin * originalTextSize.width + cos * originalTextSize.height,
|
|
203
|
+
};
|
|
204
|
+
const bottom = Math.max(0, calculateSin(rotateAngle) * originalTextSize.width);
|
|
205
|
+
let y = 0;
|
|
206
|
+
switch (axis.title.align) {
|
|
207
|
+
case 'left': {
|
|
208
|
+
y = -bottom + axisHeight;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case 'center': {
|
|
212
|
+
y = -bottom + axisHeight / 2 + rotatedTitleSize.height / 2;
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case 'right': {
|
|
216
|
+
y = -bottom + rotatedTitleSize.height;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const left = Math.min(0, calculateCos(rotateAngle) * originalTextSize.width);
|
|
221
|
+
const x = axis.position === 'left'
|
|
222
|
+
? -left - labelsWidth - axis.labels.margin - axis.title.margin
|
|
223
|
+
: -left + width + labelsWidth + axis.labels.margin + axis.title.margin;
|
|
224
|
+
title = {
|
|
225
|
+
content: titleContent,
|
|
226
|
+
style: axis.title.style,
|
|
227
|
+
size: rotatedTitleSize,
|
|
228
|
+
x: x,
|
|
229
|
+
y: axisPlotTopPosition + y,
|
|
230
|
+
rotate: rotateAngle,
|
|
231
|
+
offset: -(originalTextSize.height / titleContent.length) * (titleContent.length - 1),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const plotBands = [];
|
|
235
|
+
axis.plotBands.forEach((plotBand) => {
|
|
236
|
+
var _a, _b;
|
|
237
|
+
const axisScale = scale;
|
|
238
|
+
const { from, to } = getBandsPosition({
|
|
239
|
+
band: plotBand,
|
|
240
|
+
axisScale,
|
|
241
|
+
axis: 'y',
|
|
242
|
+
});
|
|
243
|
+
const halfBandwidth = ((_b = (_a = axisScale.bandwidth) === null || _a === void 0 ? void 0 : _a.call(axisScale)) !== null && _b !== void 0 ? _b : 0) / 2;
|
|
244
|
+
const startPos = halfBandwidth + Math.min(from, to);
|
|
245
|
+
const endPos = Math.min(Math.abs(to - from), axisHeight - Math.min(from, to));
|
|
246
|
+
const top = Math.max(0, startPos);
|
|
247
|
+
plotBands.push({
|
|
248
|
+
layerPlacement: plotBand.layerPlacement,
|
|
249
|
+
x: 0,
|
|
250
|
+
y: axisPlotTopPosition + top,
|
|
251
|
+
width,
|
|
252
|
+
height: Math.min(endPos, axisHeight),
|
|
253
|
+
color: plotBand.color,
|
|
254
|
+
opacity: plotBand.opacity,
|
|
255
|
+
label: plotBand.label.text
|
|
256
|
+
? {
|
|
257
|
+
text: plotBand.label.text,
|
|
258
|
+
style: plotBand.label.style,
|
|
259
|
+
x: plotBand.label.padding,
|
|
260
|
+
y: plotBand.label.padding,
|
|
261
|
+
}
|
|
262
|
+
: null,
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
const plotLines = [];
|
|
266
|
+
for (let i = 0; i < axis.plotLines.length; i++) {
|
|
267
|
+
const plotLine = axis.plotLines[i];
|
|
268
|
+
const axisScale = scale;
|
|
269
|
+
const plotLineValue = Number(axisScale(plotLine.value));
|
|
270
|
+
const points = [
|
|
271
|
+
[0, plotLineValue],
|
|
272
|
+
[width, plotLineValue],
|
|
273
|
+
];
|
|
274
|
+
let label = null;
|
|
275
|
+
if (plotLine.label.text) {
|
|
276
|
+
const getTitleTextSize = getTextSizeFn({ style: plotLine.label.style });
|
|
277
|
+
const size = await getTitleTextSize(plotLine.label.text);
|
|
278
|
+
label = {
|
|
279
|
+
text: plotLine.label.text,
|
|
280
|
+
style: plotLine.label.style,
|
|
281
|
+
x: plotLine.label.padding,
|
|
282
|
+
y: Math.max(0, plotLineValue - size.height - plotLine.label.padding),
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
plotLines.push({
|
|
286
|
+
layerPlacement: plotLine.layerPlacement,
|
|
287
|
+
x: 0,
|
|
288
|
+
y: axisPlotTopPosition,
|
|
289
|
+
width,
|
|
290
|
+
color: plotLine.color,
|
|
291
|
+
opacity: plotLine.opacity,
|
|
292
|
+
label,
|
|
293
|
+
points,
|
|
294
|
+
lineWidth: plotLine.width,
|
|
295
|
+
dashStyle: plotLine.dashStyle,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
id: getUniqId(),
|
|
300
|
+
title,
|
|
301
|
+
ticks,
|
|
302
|
+
domain,
|
|
303
|
+
plotBands,
|
|
304
|
+
plotLines,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.gcharts-y-axis__domain {
|
|
2
|
+
stroke: var(--g-color-line-generic-active);
|
|
3
|
+
}
|
|
4
|
+
.gcharts-y-axis__label {
|
|
5
|
+
fill: var(--g-color-text-secondary);
|
|
6
|
+
stroke: none;
|
|
7
|
+
dominant-baseline: text-after-edge;
|
|
8
|
+
}
|
|
9
|
+
.gcharts-y-axis__tick {
|
|
10
|
+
stroke: var(--g-color-line-generic);
|
|
11
|
+
}
|
|
12
|
+
.gcharts-y-axis__title {
|
|
13
|
+
dominant-baseline: text-after-edge;
|
|
14
|
+
fill: var(--g-color-text-secondary);
|
|
15
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { DashStyle } from 'src/constants';
|
|
2
|
+
import type { BaseTextStyle, HtmlItem, PlotLayerPlacement } from '../../types';
|
|
3
|
+
export type TextRowData = {
|
|
4
|
+
text: string;
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
size: {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export type AxisSvgLabelData = {
|
|
13
|
+
content: TextRowData[];
|
|
14
|
+
title?: string;
|
|
15
|
+
style: BaseTextStyle;
|
|
16
|
+
size: {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
export type AxisTickLine = {
|
|
22
|
+
points: [number, number][];
|
|
23
|
+
};
|
|
24
|
+
export type AxisTickData = {
|
|
25
|
+
line: AxisTickLine | null;
|
|
26
|
+
svgLabel: AxisSvgLabelData | null;
|
|
27
|
+
htmlLabel: HtmlItem | null;
|
|
28
|
+
};
|
|
29
|
+
export type AxisTitleData = {
|
|
30
|
+
content: TextRowData[];
|
|
31
|
+
style: BaseTextStyle;
|
|
32
|
+
size: {
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
};
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
rotate: number;
|
|
39
|
+
offset: number;
|
|
40
|
+
};
|
|
41
|
+
export type AxisPlotLineLabel = {
|
|
42
|
+
text: string;
|
|
43
|
+
style: BaseTextStyle;
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
};
|
|
47
|
+
export type AxisPlotLineData = {
|
|
48
|
+
layerPlacement: PlotLayerPlacement;
|
|
49
|
+
x: number;
|
|
50
|
+
y: number;
|
|
51
|
+
width: number;
|
|
52
|
+
points: [number, number][];
|
|
53
|
+
color: string;
|
|
54
|
+
lineWidth: number;
|
|
55
|
+
opacity: number;
|
|
56
|
+
label: AxisPlotLineLabel | null;
|
|
57
|
+
dashStyle: DashStyle;
|
|
58
|
+
};
|
|
59
|
+
export type AxisPlotBandData = {
|
|
60
|
+
layerPlacement: PlotLayerPlacement;
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
width: number;
|
|
64
|
+
height: number;
|
|
65
|
+
color: string;
|
|
66
|
+
opacity: number;
|
|
67
|
+
label: AxisPlotLineLabel | null;
|
|
68
|
+
};
|
|
69
|
+
export type AxisDomainData = {
|
|
70
|
+
start: [number, number];
|
|
71
|
+
end: [number, number];
|
|
72
|
+
lineColor: string;
|
|
73
|
+
};
|
|
74
|
+
export type AxisYData = {
|
|
75
|
+
id: string;
|
|
76
|
+
title: AxisTitleData | null;
|
|
77
|
+
domain: AxisDomainData;
|
|
78
|
+
ticks: AxisTickData[];
|
|
79
|
+
plotLines: AxisPlotLineData[];
|
|
80
|
+
plotBands: AxisPlotBandData[];
|
|
81
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ChartScale, PreparedAxis } from '../../hooks';
|
|
2
|
+
export declare function getTickValues({ scale, axis, labelLineHeight, }: {
|
|
3
|
+
scale: ChartScale;
|
|
4
|
+
axis: PreparedAxis;
|
|
5
|
+
labelLineHeight: number;
|
|
6
|
+
}): {
|
|
7
|
+
y: number;
|
|
8
|
+
value: number | Date;
|
|
9
|
+
}[] | {
|
|
10
|
+
y: number;
|
|
11
|
+
value: string;
|
|
12
|
+
}[];
|