evui 3.3.4 → 3.3.7

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.
@@ -8,31 +8,11 @@
8
8
  disabled,
9
9
  }"
10
10
  >
11
- <template v-if="!multiple">
12
- <span
13
- v-if="!clearable || !isClearableIcon"
14
- class="ev-input-suffix"
15
- @click="clickSelectInput"
16
- >
17
- <i
18
- class="ev-input-suffix-arrow ev-icon-s-arrow-down"
19
- :class="{
20
- selected: isDropbox,
21
- }"
22
- />
23
- </span>
24
- <input
25
- v-model="selectedModel"
26
- type="text"
27
- class="ev-input"
28
- readonly
29
- :placeholder="computedPlaceholder"
30
- :disabled="disabled"
31
- @click="clickSelectInput"
32
- />
33
- </template>
34
- <template v-else>
35
- <div class="ev-select-tag-wrapper">
11
+ <div
12
+ ref="selectWrapper"
13
+ class="ev-select__wrapper"
14
+ >
15
+ <template v-if="!multiple">
36
16
  <span
37
17
  v-if="!clearable || !isClearableIcon"
38
18
  class="ev-input-suffix"
@@ -46,66 +26,90 @@
46
26
  />
47
27
  </span>
48
28
  <input
29
+ v-model="selectedModel"
49
30
  type="text"
50
- class="ev-input multiple"
31
+ class="ev-input"
51
32
  readonly
52
33
  :placeholder="computedPlaceholder"
53
34
  :disabled="disabled"
54
35
  @click="clickSelectInput"
55
36
  />
56
- <template v-if="!collapseTags">
57
- <div
58
- v-for="item in selectedModel"
59
- :key="item"
60
- class="ev-select-tag"
37
+ </template>
38
+ <template v-else>
39
+ <div class="ev-select-tag-wrapper">
40
+ <span
41
+ v-if="!clearable || !isClearableIcon"
42
+ class="ev-input-suffix"
43
+ @click="clickSelectInput"
61
44
  >
62
- <span class="ev-tag-name">
63
- {{ item.name }}
64
- </span>
65
- <span
66
- class="ev-tag-suffix"
67
- @click.stop="[removeMv(item.value), changeDropboxPosition()]"
45
+ <i
46
+ class="ev-input-suffix-arrow ev-icon-s-arrow-down"
47
+ :class="{
48
+ selected: isDropbox,
49
+ }"
50
+ />
51
+ </span>
52
+ <input
53
+ type="text"
54
+ class="ev-input multiple"
55
+ readonly
56
+ :placeholder="computedPlaceholder"
57
+ :disabled="disabled"
58
+ @click="clickSelectInput"
59
+ />
60
+ <template v-if="!collapseTags">
61
+ <div
62
+ v-for="item in selectedModel"
63
+ :key="item"
64
+ class="ev-select-tag"
68
65
  >
69
- <i class="ev-tag-suffix-close ev-icon-error" />
70
- </span>
71
- </div>
72
- </template>
73
- <template v-else>
74
- <div
75
- v-if="selectedModel.length"
76
- class="ev-select-tag"
77
- >
78
- <span class="ev-tag-name">
79
- {{ selectedModel[0].name }}
80
- </span>
81
- <span
82
- class="ev-tag-suffix"
83
- @click.stop="[removeMv(selectedModel[0].value), changeDropboxPosition()]"
66
+ <span class="ev-tag-name">
67
+ {{ item.name }}
68
+ </span>
69
+ <span
70
+ class="ev-tag-suffix"
71
+ @click.stop="[removeMv(item.value), changeDropboxPosition()]"
72
+ >
73
+ <i class="ev-tag-suffix-close ev-icon-error" />
74
+ </span>
75
+ </div>
76
+ </template>
77
+ <template v-else>
78
+ <div
79
+ v-if="selectedModel.length"
80
+ class="ev-select-tag"
84
81
  >
85
- <i class="ev-tag-suffix-close ev-icon-error" />
86
- </span>
87
- </div>
88
- <div
89
- v-if="selectedModel.length > 1"
90
- class="ev-select-tag num"
91
- >
92
- <span class="ev-tag-name">
93
- + {{ selectedModel.length - 1 }}
94
- </span>
95
- </div>
96
- </template>
97
- </div>
98
- </template>
99
- <template v-if="clearable">
100
- <span
101
- v-show="isClearableIcon"
102
- class="ev-input-suffix"
103
- @click.stop="[removeAllMv(), clickOutsideDropbox()]"
104
- >
105
- <i class="ev-icon-error" />
106
- </span>
107
- </template>
108
- <div class="ev-select-dropbox-wrapper">
82
+ <span class="ev-tag-name">
83
+ {{ selectedModel[0].name }}
84
+ </span>
85
+ <span
86
+ class="ev-tag-suffix"
87
+ @click.stop="[removeMv(selectedModel[0].value), changeDropboxPosition()]"
88
+ >
89
+ <i class="ev-tag-suffix-close ev-icon-error" />
90
+ </span>
91
+ </div>
92
+ <div
93
+ v-if="selectedModel.length > 1"
94
+ class="ev-select-tag num"
95
+ >
96
+ <span class="ev-tag-name">
97
+ + {{ selectedModel.length - 1 }}
98
+ </span>
99
+ </div>
100
+ </template>
101
+ </div>
102
+ </template>
103
+ <template v-if="clearable">
104
+ <span
105
+ v-show="isClearableIcon"
106
+ class="ev-input-suffix"
107
+ @click.stop="[removeAllMv(), clickOutsideDropbox()]"
108
+ >
109
+ <i class="ev-icon-error" />
110
+ </span>
111
+ </template>
112
+ <div class="ev-select-dropbox-wrapper">
109
113
  <div
110
114
  v-if="isDropbox"
111
115
  ref="dropbox"
@@ -153,6 +157,7 @@
153
157
  </div>
154
158
  </div>
155
159
  </div>
160
+ </div>
156
161
  </div>
157
162
  </template>
158
163
 
@@ -228,6 +233,7 @@ export default {
228
233
 
229
234
  const {
230
235
  select,
236
+ selectWrapper,
231
237
  dropbox,
232
238
  itemWrapper,
233
239
  isDropbox,
@@ -251,6 +257,7 @@ export default {
251
257
  removeAllMv,
252
258
 
253
259
  select,
260
+ selectWrapper,
254
261
  dropbox,
255
262
  itemWrapper,
256
263
  isDropbox,
@@ -271,21 +278,18 @@ export default {
271
278
  @import '../../style/index.scss';
272
279
 
273
280
  .ev-select {
274
- $select-height: 35px;
281
+ $select-height: $input-default-height;
275
282
  display: block;
276
283
  position: relative;
277
284
  width: 100%;
278
- min-height: $select-height;
279
285
  border-radius: $default-radius;
280
286
  cursor: pointer;
281
287
 
282
- &.disabled {
283
- background-color: #F5F7FA;
284
- border-color: #E4E7ED;
285
- color: #C0C4CC;
286
- }
287
-
288
288
  @import '../../style/components/input.scss';
289
+
290
+ &__wrapper {
291
+ position: relative;
292
+ }
289
293
  .ev-input {
290
294
  padding: 0 30px 0 15px;
291
295
  border: 1px solid #B2B2B2;
@@ -320,7 +324,6 @@ export default {
320
324
  }
321
325
 
322
326
  .ev-select-tag-wrapper {
323
- $select-height: 35px;
324
327
  display: flex;
325
328
  width: 100%;
326
329
  height: 100%;
@@ -371,7 +374,7 @@ export default {
371
374
  }
372
375
 
373
376
  .ev-select-dropbox {
374
- $select-height: 35px;
377
+ $select-height: $input-default-height;
375
378
  position: absolute;
376
379
  width: 100%;
377
380
  max-height: $select-height * 5;
@@ -107,6 +107,7 @@ export const useDropdown = (param) => {
107
107
  const isDropbox = ref(false);
108
108
  const filterTextRef = ref(props.filterText);
109
109
  const select = ref(null);
110
+ const selectWrapper = ref(null);
110
111
  const dropbox = ref(null);
111
112
  const itemWrapper = ref(null);
112
113
  const dropboxPosition = reactive({
@@ -131,8 +132,8 @@ export const useDropdown = (param) => {
131
132
  */
132
133
  const changeDropboxPosition = async () => {
133
134
  await nextTick();
134
- const selectHeight = select.value?.getBoundingClientRect().height;
135
- const selectY = select.value?.getBoundingClientRect().y;
135
+ const selectHeight = selectWrapper.value?.getBoundingClientRect().height;
136
+ const selectY = selectWrapper.value?.getBoundingClientRect().y;
136
137
  const dropboxHeight = dropbox.value?.getBoundingClientRect().height;
137
138
  const docHeight = document.documentElement.clientHeight;
138
139
  if (docHeight < selectY + selectHeight + dropboxHeight) {
@@ -245,6 +246,7 @@ export const useDropdown = (param) => {
245
246
 
246
247
  return {
247
248
  select,
249
+ selectWrapper,
248
250
  dropbox,
249
251
  itemWrapper,
250
252
  isDropbox,
@@ -162,7 +162,7 @@
162
162
  </template>
163
163
 
164
164
  <script>
165
- import { reactive, toRefs, computed, watch, onMounted, onActivated } from 'vue';
165
+ import { reactive, toRefs, computed, watch, onMounted, onActivated, nextTick } from 'vue';
166
166
  import treeGridNode from './TreeGridNode';
167
167
  import Toolbar from './treeGrid.toolbar';
168
168
  import {
@@ -229,29 +229,26 @@ export default {
229
229
  'check-all': null,
230
230
  },
231
231
  setup(props) {
232
- const {
233
- isRenderer,
234
- getComponentName,
235
- getConvertValue,
236
- getColumnIndex,
237
- setPixelUnit,
238
- } = commonFunctions();
239
232
  const elementInfo = reactive({
240
233
  body: null,
241
234
  header: null,
242
235
  resizeLine: null,
243
236
  'grid-wrapper': null,
244
237
  });
245
- const searchValue = computed(() => (props.option.searchValue || ''));
238
+ const filterInfo = reactive({
239
+ isSearch: false,
240
+ searchWord: '',
241
+ });
246
242
  const stores = reactive({
247
243
  treeStore: [],
248
244
  viewStore: [],
249
245
  filterStore: [],
250
- searchStore: computed(() => stores.treeStore.filter(item => item.isFilter)),
251
246
  treeRows: props.rows,
247
+ searchStore: computed(() => stores.treeStore.filter(item => item.isFilter)),
252
248
  showTreeStore: computed(() => stores.treeStore.filter(item => item.show)),
253
249
  orderedColumns: computed(() =>
254
250
  props.columns.map((column, index) => ({ index, ...column }))),
251
+ store: computed(() => (filterInfo.isSearch ? stores.searchStore : stores.treeStore)),
255
252
  });
256
253
  const checkInfo = reactive({
257
254
  prevCheckedRow: [],
@@ -259,6 +256,14 @@ export default {
259
256
  checkedRows: props.checked,
260
257
  useCheckbox: computed(() => props.option.useCheckbox || {}),
261
258
  });
259
+ const {
260
+ isRenderer,
261
+ getComponentName,
262
+ getConvertValue,
263
+ getColumnIndex,
264
+ setPixelUnit,
265
+ checkHeader,
266
+ } = commonFunctions({ checkInfo });
262
267
  const scrollInfo = reactive({
263
268
  lastScroll: {
264
269
  top: 0,
@@ -296,7 +301,6 @@ export default {
296
301
  borderStyle: computed(() => props.option.style?.border || ''),
297
302
  highlightIdx: computed(() => props.option.style?.highlight),
298
303
  });
299
-
300
304
  const {
301
305
  updateVScroll,
302
306
  updateHScroll,
@@ -311,7 +315,7 @@ export default {
311
315
  const {
312
316
  onCheck,
313
317
  onCheckAll,
314
- } = checkEvent({ checkInfo, stores });
318
+ } = checkEvent({ checkInfo, stores, checkHeader });
315
319
 
316
320
  const {
317
321
  calculatedColumn,
@@ -332,7 +336,7 @@ export default {
332
336
 
333
337
  const {
334
338
  onSearch,
335
- } = filterEvent({ checkInfo, stores, getConvertValue, onResize });
339
+ } = filterEvent({ stores, filterInfo, getConvertValue, onResize, checkHeader });
336
340
 
337
341
  onMounted(() => {
338
342
  stores.treeStore = setTreeNodeStore();
@@ -343,17 +347,14 @@ export default {
343
347
  watch(
344
348
  () => props.checked,
345
349
  (checkedList) => {
346
- let store = stores.treeStore;
347
- if (stores.searchStore.length > 0) {
348
- store = stores.searchStore;
349
- }
350
+ const store = stores.store;
350
351
  checkInfo.checkedRows = checkedList;
351
352
  checkInfo.isHeaderChecked = false;
352
353
  if (store.length) {
353
354
  store.forEach((row) => {
354
- row.checked = checkedList.includes(row);
355
+ row.checked = !!checkedList.find(c => c.index === row.index);
355
356
  });
356
- checkInfo.isHeaderChecked = store.every(n => n.checked === true);
357
+ checkHeader(store);
357
358
  }
358
359
  updateVScroll();
359
360
  },
@@ -379,10 +380,15 @@ export default {
379
380
  onResize();
380
381
  }, { deep: true },
381
382
  );
383
+ watch(
384
+ () => stores.treeStore.length,
385
+ () => {
386
+ checkHeader(stores.store);
387
+ },
388
+ );
382
389
  watch(
383
390
  () => [props.width, props.height, resizeInfo.adjust, props.option.columnWidth],
384
391
  (value) => {
385
- // columnWidth computed readonly
386
392
  resizeInfo.columnWidth = value[3];
387
393
  stores.orderedColumns.map((column) => {
388
394
  const item = column;
@@ -397,12 +403,13 @@ export default {
397
403
  },
398
404
  );
399
405
  watch(
400
- () => searchValue.value,
406
+ () => props.option.searchValue,
401
407
  (value) => {
402
- const searchWord = value?.value ?? value;
403
- if (searchWord) {
404
- onSearch(searchWord);
405
- }
408
+ nextTick(() => {
409
+ if (value !== undefined) {
410
+ onSearch(value?.value ?? value);
411
+ }
412
+ });
406
413
  }, { immediate: true },
407
414
  );
408
415
  const gridStyle = computed(() => ({
@@ -455,6 +462,7 @@ export default {
455
462
  ...toRefs(styleInfo),
456
463
  ...toRefs(elementInfo),
457
464
  ...toRefs(stores),
465
+ ...toRefs(filterInfo),
458
466
  ...toRefs(scrollInfo),
459
467
  ...toRefs(resizeInfo),
460
468
  ...toRefs(selectInfo),
@@ -14,6 +14,7 @@
14
14
  >
15
15
  <ev-checkbox
16
16
  v-model="node.checked"
17
+ :disabled="node._disabled"
17
18
  class="row-checkbox-input"
18
19
  @change="onCheck($event, node)"
19
20
  />
@@ -1,9 +1,9 @@
1
1
  import { getCurrentInstance, nextTick } from 'vue';
2
- import { cloneDeep } from 'lodash-es';
3
2
  import { numberWithComma } from '@/common/utils';
4
3
 
5
- export const commonFunctions = () => {
4
+ export const commonFunctions = (params) => {
6
5
  const { props } = getCurrentInstance();
6
+ const { checkInfo } = params;
7
7
  /**
8
8
  * 해당 컬럼이 사용자 지정 컬럼인지 확인한다.
9
9
  *
@@ -60,7 +60,24 @@ export const commonFunctions = () => {
60
60
  }
61
61
  return size;
62
62
  };
63
- return { isRenderer, getComponentName, getConvertValue, getColumnIndex, setPixelUnit };
63
+ const checkHeader = (rows) => {
64
+ checkInfo.isHeaderChecked = !!rows.length && rows.every(row => row.checked);
65
+ const disabledList = rows.filter(row => row._disabled);
66
+ if (disabledList.length) {
67
+ const checkedList = rows.filter(row => row.checked);
68
+ if (disabledList.length + checkedList.length === rows.length) {
69
+ checkInfo.isHeaderChecked = true;
70
+ }
71
+ }
72
+ };
73
+ return {
74
+ isRenderer,
75
+ getComponentName,
76
+ getConvertValue,
77
+ getColumnIndex,
78
+ setPixelUnit,
79
+ checkHeader,
80
+ };
64
81
  };
65
82
 
66
83
  export const scrollEvent = (params) => {
@@ -369,7 +386,7 @@ export const clickEvent = (params) => {
369
386
  };
370
387
 
371
388
  export const checkEvent = (params) => {
372
- const { checkInfo, stores } = params;
389
+ const { checkInfo, stores, checkHeader } = params;
373
390
  const { emit } = getCurrentInstance();
374
391
  /**
375
392
  * row에 대한 체크 상태를 해제한다.
@@ -388,13 +405,14 @@ export const checkEvent = (params) => {
388
405
  if (node.hasChild) {
389
406
  node.children.forEach((children) => {
390
407
  const childNode = children;
391
- if (node.checked && !childNode.checked) {
408
+ if (node.checked && !childNode.checked && !childNode._disabled) {
392
409
  checkInfo.checkedRows.push(childNode);
393
410
  }
394
411
  if (!node.checked) {
395
- checkInfo.checkedRows = checkInfo.checkedRows.filter(it => it.index !== childNode.index);
412
+ checkInfo.checkedRows = checkInfo.checkedRows
413
+ .filter(checked => checked.index !== childNode.index);
396
414
  }
397
- childNode.checked = node.checked;
415
+ childNode.checked = node.checked && !childNode._disabled;
398
416
 
399
417
  if (childNode.hasChild) {
400
418
  onCheckChildren(childNode);
@@ -406,9 +424,17 @@ export const checkEvent = (params) => {
406
424
  const parentNode = node.parent;
407
425
  if (parentNode) {
408
426
  const isCheck = parentNode.children.every(n => n.checked);
409
- parentNode.checked = isCheck;
427
+ parentNode.checked = isCheck && !parentNode._disabled;
428
+ const disabledList = parentNode.children.filter(n => n._disabled);
429
+ if (disabledList.length) {
430
+ const checkedList = parentNode.children.filter(n => n.checked);
431
+ if (disabledList.length + checkedList.length === parentNode.children.length) {
432
+ parentNode.checked = true;
433
+ }
434
+ }
410
435
  if (!parentNode.checked) {
411
- checkInfo.checkedRows = checkInfo.checkedRows.filter(it => it.index !== parentNode.index);
436
+ checkInfo.checkedRows = checkInfo.checkedRows
437
+ .filter(checked => checked.index !== parentNode.index);
412
438
  } else {
413
439
  checkInfo.checkedRows.push(parentNode);
414
440
  }
@@ -424,16 +450,9 @@ export const checkEvent = (params) => {
424
450
  * @param {array} rowData - row 데이터
425
451
  */
426
452
  const onCheck = (event, rowData) => {
427
- let store = stores.treeStore;
428
- if (stores.searchStore.length > 0) {
429
- store = stores.searchStore;
430
- }
453
+ const store = stores.store;
431
454
  const isSingleMode = () => checkInfo.useCheckbox.mode === 'single';
432
- const checkedHeader = (checkStore) => {
433
- const isCheck = checkStore.every(n => n.checked === true);
434
- checkInfo.isHeaderChecked = isCheck;
435
- };
436
- const unCheckedHeader = () => {
455
+ const unCheckHeader = () => {
437
456
  if (checkInfo.isHeaderChecked) {
438
457
  checkInfo.isHeaderChecked = false;
439
458
  }
@@ -464,9 +483,9 @@ export const checkEvent = (params) => {
464
483
  onSingleMode();
465
484
  if (rowData.checked) {
466
485
  addCheckedRow(rowData);
467
- checkedHeader(store);
486
+ checkHeader(store);
468
487
  } else {
469
- unCheckedHeader();
488
+ unCheckHeader();
470
489
  removeCheckedRow(rowData);
471
490
  onCheckChildren(rowData);
472
491
  onCheckParent(rowData);
@@ -482,27 +501,20 @@ export const checkEvent = (params) => {
482
501
  */
483
502
  const onCheckAll = (event) => {
484
503
  const status = checkInfo.isHeaderChecked;
485
- const checked = [];
486
- let item;
487
- let store = stores.treeStore;
488
- if (status && stores.searchStore.length > 0) {
489
- store = stores.searchStore;
490
- }
491
- for (let ix = 0; ix < store.length; ix++) {
492
- item = store[ix];
493
- if (status) {
494
- checked.push(item);
504
+ const store = stores.store;
505
+ store.forEach((row) => {
506
+ row.checked = status && !row._disabled;
507
+ if (row.checked) {
508
+ if (!checkInfo.checkedRows.find(checked => checked.index === row.index)) {
509
+ checkInfo.checkedRows.push(row);
510
+ }
511
+ } else {
512
+ checkInfo.checkedRows = checkInfo.checkedRows
513
+ .filter(checked => checked.index !== row.index);
495
514
  }
496
- item.checked = status;
497
- }
498
- checkInfo.checkedRows = checked;
499
- if (stores.searchStore.length > 0) {
500
- store.forEach((node) => {
501
- onCheckChildren(node);
502
- });
503
- }
504
- emit('update:checked', checked);
505
- emit('check-all', event, checked);
515
+ });
516
+ emit('update:checked', checkInfo.checkedRows);
517
+ emit('check-all', event, checkInfo.checkedRows);
506
518
  };
507
519
  return { onCheck, onCheckAll };
508
520
  };
@@ -614,7 +626,6 @@ export const treeEvent = (params) => {
614
626
  }
615
627
  if (node.children) {
616
628
  node.hasChild = true;
617
- node.children = cloneDeep(node.children);
618
629
  node.children.forEach(child =>
619
630
  setNodeData({
620
631
  node: child,
@@ -655,7 +666,7 @@ export const treeEvent = (params) => {
655
666
  };
656
667
 
657
668
  export const filterEvent = (params) => {
658
- const { checkInfo, stores, getConvertValue, onResize } = params;
669
+ const { stores, filterInfo, getConvertValue, onResize, checkHeader } = params;
659
670
  const makeParentShow = (data) => {
660
671
  if (!data?.parent) {
661
672
  return;
@@ -689,7 +700,9 @@ export const filterEvent = (params) => {
689
700
  clearTimeout(timer);
690
701
  }
691
702
  timer = setTimeout(() => {
692
- let store = stores.treeStore;
703
+ filterInfo.isSearch = false;
704
+ filterInfo.searchWord = searchWord;
705
+ const store = stores.treeStore;
693
706
  store.forEach((row) => {
694
707
  row.show = false;
695
708
  row.isFilter = false;
@@ -726,22 +739,18 @@ export const filterEvent = (params) => {
726
739
  makeParentShow(row);
727
740
  makeChildShow(row);
728
741
  });
742
+ filterInfo.isSearch = true;
729
743
  } else {
730
744
  store.forEach((row) => {
731
745
  row.show = true;
732
746
  row.isFilter = false;
733
747
  });
734
748
  store.forEach((row) => {
735
- if (row.hasChild) {
736
- makeChildShow(row);
737
- }
749
+ makeParentShow(row);
750
+ makeChildShow(row);
738
751
  });
739
752
  }
740
- if (stores.searchStore.length > 0) {
741
- store = stores.searchStore;
742
- }
743
- const isCheck = store.length > 0 && store.every(n => n.checked === true);
744
- checkInfo.isHeaderChecked = isCheck;
753
+ checkHeader(stores.store);
745
754
  onResize();
746
755
  }, 500);
747
756
  };