evui 3.2.3 → 3.3.3

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.2.3",
3
+ "version": "3.3.3",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <button
3
+ ref="buttonRef"
3
4
  class="ev-button"
4
5
  :class="{
5
6
  disabled,
@@ -17,6 +18,8 @@
17
18
  </template>
18
19
 
19
20
  <script>
21
+ import { onMounted, ref } from 'vue';
22
+
20
23
  export default {
21
24
  name: 'EvButton',
22
25
  props: {
@@ -52,6 +55,19 @@ export default {
52
55
  emits: {
53
56
  click: null,
54
57
  },
58
+ setup(props) {
59
+ const buttonRef = ref(null);
60
+
61
+ onMounted(() => {
62
+ if (props.autoFocus) {
63
+ buttonRef.value.focus();
64
+ }
65
+ });
66
+
67
+ return {
68
+ buttonRef,
69
+ };
70
+ },
55
71
  };
56
72
  </script>
57
73
 
@@ -105,13 +105,20 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
105
105
  }
106
106
  });
107
107
 
108
- const redrawChart = () => {
109
- if (evChart && 'resize' in evChart && isInit) {
110
- evChart.resize();
108
+ const redraw = () => {
109
+ if (evChart && isInit) {
110
+ evChart.update({
111
+ updateSeries: true,
112
+ updateSelTip: { update: true, keepDomain: false },
113
+ });
111
114
  }
112
115
  };
113
116
 
114
- const onResize = debounce(redrawChart, props.resizeTimeout);
117
+ const onResize = debounce(() => {
118
+ if (evChart && 'resize' in evChart && isInit) {
119
+ evChart.resize();
120
+ }
121
+ }, props.resizeTimeout);
115
122
 
116
123
  const selectItemByLabel = (label) => {
117
124
  evChart.selectItemByLabel(label);
@@ -121,6 +128,7 @@ import { onMounted, onBeforeUnmount, watch, onDeactivated } from 'vue';
121
128
  wrapper,
122
129
  wrapperStyle,
123
130
  onResize,
131
+ redraw,
124
132
  selectItemByLabel,
125
133
  };
126
134
  },
@@ -260,7 +260,7 @@ const modules = {
260
260
  const dragEnd = (e) => {
261
261
  const dragInfo = this.dragInfo;
262
262
 
263
- if (dragInfo?.isMove) {
263
+ if (dragInfo?.isMove && dragInfo?.width > 1 && dragInfo?.height > 1) {
264
264
  const args = {
265
265
  e,
266
266
  data: this.findSelectedItems(dragInfo),
@@ -581,10 +581,10 @@ const modules = {
581
581
  const yMax = dataRangeY.graphMin + graphHeight * (1 - yMaxRatio);
582
582
 
583
583
  return {
584
- xMin: +parseFloat(xMin).toFixed(3),
585
- xMax: +parseFloat(xMax).toFixed(3),
586
- yMin: +parseFloat(yMin).toFixed(3),
587
- yMax: +parseFloat(yMax).toFixed(3),
584
+ xMin: Math.max(+parseFloat(xMin).toFixed(3), dataRangeX.graphMin),
585
+ xMax: Math.min(+parseFloat(xMax).toFixed(3), dataRangeX.graphMax),
586
+ yMin: Math.max(+parseFloat(yMin).toFixed(3), dataRangeY.graphMin),
587
+ yMax: Math.min(+parseFloat(yMax).toFixed(3), dataRangeY.graphMax),
588
588
  };
589
589
  },
590
590
 
@@ -341,7 +341,7 @@ export default {
341
341
  items: [],
342
342
  },
343
343
  isSearch: false,
344
- searchWord: '',
344
+ searchValue: computed(() => (props.option.searchValue || '')),
345
345
  });
346
346
  const stores = reactive({
347
347
  viewStore: [],
@@ -471,7 +471,7 @@ export default {
471
471
  setStore(props.rows);
472
472
  });
473
473
  onActivated(() => {
474
- updateVScroll();
474
+ onResize();
475
475
  });
476
476
  const ROW_INDEX = 0;
477
477
  const ROW_CHECK_INDEX = 1;
@@ -512,7 +512,7 @@ export default {
512
512
  (value) => {
513
513
  setStore(value);
514
514
  if (filterInfo.isSearch) {
515
- onSearch(filterInfo.searchWord);
515
+ onSearch(filterInfo.searchValue);
516
516
  }
517
517
  },
518
518
  );
@@ -581,6 +581,15 @@ export default {
581
581
  setStore([], false);
582
582
  },
583
583
  );
584
+ watch(
585
+ () => filterInfo.searchValue,
586
+ (value) => {
587
+ const searchValue = value?.value ?? value;
588
+ if (searchValue) {
589
+ onSearch(searchValue);
590
+ }
591
+ }, { immediate: true },
592
+ );
584
593
  const isFilterButton = field => filterInfo.isFiltering && field !== 'db-icon' && field !== 'user-icon';
585
594
  return {
586
595
  showHeader,
@@ -208,7 +208,7 @@
208
208
  text-align: right;
209
209
  }
210
210
  &.string,
211
- &.stringnumber {
211
+ &.stringNumber {
212
212
  text-align: left;
213
213
  }
214
214
  &.center {
@@ -1,4 +1,4 @@
1
- import { getCurrentInstance } from 'vue';
1
+ import { getCurrentInstance, nextTick } from 'vue';
2
2
  import { isEqual, uniqBy } from 'lodash-es';
3
3
  import { numberWithComma } from '@/common/utils';
4
4
 
@@ -75,23 +75,25 @@ export const scrollEvent = (params) => {
75
75
  const updateVScroll = () => {
76
76
  const bodyEl = elementInfo.body;
77
77
  const rowHeight = resizeInfo.rowHeight;
78
- const rowCount = bodyEl.clientHeight > rowHeight
79
- ? Math.ceil(bodyEl.clientHeight / rowHeight) : stores.store.length;
80
- const totalScrollHeight = stores.store.length * rowHeight;
81
- let firstVisibleIndex = Math.floor(bodyEl.scrollTop / rowHeight);
82
- if (firstVisibleIndex > stores.store.length - 1) {
83
- firstVisibleIndex = 0;
84
- }
78
+ if (bodyEl) {
79
+ const rowCount = bodyEl.clientHeight > rowHeight
80
+ ? Math.ceil(bodyEl.clientHeight / rowHeight) : stores.store.length;
81
+ const totalScrollHeight = stores.store.length * rowHeight;
82
+ let firstVisibleIndex = Math.floor(bodyEl.scrollTop / rowHeight);
83
+ if (firstVisibleIndex > stores.store.length - 1) {
84
+ firstVisibleIndex = 0;
85
+ }
85
86
 
86
- const lastVisibleIndex = firstVisibleIndex + rowCount;
87
- const firstIndex = Math.max(firstVisibleIndex, 0);
88
- const lastIndex = lastVisibleIndex;
87
+ const lastVisibleIndex = firstVisibleIndex + rowCount;
88
+ const firstIndex = Math.max(firstVisibleIndex, 0);
89
+ const lastIndex = lastVisibleIndex;
89
90
 
90
- stores.viewStore = stores.store.slice(firstIndex, lastIndex);
91
- scrollInfo.hasVerticalScrollBar = rowCount < stores.store.length;
92
- scrollInfo.vScrollTopHeight = firstIndex * rowHeight;
93
- scrollInfo.vScrollBottomHeight = totalScrollHeight - (stores.viewStore.length * rowHeight)
94
- - scrollInfo.vScrollTopHeight;
91
+ stores.viewStore = stores.store.slice(firstIndex, lastIndex);
92
+ scrollInfo.hasVerticalScrollBar = rowCount < stores.store.length;
93
+ scrollInfo.vScrollTopHeight = firstIndex * rowHeight;
94
+ scrollInfo.vScrollBottomHeight = totalScrollHeight - (stores.viewStore.length * rowHeight)
95
+ - scrollInfo.vScrollTopHeight;
96
+ }
95
97
  };
96
98
  /**
97
99
  * 수평 스크롤의 위치 계산 후 적용한다.
@@ -114,7 +116,7 @@ export const scrollEvent = (params) => {
114
116
  const isHorizontal = !(scrollLeft === lastLeft);
115
117
  const isVertical = !(scrollTop === lastTop);
116
118
 
117
- if (isVertical && bodyEl.clientHeight) {
119
+ if (isVertical && bodyEl?.clientHeight) {
118
120
  updateVScroll();
119
121
  }
120
122
 
@@ -223,22 +225,24 @@ export const resizeEvent = (params) => {
223
225
  * grid resize 이벤트를 처리한다.
224
226
  */
225
227
  const onResize = () => {
226
- if (resizeInfo.adjust) {
227
- stores.orderedColumns.map((column) => {
228
- const item = column;
228
+ nextTick(() => {
229
+ if (resizeInfo.adjust) {
230
+ stores.orderedColumns.map((column) => {
231
+ const item = column;
229
232
 
230
- if (!props.columns[column.index].width && !item.resized) {
231
- item.width = 0;
232
- }
233
+ if (!props.columns[column.index].width && !item.resized) {
234
+ item.width = 0;
235
+ }
233
236
 
234
- return item;
235
- }, this);
236
- }
237
+ return item;
238
+ }, this);
239
+ }
237
240
 
238
- calculatedColumn();
239
- if (elementInfo.body?.clientHeight) {
240
- updateVScroll();
241
- }
241
+ calculatedColumn();
242
+ if (elementInfo.body?.clientHeight) {
243
+ updateVScroll();
244
+ }
245
+ });
242
246
  };
243
247
 
244
248
  const onShow = (isVisible) => {
@@ -496,21 +500,32 @@ export const sortEvent = (params) => {
496
500
  const index = getColumnIndex(sortInfo.sortField);
497
501
  const type = props.columns[index]?.type || 'string';
498
502
  const sortFn = sortInfo.sortOrder === 'desc' ? setDesc : setAsc;
499
- if (type === 'string') {
500
- stores.store.sort((a, b) => {
501
- if (typeof a[ROW_DATA_INDEX][index] === 'string') {
502
- return sortFn(a[ROW_DATA_INDEX][index]?.toLowerCase(),
503
- b[ROW_DATA_INDEX][index]?.toLowerCase());
504
- }
505
- return 0;
506
- });
507
- } else {
508
- stores.store.sort((a, b) => {
509
- if (typeof a[ROW_DATA_INDEX][index] === 'number') {
510
- return sortFn(a[ROW_DATA_INDEX][index], b[ROW_DATA_INDEX][index]);
511
- }
512
- return 0;
513
- });
503
+ switch (type) {
504
+ case 'string':
505
+ stores.store.sort((a, b) => {
506
+ if (typeof a[ROW_DATA_INDEX][index] === 'string') {
507
+ return sortFn(a[ROW_DATA_INDEX][index]?.toLowerCase(),
508
+ b[ROW_DATA_INDEX][index]?.toLowerCase());
509
+ }
510
+ return 0;
511
+ });
512
+ break;
513
+ case 'stringNumber':
514
+ stores.store.sort((a, b) => {
515
+ if (typeof a[ROW_DATA_INDEX][index] === 'string' || typeof a[ROW_DATA_INDEX][index] === 'number') {
516
+ return sortFn(Number(a[ROW_DATA_INDEX][index]), Number(b[ROW_DATA_INDEX][index]));
517
+ }
518
+ return 0;
519
+ });
520
+ break;
521
+ default:
522
+ stores.store.sort((a, b) => {
523
+ if (typeof a[ROW_DATA_INDEX][index] === 'number' || typeof a[ROW_DATA_INDEX][index] === 'boolean') {
524
+ return sortFn(a[ROW_DATA_INDEX][index], b[ROW_DATA_INDEX][index]);
525
+ }
526
+ return 0;
527
+ });
528
+ break;
514
529
  }
515
530
  };
516
531
  return { onSort, setSort };
@@ -710,7 +725,6 @@ export const filterEvent = (params) => {
710
725
  }
711
726
  timer = setTimeout(() => {
712
727
  filterInfo.isSearch = false;
713
- filterInfo.searchWord = searchWord;
714
728
  if (searchWord) {
715
729
  stores.searchStore = stores.store.filter((row) => {
716
730
  let isShow = false;
@@ -737,14 +751,12 @@ export const filterEvent = (params) => {
737
751
  }
738
752
  const store = stores.store;
739
753
  let checkedCount = 0;
740
- for (let ix = 0; ix < store.length; ix++) {
741
- if (checkInfo.checkedIndex.has(store[ix][ROW_INDEX])) {
742
- store[ix][ROW_CHECK_INDEX] = true;
754
+ store.forEach((row) => {
755
+ row[ROW_CHECK_INDEX] = checkInfo.checkedRows.includes(row[ROW_DATA_INDEX]);
756
+ if (row[ROW_CHECK_INDEX]) {
743
757
  checkedCount += 1;
744
- } else {
745
- store[ix][ROW_CHECK_INDEX] = false;
746
758
  }
747
- }
759
+ });
748
760
  if (store.length && store.length === checkedCount) {
749
761
  checkInfo.isHeaderChecked = true;
750
762
  } else {
@@ -14,11 +14,12 @@
14
14
  ref="msgRef"
15
15
  class="ev-message-box"
16
16
  :class="{
17
- [`type-${type}`]: !!type,
18
- 'show-close': showClose,
19
- 'has-icon': !!iconClass,
20
- 'has-title': !!title,
21
- }"
17
+ [`type-${type}`]: !!type,
18
+ 'show-close': showClose,
19
+ 'has-icon': !!iconClass,
20
+ 'has-title': !!title,
21
+ }"
22
+ tabindex="-1"
22
23
  >
23
24
  <span
24
25
  v-if="iconClass"
@@ -52,6 +53,7 @@
52
53
  v-if="showCancelBtn"
53
54
  size="small"
54
55
  class="ev-message-box-cancel"
56
+ :auto-focus="hasFocus('cancelBtn')"
55
57
  @click="closeMsg('cancel')"
56
58
  >
57
59
  {{ cancelBtnText }}
@@ -61,6 +63,7 @@
61
63
  type="primary"
62
64
  size="small"
63
65
  class="ev-message-box-confirm"
66
+ :auto-focus="hasFocus('confirmBtn')"
64
67
  @click="closeMsg('ok')"
65
68
  >
66
69
  {{ confirmBtnText }}
@@ -80,7 +83,7 @@
80
83
  </template>
81
84
 
82
85
  <script>
83
- import { reactive, toRefs, watch, onMounted } from 'vue';
86
+ import { reactive, toRefs, watch, onMounted, ref } from 'vue';
84
87
  import EvButton from '@/components/button/Button.vue';
85
88
 
86
89
  export default {
@@ -142,8 +145,14 @@ export default {
142
145
  type: Function,
143
146
  default: null,
144
147
  },
148
+ focusable: {
149
+ type: Boolean,
150
+ default: false,
151
+ },
145
152
  },
146
153
  setup(props) {
154
+ const msgRef = ref(null);
155
+
147
156
  const state = reactive({
148
157
  isShow: true,
149
158
  iconClass: '',
@@ -190,9 +199,27 @@ export default {
190
199
  }
191
200
  };
192
201
 
202
+ const hasFocus = (type) => {
203
+ if (!props.focusable) return false;
204
+
205
+ switch (type) {
206
+ case 'confirmBtn':
207
+ return props.showConfirmBtn;
208
+ case 'cancelBtn':
209
+ return !props.showConfirmBtn && props.showCancelBtn;
210
+ case 'messagebox':
211
+ return !props.showConfirmBtn && !props.showCancelBtn;
212
+ default:
213
+ return false;
214
+ }
215
+ };
216
+
193
217
  onMounted(() => {
194
218
  setState();
195
219
  document.addEventListener('keydown', keydown);
220
+ if (hasFocus('messagebox')) {
221
+ msgRef.value.focus();
222
+ }
196
223
  });
197
224
  watch(() => state.isShow, (val) => {
198
225
  if (!val) {
@@ -202,6 +229,8 @@ export default {
202
229
  return {
203
230
  closeMsg,
204
231
  ...toRefs(state),
232
+ msgRef,
233
+ hasFocus,
205
234
  };
206
235
  },
207
236
  };
@@ -242,6 +242,7 @@ export default {
242
242
  resizeLine: null,
243
243
  'grid-wrapper': null,
244
244
  });
245
+ const searchValue = computed(() => (props.option.searchValue || ''));
245
246
  const stores = reactive({
246
247
  treeStore: [],
247
248
  viewStore: [],
@@ -331,13 +332,13 @@ export default {
331
332
 
332
333
  const {
333
334
  onSearch,
334
- } = filterEvent({ checkInfo, stores, getConvertValue, calculatedColumn, updateVScroll });
335
+ } = filterEvent({ checkInfo, stores, getConvertValue, onResize });
335
336
 
336
337
  onMounted(() => {
337
338
  stores.treeStore = setTreeNodeStore();
338
339
  });
339
340
  onActivated(() => {
340
- updateVScroll();
341
+ onResize();
341
342
  });
342
343
  watch(
343
344
  () => props.checked,
@@ -376,7 +377,6 @@ export default {
376
377
  stores.treeRows = newData;
377
378
  stores.treeStore = setTreeNodeStore();
378
379
  onResize();
379
- updateVScroll();
380
380
  }, { deep: true },
381
381
  );
382
382
  watch(
@@ -396,6 +396,15 @@ export default {
396
396
  onResize();
397
397
  },
398
398
  );
399
+ watch(
400
+ () => searchValue.value,
401
+ (value) => {
402
+ const searchWord = value?.value ?? value;
403
+ if (searchWord) {
404
+ onSearch(searchWord);
405
+ }
406
+ }, { immediate: true },
407
+ );
399
408
  const gridStyle = computed(() => ({
400
409
  width: resizeInfo.gridWidth,
401
410
  height: resizeInfo.gridHeight,
@@ -198,7 +198,7 @@
198
198
  text-align: right;
199
199
  }
200
200
  &.string,
201
- &.stringnumber {
201
+ &.stringNumber {
202
202
  text-align: left;
203
203
  }
204
204
  &.center {
@@ -70,24 +70,26 @@ export const scrollEvent = (params) => {
70
70
  const updateVScroll = () => {
71
71
  const store = stores.showTreeStore;
72
72
  const bodyEl = elementInfo.body;
73
- const rowHeight = resizeInfo.rowHeight;
74
- const rowCount = bodyEl.clientHeight > rowHeight
75
- ? Math.ceil(bodyEl.clientHeight / rowHeight) : store.length;
76
- const totalScrollHeight = store.length * rowHeight;
77
- let firstVisibleIndex = Math.floor(bodyEl.scrollTop / rowHeight);
78
- if (firstVisibleIndex > store.length - 1) {
79
- firstVisibleIndex = 0;
80
- }
73
+ if (bodyEl) {
74
+ const rowHeight = resizeInfo.rowHeight;
75
+ const rowCount = bodyEl.clientHeight > rowHeight
76
+ ? Math.ceil(bodyEl.clientHeight / rowHeight) : store.length;
77
+ const totalScrollHeight = store.length * rowHeight;
78
+ let firstVisibleIndex = Math.floor(bodyEl.scrollTop / rowHeight);
79
+ if (firstVisibleIndex > store.length - 1) {
80
+ firstVisibleIndex = 0;
81
+ }
81
82
 
82
- const lastVisibleIndex = firstVisibleIndex + rowCount;
83
- const firstIndex = Math.max(firstVisibleIndex, 0);
84
- const lastIndex = lastVisibleIndex;
83
+ const lastVisibleIndex = firstVisibleIndex + rowCount;
84
+ const firstIndex = Math.max(firstVisibleIndex, 0);
85
+ const lastIndex = lastVisibleIndex;
85
86
 
86
- stores.viewStore = store.slice(firstIndex, lastIndex);
87
- scrollInfo.hasVerticalScrollBar = rowCount < store.length;
88
- scrollInfo.vScrollTopHeight = firstIndex * rowHeight;
89
- scrollInfo.vScrollBottomHeight = totalScrollHeight - (stores.viewStore.length * rowHeight)
90
- - scrollInfo.vScrollTopHeight;
87
+ stores.viewStore = store.slice(firstIndex, lastIndex);
88
+ scrollInfo.hasVerticalScrollBar = rowCount < store.length;
89
+ scrollInfo.vScrollTopHeight = firstIndex * rowHeight;
90
+ scrollInfo.vScrollBottomHeight = totalScrollHeight - (stores.viewStore.length * rowHeight)
91
+ - scrollInfo.vScrollTopHeight;
92
+ }
91
93
  };
92
94
  /**
93
95
  * 수평 스크롤의 위치 계산 후 적용한다.
@@ -110,7 +112,7 @@ export const scrollEvent = (params) => {
110
112
  const isHorizontal = !(scrollLeft === lastLeft);
111
113
  const isVertical = !(scrollTop === lastTop);
112
114
 
113
- if (isVertical) {
115
+ if (isVertical && bodyEl.clientHeight) {
114
116
  updateVScroll();
115
117
  }
116
118
 
@@ -223,22 +225,25 @@ export const resizeEvent = (params) => {
223
225
  /**
224
226
  * grid resize 이벤트를 처리한다.
225
227
  */
226
- const onResize = async () => {
227
- await nextTick();
228
- if (resizeInfo.adjust) {
229
- stores.orderedColumns.map((column) => {
230
- const item = column;
231
-
232
- if (!props.columns[column.index].width && !item.resized) {
233
- item.width = 0;
234
- }
228
+ const onResize = () => {
229
+ nextTick(() => {
230
+ if (resizeInfo.adjust) {
231
+ stores.orderedColumns.map((column) => {
232
+ const item = column;
233
+
234
+ if (!props.columns[column.index].width && !item.resized) {
235
+ item.width = 0;
236
+ }
235
237
 
236
- return item;
237
- }, this);
238
- }
238
+ return item;
239
+ }, this);
240
+ }
239
241
 
240
- calculatedColumn();
241
- updateVScroll();
242
+ calculatedColumn();
243
+ if (elementInfo.body?.clientHeight) {
244
+ updateVScroll();
245
+ }
246
+ });
242
247
  };
243
248
  const onShow = (isVisible) => {
244
249
  if (isVisible) {
@@ -603,7 +608,7 @@ export const treeEvent = (params) => {
603
608
 
604
609
  nodeList.push(node);
605
610
 
606
- if (typeof parent !== 'undefined') {
611
+ if (!Object.hasOwnProperty.call(node, 'parent')) {
607
612
  node.parent = parent;
608
613
  }
609
614
  if (node.children) {
@@ -648,7 +653,7 @@ export const treeEvent = (params) => {
648
653
  };
649
654
 
650
655
  export const filterEvent = (params) => {
651
- const { checkInfo, stores, getConvertValue, calculatedColumn, updateVScroll } = params;
656
+ const { checkInfo, stores, getConvertValue, onResize } = params;
652
657
  const makeParentShow = (data) => {
653
658
  if (!data?.parent) {
654
659
  return;
@@ -735,8 +740,7 @@ export const filterEvent = (params) => {
735
740
  }
736
741
  const isCheck = store.length > 0 && store.every(n => n.checked === true);
737
742
  checkInfo.isHeaderChecked = isCheck;
738
- calculatedColumn();
739
- updateVScroll();
743
+ onResize();
740
744
  }, 500);
741
745
  };
742
746
  return { onSearch };
@@ -433,8 +433,8 @@ const useMouseEvent = (param) => {
433
433
  setDragStyle({
434
434
  top: `${tempTop}px`,
435
435
  left: `${tempLeft}px`,
436
- width: props.width,
437
- height: props.height,
436
+ width: dragStyle.width ?? props.width,
437
+ height: dragStyle.height ?? props.height,
438
438
  });
439
439
  } else if (props.resizable && clickedInfo.pressedSpot === 'border') {
440
440
  resizeWindow(e);