evui 3.3.18 → 3.3.21

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.18",
3
+ "version": "3.3.21",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -8,6 +8,7 @@ import StepScale from './scale/scale.step';
8
8
  import TimeCategoryScale from './scale/scale.time.category';
9
9
  import Title from './plugins/plugins.title';
10
10
  import Legend from './plugins/plugins.legend';
11
+ import GradientLegend from './plugins/plugins.legend.gradient';
11
12
  import Interaction from './plugins/plugins.interaction';
12
13
  import Tooltip from './plugins/plugins.tooltip';
13
14
  import Pie from './plugins/plugins.pie';
@@ -23,6 +24,10 @@ class EvChart {
23
24
  Object.assign(this, Pie);
24
25
  Object.assign(this, Tip);
25
26
 
27
+ if (options.type === 'heatMap' && options.legend.type === 'gradient') {
28
+ Object.assign(this, GradientLegend);
29
+ }
30
+
26
31
  this.target = target;
27
32
  this.data = data;
28
33
  this.options = options;
@@ -163,6 +168,7 @@ class EvChart {
163
168
  maxTipOpt: { background: maxTip.background, color: maxTip.color },
164
169
  selectLabel: { option: selectLabel, selected: this.defaultSelectInfo },
165
170
  selectSeries: { option: selectSeries, selected: this.defaultSelectInfo },
171
+ overlayCtx: this.overlayCtx,
166
172
  };
167
173
 
168
174
  let showIndex = 0;
@@ -443,8 +449,8 @@ class EvChart {
443
449
  const { width, height } = this.getChartDOMRect();
444
450
 
445
451
  const padding = this.options.padding;
446
- const xAxisTitleOpt = this.options.axesX[0]?.title;
447
- const yAxisTitleOpt = this.options.axesY[0]?.title;
452
+ const xAxisTitleOpt = this.options.axesX?.[0]?.title;
453
+ const yAxisTitleOpt = this.options.axesY?.[0]?.title;
448
454
  const titleMargin = 10;
449
455
 
450
456
  let xAxisTitleHeight = 0;
@@ -1,4 +1,5 @@
1
1
  import { defaultsDeep } from 'lodash-es';
2
+ import { truthy } from '@/common/utils';
2
3
  import { COLOR, BAR_OPTION } from '../helpers/helpers.constant';
3
4
  import Canvas from '../helpers/helpers.canvas';
4
5
  import Util from '../helpers/helpers.util';
@@ -359,13 +360,11 @@ class Bar {
359
360
  ctx.textAlign = isHorizontal && align !== 'center' ? 'left' : 'center';
360
361
 
361
362
  let value;
362
- const isStacked = !isNaN(data.o);
363
- if (data.o === null) {
364
- value = isHorizontal ? data.x : data.y;
365
- } else if (isStacked) {
363
+ const isStacked = truthy(this.stackIndex);
364
+ if (isStacked) {
366
365
  value = data.o;
367
366
  } else {
368
- value = '';
367
+ value = (isHorizontal ? data.x : data.y) ?? '';
369
368
  }
370
369
 
371
370
  let formattedTxt;
@@ -374,49 +373,73 @@ class Bar {
374
373
  }
375
374
 
376
375
  if (!formatter || typeof formattedTxt !== 'string') {
377
- formattedTxt = Util.labelSignFormat(value, decimalPoint);
376
+ formattedTxt = Util.labelSignFormat(value, decimalPoint) ?? '';
378
377
  }
379
378
 
380
- const vw = Math.round(ctx.measureText(formattedTxt).width);
381
- const vh = fontSize + 4;
379
+ const textWidth = Math.round(ctx.measureText(formattedTxt).width);
380
+ const textHeight = fontSize + 4;
382
381
  const minXPos = x + 10;
383
382
  const minYPos = y - 10;
383
+ const widthFreeSpaceToDraw = w - 10;
384
+ const heightFreeSpaceToDraw = Math.abs(h + 10);
384
385
  const centerX = x + (w / 2) <= minXPos ? minXPos : x + (w / 2);
385
386
  const centerY = y + (h / 2) >= minYPos ? minYPos : y + (h / 2);
386
387
  const centerYHorizontal = isHighlight ? y + (h / 2) : y - (h / 2);
387
388
 
388
389
  switch (align) {
389
- case 'start':
390
+ case 'start': {
390
391
  if (isHorizontal) {
391
- ctx.fillText(formattedTxt, minXPos, centerYHorizontal);
392
- } else {
392
+ if (textWidth < widthFreeSpaceToDraw) {
393
+ ctx.fillText(formattedTxt, minXPos, centerYHorizontal);
394
+ }
395
+ } else if (textHeight < heightFreeSpaceToDraw) {
393
396
  ctx.fillText(formattedTxt, centerX, minYPos);
394
397
  }
398
+
395
399
  break;
396
- case 'center':
400
+ }
401
+
402
+ case 'center': {
397
403
  if (isHorizontal) {
398
- ctx.fillText(formattedTxt, centerX, centerYHorizontal);
399
- } else {
404
+ if (textWidth < widthFreeSpaceToDraw) {
405
+ ctx.fillText(formattedTxt, centerX, centerYHorizontal);
406
+ }
407
+ } else if (textHeight < heightFreeSpaceToDraw) {
400
408
  ctx.fillText(formattedTxt, centerX, centerY);
401
409
  }
410
+
402
411
  break;
403
- case 'out':
412
+ }
413
+
414
+ case 'out': {
415
+ if (isStacked) {
416
+ console.warn('[EVUI][Bar Chart] In case of Stack Bar Chart, \'out\' of \'showValue\'\'s align is not supported.');
417
+ return;
418
+ }
419
+
404
420
  if (isHorizontal) {
405
421
  ctx.fillText(formattedTxt, minXPos + w, centerYHorizontal);
406
422
  } else {
407
- ctx.fillText(formattedTxt, centerX, y + h - (vh / 2));
423
+ ctx.fillText(formattedTxt, centerX, y + h - (textHeight / 2));
408
424
  }
425
+
409
426
  break;
410
- case 'end':
427
+ }
428
+
411
429
  default:
430
+ case 'end': {
412
431
  if (isHorizontal) {
413
- const xPos = x + w - (vw * 2);
414
- ctx.fillText(formattedTxt, xPos <= minXPos ? minXPos : xPos, centerYHorizontal);
415
- } else {
416
- const yPos = y + h + vh;
432
+ if (textWidth < widthFreeSpaceToDraw) {
433
+ const xPos = x + w - (textWidth * 2);
434
+ ctx.fillText(formattedTxt, xPos <= minXPos ? minXPos : xPos, centerYHorizontal);
435
+ }
436
+ } else if (textHeight < heightFreeSpaceToDraw) {
437
+ const yPos = y + h + textHeight;
417
438
  ctx.fillText(formattedTxt, centerX, yPos >= minYPos ? minYPos : yPos);
418
439
  }
440
+
419
441
  break;
442
+ }
420
443
  }
421
444
 
422
445
  ctx.restore();
@@ -1,19 +1,24 @@
1
1
  import { merge } from 'lodash-es';
2
2
  import Util from '../helpers/helpers.util';
3
3
  import { HEAT_MAP_OPTION } from '../helpers/helpers.constant';
4
+ import { convertToPercent } from '../../../common/utils';
4
5
 
5
6
  class HeatMap {
6
- constructor(sId, opt, colorOpt) {
7
+ constructor(sId, opt, colorOpt, isGradient) {
7
8
  const merged = merge({}, HEAT_MAP_OPTION, opt);
8
9
  Object.keys(merged).forEach((key) => {
9
10
  this[key] = merged[key];
10
11
  });
11
12
 
12
- this.createColorAxis(colorOpt);
13
+ this.isGradient = isGradient;
14
+ this.createColorState(colorOpt);
13
15
 
14
16
  this.sId = sId;
15
17
  this.data = [];
16
- this.labels = {};
18
+ this.labels = {
19
+ x: [],
20
+ y: [],
21
+ };
17
22
  this.valueOpt = {};
18
23
  this.size = {
19
24
  w: 0,
@@ -27,41 +32,66 @@ class HeatMap {
27
32
  * @param colorOpt
28
33
  * @returns {*[]}
29
34
  */
30
- createColorAxis(colorOpt) {
31
- const colorAxis = [];
35
+ createColorState(colorOpt) {
36
+ const colorState = [];
37
+ const regex = /[^0-9]&[^,]/g;
32
38
  const { min, max, categoryCnt, error, stroke } = colorOpt;
33
39
 
34
- const minColor = min.includes('#') ? Util.hexToRgb(min) : min;
35
- const maxColor = max.includes('#') ? Util.hexToRgb(max) : max;
40
+ const minColor = min.includes('#') ? Util.hexToRgb(min) : min.replace(regex, '');
41
+ const maxColor = max.includes('#') ? Util.hexToRgb(max) : max.replace(regex, '');
36
42
 
37
43
  const [minR, minG, minB] = minColor.split(',');
38
44
  const [maxR, maxG, maxB] = maxColor.split(',');
39
45
 
40
- const unitR = Math.floor((minR - maxR) / (categoryCnt - 1));
41
- const unitG = Math.floor((minG - maxG) / (categoryCnt - 1));
42
- const unitB = Math.floor((minB - maxB) / (categoryCnt - 1));
43
-
44
- for (let ix = 0; ix < categoryCnt; ix++) {
45
- const r = +minR - (unitR * ix);
46
- const g = +minG - (unitG * ix);
47
- const b = +minB - (unitB * ix);
48
-
49
- colorAxis.push({
50
- id: `color#${ix}`,
51
- value: `rgb(${r},${g},${b})`,
52
- state: 'normal',
53
- show: true,
54
- });
46
+ if (this.isGradient) {
47
+ colorState.push({
48
+ minColor: { minR, minG, minB },
49
+ maxColor: { maxR, maxG, maxB },
50
+ categoryCnt,
51
+ start: 0,
52
+ end: 100,
53
+ selectedValue: null,
54
+ });
55
+ } else {
56
+ const unitR = Math.floor((minR - maxR) / (categoryCnt - 1));
57
+ const unitG = Math.floor((minG - maxG) / (categoryCnt - 1));
58
+ const unitB = Math.floor((minB - maxB) / (categoryCnt - 1));
59
+
60
+ for (let ix = 0; ix < categoryCnt; ix++) {
61
+ const r = +minR - (unitR * ix);
62
+ const g = +minG - (unitG * ix);
63
+ const b = +minB - (unitB * ix);
64
+
65
+ colorState.push({
66
+ id: `color#${ix}`,
67
+ color: `rgb(${r},${g},${b})`,
68
+ state: 'normal',
69
+ show: true,
70
+ });
71
+ }
55
72
  }
56
73
 
57
- this.colorAxis = colorAxis;
74
+ this.colorState = colorState;
58
75
  this.errorColor = error;
59
76
  this.stroke = stroke;
60
77
  }
61
78
 
62
- getColorIndex(value) {
79
+ getColorForGradient(value) {
80
+ const { minColor, maxColor } = this.colorState[0];
81
+
82
+ const { minR, minG, minB } = minColor;
83
+ const { maxR, maxG, maxB } = maxColor;
84
+
85
+ const r = +minR - Math.floor(((minR - maxR) * value) / 100);
86
+ const g = +minG - Math.floor(((minG - maxG) * value) / 100);
87
+ const b = +minB - Math.floor(((minB - maxB) * value) / 100);
88
+
89
+ return `rgb(${r},${g},${b})`;
90
+ }
91
+
92
+ getColorIndexForIcon(value) {
63
93
  const { existError, min, interval, decimalPoint } = this.valueOpt;
64
- const maxIndex = this.colorAxis.length - 1;
94
+ const maxIndex = this.colorState.length - 1;
65
95
  if (existError && value < 0) {
66
96
  return maxIndex;
67
97
  }
@@ -75,11 +105,52 @@ class HeatMap {
75
105
  return colorIndex;
76
106
  }
77
107
 
108
+ getItemInfo(value) {
109
+ const { min, max } = this.valueOpt;
110
+ const itemInfo = {
111
+ show: false,
112
+ opacity: 0,
113
+ dataColor: null,
114
+ id: null,
115
+ isHighlight: null,
116
+ };
117
+ if (this.isGradient) {
118
+ const ratio = convertToPercent(value - min, max - min);
119
+ const { start, end, selectedValue } = this.colorState[0];
120
+ if (value < 0 || (start <= ratio && ratio <= end)) {
121
+ itemInfo.show = true;
122
+ itemInfo.isHighlight = selectedValue !== null
123
+ && (Math.floor(value) === Math.floor(min + ((max - min) * (selectedValue / 100))));
124
+ itemInfo.opacity = 1;
125
+ itemInfo.dataColor = value < 0
126
+ ? this.errorColor : this.getColorForGradient(ratio);
127
+ }
128
+ } else {
129
+ const colorIndex = this.getColorIndexForIcon(value);
130
+ const { show, state, color, id } = this.colorState[colorIndex];
131
+ itemInfo.show = show;
132
+ itemInfo.opacity = state === 'downplay' ? 0.1 : 1;
133
+ itemInfo.dataColor = value < 0 ? this.errorColor : color;
134
+ itemInfo.id = id;
135
+ }
136
+ return itemInfo;
137
+ }
138
+
78
139
  drawItem(ctx, x, y, w, h) {
79
140
  ctx.beginPath();
80
141
  if (this.stroke.show) {
81
- ctx.strokeRect(x, y, w, h);
82
- ctx.fillRect(x, y, w, h);
142
+ const { radius } = this.stroke;
143
+ if (radius > 0 && radius < h && radius < w) {
144
+ ctx.moveTo(x + radius, y);
145
+ ctx.arcTo(x + w, y, x + w, y + h, radius);
146
+ ctx.arcTo(x + w, y + h, x, y + h, radius);
147
+ ctx.arcTo(x, y + h, x, y, radius);
148
+ ctx.arcTo(x, y, x + w, y, radius);
149
+ ctx.fill();
150
+ } else {
151
+ ctx.strokeRect(x, y, w, h);
152
+ ctx.fillRect(x, y, w, h);
153
+ }
83
154
  } else {
84
155
  const aliasPixel = Util.aliasPixel(1);
85
156
  ctx.fillRect(
@@ -122,7 +193,7 @@ class HeatMap {
122
193
  return;
123
194
  }
124
195
 
125
- const { ctx, chartRect, labelOffset } = param;
196
+ const { ctx, chartRect, labelOffset, overlayCtx } = param;
126
197
 
127
198
  const xArea = chartRect.chartWidth - (labelOffset.left + labelOffset.right);
128
199
  const yArea = chartRect.chartHeight - (labelOffset.top + labelOffset.bottom);
@@ -142,11 +213,11 @@ class HeatMap {
142
213
 
143
214
  if (xp !== null && yp !== null
144
215
  && (value !== null && value !== undefined)) {
145
- const colorIndex = this.getColorIndex(value);
146
- const opacity = this.colorAxis[colorIndex].state === 'downplay' ? 0.1 : 1;
147
- item.dataColor = value < 0 ? this.errorColor : this.colorAxis[colorIndex].value;
148
- item.cId = this.colorAxis[colorIndex].id;
149
- if (this.colorAxis[colorIndex].show) {
216
+ const { show, opacity, dataColor, id, isHighlight } = this.getItemInfo(value);
217
+ item.dataColor = dataColor;
218
+ item.cId = id;
219
+ ctx.save();
220
+ if (show) {
150
221
  ctx.fillStyle = Util.colorStringToRgba(item.dataColor, opacity);
151
222
  if (this.stroke.show) {
152
223
  const { color, lineWidth, opacity: sOpacity } = this.stroke;
@@ -161,25 +232,25 @@ class HeatMap {
161
232
  h -= (lineWidth * 2);
162
233
  }
163
234
  this.drawItem(ctx, xp, yp, w, h);
235
+ ctx.restore();
236
+
237
+ item.xp = xp;
238
+ item.yp = yp;
239
+ item.w = w;
240
+ item.h = h;
164
241
 
165
242
  if (this.showValue.use) {
166
243
  this.drawValueLabels({
167
244
  context: ctx,
168
245
  data: item,
169
- positions: {
170
- x: xp,
171
- y: yp,
172
- w,
173
- h,
174
- },
175
246
  });
176
247
  }
248
+ if (isHighlight) {
249
+ this.itemHighlight({
250
+ data: item,
251
+ }, overlayCtx);
252
+ }
177
253
  }
178
-
179
- item.xp = xp;
180
- item.yp = yp;
181
- item.w = w;
182
- item.h = h;
183
254
  }
184
255
  });
185
256
  }
@@ -190,9 +261,9 @@ class HeatMap {
190
261
  * @param context canvas context
191
262
  * @param data series value data (model.store.js addData return value)
192
263
  */
193
- drawValueLabels({ context, data, positions }) {
264
+ drawValueLabels({ context, data }) {
194
265
  const { fontSize, textColor, align, formatter, decimalPoint } = this.showValue;
195
- const { x, y, w, h } = positions;
266
+ const { xp: x, yp: y, w, h, o: value } = data;
196
267
  const ctx = context;
197
268
 
198
269
  ctx.save();
@@ -204,8 +275,6 @@ class HeatMap {
204
275
  ctx.textBaseline = 'middle';
205
276
  ctx.textAlign = align !== 'center' ? 'left' : 'center';
206
277
 
207
- const value = data.o;
208
-
209
278
  let formattedTxt;
210
279
  if (formatter) {
211
280
  formattedTxt = formatter(value);
@@ -295,31 +364,36 @@ class HeatMap {
295
364
  const h = gdata.h;
296
365
  const cId = gdata.cId;
297
366
 
298
- const isShow = this.colorAxis.find(({ id }) => id === cId)?.show;
299
-
367
+ let isShow;
368
+ if (this.isGradient) {
369
+ const { min, max } = this.valueOpt;
370
+ const ratio = convertToPercent(gdata.o - min, max - min);
371
+ const { start, end } = this.colorState[0];
372
+ isShow = (start <= ratio && ratio <= end) || gdata.o === -1;
373
+ } else {
374
+ isShow = this.colorState.find(({ id }) => id === cId)?.show;
375
+ }
300
376
  ctx.save();
377
+ ctx.shadowOffsetX = 2;
378
+ ctx.shadowOffsetY = 2;
379
+ ctx.shadowBlur = 4;
380
+
301
381
  if (x !== null && y !== null && isShow) {
302
382
  const color = gdata.dataColor;
303
- ctx.strokeStyle = Util.colorStringToRgba(color, 1);
304
- ctx.fillStyle = Util.colorStringToRgba(color, this.highlight.maxShadowOpacity);
305
- ctx.shadowColor = color;
306
- this.drawItem(ctx, x, y, w, h);
383
+ ctx.shadowColor = Util.colorStringToRgba('#605F5F');
384
+ ctx.strokeStyle = Util.colorStringToRgba(color);
385
+ ctx.fillStyle = Util.colorStringToRgba(color);
386
+ this.drawItem(ctx, x - 2, y - 2, w + 4, h + 4);
387
+
388
+ ctx.restore();
307
389
 
308
390
  if (this.showValue.use) {
309
391
  this.drawValueLabels({
310
392
  context: ctx,
311
393
  data: gdata,
312
- positions: {
313
- x,
314
- y,
315
- w,
316
- h,
317
- },
318
394
  });
319
395
  }
320
396
  }
321
-
322
- ctx.restore();
323
397
  }
324
398
 
325
399
  /**
@@ -142,6 +142,7 @@ export const HEAT_MAP_OPTION = {
142
142
  show: true,
143
143
  highlight: {
144
144
  maxShadowOpacity: 0.4,
145
+ brightness: 150,
145
146
  },
146
147
  xAxisIndex: 0,
147
148
  yAxisIndex: 0,
@@ -54,7 +54,9 @@ const modules = {
54
54
  return new Pie(id, opt, index);
55
55
  } else if (type === 'heatMap') {
56
56
  this.seriesInfo.charts.heatMap.push(id);
57
- return new HeatMap(id, opt, this.options.heatMapColor);
57
+ const { heatMapColor, legend } = this.options;
58
+ const isGradient = legend.type === 'gradient';
59
+ return new HeatMap(id, opt, heatMapColor, isGradient);
58
60
  }
59
61
 
60
62
  return false;
@@ -418,7 +418,7 @@ const modules = {
418
418
  },
419
419
 
420
420
  getSeriesValueOptForHeatMap(series) {
421
- const data = series.data;
421
+ const { data, colorState, isGradient } = series;
422
422
  const colorOpt = this.options.heatMapColor;
423
423
  const categoryCnt = colorOpt.categoryCnt;
424
424
  const decimalPoint = colorOpt.decimalPoint;
@@ -441,10 +441,14 @@ const modules = {
441
441
  }
442
442
  });
443
443
 
444
- if (isExistError && series.colorAxis.length === categoryCnt) {
445
- series.colorAxis.push({
444
+ if (
445
+ isExistError
446
+ && !isGradient
447
+ && colorState.length === categoryCnt
448
+ ) {
449
+ colorState.push({
446
450
  id: `color#${categoryCnt}`,
447
- value: colorOpt.error,
451
+ color: colorOpt.error,
448
452
  state: 'normal',
449
453
  show: true,
450
454
  });