@gravity-ui/charts 1.14.0 → 1.16.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/ChartInner/useChartInnerProps.js +7 -2
- package/dist/cjs/components/Tooltip/ChartTooltipContent.d.ts +5 -2
- package/dist/cjs/components/Tooltip/ChartTooltipContent.js +2 -2
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/Row.d.ts +2 -1
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/Row.js +3 -3
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/{RowTotals.d.ts → RowWithAggregation.d.ts} +2 -1
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/{RowTotals.js → RowWithAggregation.js} +3 -3
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.d.ts +4 -1
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.js +187 -87
- package/dist/cjs/components/Tooltip/index.js +1 -1
- package/dist/cjs/components/Tooltip/styles.css +11 -1
- package/dist/cjs/hooks/useAxisScales/index.d.ts +1 -0
- package/dist/cjs/hooks/useAxisScales/index.js +53 -37
- package/dist/cjs/hooks/useChartOptions/x-axis.d.ts +3 -1
- package/dist/cjs/hooks/useChartOptions/x-axis.js +4 -3
- package/dist/cjs/hooks/useSeries/prepare-area.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-bar-x.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-bar-y.d.ts +1 -0
- package/dist/cjs/hooks/useSeries/prepare-bar-y.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-line.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-pie.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-radar.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-sankey.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-scatter.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-treemap.js +1 -0
- package/dist/cjs/hooks/useSeries/prepare-waterfall.js +1 -0
- package/dist/cjs/hooks/useSeries/types.d.ts +2 -1
- package/dist/cjs/hooks/utils/bar-x.d.ts +16 -0
- package/dist/cjs/hooks/utils/bar-x.js +41 -0
- package/dist/cjs/i18n/keysets/en.json +2 -1
- package/dist/cjs/i18n/keysets/ru.json +2 -1
- package/dist/cjs/types/chart/base.d.ts +13 -0
- package/dist/cjs/types/chart/tooltip.d.ts +19 -1
- package/dist/cjs/utils/chart/format.d.ts +9 -2
- package/dist/cjs/utils/chart/format.js +39 -5
- package/dist/cjs/utils/chart/index.d.ts +7 -11
- package/dist/cjs/utils/chart/index.js +13 -28
- package/dist/cjs/utils/misc.d.ts +1 -0
- package/dist/cjs/utils/misc.js +6 -0
- package/dist/esm/components/ChartInner/useChartInnerProps.js +7 -2
- package/dist/esm/components/Tooltip/ChartTooltipContent.d.ts +5 -2
- package/dist/esm/components/Tooltip/ChartTooltipContent.js +2 -2
- package/dist/esm/components/Tooltip/DefaultTooltipContent/Row.d.ts +2 -1
- package/dist/esm/components/Tooltip/DefaultTooltipContent/Row.js +3 -3
- package/dist/esm/components/Tooltip/DefaultTooltipContent/{RowTotals.d.ts → RowWithAggregation.d.ts} +2 -1
- package/dist/esm/components/Tooltip/DefaultTooltipContent/{RowTotals.js → RowWithAggregation.js} +3 -3
- package/dist/esm/components/Tooltip/DefaultTooltipContent/index.d.ts +4 -1
- package/dist/esm/components/Tooltip/DefaultTooltipContent/index.js +187 -87
- package/dist/esm/components/Tooltip/index.js +1 -1
- package/dist/esm/components/Tooltip/styles.css +11 -1
- package/dist/esm/hooks/useAxisScales/index.d.ts +1 -0
- package/dist/esm/hooks/useAxisScales/index.js +53 -37
- package/dist/esm/hooks/useChartOptions/x-axis.d.ts +3 -1
- package/dist/esm/hooks/useChartOptions/x-axis.js +4 -3
- package/dist/esm/hooks/useSeries/prepare-area.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-bar-x.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-bar-y.d.ts +1 -0
- package/dist/esm/hooks/useSeries/prepare-bar-y.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-line.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-pie.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-radar.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-sankey.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-scatter.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-treemap.js +1 -0
- package/dist/esm/hooks/useSeries/prepare-waterfall.js +1 -0
- package/dist/esm/hooks/useSeries/types.d.ts +2 -1
- package/dist/esm/hooks/utils/bar-x.d.ts +16 -0
- package/dist/esm/hooks/utils/bar-x.js +41 -0
- package/dist/esm/i18n/keysets/en.json +2 -1
- package/dist/esm/i18n/keysets/ru.json +2 -1
- package/dist/esm/types/chart/base.d.ts +13 -0
- package/dist/esm/types/chart/tooltip.d.ts +19 -1
- package/dist/esm/utils/chart/format.d.ts +9 -2
- package/dist/esm/utils/chart/format.js +39 -5
- package/dist/esm/utils/chart/index.d.ts +7 -11
- package/dist/esm/utils/chart/index.js +13 -28
- package/dist/esm/utils/misc.d.ts +1 -0
- package/dist/esm/utils/misc.js +6 -0
- package/package.json +1 -1
|
@@ -39,8 +39,13 @@ export function useChartInnerProps(props) {
|
|
|
39
39
|
const [xAxis, setXAxis] = React.useState(null);
|
|
40
40
|
React.useEffect(() => {
|
|
41
41
|
setXAxis(null);
|
|
42
|
-
getPreparedXAxis({
|
|
43
|
-
|
|
42
|
+
getPreparedXAxis({
|
|
43
|
+
xAxis: data.xAxis,
|
|
44
|
+
width,
|
|
45
|
+
seriesData: zoomedSeriesData,
|
|
46
|
+
seriesOptions: preparedSeriesOptions,
|
|
47
|
+
}).then((val) => setXAxis(val));
|
|
48
|
+
}, [data.xAxis, preparedSeriesOptions, width, zoomedSeriesData]);
|
|
44
49
|
const [yAxis, setYAxis] = React.useState([]);
|
|
45
50
|
React.useEffect(() => {
|
|
46
51
|
setYAxis([]);
|
|
@@ -2,10 +2,13 @@ import React from 'react';
|
|
|
2
2
|
import type { ChartTooltip, ChartXAxis, ChartYAxis, TooltipDataChunk } from '../../types';
|
|
3
3
|
export interface ChartTooltipContentProps {
|
|
4
4
|
hovered?: TooltipDataChunk[];
|
|
5
|
-
|
|
6
|
-
yAxis?: ChartYAxis;
|
|
5
|
+
pinned?: boolean;
|
|
7
6
|
renderer?: ChartTooltip['renderer'];
|
|
7
|
+
rowRenderer?: ChartTooltip['rowRenderer'];
|
|
8
8
|
valueFormat?: ChartTooltip['valueFormat'];
|
|
9
|
+
headerFormat?: ChartTooltip['headerFormat'];
|
|
9
10
|
totals?: ChartTooltip['totals'];
|
|
11
|
+
xAxis?: ChartXAxis | null;
|
|
12
|
+
yAxis?: ChartYAxis;
|
|
10
13
|
}
|
|
11
14
|
export declare const ChartTooltipContent: (props: ChartTooltipContentProps) => React.JSX.Element | null;
|
|
@@ -2,10 +2,10 @@ import React from 'react';
|
|
|
2
2
|
import isNil from 'lodash/isNil';
|
|
3
3
|
import { DefaultTooltipContent } from './DefaultTooltipContent';
|
|
4
4
|
export const ChartTooltipContent = (props) => {
|
|
5
|
-
const { hovered, xAxis, yAxis, renderer, valueFormat, totals } = props;
|
|
5
|
+
const { hovered, xAxis, yAxis, renderer, rowRenderer, valueFormat, headerFormat, totals, pinned, } = props;
|
|
6
6
|
if (!hovered) {
|
|
7
7
|
return null;
|
|
8
8
|
}
|
|
9
9
|
const customTooltip = renderer === null || renderer === void 0 ? void 0 : renderer({ hovered, xAxis, yAxis });
|
|
10
|
-
return isNil(customTooltip) ? (React.createElement(DefaultTooltipContent, { hovered: hovered,
|
|
10
|
+
return isNil(customTooltip) ? (React.createElement(DefaultTooltipContent, { hovered: hovered, pinned: pinned, rowRenderer: rowRenderer, totals: totals, valueFormat: valueFormat, headerFormat: headerFormat, xAxis: xAxis, yAxis: yAxis })) : (customTooltip);
|
|
11
11
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
export declare function Row(props: {
|
|
3
3
|
label: React.ReactNode;
|
|
4
|
-
value: React.ReactNode;
|
|
5
4
|
active?: boolean;
|
|
6
5
|
className?: string;
|
|
7
6
|
color?: string;
|
|
8
7
|
striped?: boolean;
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
value?: React.ReactNode;
|
|
9
10
|
}): React.JSX.Element;
|
|
@@ -2,9 +2,9 @@ import React from 'react';
|
|
|
2
2
|
import { block } from '../../../utils';
|
|
3
3
|
const b = block('tooltip');
|
|
4
4
|
export function Row(props) {
|
|
5
|
-
const { label, value, active, color, className, striped } = props;
|
|
6
|
-
return (React.createElement("div", { className: b('content-row', { active, striped }, className) },
|
|
5
|
+
const { label, value, active, color, className, striped, style } = props;
|
|
6
|
+
return (React.createElement("div", { className: b('content-row', { active, striped }, className), style: style },
|
|
7
7
|
color && React.createElement("div", { className: b('content-row-color'), style: { backgroundColor: color } }),
|
|
8
8
|
label,
|
|
9
|
-
React.createElement("span", { className: b('content-row-value') }, value)));
|
|
9
|
+
value && React.createElement("span", { className: b('content-row-value') }, value)));
|
|
10
10
|
}
|
package/dist/cjs/components/Tooltip/DefaultTooltipContent/{RowTotals.d.ts → RowWithAggregation.d.ts}
RENAMED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ChartTooltipTotalsAggregationValue, ChartTooltipTotalsBuiltInAggregation, ValueFormat } from '../../../types';
|
|
3
3
|
import type { HoveredValue } from './utils';
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function RowWithAggregation(props: {
|
|
5
5
|
aggregation: ChartTooltipTotalsBuiltInAggregation | (() => ChartTooltipTotalsAggregationValue);
|
|
6
6
|
values: HoveredValue[];
|
|
7
7
|
label?: string;
|
|
8
|
+
style?: React.CSSProperties;
|
|
8
9
|
valueFormat?: ValueFormat;
|
|
9
10
|
}): React.JSX.Element;
|
package/dist/cjs/components/Tooltip/DefaultTooltipContent/{RowTotals.js → RowWithAggregation.js}
RENAMED
|
@@ -4,8 +4,8 @@ import { getFormattedValue } from '../../../utils/chart/format';
|
|
|
4
4
|
import { Row } from './Row';
|
|
5
5
|
import { getBuiltInAggregatedValue, getBuiltInAggregationLabel } from './utils';
|
|
6
6
|
const b = block('tooltip');
|
|
7
|
-
export function
|
|
8
|
-
const { aggregation, label, valueFormat, values } = props;
|
|
7
|
+
export function RowWithAggregation(props) {
|
|
8
|
+
const { aggregation, label, style, valueFormat, values } = props;
|
|
9
9
|
let resultLabel = label;
|
|
10
10
|
if (!resultLabel && typeof aggregation === 'string') {
|
|
11
11
|
resultLabel = getBuiltInAggregationLabel({ aggregation });
|
|
@@ -19,5 +19,5 @@ export function RowTotals(props) {
|
|
|
19
19
|
format: valueFormat || { type: 'number' },
|
|
20
20
|
})
|
|
21
21
|
: resultValue;
|
|
22
|
-
return (React.createElement(Row, { className: b('content-row-totals'), label: resultLabel, value: formattedResultValue }));
|
|
22
|
+
return (React.createElement(Row, { className: b('content-row-totals'), label: resultLabel, style: style, value: formattedResultValue }));
|
|
23
23
|
}
|
|
@@ -2,10 +2,13 @@ import React from 'react';
|
|
|
2
2
|
import type { ChartTooltip, ChartXAxis, ChartYAxis, TooltipDataChunk, ValueFormat } from '../../../types';
|
|
3
3
|
type Props = {
|
|
4
4
|
hovered: TooltipDataChunk[];
|
|
5
|
+
pinned?: boolean;
|
|
6
|
+
rowRenderer?: ChartTooltip['rowRenderer'];
|
|
5
7
|
totals?: ChartTooltip['totals'];
|
|
6
8
|
valueFormat?: ValueFormat;
|
|
9
|
+
headerFormat?: ChartTooltip['headerFormat'];
|
|
7
10
|
xAxis?: ChartXAxis | null;
|
|
8
11
|
yAxis?: ChartYAxis;
|
|
9
12
|
};
|
|
10
|
-
export declare const DefaultTooltipContent: ({ hovered,
|
|
13
|
+
export declare const DefaultTooltipContent: ({ hovered, pinned, rowRenderer, totals, valueFormat, headerFormat, xAxis, yAxis, }: Props) => React.JSX.Element;
|
|
11
14
|
export {};
|
|
@@ -1,102 +1,202 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Divider } from '@gravity-ui/uikit';
|
|
3
3
|
import get from 'lodash/get';
|
|
4
|
-
import
|
|
4
|
+
import isEqual from 'lodash/isEqual';
|
|
5
|
+
import { usePrevious } from '../../../hooks';
|
|
6
|
+
import { i18n } from '../../../i18n';
|
|
7
|
+
import { block, hasVerticalScrollbar } from '../../../utils';
|
|
5
8
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
6
9
|
import { Row } from './Row';
|
|
7
|
-
import {
|
|
10
|
+
import { RowWithAggregation } from './RowWithAggregation';
|
|
8
11
|
import { getDefaultValueFormat, getHoveredValues, getMeasureValue, getPreparedAggregation, getXRowData, } from './utils';
|
|
9
12
|
const b = block('tooltip');
|
|
10
|
-
export const DefaultTooltipContent = ({ hovered,
|
|
13
|
+
export const DefaultTooltipContent = ({ hovered, pinned, rowRenderer, totals, valueFormat, headerFormat, xAxis, yAxis, }) => {
|
|
14
|
+
var _a;
|
|
15
|
+
const [visibleRows, setVisibleRows] = React.useState();
|
|
16
|
+
const [maxContentRowsHeight, setMaxContentRowsHeight] = React.useState();
|
|
17
|
+
const [scrollBarWidth, setScrollBarWidth] = React.useState(0);
|
|
18
|
+
const contentRowsRef = React.useRef(null);
|
|
11
19
|
const measureValue = getMeasureValue({ data: hovered, xAxis, yAxis });
|
|
12
20
|
const hoveredValues = getHoveredValues({ hovered, xAxis, yAxis });
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const format = valueFormat || getDefaultValueFormat({ axis: xAxis });
|
|
55
|
-
const formattedValue = getFormattedValue({
|
|
56
|
-
value: hoveredValues[i],
|
|
57
|
-
format,
|
|
58
|
-
});
|
|
59
|
-
return (React.createElement(Row, { key: id, active: active, color: color, label: React.createElement("span", { dangerouslySetInnerHTML: { __html: series.name } }), striped: striped, value: formattedValue }));
|
|
60
|
-
}
|
|
61
|
-
case 'pie':
|
|
62
|
-
case 'treemap': {
|
|
63
|
-
const seriesData = data;
|
|
64
|
-
const formattedValue = getFormattedValue({
|
|
65
|
-
value: hoveredValues[i],
|
|
66
|
-
format: valueFormat || { type: 'number' },
|
|
67
|
-
});
|
|
68
|
-
return (React.createElement(Row, { key: id, color: color, label: React.createElement("span", { dangerouslySetInnerHTML: {
|
|
69
|
-
__html: [seriesData.name || seriesData.id]
|
|
70
|
-
.flat()
|
|
71
|
-
.join('\n'),
|
|
72
|
-
} }), value: formattedValue }));
|
|
21
|
+
const prevHoveredValues = usePrevious(hoveredValues);
|
|
22
|
+
const visibleHovered = pinned || !visibleRows ? hovered : hovered.slice(0, visibleRows);
|
|
23
|
+
const restHoveredValues = pinned || !visibleRows ? [] : hoveredValues.slice(visibleRows);
|
|
24
|
+
const renderRow = ({ id, name, color, active, striped, value, formattedValue, }) => {
|
|
25
|
+
if (typeof rowRenderer === 'function') {
|
|
26
|
+
return rowRenderer({
|
|
27
|
+
id,
|
|
28
|
+
name,
|
|
29
|
+
color,
|
|
30
|
+
value,
|
|
31
|
+
formattedValue,
|
|
32
|
+
striped,
|
|
33
|
+
active,
|
|
34
|
+
className: b('content-row', { active, striped }),
|
|
35
|
+
hovered,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return (React.createElement(Row, { key: id, active: active, color: color, label: React.createElement("span", { dangerouslySetInnerHTML: { __html: name } }), striped: striped, value: formattedValue }));
|
|
39
|
+
};
|
|
40
|
+
const formattedHeadValue = headerFormat
|
|
41
|
+
? getFormattedValue({ value: measureValue, format: headerFormat })
|
|
42
|
+
: measureValue;
|
|
43
|
+
React.useEffect(() => {
|
|
44
|
+
if (!contentRowsRef.current) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!hasVerticalScrollbar(contentRowsRef.current)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!isEqual(hoveredValues, prevHoveredValues)) {
|
|
51
|
+
const { clientHeight } = contentRowsRef.current;
|
|
52
|
+
const { top: containerTop } = contentRowsRef.current.getBoundingClientRect();
|
|
53
|
+
const rows = contentRowsRef.current.querySelectorAll(`.${b('content-row')}`);
|
|
54
|
+
let nextVisibleRows = 0;
|
|
55
|
+
let nextMaxContentRowsHeight = 0;
|
|
56
|
+
for (let i = 0; i < rows.length; i++) {
|
|
57
|
+
const row = rows[i];
|
|
58
|
+
const { top, height } = row.getBoundingClientRect();
|
|
59
|
+
if (top - containerTop + height <= clientHeight) {
|
|
60
|
+
nextVisibleRows += 1;
|
|
61
|
+
nextMaxContentRowsHeight += height;
|
|
73
62
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const formattedValue = getFormattedValue({
|
|
77
|
-
value: hoveredValues[i],
|
|
78
|
-
format: valueFormat || { type: 'number' },
|
|
79
|
-
});
|
|
80
|
-
return (React.createElement(Row, { key: id, color: source.color, label: React.createElement("span", null,
|
|
81
|
-
source.name,
|
|
82
|
-
" \u2192 ", target === null || target === void 0 ? void 0 :
|
|
83
|
-
target.name,
|
|
84
|
-
":"), value: formattedValue }));
|
|
85
|
-
}
|
|
86
|
-
case 'radar': {
|
|
87
|
-
const radarSeries = series;
|
|
88
|
-
const formattedValue = getFormattedValue({
|
|
89
|
-
value: hoveredValues[i],
|
|
90
|
-
format: valueFormat || { type: 'number' },
|
|
91
|
-
});
|
|
92
|
-
return (React.createElement(Row, { key: id, active: active, color: color, label: radarSeries.name || radarSeries.id, value: formattedValue }));
|
|
93
|
-
}
|
|
94
|
-
default: {
|
|
95
|
-
return null;
|
|
63
|
+
else {
|
|
64
|
+
break;
|
|
96
65
|
}
|
|
97
66
|
}
|
|
98
|
-
|
|
67
|
+
setVisibleRows(nextVisibleRows - 1);
|
|
68
|
+
setMaxContentRowsHeight(nextMaxContentRowsHeight);
|
|
69
|
+
}
|
|
70
|
+
}, [hoveredValues, prevHoveredValues]);
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
if (!contentRowsRef.current) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (pinned) {
|
|
76
|
+
const { offsetWidth, clientWidth } = contentRowsRef.current;
|
|
77
|
+
setScrollBarWidth(offsetWidth - clientWidth);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
setScrollBarWidth(0);
|
|
81
|
+
}
|
|
82
|
+
}, [pinned]);
|
|
83
|
+
return (React.createElement("div", { className: b('content') },
|
|
84
|
+
formattedHeadValue && (React.createElement("div", { className: b('series-name'), dangerouslySetInnerHTML: { __html: formattedHeadValue } })),
|
|
85
|
+
React.createElement("div", { className: b('content-rows', { pinned }), ref: contentRowsRef, style: { maxHeight: maxContentRowsHeight } },
|
|
86
|
+
visibleHovered.map((seriesItem, i) => {
|
|
87
|
+
var _a;
|
|
88
|
+
const { data, series, closest } = seriesItem;
|
|
89
|
+
const id = `${get(series, 'id')}_${i}`;
|
|
90
|
+
const color = get(data, 'color') || get(series, 'color');
|
|
91
|
+
// TODO: improve action item display https://github.com/gravity-ui/charts/issues/208
|
|
92
|
+
const active = closest && hovered.length > 1;
|
|
93
|
+
const striped = (i + 1) % 2 === 0;
|
|
94
|
+
const rowValueFormat = get(series, 'tooltip.valueFormat', valueFormat);
|
|
95
|
+
switch (series.type) {
|
|
96
|
+
case 'scatter':
|
|
97
|
+
case 'line':
|
|
98
|
+
case 'area':
|
|
99
|
+
case 'bar-x': {
|
|
100
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
101
|
+
const formattedValue = getFormattedValue({
|
|
102
|
+
value: hoveredValues[i],
|
|
103
|
+
format,
|
|
104
|
+
});
|
|
105
|
+
return renderRow({
|
|
106
|
+
id,
|
|
107
|
+
active,
|
|
108
|
+
color,
|
|
109
|
+
name: series.name,
|
|
110
|
+
striped,
|
|
111
|
+
value: hoveredValues[i],
|
|
112
|
+
formattedValue,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
case 'waterfall': {
|
|
116
|
+
const isTotal = get(data, 'total', false);
|
|
117
|
+
const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
|
|
118
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
119
|
+
const subTotal = getFormattedValue({
|
|
120
|
+
value: subTotalValue,
|
|
121
|
+
format,
|
|
122
|
+
});
|
|
123
|
+
const formattedValue = getFormattedValue({
|
|
124
|
+
value: hoveredValues[i],
|
|
125
|
+
format,
|
|
126
|
+
});
|
|
127
|
+
return (React.createElement(React.Fragment, { key: id },
|
|
128
|
+
!isTotal && (React.createElement(React.Fragment, null,
|
|
129
|
+
React.createElement("div", { className: b('series-name') }, getXRowData(data, xAxis)),
|
|
130
|
+
React.createElement(Row, { label: series.name, value: formattedValue }))),
|
|
131
|
+
React.createElement(Row, { label: isTotal ? 'Total' : 'Subtotal', value: subTotal })));
|
|
132
|
+
}
|
|
133
|
+
case 'bar-y': {
|
|
134
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: xAxis });
|
|
135
|
+
const formattedValue = getFormattedValue({
|
|
136
|
+
value: hoveredValues[i],
|
|
137
|
+
format,
|
|
138
|
+
});
|
|
139
|
+
return renderRow({
|
|
140
|
+
id,
|
|
141
|
+
active,
|
|
142
|
+
color,
|
|
143
|
+
name: series.name,
|
|
144
|
+
striped,
|
|
145
|
+
value: hoveredValues[i],
|
|
146
|
+
formattedValue,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
case 'pie':
|
|
150
|
+
case 'treemap': {
|
|
151
|
+
const seriesData = data;
|
|
152
|
+
const formattedValue = getFormattedValue({
|
|
153
|
+
value: hoveredValues[i],
|
|
154
|
+
format: rowValueFormat || { type: 'number' },
|
|
155
|
+
});
|
|
156
|
+
return renderRow({
|
|
157
|
+
id,
|
|
158
|
+
color,
|
|
159
|
+
name: [seriesData.name || seriesData.id].flat().join('\n'),
|
|
160
|
+
value: hoveredValues[i],
|
|
161
|
+
formattedValue,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
case 'sankey': {
|
|
165
|
+
const { target, data: source } = seriesItem;
|
|
166
|
+
const formattedValue = getFormattedValue({
|
|
167
|
+
value: hoveredValues[i],
|
|
168
|
+
format: rowValueFormat || { type: 'number' },
|
|
169
|
+
});
|
|
170
|
+
return renderRow({
|
|
171
|
+
id,
|
|
172
|
+
color,
|
|
173
|
+
name: `${source.name} → ${target === null || target === void 0 ? void 0 : target.name}`,
|
|
174
|
+
value: hoveredValues[i],
|
|
175
|
+
formattedValue,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
case 'radar': {
|
|
179
|
+
const radarSeries = series;
|
|
180
|
+
const formattedValue = getFormattedValue({
|
|
181
|
+
value: hoveredValues[i],
|
|
182
|
+
format: rowValueFormat || { type: 'number' },
|
|
183
|
+
});
|
|
184
|
+
return renderRow({
|
|
185
|
+
id,
|
|
186
|
+
color,
|
|
187
|
+
active,
|
|
188
|
+
name: radarSeries.name || radarSeries.id,
|
|
189
|
+
value: hoveredValues[i],
|
|
190
|
+
formattedValue,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
default: {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}),
|
|
198
|
+
Boolean(restHoveredValues.length) && (React.createElement(Row, { label: i18n('tooltip', 'label_more', { count: restHoveredValues.length }), striped: (visibleHovered.length + 1) % 2 === 0 }))),
|
|
99
199
|
(totals === null || totals === void 0 ? void 0 : totals.enabled) && hovered.length > 1 && (React.createElement(React.Fragment, null,
|
|
100
200
|
React.createElement(Divider, { className: b('content-row-totals-divider') }),
|
|
101
|
-
React.createElement(
|
|
201
|
+
React.createElement(RowWithAggregation, { aggregation: getPreparedAggregation({ hovered, totals, xAxis, yAxis }), label: totals.label, style: { marginRight: scrollBarWidth }, values: hoveredValues, valueFormat: (_a = totals.valueFormat) !== null && _a !== void 0 ? _a : valueFormat })))));
|
|
102
202
|
};
|
|
@@ -23,5 +23,5 @@ export const Tooltip = (props) => {
|
|
|
23
23
|
}, [left, top]);
|
|
24
24
|
return (hovered === null || hovered === void 0 ? void 0 : hovered.length) ? (React.createElement(Popup, { anchorElement: anchor, className: b({ pinned: tooltipPinned }), disableTransition: true, floatingStyles: tooltipPinned ? undefined : { pointerEvents: 'none' }, offset: { mainAxis: 20 }, onOpenChange: tooltipPinned ? handleOnOpenChange : undefined, open: true, placement: ['right', 'left', 'top', 'bottom'] },
|
|
25
25
|
React.createElement("div", { className: b('popup-content') },
|
|
26
|
-
React.createElement(ChartTooltipContent, { hovered: hovered,
|
|
26
|
+
React.createElement(ChartTooltipContent, { hovered: hovered, pinned: tooltipPinned, renderer: tooltip.renderer, rowRenderer: tooltip.rowRenderer, totals: tooltip.totals, valueFormat: tooltip.valueFormat, headerFormat: tooltip.headerFormat, xAxis: xAxis, yAxis: yAxis })))) : null;
|
|
27
27
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
.gcharts-tooltip {
|
|
2
|
+
padding: var(--gcharts-tooltip-content-padding, 8px 0);
|
|
2
3
|
box-shadow: 0 2px 12px var(--g-color-sfx-shadow);
|
|
3
4
|
}
|
|
4
5
|
.gcharts-tooltip__popup-content {
|
|
@@ -7,7 +8,16 @@
|
|
|
7
8
|
background-color: var(--g-color-infographics-tooltip-bg);
|
|
8
9
|
}
|
|
9
10
|
.gcharts-tooltip__content {
|
|
10
|
-
|
|
11
|
+
display: flex;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
max-height: 90vh;
|
|
15
|
+
}
|
|
16
|
+
.gcharts-tooltip__content-rows {
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
}
|
|
19
|
+
.gcharts-tooltip__content-rows_pinned {
|
|
20
|
+
overflow: auto;
|
|
11
21
|
}
|
|
12
22
|
.gcharts-tooltip__series-name {
|
|
13
23
|
padding: 2px 14px 6px;
|
|
@@ -29,6 +29,7 @@ export declare function createXScale(args: {
|
|
|
29
29
|
axis: PreparedAxis | ChartAxis;
|
|
30
30
|
boundsWidth: number;
|
|
31
31
|
series: (PreparedSeries | ChartSeries)[];
|
|
32
|
+
seriesOptions: PreparedSeriesOptions;
|
|
32
33
|
hasZoomX?: boolean;
|
|
33
34
|
}): ScaleBand<string> | ScaleLinear<number, number, never> | ScaleTime<number, number, never>;
|
|
34
35
|
/**
|
|
@@ -4,6 +4,7 @@ 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
6
|
import { getBarYLayoutForNumericScale, groupBarYDataByYValue } from '../utils';
|
|
7
|
+
import { getBarXLayoutForNumericScale, groupBarXDataByXValue } from '../utils/bar-x';
|
|
7
8
|
const X_AXIS_ZOOM_PADDING = 0.02;
|
|
8
9
|
function isNumericalArrayData(data) {
|
|
9
10
|
return data.every((d) => typeof d === 'number' || d === null);
|
|
@@ -130,32 +131,55 @@ function calculateXAxisPadding(series) {
|
|
|
130
131
|
});
|
|
131
132
|
return result;
|
|
132
133
|
}
|
|
134
|
+
function getXScaleRange({ boundsWidth, series, seriesOptions, hasZoomX, axis, maxPadding, }) {
|
|
135
|
+
const xAxisZoomPadding = boundsWidth * X_AXIS_ZOOM_PADDING;
|
|
136
|
+
const xRange = [0, boundsWidth - maxPadding];
|
|
137
|
+
const xRangeZoom = [0 + xAxisZoomPadding, boundsWidth - xAxisZoomPadding];
|
|
138
|
+
const range = hasZoomX ? xRangeZoom : xRange;
|
|
139
|
+
const barXSeries = series.filter((s) => s.type === SeriesType.BarX);
|
|
140
|
+
if (barXSeries.length) {
|
|
141
|
+
const groupedData = groupBarXDataByXValue(barXSeries, axis);
|
|
142
|
+
if (Object.keys(groupedData).length > 1) {
|
|
143
|
+
const { bandSize } = getBarXLayoutForNumericScale({
|
|
144
|
+
plotWidth: boundsWidth - maxPadding,
|
|
145
|
+
groupedData,
|
|
146
|
+
seriesOptions,
|
|
147
|
+
});
|
|
148
|
+
const offset = bandSize / 2;
|
|
149
|
+
return [range[0] + offset, range[1] - offset];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return range;
|
|
153
|
+
}
|
|
133
154
|
// eslint-disable-next-line complexity
|
|
134
155
|
export function createXScale(args) {
|
|
135
|
-
const { axis, boundsWidth, series, hasZoomX } = args;
|
|
156
|
+
const { axis, boundsWidth, series, seriesOptions, hasZoomX } = args;
|
|
136
157
|
const xMinProps = get(axis, 'min');
|
|
137
158
|
const xMaxProps = get(axis, 'max');
|
|
138
159
|
const xType = get(axis, 'type', DEFAULT_AXIS_TYPE);
|
|
139
160
|
const xCategories = get(axis, 'categories');
|
|
140
|
-
const xTimestamps = get(axis, 'timestamps');
|
|
141
161
|
const maxPadding = get(axis, 'maxPadding', 0);
|
|
142
162
|
const xAxisMaxPadding = boundsWidth * maxPadding + calculateXAxisPadding(series);
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
163
|
+
const range = getXScaleRange({
|
|
164
|
+
boundsWidth,
|
|
165
|
+
series,
|
|
166
|
+
seriesOptions,
|
|
167
|
+
hasZoomX,
|
|
168
|
+
axis,
|
|
169
|
+
maxPadding: xAxisMaxPadding,
|
|
170
|
+
});
|
|
146
171
|
switch (axis.order) {
|
|
147
172
|
case 'sortDesc':
|
|
148
173
|
case 'reverse': {
|
|
149
|
-
|
|
150
|
-
xRangeZoom.reverse();
|
|
174
|
+
range.reverse();
|
|
151
175
|
}
|
|
152
176
|
}
|
|
153
177
|
switch (xType) {
|
|
154
178
|
case 'linear':
|
|
155
179
|
case 'logarithmic': {
|
|
156
|
-
const
|
|
157
|
-
if (isNumericalArrayData(
|
|
158
|
-
const [xMinDomain, xMaxDomain] = extent(
|
|
180
|
+
const domainData = getDomainDataXBySeries(series);
|
|
181
|
+
if (isNumericalArrayData(domainData)) {
|
|
182
|
+
const [xMinDomain, xMaxDomain] = extent(domainData);
|
|
159
183
|
let xMin;
|
|
160
184
|
let xMax;
|
|
161
185
|
if (typeof xMinProps === 'number') {
|
|
@@ -176,11 +200,10 @@ export function createXScale(args) {
|
|
|
176
200
|
: xMaxDomain;
|
|
177
201
|
}
|
|
178
202
|
const scaleFn = xType === 'logarithmic' ? scaleLog : scaleLinear;
|
|
179
|
-
const scale = scaleFn()
|
|
180
|
-
.domain([xMin, xMax])
|
|
181
|
-
.range(hasZoomX ? xRangeZoom : xRange);
|
|
203
|
+
const scale = scaleFn().domain([xMin, xMax]).range(range);
|
|
182
204
|
if (!hasZoomX) {
|
|
183
|
-
|
|
205
|
+
// 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
|
|
206
|
+
scale.nice(Math.max(10, domainData.length));
|
|
184
207
|
}
|
|
185
208
|
return scale;
|
|
186
209
|
}
|
|
@@ -195,40 +218,27 @@ export function createXScale(args) {
|
|
|
195
218
|
});
|
|
196
219
|
const xScale = scaleBand().domain(filteredCategories).range([0, boundsWidth]);
|
|
197
220
|
if (xScale.step() / 2 < xAxisMaxPadding) {
|
|
198
|
-
xScale.range(
|
|
221
|
+
xScale.range(range);
|
|
199
222
|
}
|
|
200
223
|
return xScale;
|
|
201
224
|
}
|
|
202
225
|
break;
|
|
203
226
|
}
|
|
204
227
|
case 'datetime': {
|
|
205
|
-
|
|
206
|
-
|
|
228
|
+
let domain = null;
|
|
229
|
+
const domainData = get(axis, 'timestamps') || getDomainDataXBySeries(series);
|
|
230
|
+
if (isNumericalArrayData(domainData)) {
|
|
231
|
+
const [xMinTimestamp, xMaxTimestamp] = extent(domainData);
|
|
207
232
|
const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
|
|
208
233
|
const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.range(hasZoomX ? xRangeZoom : xRange);
|
|
234
|
+
domain = [xMin, xMax];
|
|
235
|
+
const scale = scaleUtc().domain(domain).range(range);
|
|
212
236
|
if (!hasZoomX) {
|
|
213
|
-
|
|
237
|
+
// 10 is the default value for the number of ticks. Here, to preserve the appearance of a series with a small number of points
|
|
238
|
+
scale.nice(Math.max(10, domainData.length));
|
|
214
239
|
}
|
|
215
240
|
return scale;
|
|
216
241
|
}
|
|
217
|
-
else {
|
|
218
|
-
const domain = getDomainDataXBySeries(series);
|
|
219
|
-
if (isNumericalArrayData(domain)) {
|
|
220
|
-
const [xMinTimestamp, xMaxTimestamp] = extent(domain);
|
|
221
|
-
const xMin = typeof xMinProps === 'number' ? xMinProps : xMinTimestamp;
|
|
222
|
-
const xMax = typeof xMaxProps === 'number' ? xMaxProps : xMaxTimestamp;
|
|
223
|
-
const scale = scaleUtc()
|
|
224
|
-
.domain([xMin, xMax])
|
|
225
|
-
.range(hasZoomX ? xRangeZoom : xRange);
|
|
226
|
-
if (!hasZoomX) {
|
|
227
|
-
scale.nice();
|
|
228
|
-
}
|
|
229
|
-
return scale;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
242
|
break;
|
|
233
243
|
}
|
|
234
244
|
}
|
|
@@ -242,7 +252,13 @@ const createScales = (args) => {
|
|
|
242
252
|
visibleSeries = visibleSeries.length === 0 ? series : visibleSeries;
|
|
243
253
|
return {
|
|
244
254
|
xScale: xAxis
|
|
245
|
-
? createXScale({
|
|
255
|
+
? createXScale({
|
|
256
|
+
axis: xAxis,
|
|
257
|
+
boundsWidth,
|
|
258
|
+
series: visibleSeries,
|
|
259
|
+
seriesOptions,
|
|
260
|
+
hasZoomX,
|
|
261
|
+
})
|
|
246
262
|
: undefined,
|
|
247
263
|
yScale: yAxis.map((axis, index) => {
|
|
248
264
|
const axisSeries = series.filter((s) => {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ChartSeries, ChartXAxis } from '../../types';
|
|
2
|
+
import type { PreparedSeriesOptions } from '../useSeries/types';
|
|
2
3
|
import type { PreparedAxis } from './types';
|
|
3
|
-
export declare const getPreparedXAxis: ({ xAxis, seriesData, width, }: {
|
|
4
|
+
export declare const getPreparedXAxis: ({ xAxis, seriesData, seriesOptions, width, }: {
|
|
4
5
|
xAxis?: ChartXAxis;
|
|
5
6
|
seriesData: ChartSeries[];
|
|
7
|
+
seriesOptions: PreparedSeriesOptions;
|
|
6
8
|
width: number;
|
|
7
9
|
}) => Promise<PreparedAxis>;
|