evui 3.4.57 → 3.4.59

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.4.57",
3
+ "version": "3.4.59",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -95,6 +95,7 @@
95
95
  const injectIsChartGroup = inject('isChartGroup', false);
96
96
  const injectBrushSeries = inject('brushSeries', { list: [], chartIdx: null });
97
97
  const injectGroupSelectedLabel = inject('groupSelectedLabel', null);
98
+ const injectGroupHoveredLabel = inject('groupHoveredLabel', null);
98
99
  const injectBrushIdx = inject('brushIdx', { start: 0, end: -1 });
99
100
  const injectEvChartPropsInGroup = inject('evChartPropsInGroup', []);
100
101
 
@@ -105,7 +106,7 @@
105
106
  selectSeriesInfo,
106
107
  getNormalizedData,
107
108
  getNormalizedOptions,
108
- } = useModel(injectGroupSelectedLabel);
109
+ } = useModel(injectGroupSelectedLabel, injectGroupHoveredLabel);
109
110
 
110
111
  const normalizedData = getNormalizedData(props.data);
111
112
  const normalizedOptions = getNormalizedOptions(props.options);
@@ -278,6 +279,19 @@
278
279
  });
279
280
  });
280
281
 
282
+ watch(() => injectGroupHoveredLabel?.value, (newHoveredLabel) => {
283
+ if (!newHoveredLabel) {
284
+ return;
285
+ }
286
+ if (props.options.syncHover !== false) {
287
+ if (newHoveredLabel.label == null) {
288
+ evChart.overlayClear();
289
+ } else {
290
+ evChart.drawSyncedIndicator(newHoveredLabel);
291
+ }
292
+ }
293
+ }, { deep: true, flush: 'post' });
294
+
281
295
  onMounted(async () => {
282
296
  if (injectEvChartPropsInGroup?.value) {
283
297
  injectEvChartPropsInGroup.value.push(props);
@@ -170,6 +170,10 @@ class EvChart {
170
170
  this.chartRect = this.getChartRect();
171
171
  }
172
172
 
173
+ drawSyncedIndicator({ horizontal, label, mousePosition }) {
174
+ this.drawSyncedIndicator({ horizontal, label, mousePosition });
175
+ }
176
+
173
177
  /**
174
178
  * To draw canvas chart, it processes several sequential jobs
175
179
  * @param {any} [hitInfo=undefined] from mousemove callback (object or object[] of undefined)
@@ -61,6 +61,12 @@ const modules = {
61
61
 
62
62
  if (indicator.use && type !== 'pie' && type !== 'scatter' && type !== 'heatMap') {
63
63
  this.drawIndicator(offset, indicator.color);
64
+ const label = this.getTimeLabel(offset);
65
+ args.hoveredLabel = {
66
+ horizontal: this.options.horizontal,
67
+ label,
68
+ mousePosition: [e.clientX, e.clientY],
69
+ };
64
70
  }
65
71
 
66
72
  if (typeof this.listeners['mouse-move'] === 'function') {
@@ -92,6 +98,7 @@ const modules = {
92
98
  if (tooltip.use) {
93
99
  this.tooltipClear();
94
100
  }
101
+ this.listeners['mouse-leave']();
95
102
  };
96
103
 
97
104
  /**
@@ -1,5 +1,6 @@
1
1
  import { convertToPercent } from '@/common/utils';
2
2
  import debounce from '@/common/utils.debounce';
3
+ import { inRange } from 'lodash-es';
3
4
  import Canvas from '../helpers/helpers.canvas';
4
5
  import Util from '../helpers/helpers.util';
5
6
 
@@ -778,6 +779,94 @@ const modules = {
778
779
  ctx.closePath();
779
780
  }
780
781
  },
782
+ /**
783
+ * Get hovered axis label with mousemove
784
+ * @param {object} offset mousemove callback
785
+ *
786
+ * @returns {number | null} hovered axis label
787
+ */
788
+ getTimeLabel(offset) {
789
+ const options = this.options;
790
+
791
+ if (
792
+ options.syncHover === false
793
+ || (!options.horizontal && !options.axesX.every(({ type }) => type === 'time'))
794
+ || (options.horizontal && !options.axesY.every(({ type }) => type === 'time'))) {
795
+ return null;
796
+ }
797
+
798
+ const fromTime = +this.data.labels?.[0];
799
+ const toTime = +this.data.labels?.[this.data.labels.length - 1];
800
+ if (fromTime == null || toTime == null) {
801
+ return null;
802
+ }
803
+
804
+ const [offsetX, offsetY] = offset;
805
+ const graphPos = {
806
+ x1: this.chartRect.x1 + this.labelOffset.left,
807
+ x2: this.chartRect.x2 - this.labelOffset.right,
808
+ y1: this.chartRect.y1 + this.labelOffset.top,
809
+ y2: this.chartRect.y2 - this.labelOffset.bottom,
810
+ };
811
+
812
+ if (options.horizontal) {
813
+ const chartHeight = graphPos.y2 - graphPos.y1;
814
+ const hoverYAxis = offsetY - graphPos.y1;
815
+ const hoverTime = ((hoverYAxis * (toTime - fromTime)) / chartHeight) + fromTime;
816
+ return Math.round(hoverTime);
817
+ }
818
+ const chartWidth = graphPos.x2 - graphPos.x1;
819
+ const hoverXAxis = offsetX - graphPos.x1;
820
+ const hoverTime = ((hoverXAxis * (toTime - fromTime)) / chartWidth) + fromTime;
821
+ return Math.round(hoverTime);
822
+ },
823
+ /**
824
+ * Draw chart indicator with other grouped chart's mousemove
825
+ * @param {object} hoveredLabel chart direction and hovered axis label
826
+ *
827
+ * @returns {undefined}
828
+ */
829
+ drawSyncedIndicator({ horizontal, label, mousePosition }) {
830
+ if (!mousePosition || !!horizontal !== !!this.options.horizontal) {
831
+ return;
832
+ }
833
+ if (
834
+ this.options.syncHover === false
835
+ || (!horizontal && !this.options.axesX.every(({ type }) => type === 'time'))
836
+ || (horizontal && !this.options.axesY.every(({ type }) => type === 'time'))) {
837
+ return;
838
+ }
839
+ const fromTime = +this.data.labels?.[0];
840
+ const toTime = +this.data.labels?.[this.data.labels.length - 1];
841
+ if (fromTime == null || toTime == null) {
842
+ return;
843
+ }
844
+ const [clientX, clientY] = mousePosition;
845
+ const { top, bottom, left, right } = this.chartDOM.getBoundingClientRect();
846
+
847
+ const isHoveredChart = inRange(clientX, left, right) && inRange(clientY, bottom, top);
848
+ if (isHoveredChart) {
849
+ return;
850
+ }
851
+
852
+ this.overlayClear();
853
+ const graphPos = {
854
+ x1: this.chartRect.x1 + this.labelOffset.left,
855
+ x2: this.chartRect.x2 - this.labelOffset.right,
856
+ y1: this.chartRect.y1 + this.labelOffset.top,
857
+ y2: this.chartRect.y2 - this.labelOffset.bottom,
858
+ };
859
+
860
+ if (horizontal) {
861
+ const chartHeight = graphPos.y2 - graphPos.y1;
862
+ const offsetY = (chartHeight * (label - fromTime)) / (toTime - fromTime) + graphPos.y1;
863
+ this.drawIndicator([graphPos.x2, offsetY], this.options.indicator.color);
864
+ } else {
865
+ const chartWidth = graphPos.x2 - graphPos.x1;
866
+ const offsetX = (chartWidth * (label - fromTime)) / (toTime - fromTime) + graphPos.x1;
867
+ this.drawIndicator([offsetX, graphPos.y2], this.options.indicator.color);
868
+ }
869
+ },
781
870
 
782
871
  /**
783
872
  * Clear tooltip canvas
@@ -253,7 +253,7 @@ const DEFAULT_DATA = {
253
253
  data: {},
254
254
  };
255
255
 
256
- export const useModel = (injectGroupSelectedLabel) => {
256
+ export const useModel = (injectGroupSelectedLabel, injectGroupHoveredLabel) => {
257
257
  const { props, emit } = getCurrentInstance();
258
258
 
259
259
  const getNormalizedOptions = (options) => {
@@ -338,9 +338,17 @@ export const useModel = (injectGroupSelectedLabel) => {
338
338
  emit('drag-select', e);
339
339
  },
340
340
  'mouse-move': async (e) => {
341
+ if (injectGroupHoveredLabel?.value) {
342
+ injectGroupHoveredLabel.value = e.hoveredLabel;
343
+ }
341
344
  await nextTick();
342
345
  emit('mouse-move', e);
343
346
  },
347
+ 'mouse-leave': () => {
348
+ if (injectGroupHoveredLabel?.value) {
349
+ injectGroupHoveredLabel.value.label = null;
350
+ }
351
+ },
344
352
  };
345
353
 
346
354
  return {
@@ -18,7 +18,7 @@
18
18
  </template>
19
19
 
20
20
  <script>
21
- import { onMounted, watch, provide, toRef, computed } from 'vue';
21
+ import { onMounted, ref, watch, provide, toRef, computed } from 'vue';
22
22
  import evChartToolbar from '../chart/ChartToolbar';
23
23
  import { useGroupModel } from './uses';
24
24
  import { useZoomModel } from '../chart/uses';
@@ -50,6 +50,7 @@ export default {
50
50
  'update:groupSelectedLabel',
51
51
  'update:zoomStartIdx',
52
52
  'update:zoomEndIdx',
53
+ 'update:groupHoveredLabel',
53
54
  ],
54
55
  setup(props, { emit }) {
55
56
  const {
@@ -70,6 +71,18 @@ export default {
70
71
  set: val => emit('update:groupSelectedLabel', val),
71
72
  });
72
73
  provide('groupSelectedLabel', groupSelectedLabel);
74
+ const groupHoveredLabel = ref(null);
75
+ provide('groupHoveredLabel', groupHoveredLabel);
76
+
77
+ watch(() => props.options.syncHover, (newSyncHover) => {
78
+ if (newSyncHover) {
79
+ groupHoveredLabel.value = { label: '', horizontal: false };
80
+ } else {
81
+ groupHoveredLabel.value = null;
82
+ }
83
+ }, {
84
+ immediate: true,
85
+ });
73
86
 
74
87
  const {
75
88
  evChartZoomOptions,
@@ -8,10 +8,7 @@
8
8
  disabled,
9
9
  }"
10
10
  >
11
- <div
12
- ref="selectWrapper"
13
- class="ev-select__wrapper"
14
- >
11
+ <div ref="selectWrapper" class="ev-select__wrapper">
15
12
  <template v-if="!multiple">
16
13
  <span
17
14
  v-if="!clearable || !isClearableIcon"
@@ -75,24 +72,20 @@
75
72
  </div>
76
73
  </template>
77
74
  <template v-else>
78
- <div
79
- v-if="selectedModel.length"
80
- class="ev-select-tag"
81
- >
75
+ <div v-if="selectedModel.length" class="ev-select-tag">
82
76
  <span class="ev-tag-name">
83
77
  {{ selectedModel[0].name }}
84
78
  </span>
85
79
  <span
86
80
  class="ev-tag-suffix"
87
- @click.stop="[removeMv(selectedModel[0].value), changeDropboxPosition()]"
81
+ @click.stop="
82
+ [removeMv(selectedModel[0].value), changeDropboxPosition()]
83
+ "
88
84
  >
89
85
  <i class="ev-tag-suffix-close ev-icon-error" />
90
86
  </span>
91
87
  </div>
92
- <div
93
- v-if="selectedModel.length > 1"
94
- class="ev-select-tag num"
95
- >
88
+ <div v-if="selectedModel.length > 1" class="ev-select-tag num">
96
89
  <span class="ev-tag-name">
97
90
  + {{ selectedModel.length - 1 }}
98
91
  </span>
@@ -114,7 +107,7 @@
114
107
  v-if="isDropbox"
115
108
  ref="dropbox"
116
109
  class="ev-select-dropbox"
117
- :style="dropboxPosition"
110
+ :style="[dropboxPosition, { width: dropboxWidth }]"
118
111
  >
119
112
  <template v-if="filterable">
120
113
  <slot
@@ -140,9 +133,11 @@
140
133
  v-if="multiple"
141
134
  class="ev-select-dropbox-item all-check"
142
135
  :class="{
143
- selected: allCheck
144
- }"
145
- @click.self.prevent="[changeAllCheck(false), changeDropboxPosition()]"
136
+ selected: allCheck,
137
+ }"
138
+ @click.self.prevent="
139
+ [changeAllCheck(false), changeDropboxPosition()]
140
+ "
146
141
  >
147
142
  <ev-checkbox
148
143
  v-model="allCheck"
@@ -150,38 +145,30 @@
150
145
  @change="[changeAllCheck(true), changeDropboxPosition()]"
151
146
  />
152
147
  </div>
153
- <div
154
- ref="itemWrapper"
155
- class="ev-select-dropbox-list"
156
- >
148
+ <div ref="itemWrapper" class="ev-select-dropbox-list">
157
149
  <template v-if="multiple">
158
- <ev-checkbox-group
159
- v-model="mv"
160
- >
161
- <ul
162
- v-if="filteredItems.length"
163
- class="ev-select-dropbox-ul"
164
- >
150
+ <ev-checkbox-group v-model="mv">
151
+ <ul v-if="filteredItems.length" class="ev-select-dropbox-ul">
165
152
  <li
166
153
  v-for="(item, idx) in filteredItems"
167
154
  :key="`${item.value}_${idx}`"
168
155
  class="ev-select-dropbox-item"
169
156
  :class="{
170
157
  selected: selectedItemClass(item.value),
171
- disabled: item.disabled
172
- }"
158
+ disabled: item.disabled,
159
+ }"
173
160
  :title="item.name"
174
- @click.self.prevent="item.disabled
175
- ? [] : [clickItem(item.value), changeDropboxPosition()]"
161
+ @click.self.prevent="
162
+ item.disabled
163
+ ? []
164
+ : [clickItem(item.value), changeDropboxPosition()]
165
+ "
176
166
  >
177
167
  <ev-checkbox
178
168
  :label="item.value"
179
169
  :disabled="item.disabled"
180
170
  >
181
- <i
182
- v-if="item.iconClass"
183
- :class="item.iconClass"
184
- />
171
+ <i v-if="item.iconClass" :class="item.iconClass" />
185
172
  {{ item.name }}
186
173
  </ev-checkbox>
187
174
  </li>
@@ -194,30 +181,27 @@
194
181
  </ev-checkbox-group>
195
182
  </template>
196
183
  <template v-else>
197
- <ul
198
- v-if="filteredItems.length"
199
- class="ev-select-dropbox-ul"
200
- >
184
+ <ul v-if="filteredItems.length" class="ev-select-dropbox-ul">
201
185
  <li
202
186
  v-for="(item, idx) in filteredItems"
203
187
  :key="`${item.value}_${idx}`"
204
188
  class="ev-select-dropbox-item"
205
189
  :class="{
206
- selected: selectedItemClass(item.value),
207
- disabled: item.disabled
208
- }"
190
+ selected: selectedItemClass(item.value),
191
+ disabled: item.disabled,
192
+ }"
209
193
  :title="item.name"
210
- @click.stop.prevent="item.disabled
211
- ? [] : [clickItem(item.value), changeDropboxPosition()]"
194
+ @click.stop.prevent="
195
+ item.disabled
196
+ ? []
197
+ : [clickItem(item.value), changeDropboxPosition()]
198
+ "
212
199
  >
213
200
  <ev-checkbox
214
201
  :model-value="mv === item.value"
215
202
  :disabled="item.disabled"
216
203
  >
217
- <i
218
- v-if="item.iconClass"
219
- :class="item.iconClass"
220
- />
204
+ <i v-if="item.iconClass" :class="item.iconClass" />
221
205
  {{ item.name }}
222
206
  </ev-checkbox>
223
207
  </li>
@@ -226,30 +210,24 @@
226
210
  </div>
227
211
  </template>
228
212
  <template v-else>
229
- <div
230
- ref="itemWrapper"
231
- class="ev-select-dropbox-list"
232
- >
233
- <ul
234
- v-if="filteredItems.length"
235
- class="ev-select-dropbox-ul"
236
- >
213
+ <div ref="itemWrapper" class="ev-select-dropbox-list">
214
+ <ul v-if="filteredItems.length" class="ev-select-dropbox-ul">
237
215
  <li
238
216
  v-for="(item, idx) in filteredItems"
239
217
  :key="`${item.value}_${idx}`"
240
218
  class="ev-select-dropbox-item"
241
219
  :class="{
242
220
  selected: selectedItemClass(item.value),
243
- disabled: item.disabled
221
+ disabled: item.disabled,
244
222
  }"
245
223
  :title="item.name"
246
- @click.stop.prevent="item.disabled
247
- ? [] : [clickItem(item.value), changeDropboxPosition()]"
224
+ @click.stop.prevent="
225
+ item.disabled
226
+ ? []
227
+ : [clickItem(item.value), changeDropboxPosition()]
228
+ "
248
229
  >
249
- <i
250
- v-if="item.iconClass"
251
- :class="item.iconClass"
252
- />
230
+ <i v-if="item.iconClass" :class="item.iconClass" />
253
231
  {{ item.name }}
254
232
  </li>
255
233
  </ul>
@@ -367,6 +345,7 @@ export default {
367
345
  selectedItemClass,
368
346
  allCheck,
369
347
  changeAllCheck,
348
+ dropboxWidth,
370
349
  } = useDropdown({ mv, changeMv });
371
350
 
372
351
  return {
@@ -394,6 +373,7 @@ export default {
394
373
  selectedItemClass,
395
374
  allCheck,
396
375
  changeAllCheck,
376
+ dropboxWidth,
397
377
  };
398
378
  },
399
379
  };
@@ -452,7 +432,7 @@ export default {
452
432
  display: flex;
453
433
  width: 100%;
454
434
  height: 100%;
455
- padding: 3px 0;
435
+ padding: 3px 30px 3px 0;
456
436
  min-height: $select-height;
457
437
  flex-wrap: wrap;
458
438
  align-items: center;
@@ -463,6 +443,7 @@ export default {
463
443
  .ev-select-tag {
464
444
  display: flex;
465
445
  position: relative;
446
+ max-width: 100%;
466
447
  height: 24px;
467
448
  padding: 0 19px 0 8px;
468
449
  margin: 2px 0 2px 6px;
@@ -478,6 +459,12 @@ export default {
478
459
  padding-right: 8px;
479
460
  }
480
461
 
462
+ .ev-tag-name {
463
+ overflow: hidden;
464
+ text-overflow: ellipsis;
465
+ white-space: nowrap;
466
+ }
467
+
481
468
  .ev-tag-suffix {
482
469
  display: flex;
483
470
  position: absolute;
@@ -506,7 +493,7 @@ export default {
506
493
  background-color: #FCFCFC;
507
494
  border: 1px solid #E4E7ED;
508
495
  color: #606266;
509
- box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
496
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
510
497
  border-radius: 4px;
511
498
  z-index: 100;
512
499
  cursor: pointer;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  ref, reactive, computed, watch,
3
- nextTick, getCurrentInstance,
3
+ nextTick, getCurrentInstance, onMounted, onUnmounted,
4
4
  } from 'vue';
5
5
  import {
6
6
  getRegExp,
@@ -115,6 +115,8 @@ export const useDropdown = (param) => {
115
115
  const selectWrapper = ref(null);
116
116
  const dropbox = ref(null);
117
117
  const itemWrapper = ref(null);
118
+ const dropboxWidth = ref('100%');
119
+ const initialDropboxWidth = ref(null);
118
120
  const dropboxPosition = reactive({
119
121
  top: 0,
120
122
  });
@@ -185,15 +187,57 @@ export const useDropdown = (param) => {
185
187
  }
186
188
  };
187
189
 
190
+ const calculateDropboxWidth = async () => {
191
+ if (itemWrapper.value && dropbox.value) {
192
+ await nextTick();
193
+
194
+ if (initialDropboxWidth.value === null) {
195
+ initialDropboxWidth.value = dropbox.value.offsetWidth;
196
+ }
197
+
198
+ const items = itemWrapper.value.querySelectorAll('.ev-select-dropbox-item');
199
+ let maxWidth = 0;
200
+ items.forEach((item) => {
201
+ const itemWidth = item.scrollWidth;
202
+ if (itemWidth > maxWidth) {
203
+ maxWidth = itemWidth;
204
+ }
205
+ });
206
+
207
+ const windowWidth = window.innerWidth;
208
+ const dropboxRect = dropbox.value.getBoundingClientRect();
209
+ const dropboxLeft = dropboxRect.left;
210
+ const rightMargin = Math.max(windowWidth - (dropboxLeft + maxWidth), 10);
211
+ const maxAllowedWidth = windowWidth - dropboxLeft - rightMargin - 10;
212
+
213
+ const finalWidth = Math.max(Math.min(maxWidth, maxAllowedWidth), initialDropboxWidth.value);
214
+
215
+ dropboxWidth.value = `${Math.max(finalWidth, 100)}px`;
216
+ } else {
217
+ dropboxWidth.value = '100%';
218
+ }
219
+ };
220
+
221
+
188
222
  watch(
189
223
  () => isDropbox.value,
190
- (cur) => {
224
+ async (cur) => {
191
225
  if (cur) {
192
- scrollToSelectedItem();
226
+ await scrollToSelectedItem();
227
+ await calculateDropboxWidth();
193
228
  }
194
229
  },
195
230
  );
196
231
 
232
+ watch(
233
+ () => filteredItems.value,
234
+ async () => {
235
+ await changeDropboxPosition();
236
+ await calculateDropboxWidth();
237
+ },
238
+ );
239
+
240
+
197
241
  if (props.filterable) {
198
242
  watch(
199
243
  () => filteredItems.value,
@@ -286,6 +330,14 @@ export const useDropdown = (param) => {
286
330
  }
287
331
  });
288
332
 
333
+ onMounted(() => {
334
+ window.addEventListener('resize', calculateDropboxWidth);
335
+ });
336
+
337
+ onUnmounted(() => {
338
+ window.removeEventListener('resize', calculateDropboxWidth);
339
+ });
340
+
289
341
  return {
290
342
  select,
291
343
  selectWrapper,
@@ -303,5 +355,6 @@ export const useDropdown = (param) => {
303
355
  selectedItemClass,
304
356
  allCheck,
305
357
  changeAllCheck,
358
+ dropboxWidth,
306
359
  };
307
360
  };