evui 3.3.15 → 3.3.18

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.15",
3
+ "version": "3.3.18",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -189,8 +189,8 @@ class EvChart {
189
189
  break;
190
190
  }
191
191
  case 'bar': {
192
- const { thickness, borderRadius } = this.options;
193
- series.draw({ thickness, borderRadius, showSeriesCount, showIndex, ...opt });
192
+ const { thickness, cPadRatio, borderRadius } = this.options;
193
+ series.draw({ thickness, cPadRatio, borderRadius, showSeriesCount, showIndex, ...opt });
194
194
  if (series.show) {
195
195
  showIndex++;
196
196
  }
@@ -443,15 +443,31 @@ class EvChart {
443
443
  const { width, height } = this.getChartDOMRect();
444
444
 
445
445
  const padding = this.options.padding;
446
+ const xAxisTitleOpt = this.options.axesX[0]?.title;
447
+ const yAxisTitleOpt = this.options.axesY[0]?.title;
448
+ const titleMargin = 10;
449
+
450
+ let xAxisTitleHeight = 0;
451
+ if (xAxisTitleOpt?.use && xAxisTitleOpt?.text) {
452
+ const fontSize = isNaN(xAxisTitleOpt?.fontSize) ? 12 : xAxisTitleOpt?.fontSize;
453
+ xAxisTitleHeight = fontSize + titleMargin;
454
+ }
455
+
456
+ let yAxisTitleHeight = 0;
457
+ if (yAxisTitleOpt?.use && yAxisTitleOpt?.text) {
458
+ const fontSize = isNaN(yAxisTitleOpt?.fontSize) ? 12 : yAxisTitleOpt?.fontSize;
459
+ yAxisTitleHeight = fontSize + titleMargin;
460
+ }
461
+
446
462
  const horizontalPadding = padding.left + padding.right;
447
- const verticalPadding = padding.top + padding.bottom;
463
+ const verticalPadding = padding.top + padding.bottom + xAxisTitleHeight + yAxisTitleHeight;
448
464
  const chartWidth = width > horizontalPadding ? width - horizontalPadding : width;
449
465
  const chartHeight = height > verticalPadding ? height - verticalPadding : height;
450
466
 
451
467
  const x1 = padding.left;
452
468
  const x2 = Math.max(width - padding.right, x1 + 2);
453
- const y1 = padding.top;
454
- const y2 = Math.max(height - padding.bottom, y1 + 2);
469
+ const y1 = padding.top + yAxisTitleHeight;
470
+ const y2 = Math.max(height - padding.bottom - xAxisTitleHeight, y1 + 2);
455
471
 
456
472
  return {
457
473
  x1,
@@ -67,7 +67,14 @@ class Bar {
67
67
 
68
68
  const dArea = isHorizontal ? yArea : xArea;
69
69
  const cArea = dArea / (this.data.length || 1);
70
- const cPad = 2;
70
+
71
+ let cPad;
72
+ const isUnableToDrawCategoryPadding = param.cPadRatio >= 1 || param.cPadRatio <= 0;
73
+ if (isUnableToDrawCategoryPadding) {
74
+ cPad = 2;
75
+ } else {
76
+ cPad = Math.max((dArea * (param.cPadRatio / 2)) / this.data.length, 2);
77
+ }
71
78
 
72
79
  let bArea;
73
80
  let w;
@@ -60,13 +60,14 @@ class HeatMap {
60
60
  }
61
61
 
62
62
  getColorIndex(value) {
63
- const existError = this.valueOpt.existError;
63
+ const { existError, min, interval, decimalPoint } = this.valueOpt;
64
64
  const maxIndex = this.colorAxis.length - 1;
65
65
  if (existError && value < 0) {
66
66
  return maxIndex;
67
67
  }
68
68
 
69
- const colorIndex = Math.floor(value / this.valueOpt.interval);
69
+ const colorIndex = Math.floor(+(value - min).toFixed(decimalPoint) / interval);
70
+
70
71
  if (colorIndex >= maxIndex) {
71
72
  return existError ? maxIndex - 1 : maxIndex;
72
73
  }
@@ -292,9 +293,12 @@ class HeatMap {
292
293
  const y = gdata.yp;
293
294
  const w = gdata.w;
294
295
  const h = gdata.h;
296
+ const cId = gdata.cId;
297
+
298
+ const isShow = this.colorAxis.find(({ id }) => id === cId)?.show;
295
299
 
296
300
  ctx.save();
297
- if (x !== null && y !== null) {
301
+ if (x !== null && y !== null && isShow) {
298
302
  const color = gdata.dataColor;
299
303
  ctx.strokeStyle = Util.colorStringToRgba(color, 1);
300
304
  ctx.fillStyle = Util.colorStringToRgba(color, this.highlight.maxShadowOpacity);
@@ -102,6 +102,16 @@ export const AXIS_OPTION = {
102
102
  fitWidth: false,
103
103
  fitDir: 'right',
104
104
  },
105
+ title: {
106
+ use: false,
107
+ text: null,
108
+ fontWeight: 400,
109
+ fontSize: 12,
110
+ fontFamily: 'Roboto',
111
+ textAlign: 'right',
112
+ fontStyle: 'normal',
113
+ color: '#808080',
114
+ },
105
115
  };
106
116
 
107
117
  export const PLOT_LINE_OPTION = {
@@ -127,7 +127,14 @@ export default {
127
127
  * @returns {string} computed value
128
128
  */
129
129
  getLabelStyle(style) {
130
- return `normal normal ${style.fontWeight} ${style.fontSize}px ${style.fontFamily}`;
130
+ const {
131
+ fontStyle = 'normal',
132
+ fontWeight = 'norma',
133
+ fontSize = '12',
134
+ fontFamily = 'Roboto',
135
+ } = style;
136
+
137
+ return `${fontStyle} normal ${fontWeight} ${fontSize}px ${fontFamily}`;
131
138
  },
132
139
 
133
140
  /**
@@ -421,6 +421,7 @@ const modules = {
421
421
  const data = series.data;
422
422
  const colorOpt = this.options.heatMapColor;
423
423
  const categoryCnt = colorOpt.categoryCnt;
424
+ const decimalPoint = colorOpt.decimalPoint;
424
425
 
425
426
  let minValue;
426
427
  let maxValue = 0;
@@ -449,11 +450,21 @@ const modules = {
449
450
  });
450
451
  }
451
452
 
453
+ let interval = maxValue > minValue ? Math.floor((maxValue - minValue) / categoryCnt) : 1;
454
+ if ((maxValue - minValue) <= categoryCnt) {
455
+ if (decimalPoint > 0) {
456
+ interval = +((maxValue - minValue) / categoryCnt).toFixed(decimalPoint);
457
+ } else {
458
+ interval = 1;
459
+ }
460
+ }
461
+
452
462
  return {
453
463
  min: minValue,
454
464
  max: maxValue,
455
- interval: Math.ceil((maxValue - minValue) / categoryCnt),
465
+ interval,
456
466
  existError: isExistError,
467
+ decimalPoint,
457
468
  };
458
469
  },
459
470
 
@@ -82,20 +82,40 @@ const modules = {
82
82
  Object.values(seriesList).forEach((series) => {
83
83
  if (!series.isExistGrp && series.showLegend) {
84
84
  const { colorAxis, valueOpt } = series;
85
- const { min, max, interval, existError } = valueOpt;
86
- const endIndex = colorAxis.length - 1;
87
- colorAxis.forEach((colorItem, index) => {
85
+ const { min, max, interval, existError, decimalPoint } = valueOpt;
86
+ const length = colorAxis.length;
87
+ const endIndex = existError ? length - 2 : length - 1;
88
+ for (let index = 0; index < length; index++) {
89
+ const colorItem = colorAxis[index];
88
90
  const minValue = min + (interval * index);
89
- const maxValue = index === endIndex
90
- ? max : minValue + interval;
91
- const name = existError && index === endIndex ? 'error' : `${minValue} - ${maxValue}`;
91
+ let maxValue = minValue + interval;
92
+ if (index < endIndex) {
93
+ maxValue -= (0.1 ** decimalPoint);
94
+ } else {
95
+ maxValue = max + (0.1 ** decimalPoint);
96
+ }
97
+
98
+ let name = `${minValue.toFixed(decimalPoint)} - ${maxValue.toFixed(decimalPoint)}`;
99
+ if (min === undefined || max === undefined) {
100
+ if (index === 0) {
101
+ name = '0';
102
+ } else {
103
+ break;
104
+ }
105
+ } else if (existError && index === endIndex + 1) {
106
+ name = 'error';
107
+ } else if (minValue > max) {
108
+ break;
109
+ } else if (interval <= 1 && decimalPoint === 0) {
110
+ name = minValue;
111
+ }
92
112
 
93
113
  this.addLegend({
94
114
  cId: colorItem.id,
95
115
  color: colorItem.value,
96
116
  name,
97
117
  });
98
- });
118
+ }
99
119
  }
100
120
  });
101
121
  },
@@ -176,12 +176,21 @@ const modules = {
176
176
  const boxPadding = { t: 8, b: 8, r: 20, l: 16 };
177
177
  const isHorizontal = this.options.horizontal;
178
178
  const opt = this.options.tooltip;
179
+ const valueFormatter = typeof opt.formatter === 'function' ? opt.formatter : opt.formatter?.value;
180
+ const titleFormatter = opt.formatter?.title;
179
181
 
180
182
  // draw tooltip Title(axis label) and add style class for wrap line about too much long label.
181
183
  if (this.axesX.length && this.axesY.length) {
182
- this.tooltipHeaderDOM.textContent = this.options.horizontal
183
- ? this.axesY[hitAxis.y].getLabelFormat(hitItem.y)
184
- : this.axesX[hitAxis.x].getLabelFormat(hitItem.x);
184
+ if (titleFormatter) {
185
+ this.tooltipHeaderDOM.textContent = titleFormatter({
186
+ x: hitItem.x,
187
+ y: hitItem.y,
188
+ });
189
+ } else {
190
+ this.tooltipHeaderDOM.textContent = this.options.horizontal
191
+ ? this.axesY[hitAxis.y].getLabelFormat(hitItem.y)
192
+ : this.axesX[hitAxis.x].getLabelFormat(hitItem.x);
193
+ }
185
194
  }
186
195
 
187
196
  if (opt.textOverflow) {
@@ -305,14 +314,14 @@ const modules = {
305
314
 
306
315
  // 3. Draw value
307
316
  let formattedTxt;
308
- if (opt.formatter) {
317
+ if (valueFormatter) {
309
318
  if (this.options.type === 'pie') {
310
- formattedTxt = opt.formatter({
319
+ formattedTxt = valueFormatter({
311
320
  value,
312
321
  name,
313
322
  });
314
323
  } else {
315
- formattedTxt = opt.formatter({
324
+ formattedTxt = valueFormatter({
316
325
  x: this.options.horizontal ? value : hitItem.x,
317
326
  y: this.options.horizontal ? hitItem.y : value,
318
327
  name,
@@ -320,7 +329,7 @@ const modules = {
320
329
  }
321
330
  }
322
331
 
323
- if (!opt.formatter || typeof formattedTxt !== 'string') {
332
+ if (!valueFormatter || typeof formattedTxt !== 'string') {
324
333
  formattedTxt = numberWithComma(value);
325
334
  }
326
335
 
@@ -356,13 +365,26 @@ const modules = {
356
365
  const boxPadding = { t: 8, b: 8, r: 20, l: 16 };
357
366
  const isHorizontal = this.options.horizontal;
358
367
  const opt = this.options.tooltip;
359
- let xValue = '';
360
- let yValue = '';
368
+ const valueFormatter = typeof opt.formatter === 'function' ? opt.formatter : opt.formatter?.value;
369
+ const titleFormatter = opt.formatter?.title;
370
+
371
+ const colorAxis = Object.values(this.seriesList)[0].colorAxis;
372
+ const isShow = colorAxis.find(({ id }) => id === hitItem.cId)?.show;
373
+ if (!isShow) {
374
+ this.tooltipClear();
375
+ return;
376
+ }
361
377
 
362
378
  // draw tooltip Title(axis label) and add style class for wrap line about too much long label.
363
379
  if (this.axesX.length) {
364
- xValue = this.axesX[hitAxis.x].getLabelFormat(hitItem.x);
365
- this.tooltipHeaderDOM.textContent = xValue;
380
+ if (titleFormatter) {
381
+ this.tooltipHeaderDOM.textContent = titleFormatter({
382
+ x: hitItem.x,
383
+ y: hitItem.y,
384
+ });
385
+ } else {
386
+ this.tooltipHeaderDOM.textContent = this.axesX[hitAxis.x].getLabelFormat(hitItem.x);
387
+ }
366
388
  }
367
389
 
368
390
  if (opt.textOverflow) {
@@ -403,21 +425,20 @@ const modules = {
403
425
  // 2. Draw value y names
404
426
  ctx.textBaseline = 'Bottom';
405
427
  if (this.axesY.length) {
406
- yValue = this.axesY[hitAxis.y].getLabelFormat(hitItem.y);
407
- ctx.fillText(yValue, itemX + COLOR_MARGIN, itemY);
428
+ ctx.fillText(this.axesY[hitAxis.y].getLabelFormat(hitItem.y), itemX + COLOR_MARGIN, itemY);
408
429
  }
409
430
 
410
431
  // 3. Draw value
411
432
  let formattedTxt = itemValue;
412
- if (opt.formatter) {
413
- formattedTxt = opt.formatter({
414
- x: xValue,
415
- y: yValue,
433
+ if (valueFormatter) {
434
+ formattedTxt = valueFormatter({
435
+ x: hitItem.x,
436
+ y: hitItem.y,
416
437
  value: itemValue,
417
438
  });
418
439
  }
419
440
 
420
- if ((!opt.formatter || typeof formattedTxt !== 'string') && itemValue !== 'error') {
441
+ if ((!valueFormatter || typeof formattedTxt !== 'string') && itemValue !== 'error') {
421
442
  formattedTxt = numberWithComma(itemValue);
422
443
  }
423
444
 
@@ -557,15 +578,16 @@ const modules = {
557
578
 
558
579
  // 3. Draw value
559
580
  let formattedTxt;
560
- if (opt.formatter) {
561
- formattedTxt = opt.formatter({
581
+ const formatter = typeof opt.formatter === 'function' ? opt.formatter : opt.formatter?.value;
582
+ if (formatter) {
583
+ formattedTxt = formatter({
562
584
  x: xValue,
563
585
  y: yValue,
564
586
  name,
565
587
  });
566
588
  }
567
589
 
568
- if (!opt.formatter || typeof formattedTxt !== 'string') {
590
+ if (!formatter || typeof formattedTxt !== 'string') {
569
591
  const formattedXValue = xAxisOpt.type === 'time'
570
592
  ? dayjs(xValue).format(xAxisOpt.timeFormat)
571
593
  : numberWithComma(xValue);
@@ -156,6 +156,52 @@ class Scale {
156
156
  };
157
157
  }
158
158
 
159
+ /**
160
+ * Draw Axis Title
161
+ *
162
+ * @param {object} chartRect min/max information
163
+ * @param {object} labelOffset label offset information
164
+ *
165
+ * @returns {undefined}
166
+ */
167
+ drawAxisTitle(chartRect, labelOffset) {
168
+ const titleOpt = this.title;
169
+
170
+ if (!titleOpt?.use || isNaN(titleOpt.fontSize)) {
171
+ return;
172
+ }
173
+
174
+ const ctx = this.ctx;
175
+ ctx.save();
176
+ ctx.font = Util.getLabelStyle(titleOpt);
177
+ ctx.fillStyle = titleOpt.color;
178
+ ctx.textAlign = titleOpt.textAlign;
179
+
180
+ const axisLinePosition = {
181
+ xLeft: chartRect.x1 + labelOffset.left,
182
+ xRight: chartRect.x2 - labelOffset.right,
183
+ yTop: chartRect.y1,
184
+ };
185
+
186
+ let titleXPos;
187
+ let titleYPos;
188
+
189
+ const margin = 10;
190
+ if (this.type === 'x') {
191
+ titleXPos = axisLinePosition.xRight;
192
+ titleYPos = chartRect.y2 + titleOpt.fontSize + margin;
193
+ } else {
194
+ titleYPos = axisLinePosition.yTop - titleOpt.fontSize - margin;
195
+ titleXPos = axisLinePosition.xLeft;
196
+ }
197
+
198
+ if (titleXPos > 0 && titleYPos > 0) {
199
+ ctx.fillText(titleOpt.text, titleXPos, titleYPos);
200
+ }
201
+
202
+ ctx.restore();
203
+ }
204
+
159
205
  /**
160
206
  * Draw axis
161
207
  * @param {object} chartRect min/max information
@@ -186,6 +232,8 @@ class Scale {
186
232
 
187
233
  let aliasPixel;
188
234
 
235
+ this.drawAxisTitle(chartRect, labelOffset);
236
+
189
237
  // label font 설정
190
238
  ctx.font = Util.getLabelStyle(this.labelStyle);
191
239
  ctx.fillStyle = this.labelStyle.color;
@@ -89,6 +89,8 @@ class StepScale extends Scale {
89
89
  const offsetCounterPoint = aPos[this.units.rectOffsetCounter(this.position)];
90
90
  const maxWidth = chartRect.chartWidth / (this.labels.length + 2);
91
91
 
92
+ this.drawAxisTitle(chartRect, labelOffset);
93
+
92
94
  if (this.labelStyle?.show) {
93
95
  // label font 설정
94
96
  ctx.font = Util.getLabelStyle(this.labelStyle);
@@ -132,6 +132,8 @@ class TimeCategoryScale extends Scale {
132
132
  const offsetPoint = aPos[this.units.rectOffset(this.position)];
133
133
  const offsetCounterPoint = aPos[this.units.rectOffsetCounter(this.position)];
134
134
 
135
+ this.drawAxisTitle(chartRect, labelOffset);
136
+
135
137
  // label font 설정
136
138
  ctx.font = Util.getLabelStyle(this.labelStyle);
137
139
 
@@ -43,6 +43,7 @@ const DEFAULT_OPTIONS = {
43
43
  width: '100%',
44
44
  height: '100%',
45
45
  thickness: 1,
46
+ cPadRatio: 0,
46
47
  borderRadius: 0,
47
48
  combo: false,
48
49
  tooltip: {
@@ -116,6 +117,7 @@ const DEFAULT_OPTIONS = {
116
117
  opacity: 1,
117
118
  },
118
119
  error: '#FF0000',
120
+ decimalPoint: 0,
119
121
  },
120
122
  };
121
123
 
@@ -874,12 +874,14 @@ export const contextMenuEvent = (params) => {
874
874
  } else {
875
875
  rowIndex = target.parentElement.parentElement.dataset.index;
876
876
  }
877
-
877
+ let clickedRow;
878
878
  if (rowIndex) {
879
- const rowData = stores.viewStore[+rowIndex][ROW_DATA_INDEX];
880
- selectInfo.selectedRow = rowData;
879
+ clickedRow = stores.viewStore.find(row => row[ROW_INDEX] === +rowIndex)?.[ROW_DATA_INDEX];
880
+ }
881
+ if (clickedRow) {
882
+ selectInfo.selectedRow = clickedRow;
881
883
  setContextMenu();
882
- emit('update:selected', rowData);
884
+ emit('update:selected', clickedRow);
883
885
  } else {
884
886
  selectInfo.selectedRow = [];
885
887
  setContextMenu(false);
@@ -6,6 +6,7 @@
6
6
  :item="menu"
7
7
  :selected-item="modelValue"
8
8
  :expandable="expandable"
9
+ :disabled="disabled"
9
10
  :comp="component"
10
11
  @click="clickMenu"
11
12
  />
@@ -34,14 +35,21 @@ export default {
34
35
  type: Boolean,
35
36
  default: true,
36
37
  },
38
+ disabled: {
39
+ type: Boolean,
40
+ default: false,
41
+ },
37
42
  },
38
43
  emits: ['update:modelValue', 'change'],
39
44
  setup(props, { emit }) {
40
45
  const prevMenuItem = ref(props.items.filter(item => props.modelValue === item.value));
41
- const clickMenu = (newMenuItem) => {
42
- emit('update:modelValue', newMenuItem.value);
43
- emit('change', newMenuItem, prevMenuItem.value);
44
- prevMenuItem.value = newMenuItem;
46
+ const clickMenu = (params) => {
47
+ if (!params.disabled) {
48
+ const newMenuItem = params.item;
49
+ emit('update:modelValue', newMenuItem.value);
50
+ emit('change', newMenuItem, prevMenuItem.value);
51
+ prevMenuItem.value = newMenuItem;
52
+ }
45
53
  };
46
54
  const component = MenuItem;
47
55
  return {
@@ -4,14 +4,17 @@
4
4
  :class="[
5
5
  'ev-menu-item',
6
6
  `depth${depth}`,
7
- { active: item.value === selectedItem },
7
+ { active: !item.disabled && item.value === selectedItem },
8
8
  ]">
9
9
  <div
10
10
  :class="[
11
11
  'ev-menu-title',
12
- { 'expandable': hasChild && expandable },
12
+ {
13
+ 'expandable': hasChild && expandable,
14
+ 'disabled': item.disabled,
15
+ },
13
16
  ]"
14
- @click="clickMenu(item, depth)"
17
+ @click="clickMenu({item, depth, disabled: item.disabled})"
15
18
  >
16
19
  <i
17
20
  v-if="!!item.iconClass"
@@ -43,6 +46,7 @@
43
46
  :item="menu"
44
47
  :selected-item="selectedItem"
45
48
  :expandable="expandable"
49
+ :disabled="disabled"
46
50
  :comp="comp"
47
51
  @click="clickMenu"
48
52
  />
@@ -77,6 +81,9 @@ export default {
77
81
  } else if (obj.hidden !== undefined && typeof obj.hidden !== 'boolean') {
78
82
  console.warn('[EVUI][Menu] hidden attribute must be \'Boolean\' type.');
79
83
  return false;
84
+ } else if (obj.disabled !== undefined && typeof obj.disabled !== 'boolean') {
85
+ console.warn('[EVUI][Menu] disabled attribute must be \'Boolean\' type.');
86
+ return false;
80
87
  }
81
88
  return true;
82
89
  },
@@ -93,6 +100,10 @@ export default {
93
100
  type: Object,
94
101
  default: () => {},
95
102
  },
103
+ disabled: {
104
+ type: Boolean,
105
+ default: false,
106
+ },
96
107
  },
97
108
  emits: ['click'],
98
109
  setup(props, { emit }) {
@@ -100,13 +111,13 @@ export default {
100
111
  const isExpand = ref(defaultExpand);
101
112
  const hasChild = computed(() => !!props.item.children && !!props.item.children.length);
102
113
 
103
- const clickMenu = (menuItem, depth) => {
104
- if (hasChild.value && depth === props.depth) {
114
+ const clickMenu = (params) => {
115
+ if (hasChild.value && params.depth === props.depth) {
105
116
  if (props.expandable) {
106
117
  isExpand.value = !isExpand.value;
107
118
  }
108
119
  } else {
109
- emit('click', menuItem, props.depth);
120
+ emit('click', params);
110
121
  }
111
122
  };
112
123
 
@@ -149,6 +160,13 @@ export default {
149
160
  &.expandable {
150
161
  padding-right: 27px;
151
162
  }
163
+ &.disabled {
164
+ color: #848484 !important;
165
+ &:hover {
166
+ cursor: not-allowed;
167
+ color: #848484 !important;
168
+ }
169
+ }
152
170
  .list-expend-icon {
153
171
  position: absolute;
154
172
  top: 50%;
@@ -7,7 +7,6 @@
7
7
  }"
8
8
  v-bind="$attrs"
9
9
  :aria-current="page.isCurrent"
10
- @click.prevent="page.click"
11
10
  >
12
11
  <slot>{{ page.number }}</slot>
13
12
  </span>