@gravity-ui/charts 1.40.0 → 1.41.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/README.md +4 -24
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/Row.js +4 -4
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/RowWithAggregation.js +3 -2
- package/dist/cjs/components/Tooltip/DefaultTooltipContent/index.js +128 -118
- package/dist/cjs/components/Tooltip/styles.css +33 -7
- package/dist/cjs/components/index.d.ts +4 -1
- package/dist/cjs/components/index.js +8 -3
- package/dist/cjs/hooks/useShapes/bar-x/prepare-data.js +3 -2
- package/dist/cjs/hooks/useShapes/bar-x/types.d.ts +5 -0
- package/dist/cjs/types/chart/tooltip.d.ts +24 -1
- package/dist/esm/components/Tooltip/DefaultTooltipContent/Row.js +4 -4
- package/dist/esm/components/Tooltip/DefaultTooltipContent/RowWithAggregation.js +3 -2
- package/dist/esm/components/Tooltip/DefaultTooltipContent/index.js +128 -118
- package/dist/esm/components/Tooltip/styles.css +33 -7
- package/dist/esm/components/index.d.ts +4 -1
- package/dist/esm/components/index.js +8 -3
- package/dist/esm/hooks/useShapes/bar-x/prepare-data.js +3 -2
- package/dist/esm/hooks/useShapes/bar-x/types.d.ts +5 -0
- package/dist/esm/types/chart/tooltip.d.ts +24 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,31 +1,11 @@
|
|
|
1
|
-
# Gravity UI Charts
|
|
1
|
+
# Gravity UI Charts · [](https://www.npmjs.com/package/@gravity-ui/charts) [](LICENSE) [](https://github.com/gravity-ui/charts/actions/workflows/ci.yml?query=branch:main) [](https://preview.gravity-ui.com/charts/)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/@gravity-ui/charts) [](https://github.com/gravity-ui/charts/actions/workflows/ci.yml?query=branch:main) [](https://preview.gravity-ui.com/charts/)
|
|
3
|
+
React charting library with 10+ chart types: area, bar, line, pie, scatter, treemap, and more.
|
|
6
4
|
|
|
7
5
|
## Documentation
|
|
8
6
|
|
|
9
7
|
- [Overview](https://gravity-ui.github.io/charts/pages/overview.html)
|
|
8
|
+
- [Get started](https://gravity-ui.github.io/charts/pages/get-started.html)
|
|
9
|
+
- [Development](https://gravity-ui.github.io/charts/pages/development.html)
|
|
10
10
|
- [API](https://gravity-ui.github.io/charts/pages/api/overview.html)
|
|
11
11
|
- [Guides](https://gravity-ui.github.io/charts/pages/guides/tooltip.html)
|
|
12
|
-
|
|
13
|
-
## Get started
|
|
14
|
-
|
|
15
|
-
### Install
|
|
16
|
-
|
|
17
|
-
```shell
|
|
18
|
-
npm install @gravity-ui/uikit @gravity-ui/charts
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
### Development
|
|
22
|
-
|
|
23
|
-
To start the development server with storybook run the following:
|
|
24
|
-
|
|
25
|
-
```shell
|
|
26
|
-
npm run start
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Contributing
|
|
30
|
-
|
|
31
|
-
Please refer to the [contributing document](https://github.com/gravity-ui/charts/blob/main/CONTRIBUTING.md) if you wish to make pull requests.
|
|
@@ -12,8 +12,8 @@ export function Row(props) {
|
|
|
12
12
|
}
|
|
13
13
|
return null;
|
|
14
14
|
}, [color, colorSymbol]);
|
|
15
|
-
return (React.createElement("
|
|
16
|
-
colorItem,
|
|
17
|
-
React.createElement("
|
|
18
|
-
value && React.createElement("
|
|
15
|
+
return (React.createElement("tr", { className: b('content-row', { active, striped }, className), style: style },
|
|
16
|
+
colorItem && React.createElement("td", { className: b('content-row-color-cell') }, colorItem),
|
|
17
|
+
React.createElement("td", { className: b('content-row-label-cell') }, label),
|
|
18
|
+
value && React.createElement("td", { className: b('content-row-value-cell') }, value)));
|
|
19
19
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { block } from '../../../utils';
|
|
3
3
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
4
|
-
import { Row } from './Row';
|
|
5
4
|
import { getBuiltInAggregatedValue, getBuiltInAggregationLabel } from './utils';
|
|
6
5
|
const b = block('tooltip');
|
|
7
6
|
export function RowWithAggregation(props) {
|
|
@@ -19,5 +18,7 @@ export function RowWithAggregation(props) {
|
|
|
19
18
|
format: valueFormat || { type: 'number' },
|
|
20
19
|
})
|
|
21
20
|
: resultValue;
|
|
22
|
-
return (React.createElement(
|
|
21
|
+
return (React.createElement("div", { className: b('content-row', { totals: true }), style: style },
|
|
22
|
+
React.createElement("span", { className: b('content-row-totals-label') }, resultLabel),
|
|
23
|
+
formattedResultValue && (React.createElement("span", { className: b('content-row-totals-value') }, formattedResultValue))));
|
|
23
24
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Divider } from '@gravity-ui/uikit';
|
|
3
|
+
import parse from 'html-react-parser';
|
|
3
4
|
import get from 'lodash/get';
|
|
4
5
|
import isEqual from 'lodash/isEqual';
|
|
5
6
|
import { usePrevious } from '../../../hooks';
|
|
@@ -35,7 +36,7 @@ export const DefaultTooltipContent = ({ hovered, pinned, rowRenderer, totals, va
|
|
|
35
36
|
hovered,
|
|
36
37
|
});
|
|
37
38
|
if (typeof result === 'string') {
|
|
38
|
-
return React.createElement(
|
|
39
|
+
return React.createElement(React.Fragment, { key: id }, parse(result));
|
|
39
40
|
}
|
|
40
41
|
return result;
|
|
41
42
|
}
|
|
@@ -75,127 +76,136 @@ export const DefaultTooltipContent = ({ hovered, pinned, rowRenderer, totals, va
|
|
|
75
76
|
setScrollBarWidth(0);
|
|
76
77
|
}
|
|
77
78
|
}, [pinned]);
|
|
79
|
+
const rowsContent = (React.createElement(React.Fragment, null, visibleHovered.map((seriesItem, i) => {
|
|
80
|
+
var _a;
|
|
81
|
+
const { data, series, closest } = seriesItem;
|
|
82
|
+
const id = `${get(series, 'id')}_${i}`;
|
|
83
|
+
const color = get(data, 'color') || get(series, 'color');
|
|
84
|
+
// TODO: improve active item display https://github.com/gravity-ui/charts/issues/208
|
|
85
|
+
const active = closest && hovered.length > 1;
|
|
86
|
+
const striped = (i + 1) % 2 === 0;
|
|
87
|
+
const rowValueFormat = get(series, 'tooltip.valueFormat', valueFormat);
|
|
88
|
+
switch (series.type) {
|
|
89
|
+
case 'scatter':
|
|
90
|
+
case 'line':
|
|
91
|
+
case 'area':
|
|
92
|
+
case 'bar-x': {
|
|
93
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
94
|
+
const formattedValue = getFormattedValue({
|
|
95
|
+
value: hoveredValues[i],
|
|
96
|
+
format,
|
|
97
|
+
});
|
|
98
|
+
return renderRow({
|
|
99
|
+
id,
|
|
100
|
+
active,
|
|
101
|
+
color,
|
|
102
|
+
name: series.name,
|
|
103
|
+
striped,
|
|
104
|
+
value: hoveredValues[i],
|
|
105
|
+
formattedValue,
|
|
106
|
+
series,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
case 'waterfall': {
|
|
110
|
+
const isTotal = get(data, 'total', false);
|
|
111
|
+
const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
|
|
112
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
113
|
+
const subTotal = getFormattedValue({
|
|
114
|
+
value: subTotalValue,
|
|
115
|
+
format,
|
|
116
|
+
});
|
|
117
|
+
const formattedValue = getFormattedValue({
|
|
118
|
+
value: hoveredValues[i],
|
|
119
|
+
format,
|
|
120
|
+
});
|
|
121
|
+
return (React.createElement(React.Fragment, { key: id },
|
|
122
|
+
!isTotal && (React.createElement(React.Fragment, null,
|
|
123
|
+
React.createElement("div", { className: b('series-name') }, getXRowData(data, xAxis)),
|
|
124
|
+
React.createElement(Row, { label: series.name, value: formattedValue }))),
|
|
125
|
+
React.createElement(Row, { label: isTotal ? 'Total' : 'Subtotal', value: subTotal })));
|
|
126
|
+
}
|
|
127
|
+
case 'bar-y': {
|
|
128
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: xAxis });
|
|
129
|
+
const formattedValue = getFormattedValue({
|
|
130
|
+
value: hoveredValues[i],
|
|
131
|
+
format,
|
|
132
|
+
});
|
|
133
|
+
return renderRow({
|
|
134
|
+
id,
|
|
135
|
+
active,
|
|
136
|
+
color,
|
|
137
|
+
name: series.name,
|
|
138
|
+
striped,
|
|
139
|
+
value: hoveredValues[i],
|
|
140
|
+
formattedValue,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
case 'pie':
|
|
144
|
+
case 'heatmap':
|
|
145
|
+
case 'treemap':
|
|
146
|
+
case 'funnel': {
|
|
147
|
+
const seriesData = data;
|
|
148
|
+
const formattedValue = getFormattedValue({
|
|
149
|
+
value: hoveredValues[i],
|
|
150
|
+
format: rowValueFormat || { type: 'number' },
|
|
151
|
+
});
|
|
152
|
+
return renderRow({
|
|
153
|
+
id,
|
|
154
|
+
color,
|
|
155
|
+
name: [seriesData.name || seriesData.id].flat().join('\n'),
|
|
156
|
+
value: hoveredValues[i],
|
|
157
|
+
formattedValue,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
case 'sankey': {
|
|
161
|
+
const { target, data: source } = seriesItem;
|
|
162
|
+
const formattedValue = getFormattedValue({
|
|
163
|
+
value: hoveredValues[i],
|
|
164
|
+
format: rowValueFormat || { type: 'number' },
|
|
165
|
+
});
|
|
166
|
+
return renderRow({
|
|
167
|
+
id,
|
|
168
|
+
color,
|
|
169
|
+
name: `${source.name} → ${target === null || target === void 0 ? void 0 : target.name}`,
|
|
170
|
+
value: hoveredValues[i],
|
|
171
|
+
formattedValue,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
case 'radar': {
|
|
175
|
+
const radarSeries = series;
|
|
176
|
+
const formattedValue = getFormattedValue({
|
|
177
|
+
value: hoveredValues[i],
|
|
178
|
+
format: rowValueFormat || { type: 'number' },
|
|
179
|
+
});
|
|
180
|
+
return renderRow({
|
|
181
|
+
id,
|
|
182
|
+
color,
|
|
183
|
+
active,
|
|
184
|
+
name: radarSeries.name || radarSeries.id,
|
|
185
|
+
value: hoveredValues[i],
|
|
186
|
+
formattedValue,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
default: {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
})));
|
|
78
194
|
return (React.createElement("div", { className: b('content'), "data-qa": qa },
|
|
79
195
|
formattedHeadValue && (React.createElement("div", { className: b('series-name') },
|
|
80
196
|
React.createElement("div", { className: b('series-name-text'), dangerouslySetInnerHTML: { __html: formattedHeadValue } }))),
|
|
81
197
|
React.createElement("div", { className: b('content-rows', { pinned }), ref: contentRowsRef, style: pinned ? { maxHeight: maxContentRowsHeight } : undefined },
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// TODO: improve action item display https://github.com/gravity-ui/charts/issues/208
|
|
88
|
-
const active = closest && hovered.length > 1;
|
|
89
|
-
const striped = (i + 1) % 2 === 0;
|
|
90
|
-
const rowValueFormat = get(series, 'tooltip.valueFormat', valueFormat);
|
|
91
|
-
switch (series.type) {
|
|
92
|
-
case 'scatter':
|
|
93
|
-
case 'line':
|
|
94
|
-
case 'area':
|
|
95
|
-
case 'bar-x': {
|
|
96
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
97
|
-
const formattedValue = getFormattedValue({
|
|
98
|
-
value: hoveredValues[i],
|
|
99
|
-
format,
|
|
100
|
-
});
|
|
101
|
-
return renderRow({
|
|
102
|
-
id,
|
|
103
|
-
active,
|
|
104
|
-
color,
|
|
105
|
-
name: series.name,
|
|
106
|
-
striped,
|
|
107
|
-
value: hoveredValues[i],
|
|
108
|
-
formattedValue,
|
|
109
|
-
series,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
case 'waterfall': {
|
|
113
|
-
const isTotal = get(data, 'total', false);
|
|
114
|
-
const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
|
|
115
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
116
|
-
const subTotal = getFormattedValue({
|
|
117
|
-
value: subTotalValue,
|
|
118
|
-
format,
|
|
119
|
-
});
|
|
120
|
-
const formattedValue = getFormattedValue({
|
|
121
|
-
value: hoveredValues[i],
|
|
122
|
-
format,
|
|
123
|
-
});
|
|
124
|
-
return (React.createElement(React.Fragment, { key: id },
|
|
125
|
-
!isTotal && (React.createElement(React.Fragment, null,
|
|
126
|
-
React.createElement("div", { className: b('series-name') }, getXRowData(data, xAxis)),
|
|
127
|
-
React.createElement(Row, { label: series.name, value: formattedValue }))),
|
|
128
|
-
React.createElement(Row, { label: isTotal ? 'Total' : 'Subtotal', value: subTotal })));
|
|
129
|
-
}
|
|
130
|
-
case 'bar-y': {
|
|
131
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: xAxis });
|
|
132
|
-
const formattedValue = getFormattedValue({
|
|
133
|
-
value: hoveredValues[i],
|
|
134
|
-
format,
|
|
135
|
-
});
|
|
136
|
-
return renderRow({
|
|
137
|
-
id,
|
|
138
|
-
active,
|
|
139
|
-
color,
|
|
140
|
-
name: series.name,
|
|
141
|
-
striped,
|
|
142
|
-
value: hoveredValues[i],
|
|
143
|
-
formattedValue,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
case 'pie':
|
|
147
|
-
case 'heatmap':
|
|
148
|
-
case 'treemap':
|
|
149
|
-
case 'funnel': {
|
|
150
|
-
const seriesData = data;
|
|
151
|
-
const formattedValue = getFormattedValue({
|
|
152
|
-
value: hoveredValues[i],
|
|
153
|
-
format: rowValueFormat || { type: 'number' },
|
|
154
|
-
});
|
|
155
|
-
return renderRow({
|
|
156
|
-
id,
|
|
157
|
-
color,
|
|
158
|
-
name: [seriesData.name || seriesData.id].flat().join('\n'),
|
|
159
|
-
value: hoveredValues[i],
|
|
160
|
-
formattedValue,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
case 'sankey': {
|
|
164
|
-
const { target, data: source } = seriesItem;
|
|
165
|
-
const formattedValue = getFormattedValue({
|
|
166
|
-
value: hoveredValues[i],
|
|
167
|
-
format: rowValueFormat || { type: 'number' },
|
|
168
|
-
});
|
|
169
|
-
return renderRow({
|
|
170
|
-
id,
|
|
171
|
-
color,
|
|
172
|
-
name: `${source.name} → ${target === null || target === void 0 ? void 0 : target.name}`,
|
|
173
|
-
value: hoveredValues[i],
|
|
174
|
-
formattedValue,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
case 'radar': {
|
|
178
|
-
const radarSeries = series;
|
|
179
|
-
const formattedValue = getFormattedValue({
|
|
180
|
-
value: hoveredValues[i],
|
|
181
|
-
format: rowValueFormat || { type: 'number' },
|
|
182
|
-
});
|
|
183
|
-
return renderRow({
|
|
184
|
-
id,
|
|
185
|
-
color,
|
|
186
|
-
active,
|
|
187
|
-
name: radarSeries.name || radarSeries.id,
|
|
188
|
-
value: hoveredValues[i],
|
|
189
|
-
formattedValue,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
default: {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}),
|
|
197
|
-
Boolean(restHoveredValues.length) && (React.createElement(Row, { label: i18n('tooltip', 'label_more', { count: restHoveredValues.length }), striped: (visibleHovered.length + 1) % 2 === 0 }))),
|
|
198
|
+
React.createElement("table", { className: b('content-rows-table') },
|
|
199
|
+
React.createElement("tbody", null, rowsContent))),
|
|
200
|
+
Boolean(restHoveredValues.length) && (React.createElement("div", { className: b('content-row', {
|
|
201
|
+
striped: (visibleHovered.length + 1) % 2 === 0,
|
|
202
|
+
}) }, i18n('tooltip', 'label_more', { count: restHoveredValues.length }))),
|
|
198
203
|
(totals === null || totals === void 0 ? void 0 : totals.enabled) && hovered.length > 1 && (React.createElement(React.Fragment, null,
|
|
199
204
|
React.createElement(Divider, { className: b('content-row-totals-divider') }),
|
|
200
|
-
React.createElement(RowWithAggregation, { aggregation: getPreparedAggregation({
|
|
205
|
+
React.createElement(RowWithAggregation, { aggregation: getPreparedAggregation({
|
|
206
|
+
hovered,
|
|
207
|
+
totals,
|
|
208
|
+
xAxis,
|
|
209
|
+
yAxis,
|
|
210
|
+
}), label: totals.label, style: { marginRight: scrollBarWidth }, values: hoveredValues, valueFormat: (_a = totals.valueFormat) !== null && _a !== void 0 ? _a : valueFormat })))));
|
|
201
211
|
};
|
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
background-color: var(--g-color-infographics-tooltip-bg);
|
|
4
4
|
box-shadow: 0 2px 12px var(--g-color-sfx-shadow);
|
|
5
5
|
}
|
|
6
|
+
tr.gcharts-tooltip__content-row {
|
|
7
|
+
display: table-row;
|
|
8
|
+
padding: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
.gcharts-tooltip__popup-content {
|
|
7
|
-
max-width: 450px;
|
|
8
12
|
text-wrap: nowrap;
|
|
9
13
|
border-radius: 4px;
|
|
10
14
|
}
|
|
@@ -20,7 +24,13 @@
|
|
|
20
24
|
.gcharts-tooltip__content-rows_pinned {
|
|
21
25
|
overflow: auto;
|
|
22
26
|
}
|
|
27
|
+
.gcharts-tooltip__content-rows-table {
|
|
28
|
+
width: 100%;
|
|
29
|
+
padding: 0;
|
|
30
|
+
border-collapse: collapse;
|
|
31
|
+
}
|
|
23
32
|
.gcharts-tooltip__series-name {
|
|
33
|
+
max-width: 450px;
|
|
24
34
|
padding: 2px 14px 6px;
|
|
25
35
|
font-size: 13px;
|
|
26
36
|
font-weight: 600;
|
|
@@ -45,8 +55,23 @@
|
|
|
45
55
|
font-weight: 600;
|
|
46
56
|
background-color: var(--g-color-base-info-medium);
|
|
47
57
|
}
|
|
48
|
-
.gcharts-tooltip__content-
|
|
58
|
+
.gcharts-tooltip__content-row_totals {
|
|
59
|
+
color: var(--g-color-text-complementary);
|
|
60
|
+
}
|
|
61
|
+
.gcharts-tooltip__content-row-totals-label {
|
|
49
62
|
overflow: hidden;
|
|
63
|
+
max-width: 400px;
|
|
64
|
+
white-space: nowrap;
|
|
65
|
+
text-overflow: ellipsis;
|
|
66
|
+
}
|
|
67
|
+
.gcharts-tooltip__content-row-totals-value {
|
|
68
|
+
flex-shrink: 0;
|
|
69
|
+
margin-inline-start: auto;
|
|
70
|
+
}
|
|
71
|
+
.gcharts-tooltip__content-row-label-cell {
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
max-width: 400px;
|
|
74
|
+
padding: 2px 4px;
|
|
50
75
|
white-space: nowrap;
|
|
51
76
|
text-overflow: ellipsis;
|
|
52
77
|
}
|
|
@@ -58,12 +83,13 @@
|
|
|
58
83
|
border-radius: 2px;
|
|
59
84
|
background-color: #dddddd;
|
|
60
85
|
}
|
|
61
|
-
.gcharts-tooltip__content-row-
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
.gcharts-tooltip__content-row-color-cell {
|
|
87
|
+
width: 16px;
|
|
88
|
+
padding: 2px 4px 2px 14px;
|
|
64
89
|
}
|
|
65
|
-
.gcharts-tooltip__content-row-
|
|
66
|
-
|
|
90
|
+
.gcharts-tooltip__content-row-value-cell {
|
|
91
|
+
padding: 2px 14px 2px 4px;
|
|
92
|
+
text-align: end;
|
|
67
93
|
}
|
|
68
94
|
.gcharts-tooltip__content-row-totals-divider {
|
|
69
95
|
margin-block: 5px 5px;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ChartData } from '../types';
|
|
3
3
|
export * from './Tooltip/ChartTooltipContent';
|
|
4
|
+
export interface ChartReflowOptions {
|
|
5
|
+
immediate?: boolean;
|
|
6
|
+
}
|
|
4
7
|
export interface ChartRef {
|
|
5
|
-
reflow: () => void;
|
|
8
|
+
reflow: (options?: ChartReflowOptions) => void;
|
|
6
9
|
}
|
|
7
10
|
export interface ChartDimentions {
|
|
8
11
|
height: number;
|
|
@@ -30,10 +30,15 @@ export const Chart = React.forwardRef(function Chart(props, forwardedRef) {
|
|
|
30
30
|
return debounced.current;
|
|
31
31
|
}, [handleResize]);
|
|
32
32
|
React.useImperativeHandle(forwardedRef, () => ({
|
|
33
|
-
reflow() {
|
|
34
|
-
|
|
33
|
+
reflow(options) {
|
|
34
|
+
if (options === null || options === void 0 ? void 0 : options.immediate) {
|
|
35
|
+
handleResize();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
debuncedHandleResize();
|
|
39
|
+
}
|
|
35
40
|
},
|
|
36
|
-
}), [debuncedHandleResize]);
|
|
41
|
+
}), [debuncedHandleResize, handleResize]);
|
|
37
42
|
React.useEffect(() => {
|
|
38
43
|
// dimensions initialize
|
|
39
44
|
handleResize();
|
|
@@ -162,6 +162,7 @@ export const prepareBarXData = async (args) => {
|
|
|
162
162
|
: yAxisTop + base + negativeStackHeight,
|
|
163
163
|
width: rectWidth,
|
|
164
164
|
height: shapeHeight,
|
|
165
|
+
_height: height,
|
|
165
166
|
opacity: get(yValue.data, 'opacity', null),
|
|
166
167
|
data: yValue.data,
|
|
167
168
|
series: yValue.series,
|
|
@@ -178,9 +179,9 @@ export const prepareBarXData = async (args) => {
|
|
|
178
179
|
}
|
|
179
180
|
if (series.some((s) => s.stacking === 'percent')) {
|
|
180
181
|
let acc = 0;
|
|
181
|
-
const ratio = plotHeight /
|
|
182
|
+
const ratio = plotHeight / positiveStackHeight;
|
|
182
183
|
stackItems.forEach((item) => {
|
|
183
|
-
item.height = item.
|
|
184
|
+
item.height = item._height * ratio;
|
|
184
185
|
item.y = plotHeight - item.height - acc;
|
|
185
186
|
acc += item.height + 1;
|
|
186
187
|
});
|
|
@@ -10,4 +10,9 @@ export type PreparedBarXData = Omit<TooltipDataChunkBarX, 'series'> & {
|
|
|
10
10
|
label?: LabelData;
|
|
11
11
|
htmlElements: HtmlItem[];
|
|
12
12
|
isLastStackItem: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* the utility field for storing the original height (for recalculations, etc.)
|
|
15
|
+
* should not be used for displaying
|
|
16
|
+
*/
|
|
17
|
+
_height: number;
|
|
13
18
|
};
|
|
@@ -113,6 +113,10 @@ export type ChartTooltipRowRendererArgs = {
|
|
|
113
113
|
value: string | number | null | undefined;
|
|
114
114
|
formattedValue?: string;
|
|
115
115
|
hovered?: TooltipDataChunk<unknown>[];
|
|
116
|
+
/**
|
|
117
|
+
* CSS class name pre-built with active/striped modifiers.
|
|
118
|
+
* Apply it to the root `<tr>` element of the returned row: `<tr className={className}>`.
|
|
119
|
+
*/
|
|
116
120
|
className?: string;
|
|
117
121
|
};
|
|
118
122
|
export type ChartTooltipSortComparator<T = MeaningfulAny> = (a: TooltipDataChunk<T>, b: TooltipDataChunk<T>) => number;
|
|
@@ -123,7 +127,26 @@ export interface ChartTooltip<T = MeaningfulAny> {
|
|
|
123
127
|
/**
|
|
124
128
|
* Defines the way a single data/series is displayed (corresponding to a separate selected point/ruler/shape on the chart).
|
|
125
129
|
* It is useful in cases where you need to display additional information, but keep the general format of the tooltip.
|
|
126
|
-
*
|
|
130
|
+
*
|
|
131
|
+
* The returned React element must be a `<tr>` so that it fits into the table layout used by the tooltip.
|
|
132
|
+
* Apply the `className` arg to the root `<tr>` to get the correct active/striped styles.
|
|
133
|
+
*
|
|
134
|
+
* If a string is returned, it will be parsed as HTML and rendered as-is — the string must be a complete
|
|
135
|
+
* `<tr>...</tr>` element.
|
|
136
|
+
* @example React element
|
|
137
|
+
* ```tsx
|
|
138
|
+
* rowRenderer: ({id, name, value, className}) => (
|
|
139
|
+
* <tr key={id} className={className}>
|
|
140
|
+
* <td>{name}</td>
|
|
141
|
+
* <td>{value}</td>
|
|
142
|
+
* </tr>
|
|
143
|
+
* )
|
|
144
|
+
* ```
|
|
145
|
+
* @example Raw HTML string
|
|
146
|
+
* ```ts
|
|
147
|
+
* rowRenderer: ({name, value, className}) =>
|
|
148
|
+
* `<tr class="${className}"><td>${name}</td><td>${value}</td></tr>`
|
|
149
|
+
* ```
|
|
127
150
|
*/
|
|
128
151
|
rowRenderer?: ((args: ChartTooltipRowRendererArgs) => React.ReactElement | string) | null;
|
|
129
152
|
pin?: {
|
|
@@ -12,8 +12,8 @@ export function Row(props) {
|
|
|
12
12
|
}
|
|
13
13
|
return null;
|
|
14
14
|
}, [color, colorSymbol]);
|
|
15
|
-
return (React.createElement("
|
|
16
|
-
colorItem,
|
|
17
|
-
React.createElement("
|
|
18
|
-
value && React.createElement("
|
|
15
|
+
return (React.createElement("tr", { className: b('content-row', { active, striped }, className), style: style },
|
|
16
|
+
colorItem && React.createElement("td", { className: b('content-row-color-cell') }, colorItem),
|
|
17
|
+
React.createElement("td", { className: b('content-row-label-cell') }, label),
|
|
18
|
+
value && React.createElement("td", { className: b('content-row-value-cell') }, value)));
|
|
19
19
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { block } from '../../../utils';
|
|
3
3
|
import { getFormattedValue } from '../../../utils/chart/format';
|
|
4
|
-
import { Row } from './Row';
|
|
5
4
|
import { getBuiltInAggregatedValue, getBuiltInAggregationLabel } from './utils';
|
|
6
5
|
const b = block('tooltip');
|
|
7
6
|
export function RowWithAggregation(props) {
|
|
@@ -19,5 +18,7 @@ export function RowWithAggregation(props) {
|
|
|
19
18
|
format: valueFormat || { type: 'number' },
|
|
20
19
|
})
|
|
21
20
|
: resultValue;
|
|
22
|
-
return (React.createElement(
|
|
21
|
+
return (React.createElement("div", { className: b('content-row', { totals: true }), style: style },
|
|
22
|
+
React.createElement("span", { className: b('content-row-totals-label') }, resultLabel),
|
|
23
|
+
formattedResultValue && (React.createElement("span", { className: b('content-row-totals-value') }, formattedResultValue))));
|
|
23
24
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Divider } from '@gravity-ui/uikit';
|
|
3
|
+
import parse from 'html-react-parser';
|
|
3
4
|
import get from 'lodash/get';
|
|
4
5
|
import isEqual from 'lodash/isEqual';
|
|
5
6
|
import { usePrevious } from '../../../hooks';
|
|
@@ -35,7 +36,7 @@ export const DefaultTooltipContent = ({ hovered, pinned, rowRenderer, totals, va
|
|
|
35
36
|
hovered,
|
|
36
37
|
});
|
|
37
38
|
if (typeof result === 'string') {
|
|
38
|
-
return React.createElement(
|
|
39
|
+
return React.createElement(React.Fragment, { key: id }, parse(result));
|
|
39
40
|
}
|
|
40
41
|
return result;
|
|
41
42
|
}
|
|
@@ -75,127 +76,136 @@ export const DefaultTooltipContent = ({ hovered, pinned, rowRenderer, totals, va
|
|
|
75
76
|
setScrollBarWidth(0);
|
|
76
77
|
}
|
|
77
78
|
}, [pinned]);
|
|
79
|
+
const rowsContent = (React.createElement(React.Fragment, null, visibleHovered.map((seriesItem, i) => {
|
|
80
|
+
var _a;
|
|
81
|
+
const { data, series, closest } = seriesItem;
|
|
82
|
+
const id = `${get(series, 'id')}_${i}`;
|
|
83
|
+
const color = get(data, 'color') || get(series, 'color');
|
|
84
|
+
// TODO: improve active item display https://github.com/gravity-ui/charts/issues/208
|
|
85
|
+
const active = closest && hovered.length > 1;
|
|
86
|
+
const striped = (i + 1) % 2 === 0;
|
|
87
|
+
const rowValueFormat = get(series, 'tooltip.valueFormat', valueFormat);
|
|
88
|
+
switch (series.type) {
|
|
89
|
+
case 'scatter':
|
|
90
|
+
case 'line':
|
|
91
|
+
case 'area':
|
|
92
|
+
case 'bar-x': {
|
|
93
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
94
|
+
const formattedValue = getFormattedValue({
|
|
95
|
+
value: hoveredValues[i],
|
|
96
|
+
format,
|
|
97
|
+
});
|
|
98
|
+
return renderRow({
|
|
99
|
+
id,
|
|
100
|
+
active,
|
|
101
|
+
color,
|
|
102
|
+
name: series.name,
|
|
103
|
+
striped,
|
|
104
|
+
value: hoveredValues[i],
|
|
105
|
+
formattedValue,
|
|
106
|
+
series,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
case 'waterfall': {
|
|
110
|
+
const isTotal = get(data, 'total', false);
|
|
111
|
+
const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
|
|
112
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
113
|
+
const subTotal = getFormattedValue({
|
|
114
|
+
value: subTotalValue,
|
|
115
|
+
format,
|
|
116
|
+
});
|
|
117
|
+
const formattedValue = getFormattedValue({
|
|
118
|
+
value: hoveredValues[i],
|
|
119
|
+
format,
|
|
120
|
+
});
|
|
121
|
+
return (React.createElement(React.Fragment, { key: id },
|
|
122
|
+
!isTotal && (React.createElement(React.Fragment, null,
|
|
123
|
+
React.createElement("div", { className: b('series-name') }, getXRowData(data, xAxis)),
|
|
124
|
+
React.createElement(Row, { label: series.name, value: formattedValue }))),
|
|
125
|
+
React.createElement(Row, { label: isTotal ? 'Total' : 'Subtotal', value: subTotal })));
|
|
126
|
+
}
|
|
127
|
+
case 'bar-y': {
|
|
128
|
+
const format = rowValueFormat || getDefaultValueFormat({ axis: xAxis });
|
|
129
|
+
const formattedValue = getFormattedValue({
|
|
130
|
+
value: hoveredValues[i],
|
|
131
|
+
format,
|
|
132
|
+
});
|
|
133
|
+
return renderRow({
|
|
134
|
+
id,
|
|
135
|
+
active,
|
|
136
|
+
color,
|
|
137
|
+
name: series.name,
|
|
138
|
+
striped,
|
|
139
|
+
value: hoveredValues[i],
|
|
140
|
+
formattedValue,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
case 'pie':
|
|
144
|
+
case 'heatmap':
|
|
145
|
+
case 'treemap':
|
|
146
|
+
case 'funnel': {
|
|
147
|
+
const seriesData = data;
|
|
148
|
+
const formattedValue = getFormattedValue({
|
|
149
|
+
value: hoveredValues[i],
|
|
150
|
+
format: rowValueFormat || { type: 'number' },
|
|
151
|
+
});
|
|
152
|
+
return renderRow({
|
|
153
|
+
id,
|
|
154
|
+
color,
|
|
155
|
+
name: [seriesData.name || seriesData.id].flat().join('\n'),
|
|
156
|
+
value: hoveredValues[i],
|
|
157
|
+
formattedValue,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
case 'sankey': {
|
|
161
|
+
const { target, data: source } = seriesItem;
|
|
162
|
+
const formattedValue = getFormattedValue({
|
|
163
|
+
value: hoveredValues[i],
|
|
164
|
+
format: rowValueFormat || { type: 'number' },
|
|
165
|
+
});
|
|
166
|
+
return renderRow({
|
|
167
|
+
id,
|
|
168
|
+
color,
|
|
169
|
+
name: `${source.name} → ${target === null || target === void 0 ? void 0 : target.name}`,
|
|
170
|
+
value: hoveredValues[i],
|
|
171
|
+
formattedValue,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
case 'radar': {
|
|
175
|
+
const radarSeries = series;
|
|
176
|
+
const formattedValue = getFormattedValue({
|
|
177
|
+
value: hoveredValues[i],
|
|
178
|
+
format: rowValueFormat || { type: 'number' },
|
|
179
|
+
});
|
|
180
|
+
return renderRow({
|
|
181
|
+
id,
|
|
182
|
+
color,
|
|
183
|
+
active,
|
|
184
|
+
name: radarSeries.name || radarSeries.id,
|
|
185
|
+
value: hoveredValues[i],
|
|
186
|
+
formattedValue,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
default: {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
})));
|
|
78
194
|
return (React.createElement("div", { className: b('content'), "data-qa": qa },
|
|
79
195
|
formattedHeadValue && (React.createElement("div", { className: b('series-name') },
|
|
80
196
|
React.createElement("div", { className: b('series-name-text'), dangerouslySetInnerHTML: { __html: formattedHeadValue } }))),
|
|
81
197
|
React.createElement("div", { className: b('content-rows', { pinned }), ref: contentRowsRef, style: pinned ? { maxHeight: maxContentRowsHeight } : undefined },
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
// TODO: improve action item display https://github.com/gravity-ui/charts/issues/208
|
|
88
|
-
const active = closest && hovered.length > 1;
|
|
89
|
-
const striped = (i + 1) % 2 === 0;
|
|
90
|
-
const rowValueFormat = get(series, 'tooltip.valueFormat', valueFormat);
|
|
91
|
-
switch (series.type) {
|
|
92
|
-
case 'scatter':
|
|
93
|
-
case 'line':
|
|
94
|
-
case 'area':
|
|
95
|
-
case 'bar-x': {
|
|
96
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
97
|
-
const formattedValue = getFormattedValue({
|
|
98
|
-
value: hoveredValues[i],
|
|
99
|
-
format,
|
|
100
|
-
});
|
|
101
|
-
return renderRow({
|
|
102
|
-
id,
|
|
103
|
-
active,
|
|
104
|
-
color,
|
|
105
|
-
name: series.name,
|
|
106
|
-
striped,
|
|
107
|
-
value: hoveredValues[i],
|
|
108
|
-
formattedValue,
|
|
109
|
-
series,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
case 'waterfall': {
|
|
113
|
-
const isTotal = get(data, 'total', false);
|
|
114
|
-
const subTotalValue = (_a = seriesItem.subTotal) !== null && _a !== void 0 ? _a : 0;
|
|
115
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: yAxis });
|
|
116
|
-
const subTotal = getFormattedValue({
|
|
117
|
-
value: subTotalValue,
|
|
118
|
-
format,
|
|
119
|
-
});
|
|
120
|
-
const formattedValue = getFormattedValue({
|
|
121
|
-
value: hoveredValues[i],
|
|
122
|
-
format,
|
|
123
|
-
});
|
|
124
|
-
return (React.createElement(React.Fragment, { key: id },
|
|
125
|
-
!isTotal && (React.createElement(React.Fragment, null,
|
|
126
|
-
React.createElement("div", { className: b('series-name') }, getXRowData(data, xAxis)),
|
|
127
|
-
React.createElement(Row, { label: series.name, value: formattedValue }))),
|
|
128
|
-
React.createElement(Row, { label: isTotal ? 'Total' : 'Subtotal', value: subTotal })));
|
|
129
|
-
}
|
|
130
|
-
case 'bar-y': {
|
|
131
|
-
const format = rowValueFormat || getDefaultValueFormat({ axis: xAxis });
|
|
132
|
-
const formattedValue = getFormattedValue({
|
|
133
|
-
value: hoveredValues[i],
|
|
134
|
-
format,
|
|
135
|
-
});
|
|
136
|
-
return renderRow({
|
|
137
|
-
id,
|
|
138
|
-
active,
|
|
139
|
-
color,
|
|
140
|
-
name: series.name,
|
|
141
|
-
striped,
|
|
142
|
-
value: hoveredValues[i],
|
|
143
|
-
formattedValue,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
case 'pie':
|
|
147
|
-
case 'heatmap':
|
|
148
|
-
case 'treemap':
|
|
149
|
-
case 'funnel': {
|
|
150
|
-
const seriesData = data;
|
|
151
|
-
const formattedValue = getFormattedValue({
|
|
152
|
-
value: hoveredValues[i],
|
|
153
|
-
format: rowValueFormat || { type: 'number' },
|
|
154
|
-
});
|
|
155
|
-
return renderRow({
|
|
156
|
-
id,
|
|
157
|
-
color,
|
|
158
|
-
name: [seriesData.name || seriesData.id].flat().join('\n'),
|
|
159
|
-
value: hoveredValues[i],
|
|
160
|
-
formattedValue,
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
case 'sankey': {
|
|
164
|
-
const { target, data: source } = seriesItem;
|
|
165
|
-
const formattedValue = getFormattedValue({
|
|
166
|
-
value: hoveredValues[i],
|
|
167
|
-
format: rowValueFormat || { type: 'number' },
|
|
168
|
-
});
|
|
169
|
-
return renderRow({
|
|
170
|
-
id,
|
|
171
|
-
color,
|
|
172
|
-
name: `${source.name} → ${target === null || target === void 0 ? void 0 : target.name}`,
|
|
173
|
-
value: hoveredValues[i],
|
|
174
|
-
formattedValue,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
case 'radar': {
|
|
178
|
-
const radarSeries = series;
|
|
179
|
-
const formattedValue = getFormattedValue({
|
|
180
|
-
value: hoveredValues[i],
|
|
181
|
-
format: rowValueFormat || { type: 'number' },
|
|
182
|
-
});
|
|
183
|
-
return renderRow({
|
|
184
|
-
id,
|
|
185
|
-
color,
|
|
186
|
-
active,
|
|
187
|
-
name: radarSeries.name || radarSeries.id,
|
|
188
|
-
value: hoveredValues[i],
|
|
189
|
-
formattedValue,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
default: {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}),
|
|
197
|
-
Boolean(restHoveredValues.length) && (React.createElement(Row, { label: i18n('tooltip', 'label_more', { count: restHoveredValues.length }), striped: (visibleHovered.length + 1) % 2 === 0 }))),
|
|
198
|
+
React.createElement("table", { className: b('content-rows-table') },
|
|
199
|
+
React.createElement("tbody", null, rowsContent))),
|
|
200
|
+
Boolean(restHoveredValues.length) && (React.createElement("div", { className: b('content-row', {
|
|
201
|
+
striped: (visibleHovered.length + 1) % 2 === 0,
|
|
202
|
+
}) }, i18n('tooltip', 'label_more', { count: restHoveredValues.length }))),
|
|
198
203
|
(totals === null || totals === void 0 ? void 0 : totals.enabled) && hovered.length > 1 && (React.createElement(React.Fragment, null,
|
|
199
204
|
React.createElement(Divider, { className: b('content-row-totals-divider') }),
|
|
200
|
-
React.createElement(RowWithAggregation, { aggregation: getPreparedAggregation({
|
|
205
|
+
React.createElement(RowWithAggregation, { aggregation: getPreparedAggregation({
|
|
206
|
+
hovered,
|
|
207
|
+
totals,
|
|
208
|
+
xAxis,
|
|
209
|
+
yAxis,
|
|
210
|
+
}), label: totals.label, style: { marginRight: scrollBarWidth }, values: hoveredValues, valueFormat: (_a = totals.valueFormat) !== null && _a !== void 0 ? _a : valueFormat })))));
|
|
201
211
|
};
|
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
background-color: var(--g-color-infographics-tooltip-bg);
|
|
4
4
|
box-shadow: 0 2px 12px var(--g-color-sfx-shadow);
|
|
5
5
|
}
|
|
6
|
+
tr.gcharts-tooltip__content-row {
|
|
7
|
+
display: table-row;
|
|
8
|
+
padding: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
6
11
|
.gcharts-tooltip__popup-content {
|
|
7
|
-
max-width: 450px;
|
|
8
12
|
text-wrap: nowrap;
|
|
9
13
|
border-radius: 4px;
|
|
10
14
|
}
|
|
@@ -20,7 +24,13 @@
|
|
|
20
24
|
.gcharts-tooltip__content-rows_pinned {
|
|
21
25
|
overflow: auto;
|
|
22
26
|
}
|
|
27
|
+
.gcharts-tooltip__content-rows-table {
|
|
28
|
+
width: 100%;
|
|
29
|
+
padding: 0;
|
|
30
|
+
border-collapse: collapse;
|
|
31
|
+
}
|
|
23
32
|
.gcharts-tooltip__series-name {
|
|
33
|
+
max-width: 450px;
|
|
24
34
|
padding: 2px 14px 6px;
|
|
25
35
|
font-size: 13px;
|
|
26
36
|
font-weight: 600;
|
|
@@ -45,8 +55,23 @@
|
|
|
45
55
|
font-weight: 600;
|
|
46
56
|
background-color: var(--g-color-base-info-medium);
|
|
47
57
|
}
|
|
48
|
-
.gcharts-tooltip__content-
|
|
58
|
+
.gcharts-tooltip__content-row_totals {
|
|
59
|
+
color: var(--g-color-text-complementary);
|
|
60
|
+
}
|
|
61
|
+
.gcharts-tooltip__content-row-totals-label {
|
|
49
62
|
overflow: hidden;
|
|
63
|
+
max-width: 400px;
|
|
64
|
+
white-space: nowrap;
|
|
65
|
+
text-overflow: ellipsis;
|
|
66
|
+
}
|
|
67
|
+
.gcharts-tooltip__content-row-totals-value {
|
|
68
|
+
flex-shrink: 0;
|
|
69
|
+
margin-inline-start: auto;
|
|
70
|
+
}
|
|
71
|
+
.gcharts-tooltip__content-row-label-cell {
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
max-width: 400px;
|
|
74
|
+
padding: 2px 4px;
|
|
50
75
|
white-space: nowrap;
|
|
51
76
|
text-overflow: ellipsis;
|
|
52
77
|
}
|
|
@@ -58,12 +83,13 @@
|
|
|
58
83
|
border-radius: 2px;
|
|
59
84
|
background-color: #dddddd;
|
|
60
85
|
}
|
|
61
|
-
.gcharts-tooltip__content-row-
|
|
62
|
-
|
|
63
|
-
|
|
86
|
+
.gcharts-tooltip__content-row-color-cell {
|
|
87
|
+
width: 16px;
|
|
88
|
+
padding: 2px 4px 2px 14px;
|
|
64
89
|
}
|
|
65
|
-
.gcharts-tooltip__content-row-
|
|
66
|
-
|
|
90
|
+
.gcharts-tooltip__content-row-value-cell {
|
|
91
|
+
padding: 2px 14px 2px 4px;
|
|
92
|
+
text-align: end;
|
|
67
93
|
}
|
|
68
94
|
.gcharts-tooltip__content-row-totals-divider {
|
|
69
95
|
margin-block: 5px 5px;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { ChartData } from '../types';
|
|
3
3
|
export * from './Tooltip/ChartTooltipContent';
|
|
4
|
+
export interface ChartReflowOptions {
|
|
5
|
+
immediate?: boolean;
|
|
6
|
+
}
|
|
4
7
|
export interface ChartRef {
|
|
5
|
-
reflow: () => void;
|
|
8
|
+
reflow: (options?: ChartReflowOptions) => void;
|
|
6
9
|
}
|
|
7
10
|
export interface ChartDimentions {
|
|
8
11
|
height: number;
|
|
@@ -30,10 +30,15 @@ export const Chart = React.forwardRef(function Chart(props, forwardedRef) {
|
|
|
30
30
|
return debounced.current;
|
|
31
31
|
}, [handleResize]);
|
|
32
32
|
React.useImperativeHandle(forwardedRef, () => ({
|
|
33
|
-
reflow() {
|
|
34
|
-
|
|
33
|
+
reflow(options) {
|
|
34
|
+
if (options === null || options === void 0 ? void 0 : options.immediate) {
|
|
35
|
+
handleResize();
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
debuncedHandleResize();
|
|
39
|
+
}
|
|
35
40
|
},
|
|
36
|
-
}), [debuncedHandleResize]);
|
|
41
|
+
}), [debuncedHandleResize, handleResize]);
|
|
37
42
|
React.useEffect(() => {
|
|
38
43
|
// dimensions initialize
|
|
39
44
|
handleResize();
|
|
@@ -162,6 +162,7 @@ export const prepareBarXData = async (args) => {
|
|
|
162
162
|
: yAxisTop + base + negativeStackHeight,
|
|
163
163
|
width: rectWidth,
|
|
164
164
|
height: shapeHeight,
|
|
165
|
+
_height: height,
|
|
165
166
|
opacity: get(yValue.data, 'opacity', null),
|
|
166
167
|
data: yValue.data,
|
|
167
168
|
series: yValue.series,
|
|
@@ -178,9 +179,9 @@ export const prepareBarXData = async (args) => {
|
|
|
178
179
|
}
|
|
179
180
|
if (series.some((s) => s.stacking === 'percent')) {
|
|
180
181
|
let acc = 0;
|
|
181
|
-
const ratio = plotHeight /
|
|
182
|
+
const ratio = plotHeight / positiveStackHeight;
|
|
182
183
|
stackItems.forEach((item) => {
|
|
183
|
-
item.height = item.
|
|
184
|
+
item.height = item._height * ratio;
|
|
184
185
|
item.y = plotHeight - item.height - acc;
|
|
185
186
|
acc += item.height + 1;
|
|
186
187
|
});
|
|
@@ -10,4 +10,9 @@ export type PreparedBarXData = Omit<TooltipDataChunkBarX, 'series'> & {
|
|
|
10
10
|
label?: LabelData;
|
|
11
11
|
htmlElements: HtmlItem[];
|
|
12
12
|
isLastStackItem: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* the utility field for storing the original height (for recalculations, etc.)
|
|
15
|
+
* should not be used for displaying
|
|
16
|
+
*/
|
|
17
|
+
_height: number;
|
|
13
18
|
};
|
|
@@ -113,6 +113,10 @@ export type ChartTooltipRowRendererArgs = {
|
|
|
113
113
|
value: string | number | null | undefined;
|
|
114
114
|
formattedValue?: string;
|
|
115
115
|
hovered?: TooltipDataChunk<unknown>[];
|
|
116
|
+
/**
|
|
117
|
+
* CSS class name pre-built with active/striped modifiers.
|
|
118
|
+
* Apply it to the root `<tr>` element of the returned row: `<tr className={className}>`.
|
|
119
|
+
*/
|
|
116
120
|
className?: string;
|
|
117
121
|
};
|
|
118
122
|
export type ChartTooltipSortComparator<T = MeaningfulAny> = (a: TooltipDataChunk<T>, b: TooltipDataChunk<T>) => number;
|
|
@@ -123,7 +127,26 @@ export interface ChartTooltip<T = MeaningfulAny> {
|
|
|
123
127
|
/**
|
|
124
128
|
* Defines the way a single data/series is displayed (corresponding to a separate selected point/ruler/shape on the chart).
|
|
125
129
|
* It is useful in cases where you need to display additional information, but keep the general format of the tooltip.
|
|
126
|
-
*
|
|
130
|
+
*
|
|
131
|
+
* The returned React element must be a `<tr>` so that it fits into the table layout used by the tooltip.
|
|
132
|
+
* Apply the `className` arg to the root `<tr>` to get the correct active/striped styles.
|
|
133
|
+
*
|
|
134
|
+
* If a string is returned, it will be parsed as HTML and rendered as-is — the string must be a complete
|
|
135
|
+
* `<tr>...</tr>` element.
|
|
136
|
+
* @example React element
|
|
137
|
+
* ```tsx
|
|
138
|
+
* rowRenderer: ({id, name, value, className}) => (
|
|
139
|
+
* <tr key={id} className={className}>
|
|
140
|
+
* <td>{name}</td>
|
|
141
|
+
* <td>{value}</td>
|
|
142
|
+
* </tr>
|
|
143
|
+
* )
|
|
144
|
+
* ```
|
|
145
|
+
* @example Raw HTML string
|
|
146
|
+
* ```ts
|
|
147
|
+
* rowRenderer: ({name, value, className}) =>
|
|
148
|
+
* `<tr class="${className}"><td>${name}</td><td>${value}</td></tr>`
|
|
149
|
+
* ```
|
|
127
150
|
*/
|
|
128
151
|
rowRenderer?: ((args: ChartTooltipRowRendererArgs) => React.ReactElement | string) | null;
|
|
129
152
|
pin?: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gravity-ui/charts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.41.0",
|
|
4
4
|
"description": "A flexible JavaScript library for data visualization and chart rendering using React",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/cjs/index.js",
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"d3": "^7.9.0",
|
|
74
74
|
"d3-sankey": "^0.12.3",
|
|
75
75
|
"d3-selection": "^3.0.0",
|
|
76
|
+
"html-react-parser": "^5.2.17",
|
|
76
77
|
"lodash": "^4.17.21",
|
|
77
78
|
"tslib": "^2.6.2"
|
|
78
79
|
},
|