sea-chart 2.0.42 → 2.0.44

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 (60) hide show
  1. package/dist/components/color-popover/color-rules/rule-filters/number-input.js +2 -2
  2. package/dist/components/drill-down-settings/drill-down-fields-popover/index.js +2 -2
  3. package/dist/components/tooltip/index.js +11 -4
  4. package/dist/constants/index.js +8 -3
  5. package/dist/context.js +10 -7
  6. package/dist/model/base-model.js +4 -0
  7. package/dist/model/combination.js +4 -0
  8. package/dist/model/horizontal-bar.js +4 -0
  9. package/dist/model/horizontal-group-bar.js +5 -1
  10. package/dist/model/stacked-horizontal-bar.js +4 -0
  11. package/dist/settings/advance-bar-settings/style-settings.js +24 -7
  12. package/dist/settings/bar-settings/style-settings.js +24 -7
  13. package/dist/settings/combination-settings/style-settings.js +35 -10
  14. package/dist/settings/time-comparison-settings/style-settings.js +24 -7
  15. package/dist/settings/widgets/axis-title-font-settings/index.js +39 -0
  16. package/dist/utils/cell-format-utils.js +2 -2
  17. package/dist/utils/chart-utils/base-utils.js +49 -13
  18. package/dist/utils/chart-utils/original-data-utils/basic-chart-calculator.js +4 -3
  19. package/dist/utils/chart-utils/original-data-utils/mirror-calculator.js +3 -3
  20. package/dist/utils/chart-utils/original-data-utils/pivot-table-calculator.js +2 -2
  21. package/dist/utils/chart-utils/sql-statistics-utils.js +64 -18
  22. package/dist/utils/row-record-utils.js +21 -5
  23. package/dist/utils/sql/column-2-sql-column.js +2 -0
  24. package/dist/view/index.css +0 -17
  25. package/dist/view/wrapper/area-group.js +14 -25
  26. package/dist/view/wrapper/area.js +14 -27
  27. package/dist/view/wrapper/bar-compare.js +10 -15
  28. package/dist/view/wrapper/bar-custom-stack.js +19 -81
  29. package/dist/view/wrapper/bar-group.js +15 -50
  30. package/dist/view/wrapper/bar-stack.js +12 -40
  31. package/dist/view/wrapper/bar.js +10 -23
  32. package/dist/view/wrapper/basic-number-card.js +9 -2
  33. package/dist/view/wrapper/chart-component.js +461 -76
  34. package/dist/view/wrapper/combination.js +17 -29
  35. package/dist/view/wrapper/completeness-group.js +13 -59
  36. package/dist/view/wrapper/completeness.js +14 -60
  37. package/dist/view/wrapper/dashboard.js +8 -14
  38. package/dist/view/wrapper/funnel.js +6 -18
  39. package/dist/view/wrapper/heat-map.js +80 -50
  40. package/dist/view/wrapper/horizontal-bar-group.js +14 -25
  41. package/dist/view/wrapper/horizontal-bar-stack.js +16 -61
  42. package/dist/view/wrapper/horizontal-bar.js +10 -25
  43. package/dist/view/wrapper/index.js +3 -0
  44. package/dist/view/wrapper/line-group.js +11 -21
  45. package/dist/view/wrapper/line.js +11 -23
  46. package/dist/view/wrapper/map-bubble.js +9 -14
  47. package/dist/view/wrapper/map-world-bubble.js +9 -14
  48. package/dist/view/wrapper/map-world.js +9 -14
  49. package/dist/view/wrapper/map.js +9 -13
  50. package/dist/view/wrapper/mirror.js +7 -20
  51. package/dist/view/wrapper/pie.js +9 -12
  52. package/dist/view/wrapper/ring.js +10 -13
  53. package/dist/view/wrapper/scatter.js +13 -23
  54. package/dist/view/wrapper/table/pivot-table-display-name.js +2 -2
  55. package/dist/view/wrapper/table-element/components/formatter.js +2 -2
  56. package/dist/view/wrapper/table-element/components/records-body.js +8 -2
  57. package/dist/view/wrapper/table-element/components/records.js +2 -4
  58. package/dist/view/wrapper/treemap.js +8 -18
  59. package/dist/view/wrapper/trend.js +3 -2
  60. package/package.json +2 -2
@@ -4,7 +4,7 @@ import * as d3 from 'd3';
4
4
  import { cloneDeep, debounce } from 'lodash-es';
5
5
  import { getTableById, getTableColumnByKey } from 'dtable-utils';
6
6
  import { BaseUtils, formatXAxisLabel } from '../../utils';
7
- import { CHART_TYPE, CHART_SUMMARY_TYPE, LABEL_POSITION_TYPE, CHART_THEME_COLOR, EMPTY_NAME, THEME_NAME_MAP, CHART_STYLE_COLORS } from '../../constants';
7
+ import { CHART_TYPE, CHART_SUMMARY_TYPE, LABEL_POSITION_TYPE, CHART_THEME_COLOR, EMPTY_NAME, THEME_NAME_MAP, CHART_STYLE_COLORS, DEFAULT_AXIS_LABEL_FONT_COLOR, DEFAULT_AXIS_LABEL_FONT_COLOR_DARK, DEFAULT_AXIS_LABEL_FONT_SIZE } from '../../constants';
8
8
  import { FILL_BORDER_COLOR_MAP } from '../../constants/style';
9
9
  import intl from '../../intl';
10
10
  import '../../assets/css/sea-chart-d3-tooltip.css';
@@ -25,12 +25,23 @@ export default class ChartComponent extends Component {
25
25
  this.destroyChart();
26
26
  this.createChart();
27
27
  this.drawChart();
28
- if (this.needRenderAxisLabel) {
29
- this.renderAxisLabel(this.props.chart, this.props.tables);
30
- }
31
28
  }, 300);
32
29
  this.destroyChart = () => {
33
30
  this.chart && this.chart.node() && this.chart.node().remove();
31
+ this.container && this.setAxisTitlePadding(this.container, {
32
+ left: null,
33
+ bottom: null,
34
+ right: null
35
+ });
36
+ this._clipMaskByRect.clear();
37
+ this._defs = null;
38
+ };
39
+ this.toggleRecords = function (statisticRecord) {
40
+ if (!statisticRecord) return;
41
+ for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
42
+ args[_key - 1] = arguments[_key];
43
+ }
44
+ _this.props.toggleRecords && _this.props.toggleRecords(statisticRecord, ...args);
34
45
  };
35
46
  this.getTitle = (tables, table_id, yAxisType, yAxisKey) => {
36
47
  return yAxisType === CHART_SUMMARY_TYPE.COUNT ? intl.get('Amount') : this.getAxisLabel(tables, table_id, yAxisKey);
@@ -44,6 +55,30 @@ export default class ChartComponent extends Component {
44
55
  const column = this.getColumn(tables, tableId, columnKey);
45
56
  return column.name || '';
46
57
  };
58
+ this.getAxisTitleHeight = fontSize => {
59
+ const safeFontSize = Number(fontSize) || DEFAULT_AXIS_LABEL_FONT_SIZE;
60
+ return Math.max(this.axisTitleMinHeight, safeFontSize + 4);
61
+ };
62
+ this.setAxisTitlePadding = function (chartContainer) {
63
+ let {
64
+ left = null,
65
+ bottom = null,
66
+ right = null
67
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
68
+ if (!chartContainer) return;
69
+ chartContainer.style.paddingLeft = left != null ? `${left}px` : '';
70
+ chartContainer.style.paddingBottom = bottom != null ? `${bottom}px` : '';
71
+ chartContainer.style.paddingRight = right != null ? `${right}px` : '';
72
+ };
73
+ this.getRotatedAxisTitleTransform = function (containerHeight, titleHeight) {
74
+ let placement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'left';
75
+ const translateX = containerHeight / 2 - titleHeight / 2;
76
+ const translateY = containerHeight / 2 - titleHeight / 2;
77
+ if (placement === 'right') {
78
+ return `translate(${translateX}px, ${translateY}px) rotate(-90deg)`;
79
+ }
80
+ return `translate(-${translateX}px, ${translateY}px) rotate(-90deg)`;
81
+ };
47
82
  this.initChart = function (container, id) {
48
83
  let initConfig = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
49
84
  const {
@@ -61,6 +96,8 @@ export default class ChartComponent extends Component {
61
96
  const height = customHeight ? customHeight : containerHeight - marginBottom;
62
97
  _this.chart = d3.create('svg').attr('id', id).attr('width', width).attr('height', height).attr('viewBox', [0, 0, width, height]);
63
98
  _this.container.appendChild(_this.chart.node());
99
+ _this._defs = null;
100
+ _this._clipMaskByRect = new Map();
64
101
  _this.chartBoundingClientRect = {
65
102
  ...JSON.parse(JSON.stringify(_this.chart.node().getBoundingClientRect())),
66
103
  ...initConfig,
@@ -68,6 +105,12 @@ export default class ChartComponent extends Component {
68
105
  height
69
106
  };
70
107
  };
108
+ this.getDefs = () => {
109
+ if (!this._defs || !this._defs.node() || !this._defs.node().isConnected) {
110
+ this._defs = this.chart.append('defs').attr('class', 'sea-chart-defs');
111
+ }
112
+ return this._defs;
113
+ };
71
114
  this.initDefs = () => {
72
115
  if (this.container) {
73
116
  // add Mask
@@ -222,23 +265,38 @@ export default class ChartComponent extends Component {
222
265
  y_axis_summary_type,
223
266
  type,
224
267
  title_name,
268
+ x_axis_label_font_size,
269
+ x_axis_label_font_color,
270
+ y_axis_label_font_size,
271
+ y_axis_label_font_color,
225
272
  // left axis - combination chart
226
273
  show_y_axis_left_label,
227
274
  y_axis_left_label_position,
228
275
  y_axis_left_summary_type,
229
276
  y_axis_left_summary_column,
277
+ y_axis_left_label_font_size,
278
+ y_axis_left_label_font_color,
230
279
  // right axis - combination chart
231
280
  show_y_axis_right_label,
232
281
  y_axis_right_label_position,
233
282
  y_axis_right_summary_type,
234
- y_axis_right_summary_column
283
+ y_axis_right_summary_column,
284
+ y_axis_right_label_font_size,
285
+ y_axis_right_label_font_color
235
286
  } = chart.config;
236
287
  if (y_axis_summary_column_key) {
237
288
  y_axis_column_key = y_axis_summary_column_key;
238
289
  }
239
290
  const table = getTableById(tables, table_id);
240
- let textColor;
241
- this.globalTheme === THEME_NAME_MAP.DARK ? textColor = '#fff' : textColor = '#999';
291
+ const defaultTextColor = this.globalTheme === THEME_NAME_MAP.DARK ? DEFAULT_AXIS_LABEL_FONT_COLOR_DARK : DEFAULT_AXIS_LABEL_FONT_COLOR;
292
+ const xColor = x_axis_label_font_color || defaultTextColor;
293
+ const xFontSize = x_axis_label_font_size || DEFAULT_AXIS_LABEL_FONT_SIZE;
294
+ const yColor = y_axis_label_font_color || defaultTextColor;
295
+ const yFontSize = y_axis_label_font_size || DEFAULT_AXIS_LABEL_FONT_SIZE;
296
+ const yLeftColor = y_axis_left_label_font_color || defaultTextColor;
297
+ const yLeftFontSize = y_axis_left_label_font_size || DEFAULT_AXIS_LABEL_FONT_SIZE;
298
+ const yRightColor = y_axis_right_label_font_color || defaultTextColor;
299
+ const yRightFontSize = y_axis_right_label_font_size || DEFAULT_AXIS_LABEL_FONT_SIZE;
242
300
 
243
301
  // xAxis
244
302
  const xAxisID = `chart-x-axis-label_${chart.id}`;
@@ -246,17 +304,18 @@ export default class ChartComponent extends Component {
246
304
  const {
247
305
  width: containerWidth
248
306
  } = this.chartBoundingClientRect;
307
+ const xTitleHeight = this.getAxisTitleHeight(xFontSize);
249
308
  if (!xLabel && x_axis_show_label) {
250
309
  const div = document.createElement('div');
251
310
  div.id = xAxisID;
252
311
  div.className = 'chart-axis-label';
253
312
  const column = getTableColumnByKey(table, x_axis_column_key);
254
313
  div.innerHTML = `${column ? column.name : ''}`;
255
- div.setAttribute('style', `color:${textColor}; width: ${containerWidth}px; text-align: ${x_axis_label_position}; position: absolute; bottom: 0;`);
314
+ div.setAttribute('style', `color:${xColor}; font-size:${xFontSize}px; width: ${containerWidth}px; height: ${xTitleHeight}px; line-height: ${xTitleHeight}px; text-align: ${x_axis_label_position}; position: absolute; bottom: 0;`);
256
315
  chartContainer.appendChild(div);
257
316
  }
258
317
  if (xLabel && x_axis_show_label) {
259
- xLabel.setAttribute('style', `color:${textColor}; width: ${containerWidth}px; text-align: ${x_axis_label_position}; position: absolute; bottom: 0;`);
318
+ xLabel.setAttribute('style', `color:${xColor}; font-size:${xFontSize}px; width: ${containerWidth}px; height: ${xTitleHeight}px; line-height: ${xTitleHeight}px; text-align: ${x_axis_label_position}; position: absolute; bottom: 0;`);
260
319
  }
261
320
  if (xLabel && !x_axis_show_label) {
262
321
  xLabel.parentNode.removeChild(xLabel);
@@ -265,7 +324,7 @@ export default class ChartComponent extends Component {
265
324
  // yAxis
266
325
  const yAxisID = `chart-y-axis-label_${chart.id}`;
267
326
  const yLabel = chartContainer.querySelector(`#${yAxisID}`);
268
- const divHeight = 20;
327
+ const yTitleHeight = this.getAxisTitleHeight(yFontSize);
269
328
  if (!yLabel && y_axis_show_label) {
270
329
  const div = document.createElement('div');
271
330
  div.id = yAxisID;
@@ -286,7 +345,7 @@ export default class ChartComponent extends Component {
286
345
  let textAlign = 'center';
287
346
  if (y_axis_label_position === LABEL_POSITION_TYPE.BOTTOM) textAlign = 'left';
288
347
  if (y_axis_label_position === LABEL_POSITION_TYPE.TOP) textAlign = 'right';
289
- div.setAttribute('style', `color:${textColor}; position: absolute; width: ${containerHeight}px; height: ${divHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: translate(-${containerHeight / 2 - divHeight / 2}px, ${containerHeight / 2 - divHeight / 2}px) rotate(-90deg)`);
348
+ div.setAttribute('style', `color:${yColor}; font-size:${yFontSize}px; position: absolute; width: ${containerHeight}px; height: ${yTitleHeight}px; line-height: ${yTitleHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: ${this.getRotatedAxisTitleTransform(containerHeight, yTitleHeight)}`);
290
349
  chartContainer.appendChild(div);
291
350
  }
292
351
  if (yLabel && y_axis_show_label) {
@@ -296,7 +355,7 @@ export default class ChartComponent extends Component {
296
355
  let textAlign = 'center';
297
356
  if (y_axis_label_position === LABEL_POSITION_TYPE.BOTTOM) textAlign = 'left';
298
357
  if (y_axis_label_position === LABEL_POSITION_TYPE.TOP) textAlign = 'right';
299
- yLabel.setAttribute('style', `color:${textColor}; position: absolute; width: ${containerHeight}px; height: ${divHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: translate(-${containerHeight / 2 - divHeight / 2}px, ${containerHeight / 2 - divHeight / 2}px) rotate(-90deg)`);
358
+ yLabel.setAttribute('style', `color:${yColor}; font-size:${yFontSize}px; position: absolute; width: ${containerHeight}px; height: ${yTitleHeight}px; line-height: ${yTitleHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: ${this.getRotatedAxisTitleTransform(containerHeight, yTitleHeight)}`);
300
359
  }
301
360
  if (yLabel && !y_axis_show_label) {
302
361
  yLabel.parentNode.removeChild(yLabel);
@@ -305,6 +364,7 @@ export default class ChartComponent extends Component {
305
364
  // yAxis(left) - combination chart
306
365
  const yAxisLeftID = `chart-y-axis-left-label_${chart.id}`;
307
366
  const yAxisLeftLabel = chartContainer.querySelector(`#${yAxisLeftID}`);
367
+ const yLeftTitleHeight = this.getAxisTitleHeight(yLeftFontSize);
308
368
  if (!yAxisLeftLabel && show_y_axis_left_label) {
309
369
  const div = document.createElement('div');
310
370
  div.id = yAxisLeftID;
@@ -319,7 +379,7 @@ export default class ChartComponent extends Component {
319
379
  let textAlign = 'center';
320
380
  if (y_axis_left_label_position === LABEL_POSITION_TYPE.BOTTOM) textAlign = 'left';
321
381
  if (y_axis_left_label_position === LABEL_POSITION_TYPE.TOP) textAlign = 'right';
322
- div.setAttribute('style', `color:${textColor}; position: absolute; width: ${containerHeight}px; height: ${divHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: translate(-${containerHeight / 2 - divHeight / 2}px, ${containerHeight / 2 - divHeight / 2}px) rotate(-90deg)`);
382
+ div.setAttribute('style', `color:${yLeftColor}; font-size:${yLeftFontSize}px; position: absolute; width: ${containerHeight}px; height: ${yLeftTitleHeight}px; line-height: ${yLeftTitleHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: ${this.getRotatedAxisTitleTransform(containerHeight, yLeftTitleHeight)}`);
323
383
  chartContainer.appendChild(div);
324
384
  }
325
385
  if (yAxisLeftLabel && show_y_axis_left_label) {
@@ -327,7 +387,7 @@ export default class ChartComponent extends Component {
327
387
  let textAlign = 'center';
328
388
  if (y_axis_left_label_position === 'bottom') textAlign = 'left';
329
389
  if (y_axis_left_label_position === 'top') textAlign = 'right';
330
- yAxisLeftLabel.setAttribute('style', `color:${textColor}; position: absolute; width: ${containerHeight}px; height: ${divHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: translate(-${containerHeight / 2 - divHeight / 2}px, ${containerHeight / 2 - divHeight / 2}px) rotate(-90deg)`);
390
+ yAxisLeftLabel.setAttribute('style', `color:${yLeftColor}; font-size:${yLeftFontSize}px; position: absolute; width: ${containerHeight}px; height: ${yLeftTitleHeight}px; line-height: ${yLeftTitleHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: ${this.getRotatedAxisTitleTransform(containerHeight, yLeftTitleHeight)}`);
331
391
  }
332
392
  if (yAxisLeftLabel && !show_y_axis_left_label) {
333
393
  yAxisLeftLabel.parentNode.removeChild(yAxisLeftLabel);
@@ -336,6 +396,7 @@ export default class ChartComponent extends Component {
336
396
  // yAxis(right) - combination chart
337
397
  const yAxisRightID = `chart-y-axis-right-label_${chart.id}`;
338
398
  const yAxisRightLabel = chartContainer.querySelector(`#${yAxisRightID}`);
399
+ const yRightTitleHeight = this.getAxisTitleHeight(yRightFontSize);
339
400
  if (!yAxisRightLabel && show_y_axis_right_label) {
340
401
  const div = document.createElement('div');
341
402
  div.id = yAxisRightID;
@@ -350,7 +411,7 @@ export default class ChartComponent extends Component {
350
411
  let textAlign = 'center';
351
412
  if (y_axis_right_label_position === LABEL_POSITION_TYPE.BOTTOM) textAlign = 'left';
352
413
  if (y_axis_right_label_position === LABEL_POSITION_TYPE.TOP) textAlign = 'right';
353
- div.setAttribute('style', `color:${textColor}; position: absolute; width: ${containerHeight}px; height: ${divHeight}px; text-align: ${textAlign}; top: 0; right: 0; transform: translate(${containerHeight / 2 - divHeight / 2}px, ${containerHeight / 2 - divHeight / 2}px) rotate(-90deg)`);
414
+ div.setAttribute('style', `color:${yRightColor}; font-size:${yRightFontSize}px; position: absolute; width: ${containerHeight}px; height: ${yRightTitleHeight}px; line-height: ${yRightTitleHeight}px; text-align: ${textAlign}; top: 0; right: 0; transform: ${this.getRotatedAxisTitleTransform(containerHeight, yRightTitleHeight, 'right')}`);
354
415
  chartContainer.appendChild(div);
355
416
  }
356
417
  if (yAxisRightLabel && show_y_axis_right_label) {
@@ -358,11 +419,16 @@ export default class ChartComponent extends Component {
358
419
  let textAlign = 'center';
359
420
  if (y_axis_right_label_position === 'bottom') textAlign = 'left';
360
421
  if (y_axis_right_label_position === 'top') textAlign = 'right';
361
- yAxisRightLabel.setAttribute('style', `color:${textColor}; position: absolute; width: ${containerHeight}px; height: ${divHeight}px; text-align: ${textAlign}; top: 0; right: 0; transform: translate(${containerHeight / 2 - divHeight / 2}px, ${containerHeight / 2 - divHeight / 2}px) rotate(-90deg)`);
422
+ yAxisRightLabel.setAttribute('style', `color:${yRightColor}; font-size:${yRightFontSize}px; position: absolute; width: ${containerHeight}px; height: ${yRightTitleHeight}px; line-height: ${yRightTitleHeight}px; text-align: ${textAlign}; top: 0; right: 0; transform: ${this.getRotatedAxisTitleTransform(containerHeight, yRightTitleHeight, 'right')}`);
362
423
  }
363
424
  if (yAxisRightLabel && !show_y_axis_right_label) {
364
425
  yAxisRightLabel.parentNode.removeChild(yAxisRightLabel);
365
426
  }
427
+ this.setAxisTitlePadding(chartContainer, {
428
+ left: y_axis_show_label ? yTitleHeight : show_y_axis_left_label ? yLeftTitleHeight : null,
429
+ right: show_y_axis_right_label ? yRightTitleHeight : null,
430
+ bottom: x_axis_show_label ? xTitleHeight : null
431
+ });
366
432
  };
367
433
  this.renderHorizontalLabel = (chart, tables, chartContainer) => {
368
434
  if (!this.chart || !chart) return;
@@ -374,15 +440,24 @@ export default class ChartComponent extends Component {
374
440
  horizontal_axis_label_position,
375
441
  vertical_axis_label_position,
376
442
  show_vertical_axis_label,
377
- show_horizontal_axis_label
443
+ show_horizontal_axis_label,
444
+ horizontal_axis_label_font_size,
445
+ horizontal_axis_label_font_color,
446
+ vertical_axis_label_font_size,
447
+ vertical_axis_label_font_color
378
448
  } = chart.config;
379
449
  const table = getTableById(tables, table_id);
380
- const textColor = '#999999';
450
+ const defaultTextColor = this.globalTheme === THEME_NAME_MAP.DARK ? DEFAULT_AXIS_LABEL_FONT_COLOR_DARK : DEFAULT_AXIS_LABEL_FONT_COLOR;
451
+ const xColor = horizontal_axis_label_font_color || defaultTextColor;
452
+ const xFontSize = horizontal_axis_label_font_size || DEFAULT_AXIS_LABEL_FONT_SIZE;
453
+ const yColor = vertical_axis_label_font_color || defaultTextColor;
454
+ const yFontSize = vertical_axis_label_font_size || DEFAULT_AXIS_LABEL_FONT_SIZE;
381
455
  const xAxisID = `chart-x-axis-label_${chart.id}`;
382
456
  const xLabel = chartContainer.querySelector(`#${xAxisID}`);
383
457
  const {
384
458
  width: containerWidth
385
459
  } = this.chartBoundingClientRect;
460
+ const xTitleHeight = this.getAxisTitleHeight(xFontSize);
386
461
  if (!xLabel && show_horizontal_axis_label) {
387
462
  const div = document.createElement('div');
388
463
  div.id = xAxisID;
@@ -393,18 +468,18 @@ export default class ChartComponent extends Component {
393
468
  const column = getTableColumnByKey(table, horizontal_axis_column_key);
394
469
  div.innerHTML = `${column ? column.name : ''}`;
395
470
  }
396
- div.setAttribute('style', `color:${textColor}; width: ${containerWidth}px; text-align: ${horizontal_axis_label_position}; position: absolute`);
471
+ div.setAttribute('style', `color:${xColor}; font-size:${xFontSize}px; width: ${containerWidth}px; height: ${xTitleHeight}px; line-height: ${xTitleHeight}px; text-align: ${horizontal_axis_label_position}; position: absolute`);
397
472
  chartContainer.appendChild(div);
398
473
  }
399
474
  if (xLabel && show_horizontal_axis_label) {
400
- xLabel.setAttribute('style', `color:${textColor}; width: ${containerWidth}px; text-align: ${horizontal_axis_label_position}; position: absolute`);
475
+ xLabel.setAttribute('style', `color:${xColor}; font-size:${xFontSize}px; width: ${containerWidth}px; height: ${xTitleHeight}px; line-height: ${xTitleHeight}px; text-align: ${horizontal_axis_label_position}; position: absolute`);
401
476
  }
402
477
  if (xLabel && !show_horizontal_axis_label) {
403
478
  xLabel.parentNode.removeChild(xLabel);
404
479
  }
405
480
  const yAxisID = `chart-y-axis-label_${chart.id}`;
406
481
  const yLabel = chartContainer.querySelector(`#${yAxisID}`);
407
- const divHeight = 20;
482
+ const yTitleHeight = this.getAxisTitleHeight(yFontSize);
408
483
  if (!yLabel && show_vertical_axis_label) {
409
484
  const div = document.createElement('div');
410
485
  div.id = yAxisID;
@@ -417,30 +492,33 @@ export default class ChartComponent extends Component {
417
492
  let textAlign = 'center';
418
493
  if (vertical_axis_label_position === LABEL_POSITION_TYPE.BOTTOM) textAlign = 'left';
419
494
  if (vertical_axis_label_position === LABEL_POSITION_TYPE.TOP) textAlign = 'right';
420
- div.setAttribute('style', `color:${textColor}; position: absolute; width: ${containerHeight}px; height: ${divHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: translate(-${containerHeight / 2 - divHeight / 2}px, ${containerHeight / 2 - divHeight / 2}px) rotate(-90deg)`);
495
+ div.setAttribute('style', `color:${yColor}; font-size:${yFontSize}px; position: absolute; width: ${containerHeight}px; height: ${yTitleHeight}px; line-height: ${yTitleHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: ${this.getRotatedAxisTitleTransform(containerHeight, yTitleHeight)}`);
421
496
  chartContainer.appendChild(div);
422
497
  }
423
- if (yLabel && show_horizontal_axis_label) {
498
+ if (yLabel && show_vertical_axis_label) {
424
499
  const {
425
500
  height: containerHeight
426
501
  } = this.chartBoundingClientRect;
427
502
  let textAlign = 'center';
428
503
  if (vertical_axis_label_position === LABEL_POSITION_TYPE.BOTTOM) textAlign = 'left';
429
504
  if (vertical_axis_label_position === LABEL_POSITION_TYPE.TOP) textAlign = 'right';
430
- yLabel.setAttribute('style', `color:${textColor}; position: absolute; width: ${containerHeight}px; height: ${divHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: translate(-${containerHeight / 2 - divHeight / 2}px, ${containerHeight / 2 - divHeight / 2}px) rotate(-90deg)`);
505
+ yLabel.setAttribute('style', `color:${yColor}; font-size:${yFontSize}px; position: absolute; width: ${containerHeight}px; height: ${yTitleHeight}px; line-height: ${yTitleHeight}px; text-align: ${textAlign}; top: 0; left: 0; transform: ${this.getRotatedAxisTitleTransform(containerHeight, yTitleHeight)}`);
431
506
  }
432
- if (yLabel && !show_horizontal_axis_label) {
507
+ if (yLabel && !show_vertical_axis_label) {
433
508
  yLabel.parentNode.removeChild(yLabel);
434
509
  }
435
- };
436
- this.isShowVerticalAxisLabel = chart => {
437
- return !!(chart && chart.config && chart.config.show_vertical_axis_label);
438
- };
439
- this.isShowHorizontalAxisLabel = chart => {
440
- return !!(chart && chart.config && chart.config.show_horizontal_axis_label);
510
+ this.setAxisTitlePadding(chartContainer, {
511
+ left: show_vertical_axis_label ? yTitleHeight : null,
512
+ bottom: show_horizontal_axis_label ? xTitleHeight : null,
513
+ right: null
514
+ });
441
515
  };
442
516
  this.formatterLegendName = name => {
443
- if (!name && typeof name !== 'number' || name.trim() === 'undefined' || name.trim() === 'null') {
517
+ if (typeof name !== 'string') {
518
+ if (!name && typeof name !== 'number') return intl.get(EMPTY_NAME);
519
+ return intl.get(String(name)) || String(name);
520
+ }
521
+ if (name.trim() === 'undefined' || name.trim() === 'null' || name.trim() === '') {
444
522
  return intl.get(EMPTY_NAME);
445
523
  } else if (name === '_Others') {
446
524
  return intl.get('Others');
@@ -476,8 +554,9 @@ export default class ChartComponent extends Component {
476
554
  };
477
555
 
478
556
  // sort legend data and format data
479
- data.forEach(item => item[legendName] = String(item[legendName]));
480
- const legendData = BaseUtils.sortDataByGroupName(cloneDeep(data), legendName, groupColumn, chart);
557
+ const clonedData = cloneDeep(data);
558
+ clonedData.forEach(item => item[legendName] = String(item[legendName]));
559
+ const legendData = BaseUtils.sortDataByGroupName(clonedData, legendName, groupColumn, chart);
481
560
  const {
482
561
  width: chartWidth,
483
562
  height: chartHeight,
@@ -965,18 +1044,6 @@ export default class ChartComponent extends Component {
965
1044
  }
966
1045
  return statisticNewData;
967
1046
  };
968
- this.isShowXAxisLabel = chart => {
969
- return !!(chart && chart.config && chart.config.x_axis_show_label);
970
- };
971
- this.isShowYAxisLabel = chart => {
972
- return !!(chart && chart.config && chart.config.y_axis_show_label);
973
- };
974
- this.isShowYAxisLeftLabel = chart => {
975
- return !!(chart && chart.config && chart.config.show_y_axis_left_label);
976
- };
977
- this.isShowYAxisRightLabel = chart => {
978
- return !!(chart && chart.config && chart.config.show_y_axis_right_label);
979
- };
980
1047
  this.getThemeColors = function () {
981
1048
  let themeColors = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
982
1049
  let theme = arguments.length > 1 ? arguments[1] : undefined;
@@ -1065,7 +1132,7 @@ export default class ChartComponent extends Component {
1065
1132
  allGroup.forEach(g => {
1066
1133
  const rects = Array.from(g.children).filter(item => item.tagName !== 'text');
1067
1134
  rects.forEach(item => {
1068
- d3.select(item).transition().duration(this.transitionDuration).attr('opacity', 1);
1135
+ d3.select(item).transition().duration(this.interactionDuration).attr('opacity', 1);
1069
1136
  if ([CHART_TYPE.PIE, CHART_TYPE.RING].includes(chartType)) {
1070
1137
  d3.select(item).attr('stroke-width', 2);
1071
1138
  }
@@ -1077,7 +1144,7 @@ export default class ChartComponent extends Component {
1077
1144
  const rects = Array.from(g.children).filter(item => item.tagName !== 'text');
1078
1145
  rects.forEach(item => {
1079
1146
  if (item.getAttribute('data-groupName') !== curGroupName) {
1080
- d3.selectAll([item]).transition().duration(this.transitionDuration).attr('opacity', 0.3);
1147
+ d3.selectAll([item]).transition().duration(this.interactionDuration).attr('opacity', 0.3);
1081
1148
  } else {
1082
1149
  if ([CHART_TYPE.PIE, CHART_TYPE.RING].includes(chartType)) {
1083
1150
  d3.select(item).attr('stroke-width', 0.5);
@@ -1087,37 +1154,222 @@ export default class ChartComponent extends Component {
1087
1154
  });
1088
1155
  }
1089
1156
  };
1090
- // Use clipPath to make rectangle rounded corners
1091
1157
  this.addClipPath = _ref9 => {
1092
1158
  let {
1093
1159
  rect,
1094
- parentNode,
1095
1160
  attr,
1096
- rectId,
1097
1161
  borderRadiusVal
1098
1162
  } = _ref9;
1163
+ return this.addRoundedClip(rect, attr === 'x' ? 'x' : 'y', null, borderRadiusVal);
1164
+ };
1165
+ this.addRoundedClip = function (rect, orient) {
1166
+ let width = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
1167
+ let radiusOverride = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
1099
1168
  const {
1100
1169
  borderRadius
1101
- } = this.chartBoundingClientRect;
1102
- const clipRect = d3.select(rect.cloneNode());
1103
- if (attr === 'x') {
1104
- const rectHeight = Number(rect.getAttribute('height'));
1105
- const rectWidth = Number(rect.getAttribute('width'));
1106
- const computedBorderRadius = Math.min(rectHeight * borderRadius, rectWidth / 2);
1107
- const safeBorderRadius = Math.min(borderRadiusVal !== null && borderRadiusVal !== void 0 ? borderRadiusVal : computedBorderRadius, rectWidth / 2, rectHeight / 2);
1108
- clipRect.attr('rx', safeBorderRadius);
1109
- clipRect.attr('x', Number(rect.getAttribute('x')) - safeBorderRadius).attr('width', rectWidth + safeBorderRadius);
1170
+ } = _this.chartBoundingClientRect;
1171
+ const x = Number(rect.getAttribute('x'));
1172
+ const y = Number(rect.getAttribute('y'));
1173
+ const w = width != null ? width : Number(rect.getAttribute('width'));
1174
+ const h = Number(rect.getAttribute('height'));
1175
+ const computedRadius = orient === 'x' ? Math.min(h * borderRadius, h / 2, w / 2) : Math.min(w * borderRadius, w / 2, h / 2);
1176
+ const radius = radiusOverride != null ? Math.min(radiusOverride, w / 2, h / 2) : computedRadius;
1177
+ const d = orient === 'x' ? _this.getRightRoundedRectPath(x, y, w, h, radius) : _this.getTopRoundedRectPath(x, y, w, h, radius);
1178
+ const clipId = `rounded-clip-${Math.random().toString(36).slice(2)}`;
1179
+ const path = _this.getDefs().append('clipPath').attr('id', clipId).append('path').attr('class', 'rounded-clip-path').attr('data-x', x).attr('data-y', y).attr('data-width', w).attr('data-height', h).attr('data-radius', radius).attr('data-orient', orient).attr('d', d);
1180
+ d3.select(rect).attr('clip-path', `url(#${clipId})`);
1181
+ _this._clipMaskByRect.set(rect, path.node());
1182
+ return path.node();
1183
+ };
1184
+ this.animateMaskForRect = (rect, orient, baseline, t) => {
1185
+ const path = this._clipMaskByRect && this._clipMaskByRect.get(rect);
1186
+ if (!path) return;
1187
+ const x = Number(path.getAttribute('data-x'));
1188
+ const y = Number(path.getAttribute('data-y'));
1189
+ const width = Number(path.getAttribute('data-width'));
1190
+ const height = Number(path.getAttribute('data-height'));
1191
+ const radius = Number(path.getAttribute('data-radius'));
1192
+ const el = d3.select(path);
1193
+ if (orient === 'x') {
1194
+ const interpX = d3.interpolateNumber(baseline, x);
1195
+ const interpW = d3.interpolateNumber(0, width);
1196
+ el.attr('d', this.getRightRoundedRectPath(baseline, y, 0, height, radius)).transition(t).attrTween('d', () => tt => this.getRightRoundedRectPath(interpX(tt), y, interpW(tt), height, radius));
1110
1197
  } else {
1111
- const rectWidth = Number(rect.getAttribute('width'));
1112
- const rectHeight = Number(rect.getAttribute('height'));
1113
- const computedBorderRadius = Math.min(rectWidth * borderRadius, rectHeight / 2);
1114
- const safeBorderRadius = Math.min(borderRadiusVal !== null && borderRadiusVal !== void 0 ? borderRadiusVal : computedBorderRadius, rectWidth / 2, rectHeight / 2);
1115
- clipRect.attr('rx', safeBorderRadius);
1116
- clipRect.attr('height', rectHeight + safeBorderRadius);
1198
+ const interpY = d3.interpolateNumber(baseline, y);
1199
+ const interpH = d3.interpolateNumber(0, height);
1200
+ el.attr('d', this.getTopRoundedRectPath(x, baseline, width, 0, radius)).transition(t).attrTween('d', () => tt => this.getTopRoundedRectPath(x, interpY(tt), width, interpH(tt), radius));
1117
1201
  }
1118
- const clipPath = d3.select(parentNode).append('clipPath').attr('opacity', 1).attr('id', rectId);
1119
- clipPath.node().appendChild(clipRect.node());
1120
- d3.select(rect).attr('clip-path', `url(#${rectId})`);
1202
+ };
1203
+ this.getTopRoundedRectPath = (x, y, width, height, radius) => {
1204
+ const r = Math.min(radius, width / 2, height / 2);
1205
+ if (height <= 0) return `M${x},${y} L${x + width},${y} L${x + width},${y} L${x},${y} Z`;
1206
+ return `M${x},${y + r} Q${x},${y} ${x + r},${y} L${x + width - r},${y} Q${x + width},${y} ${x + width},${y + r} L${x + width},${y + height} L${x},${y + height} Z`;
1207
+ };
1208
+ this.getRightRoundedRectPath = (x, y, width, height, radius) => {
1209
+ const r = Math.min(radius, width / 2, height / 2);
1210
+ if (width <= 0) return `M${x},${y} L${x},${y + height} L${x},${y + height} L${x},${y} Z`;
1211
+ return `M${x},${y} L${x + width - r},${y} Q${x + width},${y} ${x + width},${y + r} L${x + width},${y + height - r} Q${x + width},${y + height} ${x + width - r},${y + height} L${x},${y + height} Z`;
1212
+ };
1213
+ this.animateRectGrowInY = function (selection) {
1214
+ let duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.transitionDuration;
1215
+ if (!selection || selection.empty()) return;
1216
+ const self = _this;
1217
+ selection.each(function () {
1218
+ const finalY = Number(this.getAttribute('y'));
1219
+ const finalHeight = Number(this.getAttribute('height'));
1220
+ if (!(finalHeight > 0)) return;
1221
+ const baseline = finalY + finalHeight;
1222
+ const t = d3.transition(self.enterTransitionName).duration(duration).ease(d3.easeCubicOut);
1223
+ d3.select(this).attr('y', baseline).attr('height', 0).transition(t).attr('y', finalY).attr('height', finalHeight);
1224
+ self.animateMaskForRect(this, 'y', baseline, t);
1225
+ });
1226
+ };
1227
+ this.animateStackGrowInY = function (selection) {
1228
+ let groupKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node => node.getAttribute('x');
1229
+ let duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _this.transitionDuration;
1230
+ if (!selection || selection.empty()) return null;
1231
+ const columns = new Map();
1232
+ selection.each(function () {
1233
+ if (!(Number(this.getAttribute('height')) > 0)) return;
1234
+ const key = groupKey(this);
1235
+ if (!columns.has(key)) columns.set(key, []);
1236
+ columns.get(key).push(this);
1237
+ });
1238
+ if (columns.size === 0) return null;
1239
+ const t = d3.transition(_this.enterTransitionName).duration(duration).ease(d3.easeCubicOut);
1240
+ columns.forEach(rects => {
1241
+ let baselineY = -Infinity;
1242
+ rects.forEach(rect => {
1243
+ baselineY = Math.max(baselineY, Number(rect.getAttribute('y')) + Number(rect.getAttribute('height')));
1244
+ });
1245
+ rects.forEach(rect => {
1246
+ const finalY = Number(rect.getAttribute('y'));
1247
+ const finalHeight = Number(rect.getAttribute('height'));
1248
+ d3.select(rect).attr('y', baselineY).attr('height', 0).transition(t).attr('y', finalY).attr('height', finalHeight);
1249
+ _this.animateMaskForRect(rect, 'y', baselineY, t);
1250
+ });
1251
+ });
1252
+ return t;
1253
+ };
1254
+ this.animateRectGrowInX = function (selection) {
1255
+ let duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.transitionDuration;
1256
+ if (!selection || selection.empty()) return;
1257
+ const self = _this;
1258
+ selection.each(function () {
1259
+ const finalX = Number(this.getAttribute('x'));
1260
+ const finalWidth = Number(this.getAttribute('width'));
1261
+ if (!(finalWidth > 0)) return;
1262
+ const t = d3.transition(self.enterTransitionName).duration(duration).ease(d3.easeCubicOut);
1263
+ d3.select(this).attr('width', 0).transition(t).attr('width', finalWidth);
1264
+ self.animateMaskForRect(this, 'x', finalX, t);
1265
+ });
1266
+ };
1267
+ this.animateStackGrowInX = function (selection) {
1268
+ let groupKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node => node.getAttribute('y');
1269
+ let duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _this.transitionDuration;
1270
+ if (!selection || selection.empty()) return null;
1271
+ const rows = new Map();
1272
+ selection.each(function () {
1273
+ if (!(Number(this.getAttribute('width')) > 0)) return;
1274
+ const key = groupKey(this);
1275
+ if (!rows.has(key)) rows.set(key, []);
1276
+ rows.get(key).push(this);
1277
+ });
1278
+ if (rows.size === 0) return null;
1279
+ const t = d3.transition(_this.enterTransitionName).duration(duration).ease(d3.easeCubicOut);
1280
+ rows.forEach(rects => {
1281
+ let baselineX = Infinity;
1282
+ rects.forEach(rect => {
1283
+ baselineX = Math.min(baselineX, Number(rect.getAttribute('x')));
1284
+ });
1285
+ rects.forEach(rect => {
1286
+ const finalX = Number(rect.getAttribute('x'));
1287
+ const finalWidth = Number(rect.getAttribute('width'));
1288
+ d3.select(rect).attr('x', baselineX).attr('width', 0).transition(t).attr('x', finalX).attr('width', finalWidth);
1289
+ _this.animateMaskForRect(rect, 'x', baselineX, t);
1290
+ });
1291
+ });
1292
+ return t;
1293
+ };
1294
+ this.animateArcGrowIn = function (selection, arc) {
1295
+ let duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _this.transitionDuration;
1296
+ if (!selection || selection.empty()) return;
1297
+ const transitionName = _this.enterTransitionName;
1298
+ selection.each(function (d) {
1299
+ const path = d3.select(this);
1300
+ const interpolate = d3.interpolate({
1301
+ startAngle: d.startAngle,
1302
+ endAngle: d.startAngle,
1303
+ padAngle: d.padAngle
1304
+ }, {
1305
+ startAngle: d.startAngle,
1306
+ endAngle: d.endAngle,
1307
+ padAngle: d.padAngle
1308
+ });
1309
+ path.attr('d', arc(interpolate(0))).transition(transitionName).duration(duration).ease(d3.easeCubicOut).attrTween('d', () => t => arc(interpolate(t)));
1310
+ });
1311
+ };
1312
+ this.animatePathDrawIn = function (selection) {
1313
+ let duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.transitionDuration;
1314
+ if (!selection || selection.empty()) return;
1315
+ const transitionName = _this.enterTransitionName;
1316
+ selection.each(function () {
1317
+ const path = d3.select(this);
1318
+ let totalLength = 0;
1319
+ try {
1320
+ totalLength = this.getTotalLength();
1321
+ } catch (e) {
1322
+ totalLength = 0;
1323
+ }
1324
+ if (!(totalLength > 0)) return;
1325
+ path.attr('stroke-dasharray', `${totalLength} ${totalLength}`).attr('stroke-dashoffset', totalLength).transition(transitionName).duration(duration).ease(d3.easeLinear).attr('stroke-dashoffset', 0).on('end', function () {
1326
+ d3.select(this).attr('stroke-dasharray', null).attr('stroke-dashoffset', null);
1327
+ });
1328
+ });
1329
+ };
1330
+ this.animateRevealInX = function (selection, width) {
1331
+ let duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _this.transitionDuration;
1332
+ if (!selection || selection.empty()) return;
1333
+ const {
1334
+ height: chartHeight
1335
+ } = _this.chartBoundingClientRect;
1336
+ const clipId = `reveal-clip-${Math.random().toString(36).slice(2)}`;
1337
+ const clipPath = _this.getDefs().append('clipPath').attr('id', clipId);
1338
+ const clipRect = clipPath.append('rect').attr('x', 0).attr('y', 0).attr('width', 0).attr('height', chartHeight);
1339
+ selection.attr('clip-path', `url(#${clipId})`);
1340
+ clipRect.transition(_this.enterTransitionName).duration(duration).ease(d3.easeLinear).attr('width', width).on('end', () => {
1341
+ selection.attr('clip-path', null);
1342
+ clipPath.remove();
1343
+ });
1344
+ };
1345
+ this.animateFadeIn = function (selection) {
1346
+ let duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.transitionDuration;
1347
+ let delay = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
1348
+ let finalOpacity = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
1349
+ if (!selection || selection.empty()) return;
1350
+ const transitionName = _this.enterTransitionName;
1351
+ selection.each(function () {
1352
+ const el = d3.select(this);
1353
+ let target = finalOpacity;
1354
+ if (target == null) {
1355
+ const orig = this.getAttribute('opacity');
1356
+ const parsed = orig === null || orig === '' ? 1 : Number(orig);
1357
+ target = Number.isNaN(parsed) ? 1 : parsed;
1358
+ }
1359
+ if (!(target > 0)) return;
1360
+ el.attr('opacity', 0).transition(transitionName).duration(duration).delay(delay).ease(d3.easeLinear).attr('opacity', target);
1361
+ });
1362
+ };
1363
+ this.animateCircleZoomIn = function (selection) {
1364
+ let duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.transitionDuration;
1365
+ if (!selection || selection.empty()) return;
1366
+ const transitionName = _this.enterTransitionName;
1367
+ selection.each(function () {
1368
+ const circle = d3.select(this);
1369
+ const finalR = Number(this.getAttribute('r')) || 0;
1370
+ if (!(finalR > 0)) return;
1371
+ circle.attr('r', 0).transition(transitionName).duration(duration).ease(d3.easeCubicOut).attr('r', finalR);
1372
+ });
1121
1373
  };
1122
1374
  this.drawYaxis = function (g, theme) {
1123
1375
  let rightAxisOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
@@ -1131,33 +1383,92 @@ export default class ChartComponent extends Component {
1131
1383
  // add text
1132
1384
  g.selectAll('text').attr('font-size', theme.fontSize);
1133
1385
  g.selectAll('text').attr('fill', theme.textColor);
1386
+ _this.thinYAxisTicks(g);
1134
1387
  _this.checkTextOverflow(g.selectAll('text').nodes());
1135
1388
 
1136
1389
  // line
1137
1390
  g.selectAll('line').node() && g.selectAll('line').node().remove(); // delete the first line
1138
- g.selectAll('.tick line').clone().attr('x2', chartWidth - insertPadding * 2 - _this.horizontalOverflowOffset - rightAxisOffset).attr('stroke', theme.gridColor).attr('stroke-dasharray', '8,3');
1391
+ g.selectAll('.tick line').clone().attr('x2', chartWidth - _this.getYAxisTranslateX() - insertPadding - rightAxisOffset).attr('stroke', theme.gridColor).attr('stroke-dasharray', '8,3');
1139
1392
 
1140
1393
  // update g translateX
1141
- g.attr('transform', `translate(${insertPadding + _this.horizontalOverflowOffset}, 0)`);
1394
+ g.attr('transform', `translate(${_this.getYAxisTranslateX()}, 0)`);
1395
+ };
1396
+ this.thinYAxisTicks = g => {
1397
+ const ticks = g.selectAll('.tick').nodes();
1398
+ if (ticks.length <= 2) return;
1399
+ const visibleTicks = ticks.map((tick, index) => {
1400
+ const text = d3.select(tick).select('text').node();
1401
+ if (!text) return null;
1402
+ const {
1403
+ top,
1404
+ bottom
1405
+ } = text.getBoundingClientRect();
1406
+ return {
1407
+ tick,
1408
+ index,
1409
+ top,
1410
+ bottom
1411
+ };
1412
+ }).filter(Boolean).sort((a, b) => a.top - b.top);
1413
+ const keptTicks = [];
1414
+ let prevBottom = -Infinity;
1415
+ visibleTicks.forEach(_ref10 => {
1416
+ let {
1417
+ tick,
1418
+ index,
1419
+ top,
1420
+ bottom
1421
+ } = _ref10;
1422
+ const shouldKeepFirstTick = index === 0;
1423
+ const shouldKeepLastTick = index === ticks.length - 1;
1424
+ const hasEnoughSpace = top > prevBottom + this.yAxisTickLabelGap;
1425
+ if (shouldKeepLastTick) {
1426
+ keptTicks.forEach(keptTick => {
1427
+ if (keptTick.bottom + this.yAxisTickLabelGap >= top) {
1428
+ d3.select(keptTick.tick).attr('opacity', 0);
1429
+ }
1430
+ });
1431
+ d3.select(tick).attr('opacity', 1);
1432
+ return;
1433
+ }
1434
+ if (shouldKeepFirstTick || hasEnoughSpace) {
1435
+ d3.select(tick).attr('opacity', 1);
1436
+ keptTicks.push({
1437
+ tick,
1438
+ bottom
1439
+ });
1440
+ prevBottom = bottom;
1441
+ return;
1442
+ }
1443
+ d3.select(tick).attr('opacity', 0);
1444
+ });
1142
1445
  };
1143
1446
  this.checkTextOverflow = allTextEl => {
1144
1447
  const {
1145
1448
  insertPadding
1146
1449
  } = this.chartBoundingClientRect;
1147
1450
  const allTextWidth = allTextEl.map(item => {
1451
+ const tick = item.parentNode;
1452
+ if (tick && Number(tick.getAttribute('opacity')) === 0) return 0;
1148
1453
  const {
1149
1454
  width
1150
1455
  } = item.getBoundingClientRect();
1151
1456
  return width;
1152
1457
  });
1153
- const maxTextWidth = Math.max(...allTextWidth);
1458
+ const maxTextWidth = Math.max(...allTextWidth, 0);
1154
1459
  this.horizontalOverflowOffset = 0;
1155
- if (maxTextWidth + 3 > insertPadding) {
1156
- // horizontal overflow
1157
- const offset = maxTextWidth + 3 - insertPadding; // 3 is text and line default spacing
1460
+ const requiredLeftSpace = maxTextWidth + 3;
1461
+ if (requiredLeftSpace > insertPadding) {
1462
+ const offset = requiredLeftSpace - insertPadding;
1158
1463
  this.horizontalOverflowOffset = offset;
1159
1464
  }
1160
1465
  };
1466
+ this.getYAxisTranslateX = () => {
1467
+ const {
1468
+ insertPadding
1469
+ } = this.chartBoundingClientRect;
1470
+ return insertPadding + this.horizontalOverflowOffset;
1471
+ };
1161
1472
  this.checkTickOverlap = (g, axis) => {
1162
1473
  const allTicks = g.selectAll('.tick').nodes();
1163
1474
  if (axis === 'yAxis') {
@@ -1309,10 +1620,84 @@ export default class ChartComponent extends Component {
1309
1620
  const annotationWrapper = contentWrapper.insert('g', ':first-child').attr('class', 'vertical-annotation-wrapper');
1310
1621
  annotationWrapper.append('line').attr('x1', x).attr('y1', insertPadding + marginTop).attr('x2', x).attr('y2', chartHeight - insertPadding).attr('stroke', theme.XAxisColor);
1311
1622
  };
1623
+ this.setLegendForHeatMap = _ref11 => {
1624
+ let {
1625
+ exampleColors,
1626
+ themeColors
1627
+ } = _ref11;
1628
+ const legendOffsetY = 3;
1629
+ const legendItemTextWidth = 32;
1630
+ const legendItemWidth = 12;
1631
+ const legendItemHeight = 12;
1632
+ const legendItemGap = 3;
1633
+ const legendItemRadius = 2;
1634
+ const legendWrapper = this.chart.append('g').attr('class', 'legend-heat-map-wrapper').attr('transform', `translate(0, ${legendOffsetY})`);
1635
+ legendWrapper.append('text').attr('y', 10).attr('fill', themeColors.textColor).attr('font-size', 12).attr('font-weight', 'bold').text(intl.get('Less'));
1636
+ exampleColors.forEach((color, index) => {
1637
+ legendWrapper.append('rect').attr('x', legendItemTextWidth + index * legendItemWidth + index * legendItemGap).attr('width', legendItemWidth).attr('height', legendItemHeight).attr('rx', legendItemRadius).attr('fill', color).attr('stroke', FILL_BORDER_COLOR_MAP[color]).attr('stroke-width', 1);
1638
+ });
1639
+ legendWrapper.append('text').attr('x', 182).attr('y', 10).attr('fill', themeColors.textColor).attr('font-size', 12).attr('font-weight', 'bold').text(intl.get('More'));
1640
+ };
1641
+ this.setContinuousLegend = _ref12 => {
1642
+ var _this$chart$node2;
1643
+ let {
1644
+ previewType,
1645
+ theme,
1646
+ colorRange = [],
1647
+ legendDirection,
1648
+ legendSize,
1649
+ legendTextRange,
1650
+ bubbleColor,
1651
+ type
1652
+ } = _ref12;
1653
+ const {
1654
+ width: chartWidth,
1655
+ height: chartHeight,
1656
+ insertPadding
1657
+ } = this.chartBoundingClientRect;
1658
+ const legendRectWidth = legendDirection === 'vertical' ? 12 : Math.min(legendSize * 100, chartWidth - insertPadding * 2);
1659
+ const legendRectHeight = legendDirection === 'vertical' ? Math.min(legendSize * 100, chartHeight - insertPadding * 2) : 12;
1660
+ const legendTextOffset = 2;
1661
+ const legendTextHeightSpace = legendDirection === 'vertical' ? 0 : 14 + legendTextOffset;
1662
+ if (![CHART_TYPE.MAP_BUBBLE, CHART_TYPE.WORLD_MAP_BUBBLE].includes(type)) {
1663
+ var _this$chart$node;
1664
+ const gradient = this.chart.append('defs').attr('class', 'linear-gradient-wrapper').append('linearGradient').attr("id", `gradient-${(_this$chart$node = this.chart.node()) === null || _this$chart$node === void 0 ? void 0 : _this$chart$node.id}-${previewType}`).attr("x1", '0%').attr("y1", '0%').attr("x2", legendDirection === 'vertical' ? '0%' : '100%').attr("y2", legendDirection === 'vertical' ? '100%' : '0%');
1665
+ gradient.append('stop').attr('offset', '0%').attr('stop-color', `${colorRange[0]}`);
1666
+ gradient.append('stop').attr('offset', '50%').attr('stop-color', `${colorRange[4]}`);
1667
+ gradient.append('stop').attr('offset', '100%').attr('stop-color', `${colorRange[8]}`);
1668
+ }
1669
+ const continuousLegendWrapper = this.chart.append('g').attr('class', 'legend-continuous-wrapper').attr('transform', `translate(0, ${chartHeight - legendRectHeight - legendTextHeightSpace})`);
1670
+ continuousLegendWrapper.append('rect').attr('width', legendRectWidth).attr('height', legendRectHeight).attr('fill', [CHART_TYPE.MAP_BUBBLE, CHART_TYPE.WORLD_MAP_BUBBLE].includes(type) ? bubbleColor : `url(#gradient-${(_this$chart$node2 = this.chart.node()) === null || _this$chart$node2 === void 0 ? void 0 : _this$chart$node2.id}-${previewType})`).call(g => {
1671
+ if ([CHART_TYPE.MAP_BUBBLE, CHART_TYPE.WORLD_MAP_BUBBLE].includes(type)) {
1672
+ continuousLegendWrapper.append('polygon').attr('points', `0,0 ${legendRectWidth - 0.5},0 0,${legendRectHeight - 0.5}`).attr('fill', '#fff').attr('stroke', '#fff');
1673
+ }
1674
+ });
1675
+ continuousLegendWrapper.append('text').attr('class', 'range-start').attr('stroke', '#fff').attr('stroke-width', 1).attr('paint-order', 'stroke').attr('fill', theme.labelColor).text(legendTextRange[0]).call(g => {
1676
+ const {
1677
+ height
1678
+ } = g.node().getBoundingClientRect();
1679
+ g.attr('x', legendDirection === 'vertical' ? legendRectWidth + legendTextOffset : legendTextOffset);
1680
+ g.attr('y', legendDirection === 'vertical' ? height : height + legendRectHeight);
1681
+ });
1682
+ continuousLegendWrapper.append('text').attr('class', 'range-end').attr('stroke', '#fff').attr('stroke-width', 1).attr('paint-order', 'stroke').attr('fill', theme.labelColor).text(legendTextRange[1]).call(g => {
1683
+ const {
1684
+ width,
1685
+ height
1686
+ } = g.node().getBoundingClientRect();
1687
+ g.attr('x', legendDirection === 'vertical' ? legendRectWidth + legendTextOffset : legendRectWidth - width);
1688
+ g.attr('y', legendDirection === 'vertical' ? legendRectHeight - legendTextOffset : height + legendRectHeight);
1689
+ });
1690
+ };
1312
1691
  this.initLabelStroke(props === null || props === void 0 ? void 0 : props.globalTheme);
1313
1692
  this.chartBoundingClientRect = {};
1314
- this.transitionDuration = 500;
1693
+ this.transitionDuration = 450;
1694
+ this.interactionDuration = 150;
1315
1695
  this.areaTransitionDuration = 200;
1696
+ this.yAxisTickLabelGap = 4;
1697
+ this.axisTitleMinHeight = 20;
1698
+ this.enterTransitionName = 'enter';
1699
+ this._defs = null;
1700
+ this._clipMaskByRect = new Map();
1316
1701
  }
1317
1702
  componentDidMount() {
1318
1703
  window.addEventListener('resize', this.handleResize);