@gravity-ui/charts 1.42.2 → 1.42.4

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 (83) hide show
  1. package/dist/cjs/components/AxisX/AxisX.js +4 -4
  2. package/dist/cjs/components/AxisX/prepare-axis-data.js +17 -13
  3. package/dist/cjs/components/AxisY/AxisY.js +4 -4
  4. package/dist/cjs/components/AxisY/prepare-axis-data.js +27 -21
  5. package/dist/cjs/components/AxisY/prepare-axis-title.js +8 -3
  6. package/dist/cjs/components/AxisY/styles.css +1 -1
  7. package/dist/cjs/components/ChartInner/index.js +5 -15
  8. package/dist/cjs/components/ChartInner/useChartInnerProps.d.ts +4 -1
  9. package/dist/cjs/components/ChartInner/useChartInnerProps.js +22 -11
  10. package/dist/cjs/components/ChartInner/utils/title.d.ts +1 -1
  11. package/dist/cjs/components/ChartInner/utils/title.js +3 -3
  12. package/dist/cjs/components/Legend/index.js +19 -13
  13. package/dist/cjs/components/Legend/styles.css +1 -1
  14. package/dist/cjs/components/Tooltip/DefaultTooltipContent/Row.js +1 -1
  15. package/dist/cjs/components/utils/axis-title.js +1 -1
  16. package/dist/cjs/core/axes/x-axis.js +2 -2
  17. package/dist/cjs/core/axes/y-axis.js +2 -2
  18. package/dist/cjs/core/layout/split.d.ts +2 -2
  19. package/dist/cjs/core/layout/split.js +22 -19
  20. package/dist/cjs/core/series/prepare-legend.js +7 -7
  21. package/dist/cjs/core/series/types.d.ts +2 -0
  22. package/dist/cjs/core/utils/axis-generators/bottom.js +6 -16
  23. package/dist/cjs/core/utils/common.d.ts +0 -4
  24. package/dist/cjs/core/utils/common.js +0 -13
  25. package/dist/cjs/core/utils/labels.d.ts +1 -0
  26. package/dist/cjs/core/utils/labels.js +5 -5
  27. package/dist/cjs/core/utils/text.d.ts +1 -0
  28. package/dist/cjs/core/utils/text.js +4 -0
  29. package/dist/cjs/hooks/useShapes/area/prepare-data.js +1 -1
  30. package/dist/cjs/hooks/useShapes/bar-y/prepare-data.js +3 -2
  31. package/dist/cjs/hooks/useShapes/funnel/prepare-data.js +4 -1
  32. package/dist/cjs/hooks/useShapes/heatmap/prepare-data.js +1 -1
  33. package/dist/cjs/hooks/useShapes/line/prepare-data.js +4 -1
  34. package/dist/cjs/hooks/useShapes/pie/prepare-data.js +9 -2
  35. package/dist/cjs/hooks/useShapes/radar/prepare-data.js +17 -7
  36. package/dist/cjs/hooks/useShapes/sankey/prepare-data.js +1 -1
  37. package/dist/cjs/hooks/useShapes/sankey/sankey-layout.d.ts +49 -0
  38. package/dist/cjs/hooks/useShapes/sankey/sankey-layout.js +362 -0
  39. package/dist/cjs/hooks/useShapes/styles.css +4 -4
  40. package/dist/cjs/hooks/useShapes/treemap/prepare-data.js +3 -1
  41. package/dist/cjs/types/chart-ui.d.ts +1 -0
  42. package/dist/esm/components/AxisX/AxisX.js +4 -4
  43. package/dist/esm/components/AxisX/prepare-axis-data.js +17 -13
  44. package/dist/esm/components/AxisY/AxisY.js +4 -4
  45. package/dist/esm/components/AxisY/prepare-axis-data.js +27 -21
  46. package/dist/esm/components/AxisY/prepare-axis-title.js +8 -3
  47. package/dist/esm/components/AxisY/styles.css +1 -1
  48. package/dist/esm/components/ChartInner/index.js +5 -15
  49. package/dist/esm/components/ChartInner/useChartInnerProps.d.ts +4 -1
  50. package/dist/esm/components/ChartInner/useChartInnerProps.js +22 -11
  51. package/dist/esm/components/ChartInner/utils/title.d.ts +1 -1
  52. package/dist/esm/components/ChartInner/utils/title.js +3 -3
  53. package/dist/esm/components/Legend/index.js +19 -13
  54. package/dist/esm/components/Legend/styles.css +1 -1
  55. package/dist/esm/components/Tooltip/DefaultTooltipContent/Row.js +1 -1
  56. package/dist/esm/components/utils/axis-title.js +1 -1
  57. package/dist/esm/core/axes/x-axis.js +2 -2
  58. package/dist/esm/core/axes/y-axis.js +2 -2
  59. package/dist/esm/core/layout/split.d.ts +2 -2
  60. package/dist/esm/core/layout/split.js +22 -19
  61. package/dist/esm/core/series/prepare-legend.js +7 -7
  62. package/dist/esm/core/series/types.d.ts +2 -0
  63. package/dist/esm/core/utils/axis-generators/bottom.js +6 -16
  64. package/dist/esm/core/utils/common.d.ts +0 -4
  65. package/dist/esm/core/utils/common.js +0 -13
  66. package/dist/esm/core/utils/labels.d.ts +1 -0
  67. package/dist/esm/core/utils/labels.js +5 -5
  68. package/dist/esm/core/utils/text.d.ts +1 -0
  69. package/dist/esm/core/utils/text.js +4 -0
  70. package/dist/esm/hooks/useShapes/area/prepare-data.js +1 -1
  71. package/dist/esm/hooks/useShapes/bar-y/prepare-data.js +3 -2
  72. package/dist/esm/hooks/useShapes/funnel/prepare-data.js +4 -1
  73. package/dist/esm/hooks/useShapes/heatmap/prepare-data.js +1 -1
  74. package/dist/esm/hooks/useShapes/line/prepare-data.js +4 -1
  75. package/dist/esm/hooks/useShapes/pie/prepare-data.js +9 -2
  76. package/dist/esm/hooks/useShapes/radar/prepare-data.js +17 -7
  77. package/dist/esm/hooks/useShapes/sankey/prepare-data.js +1 -1
  78. package/dist/esm/hooks/useShapes/sankey/sankey-layout.d.ts +49 -0
  79. package/dist/esm/hooks/useShapes/sankey/sankey-layout.js +362 -0
  80. package/dist/esm/hooks/useShapes/styles.css +4 -4
  81. package/dist/esm/hooks/useShapes/treemap/prepare-data.js +3 -1
  82. package/dist/esm/types/chart-ui.d.ts +1 -0
  83. package/package.json +1 -3
@@ -45,7 +45,7 @@ export const AxisX = (props) => {
45
45
  .html((d) => d.text)
46
46
  .attr('x', (d) => d.x)
47
47
  .attr('y', (d) => d.y)
48
- .attr('dominant-baseline', 'text-before-edge')
48
+ .attr('dominant-baseline', 'hanging')
49
49
  .attr('text-anchor', 'start');
50
50
  }
51
51
  if (preparedAxisData.domain) {
@@ -98,7 +98,7 @@ export const AxisX = (props) => {
98
98
  .attr('y', (d) => d.y)
99
99
  .attr('text-anchor', 'start')
100
100
  .attr('class', labelClassName)
101
- .style('dominant-baseline', 'text-before-edge')
101
+ .style('dominant-baseline', 'hanging')
102
102
  .style('font-size', label.style.fontSize)
103
103
  .style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '');
104
104
  }
@@ -134,7 +134,7 @@ export const AxisX = (props) => {
134
134
  .style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '')
135
135
  .style('font-size', label.style.fontSize)
136
136
  .style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
137
- .style('dominant-baseline', 'text-before-edge')
137
+ .style('dominant-baseline', 'hanging')
138
138
  .style('text-anchor', 'start')
139
139
  .attr('transform', `translate(${label.x}, ${label.y}) rotate(${label.rotate})`)
140
140
  .attr('data-qa', (_c = label.qa) !== null && _c !== void 0 ? _c : null);
@@ -176,7 +176,7 @@ export const AxisX = (props) => {
176
176
  .style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '')
177
177
  .style('font-size', label.style.fontSize)
178
178
  .style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
179
- .style('dominant-baseline', 'text-before-edge')
179
+ .style('dominant-baseline', 'hanging')
180
180
  .style('text-anchor', 'start')
181
181
  .attr('transform', `translate(${label.x}, ${label.y}) rotate(${label.rotate})`)
182
182
  .attr('data-qa', (_c = label.qa) !== null && _c !== void 0 ? _c : null);
@@ -1,5 +1,5 @@
1
1
  import { getUniqId } from '@gravity-ui/uikit';
2
- import { calculateSin, formatAxisTickLabel, getBandsPosition, getLabelsSize, getMinSpaceBetween, getTextSizeFn, getTextWithElipsis, } from '../../core/utils';
2
+ import { calculateCos, calculateSin, formatAxisTickLabel, getBandsPosition, getLabelsSize, getMinSpaceBetween, getTextSizeFn, getTextWithElipsis, } from '../../core/utils';
3
3
  import { getXAxisTickValues } from '../../core/utils/axis/x-axis';
4
4
  import { getMultilineTitleContentRows } from '../utils/axis-title';
5
5
  async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxWidth, axisWidth, boundsOffsetLeft, boundsOffsetRight, }) {
@@ -56,7 +56,9 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxWid
56
56
  x = left + actualTextWidth / 2 - xOffset;
57
57
  }
58
58
  const yOffset = rotation <= 0 ? textSize.width * calculateSin(a) : 0;
59
- const y = top + yOffset + axis.labels.margin;
59
+ const hOffset = textSize.hangingOffset;
60
+ const y = top + yOffset + axis.labels.margin + hOffset * calculateCos(rotation);
61
+ x -= hOffset * calculateSin(rotation);
60
62
  const svgLabel = {
61
63
  title: ((_a = content[0]) === null || _a === void 0 ? void 0 : _a.text) === text ? undefined : text,
62
64
  content,
@@ -69,7 +71,7 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxWid
69
71
  return svgLabel;
70
72
  }
71
73
  export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRight, boundsWidth, height, scale, series, split, yAxis, }) {
72
- var _a, _b, _c, _d, _e, _f, _g;
74
+ var _a, _b, _c, _d, _e, _f, _g, _h;
73
75
  const xAxisItems = [];
74
76
  const splitPlots = (_a = split === null || split === void 0 ? void 0 : split.plots) !== null && _a !== void 0 ? _a : [];
75
77
  for (let plotIndex = 0; plotIndex < splitPlots.length; plotIndex++) {
@@ -201,7 +203,8 @@ export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRig
201
203
  const titleContent = [];
202
204
  const titleMaxWidth = axisWidth;
203
205
  if (axis.title.maxRowCount > 1) {
204
- titleContent.push(...(await getMultilineTitleContentRows({ axis, titleMaxWidth })));
206
+ const rows = await getMultilineTitleContentRows({ axis, titleMaxWidth });
207
+ titleContent.push(...rows);
205
208
  }
206
209
  else {
207
210
  const text = await getTextWithElipsis({
@@ -209,11 +212,12 @@ export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRig
209
212
  maxWidth: titleMaxWidth,
210
213
  getTextWidth: async (s) => (await getTitleTextSize(s)).width,
211
214
  });
215
+ const titleSize = await getTitleTextSize(text);
212
216
  titleContent.push({
213
217
  text,
214
218
  x: 0,
215
- y: 0,
216
- size: await getTitleTextSize(text),
219
+ y: titleSize.hangingOffset,
220
+ size: titleSize,
217
221
  });
218
222
  }
219
223
  const titleTextSize = titleContent.reduce((acc, item) => {
@@ -258,14 +262,14 @@ export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRig
258
262
  const halfBandwidth = ((_f = (_e = axisScale.bandwidth) === null || _e === void 0 ? void 0 : _e.call(axisScale)) !== null && _f !== void 0 ? _f : 0) / 2;
259
263
  const startPos = halfBandwidth + Math.min(from, to);
260
264
  const endPos = Math.min(Math.abs(to - from), axisWidth - Math.min(from, to));
261
- const getPlotLabelSize = getTextSizeFn({ style: plotBand.label.style });
262
- const labelSize = plotBand.label.text
263
- ? await getPlotLabelSize(plotBand.label.text)
264
- : null;
265
265
  const plotBandWidth = Math.min(endPos, axisWidth);
266
266
  if (plotBandWidth < 0) {
267
267
  continue;
268
268
  }
269
+ const getPlotLabelSize = getTextSizeFn({ style: plotBand.label.style });
270
+ const labelSize = plotBand.label.text
271
+ ? await getPlotLabelSize(plotBand.label.text)
272
+ : null;
269
273
  plotBands.push({
270
274
  layerPlacement: plotBand.layerPlacement,
271
275
  x: Math.max(0, startPos),
@@ -278,8 +282,8 @@ export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRig
278
282
  ? {
279
283
  text: plotBand.label.text,
280
284
  style: plotBand.label.style,
281
- x: plotBand.label.padding,
282
- y: plotBand.label.padding + ((_g = labelSize === null || labelSize === void 0 ? void 0 : labelSize.width) !== null && _g !== void 0 ? _g : 0),
285
+ x: plotBand.label.padding + ((_g = labelSize === null || labelSize === void 0 ? void 0 : labelSize.hangingOffset) !== null && _g !== void 0 ? _g : 0),
286
+ y: plotBand.label.padding + ((_h = labelSize === null || labelSize === void 0 ? void 0 : labelSize.width) !== null && _h !== void 0 ? _h : 0),
283
287
  rotate: -90,
284
288
  qa: plotBand.label.qa,
285
289
  }
@@ -305,7 +309,7 @@ export async function prepareXAxisData({ axis, boundsOffsetLeft, boundsOffsetRig
305
309
  label = {
306
310
  text: plotLine.label.text,
307
311
  style: plotLine.label.style,
308
- x: plotLineValue - plotLine.label.padding - size.height,
312
+ x: plotLineValue - plotLine.label.padding - size.height + size.hangingOffset,
309
313
  y: plotLine.label.padding + size.width,
310
314
  rotate: -90,
311
315
  qa: plotLine.label.qa,
@@ -50,7 +50,7 @@ export const AxisY = (props) => {
50
50
  .html((d) => d.text)
51
51
  .attr('x', (d) => d.x)
52
52
  .attr('y', (d) => d.y)
53
- .attr('dominant-baseline', 'text-after-edge')
53
+ .attr('dominant-baseline', 'hanging')
54
54
  .attr('text-anchor', 'start');
55
55
  }
56
56
  if (preparedAxisData.domain) {
@@ -103,7 +103,7 @@ export const AxisY = (props) => {
103
103
  .attr('y', (d) => d.y)
104
104
  .attr('text-anchor', 'start')
105
105
  .attr('class', labelClassName)
106
- .style('dominant-baseline', 'text-before-edge')
106
+ .style('dominant-baseline', 'hanging')
107
107
  .style('font-size', label.style.fontSize)
108
108
  .style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '');
109
109
  }
@@ -139,7 +139,7 @@ export const AxisY = (props) => {
139
139
  .style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '')
140
140
  .style('font-size', label.style.fontSize)
141
141
  .style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
142
- .style('dominant-baseline', 'text-before-edge')
142
+ .style('dominant-baseline', 'hanging')
143
143
  .attr('data-qa', (_c = label.qa) !== null && _c !== void 0 ? _c : null)
144
144
  .attr('x', label.x)
145
145
  .attr('y', label.y);
@@ -181,7 +181,7 @@ export const AxisY = (props) => {
181
181
  .style('fill', (_a = label.style.fontColor) !== null && _a !== void 0 ? _a : '')
182
182
  .style('font-size', label.style.fontSize)
183
183
  .style('font-weight', (_b = label.style.fontWeight) !== null && _b !== void 0 ? _b : '')
184
- .style('dominant-baseline', 'text-before-edge')
184
+ .style('dominant-baseline', 'hanging')
185
185
  .attr('data-qa', (_c = label.qa) !== null && _c !== void 0 ? _c : null)
186
186
  .attr('x', label.x)
187
187
  .attr('y', label.y);
@@ -3,7 +3,7 @@ import { calculateCos, calculateSin, formatAxisTickLabel, getBandsPosition, getL
3
3
  import { prepareHtmlYAxisTitle, prepareSvgYAxisTitle } from './prepare-axis-title';
4
4
  import { getTickValues } from './utils';
5
5
  async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHeight, topOffset, }) {
6
- var _a;
6
+ var _a, _b;
7
7
  const originalTextSize = await getTextSize(text);
8
8
  // Currently, a preliminary label calculation is used to build the chart - we cannot exceed it here.
9
9
  // Therefore, we rely on a pre-calculated number instead of the current maximum label width.
@@ -59,7 +59,9 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
59
59
  }
60
60
  }
61
61
  content.forEach((row) => {
62
+ var _a;
62
63
  row.y -= newLabelHeight / 2;
64
+ row.y += (_a = originalTextSize.hangingOffset) !== null && _a !== void 0 ? _a : 0;
63
65
  });
64
66
  size.width = newLabelWidth;
65
67
  size.height = newLabelHeight;
@@ -87,7 +89,8 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
87
89
  ? textSize.height / calculateSin(axis.labels.rotation)
88
90
  : textSize.height;
89
91
  const x = axis.position === 'left' ? -textSize.width : 0;
90
- const y = Math.max(-topOffset - top, -actualTextHeight / 2);
92
+ const y = Math.max(-topOffset - top, -actualTextHeight / 2) +
93
+ ((_a = originalTextSize.hangingOffset) !== null && _a !== void 0 ? _a : 0);
91
94
  content.push({
92
95
  text: rowText,
93
96
  x,
@@ -98,7 +101,7 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
98
101
  const x = axis.position === 'left' ? left - axis.labels.margin : left + axis.labels.margin;
99
102
  const y = top;
100
103
  const svgLabel = {
101
- title: content.length > 1 || ((_a = content[0]) === null || _a === void 0 ? void 0 : _a.text) !== text ? text : undefined,
104
+ title: content.length > 1 || ((_b = content[0]) === null || _b === void 0 ? void 0 : _b.text) !== text ? text : undefined,
102
105
  content: content,
103
106
  style: axis.labels.style,
104
107
  size: size,
@@ -109,7 +112,7 @@ async function getSvgAxisLabel({ getTextSize, text, axis, top, left, labelMaxHei
109
112
  return svgLabel;
110
113
  }
111
114
  export async function prepareYAxisData({ axis, split, scale, top: topOffset, width, height, series, }) {
112
- var _a, _b, _c;
115
+ var _a, _b, _c, _d, _e;
113
116
  const axisPlotTopPosition = ((_a = split === null || split === void 0 ? void 0 : split.plots[axis.plotIndex]) === null || _a === void 0 ? void 0 : _a.top) || 0;
114
117
  const axisHeight = ((_b = split === null || split === void 0 ? void 0 : split.plots[axis.plotIndex]) === null || _b === void 0 ? void 0 : _b.height) || height;
115
118
  const domainX = axis.position === 'left' ? 0 : width;
@@ -212,23 +215,22 @@ export async function prepareYAxisData({ axis, split, scale, top: topOffset, wid
212
215
  axisLabelsWidth: labelsWidth,
213
216
  });
214
217
  const plotBands = [];
215
- axis.plotBands.forEach((plotBand) => {
216
- var _a, _b;
218
+ for (const plotBand of axis.plotBands) {
217
219
  const axisScale = scale;
218
220
  const { from, to } = getBandsPosition({
219
221
  band: plotBand,
220
222
  axisScale,
221
223
  axis: 'y',
222
224
  });
223
- const halfBandwidth = ((_b = (_a = axisScale.bandwidth) === null || _a === void 0 ? void 0 : _a.call(axisScale)) !== null && _b !== void 0 ? _b : 0) / 2;
225
+ const halfBandwidth = ((_e = (_d = axisScale.bandwidth) === null || _d === void 0 ? void 0 : _d.call(axisScale)) !== null && _e !== void 0 ? _e : 0) / 2;
224
226
  const startPos = halfBandwidth + Math.min(from, to);
225
227
  const endPos = Math.min(Math.abs(to - from), axisHeight - Math.min(from, to));
226
228
  const top = Math.max(0, startPos);
227
229
  const plotBandHeight = Math.min(endPos, axisHeight);
228
230
  if (plotBandHeight < 0) {
229
- return;
231
+ continue;
230
232
  }
231
- plotBands.push({
233
+ const plotBandItem = {
232
234
  layerPlacement: plotBand.layerPlacement,
233
235
  x: 0,
234
236
  y: axisPlotTopPosition + top,
@@ -236,17 +238,21 @@ export async function prepareYAxisData({ axis, split, scale, top: topOffset, wid
236
238
  height: plotBandHeight,
237
239
  color: plotBand.color,
238
240
  opacity: plotBand.opacity,
239
- label: plotBand.label.text
240
- ? {
241
- text: plotBand.label.text,
242
- style: plotBand.label.style,
243
- x: plotBand.label.padding,
244
- y: plotBand.label.padding,
245
- qa: plotBand.label.qa,
246
- }
247
- : null,
248
- });
249
- });
241
+ label: null,
242
+ };
243
+ if (plotBand.label.text) {
244
+ const getPlotLabelSize = getTextSizeFn({ style: plotBand.label.style });
245
+ const labelSize = await getPlotLabelSize(plotBand.label.text);
246
+ plotBandItem.label = {
247
+ text: plotBand.label.text,
248
+ style: plotBand.label.style,
249
+ x: plotBand.label.padding,
250
+ y: plotBand.label.padding + labelSize.hangingOffset,
251
+ qa: plotBand.label.qa,
252
+ };
253
+ }
254
+ plotBands.push(plotBandItem);
255
+ }
250
256
  const plotLines = [];
251
257
  for (let i = 0; i < axis.plotLines.length; i++) {
252
258
  const plotLine = axis.plotLines[i];
@@ -267,7 +273,7 @@ export async function prepareYAxisData({ axis, split, scale, top: topOffset, wid
267
273
  text: plotLine.label.text,
268
274
  style: plotLine.label.style,
269
275
  x: plotLine.label.padding,
270
- y: Math.max(0, plotLineValue - size.height - plotLine.label.padding),
276
+ y: Math.max(0, plotLineValue - size.height - plotLine.label.padding + size.hangingOffset),
271
277
  qa: plotLine.label.qa,
272
278
  };
273
279
  }
@@ -11,7 +11,11 @@ export async function prepareSvgYAxisTitle({ axis, axisTop, axisHeight, axisWidt
11
11
  const titleContent = [];
12
12
  const titleMaxWidth = rotateAngle === 0 ? axis.title.maxWidth : sin * axisHeight;
13
13
  if (axis.title.maxRowCount > 1) {
14
- titleContent.push(...(await getMultilineTitleContentRows({ axis, titleMaxWidth })));
14
+ const rows = await getMultilineTitleContentRows({ axis, titleMaxWidth });
15
+ rows.forEach((r) => {
16
+ r.y -= r.size.height;
17
+ });
18
+ titleContent.push(...rows);
15
19
  }
16
20
  else {
17
21
  const text = await getTextWithElipsis({
@@ -19,11 +23,12 @@ export async function prepareSvgYAxisTitle({ axis, axisTop, axisHeight, axisWidt
19
23
  maxWidth: titleMaxWidth,
20
24
  getTextWidth: async (s) => (await getTitleTextSize(s)).width,
21
25
  });
26
+ const titleSize = await getTitleTextSize(text);
22
27
  titleContent.push({
23
28
  text,
24
29
  x: 0,
25
- y: 0,
26
- size: await getTitleTextSize(text),
30
+ y: titleSize.hangingOffset - titleSize.height,
31
+ size: titleSize,
27
32
  });
28
33
  }
29
34
  const originalTextSize = titleContent.reduce((acc, item) => {
@@ -4,7 +4,7 @@
4
4
  .gcharts-y-axis__label {
5
5
  fill: var(--g-color-text-secondary);
6
6
  stroke: none;
7
- dominant-baseline: text-after-edge;
7
+ dominant-baseline: text-bottom;
8
8
  }
9
9
  .gcharts-y-axis__tick {
10
10
  stroke: var(--gcharts-axis-tick-color);
@@ -20,7 +20,7 @@ import { useChartInnerHandlers } from './useChartInnerHandlers';
20
20
  import { useChartInnerProps } from './useChartInnerProps';
21
21
  import { useChartInnerState } from './useChartInnerState';
22
22
  import { useDefaultState } from './useDefaultState';
23
- import { getPreparedChart, getPreparedTitle, getPreparedTooltip, getResetZoomButtonStyle, useAsyncState, useDebouncedValue, } from './utils';
23
+ import { getPreparedTooltip, getResetZoomButtonStyle, useAsyncState, useDebouncedValue, } from './utils';
24
24
  import './styles.css';
25
25
  const b = block('chart');
26
26
  const DEBOUNCED_VALUE_DELAY = 10;
@@ -36,16 +36,6 @@ export const ChartInner = (props) => {
36
36
  const rangeSliderRef = React.useRef(null);
37
37
  const dispatcher = React.useMemo(() => getDispatcher(), []);
38
38
  const clipPathId = useUniqId();
39
- const preparedTitle = React.useMemo(() => {
40
- return getPreparedTitle({ title: data.title });
41
- }, [data.title]);
42
- const preparedChart = React.useMemo(() => {
43
- return getPreparedChart({
44
- chart: data.chart,
45
- seriesData: data.series.data,
46
- preparedTitle,
47
- });
48
- }, [data.chart, data.series.data, preparedTitle]);
49
39
  const preparedTooltip = React.useMemo(() => {
50
40
  return getPreparedTooltip({
51
41
  tooltip: data.tooltip,
@@ -62,10 +52,9 @@ export const ChartInner = (props) => {
62
52
  preparedRangeSlider,
63
53
  tooltip: preparedTooltip,
64
54
  });
65
- const { activeLegendItems, allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, legendConfig, legendItems, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, shapes, shapesData, shapesReady, xAxis, xScale, yAxis, yScale, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
55
+ const { activeLegendItems, allPreparedSeries, boundsHeight, boundsOffsetLeft, boundsOffsetTop, boundsWidth, handleLegendItemClick, legendConfig, legendItems, preparedLegend, preparedSeries, preparedSeriesOptions, preparedSplit, shapes, shapesData, shapesReady, xAxis, xScale, yAxis, yScale, preparedTitle, preparedChart, } = useChartInnerProps(Object.assign(Object.assign({}, props), { clipPathId,
66
56
  dispatcher,
67
- htmlLayout, plotNode: plotRef.current, preparedChart,
68
- rangeSliderState,
57
+ htmlLayout, plotNode: plotRef.current, rangeSliderState,
69
58
  updateZoomState,
70
59
  zoomState }));
71
60
  const prevWidth = usePrevious(width);
@@ -269,6 +258,7 @@ export const ChartInner = (props) => {
269
258
  shapes,
270
259
  React.createElement("g", { ref: plotAfterRef })),
271
260
  ((_e = xAxis === null || xAxis === void 0 ? void 0 : xAxis.rangeSlider) === null || _e === void 0 ? void 0 : _e.enabled) &&
261
+ preparedChart &&
272
262
  preparedLegend &&
273
263
  debouncedAllPreparedSeries &&
274
264
  preparedSeriesOptions && (React.createElement(RangeSlider, { activeLegendItems: activeLegendItems !== null && activeLegendItems !== void 0 ? activeLegendItems : [], boundsOffsetLeft: debouncedOffsetLeft, boundsWidth: debouncedBoundsWidth, height: height, htmlLayout: htmlLayout, onUpdate: updateRangeSliderState, preparedChart: preparedChart, preparedLegend: preparedLegend, preparedSeries: debouncedAllPreparedSeries, preparedSeriesOptions: preparedSeriesOptions, preparedRangeSlider: xAxis.rangeSlider, rangeSliderState: rangeSliderState, ref: rangeSliderRef, width: width, xAxis: data.xAxis, yAxis: data.yAxis, legendConfig: legendConfig })),
@@ -281,7 +271,7 @@ export const ChartInner = (props) => {
281
271
  React.createElement("div", { className: b('html-layer'), ref: setHtmlLayout, style: {
282
272
  '--g-html-layout-transform': `translate(${boundsOffsetLeft}px, ${boundsOffsetTop}px)`,
283
273
  } }),
284
- Object.keys(zoomState).length > 0 && preparedChart.zoom && (React.createElement(Button, { className: b('reset-zoom-button'), onClick: () => {
274
+ Object.keys(zoomState).length > 0 && (preparedChart === null || preparedChart === void 0 ? void 0 : preparedChart.zoom) && (React.createElement(Button, { className: b('reset-zoom-button'), onClick: () => {
285
275
  var _a;
286
276
  updateZoomState({});
287
277
  (_a = rangeSliderRef.current) === null || _a === void 0 ? void 0 : _a.resetState();
@@ -9,7 +9,6 @@ type Props = ChartInnerProps & {
9
9
  dispatcher: Dispatch<object>;
10
10
  htmlLayout: HTMLElement | null;
11
11
  plotNode: SVGGElement | null;
12
- preparedChart: PreparedChart;
13
12
  updateZoomState: (nextZoomState: Partial<ZoomState>) => void;
14
13
  zoomState: Partial<ZoomState>;
15
14
  rangeSliderState?: RangeSliderState;
@@ -25,6 +24,10 @@ export declare function useChartInnerProps(props: Props): {
25
24
  shapesData: ShapeData[];
26
25
  shapesReady: boolean;
27
26
  handleLegendItemClick: OnLegendItemClick;
27
+ preparedTitle: (import("../../types").ChartTitle & {
28
+ height: number;
29
+ }) | undefined;
30
+ preparedChart: PreparedChart | undefined;
28
31
  allPreparedSeries?: PreparedSeries[] | undefined;
29
32
  legendConfig?: LegendConfig | undefined;
30
33
  legendItems?: LegendItem[][] | undefined;
@@ -6,7 +6,7 @@ import { getPreparedOptions } from '../../core/series/prepare-options';
6
6
  import { getChartDimensions, getEffectiveXRange, getSortedSeriesData, getYAxisWidth, getZoomedSeriesData, isAxisRelatedSeries, } from '../../core/utils';
7
7
  import { createScales, getAxes, getPreparedSeries, getShapes, getSplit, getVisibleSeries, useZoom, } from '../../hooks';
8
8
  import { getActiveLegendItems, getAllLegendItems } from '../../hooks/useSeries/utils';
9
- import { getNormalizedXAxis, getNormalizedYAxis, recalculateYAxisLabelsWidth } from './utils';
9
+ import { getNormalizedXAxis, getNormalizedYAxis, getPreparedChart, getPreparedTitle, recalculateYAxisLabelsWidth, } from './utils';
10
10
  import { hasAtLeastOneSeriesDataPerPlot } from './utils/common';
11
11
  const CLIP_PATH_BY_SERIES_TYPE = {
12
12
  [SERIES_TYPE.Scatter]: false,
@@ -37,8 +37,8 @@ function getBoundsOffsetLeft(args) {
37
37
  return chartMarginLeft + legendOffset + leftAxisWidth;
38
38
  }
39
39
  export function useChartInnerProps(props) {
40
- var _a, _b, _c, _d, _e, _f;
41
- const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, preparedChart, rangeSliderState, width, updateZoomState, zoomState, } = props;
40
+ var _a, _b, _c, _d, _e, _f, _g, _h;
41
+ const { clipPathId, data, dispatcher, height, htmlLayout, plotNode, rangeSliderState, width, updateZoomState, zoomState, } = props;
42
42
  const [selectedLegendItems, setSelectedLegendItems] = React.useState(null);
43
43
  const [chartState, setState] = React.useState(null);
44
44
  const prevStateValue = React.useRef(chartState);
@@ -50,6 +50,12 @@ export function useChartInnerProps(props) {
50
50
  (async function () {
51
51
  var _a, _b, _c;
52
52
  const chartDataChanged = !(previousChartData.current && isEqual(previousChartData.current, data));
53
+ const preparedTitle = await getPreparedTitle({ title: data.title });
54
+ const preparedChart = getPreparedChart({
55
+ chart: data.chart,
56
+ seriesData: data.series.data,
57
+ preparedTitle,
58
+ });
53
59
  const colors = (_a = data.colors) !== null && _a !== void 0 ? _a : DEFAULT_PALETTE;
54
60
  const normalizedSeriesData = getSortedSeriesData({
55
61
  seriesData: data.series.data,
@@ -119,7 +125,7 @@ export function useChartInnerProps(props) {
119
125
  let yScale;
120
126
  let boundsWidth = 0;
121
127
  let boundsHeight = 0;
122
- const calculateAxisBasedProps = () => {
128
+ const calculateAxisBasedProps = async () => {
123
129
  const chartDimensions = getChartDimensions({
124
130
  height,
125
131
  margin: preparedChart.margin,
@@ -132,7 +138,11 @@ export function useChartInnerProps(props) {
132
138
  });
133
139
  boundsHeight = chartDimensions.boundsHeight;
134
140
  boundsWidth = chartDimensions.boundsWidth;
135
- preparedSplit = getSplit({ split: data.split, boundsHeight, chartWidth: width });
141
+ preparedSplit = await getSplit({
142
+ split: data.split,
143
+ boundsHeight,
144
+ chartWidth: width,
145
+ });
136
146
  if (preparedSeries.some(isAxisRelatedSeries)) {
137
147
  ({ xScale, yScale } = createScales({
138
148
  boundsWidth,
@@ -147,7 +157,7 @@ export function useChartInnerProps(props) {
147
157
  }));
148
158
  }
149
159
  };
150
- calculateAxisBasedProps();
160
+ await calculateAxisBasedProps();
151
161
  const newYAxis = await recalculateYAxisLabelsWidth({
152
162
  seriesData: preparedSeries,
153
163
  yAxis,
@@ -155,7 +165,7 @@ export function useChartInnerProps(props) {
155
165
  });
156
166
  if (!isEqual(yAxis, newYAxis)) {
157
167
  yAxis = newYAxis;
158
- calculateAxisBasedProps();
168
+ await calculateAxisBasedProps();
159
169
  }
160
170
  const { shapes, shapesData } = await getShapes({
161
171
  boundsWidth,
@@ -209,6 +219,8 @@ export function useChartInnerProps(props) {
209
219
  yAxis,
210
220
  yScale,
211
221
  activeLegendItems,
222
+ preparedChart,
223
+ preparedTitle,
212
224
  };
213
225
  if (currentRunRef.current === currentRun) {
214
226
  if (!isEqual(prevStateValue.current, newStateValue)) {
@@ -225,7 +237,6 @@ export function useChartInnerProps(props) {
225
237
  selectedLegendItems,
226
238
  zoomState,
227
239
  rangeSliderState,
228
- preparedChart,
229
240
  dispatcher,
230
241
  htmlLayout,
231
242
  clipPathId,
@@ -277,15 +288,15 @@ export function useChartInnerProps(props) {
277
288
  plotContainerHeight: boundsHeight,
278
289
  plotContainerWidth: boundsWidth,
279
290
  preparedSplit: chartState === null || chartState === void 0 ? void 0 : chartState.preparedSplit,
280
- preparedZoom: preparedChart.zoom,
291
+ preparedZoom: (_e = (_d = chartState === null || chartState === void 0 ? void 0 : chartState.preparedChart) === null || _d === void 0 ? void 0 : _d.zoom) !== null && _e !== void 0 ? _e : null,
281
292
  xAxis,
282
293
  xScale: chartState === null || chartState === void 0 ? void 0 : chartState.xScale,
283
294
  yAxis,
284
295
  yScale: chartState === null || chartState === void 0 ? void 0 : chartState.yScale,
285
296
  });
286
297
  // additional end
287
- return Object.assign(Object.assign({}, chartState), { preparedSeries, boundsOffsetLeft: (_d = chartState === null || chartState === void 0 ? void 0 : chartState.boundsOffsetLeft) !== null && _d !== void 0 ? _d : 0, boundsOffsetTop: (_e = chartState === null || chartState === void 0 ? void 0 : chartState.boundsOffsetTop) !== null && _e !== void 0 ? _e : 0, boundsHeight,
298
+ return Object.assign(Object.assign({}, chartState), { preparedSeries, boundsOffsetLeft: (_f = chartState === null || chartState === void 0 ? void 0 : chartState.boundsOffsetLeft) !== null && _f !== void 0 ? _f : 0, boundsOffsetTop: (_g = chartState === null || chartState === void 0 ? void 0 : chartState.boundsOffsetTop) !== null && _g !== void 0 ? _g : 0, boundsHeight,
288
299
  boundsWidth,
289
300
  xAxis,
290
- yAxis, shapesData: (_f = chartState === null || chartState === void 0 ? void 0 : chartState.shapesData) !== null && _f !== void 0 ? _f : [], shapesReady: Boolean(chartState), handleLegendItemClick });
301
+ yAxis, shapesData: (_h = chartState === null || chartState === void 0 ? void 0 : chartState.shapesData) !== null && _h !== void 0 ? _h : [], shapesReady: Boolean(chartState), handleLegendItemClick, preparedTitle: chartState === null || chartState === void 0 ? void 0 : chartState.preparedTitle, preparedChart: chartState === null || chartState === void 0 ? void 0 : chartState.preparedChart });
291
302
  }
@@ -2,4 +2,4 @@ import type { PreparedTitle } from '../../../hooks/types';
2
2
  import type { ChartData } from '../../../types';
3
3
  export declare const getPreparedTitle: ({ title, }: {
4
4
  title: ChartData["title"];
5
- }) => PreparedTitle | undefined;
5
+ }) => Promise<PreparedTitle | undefined>;
@@ -1,8 +1,8 @@
1
1
  import get from 'lodash/get';
2
- import { getHorizontalSvgTextHeight } from '../../../core/utils';
2
+ import { getTextSizeFn } from '../../../core/utils';
3
3
  const DEFAULT_TITLE_FONT_SIZE = '15px';
4
4
  const TITLE_PADDINGS = 8 * 2;
5
- export const getPreparedTitle = ({ title, }) => {
5
+ export const getPreparedTitle = async ({ title, }) => {
6
6
  var _a, _b, _c, _d, _e, _f;
7
7
  const titleText = get(title, 'text');
8
8
  const titleStyle = {
@@ -11,7 +11,7 @@ export const getPreparedTitle = ({ title, }) => {
11
11
  fontColor: (_f = (_e = title === null || title === void 0 ? void 0 : title.style) === null || _e === void 0 ? void 0 : _e.fontColor) !== null && _f !== void 0 ? _f : 'var(--g-color-text-primary)',
12
12
  };
13
13
  const titleHeight = titleText
14
- ? getHorizontalSvgTextHeight({ text: titleText, style: titleStyle }) + TITLE_PADDINGS
14
+ ? (await getTextSizeFn({ style: titleStyle })(titleText)).height + TITLE_PADDINGS
15
15
  : 0;
16
16
  const preparedTitle = titleText
17
17
  ? { text: titleText, style: titleStyle, height: titleHeight, qa: title === null || title === void 0 ? void 0 : title.qa }
@@ -3,7 +3,7 @@ import { scaleLinear } from 'd3-scale';
3
3
  import { select } from 'd3-selection';
4
4
  import { symbol } from 'd3-shape';
5
5
  import { CONTINUOUS_LEGEND_SIZE } from '../../core/constants';
6
- import { createGradientRect, getContinuesColorFn, getLabelsSize, getSymbol, getUniqId, } from '../../core/utils';
6
+ import { createGradientRect, getContinuesColorFn, getSymbol, getTextSizeFn, getUniqId, } from '../../core/utils';
7
7
  import { axisBottom } from '../../core/utils/axis-generators';
8
8
  import { formatNumber } from '../../libs';
9
9
  import { block } from '../../utils';
@@ -116,15 +116,13 @@ function renderLegendSymbol(args) {
116
116
  }
117
117
  case 'symbol': {
118
118
  const symbolAreaSize = Math.pow(d.symbol.width, 2);
119
- const y = legendLineHeight / 2;
120
119
  const bboxWidth = d.symbol.bboxWidth;
120
+ const translateX = x + bboxWidth / 2;
121
+ const translateY = legendLineHeight / 2;
121
122
  element
122
123
  .append('svg:path')
123
124
  .attr('d', () => symbol(scatterSymbol, symbolAreaSize)())
124
- .attr('transform', () => {
125
- const translateX = x + bboxWidth / 2;
126
- return 'translate(' + translateX + ',' + y + ')';
127
- })
125
+ .attr('transform', 'translate(' + translateX + ',' + translateY + ')')
128
126
  .attr('class', className)
129
127
  .style('fill', color);
130
128
  break;
@@ -148,6 +146,7 @@ export const Legend = (props) => {
148
146
  const svgElement = select(ref.current);
149
147
  svgElement.selectAll('*').remove();
150
148
  svgElement.style('opacity', 0);
149
+ const isMac = navigator.platform.toUpperCase().includes('MAC');
151
150
  const htmlElement = select(htmlLayout);
152
151
  htmlElement.selectAll('[data-legend]').remove();
153
152
  const htmlContainer = legend.html
@@ -173,7 +172,11 @@ export const Legend = (props) => {
173
172
  .append('g')
174
173
  .attr('class', b('item'))
175
174
  .on('click', function (e, d) {
176
- onItemClick({ id: d.id, name: d.name, metaKey: e.metaKey });
175
+ onItemClick({
176
+ id: d.id,
177
+ name: d.name,
178
+ metaKey: isMac ? e.metaKey : e.ctrlKey,
179
+ });
177
180
  onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate();
178
181
  });
179
182
  const getXPosition = (i) => {
@@ -212,7 +215,11 @@ export const Legend = (props) => {
212
215
  return '0px';
213
216
  })
214
217
  .on('click', function (e, d) {
215
- onItemClick({ id: d.id, name: d.name, metaKey: e.metaKey });
218
+ onItemClick({
219
+ id: d.id,
220
+ name: d.name,
221
+ metaKey: isMac ? e.metaKey : e.ctrlKey,
222
+ });
216
223
  onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate();
217
224
  })
218
225
  .html((d) => d.text);
@@ -225,6 +232,7 @@ export const Legend = (props) => {
225
232
  legendItem.symbol.bboxWidth +
226
233
  legendItem.symbol.padding);
227
234
  })
235
+ .attr('y', legend.hangingOffset)
228
236
  .attr('height', legend.height)
229
237
  .attr('class', function (d) {
230
238
  const mods = { selected: d.visible, unselected: !d.visible };
@@ -355,10 +363,7 @@ export const Legend = (props) => {
355
363
  }
356
364
  const legendTitleClassname = b('title');
357
365
  if (legend.title.enable) {
358
- const { maxWidth: titleWidth } = await getLabelsSize({
359
- labels: [legend.title.text],
360
- style: legend.title.style,
361
- });
366
+ const { width: titleWidth } = await getTextSizeFn({ style: legend.title.style })(legend.title.text);
362
367
  let dx = 0;
363
368
  switch (legend.title.align) {
364
369
  case 'center': {
@@ -381,10 +386,11 @@ export const Legend = (props) => {
381
386
  .attr('class', legendTitleClassname)
382
387
  .append('text')
383
388
  .attr('dx', dx)
389
+ .attr('y', legend.title.hangingOffset)
384
390
  .attr('font-weight', (_f = legend.title.style.fontWeight) !== null && _f !== void 0 ? _f : null)
385
391
  .attr('font-size', (_g = legend.title.style.fontSize) !== null && _g !== void 0 ? _g : null)
386
392
  .attr('fill', (_h = legend.title.style.fontColor) !== null && _h !== void 0 ? _h : null)
387
- .style('dominant-baseline', 'text-before-edge')
393
+ .style('dominant-baseline', 'hanging')
388
394
  .html(legend.title.text);
389
395
  }
390
396
  else {
@@ -19,7 +19,7 @@
19
19
  }
20
20
  .gcharts-legend__item-text {
21
21
  fill: var(--g-color-text-secondary);
22
- dominant-baseline: text-before-edge;
22
+ dominant-baseline: hanging;
23
23
  }
24
24
  .gcharts-legend__item-text_unselected {
25
25
  fill: var(--g-color-text-hint);
@@ -15,5 +15,5 @@ export function Row(props) {
15
15
  return (React.createElement("tr", { className: b('content-row', { active, striped }, className), style: style },
16
16
  colorItem && React.createElement("td", { className: b('content-row-color-cell') }, colorItem),
17
17
  React.createElement("td", { className: b('content-row-label-cell') }, label),
18
- value && React.createElement("td", { className: b('content-row-value-cell') }, value)));
18
+ value && (React.createElement("td", { className: b('content-row-value-cell') }, typeof value === 'string' ? (React.createElement("span", { dangerouslySetInnerHTML: { __html: value } })) : (value)))));
19
19
  }