evui 3.3.3 → 3.3.4

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.3",
3
+ "version": "3.3.4",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -16,6 +16,10 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
16
16
  export default {
17
17
  name: 'EvChart',
18
18
  props: {
19
+ selectedItem: {
20
+ type: Object,
21
+ default: null,
22
+ },
19
23
  options: {
20
24
  type: Object,
21
25
  default: () => ({}),
@@ -33,6 +37,7 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
33
37
  'click',
34
38
  'dbl-click',
35
39
  'drag-select',
40
+ 'update:selectedItem',
36
41
  ],
37
42
  setup(props) {
38
43
  let evChart = {};
@@ -40,6 +45,7 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
40
45
 
41
46
  const {
42
47
  eventListeners,
48
+ selectInfo,
43
49
  getNormalizedData,
44
50
  getNormalizedOptions,
45
51
  } = useModel();
@@ -60,6 +66,7 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
60
66
  normalizedData,
61
67
  normalizedOptions,
62
68
  eventListeners,
69
+ selectInfo,
63
70
  );
64
71
  };
65
72
 
@@ -93,6 +100,12 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
93
100
  updateSelTip: { update: true, keepDomain: false },
94
101
  });
95
102
  }, { deep: true });
103
+
104
+ await watch(() => props.selectedItem, (newValue) => {
105
+ if (newValue?.seriesID && !isNaN(newValue?.dataIndex)) {
106
+ evChart.selectItemByData(newValue);
107
+ }
108
+ }, { deep: true });
96
109
  });
97
110
 
98
111
  onBeforeUnmount(() => {
@@ -120,16 +133,11 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
120
133
  }
121
134
  }, props.resizeTimeout);
122
135
 
123
- const selectItemByLabel = (label) => {
124
- evChart.selectItemByLabel(label);
125
- };
126
-
127
136
  return {
128
137
  wrapper,
129
138
  wrapperStyle,
130
139
  onResize,
131
140
  redraw,
132
- selectItemByLabel,
133
141
  };
134
142
  },
135
143
  };
@@ -13,7 +13,7 @@ import Pie from './plugins/plugins.pie';
13
13
  import Tip from './element/element.tip';
14
14
 
15
15
  class EvChart {
16
- constructor(target, data, options, listeners) {
16
+ constructor(target, data, options, listeners, defaultSelectInfo) {
17
17
  Object.keys(Model).forEach(key => Object.assign(this, Model[key]));
18
18
  Object.assign(this, Title);
19
19
  Object.assign(this, Legend);
@@ -63,6 +63,8 @@ class EvChart {
63
63
  charts: { pie: [], bar: [], line: [], scatter: [] },
64
64
  count: 0,
65
65
  };
66
+
67
+ this.defaultSelectInfo = defaultSelectInfo;
66
68
  }
67
69
 
68
70
  /**
@@ -199,6 +201,26 @@ class EvChart {
199
201
  }
200
202
  }
201
203
 
204
+ /**
205
+ * Draw Tip with hitInfo and defaultSelectInfo
206
+ * @param hitInfo
207
+ */
208
+ drawTip(hitInfo) {
209
+ let tipLocationInfo;
210
+
211
+ if (hitInfo) {
212
+ tipLocationInfo = hitInfo;
213
+ } else if (this.lastHitInfo) {
214
+ tipLocationInfo = this.lastHitInfo;
215
+ } else if (this.defaultSelectInfo) {
216
+ tipLocationInfo = this.getItem(this.defaultSelectInfo, false);
217
+ } else {
218
+ tipLocationInfo = null;
219
+ }
220
+
221
+ this.drawTips(tipLocationInfo);
222
+ }
223
+
202
224
  /**
203
225
  * Create axes
204
226
  * @param {string} dir axis direction
@@ -533,6 +555,7 @@ class EvChart {
533
555
  if (!updateSelTip.keepDomain) {
534
556
  this.lastTip.pos = null;
535
557
  this.lastHitInfo = null;
558
+ this.defaultSelectInfo = null;
536
559
  }
537
560
  }
538
561
 
@@ -3,23 +3,21 @@ import Canvas from '../helpers/helpers.canvas';
3
3
 
4
4
  const modules = {
5
5
  /**
6
- * Draw TextTip with hitInfo
7
- * @param {object} [hitInfo=undefined] mouse hit information
6
+ * Draw TextTip with tip's locationInfo
7
+ * @param {object} [tipLocationInfo=undefined] tip location information
8
8
  *
9
9
  * @returns {undefined}
10
10
  */
11
- drawTip(hitInfo) {
11
+ drawTips(tipLocationInfo) {
12
12
  const opt = this.options;
13
13
  const isHorizontal = !!opt.horizontal;
14
14
  const maxTipOpt = opt.maxTip;
15
15
  const selectItemOpt = opt.selectItem;
16
+ let maxArgs;
16
17
 
17
- if (maxTipOpt.use || selectItemOpt.use) {
18
+ if (maxTipOpt.use) {
18
19
  const maxSID = this.minMax[isHorizontal ? 'x' : 'y'][0].maxSID;
19
- const selSID = (hitInfo && hitInfo.sId ? hitInfo.sId : this.lastHitInfo?.sId) ?? maxSID;
20
-
21
- const maxArgs = this.calculateTipInfo(this.seriesList[maxSID], 'max', null);
22
- const selArgs = this.calculateTipInfo(this.seriesList[selSID], 'sel', hitInfo);
20
+ maxArgs = this.calculateTipInfo(this.seriesList[maxSID], 'max', null);
23
21
 
24
22
  if (maxTipOpt.use && maxArgs) {
25
23
  this.drawTextTip({ opt: maxTipOpt, tipType: 'max', ...maxArgs });
@@ -28,11 +26,25 @@ const modules = {
28
26
  this.drawFixedIndicator({ opt: maxTipOpt, ...maxArgs });
29
27
  }
30
28
  }
29
+ }
30
+
31
+ if (selectItemOpt.use && tipLocationInfo) {
32
+ const seriesInfo = this.seriesList[tipLocationInfo?.sId];
33
+
34
+ if (!seriesInfo?.show) {
35
+ return;
36
+ }
37
+
38
+ const selArgs = this.calculateTipInfo(
39
+ seriesInfo,
40
+ 'sel',
41
+ tipLocationInfo,
42
+ );
31
43
 
32
44
  if (selectItemOpt.use && selArgs) {
33
45
  let isSamePos = false;
34
46
 
35
- if (maxTipOpt.use && maxArgs && maxArgs.dp === selArgs.dp) {
47
+ if (maxTipOpt.use && maxArgs?.dp === selArgs.dp) {
36
48
  isSamePos = true;
37
49
  }
38
50
 
@@ -45,8 +57,8 @@ const modules = {
45
57
  }
46
58
  }
47
59
 
48
- if (hitInfo && hitInfo.label !== null) {
49
- this.lastHitInfo = hitInfo;
60
+ if (tipLocationInfo && tipLocationInfo.label !== null) {
61
+ this.lastHitInfo = tipLocationInfo;
50
62
  }
51
63
  }
52
64
  },
@@ -387,43 +387,16 @@ const modules = {
387
387
  },
388
388
 
389
389
  /**
390
- * Get graph item by label index
391
- * @param {number} pos label index position
392
- *
393
- * @returns {object} graph item
394
- */
395
- getItemByLabelIndex(pos) {
396
- if (pos < 0) {
397
- return false;
398
- }
399
-
400
- return this.getItem(pos);
401
- },
402
-
403
- /**
404
- * Get graph item by label
405
- * @param {any} label label value for searching graph item
390
+ * Get graph items for each series by label index
391
+ * @param {number} labelIndex label index
406
392
  *
407
393
  * @returns {object} graph item
408
394
  */
409
- getItemByLabel(label) {
410
- if (label === null || label === undefined) {
395
+ getItemByLabelIndex(labelIndex) {
396
+ if (labelIndex < 0) {
411
397
  return false;
412
398
  }
413
399
 
414
- const labels = this.data.labels;
415
- const labelIndex = labels && labels.indexOf ? labels.indexOf(label) : -1;
416
-
417
- return this.getItem(labelIndex);
418
- },
419
-
420
- /**
421
- * Get graph items for each series by label index
422
- * @param {number} labelIndex label index
423
- *
424
- * @returns {object} graph item
425
- */
426
- getItem(labelIndex) {
427
400
  const sIds = Object.keys(this.seriesList);
428
401
  const isHorizontal = !!this.options.horizontal;
429
402
 
@@ -479,6 +452,91 @@ const modules = {
479
452
  return findInfo;
480
453
  },
481
454
 
455
+ getItem({ seriesID, dataIndex }, useApproximate = false) {
456
+ const dataInfo = this.getDataByValues(seriesID, dataIndex);
457
+ return this.getItemByPosition([dataInfo.xp, dataInfo.yp], useApproximate);
458
+ },
459
+ /**
460
+ *
461
+ * @param seriesID
462
+ * @param dataIndex
463
+ * @returns {*}
464
+ */
465
+ getDataByValues(seriesID, dataIndex) {
466
+ const series = this.seriesList[seriesID];
467
+ if (!series || isNaN(dataIndex) || dataIndex < 0 || series?.data.length <= dataIndex) {
468
+ return false;
469
+ }
470
+
471
+ return series.data[dataIndex];
472
+ },
473
+
474
+ /**
475
+ * Find graph item by position x and y
476
+ * @param {array} offset position x and y
477
+ * @param {boolean} useApproximate if it's true. it'll look for closed item on mouse position
478
+ *
479
+ * @returns {object} clicked item information
480
+ */
481
+ getItemByPosition(offset, useApproximate = false) {
482
+ const sIds = Object.keys(this.seriesList);
483
+ const isHorizontal = !!this.options.horizontal;
484
+
485
+ let maxl = null;
486
+ let maxp = null;
487
+ let maxg = null;
488
+ let maxSID = '';
489
+ let acc = 0;
490
+ let useStack = false;
491
+ let maxIndex = null;
492
+
493
+ for (let ix = 0; ix < sIds.length; ix++) {
494
+ const sId = sIds[ix];
495
+ const series = this.seriesList[sId];
496
+ const findFn = useApproximate ? series.findApproximateData : series.findGraphData;
497
+
498
+ if (findFn) {
499
+ const item = findFn.call(series, offset, isHorizontal);
500
+ const data = item.data;
501
+ const index = item.index;
502
+
503
+ if (data) {
504
+ const ldata = isHorizontal ? data.y : data.x;
505
+ const lp = isHorizontal ? data.yp : data.xp;
506
+
507
+ if (ldata !== null && ldata !== undefined) {
508
+ const g = isHorizontal ? data.o || data.x : data.o || data.y;
509
+
510
+ if (series.stackIndex) {
511
+ acc += !isNaN(data.o) ? data.o : 0;
512
+ useStack = true;
513
+ } else {
514
+ acc += data.y;
515
+ }
516
+
517
+ if (maxg === null || maxg <= g) {
518
+ maxg = g;
519
+ maxSID = sId;
520
+ maxl = ldata;
521
+ maxp = lp;
522
+ maxIndex = index;
523
+ }
524
+ }
525
+ }
526
+ }
527
+ }
528
+
529
+ return {
530
+ label: maxl,
531
+ pos: maxp,
532
+ value: maxg === null ? 0 : maxg,
533
+ sId: maxSID,
534
+ acc,
535
+ useStack,
536
+ maxIndex,
537
+ };
538
+ },
539
+
482
540
  /**
483
541
  * Create min/max information for all of data
484
542
  * @property seriesList
@@ -101,7 +101,7 @@ const modules = {
101
101
 
102
102
  if (selectItem.use) {
103
103
  const offset = this.getMousePosition(e);
104
- const hitInfo = this.findClickedData(offset, selectItem.useApproximateValue);
104
+ const hitInfo = this.getItemByPosition(offset, selectItem.useApproximateValue);
105
105
 
106
106
 
107
107
  if (hitInfo.label !== null) {
@@ -125,13 +125,18 @@ const modules = {
125
125
  const args = { e };
126
126
  if (this.options.selectItem.use) {
127
127
  const offset = this.getMousePosition(e);
128
- const hitInfo = this.findClickedData(offset);
128
+ const hitInfo = this.getItemByPosition(offset, false);
129
129
 
130
130
  if (hitInfo.label !== null) {
131
131
  this.render(hitInfo);
132
132
  }
133
133
 
134
- ({ label: args.label, value: args.value, sId: args.seriesId } = hitInfo);
134
+ ({
135
+ label: args.label,
136
+ value: args.value,
137
+ sId: args.seriesId,
138
+ maxIndex: args.dataIndex,
139
+ } = hitInfo);
135
140
  }
136
141
 
137
142
  if (typeof this.listeners.click === 'function') {
@@ -398,130 +403,21 @@ const modules = {
398
403
  },
399
404
 
400
405
  /**
401
- * Find clicked graph item on mouse position
402
- * @param {array} offset return value from getMousePosition()
403
- * @param {boolean} useApproximate if it's true. it'll look for closed item on mouse position
404
- *
405
- * @returns {object} clicked item information
406
- */
407
- findClickedData(offset, useApproximate) {
408
- const sIds = Object.keys(this.seriesList);
409
- const isHorizontal = !!this.options.horizontal;
410
-
411
- let maxl = null;
412
- let maxp = null;
413
- let maxg = null;
414
- let maxSID = '';
415
- let acc = 0;
416
- let useStack = false;
417
- let maxIndex = null;
418
-
419
- for (let ix = 0; ix < sIds.length; ix++) {
420
- const sId = sIds[ix];
421
- const series = this.seriesList[sId];
422
- const findFn = useApproximate ? series.findApproximateData : series.findGraphData;
423
-
424
- if (findFn) {
425
- const item = findFn.call(series, offset, isHorizontal);
426
- const data = item.data;
427
- const index = item.index;
428
-
429
- if (data) {
430
- const ldata = isHorizontal ? data.y : data.x;
431
- const lp = isHorizontal ? data.yp : data.xp;
432
-
433
- if (ldata !== null && ldata !== undefined) {
434
- const g = isHorizontal ? data.o || data.x : data.o || data.y;
435
-
436
- if (series.stackIndex) {
437
- acc += !isNaN(data.o) ? data.o : 0;
438
- useStack = true;
439
- } else {
440
- acc += data.y;
441
- }
442
-
443
- if (maxg === null || maxg <= g) {
444
- maxg = g;
445
- maxSID = sId;
446
- maxl = ldata;
447
- maxp = lp;
448
- maxIndex = index;
449
- }
450
- }
451
- }
452
- }
453
- }
454
-
455
- return {
456
- label: maxl,
457
- pos: maxp,
458
- value: maxg === null ? 0 : maxg,
459
- sId: maxSID,
460
- acc,
461
- useStack,
462
- maxIndex,
463
- };
464
- },
465
-
466
- /**
467
- * Find graph item by label entered from user
468
- * @param {any} label label value
469
406
  *
470
- * @returns {boolean} if it wasn't able to find it, return false. if not, return true and render.
407
+ * @param targetInfo
408
+ * @returns {boolean}
471
409
  */
472
- selectItemByLabel(label) {
473
- const findInfo = this.getItemByLabel(label);
410
+ selectItemByData(targetInfo) {
411
+ const foundInfo = this.getItem(targetInfo, false);
474
412
 
475
- if (findInfo) {
476
- this.render(findInfo);
413
+ if (foundInfo) {
414
+ this.render(foundInfo);
477
415
  } else {
478
416
  return false;
479
417
  }
480
418
 
481
419
  return true;
482
420
  },
483
- findHitItem2(offset) {
484
- const mouseX = offset[0];
485
- const mouseY = offset[1];
486
-
487
- const width = this.chartRect.chartWidth;
488
- const height = this.chartRect.chartHeight;
489
- const centerX = (width / 2) + this.chartRect.padding.left;
490
- const centerY = (height / 2) + this.chartRect.padding.top;
491
-
492
- const dx = mouseX - centerX;
493
- const dy = mouseY - centerY;
494
-
495
- let angle;
496
- angle = ((Math.atan2(-dy, -dx) * 180) / Math.PI) - 90;
497
- angle = angle < 0 ? 360 + angle : angle;
498
- const rad = ((angle * Math.PI) / 180) + (1.5 * Math.PI);
499
- const distance = Math.round(Math.sqrt((dx ** 2) + (dy ** 2)));
500
-
501
- const graphData = this.graphData;
502
- let gdata;
503
- let dsIndex = null;
504
- let sId = null;
505
-
506
- for (let ix = 0, ixLen = graphData.length; ix < ixLen; ix++) {
507
- gdata = graphData[ix];
508
- if (distance > gdata.ir && distance < gdata.or) {
509
- dsIndex = ix;
510
- }
511
- }
512
-
513
- if (graphData[dsIndex]) {
514
- for (let ix = 0, ixLen = graphData[dsIndex].data.length; ix < ixLen; ix++) {
515
- gdata = graphData[dsIndex].data[ix];
516
-
517
- if (rad > gdata.sa && rad < gdata.ea) {
518
- sId = gdata.id;
519
- }
520
- }
521
- }
522
-
523
- return { dsIndex, sId };
524
- },
525
421
 
526
422
  /**
527
423
  * Find items by series within a range
@@ -1,5 +1,5 @@
1
1
  import { ref, computed, getCurrentInstance, nextTick } from 'vue';
2
- import { defaultsDeep } from 'lodash-es';
2
+ import { cloneDeep, defaultsDeep } from 'lodash-es';
3
3
  import { getQuantity } from '@/common/utils';
4
4
 
5
5
  const DEFAULT_OPTIONS = {
@@ -97,7 +97,7 @@ const DEFAULT_DATA = {
97
97
  };
98
98
 
99
99
  export const useModel = () => {
100
- const { emit } = getCurrentInstance();
100
+ const { props, emit } = getCurrentInstance();
101
101
 
102
102
  const getNormalizedOptions = (options) => {
103
103
  const normalizedOptions = defaultsDeep({}, options, DEFAULT_OPTIONS);
@@ -110,9 +110,14 @@ export const useModel = () => {
110
110
  };
111
111
  const getNormalizedData = data => defaultsDeep(data, DEFAULT_DATA);
112
112
 
113
+ const selectInfo = cloneDeep(props.selectedItem);
114
+
113
115
  const eventListeners = {
114
116
  click: async (e) => {
115
117
  await nextTick();
118
+ if (e.label) {
119
+ emit('update:selectedItem', { seriesID: e.seriesId, dataIndex: e.dataIndex });
120
+ }
116
121
  emit('click', e);
117
122
  },
118
123
  'dbl-click': async (e) => {
@@ -127,6 +132,7 @@ export const useModel = () => {
127
132
 
128
133
  return {
129
134
  eventListeners,
135
+ selectInfo,
130
136
  getNormalizedData,
131
137
  getNormalizedOptions,
132
138
  };
@@ -1,4 +1,5 @@
1
1
  import { getCurrentInstance, nextTick } from 'vue';
2
+ import { cloneDeep } from 'lodash-es';
2
3
  import { numberWithComma } from '@/common/utils';
3
4
 
4
5
  export const commonFunctions = () => {
@@ -613,6 +614,7 @@ export const treeEvent = (params) => {
613
614
  }
614
615
  if (node.children) {
615
616
  node.hasChild = true;
617
+ node.children = cloneDeep(node.children);
616
618
  node.children.forEach(child =>
617
619
  setNodeData({
618
620
  node: child,