sea-chart 0.0.36 → 0.0.38

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.
@@ -173,6 +173,10 @@ export const CHART_THEME_COLOR = {
173
173
  gridColor: '#F3F3F3',
174
174
  XAxisColor: '#CCCCCC',
175
175
  labelColor: '#999999',
176
+ ringLabelColor: '#212529',
177
+ annotationTitleFontColor: '#666666',
178
+ annotationValueFontColor: '#212529',
179
+ annotationFontSize: 18,
176
180
  cardColor: '#545454',
177
181
  legendTextColor: '#333333',
178
182
  legendFontSize: 14,
@@ -1,5 +1,5 @@
1
1
  import { isBoolean } from '../utils';
2
- import { CHART_TYPE, CHART_SUMMARY_TYPE } from '../constants';
2
+ import { CHART_TYPE, CHART_SUMMARY_TYPE, CHART_LINE_TYPES } from '../constants';
3
3
  import BaseModel from './base-model';
4
4
  class Combination extends BaseModel {
5
5
  constructor(options) {
@@ -33,6 +33,7 @@ class Combination extends BaseModel {
33
33
  this.y_axis_max_right = options.y_axis_max_right;
34
34
  this.display_data = isBoolean(options.y_axis_show_value) ? options.y_axis_show_value : false;
35
35
  this.label_font_size = options.label_font_size;
36
+ this.line_type = options.line_type || CHART_LINE_TYPES[1];
36
37
  }
37
38
  }
38
39
  export default Combination;
@@ -1,6 +1,6 @@
1
- import BaseModel from './base-model';
2
1
  import { isBoolean } from '../utils';
3
2
  import { CHART_TYPE, CHART_LABEL_POSITIONS, CHART_LABEL_FORMATS, CHART_SUMMARY_TYPE } from '../constants';
3
+ import BaseModel from './base-model';
4
4
  class Ring extends BaseModel {
5
5
  constructor(options) {
6
6
  super({
@@ -21,6 +21,7 @@ class Ring extends BaseModel {
21
21
  this.minimum_slice_percent = options.minimum_slice_percent;
22
22
  this.sort_type = options.sort_type;
23
23
  this.label_font_size = options.label_font_size;
24
+ this.show_annotation = options.show_annotation || true;
24
25
  }
25
26
  }
26
27
  export default Ring;
@@ -8,6 +8,7 @@ import DisplayValuesSettings from '../widgets/display-values-settings';
8
8
  import { eventStopPropagation } from '../../utils';
9
9
  import { X_LABEL_POSITIONS, Y_LABEL_POSITIONS, LABEL_POSITION_TYPE_SHOW } from '../../constants';
10
10
  import intl from '../../intl';
11
+ import SelectLineType from '../widgets/select-line-type';
11
12
  const StyleSettings = _ref => {
12
13
  let {
13
14
  chart,
@@ -96,12 +97,16 @@ const StyleSettings = _ref => {
96
97
  y_axis_min_right,
97
98
  y_axis_max_right,
98
99
  display_data,
99
- label_font_size
100
+ label_font_size,
101
+ line_type
100
102
  } = config;
101
103
  const xAxisLabelPosition = xAxisLabelOptions.find(option => option.value === x_axis_label_position) || xAxisLabelOptions[0];
102
104
  const yLeftAxisLabelPosition = yAxisLabelOptions.find(option => option.value === y_axis_left_label_position) || yAxisLabelOptions[0];
103
105
  const yRightAxisLabelPosition = yAxisLabelOptions.find(option => option.value === y_axis_right_label_position) || yAxisLabelOptions[0];
104
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FormGroup, {
106
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SelectLineType, {
107
+ selectedLineType: line_type,
108
+ onChange: onChange
109
+ }), /*#__PURE__*/React.createElement(Divider, null), /*#__PURE__*/React.createElement(FormGroup, {
105
110
  className: "sea-chart-parameter-item"
106
111
  }, /*#__PURE__*/React.createElement(Label, null, intl.get(xLabel)), /*#__PURE__*/React.createElement(Switch, {
107
112
  key: "x_axis_show_label",
@@ -5,7 +5,7 @@ import { CHART_LINE_TYPES } from '../../../constants';
5
5
  import intl from '../../../intl';
6
6
  function SelectLineType(_ref) {
7
7
  let {
8
- selectedLineType,
8
+ selectedLineType = CHART_LINE_TYPES[1],
9
9
  onChange
10
10
  } = _ref;
11
11
  const onChangeLineType = lineType => {
@@ -2,7 +2,7 @@ var _SQLStatisticsUtils;
2
2
  import dayjs from 'dayjs';
3
3
  import { CellType, MULTIPLE_CELL_VALUE_COLUMN_TYPE_MAP, getFormulaDisplayString, getPrecisionNumber, getTableById, getTableColumnByKey, isNumber } from 'dtable-utils';
4
4
  import { isObject } from 'lodash';
5
- import { CHART_SUMMARY_TYPE, CHART_TYPE, SUPPORT_DATA_SORT_CHART_TYPES, LABEL_COLORS, TABLE_DIMENSIONS, Y_AXIS_TYPE_PREFIX } from '../../constants';
5
+ import { CHART_SUMMARY_TYPE, CHART_TYPE, SUPPORT_DATA_SORT_CHART_TYPES, LABEL_COLORS, TABLE_DIMENSIONS, Y_AXIS_TYPE_PREFIX, STYLE_COLORS } from '../../constants';
6
6
  import { chartColumn2SqlColumn, summaryMethodColumn2SqlColumn } from '../sql';
7
7
  import { getClientLinkDisplayString } from '../cell-format-utils';
8
8
  import context from '../../context';
@@ -1025,17 +1025,33 @@ SQLStatisticsUtils.combinationSQLResult2Javascript = (chart, sqlRows, chartSQLMa
1025
1025
  let leftSummaryColumn = getTableColumnByKey(table, y_axis_left_summary_column);
1026
1026
  const summaryMethod = y_axis_left_summary_method.toUpperCase();
1027
1027
  const {
1028
- key: groupItem
1028
+ key
1029
1029
  } = summaryMethodColumn2SqlColumn(summaryMethod, leftSummaryColumn || {});
1030
- leftSummaryColumn && !groupItems.includes(groupItem) && groupItems.push(groupItem);
1030
+ const groupItem = {
1031
+ sqlName: key,
1032
+ column: leftSummaryColumn
1033
+ };
1034
+ const exists = groupItems.find(v => v.sqlName === key);
1035
+ if (!exists && leftSummaryColumn) {
1036
+ groupItems.push(groupItem);
1037
+ }
1038
+ // leftSummaryColumn && !groupItems.includes(groupItem) && groupItems.push(groupItem);
1031
1039
  y_axis_left_group_by_numeric_columns.forEach((item, index) => {
1032
1040
  const column = getTableColumnByKey(table, item.column_key);
1033
1041
  if (column) {
1034
1042
  const itemSummaryMethod = item.summary_method.toUpperCase();
1035
1043
  const {
1036
- key: groupItem
1044
+ key
1037
1045
  } = summaryMethodColumn2SqlColumn(itemSummaryMethod, column || {});
1038
- !groupItems.includes(groupItem) && groupItems.push(groupItem);
1046
+ const groupItem = {
1047
+ sqlName: key,
1048
+ column
1049
+ };
1050
+ const exists = groupItems.find(v => v.sqlName === key);
1051
+ if (!exists) {
1052
+ groupItems.push(groupItem);
1053
+ }
1054
+ // !groupItems.includes(groupItem) && groupItems.push(groupItem);
1039
1055
  }
1040
1056
  });
1041
1057
  sqlRows.forEach((item, index) => {
@@ -1046,7 +1062,10 @@ SQLStatisticsUtils.combinationSQLResult2Javascript = (chart, sqlRows, chartSQLMa
1046
1062
  valueRight = BaseUtils.getPrecisionNumber(valueRight, rightSummaryColumn.data);
1047
1063
  }
1048
1064
  groupItems.forEach((groupItem, index) => {
1049
- let value = item[groupItem] || 0;
1065
+ var _groupItem$column;
1066
+ const sqlName = groupItem.sqlName;
1067
+ const groupName = (_groupItem$column = groupItem.column) === null || _groupItem$column === void 0 ? void 0 : _groupItem$column.name;
1068
+ let value = item[sqlName] || 0;
1050
1069
  if (y_axis_right_summary_type === CHART_SUMMARY_TYPE.ADVANCED) {
1051
1070
  value = BaseUtils.getPrecisionNumber(value, leftSummaryColumn.data);
1052
1071
  }
@@ -1054,7 +1073,8 @@ SQLStatisticsUtils.combinationSQLResult2Javascript = (chart, sqlRows, chartSQLMa
1054
1073
  name: key,
1055
1074
  value_left: value,
1056
1075
  value_right: valueRight,
1057
- color: LABEL_COLORS[index % 12]
1076
+ group_name: groupName,
1077
+ color: STYLE_COLORS[0].colors[index % 12]
1058
1078
  });
1059
1079
  });
1060
1080
  });
@@ -91,6 +91,7 @@ import ScaleTranslate from '@antv/g2/esm/interaction/action/view/scale-translate
91
91
  import ScaleZoom from '@antv/g2/esm/interaction/action/view/scale-zoom';
92
92
  import { getAngle, polarToCartesian } from '@antv/g2/esm/util/graphics';
93
93
  import { rotate, transform, translate, zoom } from '@antv/g2/esm/util/transform';
94
+ import { Action, registerShape } from '@antv/g2';
94
95
  export { getRectWithCornerRadius } from '@antv/g2/esm/geometry/shape/interval/util';
95
96
  // 注册 G 渲染引擎
96
97
  registerEngine('canvas', CanvasEngine);
@@ -208,6 +209,47 @@ registerAction('reset-button', ButtonAction, {
208
209
  name: 'reset-button',
209
210
  text: 'reset'
210
211
  });
212
+ registerShape('interval', 'withGap', {
213
+ draw(cfg, container) {
214
+ const points = cfg.points;
215
+ let path = [];
216
+ path.push(['M', points[0].x, points[0].y]);
217
+ path.push(['L', points[1].x, points[1].y - 0.0015]);
218
+ path.push(['L', points[2].x, points[2].y - 0.0015]);
219
+ path.push(['L', points[3].x, points[3].y]);
220
+ path.push('Z');
221
+ path = this.parsePath(path);
222
+ return container.addShape('path', {
223
+ attrs: {
224
+ fill: cfg.color,
225
+ path
226
+ }
227
+ });
228
+ }
229
+ });
230
+ class HighlightXOnlyBar extends Action {
231
+ highlight() {
232
+ const elements = this.context.view.geometries[0].elements;
233
+ elements.forEach(element => {
234
+ const {
235
+ data
236
+ } = element.getModel();
237
+ if (data.type === 'bar') {
238
+ element.setState('active', true);
239
+ } else {
240
+ element.hide();
241
+ }
242
+ });
243
+ }
244
+ reset() {
245
+ const elements = this.context.view.geometries[0].elements;
246
+ elements.forEach(element => {
247
+ element.setState('active', false);
248
+ element.show();
249
+ });
250
+ }
251
+ }
252
+ registerAction('highlight-x-only-bar', HighlightXOnlyBar);
211
253
 
212
254
  // 注册默认的 Interaction 交互行为
213
255
  function isPointInView(context) {
@@ -367,17 +367,15 @@ class ChartDataSQL {
367
367
  let summary_column_name = '';
368
368
  if (right_summary_type === 'COUNT') {
369
369
  summary_column_name = this._summary_column_2_sql('COUNT', groupby_column);
370
+ return "SELECT ".concat(groupby_column_name, ", ").concat(summary_column_name, " FROM ").concat(this.table_name, " ").concat(this.filter_sql, " GROUP BY ").concat(groupby_column_name, " LIMIT 0, 5000");
370
371
  } else {
371
372
  const right_summary_column = this._get_column_by_key(y_axis_right_summary_column);
372
373
  if (right_summary_column) {
373
374
  const right_summary_method = y_axis_right_summary_method.toUpperCase();
374
375
  summary_column_name = this._summary_column_2_sql(right_summary_method, right_summary_column);
376
+ return "SELECT ".concat(groupby_column_name, ", COUNT(").concat(groupby_column_name, "), ").concat(summary_column_name, " FROM ").concat(this.table_name, " ").concat(this.filter_sql, " GROUP BY ").concat(groupby_column_name, " LIMIT 0, 5000");
375
377
  }
376
378
  }
377
- if (summary_column_name) {
378
- return "SELECT ".concat(groupby_column_name, ", ").concat(summary_column_name, " FROM ").concat(this.table_name, " ").concat(this.filter_sql, " GROUP BY ").concat(groupby_column_name, " LIMIT 0, 5000");
379
- }
380
- return "SELECT ".concat(groupby_column_name, " FROM ").concat(this.table_name, " ").concat(this.filter_sql, " GROUP BY ").concat(groupby_column_name, " LIMIT 0, 5000");
381
379
  }
382
380
  if (y_axis_left_group_by_multiple_numeric_column) {
383
381
  let column_groupby_numeric_columns = y_axis_left_group_by_numeric_columns.slice(0);
@@ -78,7 +78,7 @@ class Bar extends ChartComponent {
78
78
  this.chart.interval().label(y_axis_show_value ? 'value' : false, {
79
79
  style: {
80
80
  fontSize: BaseUtils.getLabelFontSize(label_font_size),
81
- fill: theme.labelColor,
81
+ fill: theme.ringLabelColor,
82
82
  stroke: '#fff',
83
83
  lineWidth: 1
84
84
  }
@@ -347,12 +347,10 @@ export default class ChartComponent extends Component {
347
347
  }
348
348
  return label;
349
349
  };
350
- // set legend
351
- this.setLegend = function (legendName) {
352
- let theme = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : CHART_THEME_COLOR['light'];
353
- let legendPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'bottom';
354
- if (!_this.chart) return;
355
- _this.chart.legend(legendName, {
350
+ this.getLegendConfig = function () {
351
+ let theme = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : CHART_THEME_COLOR['light'];
352
+ let legendPosition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top-right';
353
+ return {
356
354
  // custom: true,
357
355
  position: legendPosition,
358
356
  itemName: {
@@ -364,6 +362,8 @@ export default class ChartComponent extends Component {
364
362
  formatter: name => {
365
363
  if (!name && typeof name !== 'number') {
366
364
  return intl.get(EMPTY_NAME);
365
+ } else if (name === '_Others') {
366
+ return intl.get('Others');
367
367
  } else {
368
368
  return intl.get(name) || name;
369
369
  }
@@ -411,7 +411,26 @@ export default class ChartComponent extends Component {
411
411
  return style;
412
412
  }
413
413
  }
414
- });
414
+ };
415
+ };
416
+ // set legend
417
+ this.setLegend = function (legendName) {
418
+ let theme = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : CHART_THEME_COLOR['light'];
419
+ let legendPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'bottom';
420
+ if (!_this.chart) return;
421
+ const basicLegendConfig = _this.getLegendConfig(theme, legendPosition);
422
+ _this.chart.legend(legendName, basicLegendConfig);
423
+ };
424
+ // theta is pie or ring chart
425
+ this.setLegendForTheta = function (legendName) {
426
+ let theme = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : CHART_THEME_COLOR['light'];
427
+ let legendPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'bottom';
428
+ let offsetX = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
429
+ if (!_this.chart) return;
430
+ const basicLegendConfig = _this.getLegendConfig(theme, legendPosition);
431
+ basicLegendConfig.offsetX = offsetX;
432
+ basicLegendConfig.itemName.style.fontSize = theme.fontSize;
433
+ _this.chart.legend(legendName, basicLegendConfig);
415
434
  };
416
435
  this.setColorMap = data => {
417
436
  let currentIdx = 0;
@@ -465,6 +484,7 @@ export default class ChartComponent extends Component {
465
484
  textOverflow: 'ellipsis'
466
485
  },
467
486
  'g2-tooltip-list-item': {
487
+ marginTop: '8px',
468
488
  marginBottom: '8px',
469
489
  display: 'flex'
470
490
  },
@@ -553,6 +573,13 @@ export default class ChartComponent extends Component {
553
573
  ...lineToolTipSettings
554
574
  });
555
575
  };
576
+ // theta is pie or ring chart
577
+ this.setToolTipForTheta = () => {
578
+ const settings = this.getToolTipSettings();
579
+ settings.showTitle = false;
580
+ settings.containerTpl = "<div class=\"g2-tooltip\">\n <ul class=\"g2-tooltip-list\"></ul>\n </div>";
581
+ this.chart.tooltip(settings);
582
+ };
556
583
  this.isShowXAxisLabel = chart => {
557
584
  return !!(chart && chart.config && chart.config.x_axis_show_label);
558
585
  };
@@ -1,11 +1,14 @@
1
1
  import React from 'react';
2
+ import PropTypes from 'prop-types';
2
3
  import classnames from 'classnames';
3
4
  import { getTableById, getTableColumnByKey } from 'dtable-utils';
5
+ import { throttle } from 'lodash-es';
4
6
  import { Chart } from '../../utils/custom-g2';
5
7
  import { BaseUtils, isFunction } from '../../utils';
6
- import { CHART_SUMMARY_TYPE, LABEL_COLORS, CHART_SUMMARY_SHOW, LABEL_POSITION_TYPE, EMPTY_NAME } from '../../constants';
8
+ import { CHART_SUMMARY_TYPE, LABEL_COLORS, CHART_SUMMARY_SHOW, LABEL_POSITION_TYPE, EMPTY_NAME, CHART_LINE_TYPES, CHART_STYLE_COLORS } from '../../constants';
7
9
  import intl from '../../intl';
8
- class Combination extends React.Component {
10
+ import ChartComponent from './chart-component';
11
+ class Combination extends ChartComponent {
9
12
  constructor(props) {
10
13
  super(props);
11
14
  this.clearChart = () => {
@@ -42,8 +45,10 @@ class Combination extends React.Component {
42
45
  y_axis_auto_range_right,
43
46
  y_axis_min_right,
44
47
  y_axis_max_right,
48
+ x_axis_column_key,
45
49
  display_data,
46
- label_font_size
50
+ label_font_size,
51
+ line_type
47
52
  } = chart.config;
48
53
  this.chart = new Chart({
49
54
  container: this.container,
@@ -52,12 +57,42 @@ class Combination extends React.Component {
52
57
  height: '100%',
53
58
  appendPadding: [display_data ? 17 : 0, 0, 0, 0]
54
59
  });
60
+ const isSmooth = line_type === CHART_LINE_TYPES[1] || !line_type;
61
+
62
+ // y-axis label width:6 + 10
63
+ const chartWidth = this.chart.width - 6 - 10;
64
+ const nameCountMap = data.reduce((acc, cur) => {
65
+ if (!acc[cur.name]) {
66
+ acc[cur.name] = 1;
67
+ } else {
68
+ acc[cur.name] += 1;
69
+ }
70
+ return acc;
71
+ }, {});
72
+ const nameNums = Object.keys(nameCountMap).length;
73
+ const countValues = Object.values(nameCountMap).reduce((acc, value) => {
74
+ acc[value] = (acc[value] || 0) + 1;
75
+ return acc;
76
+ }, {});
77
+ const mostCommonCount = Object.entries(countValues).reduce((max, entry) => {
78
+ return entry[1] > max[1] ? entry : max;
79
+ }, ['', -Infinity])[0];
80
+ const singleBarWidth = Math.round(chartWidth / (2 * nameNums * mostCommonCount));
81
+ const singleBarRadius = Math.min(singleBarWidth / 5, 10);
55
82
  const currentTheme = BaseUtils.getCurrentTheme(themeName);
83
+ this.colorMap = data.reduce((acc, cur, index) => {
84
+ if (!acc[cur.group_name]) {
85
+ acc[cur.group_name] = cur.color;
86
+ }
87
+ return acc;
88
+ }, {});
56
89
  this.chart.data(data);
57
90
  this.chart.coordinate('rect');
58
91
  const table = getTableById(tables, table_id);
59
92
  const leftSummaryColumn = getTableColumnByKey(table, y_axis_left_summary_column) || {};
60
93
  const rightSummaryColumn = getTableColumnByKey(table, y_axis_right_summary_column) || {};
94
+ // const xAxisColumn = getTableColumnByKey(table, x_axis_column_key);
95
+
61
96
  let leftValueScaleOptions = {
62
97
  formatter: value => {
63
98
  return BaseUtils.getSummaryValueDisplayString(leftSummaryColumn, value, y_axis_left_summary_method);
@@ -89,12 +124,25 @@ class Combination extends React.Component {
89
124
  value_right: rightValueScaleOptions
90
125
  });
91
126
  this.chart.axis('name', {
127
+ line: {
128
+ style: {
129
+ stroke: theme.XAxisColor,
130
+ lineWidth: 1
131
+ }
132
+ },
133
+ tickLine: {
134
+ length: 5,
135
+ // color of the tick line
136
+ stroke: theme.XAxisColor
137
+ },
92
138
  label: {
93
139
  style: {
94
140
  fill: theme.textColor,
95
- stroke: '#fff',
96
- lineWidth: 1
141
+ fontSize: theme.fontSize
97
142
  },
143
+ offset: 10,
144
+ autoRotate: true,
145
+ // rotate: data.length > 11 ? 0.3 : 0,
98
146
  formatter: name => {
99
147
  const isString = typeof name === 'string';
100
148
  if (!isString) name = String(name);
@@ -106,70 +154,142 @@ class Combination extends React.Component {
106
154
  }
107
155
  return name;
108
156
  }
109
- },
110
- line: {
111
- style: theme.gridColor
112
157
  }
113
158
  });
114
159
  this.chart.axis('value_left', {
160
+ label: {
161
+ offset: 6,
162
+ style: {
163
+ fontSize: theme.fontSize,
164
+ // fontSize: 66,
165
+ fill: theme.textColor
166
+ }
167
+ },
115
168
  grid: {
116
169
  line: {
117
170
  style: {
118
- stroke: theme.gridColor
171
+ stroke: theme.gridColor,
172
+ lineDash: [8, 3]
119
173
  }
120
174
  }
121
- },
122
- label: {
123
- style: {
124
- fill: theme.textColor
125
- }
126
175
  }
127
176
  });
128
177
  this.chart.axis('value_right', {
129
178
  grid: null,
130
179
  label: {
180
+ offset: 6,
131
181
  style: {
182
+ fontSize: theme.fontSize,
132
183
  fill: theme.textColor
133
184
  }
134
185
  }
135
186
  });
136
- let legendPosition = 'bottom';
187
+ let legendPosition = 'top-right';
188
+ // const leftLegendConfig = this.getLegendConfig(undefined, legendPosition);
189
+ // this.chart.legend(leftLegendConfig);
137
190
  this.chart.legend({
138
- custom: true,
191
+ // custom: true,
139
192
  position: legendPosition,
140
193
  itemName: {
141
194
  style: {
142
- fill: theme.textColor
195
+ fill: theme.legendTextColor,
196
+ fontSize: theme.legendFontSize
197
+ },
198
+ formatter: name => {
199
+ if (!name && typeof name !== 'number') {
200
+ return intl.get(EMPTY_NAME);
201
+ } else if (name === '_Others') {
202
+ return intl.get('Others');
203
+ } else {
204
+ return intl.get(name) || name;
205
+ }
143
206
  }
144
207
  },
208
+ itemSpacing: 28,
145
209
  items: this.getLegend(leftSummaryColumn, rightSummaryColumn, table, currentTheme)
146
210
  });
147
- this.chart.interval().position('name*value_left').color('color', color => {
211
+ const adjust = y_axis_left_group_by_multiple_numeric_column ? {
212
+ type: 'dodge',
213
+ dodgeBy: 'group_name',
214
+ dodgePadding: singleBarWidth / 3
215
+ } : {
216
+ type: 'dodge'
217
+ };
218
+ const interval = this.chart.interval().label(display_data ? 'value_left' : false, {
219
+ content: data => {
220
+ if (this.currentName) {
221
+ return data.name === this.currentName ? data.value_left : '';
222
+ }
223
+ return data.value_left;
224
+ },
225
+ style: {
226
+ fontSize: BaseUtils.getLabelFontSize(label_font_size),
227
+ fill: theme.labelColor,
228
+ stroke: '#fff',
229
+ lineWidth: 1
230
+ }
231
+ }).position('name*value_left').color('color', color => {
148
232
  if (!y_axis_left_group_by_multiple_numeric_column) {
149
233
  return currentTheme.colors[0];
150
234
  }
151
235
  return color;
152
- }).adjust('dodge').tooltip('name*value_left', (name, value) => {
236
+ }).adjust(adjust).size(singleBarWidth).tooltip('name*value_left', (name, value) => {
237
+ const {
238
+ y_axis_left_group_by_multiple_numeric_column
239
+ } = this.props.chart.config;
240
+ const isMultiple = y_axis_left_group_by_multiple_numeric_column;
241
+ let title = '';
242
+ if (isMultiple) {
243
+ title = name;
244
+ } else {
245
+ title = y_axis_left_summary_type === CHART_SUMMARY_TYPE.COUNT ? intl.get('Amount') : intl.get(CHART_SUMMARY_SHOW[y_axis_left_summary_method]);
246
+ }
153
247
  return {
154
- title: y_axis_left_summary_type === CHART_SUMMARY_TYPE.COUNT ? intl.get('Amount') : intl.get(CHART_SUMMARY_SHOW[y_axis_left_summary_method]),
248
+ title,
155
249
  value: BaseUtils.getSummaryValueDisplayString(leftSummaryColumn, value, y_axis_left_summary_method),
156
250
  name: !name && typeof name !== 'number' ? intl.get(EMPTY_NAME) : name
157
251
  };
252
+ }).state({
253
+ active: {
254
+ style: {
255
+ stroke: null
256
+ }
257
+ }
258
+ }).style({
259
+ fillOpacity: 1,
260
+ radius: [singleBarRadius, singleBarRadius, 0, 0]
158
261
  });
159
- this.chart.tooltip({
160
- showMarkers: false
161
- });
162
- this.chart.line().position('name*value_right').color(currentTheme.colors[1]).size(3).tooltip('name*value_right', (name, value) => {
163
- return {
164
- title: y_axis_right_summary_type === CHART_SUMMARY_TYPE.COUNT ? intl.get('Amount') : intl.get(CHART_SUMMARY_SHOW[y_axis_right_summary_method]),
165
- value: BaseUtils.getSummaryValueDisplayString(rightSummaryColumn, value, y_axis_right_summary_method),
166
- name: !name && typeof name !== 'number' ? intl.get(EMPTY_NAME) : name
167
- };
168
- });
169
- this.chart.point().position('name*value_right').color(currentTheme.colors[1]).size(3).shape('circle').label(display_data ? 'value_right' : false, {
262
+ this.formatDataByName(data);
263
+ if (!y_axis_left_group_by_multiple_numeric_column) {
264
+ this.setToolTipForInterval();
265
+ } else {
266
+ this.setToolTipForInterval(true, leftSummaryColumn, y_axis_left_summary_method);
267
+ }
268
+ this.chart.line().position('name*value_right').color(currentTheme.colors[1]).shape(isSmooth ? 'smooth' : 'line').style({
269
+ lineWidth: 3,
270
+ opacity: 1
271
+ }).tooltip(false);
272
+ const point = this.chart.point().position('name*value_right').color(currentTheme.colors[1]).animate({
273
+ appear: {
274
+ animation: 'fadeIn',
275
+ duration: 1000,
276
+ easing: 'easeLinear'
277
+ }
278
+ }).shape('circle').size(4).style({
279
+ stroke: 0,
280
+ fillOpacity: 1
281
+ }).label(display_data ? 'value_right' : false, {
282
+ content: data => {
283
+ if (this.currentName) {
284
+ return data.name === this.currentName ? data.value_right : '';
285
+ }
286
+ return data.value_right;
287
+ },
170
288
  style: {
171
289
  fontSize: BaseUtils.getLabelFontSize(label_font_size),
172
- fill: theme.textColor
290
+ fill: theme.labelColor,
291
+ stroke: '#fff',
292
+ lineWidth: 1
173
293
  }
174
294
  }).tooltip('name*value_right', (name, value) => {
175
295
  return {
@@ -177,7 +297,121 @@ class Combination extends React.Component {
177
297
  value: BaseUtils.getSummaryValueDisplayString(rightSummaryColumn, value, y_axis_right_summary_method),
178
298
  name: !name && typeof name !== 'number' ? intl.get(EMPTY_NAME) : name
179
299
  };
300
+ }).state({
301
+ active: {
302
+ style: {
303
+ stroke: null
304
+ }
305
+ }
180
306
  });
307
+ if (display_data) {
308
+ this.chart.on('element:mouseenter', throttle(e => {
309
+ const currentName = e.data.data.name;
310
+ if (this.currentName && this.currentName === currentName) return;
311
+ this.currentName = currentName;
312
+ this.chart.render(true);
313
+ }, 100));
314
+ this.chart.on('element:mouseleave', throttle(e => {
315
+ if (!this.currentName) return;
316
+ this.currentName = null;
317
+ this.chart.render(true);
318
+ }, 100));
319
+ }
320
+ this.chart.on('interval:mouseenter', e => {
321
+ if (this.isInsideInterval) return;
322
+ this.isInsideInterval = true;
323
+ point.style({
324
+ stroke: 0,
325
+ fillOpacity: 0.3
326
+ });
327
+ interval.tooltip('name*value_left', (name, value) => {
328
+ const {
329
+ y_axis_left_group_by_multiple_numeric_column
330
+ } = this.props.chart.config;
331
+ const isMultiple = y_axis_left_group_by_multiple_numeric_column;
332
+ let title = '';
333
+ if (isMultiple) {
334
+ title = name;
335
+ } else {
336
+ title = y_axis_left_summary_type === CHART_SUMMARY_TYPE.COUNT ? intl.get('Amount') : intl.get(CHART_SUMMARY_SHOW[y_axis_left_summary_method]);
337
+ }
338
+ return {
339
+ title,
340
+ value: BaseUtils.getSummaryValueDisplayString(rightSummaryColumn, value, y_axis_right_summary_method),
341
+ name: !name && typeof name !== 'number' ? intl.get(EMPTY_NAME) : name
342
+ };
343
+ });
344
+ point.tooltip(false);
345
+ this.setToolTipForInterval(true, leftSummaryColumn, y_axis_left_summary_method);
346
+ this.chart.render(true);
347
+ });
348
+ this.chart.on('interval:mouseleave', e => {
349
+ if (!this.isInsideInterval) return;
350
+ this.isInsideInterval = false;
351
+ point.style({
352
+ stroke: 0,
353
+ fillOpacity: 1
354
+ });
355
+ interval.tooltip(false);
356
+ point.tooltip('name*value_right', (name, value) => {
357
+ return {
358
+ title: y_axis_right_summary_type === CHART_SUMMARY_TYPE.COUNT ? intl.get('Amount') : intl.get(CHART_SUMMARY_SHOW[y_axis_right_summary_method]),
359
+ value: BaseUtils.getSummaryValueDisplayString(rightSummaryColumn, value, y_axis_right_summary_method),
360
+ name: !name && typeof name !== 'number' ? intl.get(EMPTY_NAME) : name
361
+ };
362
+ });
363
+ this.setToolTipForPointView();
364
+ this.chart.render(true);
365
+ });
366
+ this.chart.on('point:mouseenter', throttle(e => {
367
+ if (this.isInsidePoint) return;
368
+ this.isInsidePoint = true;
369
+ interval.style({
370
+ radius: [singleBarRadius, singleBarRadius, 0, 0],
371
+ fillOpacity: 0.3
372
+ });
373
+ console.log(e);
374
+ point.style('name', name => {
375
+ let r = 4;
376
+ if (name === e.data.data.name) {
377
+ r = 8;
378
+ }
379
+ return {
380
+ r,
381
+ stroke: 0,
382
+ fillOpacity: 1
383
+ };
384
+ });
385
+ this.chart.render(true);
386
+ }, 10));
387
+ this.chart.on('point:mouseleave', throttle(e => {
388
+ if (!this.isInsidePoint) return;
389
+ this.isInsidePoint = false;
390
+ interval.style({
391
+ radius: [singleBarRadius, singleBarRadius, 0, 0],
392
+ fillOpacity: 1
393
+ });
394
+ point.style('name', name => {
395
+ let r = 4;
396
+ return {
397
+ r,
398
+ stroke: 0,
399
+ fillOpacity: 1
400
+ };
401
+ });
402
+ this.chart.render(true);
403
+ }, 100));
404
+ this.chart.interaction('element-highlight-by-x', {
405
+ start: [{
406
+ trigger: 'element:mouseenter',
407
+ action: 'element-highlight-by-x:highlight'
408
+ }],
409
+ end: [{
410
+ trigger: 'element:mouseleave',
411
+ action: 'element-highlight-by-x:reset'
412
+ }]
413
+ });
414
+ // this.chart.interaction('custom-highlight-by-x');
181
415
  isFunction(customRender) && customRender(this.chart);
182
416
  this.chart.removeInteraction('legend-filter');
183
417
  this.chart.render();
@@ -286,6 +520,31 @@ class Combination extends React.Component {
286
520
  yAxisRightLabel.parentNode.removeChild(yAxisRightLabel);
287
521
  }
288
522
  };
523
+ this.drawLeftLegendSymbol = (x, y) => {
524
+ // The total width of the capsule is 20px, and its height is 6px. Thus, the radius of the semi-circle is half the height, which is 3px.
525
+ const r = 3;
526
+ // The width of the rectangular part in the middle of the capsule is the total width minus the diameters of the two semi-circles, which is 14px.
527
+ const rectWidth = 14;
528
+ y -= 1;
529
+ x -= 5;
530
+ return [['M', x - rectWidth / 2, y - r],
531
+ // Start from the left edge of the left semi-circle
532
+ ['L', x + rectWidth / 2, y - r],
533
+ // Draw the top line to the left edge of the right semi-circle
534
+ ['A', r, r, 0, 0, 1, x + rectWidth / 2, y + r],
535
+ // Draw the right semi-circle
536
+ ['L', x - rectWidth / 2, y + r],
537
+ // Draw the bottom line back to the right edge of the left semi-circle
538
+ ['A', r, r, 0, 0, 1, x - rectWidth / 2, y - r] // Draw the left semi-circle
539
+ ];
540
+ };
541
+ this.drawRightLegendSymbol = (x, y) => {
542
+ const r = 2;
543
+ const size = 12;
544
+ y -= 7;
545
+ x -= 8;
546
+ return [['M', x + r, y], ['L', x + size - r, y], ['A', r, r, 0, 0, 1, x + size, y + r], ['L', x + size, y + size - r], ['A', r, r, 0, 0, 1, x + size - r, y + size], ['L', x + r, y + size], ['A', r, r, 0, 0, 1, x, y + size - r], ['L', x, y + r], ['A', r, r, 0, 0, 1, x + r, y], ['Z']];
547
+ };
289
548
  this.getLegend = (leftSummaryColumn, rightSummaryColumn, table, currentTheme) => {
290
549
  const {
291
550
  chart
@@ -300,21 +559,18 @@ class Combination extends React.Component {
300
559
  value: 'value_left',
301
560
  name: leftSummaryColumn.name || intl.get('Left'),
302
561
  marker: {
303
- symbol: 'square',
562
+ symbol: this.drawLeftLegendSymbol,
304
563
  style: {
305
- fill: currentTheme.colors[0],
306
- r: 5
564
+ fill: this.colorMap[leftSummaryColumn.name] || currentTheme.colors[0]
307
565
  }
308
566
  }
309
567
  }, {
310
568
  value: 'value_right',
311
569
  name: rightSummaryColumn.name || intl.get('Right'),
312
570
  marker: {
313
- symbol: 'hyphen',
571
+ symbol: this.drawRightLegendSymbol,
314
572
  style: {
315
- stroke: currentTheme.colors[1],
316
- r: 5,
317
- lineWidth: 3
573
+ fill: currentTheme.colors[1]
318
574
  }
319
575
  }
320
576
  });
@@ -323,11 +579,10 @@ class Combination extends React.Component {
323
579
  value: 'value_left',
324
580
  name: leftSummaryColumn.name || intl.get('Left'),
325
581
  marker: {
326
- fill: LABEL_COLORS[0],
327
- symbol: 'square',
328
582
  style: {
329
- r: 5
330
- }
583
+ fill: this.colorMap[leftSummaryColumn.name] || currentTheme.colors[0]
584
+ },
585
+ symbol: this.drawLeftLegendSymbol
331
586
  }
332
587
  });
333
588
  y_axis_left_group_by_numeric_columns.forEach((item, index) => {
@@ -337,10 +592,9 @@ class Combination extends React.Component {
337
592
  value: 'group_name',
338
593
  name: column.name || intl.get('Left'),
339
594
  marker: {
340
- symbol: 'square',
595
+ symbol: this.drawLeftLegendSymbol,
341
596
  style: {
342
- fill: LABEL_COLORS[(index + 1) % 12],
343
- r: 5
597
+ fill: this.colorMap[column.name]
344
598
  }
345
599
  }
346
600
  });
@@ -350,17 +604,49 @@ class Combination extends React.Component {
350
604
  value: 'value_right',
351
605
  name: rightSummaryColumn.name || intl.get('Right'),
352
606
  marker: {
353
- symbol: 'hyphen',
607
+ symbol: this.drawRightLegendSymbol,
354
608
  style: {
355
- stroke: currentTheme.colors[1],
356
- r: 5,
357
- lineWidth: 3
609
+ fill: currentTheme.colors[1]
358
610
  }
359
611
  }
360
612
  });
361
613
  }
362
614
  return legendItems;
363
615
  };
616
+ this.setToolTipForInterval = (isGroup, summaryColumn, y_axis_summary_method) => {
617
+ const {
618
+ y_axis_left_group_by_multiple_numeric_column,
619
+ y_axis_right_summary_method
620
+ } = this.props.chart.config;
621
+ const settings = this.getToolTipSettings(isGroup, summaryColumn, y_axis_summary_method);
622
+ settings.showMarkers = false;
623
+ settings.showTitle = true;
624
+ if (isGroup) {
625
+ settings.customItems = items => {
626
+ const activeItem = items[0];
627
+ const name = activeItem.data.name;
628
+ const isMultiple = y_axis_left_group_by_multiple_numeric_column;
629
+ return this.formatedDataByName[name].map(item => {
630
+ const res = {
631
+ color: item.color || CHART_STYLE_COLORS[0],
632
+ data: item,
633
+ name: !item.group_name && typeof item.group_name !== 'number' && isMultiple ? intl.get(EMPTY_NAME) : item.group_name || item.name,
634
+ title: isMultiple ? item.name : intl.get(CHART_SUMMARY_SHOW[y_axis_right_summary_method]),
635
+ value: BaseUtils.getSummaryValueDisplayString(summaryColumn, item.value_left, y_axis_summary_method)
636
+ };
637
+ return res;
638
+ });
639
+ };
640
+ }
641
+ this.chart.tooltip(settings);
642
+ };
643
+ this.setToolTipForPointView = () => {
644
+ const settings = this.getToolTipSettings();
645
+ settings.showTitle = true;
646
+ settings.containerTpl = "<div class=\"g2-tooltip\">\n <ul class=\"g2-tooltip-list\"></ul>\n </div>";
647
+ settings.itemTpl = "<li class=\"g2-tooltip-list-item\">\n <span class=\"g2-tooltip-marker\" style=\"background-color:{color};display:inline-block;\"></span>\n <span class=\"g2-tooltip-name\">{name}</span>\n <span class=\"g2-tooltip-value\">{value}</span>\n </li>";
648
+ this.chart.tooltip(settings);
649
+ };
364
650
  this.isShowXAxisLabel = chart => {
365
651
  return !!(chart && chart.config && chart.config.x_axis_show_label);
366
652
  };
@@ -400,4 +686,15 @@ class Combination extends React.Component {
400
686
  });
401
687
  }
402
688
  }
689
+ Combination.propTypes = {
690
+ themeName: PropTypes.string,
691
+ canvasStyle: PropTypes.object,
692
+ chart: PropTypes.object,
693
+ summaryColumn: PropTypes.object,
694
+ result: PropTypes.array,
695
+ tables: PropTypes.array,
696
+ theme: PropTypes.object,
697
+ toggleRecords: PropTypes.func,
698
+ customRender: PropTypes.func
699
+ };
403
700
  export default Combination;
@@ -37,7 +37,7 @@ class Pie extends ChartComponent {
37
37
  } = this.props;
38
38
  if (data.length === 0) return;
39
39
  this.chart.coordinate('theta', {
40
- radius: 0.95
40
+ radius: 0.7
41
41
  });
42
42
  const {
43
43
  show_legend,
@@ -56,34 +56,8 @@ class Pie extends ChartComponent {
56
56
  this.props.toggleRecords(e.data.data);
57
57
  });
58
58
  if (show_legend) {
59
- this.chart.legend('name', {
60
- position: 'right',
61
- custom: true,
62
- itemName: {
63
- style: {
64
- fill: theme.labelColor
65
- }
66
- },
67
- items: newData.map((obj, index) => {
68
- let name = obj.name;
69
- if (!obj.name && typeof obj.name !== 'number') {
70
- name = intl.get('Empty');
71
- } else if (obj.name === '_Others') {
72
- name = intl.get('Others');
73
- }
74
- return {
75
- name,
76
- value: obj.value,
77
- marker: {
78
- symbol: 'circle',
79
- style: {
80
- r: 5,
81
- fill: colorSet[index]
82
- }
83
- }
84
- };
85
- })
86
- });
59
+ // lengend itself has a width of nearly 30px, 90 + 30 = 120
60
+ this.setLegendForTheta('name', undefined, 'right', -90);
87
61
  } else {
88
62
  this.chart.legend(false);
89
63
  }
@@ -94,18 +68,21 @@ class Pie extends ChartComponent {
94
68
  nice: false
95
69
  }
96
70
  });
97
- this.chart.interval().adjust('stack').position('value').color('name', colorSet).label('value*percent', (value, percent) => {
71
+ this.isInnerLabel = !label_position || label_position === CHART_LABEL_POSITIONS[0];
72
+ this.innerLabelOffset = '-25%';
73
+ this.outerLabelOffset = 20;
74
+ this.currentChart = this.chart.interval().adjust('stack').position('value').color('name', colorSet).label('value*percent', (value, percent) => {
98
75
  const displayValue = BaseUtils.getSummaryValueDisplayString(summaryColumn, value, summary_method);
99
76
  return {
100
77
  content: this.getLabel(displayValue, percent)
101
78
  };
102
79
  }, {
103
- offset: !label_position || label_position === CHART_LABEL_POSITIONS[0] ? -2 : undefined,
80
+ offset: this.isInnerLabel ? this.innerLabelOffset : this.outerLabelOffset,
104
81
  style: {
105
- fill: '#000000',
82
+ fill: theme.ringLabelColor,
106
83
  fontSize: BaseUtils.getLabelFontSize(label_font_size),
107
- shadowBlur: 2,
108
- shadowColor: 'rgba(0, 0, 0, .45)'
84
+ stroke: '#fff',
85
+ lineWidth: 1
109
86
  }
110
87
  }).tooltip('name*value*percent', (name, value, percent) => {
111
88
  let title = name;
@@ -127,22 +104,92 @@ class Pie extends ChartComponent {
127
104
  stroke: 0
128
105
  }
129
106
  }
107
+ }).shape('withClip2').style('value', value => {
108
+ return {
109
+ lineWidth: 2,
110
+ stroke: '#fff'
111
+ };
130
112
  });
131
- this.chart.tooltip({
132
- showTitle: false,
133
- showMarkers: false,
134
- itemTpl: '<div class="g2-tooltip-list-item"><span style="background-color:{color};" class="g2-tooltip-marker"></span>{name}: {value}</div>'
135
- });
136
- this.chart.interaction('chart-active');
113
+ this.setToolTipForTheta();
114
+ this.chart.interaction('element-highlight');
115
+ this.chart.on('interval:mouseenter', evt => this.onEnterElement(evt, {
116
+ summaryColumn,
117
+ summaryMethod: summary_method
118
+ }));
119
+ this.chart.on('interval:mouseleave', evt => this.onLeaveElement(evt, {
120
+ summaryColumn,
121
+ summaryMethod: summary_method
122
+ }));
137
123
  isFunction(customRender) && customRender(this.chart);
138
124
  this.chart.render();
139
125
  };
126
+ this.onEnterElement = (evt, _ref) => {
127
+ let {
128
+ summaryColumn,
129
+ summaryMethod
130
+ } = _ref;
131
+ const {
132
+ label_font_size
133
+ } = this.props.chart.config;
134
+ const {
135
+ theme
136
+ } = this.props;
137
+ const item = evt.data.data;
138
+ this.currentChart.label('name*value*percent', (name, value, percent) => {
139
+ let displayValue;
140
+ if (item.name === name) {
141
+ displayValue = BaseUtils.getSummaryValueDisplayString(summaryColumn, value, summaryMethod);
142
+ } else {
143
+ displayValue = '';
144
+ }
145
+ return {
146
+ content: this.getLabel(displayValue, percent)
147
+ };
148
+ }, {
149
+ offset: this.isInnerLabel ? this.innerLabelOffset : this.outerLabelOffset,
150
+ style: {
151
+ fill: theme.ringLabelColor,
152
+ fontSize: BaseUtils.getLabelFontSize(label_font_size),
153
+ stroke: '#fff',
154
+ lineWidth: 1
155
+ }
156
+ });
157
+ this.chart.render(true);
158
+ };
159
+ this.onLeaveElement = (evt, _ref2) => {
160
+ let {
161
+ summaryColumn,
162
+ summaryMethod
163
+ } = _ref2;
164
+ const {
165
+ summary_method,
166
+ label_font_size
167
+ } = this.props.chart.config;
168
+ const {
169
+ theme
170
+ } = this.props;
171
+ this.currentChart.label('value*percent', (value, percent) => {
172
+ let displayValue = BaseUtils.getSummaryValueDisplayString(summaryColumn, value, summary_method);
173
+ return {
174
+ content: this.getLabel(displayValue, percent)
175
+ };
176
+ }, {
177
+ offset: this.isInnerLabel ? this.innerLabelOffset : this.outerLabelOffset,
178
+ style: {
179
+ fill: theme.ringLabelColor,
180
+ fontSize: BaseUtils.getLabelFontSize(label_font_size),
181
+ stroke: '#fff',
182
+ lineWidth: 1
183
+ }
184
+ });
185
+ this.chart.render(true);
186
+ };
140
187
  this.getLabel = (value, percent) => {
141
188
  const {
142
189
  display_label,
143
190
  label_format
144
191
  } = this.props.chart.config;
145
- if (!display_label) {
192
+ if (!display_label || !value || !percent) {
146
193
  return '';
147
194
  }
148
195
  switch (label_format) {
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { CHART_LABEL_POSITIONS, CHART_LABEL_FORMATS } from '../../constants';
3
+ import { CHART_LABEL_POSITIONS, CHART_LABEL_FORMATS, CHART_THEME_COLOR } from '../../constants';
4
4
  import { BaseUtils, isFunction } from '../../utils';
5
5
  import intl from '../../intl';
6
6
  import ChartComponent from './chart-component';
@@ -28,21 +28,25 @@ class Ring extends ChartComponent {
28
28
  result: data,
29
29
  chart,
30
30
  tables,
31
+ theme,
31
32
  summaryColumn,
32
33
  customRender
33
34
  } = this.props;
35
+ const radius = 0.7;
36
+ const innerRadius = 0.8;
34
37
  if (data.length === 0) return;
35
38
  this.sum = data.total;
36
39
  this.chart.coordinate('theta', {
37
- radius: 0.95,
38
- innerRadius: 0.7
40
+ radius,
41
+ innerRadius
39
42
  });
40
43
  const {
41
44
  show_legend,
42
45
  display_label,
43
46
  label_position,
44
47
  label_font_size,
45
- summary_method
48
+ summary_method,
49
+ label_format
46
50
  } = chart.config;
47
51
  const {
48
52
  data: newData,
@@ -56,34 +60,8 @@ class Ring extends ChartComponent {
56
60
  this.props.toggleRecords(e.data.data);
57
61
  });
58
62
  if (show_legend) {
59
- this.chart.legend('name', {
60
- position: 'right',
61
- custom: true,
62
- itemName: {
63
- style: {
64
- fill: '#6e6e6e'
65
- }
66
- },
67
- items: newData.map((obj, index) => {
68
- let name = obj.name;
69
- if (!obj.name && typeof obj.name !== 'number') {
70
- name = intl.get('Empty');
71
- } else if (obj.name === '_Others') {
72
- name = intl.get('Others');
73
- }
74
- return {
75
- name,
76
- value: obj.value,
77
- marker: {
78
- symbol: 'circle',
79
- style: {
80
- r: 5,
81
- fill: colorSet[index]
82
- }
83
- }
84
- };
85
- })
86
- });
63
+ // lengend itself has a width of nearly 30px, 90 + 30 = 120
64
+ this.setLegendForTheta('name', undefined, 'right', -90);
87
65
  } else {
88
66
  this.chart.legend(false);
89
67
  }
@@ -94,18 +72,37 @@ class Ring extends ChartComponent {
94
72
  nice: false
95
73
  }
96
74
  });
97
- this.chart.interval().adjust('stack').position('value').color('name', colorSet).label('value*percent', (value, percent) => {
75
+ const labelWidthMap = {
76
+ [CHART_LABEL_FORMATS[0]]: 30,
77
+ [CHART_LABEL_FORMATS[1]]: 20,
78
+ [CHART_LABEL_FORMATS[2]]: 40
79
+ };
80
+ const currntLabelWidth = labelWidthMap[label_format];
81
+ let elementWidth = this.chart.height * radius * (1 - innerRadius) / 2;
82
+ const isInnerLabel = !label_position || label_position === CHART_LABEL_POSITIONS[0];
83
+ this.isInnerLabel = isInnerLabel;
84
+ let innerLabelOffset;
85
+ let outerLabelOffset = 20;
86
+ if (elementWidth < currntLabelWidth) {
87
+ innerLabelOffset = '0%';
88
+ } else {
89
+ // label position: elementWidth - labelWith / 2
90
+ innerLabelOffset = -(elementWidth - currntLabelWidth) / 2;
91
+ }
92
+ this.innerLabelOffset = innerLabelOffset;
93
+ this.outerLabelOffset = outerLabelOffset;
94
+ this.currentChart = this.chart.interval().adjust('stack').position('value').color('name', colorSet).label('value*percent', (value, percent) => {
98
95
  const displayValue = BaseUtils.getSummaryValueDisplayString(summaryColumn, value, summary_method);
99
96
  return {
100
97
  content: this.getLabel(displayValue, percent)
101
98
  };
102
99
  }, {
103
- offset: !label_position || label_position === CHART_LABEL_POSITIONS[0] ? -2 : undefined,
100
+ offset: isInnerLabel ? innerLabelOffset : 20,
104
101
  style: {
105
- fill: '#000000',
102
+ fill: theme.ringLabelColor,
106
103
  fontSize: BaseUtils.getLabelFontSize(label_font_size),
107
- shadowBlur: 2,
108
- shadowColor: 'rgba(0, 0, 0, .45)'
104
+ stroke: '#fff',
105
+ lineWidth: 1
109
106
  }
110
107
  }).tooltip('name*value*percent', (name, value, percent) => {
111
108
  let title = name;
@@ -127,25 +124,26 @@ class Ring extends ChartComponent {
127
124
  stroke: 0
128
125
  }
129
126
  }
127
+ }).shape('withGap').style('value', value => {
128
+ return {
129
+ lineWidth: 3,
130
+ stroke: '#fff'
131
+ };
130
132
  });
131
- this.chart.tooltip({
132
- showTitle: false,
133
- showMarkers: false,
134
- itemTpl: '<div class="g2-tooltip-list-item"><span style="background-color:{color};" class="g2-tooltip-marker"></span>{name}: {value}</div>'
135
- });
136
- this.chart.interaction('chart-active');
133
+ this.setToolTipForTheta();
137
134
  this.setAnnotation({
138
135
  name: intl.get('Total'),
139
136
  value: BaseUtils.getSummaryValueDisplayString(summaryColumn, this.sum, summary_method)
140
- }, '#000000');
141
- this.chart.on('interval:mouseenter', evt => this.onEnterRingChart(evt, '#000000', {
137
+ });
138
+ this.chart.on('interval:mouseenter', evt => this.onEnterRingChart(evt, {
142
139
  summaryColumn,
143
140
  summaryMethod: summary_method
144
141
  }));
145
- this.chart.on('interval:mouseleave', evt => this.onLeaveRingChart(evt, '#000000', {
142
+ this.chart.on('interval:mouseleave', evt => this.onLeaveRingChart(evt, {
146
143
  summaryColumn,
147
144
  summaryMethod: summary_method
148
145
  }));
146
+ this.chart.interaction('element-highlight');
149
147
  isFunction(customRender) && customRender(this.chart);
150
148
  this.chart.render();
151
149
  };
@@ -154,7 +152,7 @@ class Ring extends ChartComponent {
154
152
  display_label,
155
153
  label_format
156
154
  } = this.props.chart.config;
157
- if (!display_label) {
155
+ if (!display_label || !value) {
158
156
  return '';
159
157
  }
160
158
  switch (label_format) {
@@ -168,11 +166,12 @@ class Ring extends ChartComponent {
168
166
  return percent;
169
167
  }
170
168
  };
171
- this.setAnnotation = function (content, fontColor) {
169
+ this.setAnnotation = function (content) {
172
170
  let {
173
171
  summaryColumn,
174
172
  summaryMethod
175
- } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
173
+ } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
174
+ let theme = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : CHART_THEME_COLOR['light'];
176
175
  let name = content.name;
177
176
  if (!name || name === 'undefined') {
178
177
  name = intl.get('Empty');
@@ -183,46 +182,92 @@ class Ring extends ChartComponent {
183
182
  content: name,
184
183
  position: ['50%', '50%'],
185
184
  style: {
186
- fontSize: 12,
187
- fill: fontColor,
185
+ fontSize: theme.annotationFontSize,
186
+ fill: theme.annotationTitleFontColor,
188
187
  fontWeight: '300',
189
188
  textAlign: 'center'
190
189
  },
191
- offsetY: -20
190
+ offsetY: -10
192
191
  }).text({
193
192
  position: ['50%', '50%'],
194
193
  content: summaryColumn ? BaseUtils.getSummaryValueDisplayString(summaryColumn, content.value, summaryMethod) : content.value,
195
194
  style: {
196
- fontSize: 20,
197
- fill: fontColor,
195
+ fontSize: theme.annotationFontSize,
196
+ fill: theme.annotationValueFontColor,
198
197
  textAlign: 'center'
199
198
  },
200
199
  offsetY: 20
201
200
  });
202
201
  };
203
- this.onEnterRingChart = (ev, color, _ref) => {
202
+ this.onEnterRingChart = (evt, _ref) => {
204
203
  let {
205
204
  summaryColumn,
206
205
  summaryMethod
207
206
  } = _ref;
208
- const item = ev.data.data;
207
+ const {
208
+ label_font_size
209
+ } = this.props.chart.config;
210
+ const {
211
+ theme
212
+ } = this.props;
213
+ const item = evt.data.data;
209
214
  this.chart.annotation().clear(true);
210
- this.setAnnotation(item, color, {
215
+ this.currentChart.label('name*value*percent', (name, value, percent) => {
216
+ let displayValue;
217
+ if (item.name === name) {
218
+ displayValue = BaseUtils.getSummaryValueDisplayString(summaryColumn, value, summaryMethod);
219
+ } else {
220
+ displayValue = '';
221
+ }
222
+ return {
223
+ content: this.getLabel(displayValue, percent)
224
+ };
225
+ }, {
226
+ offset: this.isInnerLabel ? this.innerLabelOffset : this.outerLabelOffset,
227
+ style: {
228
+ fill: theme.ringLabelColor,
229
+ fontSize: BaseUtils.getLabelFontSize(label_font_size),
230
+ stroke: '#fff',
231
+ lineWidth: 1
232
+ }
233
+ });
234
+ this.setAnnotation(item, {
211
235
  summaryColumn,
212
236
  summaryMethod
213
237
  });
214
238
  this.chart.render(true);
215
239
  };
216
- this.onLeaveRingChart = (ev, color, _ref2) => {
240
+ this.onLeaveRingChart = (evt, _ref2) => {
217
241
  let {
218
242
  summaryColumn,
219
243
  summaryMethod
220
244
  } = _ref2;
245
+ const {
246
+ summary_method,
247
+ label_font_size
248
+ } = this.props.chart.config;
249
+ const {
250
+ theme
251
+ } = this.props;
221
252
  this.chart.annotation().clear(true);
253
+ this.currentChart.label('value*percent', (value, percent) => {
254
+ let displayValue = BaseUtils.getSummaryValueDisplayString(summaryColumn, value, summary_method);
255
+ return {
256
+ content: this.getLabel(displayValue, percent)
257
+ };
258
+ }, {
259
+ offset: this.isInnerLabel ? this.innerLabelOffset : this.outerLabelOffset,
260
+ style: {
261
+ fill: theme.ringLabelColor,
262
+ fontSize: BaseUtils.getLabelFontSize(label_font_size),
263
+ stroke: '#fff',
264
+ lineWidth: 1
265
+ }
266
+ });
222
267
  this.setAnnotation({
223
268
  name: intl.get('Total'),
224
269
  value: this.sum
225
- }, color, {
270
+ }, {
226
271
  summaryColumn,
227
272
  summaryMethod
228
273
  });
@@ -248,7 +293,7 @@ class Ring extends ChartComponent {
248
293
  }
249
294
  render() {
250
295
  return /*#__PURE__*/React.createElement("div", {
251
- className: "sea-chart-container ",
296
+ className: "sea-chart-container",
252
297
  ref: ref => this.container = ref
253
298
  });
254
299
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sea-chart",
3
- "version": "0.0.36",
3
+ "version": "0.0.38",
4
4
  "main": "./dist/index.js",
5
5
  "dependencies": {
6
6
  "@antv/data-set": "0.11.8",