@gravity-ui/charts 1.18.0 → 1.18.1
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/AxisY/prepare-axis-data.d.ts +3 -2
- package/dist/cjs/components/AxisY/prepare-axis-data.js +5 -5
- package/dist/cjs/components/AxisY/utils.d.ts +4 -2
- package/dist/cjs/components/AxisY/utils.js +47 -14
- package/dist/cjs/components/ChartInner/index.js +2 -1
- package/dist/cjs/components/ChartInner/useChartInnerProps.js +1 -2
- package/dist/cjs/hooks/useAxisScales/index.d.ts +0 -1
- package/dist/cjs/hooks/useAxisScales/index.js +42 -21
- package/dist/cjs/hooks/useChartOptions/utils.d.ts +5 -2
- package/dist/cjs/hooks/useChartOptions/utils.js +2 -3
- package/dist/cjs/hooks/useChartOptions/y-axis.d.ts +1 -3
- package/dist/cjs/hooks/useChartOptions/y-axis.js +3 -5
- package/dist/cjs/hooks/useSeries/prepare-bar-y.js +3 -1
- package/dist/cjs/hooks/useShapes/bar-y/prepare-data.d.ts +4 -2
- package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +39 -20
- package/dist/cjs/hooks/useShapes/index.js +2 -0
- package/dist/cjs/hooks/useZoom/utils.js +24 -8
- package/dist/cjs/hooks/utils/bar-y.d.ts +7 -10
- package/dist/cjs/hooks/utils/bar-y.js +33 -18
- package/dist/cjs/utils/chart/array.js +3 -0
- package/dist/cjs/utils/chart/format.js +2 -2
- package/dist/cjs/utils/chart/index.js +2 -1
- package/dist/cjs/utils/chart/labels.d.ts +27 -5
- package/dist/cjs/utils/chart/labels.js +39 -3
- package/dist/esm/components/AxisY/prepare-axis-data.d.ts +3 -2
- package/dist/esm/components/AxisY/prepare-axis-data.js +5 -5
- package/dist/esm/components/AxisY/utils.d.ts +4 -2
- package/dist/esm/components/AxisY/utils.js +47 -14
- package/dist/esm/components/ChartInner/index.js +2 -1
- package/dist/esm/components/ChartInner/useChartInnerProps.js +1 -2
- package/dist/esm/hooks/useAxisScales/index.d.ts +0 -1
- package/dist/esm/hooks/useAxisScales/index.js +42 -21
- package/dist/esm/hooks/useChartOptions/utils.d.ts +5 -2
- package/dist/esm/hooks/useChartOptions/utils.js +2 -3
- package/dist/esm/hooks/useChartOptions/y-axis.d.ts +1 -3
- package/dist/esm/hooks/useChartOptions/y-axis.js +3 -5
- package/dist/esm/hooks/useSeries/prepare-bar-y.js +3 -1
- package/dist/esm/hooks/useShapes/bar-y/prepare-data.d.ts +4 -2
- package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +39 -20
- package/dist/esm/hooks/useShapes/index.js +2 -0
- package/dist/esm/hooks/useZoom/utils.js +24 -8
- package/dist/esm/hooks/utils/bar-y.d.ts +7 -10
- package/dist/esm/hooks/utils/bar-y.js +33 -18
- package/dist/esm/utils/chart/array.js +3 -0
- package/dist/esm/utils/chart/format.js +2 -2
- package/dist/esm/utils/chart/index.js +2 -1
- package/dist/esm/utils/chart/labels.d.ts +27 -5
- package/dist/esm/utils/chart/labels.js +39 -3
- package/package.json +1 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { ChartScale, PreparedAxis, PreparedSplit } from '../../hooks';
|
|
1
|
+
import type { ChartScale, PreparedAxis, PreparedSeries, PreparedSplit } from '../../hooks';
|
|
2
2
|
import type { AxisYData } from './types';
|
|
3
|
-
export declare function prepareAxisData({ axis, split, scale, top: topOffset, width, height, }: {
|
|
3
|
+
export declare function prepareAxisData({ axis, split, scale, top: topOffset, width, height, series, }: {
|
|
4
4
|
axis: PreparedAxis;
|
|
5
5
|
split: PreparedSplit;
|
|
6
6
|
scale: ChartScale;
|
|
7
7
|
top: number;
|
|
8
8
|
width: number;
|
|
9
9
|
height: number;
|
|
10
|
+
series: PreparedSeries[];
|
|
10
11
|
}): Promise<AxisYData>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getUniqId } from '@gravity-ui/uikit';
|
|
2
|
-
import { calculateCos, calculateSin,
|
|
2
|
+
import { calculateCos, calculateSin, formatAxisTickLabel, getBandsPosition, getLabelsSize, getMinSpaceBetween, getTextSizeFn, getTextWithElipsis, wrapText, } from '../../utils';
|
|
3
3
|
import { getTickValues } from './utils';
|
|
4
4
|
async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHeight, topOffset, }) {
|
|
5
5
|
var _a;
|
|
@@ -81,7 +81,7 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
|
|
|
81
81
|
return svgLabel;
|
|
82
82
|
}
|
|
83
83
|
// eslint-disable-next-line complexity
|
|
84
|
-
export async function prepareAxisData({ axis, split, scale, top: topOffset, width, height, }) {
|
|
84
|
+
export async function prepareAxisData({ axis, split, scale, top: topOffset, width, height, series, }) {
|
|
85
85
|
var _a, _b, _c;
|
|
86
86
|
const axisPlotTopPosition = ((_a = split.plots[axis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
87
87
|
const axisHeight = ((_b = split.plots[axis.plotIndex]) === null || _b === void 0 ? void 0 : _b.height) || height;
|
|
@@ -94,9 +94,9 @@ export async function prepareAxisData({ axis, split, scale, top: topOffset, widt
|
|
|
94
94
|
const ticks = [];
|
|
95
95
|
const getTextSize = getTextSizeFn({ style: axis.labels.style });
|
|
96
96
|
const labelLineHeight = (await getTextSize('Tmp')).height;
|
|
97
|
-
const values = getTickValues({ scale, axis, labelLineHeight });
|
|
97
|
+
const values = getTickValues({ scale, axis, labelLineHeight, series });
|
|
98
|
+
const tickStep = getMinSpaceBetween(values, (d) => Number(d.value));
|
|
98
99
|
const labelMaxHeight = values.length > 1 ? values[0].y - values[1].y - axis.labels.padding * 2 : axisHeight;
|
|
99
|
-
const labelFormatter = getLabelFormatter({ axis, scale });
|
|
100
100
|
for (let i = 0; i < values.length; i++) {
|
|
101
101
|
const tickValue = values[i];
|
|
102
102
|
const y = axisPlotTopPosition + tickValue.y;
|
|
@@ -125,7 +125,7 @@ export async function prepareAxisData({ axis, split, scale, top: topOffset, widt
|
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
else {
|
|
128
|
-
const text =
|
|
128
|
+
const text = formatAxisTickLabel({ value: tickValue.value, axis, step: tickStep });
|
|
129
129
|
svgLabel = await getSvgAxisLabel({
|
|
130
130
|
getTextSize,
|
|
131
131
|
text,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import type { ChartScale, PreparedAxis } from '../../hooks';
|
|
2
|
-
|
|
1
|
+
import type { ChartScale, PreparedAxis, PreparedSeries } from '../../hooks';
|
|
2
|
+
import type { ChartSeries } from '../../types';
|
|
3
|
+
export declare function getTickValues({ scale, axis, labelLineHeight, series, }: {
|
|
3
4
|
scale: ChartScale;
|
|
4
5
|
axis: PreparedAxis;
|
|
5
6
|
labelLineHeight: number;
|
|
7
|
+
series: PreparedSeries[] | ChartSeries[];
|
|
6
8
|
}): {
|
|
7
9
|
y: number;
|
|
8
10
|
value: number | Date;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getMinSpaceBetween, getTicksCount, isBandScale } from '../../utils';
|
|
1
|
+
import { getDomainDataYBySeries, getMinSpaceBetween, getTicksCount, isBandScale } from '../../utils';
|
|
2
2
|
function thinOut(items, delta) {
|
|
3
3
|
const arr = [];
|
|
4
4
|
for (let i = 0; i < items.length; i = i + delta) {
|
|
@@ -6,30 +6,63 @@ function thinOut(items, delta) {
|
|
|
6
6
|
}
|
|
7
7
|
return arr;
|
|
8
8
|
}
|
|
9
|
-
export function getTickValues({ scale, axis, labelLineHeight, }) {
|
|
9
|
+
export function getTickValues({ scale, axis, labelLineHeight, series, }) {
|
|
10
10
|
if ('ticks' in scale && typeof scale.ticks === 'function') {
|
|
11
11
|
const range = scale.range();
|
|
12
12
|
const height = Math.abs(range[0] - range[1]);
|
|
13
13
|
if (!height) {
|
|
14
14
|
return [];
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const getScaleTicks = () => {
|
|
17
|
+
var _a;
|
|
18
|
+
if (series.some((s) => s.type === 'bar-y')) {
|
|
19
|
+
const domainData = getDomainDataYBySeries(series);
|
|
20
|
+
if (domainData.length < 3) {
|
|
21
|
+
return domainData;
|
|
22
|
+
}
|
|
23
|
+
const ticksCount = (_a = getTicksCount({ axis, range: height })) !== null && _a !== void 0 ? _a : domainData.length;
|
|
24
|
+
return scale.ticks(Math.min(ticksCount, domainData.length));
|
|
25
|
+
}
|
|
26
|
+
const ticksCount = getTicksCount({ axis, range: height });
|
|
27
|
+
return scale.ticks(ticksCount);
|
|
28
|
+
};
|
|
29
|
+
const scaleTicks = getScaleTicks();
|
|
30
|
+
const originalTickValues = scaleTicks.map((t) => ({
|
|
18
31
|
y: scale(t),
|
|
19
32
|
value: t,
|
|
20
33
|
}));
|
|
21
|
-
if (
|
|
22
|
-
return
|
|
34
|
+
if (originalTickValues.length <= 1) {
|
|
35
|
+
return originalTickValues;
|
|
23
36
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
// first, we try to draw "beautiful" tick values
|
|
38
|
+
let result = originalTickValues;
|
|
39
|
+
let availableSpaceForLabel = getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
|
|
40
|
+
let ticksCount = result.length - 1;
|
|
41
|
+
while (availableSpaceForLabel < labelLineHeight && result.length > 1) {
|
|
27
42
|
ticksCount = ticksCount ? ticksCount - 1 : result.length - 1;
|
|
28
|
-
|
|
43
|
+
const newScaleTicks = scale.ticks(ticksCount);
|
|
44
|
+
result = newScaleTicks.map((t) => ({
|
|
29
45
|
y: scale(t),
|
|
30
46
|
value: t,
|
|
31
47
|
}));
|
|
32
|
-
|
|
48
|
+
availableSpaceForLabel =
|
|
49
|
+
getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
|
|
50
|
+
}
|
|
51
|
+
// when this is not possible (for example, such values cannot be selected for the logarithmic axis with a small range)
|
|
52
|
+
// just thin out the originally proposed result
|
|
53
|
+
if (!result.length) {
|
|
54
|
+
result = originalTickValues;
|
|
55
|
+
availableSpaceForLabel =
|
|
56
|
+
getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
|
|
57
|
+
let delta = 2;
|
|
58
|
+
while (availableSpaceForLabel < labelLineHeight && result.length > 1) {
|
|
59
|
+
result = thinOut(result, delta);
|
|
60
|
+
if (result.length > 1) {
|
|
61
|
+
delta += 1;
|
|
62
|
+
availableSpaceForLabel =
|
|
63
|
+
getMinSpaceBetween(result, (d) => d.y) - axis.labels.padding * 2;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
33
66
|
}
|
|
34
67
|
return result;
|
|
35
68
|
}
|
|
@@ -47,13 +80,13 @@ export function getTickValues({ scale, axis, labelLineHeight, }) {
|
|
|
47
80
|
return items;
|
|
48
81
|
}
|
|
49
82
|
let result = [...items];
|
|
50
|
-
let
|
|
83
|
+
let availableSpaceForLabel = Math.abs(result[0].y - result[1].y) - axis.labels.padding * 2;
|
|
51
84
|
let delta = 2;
|
|
52
|
-
while (
|
|
85
|
+
while (availableSpaceForLabel < labelLineHeight && result.length > 1) {
|
|
53
86
|
result = thinOut(items, delta);
|
|
54
87
|
if (result.length > 1) {
|
|
55
88
|
delta += 1;
|
|
56
|
-
|
|
89
|
+
availableSpaceForLabel = result[0].y - result[1].y - axis.labels.padding * 2;
|
|
57
90
|
}
|
|
58
91
|
}
|
|
59
92
|
return result;
|
|
@@ -100,6 +100,7 @@ export const ChartInner = (props) => {
|
|
|
100
100
|
width: boundsWidth,
|
|
101
101
|
height: boundsHeight,
|
|
102
102
|
split: preparedSplit,
|
|
103
|
+
series: preparedSeries,
|
|
103
104
|
});
|
|
104
105
|
items.push(axisData);
|
|
105
106
|
}
|
|
@@ -108,7 +109,7 @@ export const ChartInner = (props) => {
|
|
|
108
109
|
setYAxisDataItems(items);
|
|
109
110
|
}
|
|
110
111
|
})();
|
|
111
|
-
}, [boundsHeight, boundsOffsetTop, boundsWidth, preparedSplit, yAxis, yScale]);
|
|
112
|
+
}, [boundsHeight, boundsOffsetTop, boundsWidth, preparedSeries, preparedSplit, yAxis, yScale]);
|
|
112
113
|
return (React.createElement("div", { className: b() },
|
|
113
114
|
React.createElement("svg", { ref: svgRef, width: width, height: height,
|
|
114
115
|
// We use onPointerMove here because onMouseMove works incorrectly when the zoom setting is enabled:
|
|
@@ -66,10 +66,9 @@ export function useChartInnerProps(props) {
|
|
|
66
66
|
boundsHeight: estimatedBoundsHeight,
|
|
67
67
|
width,
|
|
68
68
|
seriesData: zoomedSeriesData,
|
|
69
|
-
seriesOptions: preparedSeriesOptions,
|
|
70
69
|
yAxis: data.yAxis,
|
|
71
70
|
}).then((val) => setYAxis(val));
|
|
72
|
-
}, [data.yAxis, estimatedBoundsHeight, height,
|
|
71
|
+
}, [data.yAxis, estimatedBoundsHeight, height, width, zoomedSeriesData]);
|
|
73
72
|
const { preparedSeries, preparedLegend, handleLegendItemClick } = useSeries({
|
|
74
73
|
colors,
|
|
75
74
|
legend: data.legend,
|
|
@@ -23,7 +23,6 @@ export declare function createYScale(args: {
|
|
|
23
23
|
axis: PreparedAxis;
|
|
24
24
|
boundsHeight: number;
|
|
25
25
|
series: (PreparedSeries | ChartSeries)[];
|
|
26
|
-
seriesOptions: PreparedSeriesOptions;
|
|
27
26
|
}): ScaleBand<string> | ScaleLinear<number, number, never> | ScaleTime<number, number, never> | undefined;
|
|
28
27
|
export declare function createXScale(args: {
|
|
29
28
|
axis: PreparedAxis | ChartAxis;
|
|
@@ -3,7 +3,7 @@ import { extent, scaleBand, scaleLinear, scaleLog, scaleUtc } from 'd3';
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { DEFAULT_AXIS_TYPE, SeriesType } from '../../constants';
|
|
5
5
|
import { CHART_SERIES_WITH_VOLUME_ON_Y_AXIS, getAxisHeight, getDataCategoryValue, getDefaultMaxXAxisValue, getDefaultMinXAxisValue, getDomainDataXBySeries, getDomainDataYBySeries, getOnlyVisibleSeries, isAxisRelatedSeries, isSeriesWithCategoryValues, } from '../../utils';
|
|
6
|
-
import {
|
|
6
|
+
import { getBandSize } from '../utils';
|
|
7
7
|
import { getBarXLayoutForNumericScale, groupBarXDataByXValue } from '../utils/bar-x';
|
|
8
8
|
const X_AXIS_ZOOM_PADDING = 0.02;
|
|
9
9
|
function validateArrayData(data) {
|
|
@@ -40,25 +40,12 @@ function filterCategoriesByVisibleSeries(args) {
|
|
|
40
40
|
// axis is validated in `validation/index.ts`, so the value of `axis.type` is definitely valid.
|
|
41
41
|
// eslint-disable-next-line consistent-return
|
|
42
42
|
function getYScaleRange(args) {
|
|
43
|
-
const { axis, boundsHeight
|
|
43
|
+
const { axis, boundsHeight } = args;
|
|
44
44
|
switch (axis.type) {
|
|
45
45
|
case 'datetime':
|
|
46
46
|
case 'linear':
|
|
47
47
|
case 'logarithmic': {
|
|
48
|
-
|
|
49
|
-
const barYSeries = series.filter((s) => s.type === SeriesType.BarY);
|
|
50
|
-
if (barYSeries.length) {
|
|
51
|
-
const groupedData = groupBarYDataByYValue(barYSeries, [axis]);
|
|
52
|
-
if (Object.keys(groupedData).length > 1) {
|
|
53
|
-
const { bandSize } = getBarYLayoutForNumericScale({
|
|
54
|
-
plotHeight: boundsHeight - boundsHeight * axis.maxPadding,
|
|
55
|
-
groupedData,
|
|
56
|
-
seriesOptions: seriesOptions,
|
|
57
|
-
});
|
|
58
|
-
const offset = bandSize / 2;
|
|
59
|
-
range = [range[0] - offset, range[1] + offset];
|
|
60
|
-
}
|
|
61
|
-
}
|
|
48
|
+
const range = [boundsHeight, 0];
|
|
62
49
|
switch (axis.order) {
|
|
63
50
|
case 'sortDesc':
|
|
64
51
|
case 'reverse': {
|
|
@@ -74,12 +61,12 @@ function getYScaleRange(args) {
|
|
|
74
61
|
}
|
|
75
62
|
// eslint-disable-next-line complexity
|
|
76
63
|
export function createYScale(args) {
|
|
77
|
-
const { axis, boundsHeight, series
|
|
64
|
+
const { axis, boundsHeight, series } = args;
|
|
78
65
|
const yMinProps = get(axis, 'min');
|
|
79
66
|
const yMaxProps = get(axis, 'max');
|
|
80
67
|
const yCategories = get(axis, 'categories');
|
|
81
68
|
const yTimestamps = get(axis, 'timestamps');
|
|
82
|
-
const range = getYScaleRange({ axis, boundsHeight
|
|
69
|
+
const range = getYScaleRange({ axis, boundsHeight });
|
|
83
70
|
switch (axis.type) {
|
|
84
71
|
case 'linear':
|
|
85
72
|
case 'logarithmic': {
|
|
@@ -100,7 +87,23 @@ export function createYScale(args) {
|
|
|
100
87
|
yMax = hasSeriesWithVolumeOnYAxis ? Math.max(yMaxDomain, 0) : yMaxDomain;
|
|
101
88
|
}
|
|
102
89
|
const scaleFn = axis.type === 'logarithmic' ? scaleLog : scaleLinear;
|
|
103
|
-
|
|
90
|
+
const scale = scaleFn().domain([yMin, yMax]).range(range);
|
|
91
|
+
let offsetMin = 0;
|
|
92
|
+
let offsetMax = boundsHeight * axis.maxPadding;
|
|
93
|
+
const barYSeries = series.filter((s) => s.type === SeriesType.BarY);
|
|
94
|
+
if (barYSeries.length) {
|
|
95
|
+
if (domain.length > 1) {
|
|
96
|
+
const bandWidth = getBandSize({
|
|
97
|
+
scale: scale,
|
|
98
|
+
domain: domain,
|
|
99
|
+
});
|
|
100
|
+
offsetMin += bandWidth / 2;
|
|
101
|
+
offsetMax += bandWidth / 2;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const domainOffsetMin = Math.abs(scale.invert(offsetMin) - scale.invert(0));
|
|
105
|
+
const domainOffsetMax = Math.abs(scale.invert(offsetMax) - scale.invert(0));
|
|
106
|
+
return scale.domain([yMin - domainOffsetMin, yMax + domainOffsetMax]);
|
|
104
107
|
}
|
|
105
108
|
break;
|
|
106
109
|
}
|
|
@@ -132,7 +135,23 @@ export function createYScale(args) {
|
|
|
132
135
|
const [yMinTimestamp, yMaxTimestamp] = extent(domain);
|
|
133
136
|
const yMin = typeof yMinProps === 'number' ? yMinProps : yMinTimestamp;
|
|
134
137
|
const yMax = typeof yMaxProps === 'number' ? yMaxProps : yMaxTimestamp;
|
|
135
|
-
|
|
138
|
+
const scale = scaleUtc().domain([yMin, yMax]).range(range);
|
|
139
|
+
let offsetMin = 0;
|
|
140
|
+
let offsetMax = boundsHeight * axis.maxPadding;
|
|
141
|
+
const barYSeries = series.filter((s) => s.type === SeriesType.BarY);
|
|
142
|
+
if (barYSeries.length) {
|
|
143
|
+
if (Object.keys(domain).length > 1) {
|
|
144
|
+
const bandWidth = getBandSize({
|
|
145
|
+
scale: scale,
|
|
146
|
+
domain: domain,
|
|
147
|
+
});
|
|
148
|
+
offsetMin += bandWidth / 2;
|
|
149
|
+
offsetMax += bandWidth / 2;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const domainOffsetMin = Math.abs(scale.invert(offsetMin).getTime() - scale.invert(0).getTime());
|
|
153
|
+
const domainOffsetMax = Math.abs(scale.invert(offsetMax).getTime() - scale.invert(0).getTime());
|
|
154
|
+
return scale.domain([yMin - domainOffsetMin, yMax + domainOffsetMax]);
|
|
136
155
|
}
|
|
137
156
|
}
|
|
138
157
|
}
|
|
@@ -207,6 +226,9 @@ export function createXScale(args) {
|
|
|
207
226
|
if (hasOnlyNullValues || domainData.length === 0) {
|
|
208
227
|
return undefined;
|
|
209
228
|
}
|
|
229
|
+
if (series.some((s) => s.stacking === 'percent')) {
|
|
230
|
+
return scaleLinear().domain([0, 100]).range(range);
|
|
231
|
+
}
|
|
210
232
|
if (hasNumberAndNullValues) {
|
|
211
233
|
const [xMinDomain, xMaxDomain] = extent(domainData);
|
|
212
234
|
let xMin;
|
|
@@ -304,7 +326,6 @@ const createScales = (args) => {
|
|
|
304
326
|
axis,
|
|
305
327
|
boundsHeight: axisHeight,
|
|
306
328
|
series: visibleAxisSeries.length ? visibleAxisSeries : axisSeries,
|
|
307
|
-
seriesOptions,
|
|
308
329
|
});
|
|
309
330
|
}),
|
|
310
331
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AxisPlot,
|
|
1
|
+
import type { AxisPlot, ChartAxis } from '../../types';
|
|
2
2
|
export declare function prepareAxisPlotLabel(d: AxisPlot): {
|
|
3
3
|
text: string;
|
|
4
4
|
style: {
|
|
@@ -8,4 +8,7 @@ export declare function prepareAxisPlotLabel(d: AxisPlot): {
|
|
|
8
8
|
};
|
|
9
9
|
padding: number;
|
|
10
10
|
};
|
|
11
|
-
export declare function getAxisCategories(
|
|
11
|
+
export declare function getAxisCategories({ categories, order, }?: {
|
|
12
|
+
categories?: string[];
|
|
13
|
+
order?: ChartAxis['order'];
|
|
14
|
+
}): string[] | undefined;
|
|
@@ -8,10 +8,9 @@ export function prepareAxisPlotLabel(d) {
|
|
|
8
8
|
padding: (_e = (_d = d.label) === null || _d === void 0 ? void 0 : _d.padding) !== null && _e !== void 0 ? _e : 5,
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
|
-
export function getAxisCategories(
|
|
12
|
-
const categories = axis === null || axis === void 0 ? void 0 : axis.categories;
|
|
11
|
+
export function getAxisCategories({ categories, order, } = {}) {
|
|
13
12
|
if (categories) {
|
|
14
|
-
switch (
|
|
13
|
+
switch (order) {
|
|
15
14
|
case 'reverse': {
|
|
16
15
|
return reverse(categories);
|
|
17
16
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { ChartSeries, ChartYAxis } from '../../types';
|
|
2
|
-
import type { PreparedSeriesOptions } from '../useSeries/types';
|
|
3
2
|
import type { PreparedAxis } from './types';
|
|
4
|
-
export declare const getPreparedYAxis: ({ height, boundsHeight, width, seriesData,
|
|
3
|
+
export declare const getPreparedYAxis: ({ height, boundsHeight, width, seriesData, yAxis, }: {
|
|
5
4
|
height: number;
|
|
6
5
|
boundsHeight: number;
|
|
7
6
|
width: number;
|
|
8
7
|
seriesData: ChartSeries[];
|
|
9
|
-
seriesOptions: PreparedSeriesOptions;
|
|
10
8
|
yAxis: ChartYAxis[] | undefined;
|
|
11
9
|
}) => Promise<PreparedAxis[]>;
|
|
@@ -5,7 +5,7 @@ import { calculateNumericProperty, formatAxisTickLabel, getClosestPointsRange, g
|
|
|
5
5
|
import { createYScale } from '../useAxisScales';
|
|
6
6
|
import { getAxisCategories, prepareAxisPlotLabel } from './utils';
|
|
7
7
|
const getAxisLabelMaxWidth = async (args) => {
|
|
8
|
-
const { axis, seriesData,
|
|
8
|
+
const { axis, seriesData, height } = args;
|
|
9
9
|
if (!axis.labels.enabled) {
|
|
10
10
|
return { height: 0, width: 0 };
|
|
11
11
|
}
|
|
@@ -13,14 +13,13 @@ const getAxisLabelMaxWidth = async (args) => {
|
|
|
13
13
|
axis,
|
|
14
14
|
boundsHeight: height,
|
|
15
15
|
series: seriesData,
|
|
16
|
-
seriesOptions,
|
|
17
16
|
});
|
|
18
17
|
if (!scale) {
|
|
19
18
|
return { height: 0, width: 0 };
|
|
20
19
|
}
|
|
21
20
|
const getTextSize = getTextSizeFn({ style: axis.labels.style });
|
|
22
21
|
const labelLineHeight = (await getTextSize('Tmp')).height;
|
|
23
|
-
const tickValues = getTickValues({ axis, scale, labelLineHeight });
|
|
22
|
+
const tickValues = getTickValues({ axis, scale, labelLineHeight, series: seriesData });
|
|
24
23
|
const ticks = getScaleTicks(scale);
|
|
25
24
|
const tickStep = getClosestPointsRange(axis, ticks);
|
|
26
25
|
if (axis.type === 'datetime' && !axis.labels.dateFormat) {
|
|
@@ -39,7 +38,7 @@ const getAxisLabelMaxWidth = async (args) => {
|
|
|
39
38
|
});
|
|
40
39
|
return { height: size.maxHeight, width: size.maxWidth };
|
|
41
40
|
};
|
|
42
|
-
export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData,
|
|
41
|
+
export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, yAxis, }) => {
|
|
43
42
|
const axisByPlot = [];
|
|
44
43
|
const axisItems = yAxis || [{}];
|
|
45
44
|
const hasAxisRelatedSeries = seriesData.some(isAxisRelatedSeries);
|
|
@@ -153,7 +152,6 @@ export const getPreparedYAxis = ({ height, boundsHeight, width, seriesData, seri
|
|
|
153
152
|
const { height: labelsHeight, width: labelsWidth } = await getAxisLabelMaxWidth({
|
|
154
153
|
axis: preparedAxis,
|
|
155
154
|
seriesData,
|
|
156
|
-
seriesOptions,
|
|
157
155
|
height: boundsHeight,
|
|
158
156
|
});
|
|
159
157
|
preparedAxis.labels.height = labelsHeight;
|
|
@@ -9,7 +9,9 @@ async function prepareDataLabels(series) {
|
|
|
9
9
|
const style = Object.assign({}, DEFAULT_DATALABELS_STYLE, (_a = series.dataLabels) === null || _a === void 0 ? void 0 : _a.style);
|
|
10
10
|
const html = get(series, 'dataLabels.html', false);
|
|
11
11
|
const labels = enabled
|
|
12
|
-
? series.data
|
|
12
|
+
? series.data
|
|
13
|
+
.filter((d) => Boolean(d.x || d.label))
|
|
14
|
+
.map((d) => getFormattedValue(Object.assign({ value: d.x || d.label }, series.dataLabels)))
|
|
13
15
|
: [];
|
|
14
16
|
const { maxHeight = 0, maxWidth = 0 } = await getLabelsSize({
|
|
15
17
|
labels,
|
|
@@ -2,11 +2,13 @@ import type { ChartScale } from '../../useAxisScales';
|
|
|
2
2
|
import type { PreparedAxis } from '../../useChartOptions/types';
|
|
3
3
|
import type { PreparedBarYSeries, PreparedSeriesOptions } from '../../useSeries/types';
|
|
4
4
|
import type { BarYShapesArgs } from './types';
|
|
5
|
-
export declare
|
|
5
|
+
export declare function prepareBarYData(args: {
|
|
6
|
+
boundsHeight: number;
|
|
7
|
+
boundsWidth: number;
|
|
6
8
|
series: PreparedBarYSeries[];
|
|
7
9
|
seriesOptions: PreparedSeriesOptions;
|
|
8
10
|
xAxis: PreparedAxis;
|
|
9
11
|
xScale: ChartScale;
|
|
10
12
|
yAxis: PreparedAxis[];
|
|
11
13
|
yScale: (ChartScale | undefined)[];
|
|
12
|
-
})
|
|
14
|
+
}): Promise<BarYShapesArgs>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ascending, descending, sort } from 'd3';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
|
-
import { filterOverlappingLabels, getLabelsSize, getTextSizeFn } from '../../../utils';
|
|
3
|
+
import { filterOverlappingLabels, getHtmlLabelConstraintedPosition, getLabelsSize, getSvgLabelConstraintedPosition, getTextSizeFn, } from '../../../utils';
|
|
4
4
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
5
|
-
import {
|
|
5
|
+
import { getBarYLayout, groupBarYDataByYValue } from '../../utils';
|
|
6
6
|
const DEFAULT_LABEL_PADDING = 7;
|
|
7
|
-
export
|
|
7
|
+
export async function prepareBarYData(args) {
|
|
8
8
|
var _a;
|
|
9
|
-
const { series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
|
|
9
|
+
const { boundsHeight, boundsWidth, series, seriesOptions, yAxis, xScale, yScale: [yScale], } = args;
|
|
10
10
|
const stackGap = seriesOptions['bar-y'].stackGap;
|
|
11
11
|
const xLinearScale = xScale;
|
|
12
12
|
const yLinearScale = yScale;
|
|
@@ -18,7 +18,6 @@ export const prepareBarYData = async (args) => {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
const yScaleRange = yLinearScale.range();
|
|
21
|
-
const plotHeight = Math.abs(yScaleRange[0] - yScaleRange[1]);
|
|
22
21
|
const sortingOptions = get(seriesOptions, 'bar-y.dataSorting');
|
|
23
22
|
const comparator = (sortingOptions === null || sortingOptions === void 0 ? void 0 : sortingOptions.direction) === 'desc' ? descending : ascending;
|
|
24
23
|
const sortKey = (() => {
|
|
@@ -35,13 +34,13 @@ export const prepareBarYData = async (args) => {
|
|
|
35
34
|
}
|
|
36
35
|
})();
|
|
37
36
|
const groupedData = groupBarYDataByYValue(series, yAxis);
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
const plotHeight = Math.abs(yScaleRange[0] - yScaleRange[1]);
|
|
38
|
+
const { bandSize, barGap, barSize } = getBarYLayout({
|
|
39
|
+
groupedData,
|
|
40
|
+
seriesOptions,
|
|
41
|
+
plotHeight,
|
|
42
|
+
scale: yScale,
|
|
43
|
+
});
|
|
45
44
|
const result = [];
|
|
46
45
|
const baseRangeValue = xLinearScale.range()[0];
|
|
47
46
|
Object.entries(groupedData).forEach(([yValue, val]) => {
|
|
@@ -106,7 +105,7 @@ export const prepareBarYData = async (args) => {
|
|
|
106
105
|
});
|
|
107
106
|
});
|
|
108
107
|
let labels = [];
|
|
109
|
-
|
|
108
|
+
let htmlElements = [];
|
|
110
109
|
const map = new Map();
|
|
111
110
|
for (let i = 0; i < result.length; i++) {
|
|
112
111
|
const prepared = result[i];
|
|
@@ -124,12 +123,20 @@ export const prepareBarYData = async (args) => {
|
|
|
124
123
|
style: dataLabels.style,
|
|
125
124
|
html: dataLabels.html,
|
|
126
125
|
});
|
|
127
|
-
|
|
126
|
+
const constrainedPosition = getHtmlLabelConstraintedPosition({
|
|
127
|
+
boundsHeight,
|
|
128
|
+
boundsWidth,
|
|
129
|
+
height,
|
|
130
|
+
width,
|
|
128
131
|
x,
|
|
129
132
|
y: y - height / 2,
|
|
133
|
+
});
|
|
134
|
+
htmlElements.push({
|
|
130
135
|
content,
|
|
131
136
|
size: { width, height },
|
|
132
137
|
style: dataLabels.style,
|
|
138
|
+
x: constrainedPosition.x,
|
|
139
|
+
y: constrainedPosition.y,
|
|
133
140
|
});
|
|
134
141
|
}
|
|
135
142
|
else {
|
|
@@ -138,24 +145,36 @@ export const prepareBarYData = async (args) => {
|
|
|
138
145
|
}
|
|
139
146
|
const getTextSize = map.get(dataLabels.style);
|
|
140
147
|
const { width, height } = await getTextSize(content);
|
|
141
|
-
|
|
148
|
+
const constrainedPosition = getSvgLabelConstraintedPosition({
|
|
149
|
+
boundsHeight,
|
|
150
|
+
boundsWidth,
|
|
151
|
+
height,
|
|
152
|
+
width,
|
|
142
153
|
x,
|
|
143
154
|
y: y + height / 2,
|
|
155
|
+
});
|
|
156
|
+
labels.push({
|
|
157
|
+
size: { width, height },
|
|
158
|
+
series: prepared.series,
|
|
159
|
+
style: dataLabels.style,
|
|
144
160
|
text: content,
|
|
145
161
|
textAnchor: dataLabels.inside ? 'middle' : 'right',
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
size: { width, height },
|
|
162
|
+
x: constrainedPosition.x,
|
|
163
|
+
y: constrainedPosition.y,
|
|
149
164
|
});
|
|
150
165
|
}
|
|
151
166
|
}
|
|
152
167
|
}
|
|
153
|
-
|
|
168
|
+
const allowOverlap = (_a = result[0]) === null || _a === void 0 ? void 0 : _a.series.dataLabels.allowOverlap;
|
|
169
|
+
if (labels.length && !allowOverlap) {
|
|
154
170
|
labels = filterOverlappingLabels(labels);
|
|
155
171
|
}
|
|
172
|
+
else if (htmlElements.length && !allowOverlap) {
|
|
173
|
+
htmlElements = filterOverlappingLabels(htmlElements);
|
|
174
|
+
}
|
|
156
175
|
return {
|
|
157
176
|
shapes: result,
|
|
158
177
|
labels,
|
|
159
178
|
htmlElements,
|
|
160
179
|
};
|
|
161
|
-
}
|
|
180
|
+
}
|
|
@@ -56,6 +56,8 @@ export const useShapes = (args) => {
|
|
|
56
56
|
case 'bar-y': {
|
|
57
57
|
if (xAxis && xScale && (yScale === null || yScale === void 0 ? void 0 : yScale.length)) {
|
|
58
58
|
const preparedData = await prepareBarYData({
|
|
59
|
+
boundsHeight,
|
|
60
|
+
boundsWidth,
|
|
59
61
|
series: chartSeries,
|
|
60
62
|
seriesOptions,
|
|
61
63
|
xAxis,
|
|
@@ -95,6 +95,22 @@ function selectionXToZoomBounds(args) {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
|
+
function getYMinMaxFromSelection(args) {
|
|
99
|
+
const { selection, yAxis } = args;
|
|
100
|
+
let yMin;
|
|
101
|
+
let yMax;
|
|
102
|
+
switch (yAxis.order) {
|
|
103
|
+
case 'reverse':
|
|
104
|
+
case 'sortDesc': {
|
|
105
|
+
[yMin, yMax] = selection;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
default: {
|
|
109
|
+
[yMax, yMin] = selection;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [yMin, yMax];
|
|
113
|
+
}
|
|
98
114
|
function selectionYToZoomBounds(args) {
|
|
99
115
|
const { yAxis, yScale, selection } = args;
|
|
100
116
|
switch (yAxis.type) {
|
|
@@ -104,8 +120,8 @@ function selectionYToZoomBounds(args) {
|
|
|
104
120
|
const categories = yAxis.categories || [];
|
|
105
121
|
const currentDomain = bandScale.domain();
|
|
106
122
|
const step = bandScale.step();
|
|
107
|
-
let startIndex = currentDomain.length - 1 - Math.floor(y0 / step);
|
|
108
|
-
let endIndex = currentDomain.length - 1 - Math.floor(y1 / step);
|
|
123
|
+
let startIndex = Math.max(0, currentDomain.length - 1 - Math.floor(y0 / step));
|
|
124
|
+
let endIndex = Math.min(currentDomain.length - 1, currentDomain.length - 1 - Math.floor(y1 / step));
|
|
109
125
|
const startCategory = currentDomain[startIndex];
|
|
110
126
|
const endCategory = currentDomain[endIndex];
|
|
111
127
|
startIndex = categories.indexOf(startCategory);
|
|
@@ -119,18 +135,18 @@ function selectionYToZoomBounds(args) {
|
|
|
119
135
|
return [startIndex, endIndex];
|
|
120
136
|
}
|
|
121
137
|
case 'datetime': {
|
|
122
|
-
const [
|
|
138
|
+
const [yMin, yMax] = getYMinMaxFromSelection({ selection, yAxis });
|
|
123
139
|
const timeScale = yScale;
|
|
124
|
-
const minTimestamp = timeScale.invert(
|
|
125
|
-
const maxTimestamp = timeScale.invert(
|
|
140
|
+
const minTimestamp = timeScale.invert(yMin).getTime();
|
|
141
|
+
const maxTimestamp = timeScale.invert(yMax).getTime();
|
|
126
142
|
return [minTimestamp, maxTimestamp];
|
|
127
143
|
}
|
|
128
144
|
case 'linear':
|
|
129
145
|
case 'logarithmic': {
|
|
130
|
-
const [
|
|
146
|
+
const [yMin, yMax] = getYMinMaxFromSelection({ selection, yAxis });
|
|
131
147
|
const linearScale = yScale;
|
|
132
|
-
const minValue = linearScale.invert(
|
|
133
|
-
const maxValue = linearScale.invert(
|
|
148
|
+
const minValue = linearScale.invert(yMin);
|
|
149
|
+
const maxValue = linearScale.invert(yMax);
|
|
134
150
|
return [minValue, maxValue];
|
|
135
151
|
}
|
|
136
152
|
default: {
|