@gravity-ui/charts 1.37.2 → 1.38.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.
Files changed (31) hide show
  1. package/dist/cjs/components/ChartInner/index.js +2 -1
  2. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +0 -2
  3. package/dist/cjs/components/ChartInner/useChartInnerProps.js +1 -4
  4. package/dist/cjs/components/Legend/index.js +20 -22
  5. package/dist/cjs/hooks/useChartOptions/tooltip.d.ts +5 -0
  6. package/dist/cjs/hooks/useChartOptions/tooltip.js +2 -2
  7. package/dist/cjs/hooks/useSeries/prepare-legend.js +2 -2
  8. package/dist/cjs/hooks/useSeries/prepare-line.js +3 -1
  9. package/dist/cjs/hooks/useSeries/types.d.ts +3 -1
  10. package/dist/cjs/hooks/useSeries/utils.js +6 -2
  11. package/dist/cjs/index.d.ts +1 -0
  12. package/dist/cjs/index.js +1 -0
  13. package/dist/cjs/utils/chart/symbol.d.ts +4 -0
  14. package/dist/cjs/utils/chart/symbol.js +15 -0
  15. package/dist/cjs/utils/chart/text.js +4 -6
  16. package/dist/esm/components/ChartInner/index.js +2 -1
  17. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +0 -2
  18. package/dist/esm/components/ChartInner/useChartInnerProps.js +1 -4
  19. package/dist/esm/components/Legend/index.js +20 -22
  20. package/dist/esm/hooks/useChartOptions/tooltip.d.ts +5 -0
  21. package/dist/esm/hooks/useChartOptions/tooltip.js +2 -2
  22. package/dist/esm/hooks/useSeries/prepare-legend.js +2 -2
  23. package/dist/esm/hooks/useSeries/prepare-line.js +3 -1
  24. package/dist/esm/hooks/useSeries/types.d.ts +3 -1
  25. package/dist/esm/hooks/useSeries/utils.js +6 -2
  26. package/dist/esm/index.d.ts +1 -0
  27. package/dist/esm/index.js +1 -0
  28. package/dist/esm/utils/chart/symbol.d.ts +4 -0
  29. package/dist/esm/utils/chart/symbol.js +15 -0
  30. package/dist/esm/utils/chart/text.js +4 -6
  31. package/package.json +1 -1
@@ -66,7 +66,8 @@ export const ChartInner = (props) => {
66
66
  const { allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, prevHeight, prevWidth, shapes, shapesData, shapesReady, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
67
67
  dispatcher,
68
68
  htmlLayout, plotNode: plotRef.current, preparedChart,
69
- rangeSliderState, svgContainer: svgRef.current, updateZoomState,
69
+ rangeSliderState,
70
+ updateZoomState,
70
71
  zoomState }));
71
72
  const debouncedBoundsWidth = useDebouncedValue({
72
73
  value: boundsWidth,
@@ -9,7 +9,6 @@ type Props = ChartInnerProps & {
9
9
  htmlLayout: HTMLElement | null;
10
10
  plotNode: SVGGElement | null;
11
11
  preparedChart: PreparedChart;
12
- svgContainer: SVGGElement | null;
13
12
  updateZoomState: (nextZoomState: Partial<ZoomState>) => void;
14
13
  zoomState: Partial<ZoomState>;
15
14
  rangeSliderState?: RangeSliderState;
@@ -44,7 +43,6 @@ export declare function useChartInnerProps(props: Props): {
44
43
  shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
45
44
  shapesData: import("../../hooks").ShapeData[];
46
45
  shapesReady: boolean;
47
- svgXPos: number | undefined;
48
46
  xAxis: import("../../hooks").PreparedXAxis | null;
49
47
  xScale: import("../../hooks").ChartScale | undefined;
50
48
  yAxis: (Omit<import("../../types").ChartAxis, "type" | "labels" | "plotLines" | "plotBands"> & {
@@ -75,8 +75,7 @@ export function useLegend({ preparedLegend, preparedChart, preparedSeries, width
75
75
  };
76
76
  }
77
77
  export function useChartInnerProps(props) {
78
- var _a;
79
- const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, svgContainer, width, updateZoomState, zoomState, } = props;
78
+ const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, width, updateZoomState, zoomState, } = props;
80
79
  const prevWidth = usePrevious(width);
81
80
  const prevHeight = usePrevious(height);
82
81
  const colors = React.useMemo(() => {
@@ -214,7 +213,6 @@ export function useChartInnerProps(props) {
214
213
  getYAxisWidth,
215
214
  legendConfig,
216
215
  });
217
- const { x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
218
216
  return {
219
217
  allPreparedSeries,
220
218
  boundsHeight,
@@ -234,7 +232,6 @@ export function useChartInnerProps(props) {
234
232
  shapes,
235
233
  shapesData,
236
234
  shapesReady,
237
- svgXPos: x,
238
235
  xAxis,
239
236
  xScale,
240
237
  yAxis,
@@ -71,7 +71,7 @@ function renderLegendSymbol(args) {
71
71
  const getXPosition = (i) => {
72
72
  return line.slice(0, i).reduce((acc, legendItem) => {
73
73
  return (acc +
74
- legendItem.symbol.width +
74
+ legendItem.symbol.bboxWidth +
75
75
  legendItem.symbol.padding +
76
76
  legendItem.textWidth +
77
77
  legend.itemDistance);
@@ -82,13 +82,15 @@ function renderLegendSymbol(args) {
82
82
  const x = getXPosition(i);
83
83
  const className = b('item-symbol', { shape: d.symbol.shape, unselected: !d.visible });
84
84
  const color = d.visible ? d.color : '';
85
+ const symbolType = d.symbol.symbolType;
86
+ const scatterSymbol = getSymbol(symbolType);
85
87
  switch (d.symbol.shape) {
86
88
  case 'path': {
87
89
  appendLinePathElement({
88
90
  svgRootElement: element.node(),
89
91
  x,
90
92
  height: legendLineHeight,
91
- width: d.symbol.width,
93
+ width: d.symbol.bboxWidth,
92
94
  color,
93
95
  className,
94
96
  dashStyle: d.dashStyle,
@@ -102,7 +104,7 @@ function renderLegendSymbol(args) {
102
104
  .append('rect')
103
105
  .attr('x', x)
104
106
  .attr('y', y)
105
- .attr('width', d.symbol.width)
107
+ .attr('width', d.symbol.bboxWidth)
106
108
  .attr('height', d.symbol.height)
107
109
  .attr('rx', d.symbol.radius)
108
110
  .attr('class', className)
@@ -110,18 +112,14 @@ function renderLegendSymbol(args) {
110
112
  break;
111
113
  }
112
114
  case 'symbol': {
115
+ const symbolAreaSize = Math.pow(d.symbol.width, 2);
113
116
  const y = legendLineHeight / 2;
117
+ const bboxWidth = d.symbol.bboxWidth;
114
118
  element
115
119
  .append('svg:path')
116
- .attr('d', () => {
117
- const scatterSymbol = getSymbol(d.symbol.symbolType);
118
- // D3 takes size as square pixels, so we need to make square pixels size by multiplying
119
- // https://d3js.org/d3-shape/symbol#symbol
120
- return symbol(scatterSymbol, d.symbol.width * d.symbol.width)();
121
- })
122
- .attr('transform', (_d, _index, elements) => {
123
- var _a, _b;
124
- const translateX = x + ((_b = (_a = elements[0]) === null || _a === void 0 ? void 0 : _a.getBBox()) === null || _b === void 0 ? void 0 : _b.width) / 2;
120
+ .attr('d', () => symbol(scatterSymbol, symbolAreaSize)())
121
+ .attr('transform', () => {
122
+ const translateX = x + bboxWidth / 2;
125
123
  return 'translate(' + translateX + ',' + y + ')';
126
124
  })
127
125
  .attr('class', className)
@@ -140,7 +138,7 @@ export const Legend = (props) => {
140
138
  }, [config.maxWidth]);
141
139
  React.useEffect(() => {
142
140
  async function prepareLegend() {
143
- var _a, _b, _c, _e, _f, _g, _h, _j;
141
+ var _a, _b, _c, _d, _e, _f, _g, _h;
144
142
  if (!ref.current || !htmlLayout) {
145
143
  return;
146
144
  }
@@ -157,7 +155,7 @@ export const Legend = (props) => {
157
155
  let legendTop = 0;
158
156
  if (legend.type === 'discrete') {
159
157
  const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
160
- const end = (_e = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _e === void 0 ? void 0 : _e.end;
158
+ const end = (_d = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _d === void 0 ? void 0 : _d.end;
161
159
  const pageItems = typeof start === 'number' && typeof end === 'number'
162
160
  ? items.slice(start, end)
163
161
  : items;
@@ -178,7 +176,7 @@ export const Legend = (props) => {
178
176
  const getXPosition = (i) => {
179
177
  return line.slice(0, i).reduce((acc, legendItem) => {
180
178
  return (acc +
181
- legendItem.symbol.width +
179
+ legendItem.symbol.bboxWidth +
182
180
  legendItem.symbol.padding +
183
181
  legendItem.textWidth +
184
182
  legend.itemDistance);
@@ -202,7 +200,7 @@ export const Legend = (props) => {
202
200
  return `${d.textWidth}px`;
203
201
  })
204
202
  .style('left', function (d, i) {
205
- return `${getXPosition(i) + d.symbol.width + d.symbol.padding}px`;
203
+ return `${getXPosition(i) + d.symbol.bboxWidth + d.symbol.padding}px`;
206
204
  })
207
205
  .style('top', function (d) {
208
206
  if (d.height < legendLineHeight) {
@@ -221,7 +219,7 @@ export const Legend = (props) => {
221
219
  .append('text')
222
220
  .attr('x', function (legendItem, i) {
223
221
  return (getXPosition(i) +
224
- legendItem.symbol.width +
222
+ legendItem.symbol.bboxWidth +
225
223
  legendItem.symbol.padding);
226
224
  })
227
225
  .attr('height', legend.height)
@@ -238,7 +236,7 @@ export const Legend = (props) => {
238
236
  }
239
237
  else {
240
238
  contentWidth = line.reduce((sum, l, index) => {
241
- sum += l.textWidth + l.symbol.width + l.symbol.padding;
239
+ sum += l.textWidth + l.symbol.bboxWidth + l.symbol.padding;
242
240
  if (index > 0) {
243
241
  sum += legend.itemDistance;
244
242
  }
@@ -311,7 +309,7 @@ export const Legend = (props) => {
311
309
  legendLeft = left;
312
310
  legendTop = top;
313
311
  // gradient rect
314
- const domain = (_f = legend.colorScale.domain) !== null && _f !== void 0 ? _f : [];
312
+ const domain = (_e = legend.colorScale.domain) !== null && _e !== void 0 ? _e : [];
315
313
  const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
316
314
  svgElement.call(createGradientRect, {
317
315
  y: legend.title.height + legend.title.margin,
@@ -380,9 +378,9 @@ export const Legend = (props) => {
380
378
  .attr('class', legendTitleClassname)
381
379
  .append('text')
382
380
  .attr('dx', dx)
383
- .attr('font-weight', (_g = legend.title.style.fontWeight) !== null && _g !== void 0 ? _g : null)
384
- .attr('font-size', (_h = legend.title.style.fontSize) !== null && _h !== void 0 ? _h : null)
385
- .attr('fill', (_j = legend.title.style.fontColor) !== null && _j !== void 0 ? _j : null)
381
+ .attr('font-weight', (_f = legend.title.style.fontWeight) !== null && _f !== void 0 ? _f : null)
382
+ .attr('font-size', (_g = legend.title.style.fontSize) !== null && _g !== void 0 ? _g : null)
383
+ .attr('fill', (_h = legend.title.style.fontColor) !== null && _h !== void 0 ? _h : null)
386
384
  .style('dominant-baseline', 'text-before-edge')
387
385
  .html(legend.title.text);
388
386
  }
@@ -1,5 +1,10 @@
1
1
  import type { ChartData, ChartSeries, ChartXAxis, ChartYAxis } from '../../types';
2
2
  import type { PreparedTooltip } from './types';
3
+ export declare function getDefaultTooltipHeaderFormat({ seriesData, yAxes, xAxis, }: {
4
+ seriesData: ChartSeries[];
5
+ yAxes?: ChartYAxis[];
6
+ xAxis?: ChartXAxis;
7
+ }): import("../../types").ValueFormat | undefined;
3
8
  export declare const getPreparedTooltip: (args: {
4
9
  tooltip: ChartData["tooltip"];
5
10
  seriesData: ChartSeries[];
@@ -1,7 +1,7 @@
1
1
  import get from 'lodash/get';
2
2
  import { getDefaultValueFormat } from '../../components/Tooltip/DefaultTooltipContent/utils';
3
3
  import { getDomainDataXBySeries, getDomainDataYBySeries, getMinSpaceBetween } from '../../utils';
4
- function getDefaultHeaderFormat({ seriesData, yAxes, xAxis, }) {
4
+ export function getDefaultTooltipHeaderFormat({ seriesData, yAxes, xAxis, }) {
5
5
  if (seriesData.every((item) => ['pie', 'treemap', 'waterfall', 'sankey', 'radar', 'heatmap', 'funnel'].includes(item.type))) {
6
6
  return undefined;
7
7
  }
@@ -17,5 +17,5 @@ function getDefaultHeaderFormat({ seriesData, yAxes, xAxis, }) {
17
17
  export const getPreparedTooltip = (args) => {
18
18
  var _a, _b;
19
19
  const { tooltip, seriesData, yAxes, xAxis } = args;
20
- return Object.assign(Object.assign({}, tooltip), { enabled: get(tooltip, 'enabled', true), throttle: (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.throttle) !== null && _a !== void 0 ? _a : 0, headerFormat: (_b = tooltip === null || tooltip === void 0 ? void 0 : tooltip.headerFormat) !== null && _b !== void 0 ? _b : getDefaultHeaderFormat({ seriesData, yAxes, xAxis }) });
20
+ return Object.assign(Object.assign({}, tooltip), { enabled: get(tooltip, 'enabled', true), throttle: (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.throttle) !== null && _a !== void 0 ? _a : 0, headerFormat: (_b = tooltip === null || tooltip === void 0 ? void 0 : tooltip.headerFormat) !== null && _b !== void 0 ? _b : getDefaultTooltipHeaderFormat({ seriesData, yAxes, xAxis }) });
21
21
  };
@@ -101,7 +101,7 @@ async function getGroupedLegendItems(args) {
101
101
  const item = items[i];
102
102
  const resultItem = clone(item);
103
103
  resultItem.text = item.name;
104
- const maxTextWidth = maxLegendWidth - resultItem.symbol.width - resultItem.symbol.padding;
104
+ const maxTextWidth = maxLegendWidth - resultItem.symbol.bboxWidth - resultItem.symbol.padding;
105
105
  let textHeight = 0;
106
106
  let textWidth = 0;
107
107
  if (preparedLegend.html) {
@@ -143,7 +143,7 @@ async function getGroupedLegendItems(args) {
143
143
  }
144
144
  result[lineIndex].push(resultItem);
145
145
  const symbolsWidth = result[lineIndex].reduce((acc, { symbol }) => {
146
- return acc + symbol.width + symbol.padding;
146
+ return acc + symbol.bboxWidth + symbol.padding;
147
147
  }, 0);
148
148
  const distancesWidth = (result[lineIndex].length - 1) * preparedLegend.itemDistance;
149
149
  const isOverflowedAsOnlyItemInLine = resultItem.overflowed && result[lineIndex].length === 1;
@@ -22,9 +22,11 @@ function prepareLineLegendSymbol(series, seriesOptions) {
22
22
  var _a;
23
23
  const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
24
24
  const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH);
25
+ const width = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE;
25
26
  return {
26
27
  shape: 'path',
27
- width: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE,
28
+ width: width,
29
+ bboxWidth: width,
28
30
  padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
29
31
  strokeWidth: get(series, 'lineWidth', defaultLineWidth),
30
32
  };
@@ -11,7 +11,9 @@ export type SymbolLegendSymbol = {
11
11
  shape: 'symbol';
12
12
  symbolType: `${SymbolType}`;
13
13
  } & Required<SymbolLegendSymbolOptions>;
14
- export type PreparedLegendSymbol = RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol;
14
+ export type PreparedLegendSymbol = (RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol) & {
15
+ bboxWidth: number;
16
+ };
15
17
  export type PreparedLegend = Required<Omit<ChartLegend, 'title' | 'colorScale'>> & {
16
18
  height: number;
17
19
  lineHeight: number;
@@ -1,5 +1,6 @@
1
1
  import memoize from 'lodash/memoize';
2
2
  import { SymbolType } from '../../constants';
3
+ import { getSymbolBBoxWidth } from '../../utils';
3
4
  import { getUniqId } from '../../utils/misc';
4
5
  import { DEFAULT_LEGEND_SYMBOL_PADDING, DEFAULT_LEGEND_SYMBOL_SIZE } from './constants';
5
6
  export const getActiveLegendItems = (series) => {
@@ -16,10 +17,13 @@ export const getAllLegendItems = (series) => {
16
17
  export function prepareLegendSymbol(series, symbolType) {
17
18
  var _a;
18
19
  const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
20
+ const width = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE;
21
+ const type = symbolType || SymbolType.Circle;
19
22
  return {
20
23
  shape: 'symbol',
21
- symbolType: symbolType || SymbolType.Circle,
22
- width: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE,
24
+ symbolType: type,
25
+ width,
26
+ bboxWidth: getSymbolBBoxWidth({ symbolSize: Math.pow(width, 2), symbolType: type }),
23
27
  padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
24
28
  };
25
29
  }
@@ -1,3 +1,4 @@
1
1
  export { CustomShapeRenderer, getFormattedValue } from './utils';
2
+ export { getDefaultTooltipHeaderFormat } from './hooks/useChartOptions/tooltip';
2
3
  export * from './components';
3
4
  export * from './types';
package/dist/cjs/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { CustomShapeRenderer, getFormattedValue } from './utils';
2
+ export { getDefaultTooltipHeaderFormat } from './hooks/useChartOptions/tooltip';
2
3
  export * from './components';
3
4
  export * from './types';
@@ -1,3 +1,7 @@
1
1
  import { SymbolType } from '../../constants';
2
2
  export declare const getSymbolType: (index: number) => SymbolType;
3
3
  export declare const getSymbol: (symbolType: `${SymbolType}`) => import("d3-shape").SymbolType;
4
+ export declare function getSymbolBBoxWidth({ symbolSize, symbolType, }: {
5
+ symbolSize: number;
6
+ symbolType: `${SymbolType}`;
7
+ }): number;
@@ -34,3 +34,18 @@ export const getSymbol = (symbolType) => {
34
34
  return symbolCircle;
35
35
  }
36
36
  };
37
+ export function getSymbolBBoxWidth({ symbolSize, symbolType, }) {
38
+ switch (symbolType) {
39
+ case SymbolType.Diamond:
40
+ return Math.sqrt(symbolSize * 2);
41
+ case SymbolType.Circle:
42
+ return Math.sqrt(symbolSize / Math.PI) * 2;
43
+ case SymbolType.Square:
44
+ return Math.sqrt(symbolSize);
45
+ case SymbolType.Triangle:
46
+ case SymbolType.TriangleDown:
47
+ return Math.sqrt((4 * symbolSize * Math.sqrt(3)) / 3);
48
+ default:
49
+ return 0;
50
+ }
51
+ }
@@ -176,9 +176,6 @@ function unescapeHtml(str) {
176
176
  return result.replace(value, key);
177
177
  }, str);
178
178
  }
179
- function getCssStyle(prop, el = document.body) {
180
- return window.getComputedStyle(el, null).getPropertyValue(prop);
181
- }
182
179
  let measureCanvas = null;
183
180
  export function getTextSizeFn({ style }) {
184
181
  var _a;
@@ -188,9 +185,10 @@ export function getTextSizeFn({ style }) {
188
185
  throw new Error("Couldn't get canvas context");
189
186
  }
190
187
  const element = (_a = document.getElementsByClassName(b())[0]) !== null && _a !== void 0 ? _a : document.body;
191
- const defaultFontFamily = getCssStyle('font-family', element);
192
- const defaultFontSize = getCssStyle('font-size', element);
193
- const defaultFontWeight = getCssStyle('font-weight', element);
188
+ const computedStyle = window.getComputedStyle(element, null);
189
+ const defaultFontFamily = computedStyle.getPropertyValue('font-family');
190
+ const defaultFontSize = computedStyle.getPropertyValue('font-size');
191
+ const defaultFontWeight = computedStyle.getPropertyValue('font-weight');
194
192
  return async (str) => {
195
193
  var _a, _b;
196
194
  await document.fonts.ready;
@@ -66,7 +66,8 @@ export const ChartInner = (props) => {
66
66
  const { allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, isOutsideBounds, legendConfig, legendItems, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, prevHeight, prevWidth, shapes, shapesData, shapesReady, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
67
67
  dispatcher,
68
68
  htmlLayout, plotNode: plotRef.current, preparedChart,
69
- rangeSliderState, svgContainer: svgRef.current, updateZoomState,
69
+ rangeSliderState,
70
+ updateZoomState,
70
71
  zoomState }));
71
72
  const debouncedBoundsWidth = useDebouncedValue({
72
73
  value: boundsWidth,
@@ -9,7 +9,6 @@ type Props = ChartInnerProps & {
9
9
  htmlLayout: HTMLElement | null;
10
10
  plotNode: SVGGElement | null;
11
11
  preparedChart: PreparedChart;
12
- svgContainer: SVGGElement | null;
13
12
  updateZoomState: (nextZoomState: Partial<ZoomState>) => void;
14
13
  zoomState: Partial<ZoomState>;
15
14
  rangeSliderState?: RangeSliderState;
@@ -44,7 +43,6 @@ export declare function useChartInnerProps(props: Props): {
44
43
  shapes: React.ReactElement<any, string | React.JSXElementConstructor<any>>[];
45
44
  shapesData: import("../../hooks").ShapeData[];
46
45
  shapesReady: boolean;
47
- svgXPos: number | undefined;
48
46
  xAxis: import("../../hooks").PreparedXAxis | null;
49
47
  xScale: import("../../hooks").ChartScale | undefined;
50
48
  yAxis: (Omit<import("../..").ChartAxis, "type" | "labels" | "plotLines" | "plotBands"> & {
@@ -75,8 +75,7 @@ export function useLegend({ preparedLegend, preparedChart, preparedSeries, width
75
75
  };
76
76
  }
77
77
  export function useChartInnerProps(props) {
78
- var _a;
79
- const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, svgContainer, width, updateZoomState, zoomState, } = props;
78
+ const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, width, updateZoomState, zoomState, } = props;
80
79
  const prevWidth = usePrevious(width);
81
80
  const prevHeight = usePrevious(height);
82
81
  const colors = React.useMemo(() => {
@@ -214,7 +213,6 @@ export function useChartInnerProps(props) {
214
213
  getYAxisWidth,
215
214
  legendConfig,
216
215
  });
217
- const { x } = (_a = svgContainer === null || svgContainer === void 0 ? void 0 : svgContainer.getBoundingClientRect()) !== null && _a !== void 0 ? _a : {};
218
216
  return {
219
217
  allPreparedSeries,
220
218
  boundsHeight,
@@ -234,7 +232,6 @@ export function useChartInnerProps(props) {
234
232
  shapes,
235
233
  shapesData,
236
234
  shapesReady,
237
- svgXPos: x,
238
235
  xAxis,
239
236
  xScale,
240
237
  yAxis,
@@ -71,7 +71,7 @@ function renderLegendSymbol(args) {
71
71
  const getXPosition = (i) => {
72
72
  return line.slice(0, i).reduce((acc, legendItem) => {
73
73
  return (acc +
74
- legendItem.symbol.width +
74
+ legendItem.symbol.bboxWidth +
75
75
  legendItem.symbol.padding +
76
76
  legendItem.textWidth +
77
77
  legend.itemDistance);
@@ -82,13 +82,15 @@ function renderLegendSymbol(args) {
82
82
  const x = getXPosition(i);
83
83
  const className = b('item-symbol', { shape: d.symbol.shape, unselected: !d.visible });
84
84
  const color = d.visible ? d.color : '';
85
+ const symbolType = d.symbol.symbolType;
86
+ const scatterSymbol = getSymbol(symbolType);
85
87
  switch (d.symbol.shape) {
86
88
  case 'path': {
87
89
  appendLinePathElement({
88
90
  svgRootElement: element.node(),
89
91
  x,
90
92
  height: legendLineHeight,
91
- width: d.symbol.width,
93
+ width: d.symbol.bboxWidth,
92
94
  color,
93
95
  className,
94
96
  dashStyle: d.dashStyle,
@@ -102,7 +104,7 @@ function renderLegendSymbol(args) {
102
104
  .append('rect')
103
105
  .attr('x', x)
104
106
  .attr('y', y)
105
- .attr('width', d.symbol.width)
107
+ .attr('width', d.symbol.bboxWidth)
106
108
  .attr('height', d.symbol.height)
107
109
  .attr('rx', d.symbol.radius)
108
110
  .attr('class', className)
@@ -110,18 +112,14 @@ function renderLegendSymbol(args) {
110
112
  break;
111
113
  }
112
114
  case 'symbol': {
115
+ const symbolAreaSize = Math.pow(d.symbol.width, 2);
113
116
  const y = legendLineHeight / 2;
117
+ const bboxWidth = d.symbol.bboxWidth;
114
118
  element
115
119
  .append('svg:path')
116
- .attr('d', () => {
117
- const scatterSymbol = getSymbol(d.symbol.symbolType);
118
- // D3 takes size as square pixels, so we need to make square pixels size by multiplying
119
- // https://d3js.org/d3-shape/symbol#symbol
120
- return symbol(scatterSymbol, d.symbol.width * d.symbol.width)();
121
- })
122
- .attr('transform', (_d, _index, elements) => {
123
- var _a, _b;
124
- const translateX = x + ((_b = (_a = elements[0]) === null || _a === void 0 ? void 0 : _a.getBBox()) === null || _b === void 0 ? void 0 : _b.width) / 2;
120
+ .attr('d', () => symbol(scatterSymbol, symbolAreaSize)())
121
+ .attr('transform', () => {
122
+ const translateX = x + bboxWidth / 2;
125
123
  return 'translate(' + translateX + ',' + y + ')';
126
124
  })
127
125
  .attr('class', className)
@@ -140,7 +138,7 @@ export const Legend = (props) => {
140
138
  }, [config.maxWidth]);
141
139
  React.useEffect(() => {
142
140
  async function prepareLegend() {
143
- var _a, _b, _c, _e, _f, _g, _h, _j;
141
+ var _a, _b, _c, _d, _e, _f, _g, _h;
144
142
  if (!ref.current || !htmlLayout) {
145
143
  return;
146
144
  }
@@ -157,7 +155,7 @@ export const Legend = (props) => {
157
155
  let legendTop = 0;
158
156
  if (legend.type === 'discrete') {
159
157
  const start = (_b = (_a = config.pagination) === null || _a === void 0 ? void 0 : _a.pages[pageIndex]) === null || _b === void 0 ? void 0 : _b.start;
160
- const end = (_e = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _e === void 0 ? void 0 : _e.end;
158
+ const end = (_d = (_c = config.pagination) === null || _c === void 0 ? void 0 : _c.pages[pageIndex]) === null || _d === void 0 ? void 0 : _d.end;
161
159
  const pageItems = typeof start === 'number' && typeof end === 'number'
162
160
  ? items.slice(start, end)
163
161
  : items;
@@ -178,7 +176,7 @@ export const Legend = (props) => {
178
176
  const getXPosition = (i) => {
179
177
  return line.slice(0, i).reduce((acc, legendItem) => {
180
178
  return (acc +
181
- legendItem.symbol.width +
179
+ legendItem.symbol.bboxWidth +
182
180
  legendItem.symbol.padding +
183
181
  legendItem.textWidth +
184
182
  legend.itemDistance);
@@ -202,7 +200,7 @@ export const Legend = (props) => {
202
200
  return `${d.textWidth}px`;
203
201
  })
204
202
  .style('left', function (d, i) {
205
- return `${getXPosition(i) + d.symbol.width + d.symbol.padding}px`;
203
+ return `${getXPosition(i) + d.symbol.bboxWidth + d.symbol.padding}px`;
206
204
  })
207
205
  .style('top', function (d) {
208
206
  if (d.height < legendLineHeight) {
@@ -221,7 +219,7 @@ export const Legend = (props) => {
221
219
  .append('text')
222
220
  .attr('x', function (legendItem, i) {
223
221
  return (getXPosition(i) +
224
- legendItem.symbol.width +
222
+ legendItem.symbol.bboxWidth +
225
223
  legendItem.symbol.padding);
226
224
  })
227
225
  .attr('height', legend.height)
@@ -238,7 +236,7 @@ export const Legend = (props) => {
238
236
  }
239
237
  else {
240
238
  contentWidth = line.reduce((sum, l, index) => {
241
- sum += l.textWidth + l.symbol.width + l.symbol.padding;
239
+ sum += l.textWidth + l.symbol.bboxWidth + l.symbol.padding;
242
240
  if (index > 0) {
243
241
  sum += legend.itemDistance;
244
242
  }
@@ -311,7 +309,7 @@ export const Legend = (props) => {
311
309
  legendLeft = left;
312
310
  legendTop = top;
313
311
  // gradient rect
314
- const domain = (_f = legend.colorScale.domain) !== null && _f !== void 0 ? _f : [];
312
+ const domain = (_e = legend.colorScale.domain) !== null && _e !== void 0 ? _e : [];
315
313
  const rectHeight = CONTINUOUS_LEGEND_SIZE.height;
316
314
  svgElement.call(createGradientRect, {
317
315
  y: legend.title.height + legend.title.margin,
@@ -380,9 +378,9 @@ export const Legend = (props) => {
380
378
  .attr('class', legendTitleClassname)
381
379
  .append('text')
382
380
  .attr('dx', dx)
383
- .attr('font-weight', (_g = legend.title.style.fontWeight) !== null && _g !== void 0 ? _g : null)
384
- .attr('font-size', (_h = legend.title.style.fontSize) !== null && _h !== void 0 ? _h : null)
385
- .attr('fill', (_j = legend.title.style.fontColor) !== null && _j !== void 0 ? _j : null)
381
+ .attr('font-weight', (_f = legend.title.style.fontWeight) !== null && _f !== void 0 ? _f : null)
382
+ .attr('font-size', (_g = legend.title.style.fontSize) !== null && _g !== void 0 ? _g : null)
383
+ .attr('fill', (_h = legend.title.style.fontColor) !== null && _h !== void 0 ? _h : null)
386
384
  .style('dominant-baseline', 'text-before-edge')
387
385
  .html(legend.title.text);
388
386
  }
@@ -1,5 +1,10 @@
1
1
  import type { ChartData, ChartSeries, ChartXAxis, ChartYAxis } from '../../types';
2
2
  import type { PreparedTooltip } from './types';
3
+ export declare function getDefaultTooltipHeaderFormat({ seriesData, yAxes, xAxis, }: {
4
+ seriesData: ChartSeries[];
5
+ yAxes?: ChartYAxis[];
6
+ xAxis?: ChartXAxis;
7
+ }): import("../..").ValueFormat | undefined;
3
8
  export declare const getPreparedTooltip: (args: {
4
9
  tooltip: ChartData["tooltip"];
5
10
  seriesData: ChartSeries[];
@@ -1,7 +1,7 @@
1
1
  import get from 'lodash/get';
2
2
  import { getDefaultValueFormat } from '../../components/Tooltip/DefaultTooltipContent/utils';
3
3
  import { getDomainDataXBySeries, getDomainDataYBySeries, getMinSpaceBetween } from '../../utils';
4
- function getDefaultHeaderFormat({ seriesData, yAxes, xAxis, }) {
4
+ export function getDefaultTooltipHeaderFormat({ seriesData, yAxes, xAxis, }) {
5
5
  if (seriesData.every((item) => ['pie', 'treemap', 'waterfall', 'sankey', 'radar', 'heatmap', 'funnel'].includes(item.type))) {
6
6
  return undefined;
7
7
  }
@@ -17,5 +17,5 @@ function getDefaultHeaderFormat({ seriesData, yAxes, xAxis, }) {
17
17
  export const getPreparedTooltip = (args) => {
18
18
  var _a, _b;
19
19
  const { tooltip, seriesData, yAxes, xAxis } = args;
20
- return Object.assign(Object.assign({}, tooltip), { enabled: get(tooltip, 'enabled', true), throttle: (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.throttle) !== null && _a !== void 0 ? _a : 0, headerFormat: (_b = tooltip === null || tooltip === void 0 ? void 0 : tooltip.headerFormat) !== null && _b !== void 0 ? _b : getDefaultHeaderFormat({ seriesData, yAxes, xAxis }) });
20
+ return Object.assign(Object.assign({}, tooltip), { enabled: get(tooltip, 'enabled', true), throttle: (_a = tooltip === null || tooltip === void 0 ? void 0 : tooltip.throttle) !== null && _a !== void 0 ? _a : 0, headerFormat: (_b = tooltip === null || tooltip === void 0 ? void 0 : tooltip.headerFormat) !== null && _b !== void 0 ? _b : getDefaultTooltipHeaderFormat({ seriesData, yAxes, xAxis }) });
21
21
  };
@@ -101,7 +101,7 @@ async function getGroupedLegendItems(args) {
101
101
  const item = items[i];
102
102
  const resultItem = clone(item);
103
103
  resultItem.text = item.name;
104
- const maxTextWidth = maxLegendWidth - resultItem.symbol.width - resultItem.symbol.padding;
104
+ const maxTextWidth = maxLegendWidth - resultItem.symbol.bboxWidth - resultItem.symbol.padding;
105
105
  let textHeight = 0;
106
106
  let textWidth = 0;
107
107
  if (preparedLegend.html) {
@@ -143,7 +143,7 @@ async function getGroupedLegendItems(args) {
143
143
  }
144
144
  result[lineIndex].push(resultItem);
145
145
  const symbolsWidth = result[lineIndex].reduce((acc, { symbol }) => {
146
- return acc + symbol.width + symbol.padding;
146
+ return acc + symbol.bboxWidth + symbol.padding;
147
147
  }, 0);
148
148
  const distancesWidth = (result[lineIndex].length - 1) * preparedLegend.itemDistance;
149
149
  const isOverflowedAsOnlyItemInLine = resultItem.overflowed && result[lineIndex].length === 1;
@@ -22,9 +22,11 @@ function prepareLineLegendSymbol(series, seriesOptions) {
22
22
  var _a;
23
23
  const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
24
24
  const defaultLineWidth = get(seriesOptions, 'line.lineWidth', DEFAULT_LINE_WIDTH);
25
+ const width = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE;
25
26
  return {
26
27
  shape: 'path',
27
- width: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE,
28
+ width: width,
29
+ bboxWidth: width,
28
30
  padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
29
31
  strokeWidth: get(series, 'lineWidth', defaultLineWidth),
30
32
  };
@@ -11,7 +11,9 @@ export type SymbolLegendSymbol = {
11
11
  shape: 'symbol';
12
12
  symbolType: `${SymbolType}`;
13
13
  } & Required<SymbolLegendSymbolOptions>;
14
- export type PreparedLegendSymbol = RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol;
14
+ export type PreparedLegendSymbol = (RectLegendSymbol | PathLegendSymbol | SymbolLegendSymbol) & {
15
+ bboxWidth: number;
16
+ };
15
17
  export type PreparedLegend = Required<Omit<ChartLegend, 'title' | 'colorScale'>> & {
16
18
  height: number;
17
19
  lineHeight: number;
@@ -1,5 +1,6 @@
1
1
  import memoize from 'lodash/memoize';
2
2
  import { SymbolType } from '../../constants';
3
+ import { getSymbolBBoxWidth } from '../../utils';
3
4
  import { getUniqId } from '../../utils/misc';
4
5
  import { DEFAULT_LEGEND_SYMBOL_PADDING, DEFAULT_LEGEND_SYMBOL_SIZE } from './constants';
5
6
  export const getActiveLegendItems = (series) => {
@@ -16,10 +17,13 @@ export const getAllLegendItems = (series) => {
16
17
  export function prepareLegendSymbol(series, symbolType) {
17
18
  var _a;
18
19
  const symbolOptions = ((_a = series.legend) === null || _a === void 0 ? void 0 : _a.symbol) || {};
20
+ const width = (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE;
21
+ const type = symbolType || SymbolType.Circle;
19
22
  return {
20
23
  shape: 'symbol',
21
- symbolType: symbolType || SymbolType.Circle,
22
- width: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.width) || DEFAULT_LEGEND_SYMBOL_SIZE,
24
+ symbolType: type,
25
+ width,
26
+ bboxWidth: getSymbolBBoxWidth({ symbolSize: Math.pow(width, 2), symbolType: type }),
23
27
  padding: (symbolOptions === null || symbolOptions === void 0 ? void 0 : symbolOptions.padding) || DEFAULT_LEGEND_SYMBOL_PADDING,
24
28
  };
25
29
  }
@@ -1,3 +1,4 @@
1
1
  export { CustomShapeRenderer, getFormattedValue } from './utils';
2
+ export { getDefaultTooltipHeaderFormat } from './hooks/useChartOptions/tooltip';
2
3
  export * from './components';
3
4
  export * from './types';
package/dist/esm/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { CustomShapeRenderer, getFormattedValue } from './utils';
2
+ export { getDefaultTooltipHeaderFormat } from './hooks/useChartOptions/tooltip';
2
3
  export * from './components';
3
4
  export * from './types';
@@ -1,3 +1,7 @@
1
1
  import { SymbolType } from '../../constants';
2
2
  export declare const getSymbolType: (index: number) => SymbolType;
3
3
  export declare const getSymbol: (symbolType: `${SymbolType}`) => import("d3-shape").SymbolType;
4
+ export declare function getSymbolBBoxWidth({ symbolSize, symbolType, }: {
5
+ symbolSize: number;
6
+ symbolType: `${SymbolType}`;
7
+ }): number;
@@ -34,3 +34,18 @@ export const getSymbol = (symbolType) => {
34
34
  return symbolCircle;
35
35
  }
36
36
  };
37
+ export function getSymbolBBoxWidth({ symbolSize, symbolType, }) {
38
+ switch (symbolType) {
39
+ case SymbolType.Diamond:
40
+ return Math.sqrt(symbolSize * 2);
41
+ case SymbolType.Circle:
42
+ return Math.sqrt(symbolSize / Math.PI) * 2;
43
+ case SymbolType.Square:
44
+ return Math.sqrt(symbolSize);
45
+ case SymbolType.Triangle:
46
+ case SymbolType.TriangleDown:
47
+ return Math.sqrt((4 * symbolSize * Math.sqrt(3)) / 3);
48
+ default:
49
+ return 0;
50
+ }
51
+ }
@@ -176,9 +176,6 @@ function unescapeHtml(str) {
176
176
  return result.replace(value, key);
177
177
  }, str);
178
178
  }
179
- function getCssStyle(prop, el = document.body) {
180
- return window.getComputedStyle(el, null).getPropertyValue(prop);
181
- }
182
179
  let measureCanvas = null;
183
180
  export function getTextSizeFn({ style }) {
184
181
  var _a;
@@ -188,9 +185,10 @@ export function getTextSizeFn({ style }) {
188
185
  throw new Error("Couldn't get canvas context");
189
186
  }
190
187
  const element = (_a = document.getElementsByClassName(b())[0]) !== null && _a !== void 0 ? _a : document.body;
191
- const defaultFontFamily = getCssStyle('font-family', element);
192
- const defaultFontSize = getCssStyle('font-size', element);
193
- const defaultFontWeight = getCssStyle('font-weight', element);
188
+ const computedStyle = window.getComputedStyle(element, null);
189
+ const defaultFontFamily = computedStyle.getPropertyValue('font-family');
190
+ const defaultFontSize = computedStyle.getPropertyValue('font-size');
191
+ const defaultFontWeight = computedStyle.getPropertyValue('font-weight');
194
192
  return async (str) => {
195
193
  var _a, _b;
196
194
  await document.fonts.ready;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gravity-ui/charts",
3
- "version": "1.37.2",
3
+ "version": "1.38.1",
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",