evui 3.3.4 → 3.3.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evui",
3
- "version": "3.3.4",
3
+ "version": "3.3.7",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -193,7 +193,7 @@ class EvChart {
193
193
  }
194
194
 
195
195
  if (this.options.doughnutHoleSize > 0) {
196
- this.drawDoughnutHole(series);
196
+ this.drawDoughnutHole();
197
197
  }
198
198
  break;
199
199
  }
@@ -555,7 +555,6 @@ class EvChart {
555
555
  if (!updateSelTip.keepDomain) {
556
556
  this.lastTip.pos = null;
557
557
  this.lastHitInfo = null;
558
- this.defaultSelectInfo = null;
559
558
  }
560
559
  }
561
560
 
@@ -713,6 +712,8 @@ class EvChart {
713
712
  this.overlayCanvas.removeEventListener('mouseleave', this.onMouseLeave);
714
713
  this.overlayCanvas.removeEventListener('dblclick', this.onDblClick);
715
714
  this.overlayCanvas.removeEventListener('click', this.onClick);
715
+ this.overlayCanvas.removeEventListener('mousedown', this.onMouseDown);
716
+ this.overlayCanvas.removeEventListener('wheel', this.onWheel);
716
717
  }
717
718
 
718
719
  if (this.options.tooltip.use) {
@@ -177,7 +177,7 @@ class Bar {
177
177
  /**
178
178
  * Draw item highlight
179
179
  * @param {object} item object for drawing series data
180
- * @param {object} context canvas context
180
+ * @param {CanvasRenderingContext2D} context canvas context
181
181
  *
182
182
  * @returns {undefined}
183
183
  */
@@ -20,31 +20,36 @@ class Pie {
20
20
  this.sId = sId;
21
21
  this.data = [];
22
22
  this.type = 'pie';
23
+ this.centerX = 0;
24
+ this.centerY = 0;
25
+ this.radius = 0;
26
+ this.startAngle = 0;
27
+ this.endAngle = 0;
28
+ this.slice = null;
29
+ this.state = null;
30
+ this.ctx = null;
23
31
  }
24
32
 
25
33
  /**
26
34
  * Draw series data
27
- * @param {object} param object for drawing series data
35
+ * @param context
28
36
  *
29
37
  * @returns {undefined}
30
38
  */
31
- draw(param) {
32
- const ctx = param.ctx;
33
- const centerX = param.centerX;
34
- const centerY = param.centerY;
35
- const radius = param.radius;
36
- const startAngle = param.startAngle;
37
- const endAngle = param.endAngle;
39
+ draw(context) {
40
+ this.ctx = context;
41
+
38
42
  const color = this.color;
39
43
  const noneDownplayOpacity = color.includes('rgba') ? Util.getOpacity(color) : 1;
40
44
  const opacity = this.state === 'downplay' ? 0.1 : noneDownplayOpacity;
41
45
 
42
- ctx.beginPath();
43
- ctx.fillStyle = Util.colorStringToRgba(color, opacity);
44
- ctx.moveTo(centerX, centerY);
45
- ctx.arc(centerX, centerY, radius, startAngle, endAngle);
46
- ctx.fill();
47
- ctx.closePath();
46
+ this.ctx.beginPath();
47
+ this.slice = new Path2D();
48
+ this.slice.moveTo(this.centerX, this.centerY);
49
+ this.slice.arc(this.centerX, this.centerY, this.radius, this.startAngle, this.endAngle);
50
+ this.ctx.fillStyle = Util.colorStringToRgba(color, opacity);
51
+ this.ctx.fill(this.slice);
52
+ this.ctx.closePath();
48
53
  }
49
54
 
50
55
  /**
@@ -53,38 +58,47 @@ class Pie {
53
58
  *
54
59
  * @returns {object} graph item
55
60
  */
56
- findGraphRange(offset) {
57
- const xp = offset[0];
58
- const yp = offset[1];
59
- const item = { data: null, hit: false, color: this.color };
60
- const gdata = this.data;
61
-
62
- let s = 0;
63
- let e = gdata.length - 1;
64
-
65
- while (s <= e) {
66
- const m = Math.floor((s + e) / 2);
67
- const sx = gdata[m].xp;
68
- const sy = gdata[m].yp;
69
- const ex = sx + gdata[m].w;
70
- const ey = sy + gdata[m].h;
71
-
72
- if ((sx - 4 <= xp) && (xp <= ex + 4)) {
73
- item.data = gdata[m];
74
-
75
- if ((ey - 4 <= yp) && (yp <= sy + 4)) {
76
- item.hit = true;
77
- }
78
- return item;
79
- } else if (sx + 4 < xp) {
80
- s = m + 1;
81
- } else {
82
- e = m - 1;
83
- }
61
+ findGraphData([offsetX, offsetY]) {
62
+ const item = { data: null, hit: false, color: null, index: -1 };
63
+
64
+ if (this.ctx?.isPointInPath(this.slice, offsetX, offsetY)) {
65
+ item.data = this.data;
66
+ item.hit = true;
67
+ item.color = this.color;
68
+ item.index = 0;
84
69
  }
85
70
 
86
71
  return item;
87
72
  }
73
+
74
+ /**
75
+ * Draw item highlight
76
+ *
77
+ * @param item {object} object for drawing series data
78
+ * @param context {CanvasRenderingContext2D} canvas context
79
+ *
80
+ * @returns {undefined}
81
+ */
82
+ itemHighlight(item, context) {
83
+ const ctx = context;
84
+
85
+ ctx.save();
86
+ ctx.shadowOffsetX = 0;
87
+ ctx.shadowOffsetY = 0;
88
+ ctx.shadowBlur = 4;
89
+
90
+ const color = item.data.dataColor || this.color;
91
+ ctx.fillStyle = color;
92
+ ctx.shadowColor = color;
93
+
94
+ ctx.beginPath();
95
+ ctx.moveTo(this.centerX, this.centerY);
96
+ ctx.arc(this.centerX, this.centerY, this.radius, this.startAngle, this.endAngle);
97
+ ctx.fill();
98
+ ctx.closePath();
99
+
100
+ ctx.restore();
101
+ }
88
102
  }
89
103
 
90
104
  export default Pie;
@@ -1,4 +1,5 @@
1
1
  import { numberWithComma } from '@/common/utils';
2
+ import dayjs from 'dayjs';
2
3
  import Canvas from '../helpers/helpers.canvas';
3
4
 
4
5
  const modules = {
@@ -12,23 +13,10 @@ const modules = {
12
13
  const opt = this.options;
13
14
  const isHorizontal = !!opt.horizontal;
14
15
  const maxTipOpt = opt.maxTip;
15
- const selectItemOpt = opt.selectItem;
16
+ const selTipOpt = opt.selectItem;
16
17
  let maxArgs;
17
18
 
18
- if (maxTipOpt.use) {
19
- const maxSID = this.minMax[isHorizontal ? 'x' : 'y'][0].maxSID;
20
- maxArgs = this.calculateTipInfo(this.seriesList[maxSID], 'max', null);
21
-
22
- if (maxTipOpt.use && maxArgs) {
23
- this.drawTextTip({ opt: maxTipOpt, tipType: 'max', ...maxArgs });
24
-
25
- if (maxTipOpt.showIndicator) {
26
- this.drawFixedIndicator({ opt: maxTipOpt, ...maxArgs });
27
- }
28
- }
29
- }
30
-
31
- if (selectItemOpt.use && tipLocationInfo) {
19
+ if (selTipOpt.use && tipLocationInfo) {
32
20
  const seriesInfo = this.seriesList[tipLocationInfo?.sId];
33
21
 
34
22
  if (!seriesInfo?.show) {
@@ -41,19 +29,27 @@ const modules = {
41
29
  tipLocationInfo,
42
30
  );
43
31
 
44
- if (selectItemOpt.use && selArgs) {
32
+ if (selArgs) {
45
33
  let isSamePos = false;
46
34
 
47
35
  if (maxTipOpt.use && maxArgs?.dp === selArgs.dp) {
48
36
  isSamePos = true;
49
37
  }
50
38
 
51
- if (selectItemOpt.showTextTip || selectItemOpt.showTip) {
52
- this.drawTextTip({ opt: selectItemOpt, tipType: 'sel', isSamePos, ...selArgs });
39
+ if (selTipOpt.showTextTip || selTipOpt.showTip) {
40
+ if (selTipOpt.tipText === 'label') {
41
+ const axisOpt = isHorizontal ? opt.axesY[0] : opt.axesX[0];
42
+ const label = selArgs.label;
43
+ selArgs.text = axisOpt.type === 'time' ? dayjs(label).format(axisOpt.timeFormat) : label;
44
+ } else {
45
+ selArgs.text = numberWithComma(selArgs.value);
46
+ }
47
+
48
+ this.drawTextTip({ opt: selTipOpt, tipType: 'sel', isSamePos, ...selArgs });
53
49
  }
54
50
 
55
- if (selectItemOpt.showIndicator) {
56
- this.drawFixedIndicator({ opt: selectItemOpt, ...selArgs });
51
+ if (selTipOpt.showIndicator) {
52
+ this.drawFixedIndicator({ opt: selTipOpt, ...selArgs });
57
53
  }
58
54
  }
59
55
 
@@ -61,6 +57,20 @@ const modules = {
61
57
  this.lastHitInfo = tipLocationInfo;
62
58
  }
63
59
  }
60
+
61
+ if (maxTipOpt.use) {
62
+ const maxSID = this.minMax[isHorizontal ? 'x' : 'y'][0].maxSID;
63
+ maxArgs = this.calculateTipInfo(this.seriesList[maxSID], 'max', null);
64
+
65
+ if (maxTipOpt.use && maxArgs) {
66
+ maxArgs.text = numberWithComma(maxArgs.value);
67
+ this.drawTextTip({ opt: maxTipOpt, tipType: 'max', ...maxArgs });
68
+
69
+ if (maxTipOpt.showIndicator) {
70
+ this.drawFixedIndicator({ opt: maxTipOpt, ...maxArgs });
71
+ }
72
+ }
73
+ }
64
74
  },
65
75
 
66
76
  /**
@@ -116,19 +126,24 @@ const modules = {
116
126
  }
117
127
 
118
128
  let value = isHorizontal ? series.minMax.maxX : series.minMax.maxY;
119
-
129
+ let label;
120
130
  if (tipType === 'sel') {
121
131
  if (hitInfo && hitInfo.value !== null) {
122
132
  value = hitInfo.useStack ? hitInfo.acc : hitInfo.value;
133
+ label = hitInfo.label;
123
134
  lastTip.value = value;
135
+ lastTip.label = label;
124
136
  } else if (lastTip.value !== null) {
125
137
  value = lastTip.value;
138
+ label = lastTip.label;
126
139
  } else if (lastTip.pos !== null) {
127
140
  const item = type === 'bar'
128
141
  ? this.getItemByLabelIndex(lastTip.pos) : this.getItemByLabel(lastTip.pos);
129
142
 
130
143
  value = item.useStack ? item.acc : item.value;
144
+ label = item.label;
131
145
  lastTip.value = value;
146
+ lastTip.label = label;
132
147
  }
133
148
  }
134
149
 
@@ -157,7 +172,7 @@ const modules = {
157
172
  }
158
173
 
159
174
  const sizeObj = { xArea, yArea, graphX, graphY, xsp, xep, ysp };
160
- const dataObj = { dp, value, text: numberWithComma(value), type };
175
+ const dataObj = { dp, value, label, type };
161
176
 
162
177
  return { ...sizeObj, ...dataObj };
163
178
  },
@@ -10,19 +10,19 @@ const modules = {
10
10
  */
11
11
  createDataSet(data, label) {
12
12
  Object.keys(this.seriesInfo.charts).forEach((typeKey) => {
13
- const type = this.seriesInfo.charts[typeKey];
13
+ const seriesIDs = this.seriesInfo.charts[typeKey];
14
14
 
15
- if (type.length) {
15
+ if (seriesIDs.length) {
16
16
  if (typeKey === 'pie') {
17
17
  if (this.options.sunburst) {
18
18
  this.createSunburstDataSet(data);
19
19
  } else {
20
- this.createPieDataSet(data, type);
20
+ this.createPieDataSet(data, seriesIDs);
21
21
  }
22
22
  } else if (typeKey === 'scatter') {
23
- type.forEach((sId) => {
24
- const series = this.seriesList[sId];
25
- const sData = data[sId];
23
+ seriesIDs.forEach((seriesID) => {
24
+ const series = this.seriesList[seriesID];
25
+ const sData = data[seriesID];
26
26
 
27
27
  if (series && sData) {
28
28
  series.data = this.addSeriesDSforScatter(sData);
@@ -30,9 +30,9 @@ const modules = {
30
30
  }
31
31
  });
32
32
  } else {
33
- type.forEach((sId) => {
34
- const series = this.seriesList[sId];
35
- const sData = data[sId];
33
+ seriesIDs.forEach((seriesID) => {
34
+ const series = this.seriesList[seriesID];
35
+ const sData = data[seriesID];
36
36
 
37
37
  if (series && sData) {
38
38
  if (series.isExistGrp && series.stackIndex) {
@@ -153,25 +153,22 @@ const modules = {
153
153
 
154
154
  /**
155
155
  * Take chart data and to create normalized pie data
156
- * @param {object} data chart series info
156
+ * @param {object} data chart data
157
+ * @param {String[]} seriesIDs chart series info
157
158
  *
158
159
  * @returns {undefined}
159
160
  */
160
- createPieDataSet(data, pie) {
161
+ createPieDataSet(data, seriesIDs) {
161
162
  this.pieDataSet = [];
162
163
  const ds = this.pieDataSet;
164
+ ds[0] = { data: [], ir: 0, or: 0, total: 0 };
163
165
 
164
- pie.forEach((sId) => {
165
- data[sId].forEach((value, index) => {
166
- if (!ds[index]) {
167
- ds[index] = { data: [], ir: 0, or: 0, total: 0 };
168
- }
169
-
170
- if (this.seriesList[sId].show) {
171
- ds[index].total += value || 0;
172
- ds[index].data.push({ id: sId, value, sa: 0, ea: 0 });
173
- }
174
- });
166
+ seriesIDs.forEach((sId) => {
167
+ if (this.seriesList[sId].show) {
168
+ const value = data[sId][0] ?? 0;
169
+ ds[0].total += value;
170
+ ds[0].data.push({ id: sId, value, sa: 0, ea: 0 });
171
+ }
175
172
  });
176
173
 
177
174
  ds.forEach((item) => {
@@ -160,15 +160,17 @@ const modules = {
160
160
  }
161
161
  };
162
162
 
163
- if (this.options?.tooltip?.useScrollbar) {
164
- this.overlayCanvas.addEventListener('wheel', (e) => {
165
- const isTooltipVisible = this.tooltipDOM.style.display === 'block';
163
+ this.onWheel = (e) => {
164
+ const isTooltipVisible = this.tooltipDOM.style.display === 'block';
166
165
 
167
- if (isTooltipVisible) {
168
- e.preventDefault();
169
- this.tooltipBodyDOM.scrollTop += e.deltaY;
170
- }
171
- });
166
+ if (isTooltipVisible) {
167
+ e.preventDefault();
168
+ this.tooltipBodyDOM.scrollTop += e.deltaY;
169
+ }
170
+ };
171
+
172
+ if (this.options?.tooltip?.useScrollbar) {
173
+ this.overlayCanvas.addEventListener('wheel', this.onWheel, { passive: false });
172
174
  }
173
175
 
174
176
  this.overlayCanvas.addEventListener('mousemove', this.onMouseMove);
@@ -353,7 +355,7 @@ const modules = {
353
355
  if (series.findGraphData) {
354
356
  const item = series.findGraphData(offset, isHorizontal);
355
357
 
356
- if (item.data) {
358
+ if (item?.data) {
357
359
  let gdata;
358
360
 
359
361
  if (item.data.o === null) {
@@ -408,6 +410,7 @@ const modules = {
408
410
  * @returns {boolean}
409
411
  */
410
412
  selectItemByData(targetInfo) {
413
+ this.defaultSelectInfo = targetInfo;
411
414
  const foundInfo = this.getItem(targetInfo, false);
412
415
 
413
416
  if (foundInfo) {
@@ -10,6 +10,7 @@ const modules = {
10
10
  const chartRect = this.chartRect;
11
11
  const pieDataSet = this.pieDataSet;
12
12
  const pieOption = this.options;
13
+ const isDoughnut = !!pieOption.doughnutHoleSize;
13
14
 
14
15
  let slice;
15
16
  let value;
@@ -54,14 +55,15 @@ const modules = {
54
55
  series = this.seriesList[slice.id];
55
56
 
56
57
  if (value) {
57
- series.draw({
58
- ctx,
59
- centerX,
60
- centerY,
61
- radius,
62
- startAngle,
63
- endAngle,
64
- });
58
+ series.type = isDoughnut ? 'doughnut' : 'pie';
59
+ series.centerX = centerX;
60
+ series.centerY = centerY;
61
+ series.radius = radius;
62
+ series.startAngle = startAngle;
63
+ series.endAngle = endAngle;
64
+ series.data = { o: value };
65
+
66
+ series.draw(ctx);
65
67
  startAngle += sliceAngle;
66
68
  }
67
69
  }
@@ -141,14 +143,15 @@ const modules = {
141
143
  series = this.seriesList[slice.id];
142
144
 
143
145
  if (slice.value) {
144
- series.draw({
145
- ctx,
146
- centerX,
147
- centerY,
148
- radius,
149
- startAngle: slice.sa,
150
- endAngle: slice.ea,
151
- });
146
+ series.type = 'sunburst';
147
+ series.centerX = centerX;
148
+ series.centerY = centerY;
149
+ series.radius = radius;
150
+ series.startAngle = slice.sa;
151
+ series.endAngle = slice.ea;
152
+ series.data = { o: slice.value };
153
+
154
+ series.draw(ctx);
152
155
  }
153
156
  }
154
157
  }
@@ -170,11 +173,9 @@ const modules = {
170
173
 
171
174
  /**
172
175
  * Draw doughnut hole
173
- *
174
- * @returns {undefined}
176
+ * @param ctx
175
177
  */
176
- drawDoughnutHole() {
177
- const ctx = this.bufferCtx;
178
+ drawDoughnutHole(ctx = this.bufferCtx) {
178
179
  const pieOption = this.options;
179
180
 
180
181
  const centerX = this.chartRect.width / 2;
@@ -177,9 +177,11 @@ const modules = {
177
177
  const opt = this.options.tooltip;
178
178
 
179
179
  // draw tooltip Title(axis label) and add style class for wrap line about too much long label.
180
- this.tooltipHeaderDOM.textContent = this.options.horizontal
181
- ? this.axesY[hitAxis.y].getLabelFormat(hitItem.y)
182
- : this.axesX[hitAxis.x].getLabelFormat(hitItem.x);
180
+ if (this.axesX.length && this.axesY.length) {
181
+ this.tooltipHeaderDOM.textContent = this.options.horizontal
182
+ ? this.axesY[hitAxis.y].getLabelFormat(hitItem.y)
183
+ : this.axesX[hitAxis.x].getLabelFormat(hitItem.x);
184
+ }
183
185
 
184
186
  if (opt.textOverflow) {
185
187
  this.tooltipHeaderDOM.classList.add(`ev-chart-tooltip-header--${opt.textOverflow}`);
@@ -303,11 +305,18 @@ const modules = {
303
305
  // 3. Draw value
304
306
  let formattedTxt;
305
307
  if (opt.formatter) {
306
- formattedTxt = opt.formatter({
307
- x: this.options.horizontal ? value : hitItem.x,
308
- y: this.options.horizontal ? hitItem.y : value,
309
- name,
310
- });
308
+ if (this.options.type === 'pie') {
309
+ formattedTxt = opt.formatter({
310
+ value,
311
+ name,
312
+ });
313
+ } else {
314
+ formattedTxt = opt.formatter({
315
+ x: this.options.horizontal ? value : hitItem.x,
316
+ y: this.options.horizontal ? hitItem.y : value,
317
+ name,
318
+ });
319
+ }
311
320
  }
312
321
 
313
322
  if (!opt.formatter || typeof formattedTxt !== 'string') {
@@ -349,7 +358,12 @@ const modules = {
349
358
  */
350
359
  drawItemsHighlight(hitInfo, ctx) {
351
360
  Object.keys(hitInfo.items).forEach((sId) => {
352
- this.seriesList[sId].itemHighlight(hitInfo.items[sId], ctx);
361
+ const series = this.seriesList[sId];
362
+ series.itemHighlight(hitInfo.items[sId], ctx);
363
+
364
+ if (series.type === 'sunburst' || series.type === 'doughnut') {
365
+ this.drawDoughnutHole(ctx);
366
+ }
353
367
  });
354
368
  },
355
369
 
@@ -73,6 +73,7 @@ const DEFAULT_OPTIONS = {
73
73
  selectItem: {
74
74
  use: false,
75
75
  showTextTip: false,
76
+ tipText: 'value',
76
77
  showTip: false,
77
78
  showIndicator: false,
78
79
  fixedPosTop: false,