evui 3.3.8 → 3.3.11

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 (32) hide show
  1. package/dist/evui.common.js +2982 -792
  2. package/dist/evui.common.js.map +1 -1
  3. package/dist/evui.umd.js +2982 -792
  4. package/dist/evui.umd.js.map +1 -1
  5. package/dist/evui.umd.min.js +1 -1
  6. package/dist/evui.umd.min.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/components/chart/Chart.vue +15 -6
  9. package/src/components/chart/chart.core.js +86 -31
  10. package/src/components/chart/element/element.bar.js +9 -3
  11. package/src/components/chart/element/element.heatmap.js +213 -0
  12. package/src/components/chart/element/element.pie.js +75 -5
  13. package/src/components/chart/element/element.scatter.js +26 -9
  14. package/src/components/chart/element/element.tip.js +158 -13
  15. package/src/components/chart/helpers/helpers.constant.js +22 -0
  16. package/src/components/chart/model/model.series.js +4 -0
  17. package/src/components/chart/model/model.store.js +166 -2
  18. package/src/components/chart/plugins/plugins.interaction.js +78 -10
  19. package/src/components/chart/plugins/plugins.legend.js +213 -42
  20. package/src/components/chart/plugins/plugins.pie.js +2 -0
  21. package/src/components/chart/plugins/plugins.tooltip.js +249 -6
  22. package/src/components/chart/scale/scale.js +20 -2
  23. package/src/components/chart/scale/scale.step.js +20 -5
  24. package/src/components/chart/scale/scale.time.category.js +20 -2
  25. package/src/components/chart/uses.js +20 -3
  26. package/src/components/grid/Grid.vue +276 -132
  27. package/src/components/grid/grid.filter.window.vue +1 -0
  28. package/src/components/grid/grid.pagination.vue +75 -0
  29. package/src/components/grid/grid.summary.vue +221 -0
  30. package/src/components/grid/style/grid.scss +0 -14
  31. package/src/components/grid/uses.js +157 -78
  32. package/src/components/pagination/Pagination.vue +20 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evui",
3
- "version": "3.3.8",
3
+ "version": "3.3.11",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -20,6 +20,10 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
20
20
  type: Object,
21
21
  default: null,
22
22
  },
23
+ selectedLabel: {
24
+ type: Object,
25
+ default: null,
26
+ },
23
27
  options: {
24
28
  type: Object,
25
29
  default: () => ({}),
@@ -38,6 +42,7 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
38
42
  'dbl-click',
39
43
  'drag-select',
40
44
  'update:selectedItem',
45
+ 'update:selectedLabel',
41
46
  ],
42
47
  setup(props) {
43
48
  let evChart = {};
@@ -45,7 +50,8 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
45
50
 
46
51
  const {
47
52
  eventListeners,
48
- selectInfo,
53
+ selectItemInfo,
54
+ selectLabelInfo,
49
55
  getNormalizedData,
50
56
  getNormalizedOptions,
51
57
  } = useModel();
@@ -66,7 +72,8 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
66
72
  normalizedData,
67
73
  normalizedOptions,
68
74
  eventListeners,
69
- selectInfo,
75
+ selectItemInfo,
76
+ selectLabelInfo,
70
77
  );
71
78
  };
72
79
 
@@ -102,13 +109,15 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
102
109
  }, { deep: true });
103
110
 
104
111
  await watch(() => props.selectedItem, (newValue) => {
105
- if (!newValue?.seriesID) {
106
- return;
107
- }
108
-
109
112
  const chartType = props.options?.type;
110
113
  evChart.selectItemByData(newValue, chartType);
111
114
  }, { deep: true });
115
+
116
+ await watch(() => props.selectedLabel, (newValue) => {
117
+ if (newValue.dataIndex) {
118
+ evChart.renderWithSelectLabel(newValue.dataIndex);
119
+ }
120
+ }, { deep: true });
112
121
  });
113
122
 
114
123
  onBeforeUnmount(() => {
@@ -14,7 +14,7 @@ import Pie from './plugins/plugins.pie';
14
14
  import Tip from './element/element.tip';
15
15
 
16
16
  class EvChart {
17
- constructor(target, data, options, listeners, defaultSelectInfo) {
17
+ constructor(target, data, options, listeners, defaultSelectItemInfo, defaultSelectLabelInfo) {
18
18
  Object.keys(Model).forEach(key => Object.assign(this, Model[key]));
19
19
  Object.assign(this, Title);
20
20
  Object.assign(this, Legend);
@@ -61,11 +61,12 @@ class EvChart {
61
61
  this.seriesList = {};
62
62
  this.lastTip = { pos: null, value: null };
63
63
  this.seriesInfo = {
64
- charts: { pie: [], bar: [], line: [], scatter: [] },
64
+ charts: { pie: [], bar: [], line: [], scatter: [], heatMap: [] },
65
65
  count: 0,
66
66
  };
67
67
 
68
- this.defaultSelectInfo = defaultSelectInfo;
68
+ this.defaultSelectItemInfo = defaultSelectItemInfo;
69
+ this.defaultSelectLabelInfo = defaultSelectLabelInfo;
69
70
  }
70
71
 
71
72
  /**
@@ -92,6 +93,7 @@ class EvChart {
92
93
 
93
94
  this.axesRange = this.getAxesRange();
94
95
  this.labelOffset = this.getLabelOffset();
96
+ this.initSelectedLabelInfo();
95
97
 
96
98
  this.drawChart();
97
99
 
@@ -146,11 +148,12 @@ class EvChart {
146
148
 
147
149
  /**
148
150
  * Draw each series
151
+ * @param {any} [hitInfo=undefined] from mousemove callback (object or undefined)
149
152
  *
150
153
  * @returns {undefined}
151
154
  */
152
155
  drawSeries(hitInfo) {
153
- const maxTip = this.options.maxTip;
156
+ const { maxTip, selectLabel, selectItem } = this.options;
154
157
 
155
158
  const opt = {
156
159
  ctx: this.bufferCtx,
@@ -158,6 +161,7 @@ class EvChart {
158
161
  labelOffset: this.labelOffset,
159
162
  axesSteps: this.axesSteps,
160
163
  maxTipOpt: { background: maxTip.background, color: maxTip.color },
164
+ selectLabel: { option: selectLabel, selected: this.defaultSelectLabelInfo },
161
165
  };
162
166
 
163
167
  let showIndex = 0;
@@ -177,37 +181,76 @@ class EvChart {
177
181
  for (let jx = 0; jx < chartTypeSet.length; jx++) {
178
182
  const series = this.seriesList[chartTypeSet[jx]];
179
183
 
180
- if (chartType === 'line' || chartType === 'scatter') {
181
- series.draw(opt);
182
- } else if (chartType === 'bar') {
183
- const { thickness, borderRadius } = this.options;
184
- series.draw({ thickness, borderRadius, showSeriesCount, showIndex, ...opt });
185
-
186
- if (series.show) {
187
- showIndex++;
184
+ switch (chartType) {
185
+ case 'line':
186
+ case 'heatMap': {
187
+ series.draw(opt);
188
+ break;
188
189
  }
189
- } else {
190
- const selectInfo = hitInfo
191
- ?? this.lastHitInfo
192
- ?? { sId: this.defaultSelectInfo?.seriesID };
193
-
194
- if (this.options.sunburst) {
195
- this.drawSunburst(selectInfo);
196
- } else {
197
- this.drawPie(selectInfo);
190
+ case 'bar': {
191
+ const { thickness, borderRadius } = this.options;
192
+ series.draw({ thickness, borderRadius, showSeriesCount, showIndex, ...opt });
193
+ if (series.show) {
194
+ showIndex++;
195
+ }
196
+ break;
198
197
  }
199
-
200
- if (this.options.doughnutHoleSize > 0) {
201
- this.drawDoughnutHole();
198
+ case 'pie': {
199
+ const selectInfo = hitInfo
200
+ ?? this.lastHitInfo
201
+ ?? { sId: this.defaultSelectItemInfo?.seriesID };
202
+
203
+ if (this.options.sunburst) {
204
+ this.drawSunburst(selectInfo);
205
+ } else {
206
+ this.drawPie(selectInfo);
207
+ }
208
+
209
+ if (this.options.doughnutHoleSize > 0) {
210
+ this.drawDoughnutHole();
211
+ }
212
+ break;
213
+ }
214
+ case 'scatter': {
215
+ if (selectItem.use && selectItem.useSeriesOpacity) {
216
+ if (hitInfo) {
217
+ if (hitInfo?.maxIndex || hitInfo?.maxIndex === 0) {
218
+ opt.selectInfo = {
219
+ seriesID: hitInfo.sId,
220
+ dataIndex: hitInfo.maxIndex,
221
+ };
222
+ } else {
223
+ opt.selectInfo = null;
224
+ }
225
+ } else if (this.lastHitInfo?.maxIndex || this.lastHitInfo?.maxIndex === 0) {
226
+ opt.selectInfo = {
227
+ seriesID: this.lastHitInfo.sId,
228
+ dataIndex: this.lastHitInfo.maxIndex,
229
+ };
230
+ } else if (this.defaultSelectItemInfo?.dataIndex
231
+ || this.defaultSelectItemInfo?.dataIndex === 0) {
232
+ opt.selectInfo = {
233
+ seriesID: this.defaultSelectItemInfo.seriesID,
234
+ dataIndex: this.defaultSelectItemInfo.dataIndex,
235
+ };
236
+ } else {
237
+ opt.selectInfo = null;
238
+ }
239
+ }
240
+
241
+ series.draw(opt);
242
+ break;
243
+ }
244
+ default: {
245
+ break;
202
246
  }
203
- break;
204
247
  }
205
248
  }
206
249
  }
207
250
  }
208
251
 
209
252
  /**
210
- * Draw Tip with hitInfo and defaultSelectInfo
253
+ * Draw Tip with hitInfo and defaultSelectItemInfo
211
254
  * @param hitInfo
212
255
  */
213
256
  drawTip(hitInfo) {
@@ -221,8 +264,8 @@ class EvChart {
221
264
  tipLocationInfo = hitInfo;
222
265
  } else if (this.lastHitInfo) {
223
266
  tipLocationInfo = this.lastHitInfo;
224
- } else if (this.defaultSelectInfo) {
225
- tipLocationInfo = this.getItem(this.defaultSelectInfo, false);
267
+ } else if (this.defaultSelectItemInfo) {
268
+ tipLocationInfo = this.getItem(this.defaultSelectItemInfo, false);
226
269
  } else {
227
270
  tipLocationInfo = null;
228
271
  }
@@ -281,11 +324,21 @@ class EvChart {
281
324
  */
282
325
  drawAxis(hitInfo) {
283
326
  this.axesX.forEach((axis, index) => {
284
- axis.draw(this.chartRect, this.labelOffset, this.axesSteps.x[index], hitInfo);
327
+ axis.draw(
328
+ this.chartRect,
329
+ this.labelOffset,
330
+ this.axesSteps.x[index],
331
+ hitInfo,
332
+ this.defaultSelectLabelInfo);
285
333
  });
286
334
 
287
335
  this.axesY.forEach((axis, index) => {
288
- axis.draw(this.chartRect, this.labelOffset, this.axesSteps.y[index]);
336
+ axis.draw(
337
+ this.chartRect,
338
+ this.labelOffset,
339
+ this.axesSteps.y[index],
340
+ hitInfo,
341
+ this.defaultSelectLabelInfo);
289
342
  });
290
343
  }
291
344
 
@@ -545,6 +598,7 @@ class EvChart {
545
598
  bar: [],
546
599
  line: [],
547
600
  scatter: [],
601
+ heatMap: [],
548
602
  },
549
603
  count: 0,
550
604
  };
@@ -604,8 +658,9 @@ class EvChart {
604
658
  this.axesY = this.createAxes('y', options.axesY);
605
659
  this.axesRange = this.getAxesRange();
606
660
  this.labelOffset = this.getLabelOffset();
661
+ this.initSelectedLabelInfo();
607
662
 
608
- this.render();
663
+ this.render(updateInfo?.hitInfo);
609
664
 
610
665
  const isDragMove = this.dragInfo && this.drawSelectionArea;
611
666
  if (isDragMove) {
@@ -131,7 +131,13 @@ class Bar {
131
131
  }
132
132
 
133
133
  const barColor = item.dataColor || this.color;
134
- const isDownplay = this.state === 'downplay';
134
+
135
+ const selectLabelOption = param.selectLabel.option;
136
+ const selectedLabel = param.selectLabel.selected ?? { dataIndex: [] };
137
+
138
+ const isDownplay = selectLabelOption.use && selectLabelOption.useSeriesOpacity
139
+ ? selectedLabel.dataIndex.length && !selectedLabel.dataIndex.includes(index)
140
+ : this.state === 'downplay';
135
141
 
136
142
  if (typeof barColor !== 'string') {
137
143
  ctx.fillStyle = Canvas.createGradient(
@@ -332,7 +338,7 @@ class Bar {
332
338
  */
333
339
  drawValueLabels({ context, data, positions, isHighlight }) {
334
340
  const isHorizontal = this.isHorizontal;
335
- const { fontSize, textColor, align, formatter } = this.showValue;
341
+ const { fontSize, textColor, align, formatter, decimalPoint } = this.showValue;
336
342
  const { x, y, w, h } = positions;
337
343
  const ctx = context;
338
344
 
@@ -361,7 +367,7 @@ class Bar {
361
367
  }
362
368
 
363
369
  if (!formatter || typeof formattedTxt !== 'string') {
364
- formattedTxt = Util.labelSignFormat(value);
370
+ formattedTxt = Util.labelSignFormat(value, decimalPoint);
365
371
  }
366
372
 
367
373
  const vw = Math.round(ctx.measureText(formattedTxt).width);
@@ -0,0 +1,213 @@
1
+ import { merge } from 'lodash-es';
2
+ import Canvas from '../helpers/helpers.canvas';
3
+ import Util from '../helpers/helpers.util';
4
+ import { COLOR, HEAT_MAP_OPTION } from '../helpers/helpers.constant';
5
+
6
+ class HeatMap {
7
+ constructor(sId, opt, sIdx) {
8
+ const merged = merge({}, HEAT_MAP_OPTION, opt);
9
+ Object.keys(merged).forEach((key) => {
10
+ this[key] = merged[key];
11
+ });
12
+
13
+ ['color', 'pointFill', 'fillColor'].forEach((colorProp) => {
14
+ if (this[colorProp] === undefined) {
15
+ this[colorProp] = COLOR[sIdx];
16
+ }
17
+ });
18
+
19
+ this.colorAxis = this.createColorAxis(opt.colorOpt);
20
+ this.errorColor = opt.colorOpt.error;
21
+ this.borderColor = opt.colorOpt.border;
22
+
23
+ this.sId = sId;
24
+ this.data = [];
25
+ this.spaces = opt.spaces || { x: null, y: null };
26
+ this.size = {
27
+ w: 0,
28
+ h: 0,
29
+ };
30
+ this.valueOpt = {
31
+ max: 0,
32
+ interval: 0,
33
+ };
34
+ this.type = 'heatMap';
35
+ }
36
+
37
+ /**
38
+ * create series color axis
39
+ * @param colorOpt
40
+ * @returns {*[]}
41
+ */
42
+ createColorAxis(colorOpt) {
43
+ const colorAxis = [];
44
+ const { min, max, categoryCnt } = colorOpt;
45
+
46
+ const minColor = min.includes('#') ? Util.hexToRgb(min) : min;
47
+ const maxColor = max.includes('#') ? Util.hexToRgb(max) : max;
48
+
49
+ const [minR, minG, minB] = minColor.split(',');
50
+ const [maxR, maxG, maxB] = maxColor.split(',');
51
+
52
+ const unitR = Math.floor((minR - maxR) / (categoryCnt - 1));
53
+ const unitG = Math.floor((minG - maxG) / (categoryCnt - 1));
54
+ const unitB = Math.floor((minB - maxB) / (categoryCnt - 1));
55
+
56
+ for (let ix = 0; ix < categoryCnt; ix++) {
57
+ const r = +minR - (unitR * ix);
58
+ const g = +minG - (unitG * ix);
59
+ const b = +minB - (unitB * ix);
60
+
61
+ colorAxis.push({
62
+ id: `color#${ix}`,
63
+ value: `rgb(${r},${g},${b})`,
64
+ state: 'normal',
65
+ show: true,
66
+ });
67
+ }
68
+
69
+ return colorAxis;
70
+ }
71
+
72
+ getColorIndex(value) {
73
+ const existError = this.valueOpt.existError;
74
+ const maxIndex = this.colorAxis.length;
75
+ if (value < 0) {
76
+ return maxIndex - 1;
77
+ }
78
+
79
+ const colorIndex = Math.floor(value / this.valueOpt.interval);
80
+ if (colorIndex >= maxIndex) {
81
+ return existError ? maxIndex - 2 : maxIndex - 1;
82
+ }
83
+
84
+ return colorIndex;
85
+ }
86
+
87
+ drawItem(ctx, xp, yp) {
88
+ ctx.beginPath();
89
+ ctx.strokeRect(xp - this.size.w, yp + Math.SQRT2, this.size.w, this.size.h);
90
+ ctx.fillRect(xp - this.size.w, yp + Math.SQRT2, this.size.w, this.size.h);
91
+ ctx.closePath();
92
+ ctx.stroke();
93
+ }
94
+
95
+ draw(param) {
96
+ if (!this.show) {
97
+ return;
98
+ }
99
+
100
+ const { ctx, chartRect, labelOffset, axesSteps } = param;
101
+
102
+ const minmaxX = axesSteps.x[this.xAxisIndex];
103
+ const minmaxY = axesSteps.y[this.yAxisIndex];
104
+
105
+ const xArea = chartRect.chartWidth - (labelOffset.left + labelOffset.right);
106
+ const yArea = chartRect.chartHeight - (labelOffset.top + labelOffset.bottom);
107
+
108
+ const xsp = chartRect.x1 + labelOffset.left;
109
+ const ysp = chartRect.y2 - labelOffset.bottom;
110
+
111
+ this.size.w = Math.floor(xArea / (this.spaces.x || (minmaxX.graphMax - minmaxX.graphMin)));
112
+ this.size.h = Math.floor(yArea / (this.spaces.y || (minmaxY.graphMax - minmaxY.graphMin)));
113
+
114
+ this.data.forEach((item) => {
115
+ item.xp = Canvas.calculateX(item.x, minmaxX.graphMin, minmaxX.graphMax, xArea, xsp);
116
+ item.yp = Canvas.calculateY(item.y, minmaxY.graphMin, minmaxY.graphMax, yArea, ysp);
117
+
118
+ const { xp, yp, o: value } = item;
119
+
120
+ if (xp !== null && yp !== null) {
121
+ const colorIndex = this.getColorIndex(value);
122
+ const opacity = this.colorAxis[colorIndex].state === 'downplay' ? 0.1 : 1;
123
+ item.dataColor = value < 0 ? this.errorColor : this.colorAxis[colorIndex].value;
124
+ item.cId = this.colorAxis[colorIndex].id;
125
+ if (this.colorAxis[colorIndex].show) {
126
+ ctx.strokeStyle = Util.colorStringToRgba(this.borderColor, opacity);
127
+ ctx.fillStyle = Util.colorStringToRgba(item.dataColor, opacity);
128
+ this.drawItem(ctx, xp, yp);
129
+ }
130
+ }
131
+ });
132
+ }
133
+
134
+ /**
135
+ *Returns items in range
136
+ * @param {object} params range values
137
+ *
138
+ * @returns {array}
139
+ */
140
+ findItems({ xsp, ysp, width, height }) {
141
+ const gdata = this.data;
142
+ const xep = xsp + width;
143
+ const yep = ysp + height;
144
+ return gdata.filter(seriesData =>
145
+ (xsp - 1 <= seriesData.xp && seriesData.xp <= xep + 1
146
+ && ysp - 1 <= seriesData.yp && seriesData.yp <= yep + 1));
147
+ }
148
+
149
+ /**
150
+ * Draw item highlight
151
+ * @param {object} item object for drawing series data
152
+ * @param {object} context canvas context
153
+ *
154
+ * @returns {undefined}
155
+ */
156
+ itemHighlight(item, context) {
157
+ const gdata = item.data;
158
+ const ctx = context;
159
+
160
+ const x = gdata.xp;
161
+ const y = gdata.yp;
162
+
163
+ ctx.save();
164
+ if (x !== null && y !== null) {
165
+ const color = gdata.dataColor;
166
+ ctx.strokeStyle = Util.colorStringToRgba(color, 1);
167
+ ctx.fillStyle = Util.colorStringToRgba(color, this.highlight.maxShadowOpacity);
168
+ this.drawItem(ctx, x, y);
169
+ }
170
+
171
+ ctx.restore();
172
+ }
173
+
174
+ /**
175
+ * Find graph item for tooltip
176
+ * @param {array} offset mouse position
177
+ *
178
+ * @returns {object} graph item
179
+ */
180
+ findGraphData(offset) {
181
+ const xp = offset[0];
182
+ const yp = offset[1];
183
+ const item = {
184
+ data: null,
185
+ hit: false,
186
+ color: null,
187
+ name: null,
188
+ };
189
+ const wSize = this.size.w;
190
+ const hSize = this.size.h;
191
+ const gdata = this.data;
192
+
193
+ const foundItem = gdata.find((data) => {
194
+ const x = data.xp;
195
+ const y = data.yp;
196
+
197
+ return (x - wSize <= xp)
198
+ && (xp <= x)
199
+ && (y <= yp)
200
+ && (yp <= y + hSize);
201
+ });
202
+
203
+ if (foundItem) {
204
+ item.data = foundItem;
205
+ item.color = foundItem.dataColor;
206
+ item.hit = true;
207
+ }
208
+
209
+ return item;
210
+ }
211
+ }
212
+
213
+ export default HeatMap;
@@ -24,9 +24,9 @@ class Pie {
24
24
  this.centerX = 0;
25
25
  this.centerY = 0;
26
26
  this.radius = 0;
27
+ this.doughnutHoleSize = 0;
27
28
  this.startAngle = 0;
28
29
  this.endAngle = 0;
29
- this.slice = null;
30
30
  this.state = null;
31
31
  this.ctx = null;
32
32
  this.isSelect = false;
@@ -45,6 +45,7 @@ class Pie {
45
45
  const slice = new Path2D();
46
46
 
47
47
  const radius = this.isSelect ? this.radius + 5 : this.radius;
48
+ const doughnutHoleRadius = this.radius * this.doughnutHoleSize;
48
49
 
49
50
  const color = this.color;
50
51
  const noneDownplayOpacity = color.includes('rgba') ? Util.getOpacity(color) : 1;
@@ -64,9 +65,11 @@ class Pie {
64
65
  ctx.stroke(slice);
65
66
  }
66
67
 
67
- ctx.closePath();
68
+ if (this.showValue?.use) {
69
+ this.drawValueLabels(ctx, doughnutHoleRadius);
70
+ }
68
71
 
69
- this.slice = slice;
72
+ ctx.closePath();
70
73
  this.ctx = ctx;
71
74
  }
72
75
 
@@ -78,8 +81,19 @@ class Pie {
78
81
  */
79
82
  findGraphData([offsetX, offsetY]) {
80
83
  const item = { data: null, hit: false, color: null, index: -1 };
81
-
82
- if (this.show && this.ctx?.isPointInPath(this.slice, offsetX, offsetY)) {
84
+ const {
85
+ radius,
86
+ startAngle,
87
+ endAngle,
88
+ centerX,
89
+ centerY,
90
+ } = this;
91
+
92
+ const distance = Math.sqrt((offsetX - centerX) ** 2 + (offsetY - centerY) ** 2);
93
+ const radian = (2.5 * Math.PI) - Math.atan2((offsetX - centerX), (offsetY - centerY));
94
+ const isPointInPath = radius > distance && radian >= startAngle && radian <= endAngle;
95
+
96
+ if (this.show && isPointInPath) {
83
97
  item.type = this.type;
84
98
  item.data = this.data;
85
99
  item.hit = true;
@@ -101,6 +115,7 @@ class Pie {
101
115
  itemHighlight(item, context) {
102
116
  const ctx = context;
103
117
  const radius = this.isSelect ? this.radius + 5 : this.radius;
118
+ const doughnutHoleRadius = this.radius * this.doughnutHoleSize;
104
119
 
105
120
  ctx.save();
106
121
  ctx.shadowOffsetX = 0;
@@ -116,7 +131,62 @@ class Pie {
116
131
  ctx.arc(this.centerX, this.centerY, radius, this.startAngle, this.endAngle);
117
132
  ctx.lineTo(this.centerX, this.centerY);
118
133
  ctx.fill();
134
+
135
+ if (this.showValue?.use) {
136
+ this.drawValueLabels(ctx, doughnutHoleRadius);
137
+ }
138
+
119
139
  ctx.closePath();
140
+ ctx.restore();
141
+ }
142
+
143
+ /**
144
+ * Draw value label if series 'use' of showValue option is true
145
+ *
146
+ * @param context canvas context
147
+ */
148
+ drawValueLabels(context) {
149
+ const { fontSize, textColor, formatter } = this.showValue;
150
+ const ctx = context;
151
+
152
+ ctx.save();
153
+ ctx.beginPath();
154
+
155
+ ctx.font = `normal normal normal ${fontSize}px Roboto`;
156
+ ctx.fillStyle = textColor;
157
+ ctx.lineWidth = 1;
158
+ ctx.textAlign = 'center';
159
+ ctx.textBaseline = 'middle';
160
+
161
+ const value = this.data.o;
162
+
163
+ let formattedTxt;
164
+ if (formatter) {
165
+ formattedTxt = formatter(value);
166
+ }
167
+
168
+ if (!formatter || typeof formattedTxt !== 'string') {
169
+ formattedTxt = Util.labelSignFormat(value);
170
+ }
171
+
172
+ const ratio = 1.8;
173
+ const radius = this.radius - this.doughnutHoleSize;
174
+ const innerAngle = ((this.endAngle - this.startAngle) * 180) / Math.PI;
175
+ const valueHeight = fontSize + 4;
176
+ const valueWidth = Math.round(ctx.measureText(formattedTxt).width);
177
+
178
+ if (innerAngle >= valueWidth * ratio
179
+ && innerAngle >= valueHeight * ratio
180
+ && radius >= valueWidth * ratio
181
+ && radius >= valueHeight * ratio
182
+ ) {
183
+ const halfRadius = (radius / 2) + this.doughnutHoleSize;
184
+ const centerAngle = ((this.endAngle - this.startAngle) / 2) + this.startAngle;
185
+ const xPos = halfRadius * Math.cos(centerAngle) + this.centerX;
186
+ const yPos = halfRadius * Math.sin(centerAngle) + this.centerY;
187
+
188
+ ctx.fillText(formattedTxt, xPos, yPos);
189
+ }
120
190
 
121
191
  ctx.restore();
122
192
  }