@gravity-ui/charts 1.13.2 → 1.14.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/cjs/components/Axis/AxisX.d.ts +5 -2
- package/dist/cjs/components/Axis/AxisX.js +28 -11
- package/dist/cjs/components/Axis/AxisY.d.ts +5 -2
- package/dist/cjs/components/Axis/AxisY.js +67 -9
- package/dist/cjs/components/ChartInner/index.js +2 -2
- package/dist/cjs/components/Legend/index.js +5 -4
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.js +3 -3
- package/dist/cjs/hooks/useChartOptions/title.js +2 -2
- package/dist/cjs/hooks/useChartOptions/types.d.ts +1 -1
- package/dist/cjs/hooks/useChartOptions/x-axis.js +18 -10
- package/dist/cjs/hooks/useChartOptions/y-axis.js +17 -6
- package/dist/cjs/hooks/useSplit/index.js +2 -2
- package/dist/cjs/i18n/keysets/en.json +3 -1
- package/dist/cjs/i18n/keysets/ru.json +3 -1
- package/dist/cjs/types/chart/axis.d.ts +11 -1
- package/dist/cjs/utils/chart/axis-generators/bottom.d.ts +18 -13
- package/dist/cjs/utils/chart/axis-generators/bottom.js +173 -73
- package/dist/cjs/utils/chart/index.d.ts +5 -9
- package/dist/cjs/utils/chart/index.js +18 -9
- package/dist/cjs/validation/index.js +2 -22
- package/dist/cjs/validation/validate-axes.d.ts +5 -0
- package/dist/cjs/validation/validate-axes.js +46 -0
- package/dist/esm/components/Axis/AxisX.d.ts +5 -2
- package/dist/esm/components/Axis/AxisX.js +28 -11
- package/dist/esm/components/Axis/AxisY.d.ts +5 -2
- package/dist/esm/components/Axis/AxisY.js +67 -9
- package/dist/esm/components/ChartInner/index.js +2 -2
- package/dist/esm/components/Legend/index.js +5 -4
- package/dist/esm/components/Tooltip/DefaultTooltipContent/index.js +3 -3
- package/dist/esm/hooks/useChartOptions/title.js +2 -2
- package/dist/esm/hooks/useChartOptions/types.d.ts +1 -1
- package/dist/esm/hooks/useChartOptions/x-axis.js +18 -10
- package/dist/esm/hooks/useChartOptions/y-axis.js +17 -6
- package/dist/esm/hooks/useSplit/index.js +2 -2
- package/dist/esm/i18n/keysets/en.json +3 -1
- package/dist/esm/i18n/keysets/ru.json +3 -1
- package/dist/esm/types/chart/axis.d.ts +11 -1
- package/dist/esm/utils/chart/axis-generators/bottom.d.ts +18 -13
- package/dist/esm/utils/chart/axis-generators/bottom.js +173 -73
- package/dist/esm/utils/chart/index.d.ts +5 -9
- package/dist/esm/utils/chart/index.js +18 -9
- package/dist/esm/validation/index.js +2 -22
- package/dist/esm/validation/validate-axes.d.ts +5 -0
- package/dist/esm/validation/validate-axes.js +46 -0
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import { path, select } from 'd3';
|
|
|
2
2
|
import { getXAxisItems, getXAxisOffset, getXTickPosition } from '../axis';
|
|
3
3
|
import { calculateCos, calculateSin } from '../math';
|
|
4
4
|
import { getLabelsSize, setEllipsisForOverflowText } from '../text';
|
|
5
|
+
const AXIS_BOTTOM_HTML_LABELS_DATA_ATTR = 'data-axis-bottom-html-labels';
|
|
5
6
|
function addDomain(selection, options) {
|
|
6
7
|
const { size, color } = options;
|
|
7
8
|
const domainPath = selection
|
|
@@ -15,8 +16,139 @@ function addDomain(selection, options) {
|
|
|
15
16
|
domainPath.style('stroke', color);
|
|
16
17
|
}
|
|
17
18
|
}
|
|
19
|
+
function appendSvgLabels(args) {
|
|
20
|
+
var _a;
|
|
21
|
+
const { leftmostLimit, right, ticksSelection, ticks, transform, translateY, x } = args;
|
|
22
|
+
ticksSelection
|
|
23
|
+
.append('text')
|
|
24
|
+
.html(ticks.labelFormat)
|
|
25
|
+
.style('font-size', ((_a = ticks.labelsStyle) === null || _a === void 0 ? void 0 : _a.fontSize) || '')
|
|
26
|
+
.attr('fill', 'currentColor')
|
|
27
|
+
.attr('text-anchor', () => {
|
|
28
|
+
if (ticks.rotation) {
|
|
29
|
+
return ticks.rotation > 0 ? 'start' : 'end';
|
|
30
|
+
}
|
|
31
|
+
return 'middle';
|
|
32
|
+
})
|
|
33
|
+
.style('transform', transform)
|
|
34
|
+
.style('dominant-baseline', 'text-after-edge');
|
|
35
|
+
const labels = ticksSelection.selectAll('.tick text');
|
|
36
|
+
// FIXME: handle rotated overlapping labels (with a smarter approach)
|
|
37
|
+
if (ticks.rotation) {
|
|
38
|
+
const maxWidth = ticks.labelsMaxWidth * calculateCos(ticks.rotation) +
|
|
39
|
+
ticks.labelsLineHeight * calculateSin(ticks.rotation);
|
|
40
|
+
labels.each(function () {
|
|
41
|
+
setEllipsisForOverflowText(select(this), maxWidth);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
let elementX = 0;
|
|
46
|
+
// add an ellipsis to the labels that go beyond the boundaries of the chart
|
|
47
|
+
// and remove overlapping labels
|
|
48
|
+
labels
|
|
49
|
+
.nodes()
|
|
50
|
+
.map((element) => {
|
|
51
|
+
const r = element.getBoundingClientRect();
|
|
52
|
+
return {
|
|
53
|
+
left: r.left,
|
|
54
|
+
right: r.right,
|
|
55
|
+
node: element,
|
|
56
|
+
};
|
|
57
|
+
}, {})
|
|
58
|
+
.sort((a, b) => {
|
|
59
|
+
return a.left - b.left;
|
|
60
|
+
})
|
|
61
|
+
.forEach(function (item, i, nodes) {
|
|
62
|
+
var _a, _b, _c, _d;
|
|
63
|
+
const { node, left, right: currentElementPositionRigth } = item;
|
|
64
|
+
const currentElement = node;
|
|
65
|
+
if (i === 0) {
|
|
66
|
+
const text = select(currentElement);
|
|
67
|
+
const nextElement = (_a = nodes[i + 1]) === null || _a === void 0 ? void 0 : _a.node;
|
|
68
|
+
const nextElementPosition = nextElement === null || nextElement === void 0 ? void 0 : nextElement.getBoundingClientRect();
|
|
69
|
+
if (left < leftmostLimit) {
|
|
70
|
+
const rightmostPossiblePoint = (_b = nextElementPosition === null || nextElementPosition === void 0 ? void 0 : nextElementPosition.left) !== null && _b !== void 0 ? _b : right;
|
|
71
|
+
const remainSpace = rightmostPossiblePoint -
|
|
72
|
+
currentElementPositionRigth +
|
|
73
|
+
x -
|
|
74
|
+
ticks.labelsMargin;
|
|
75
|
+
text.attr('text-anchor', 'start');
|
|
76
|
+
setEllipsisForOverflowText(text, remainSpace);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (left < elementX) {
|
|
81
|
+
(_c = currentElement.closest('.tick')) === null || _c === void 0 ? void 0 : _c.remove();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
elementX = currentElementPositionRigth + ticks.labelsPaddings;
|
|
85
|
+
if (i === nodes.length - 1) {
|
|
86
|
+
const prevElement = (_d = nodes[i - 1]) === null || _d === void 0 ? void 0 : _d.node;
|
|
87
|
+
const text = select(currentElement);
|
|
88
|
+
const prevElementPosition = prevElement === null || prevElement === void 0 ? void 0 : prevElement.getBoundingClientRect();
|
|
89
|
+
const lackingSpace = Math.max(0, currentElementPositionRigth - right);
|
|
90
|
+
if (lackingSpace) {
|
|
91
|
+
const remainSpace = right - ((prevElementPosition === null || prevElementPosition === void 0 ? void 0 : prevElementPosition.right) || 0) - ticks.labelsPaddings;
|
|
92
|
+
const translateX = -lackingSpace;
|
|
93
|
+
text.style('transform', `translate(${translateX}px,${translateY}px)`);
|
|
94
|
+
setEllipsisForOverflowText(text, remainSpace);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function appendHtmlLabels(args) {
|
|
102
|
+
const { htmlSelection, labelsData, right, ticks } = args;
|
|
103
|
+
htmlSelection
|
|
104
|
+
.append('div')
|
|
105
|
+
.attr(AXIS_BOTTOM_HTML_LABELS_DATA_ATTR, 1)
|
|
106
|
+
.style('position', 'absolute');
|
|
107
|
+
labelsData.forEach((label) => {
|
|
108
|
+
var _a;
|
|
109
|
+
htmlSelection
|
|
110
|
+
.selectAll(`[${AXIS_BOTTOM_HTML_LABELS_DATA_ATTR}]`)
|
|
111
|
+
.data([label])
|
|
112
|
+
.append('div')
|
|
113
|
+
.html(function (d) {
|
|
114
|
+
return ticks.labelFormat(d.content);
|
|
115
|
+
})
|
|
116
|
+
.style('font-size', ((_a = ticks.labelsStyle) === null || _a === void 0 ? void 0 : _a.fontSize) || '')
|
|
117
|
+
.style('position', 'absolute')
|
|
118
|
+
.style('white-space', 'nowrap')
|
|
119
|
+
.style('color', 'var(--g-color-text-secondary)')
|
|
120
|
+
.style('overflow', 'hidden')
|
|
121
|
+
.style('text-overflow', 'ellipsis')
|
|
122
|
+
.style('left', function (d) {
|
|
123
|
+
const rect = this.getBoundingClientRect();
|
|
124
|
+
return `${d.left - rect.width / 2}px`;
|
|
125
|
+
})
|
|
126
|
+
.style('top', function (d) {
|
|
127
|
+
return `${d.top}px`;
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
const labelNodes = htmlSelection
|
|
131
|
+
.selectAll(`[${AXIS_BOTTOM_HTML_LABELS_DATA_ATTR}] > div`)
|
|
132
|
+
.nodes();
|
|
133
|
+
labelNodes.forEach((node, i, nodes) => {
|
|
134
|
+
var _a;
|
|
135
|
+
if (i === nodes.length - 1) {
|
|
136
|
+
const prevNodeSelection = select(nodes[i - 1]);
|
|
137
|
+
const prevRect = (_a = prevNodeSelection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
138
|
+
const nodeSelection = select(node);
|
|
139
|
+
const rect = node.getBoundingClientRect();
|
|
140
|
+
if (prevRect && prevRect.right + ticks.labelsPaddings > rect.left) {
|
|
141
|
+
const maxWidth = right - prevRect.right - ticks.labelsPaddings;
|
|
142
|
+
const leftMin = prevRect.right - ticks.labelsPaddings / 2;
|
|
143
|
+
nodeSelection.style('left', `${leftMin}px`);
|
|
144
|
+
nodeSelection.style('max-width', `${maxWidth}px`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
18
149
|
export async function axisBottom(args) {
|
|
19
|
-
const { leftmostLimit = 0, scale, ticks: { labelFormat = (value) => String(value),
|
|
150
|
+
const { boundsOffsetLeft = 0, boundsOffsetTop = 0, domain, htmlLayout, leftmostLimit = 0, scale, ticks: { count: ticksCount, items: tickItems, labelFormat = (value) => String(value), labelsHeight = 0, labelsHtml, labelsLineHeight, labelsMargin = 0, labelsMaxWidth = Infinity, labelsPaddings = 0, labelsStyle, maxTickCount, rotation = 0, tickColor, }, } = args;
|
|
151
|
+
const htmlSelection = select(htmlLayout);
|
|
20
152
|
const offset = getXAxisOffset();
|
|
21
153
|
const position = getXTickPosition({ scale, offset });
|
|
22
154
|
const values = getXAxisItems({ scale, count: ticksCount, maxCount: maxTickCount });
|
|
@@ -27,6 +159,7 @@ export async function axisBottom(args) {
|
|
|
27
159
|
return function (selection) {
|
|
28
160
|
var _a, _b, _c;
|
|
29
161
|
selection.selectAll('.tick, .domain').remove();
|
|
162
|
+
htmlSelection.selectAll(`[${AXIS_BOTTOM_HTML_LABELS_DATA_ATTR}]`).remove();
|
|
30
163
|
const rect = (_a = selection.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
31
164
|
const x = (rect === null || rect === void 0 ? void 0 : rect.x) || 0;
|
|
32
165
|
const right = x + domain.size;
|
|
@@ -46,6 +179,16 @@ export async function axisBottom(args) {
|
|
|
46
179
|
tickPath.moveTo(0, start);
|
|
47
180
|
tickPath.lineTo(0, end);
|
|
48
181
|
});
|
|
182
|
+
const htmlLabelsData = labelsHtml
|
|
183
|
+
? values.map((v) => {
|
|
184
|
+
var _a;
|
|
185
|
+
return {
|
|
186
|
+
content: v,
|
|
187
|
+
left: position(v) + offset + boundsOffsetLeft,
|
|
188
|
+
top: Math.abs(((_a = tickItems === null || tickItems === void 0 ? void 0 : tickItems[0]) === null || _a === void 0 ? void 0 : _a[1]) || 0) + labelsMargin + boundsOffsetTop,
|
|
189
|
+
};
|
|
190
|
+
})
|
|
191
|
+
: [];
|
|
49
192
|
const ticks = selection
|
|
50
193
|
.selectAll('.tick')
|
|
51
194
|
.data(values)
|
|
@@ -60,19 +203,6 @@ export async function axisBottom(args) {
|
|
|
60
203
|
.append('path')
|
|
61
204
|
.attr('d', tickPath.toString())
|
|
62
205
|
.attr('stroke', tickColor !== null && tickColor !== void 0 ? tickColor : 'currentColor');
|
|
63
|
-
ticks
|
|
64
|
-
.append('text')
|
|
65
|
-
.text(labelFormat)
|
|
66
|
-
.style('font-size', (labelsStyle === null || labelsStyle === void 0 ? void 0 : labelsStyle.fontSize) || '')
|
|
67
|
-
.attr('fill', 'currentColor')
|
|
68
|
-
.attr('text-anchor', () => {
|
|
69
|
-
if (rotation) {
|
|
70
|
-
return rotation > 0 ? 'start' : 'end';
|
|
71
|
-
}
|
|
72
|
-
return 'middle';
|
|
73
|
-
})
|
|
74
|
-
.style('transform', transform)
|
|
75
|
-
.style('dominant-baseline', 'text-after-edge');
|
|
76
206
|
// Remove tick that has the same x coordinate like domain
|
|
77
207
|
selection
|
|
78
208
|
.selectAll('.tick')
|
|
@@ -81,68 +211,38 @@ export async function axisBottom(args) {
|
|
|
81
211
|
})
|
|
82
212
|
.select('path')
|
|
83
213
|
.remove();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
214
|
+
if (labelsHtml) {
|
|
215
|
+
appendHtmlLabels({
|
|
216
|
+
htmlSelection,
|
|
217
|
+
labelsData: htmlLabelsData,
|
|
218
|
+
right,
|
|
219
|
+
ticks: {
|
|
220
|
+
labelFormat,
|
|
221
|
+
labelsHeight,
|
|
222
|
+
labelsMaxWidth,
|
|
223
|
+
labelsPaddings,
|
|
224
|
+
labelsStyle,
|
|
225
|
+
rotation,
|
|
226
|
+
},
|
|
90
227
|
});
|
|
91
228
|
}
|
|
92
229
|
else {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
.forEach(function (item, i, nodes) {
|
|
110
|
-
var _a, _b, _c, _d;
|
|
111
|
-
const { node, left, right: currentElementPositionRigth } = item;
|
|
112
|
-
const currentElement = node;
|
|
113
|
-
if (i === 0) {
|
|
114
|
-
const text = select(currentElement);
|
|
115
|
-
const nextElement = (_a = nodes[i + 1]) === null || _a === void 0 ? void 0 : _a.node;
|
|
116
|
-
const nextElementPosition = nextElement === null || nextElement === void 0 ? void 0 : nextElement.getBoundingClientRect();
|
|
117
|
-
if (left < leftmostLimit) {
|
|
118
|
-
const rightmostPossiblePoint = (_b = nextElementPosition === null || nextElementPosition === void 0 ? void 0 : nextElementPosition.left) !== null && _b !== void 0 ? _b : right;
|
|
119
|
-
const remainSpace = rightmostPossiblePoint -
|
|
120
|
-
currentElementPositionRigth +
|
|
121
|
-
x -
|
|
122
|
-
labelsMargin;
|
|
123
|
-
text.attr('text-anchor', 'start');
|
|
124
|
-
setEllipsisForOverflowText(text, remainSpace);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
if (left < elementX) {
|
|
129
|
-
(_c = currentElement.closest('.tick')) === null || _c === void 0 ? void 0 : _c.remove();
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
elementX = currentElementPositionRigth + labelsPaddings;
|
|
133
|
-
if (i === nodes.length - 1) {
|
|
134
|
-
const prevElement = (_d = nodes[i - 1]) === null || _d === void 0 ? void 0 : _d.node;
|
|
135
|
-
const text = select(currentElement);
|
|
136
|
-
const prevElementPosition = prevElement === null || prevElement === void 0 ? void 0 : prevElement.getBoundingClientRect();
|
|
137
|
-
const lackingSpace = Math.max(0, currentElementPositionRigth - right);
|
|
138
|
-
if (lackingSpace) {
|
|
139
|
-
const remainSpace = right - ((prevElementPosition === null || prevElementPosition === void 0 ? void 0 : prevElementPosition.right) || 0) - labelsPaddings;
|
|
140
|
-
const translateX = -lackingSpace;
|
|
141
|
-
text.style('transform', `translate(${translateX}px,${translateY}px)`);
|
|
142
|
-
setEllipsisForOverflowText(text, remainSpace);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
230
|
+
appendSvgLabels({
|
|
231
|
+
leftmostLimit,
|
|
232
|
+
right,
|
|
233
|
+
ticksSelection: ticks,
|
|
234
|
+
ticks: {
|
|
235
|
+
labelFormat,
|
|
236
|
+
labelsLineHeight,
|
|
237
|
+
labelsMaxWidth,
|
|
238
|
+
labelsMargin,
|
|
239
|
+
labelsPaddings,
|
|
240
|
+
labelsStyle,
|
|
241
|
+
rotation,
|
|
242
|
+
},
|
|
243
|
+
transform,
|
|
244
|
+
translateY,
|
|
245
|
+
x,
|
|
146
246
|
});
|
|
147
247
|
}
|
|
148
248
|
const { size: domainSize, color: domainColor } = domain;
|
|
@@ -61,15 +61,11 @@ export declare const formatAxisTickLabel: (args: {
|
|
|
61
61
|
value: AxisDomain;
|
|
62
62
|
step?: number;
|
|
63
63
|
}) => string;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
* @param {Partial<BaseTextStyle>} args.style - Optional style properties for the text element.
|
|
70
|
-
* @return {number} The height of the text element.
|
|
71
|
-
*/
|
|
72
|
-
export declare const getHorisontalSvgTextHeight: (args: {
|
|
64
|
+
export declare const getHorizontalHtmlTextHeight: (args: {
|
|
65
|
+
text: string;
|
|
66
|
+
style?: Partial<BaseTextStyle>;
|
|
67
|
+
}) => number;
|
|
68
|
+
export declare const getHorizontalSvgTextHeight: (args: {
|
|
73
69
|
text: string;
|
|
74
70
|
style?: Partial<BaseTextStyle>;
|
|
75
71
|
}) => number;
|
|
@@ -190,15 +190,24 @@ export const formatAxisTickLabel = (args) => {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
};
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
193
|
+
export const getHorizontalHtmlTextHeight = (args) => {
|
|
194
|
+
var _a;
|
|
195
|
+
const { text, style } = args;
|
|
196
|
+
const container = select(document.body).append('div');
|
|
197
|
+
const fontSize = get(style, 'fontSize', DEFAULT_AXIS_LABEL_FONT_SIZE);
|
|
198
|
+
container
|
|
199
|
+
.style('position', 'absolute')
|
|
200
|
+
.style('visibility', 'hidden')
|
|
201
|
+
.style('white-space', 'nowrap')
|
|
202
|
+
.html(text);
|
|
203
|
+
if (fontSize) {
|
|
204
|
+
container.style('font-size', fontSize);
|
|
205
|
+
}
|
|
206
|
+
const height = ((_a = container.node()) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().height) || 0;
|
|
207
|
+
container.remove();
|
|
208
|
+
return height;
|
|
209
|
+
};
|
|
210
|
+
export const getHorizontalSvgTextHeight = (args) => {
|
|
202
211
|
var _a;
|
|
203
212
|
const { text, style } = args;
|
|
204
213
|
const container = select(document.body).append('svg');
|
|
@@ -1,35 +1,15 @@
|
|
|
1
1
|
import get from 'lodash/get';
|
|
2
2
|
import isEmpty from 'lodash/isEmpty';
|
|
3
|
-
import {
|
|
3
|
+
import { DEFAULT_AXIS_TYPE, SeriesType, TOOLTIP_TOTALS_BUILT_IN_AGGREGATION } from '../constants';
|
|
4
4
|
import { i18n } from '../i18n';
|
|
5
5
|
import { CHART_ERROR_CODE, ChartError } from '../libs';
|
|
6
|
+
import { validateAxes } from './validate-axes';
|
|
6
7
|
function getTypeOf(value) {
|
|
7
8
|
return typeof value;
|
|
8
9
|
}
|
|
9
10
|
const AVAILABLE_SERIES_TYPES = Object.values(SeriesType);
|
|
10
11
|
const AVAILABLE_TOOLTIP_TOTALS_BUILT_IN_AGGREGATIONS = Object.values(TOOLTIP_TOTALS_BUILT_IN_AGGREGATION);
|
|
11
12
|
const AVAILABLE_TOOLTIP_TOTALS_AGGREGATION_TYPES = ['function', 'string'];
|
|
12
|
-
const AVAILABLE_AXIS_TYPES = Object.values(AXIS_TYPE);
|
|
13
|
-
function validateAxisType({ axis, key }) {
|
|
14
|
-
if (axis.type && !AVAILABLE_AXIS_TYPES.includes(axis.type)) {
|
|
15
|
-
throw new ChartError({
|
|
16
|
-
code: CHART_ERROR_CODE.INVALID_DATA,
|
|
17
|
-
message: i18n('error', 'label_invalid-axis-type', {
|
|
18
|
-
key,
|
|
19
|
-
values: AVAILABLE_AXIS_TYPES,
|
|
20
|
-
}),
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function validateAxes(args) {
|
|
25
|
-
const { xAxis, yAxis = [] } = args;
|
|
26
|
-
if (xAxis) {
|
|
27
|
-
validateAxisType({ axis: xAxis, key: 'x' });
|
|
28
|
-
}
|
|
29
|
-
yAxis.forEach((axis) => {
|
|
30
|
-
validateAxisType({ axis, key: 'y' });
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
13
|
function validateXYSeries(args) {
|
|
34
14
|
const { series, xAxis, yAxis = [] } = args;
|
|
35
15
|
const yAxisIndex = get(series, 'yAxis', 0);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { AXIS_TYPE } from '../constants';
|
|
2
|
+
import { i18n } from '../i18n';
|
|
3
|
+
import { CHART_ERROR_CODE, ChartError } from '../libs';
|
|
4
|
+
const AVAILABLE_AXIS_TYPES = Object.values(AXIS_TYPE);
|
|
5
|
+
function validateAxisType({ axis, key }) {
|
|
6
|
+
if (axis.type && !AVAILABLE_AXIS_TYPES.includes(axis.type)) {
|
|
7
|
+
throw new ChartError({
|
|
8
|
+
code: CHART_ERROR_CODE.INVALID_DATA,
|
|
9
|
+
message: i18n('error', 'label_invalid-axis-type', {
|
|
10
|
+
key,
|
|
11
|
+
values: AVAILABLE_AXIS_TYPES,
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function validateLabelsHtmlOptions(args) {
|
|
17
|
+
var _a;
|
|
18
|
+
const { axis } = args;
|
|
19
|
+
const html = (_a = axis.labels) === null || _a === void 0 ? void 0 : _a.html;
|
|
20
|
+
if (typeof html === 'undefined') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (typeof html !== 'boolean') {
|
|
24
|
+
throw new ChartError({
|
|
25
|
+
code: CHART_ERROR_CODE.INVALID_DATA,
|
|
26
|
+
message: i18n('error', 'label_invalid-axis-labels-html-type'),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (html && axis.type !== 'category') {
|
|
30
|
+
throw new ChartError({
|
|
31
|
+
code: CHART_ERROR_CODE.INVALID_DATA,
|
|
32
|
+
message: i18n('error', 'label_invalid-axis-labels-html-not-supported-axis-type'),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function validateAxes(args) {
|
|
37
|
+
const { xAxis, yAxis = [] } = args;
|
|
38
|
+
if (xAxis) {
|
|
39
|
+
validateAxisType({ axis: xAxis, key: 'x' });
|
|
40
|
+
validateLabelsHtmlOptions({ axis: xAxis });
|
|
41
|
+
}
|
|
42
|
+
yAxis.forEach((axis) => {
|
|
43
|
+
validateAxisType({ axis, key: 'y' });
|
|
44
|
+
validateLabelsHtmlOptions({ axis });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -3,13 +3,16 @@ import type { ChartScale, PreparedAxis, PreparedSplit } from '../../hooks';
|
|
|
3
3
|
import './styles.css';
|
|
4
4
|
type Props = {
|
|
5
5
|
axis: PreparedAxis;
|
|
6
|
-
|
|
6
|
+
boundsOffsetLeft: number;
|
|
7
|
+
boundsOffsetTop: number;
|
|
7
8
|
height: number;
|
|
9
|
+
htmlLayout: HTMLElement | null;
|
|
8
10
|
scale: ChartScale;
|
|
9
11
|
split: PreparedSplit;
|
|
12
|
+
width: number;
|
|
13
|
+
leftmostLimit?: number;
|
|
10
14
|
plotBeforeRef?: React.MutableRefObject<SVGGElement | null>;
|
|
11
15
|
plotAfterRef?: React.MutableRefObject<SVGGElement | null>;
|
|
12
|
-
leftmostLimit?: number;
|
|
13
16
|
};
|
|
14
17
|
export declare function getTitlePosition(args: {
|
|
15
18
|
axis: PreparedAxis;
|
|
@@ -42,11 +42,11 @@ export function getTitlePosition(args) {
|
|
|
42
42
|
return { x, y };
|
|
43
43
|
}
|
|
44
44
|
export const AxisX = React.memo(function AxisX(props) {
|
|
45
|
-
const { axis,
|
|
45
|
+
const { axis, boundsOffsetLeft, boundsOffsetTop, height: totalHeight, htmlLayout, leftmostLimit, plotAfterRef, plotBeforeRef, split, scale, width, } = props;
|
|
46
46
|
const ref = React.useRef(null);
|
|
47
47
|
React.useEffect(() => {
|
|
48
48
|
(async () => {
|
|
49
|
-
if (!ref.current) {
|
|
49
|
+
if (!ref.current || !htmlLayout) {
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
const svgElement = select(ref.current);
|
|
@@ -65,24 +65,29 @@ export const AxisX = React.memo(function AxisX(props) {
|
|
|
65
65
|
}
|
|
66
66
|
const axisScale = scale;
|
|
67
67
|
const xAxisGenerator = await axisBottom({
|
|
68
|
+
boundsOffsetLeft,
|
|
69
|
+
boundsOffsetTop,
|
|
70
|
+
domain: {
|
|
71
|
+
size: width,
|
|
72
|
+
color: axis.lineColor,
|
|
73
|
+
},
|
|
74
|
+
htmlLayout,
|
|
68
75
|
leftmostLimit,
|
|
69
76
|
scale: axisScale,
|
|
70
77
|
ticks: {
|
|
78
|
+
count: getTicksCount({ axis, range: width }),
|
|
79
|
+
labelsHtml: axis.labels.html,
|
|
71
80
|
items: tickItems,
|
|
72
81
|
labelFormat: getLabelFormatter({ axis, scale }),
|
|
73
|
-
|
|
82
|
+
labelsHeight: axis.labels.height,
|
|
83
|
+
labelsLineHeight: axis.labels.lineHeight,
|
|
74
84
|
labelsMargin: axis.labels.margin,
|
|
75
|
-
labelsStyle: axis.labels.style,
|
|
76
85
|
labelsMaxWidth: axis.labels.maxWidth,
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
labelsPaddings: axis.labels.padding,
|
|
87
|
+
labelsStyle: axis.labels.style,
|
|
79
88
|
maxTickCount: getMaxTickCount({ axis, width }),
|
|
80
89
|
rotation: axis.labels.rotation,
|
|
81
90
|
},
|
|
82
|
-
domain: {
|
|
83
|
-
size: width,
|
|
84
|
-
color: axis.lineColor,
|
|
85
|
-
},
|
|
86
91
|
});
|
|
87
92
|
svgElement.call(xAxisGenerator).attr('class', b());
|
|
88
93
|
// add an axis header if necessary
|
|
@@ -219,6 +224,18 @@ export const AxisX = React.memo(function AxisX(props) {
|
|
|
219
224
|
setPlotLines(plotAfterContainer, axis.plotLines.filter((d) => d.layerPlacement === 'after'));
|
|
220
225
|
}
|
|
221
226
|
})();
|
|
222
|
-
}, [
|
|
227
|
+
}, [
|
|
228
|
+
axis,
|
|
229
|
+
boundsOffsetLeft,
|
|
230
|
+
boundsOffsetTop,
|
|
231
|
+
htmlLayout,
|
|
232
|
+
leftmostLimit,
|
|
233
|
+
plotAfterRef,
|
|
234
|
+
plotBeforeRef,
|
|
235
|
+
scale,
|
|
236
|
+
split,
|
|
237
|
+
totalHeight,
|
|
238
|
+
width,
|
|
239
|
+
]);
|
|
223
240
|
return React.createElement("g", { ref: ref });
|
|
224
241
|
});
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ChartScale, PreparedAxis, PreparedSplit } from '../../hooks';
|
|
3
3
|
import './styles.css';
|
|
4
|
-
|
|
4
|
+
interface Props {
|
|
5
5
|
axes: PreparedAxis[];
|
|
6
|
+
boundsOffsetTop: number;
|
|
7
|
+
boundsOffsetLeft: number;
|
|
6
8
|
scale: ChartScale[];
|
|
7
9
|
width: number;
|
|
8
10
|
height: number;
|
|
11
|
+
htmlLayout: HTMLElement | null;
|
|
9
12
|
split: PreparedSplit;
|
|
10
13
|
plotBeforeRef?: React.MutableRefObject<SVGGElement | null>;
|
|
11
14
|
plotAfterRef?: React.MutableRefObject<SVGGElement | null>;
|
|
12
15
|
bottomLimit?: number;
|
|
13
16
|
topLimit?: number;
|
|
14
|
-
}
|
|
17
|
+
}
|
|
15
18
|
export declare const AxisY: (props: Props) => React.JSX.Element;
|
|
16
19
|
export {};
|