evui 3.4.8 → 3.4.10
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/dist/evui.common.js +4756 -4499
- package/dist/evui.common.js.map +1 -1
- package/dist/evui.umd.js +4756 -4499
- package/dist/evui.umd.js.map +1 -1
- package/dist/evui.umd.min.js +1 -1
- package/dist/evui.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/components/chart/chart.core.js +0 -5
- package/src/components/chart/plugins/plugins.interaction.js +5 -3
- package/src/components/chart/plugins/plugins.scrollbar.js +17 -23
- package/src/components/chart/scale/scale.js +7 -2
- package/src/components/grid/Grid.vue +29 -20
- package/src/components/grid/grid.filterSetting.vue +27 -4
- package/src/components/grid/uses.js +115 -75
- package/src/components/treeGrid/TreeGrid.vue +32 -1
- package/src/components/treeGrid/TreeGridNode.vue +63 -8
- package/src/components/treeGrid/style/treeGrid.scss +14 -1
- package/src/components/treeGrid/uses.js +4 -2
|
@@ -332,7 +332,7 @@ export const resizeEvent = (params) => {
|
|
|
332
332
|
|
|
333
333
|
export const clickEvent = (params) => {
|
|
334
334
|
const { emit } = getCurrentInstance();
|
|
335
|
-
const { selectInfo, stores
|
|
335
|
+
const { selectInfo, stores } = params;
|
|
336
336
|
const getClickedRowData = (event, row) => {
|
|
337
337
|
const tagName = event.target.tagName.toLowerCase();
|
|
338
338
|
let cellInfo = {};
|
|
@@ -355,7 +355,7 @@ export const clickEvent = (params) => {
|
|
|
355
355
|
* @param {object} event - 이벤트 객체
|
|
356
356
|
* @param {array} row - row 데이터
|
|
357
357
|
*/
|
|
358
|
-
let
|
|
358
|
+
let clickTimer = null;
|
|
359
359
|
let lastIndex = -1;
|
|
360
360
|
const onRowClick = (event, row, isRight) => {
|
|
361
361
|
if (event.target.parentElement.classList?.contains('row-checkbox-input')) {
|
|
@@ -395,8 +395,10 @@ export const clickEvent = (params) => {
|
|
|
395
395
|
}
|
|
396
396
|
};
|
|
397
397
|
|
|
398
|
-
|
|
399
|
-
|
|
398
|
+
if (clickTimer) {
|
|
399
|
+
clearTimeout(clickTimer);
|
|
400
|
+
}
|
|
401
|
+
clickTimer = setTimeout(() => {
|
|
400
402
|
if (selectInfo.useSelect) {
|
|
401
403
|
const rowData = row[ROW_DATA_INDEX];
|
|
402
404
|
const selected = row[ROW_SELECT_INDEX];
|
|
@@ -423,7 +425,6 @@ export const clickEvent = (params) => {
|
|
|
423
425
|
lastIndex = row[ROW_INDEX];
|
|
424
426
|
emit('update:selected', selectInfo.selectedRow);
|
|
425
427
|
emit('click-row', getClickedRowData(event, row));
|
|
426
|
-
setContextMenu();
|
|
427
428
|
}
|
|
428
429
|
}, 100);
|
|
429
430
|
return true;
|
|
@@ -435,7 +436,9 @@ export const clickEvent = (params) => {
|
|
|
435
436
|
* @param {array} row - row 데이터
|
|
436
437
|
*/
|
|
437
438
|
const onRowDblClick = (event, row) => {
|
|
438
|
-
|
|
439
|
+
if (clickTimer) {
|
|
440
|
+
clearTimeout(clickTimer);
|
|
441
|
+
}
|
|
439
442
|
emit('dblclick-row', getClickedRowData(event, row));
|
|
440
443
|
};
|
|
441
444
|
return { onRowClick, onRowDblClick };
|
|
@@ -523,7 +526,7 @@ export const checkEvent = (params) => {
|
|
|
523
526
|
};
|
|
524
527
|
|
|
525
528
|
export const sortEvent = (params) => {
|
|
526
|
-
const { sortInfo, stores,
|
|
529
|
+
const { sortInfo, stores, updatePagingInfo } = params;
|
|
527
530
|
function OrderQueue() {
|
|
528
531
|
this.orders = ['asc', 'desc', 'init'];
|
|
529
532
|
this.dequeue = () => this.orders.shift();
|
|
@@ -539,6 +542,7 @@ export const sortEvent = (params) => {
|
|
|
539
542
|
const onSort = (column, sortOrder) => {
|
|
540
543
|
const sortable = column.sortable === undefined ? true : column.sortable;
|
|
541
544
|
if (sortable) {
|
|
545
|
+
sortInfo.sortColumn = column;
|
|
542
546
|
if (sortInfo.sortField !== column?.field) {
|
|
543
547
|
order.orders = ['asc', 'desc', 'init'];
|
|
544
548
|
sortInfo.sortField = column?.field;
|
|
@@ -574,8 +578,8 @@ export const sortEvent = (params) => {
|
|
|
574
578
|
});
|
|
575
579
|
return;
|
|
576
580
|
}
|
|
577
|
-
const index =
|
|
578
|
-
const type =
|
|
581
|
+
const index = sortInfo.sortColumn.index;
|
|
582
|
+
const type = sortInfo.sortColumn.type || 'string';
|
|
579
583
|
const sortFn = sortInfo.sortOrder === 'desc' ? setDesc : setAsc;
|
|
580
584
|
const numberSortFn = sortInfo.sortOrder === 'desc' ? numberSetDesc : numberSetAsc;
|
|
581
585
|
const getColumnValue = (a, b) => {
|
|
@@ -641,25 +645,26 @@ export const filterEvent = (params) => {
|
|
|
641
645
|
/**
|
|
642
646
|
* 전달받은 문자열 내 해당 키워드가 존재하는지 확인한다.
|
|
643
647
|
*
|
|
644
|
-
* @param {string}
|
|
645
|
-
* @param {string}
|
|
648
|
+
* @param {string} conditionValue - 검색 키워드
|
|
649
|
+
* @param {string} value - 기준 문자열
|
|
650
|
+
* @param {string} pos - 시작, 끝나는 문자열
|
|
646
651
|
* @returns {boolean} 문자열 내 키워드 존재 유무
|
|
647
652
|
*/
|
|
648
|
-
const findLike = (
|
|
649
|
-
if (typeof
|
|
653
|
+
const findLike = (conditionValue, value, pos) => {
|
|
654
|
+
if (typeof conditionValue !== 'string' || value === null) {
|
|
650
655
|
return false;
|
|
651
656
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
let
|
|
657
|
+
const baseValueLower = value.toLowerCase();
|
|
658
|
+
const conditionValueLower = conditionValue.toLowerCase();
|
|
659
|
+
let result = baseValueLower.includes(conditionValueLower);
|
|
655
660
|
if (pos) {
|
|
656
661
|
if (pos === 'start') {
|
|
657
|
-
|
|
662
|
+
result = baseValueLower.startsWith(conditionValueLower);
|
|
658
663
|
} else if (pos === 'end') {
|
|
659
|
-
|
|
664
|
+
result = baseValueLower.endsWith(conditionValueLower);
|
|
660
665
|
}
|
|
661
666
|
}
|
|
662
|
-
return
|
|
667
|
+
return result;
|
|
663
668
|
};
|
|
664
669
|
/**
|
|
665
670
|
* 필터 조건에 따라 문자열을 확인한다.
|
|
@@ -671,20 +676,23 @@ export const filterEvent = (params) => {
|
|
|
671
676
|
const stringFilter = (item, condition) => {
|
|
672
677
|
const comparison = condition.comparison;
|
|
673
678
|
const conditionValue = condition.value;
|
|
674
|
-
|
|
679
|
+
let value = item[ROW_DATA_INDEX][condition.index];
|
|
680
|
+
if (value || value === 0) {
|
|
681
|
+
value = `${item[ROW_DATA_INDEX][condition.index]}`;
|
|
682
|
+
}
|
|
675
683
|
let result;
|
|
676
684
|
if (comparison === '=') {
|
|
677
685
|
result = conditionValue.toLowerCase() === value.toLowerCase();
|
|
678
686
|
} else if (comparison === '!=') {
|
|
679
687
|
result = conditionValue.toLowerCase() !== value.toLowerCase();
|
|
680
688
|
} else if (comparison === '%s%') {
|
|
681
|
-
result = findLike(
|
|
689
|
+
result = findLike(conditionValue, value);
|
|
682
690
|
} else if (comparison === 'notLike') {
|
|
683
|
-
result = !findLike(
|
|
691
|
+
result = !findLike(conditionValue, value);
|
|
684
692
|
} else if (comparison === 's%') {
|
|
685
|
-
result = findLike(
|
|
693
|
+
result = findLike(conditionValue, value, 'start');
|
|
686
694
|
} else if (comparison === '%s') {
|
|
687
|
-
result = findLike(
|
|
695
|
+
result = findLike(conditionValue, value, 'end');
|
|
688
696
|
} else if (comparison === 'isEmpty') {
|
|
689
697
|
result = value === undefined || value === null || value === '';
|
|
690
698
|
} else if (comparison === 'isNotEmpty') {
|
|
@@ -723,13 +731,25 @@ export const filterEvent = (params) => {
|
|
|
723
731
|
} else if (comparison === '!=') {
|
|
724
732
|
result = value !== conditionValue;
|
|
725
733
|
} else if (comparison === 'isEmpty') {
|
|
726
|
-
result = value === undefined || value === null;
|
|
734
|
+
result = value === undefined || value === null || isNaN(value);
|
|
727
735
|
} else if (comparison === 'isNotEmpty') {
|
|
728
736
|
result = !!value;
|
|
729
737
|
}
|
|
730
738
|
|
|
731
739
|
return result;
|
|
732
740
|
};
|
|
741
|
+
const booleanFilter = (item, condition) => {
|
|
742
|
+
const comparison = condition.comparison;
|
|
743
|
+
const conditionValue = condition.value;
|
|
744
|
+
const value = `${item[ROW_DATA_INDEX][condition.index]}`;
|
|
745
|
+
let result;
|
|
746
|
+
|
|
747
|
+
if (comparison === '=') {
|
|
748
|
+
result = value === conditionValue;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return result;
|
|
752
|
+
};
|
|
733
753
|
/**
|
|
734
754
|
* 필터 조건이 적용된 데이터를 반환한다.
|
|
735
755
|
*
|
|
@@ -739,60 +759,67 @@ export const filterEvent = (params) => {
|
|
|
739
759
|
* @returns {boolean} 확인 결과
|
|
740
760
|
*/
|
|
741
761
|
const getFilteringData = (data, columnType, condition) => {
|
|
742
|
-
|
|
762
|
+
let filterFn = columnType === 'string' || columnType === 'stringNumber'
|
|
743
763
|
? stringFilter : numberFilter;
|
|
764
|
+
if (columnType === 'boolean') {
|
|
765
|
+
filterFn = booleanFilter;
|
|
766
|
+
}
|
|
744
767
|
return data.filter(row => filterFn(row, condition, columnType)) || [];
|
|
745
768
|
};
|
|
746
769
|
/**
|
|
747
770
|
* 전체 데이터에서 설정된 필터 적용 후 결과를 filterStore 에 저장한다.
|
|
748
771
|
*/
|
|
749
772
|
const setFilter = () => {
|
|
750
|
-
let filterStore = [];
|
|
751
|
-
let isApply = false;
|
|
752
|
-
|
|
753
773
|
const filteringItemsByColumn = filterInfo.filteringItemsByColumn;
|
|
754
774
|
const fields = Object.keys(filteringItemsByColumn);
|
|
755
775
|
const originStore = stores.originStore;
|
|
776
|
+
let filterStore = [];
|
|
777
|
+
let filteredOnce = false;
|
|
756
778
|
|
|
757
779
|
fields.forEach((field) => {
|
|
758
780
|
const filters = filteringItemsByColumn[field];
|
|
759
781
|
const index = getColumnIndex(field);
|
|
760
782
|
const columnType = props.columns[index].type;
|
|
761
783
|
|
|
762
|
-
filters.forEach((
|
|
763
|
-
|
|
764
|
-
if (!filterStore.length && Object.keys(filteringItemsByColumn).length < 2) {
|
|
784
|
+
filters.forEach((item, ix) => {
|
|
785
|
+
if (!filterStore.length && !filteredOnce) {
|
|
765
786
|
filterStore = getFilteringData(originStore, columnType, {
|
|
766
|
-
...
|
|
787
|
+
...item,
|
|
767
788
|
index,
|
|
768
789
|
});
|
|
769
|
-
} else if (
|
|
790
|
+
} else if (ix === 0 && filterInfo.columnOperator === 'or') {
|
|
770
791
|
filterStore.push(...getFilteringData(originStore, columnType, {
|
|
771
|
-
...
|
|
792
|
+
...item,
|
|
793
|
+
index,
|
|
794
|
+
}));
|
|
795
|
+
} else if (item.operator === 'or') {
|
|
796
|
+
filterStore.push(...getFilteringData(originStore, columnType, {
|
|
797
|
+
...item,
|
|
772
798
|
index,
|
|
773
799
|
}));
|
|
774
800
|
} else {
|
|
775
801
|
filterStore = getFilteringData(filterStore, columnType, {
|
|
776
|
-
...
|
|
802
|
+
...item,
|
|
777
803
|
index,
|
|
778
804
|
});
|
|
779
805
|
}
|
|
806
|
+
filteredOnce = true;
|
|
780
807
|
});
|
|
781
808
|
});
|
|
782
809
|
|
|
783
|
-
if (!
|
|
810
|
+
if (!filteredOnce) {
|
|
784
811
|
stores.filterStore = originStore;
|
|
785
812
|
} else {
|
|
786
813
|
stores.filterStore = uniqBy(filterStore, JSON.stringify);
|
|
787
814
|
}
|
|
788
815
|
};
|
|
789
816
|
|
|
790
|
-
let
|
|
817
|
+
let searchTimer = null;
|
|
791
818
|
const onSearch = (searchWord) => {
|
|
792
|
-
if (
|
|
793
|
-
clearTimeout(
|
|
819
|
+
if (searchTimer) {
|
|
820
|
+
clearTimeout(searchTimer);
|
|
794
821
|
}
|
|
795
|
-
|
|
822
|
+
searchTimer = setTimeout(() => {
|
|
796
823
|
filterInfo.isSearch = false;
|
|
797
824
|
filterInfo.searchWord = searchWord;
|
|
798
825
|
if (searchWord) {
|
|
@@ -861,35 +888,65 @@ export const contextMenuEvent = (params) => {
|
|
|
861
888
|
/**
|
|
862
889
|
* 컨텍스트 메뉴를 설정한다.
|
|
863
890
|
*
|
|
864
|
-
* @param {
|
|
891
|
+
* @param {object} event - 이벤트 객체
|
|
865
892
|
*/
|
|
866
|
-
|
|
893
|
+
let contextmenuTimer = null;
|
|
894
|
+
const setContextMenu = (e) => {
|
|
895
|
+
if (contextmenuTimer) {
|
|
896
|
+
clearTimeout(contextmenuTimer);
|
|
897
|
+
}
|
|
867
898
|
const menuItems = [];
|
|
899
|
+
contextmenuTimer = setTimeout(() => {
|
|
900
|
+
if (contextInfo.customContextMenu.length) {
|
|
901
|
+
const customItems = contextInfo.customContextMenu.map(
|
|
902
|
+
(item) => {
|
|
903
|
+
const menuItem = item;
|
|
904
|
+
if (menuItem.validate) {
|
|
905
|
+
menuItem.disabled = !menuItem.validate(menuItem.itemId, selectInfo.selectedRow);
|
|
906
|
+
}
|
|
868
907
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
(item) => {
|
|
872
|
-
const menuItem = item;
|
|
873
|
-
if (menuItem.validate) {
|
|
874
|
-
menuItem.disabled = !menuItem.validate(menuItem.itemId, selectInfo.selectedRow);
|
|
875
|
-
}
|
|
908
|
+
menuItem.selectedRow = selectInfo.selectedRow ?? [];
|
|
909
|
+
menuItem.contextmenuInfo = selectInfo.contextmenuInfo ?? [];
|
|
876
910
|
|
|
877
|
-
|
|
878
|
-
|
|
911
|
+
return menuItem;
|
|
912
|
+
});
|
|
879
913
|
|
|
880
|
-
|
|
881
|
-
|
|
914
|
+
menuItems.push(...customItems);
|
|
915
|
+
}
|
|
882
916
|
|
|
883
|
-
menuItems
|
|
917
|
+
contextInfo.contextMenuItems = menuItems;
|
|
918
|
+
contextInfo.menu.show(e);
|
|
919
|
+
}, 200);
|
|
920
|
+
};
|
|
921
|
+
/**
|
|
922
|
+
* 마우스 우클릭 이벤트를 처리한다.
|
|
923
|
+
*
|
|
924
|
+
* @param {object} event - 이벤트 객체
|
|
925
|
+
*/
|
|
926
|
+
const onContextMenu = (e) => {
|
|
927
|
+
e.preventDefault();
|
|
928
|
+
const target = e.target;
|
|
929
|
+
const rowIndex = target.closest('.row')?.dataset?.index;
|
|
930
|
+
let clickedRow = null;
|
|
931
|
+
if (rowIndex) {
|
|
932
|
+
clickedRow = stores.viewStore.find(row => row[ROW_INDEX] === +rowIndex)?.[ROW_DATA_INDEX];
|
|
933
|
+
}
|
|
934
|
+
if (clickedRow) {
|
|
935
|
+
selectInfo.contextmenuInfo = [clickedRow];
|
|
936
|
+
setContextMenu(e);
|
|
884
937
|
}
|
|
885
|
-
|
|
886
|
-
contextInfo.contextMenuItems = menuItems;
|
|
887
938
|
};
|
|
939
|
+
/**
|
|
940
|
+
* 컬럼 기능을 수행하는 Contextmenu 를 생성한다.
|
|
941
|
+
*
|
|
942
|
+
* @param {object} event - 이벤트 객체
|
|
943
|
+
* @param {object} column - 컬럼 정보
|
|
944
|
+
*/
|
|
888
945
|
const onColumnContextMenu = (event, column) => {
|
|
889
946
|
if (event.target.className === 'column-name') {
|
|
890
947
|
const sortable = column.sortable === undefined ? true : column.sortable;
|
|
891
948
|
const filterable = filterInfo.isFiltering
|
|
892
|
-
|
|
949
|
+
&& column.filterable === undefined ? true : column.filterable;
|
|
893
950
|
contextInfo.columnMenuItems = [
|
|
894
951
|
{
|
|
895
952
|
text: 'Ascending',
|
|
@@ -933,23 +990,6 @@ export const contextMenuEvent = (params) => {
|
|
|
933
990
|
];
|
|
934
991
|
}
|
|
935
992
|
};
|
|
936
|
-
/**
|
|
937
|
-
* 마우스 우클릭 이벤트를 처리한다.
|
|
938
|
-
*
|
|
939
|
-
* @param {object} event - 이벤트 객체
|
|
940
|
-
*/
|
|
941
|
-
const onContextMenu = (event) => {
|
|
942
|
-
const target = event.target;
|
|
943
|
-
const rowIndex = target.closest('.row')?.dataset?.index;
|
|
944
|
-
let clickedRow = null;
|
|
945
|
-
if (rowIndex) {
|
|
946
|
-
clickedRow = stores.viewStore.find(row => row[ROW_INDEX] === +rowIndex)?.[ROW_DATA_INDEX];
|
|
947
|
-
}
|
|
948
|
-
if (clickedRow) {
|
|
949
|
-
selectInfo.contextmenuInfo = [clickedRow];
|
|
950
|
-
// setContextMenu();
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
993
|
return {
|
|
954
994
|
setContextMenu,
|
|
955
995
|
onContextMenu,
|
|
@@ -54,7 +54,10 @@
|
|
|
54
54
|
<li
|
|
55
55
|
v-if="useCheckbox.use"
|
|
56
56
|
:class="headerCheckboxClass"
|
|
57
|
-
:style="
|
|
57
|
+
:style="{
|
|
58
|
+
width: `${minWidth}px`,
|
|
59
|
+
'border-right': '1px solid #CFCFCF',
|
|
60
|
+
}"
|
|
58
61
|
>
|
|
59
62
|
<ev-checkbox
|
|
60
63
|
v-if="isHeaderCheckbox"
|
|
@@ -92,6 +95,21 @@
|
|
|
92
95
|
/>
|
|
93
96
|
</li>
|
|
94
97
|
</template>
|
|
98
|
+
<!-- Row Contextmenu Column -->
|
|
99
|
+
<li
|
|
100
|
+
v-if="$props.option.customContextMenu?.length"
|
|
101
|
+
:class="{
|
|
102
|
+
column: true,
|
|
103
|
+
'non-border': !!borderStyle,
|
|
104
|
+
}"
|
|
105
|
+
:style="{
|
|
106
|
+
width: '30px',
|
|
107
|
+
'min-width': '30px',
|
|
108
|
+
'margin-right': (hasVerticalScrollBar || hasHorizontalScrollBar)
|
|
109
|
+
? `${scrollWidth}px` : '0px',
|
|
110
|
+
}"
|
|
111
|
+
>
|
|
112
|
+
</li>
|
|
95
113
|
</ul>
|
|
96
114
|
</div>
|
|
97
115
|
<!-- Body -->
|
|
@@ -120,6 +138,8 @@
|
|
|
120
138
|
:collapse-icon="option.collapseIcon"
|
|
121
139
|
:parent-icon="option.parentIcon"
|
|
122
140
|
:child-icon="option.childIcon"
|
|
141
|
+
:custom-context-menu="customContextMenu"
|
|
142
|
+
:menu-ref="menu"
|
|
123
143
|
:is-resize="isResize"
|
|
124
144
|
:row-height="rowHeight"
|
|
125
145
|
:min-width="minWidth"
|
|
@@ -129,6 +149,7 @@
|
|
|
129
149
|
@expand-tree-data="handleExpand"
|
|
130
150
|
@click-tree-data="onRowClick"
|
|
131
151
|
@dbl-click-tree-data="onRowDblClick"
|
|
152
|
+
@context-menu="onContextMenu"
|
|
132
153
|
>
|
|
133
154
|
<!-- Cell Renderer -->
|
|
134
155
|
<template
|
|
@@ -152,6 +173,14 @@
|
|
|
152
173
|
</span>
|
|
153
174
|
</template>
|
|
154
175
|
</template>
|
|
176
|
+
<template
|
|
177
|
+
v-if="$slots.contextmenuIcon"
|
|
178
|
+
#contextmenuIconNode
|
|
179
|
+
>
|
|
180
|
+
<slot
|
|
181
|
+
name="contextmenuIcon"
|
|
182
|
+
/>
|
|
183
|
+
</template>
|
|
155
184
|
</tree-grid-node>
|
|
156
185
|
<tr v-if="!viewStore.length">
|
|
157
186
|
<td class="is-empty">No records</td>
|
|
@@ -469,6 +498,7 @@ export default {
|
|
|
469
498
|
isRenderer,
|
|
470
499
|
updateVScroll,
|
|
471
500
|
updateHScroll,
|
|
501
|
+
contextInfo,
|
|
472
502
|
});
|
|
473
503
|
|
|
474
504
|
const {
|
|
@@ -768,6 +798,7 @@ export default {
|
|
|
768
798
|
'margin-right': (stores.orderedColumns.length - 1 === index
|
|
769
799
|
&& scrollInfo.hasVerticalScrollBar
|
|
770
800
|
&& scrollInfo.hasHorizontalScrollBar) ? `${resizeInfo.scrollWidth}px` : '0px',
|
|
801
|
+
'border-right': stores.orderedColumns.length - 1 === index ? 'none' : '1px solid #CFCFCF',
|
|
771
802
|
};
|
|
772
803
|
};
|
|
773
804
|
const getSlotName = column => `${column}Node`;
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
<td
|
|
9
9
|
v-if="useCheckbox.use"
|
|
10
10
|
:class="checkboxClass"
|
|
11
|
-
:style="
|
|
12
|
-
width:
|
|
13
|
-
height:
|
|
11
|
+
:style="{
|
|
12
|
+
width: `${minWidth}px`,
|
|
13
|
+
height: `${rowHeight}px`,
|
|
14
|
+
'border-right': '1px solid #CFCFCF',
|
|
15
|
+
}"
|
|
14
16
|
>
|
|
15
17
|
<ev-checkbox
|
|
16
18
|
v-model="node.checked"
|
|
@@ -28,9 +30,9 @@
|
|
|
28
30
|
:data-name="column.field"
|
|
29
31
|
:data-index="node.index"
|
|
30
32
|
:class="getColumnClass(column, cellIndex)"
|
|
31
|
-
:style="getColumnStyle(column)"
|
|
33
|
+
:style="getColumnStyle(column, cellIndex)"
|
|
32
34
|
>
|
|
33
|
-
<div class="td-
|
|
35
|
+
<div class="td-content__wrapper">
|
|
34
36
|
<!--Level Depth-->
|
|
35
37
|
<span
|
|
36
38
|
v-if="cellIndex === expandColumnIdx"
|
|
@@ -76,7 +78,7 @@
|
|
|
76
78
|
<i></i>
|
|
77
79
|
</span>
|
|
78
80
|
</span>
|
|
79
|
-
<div class="
|
|
81
|
+
<div class="td-content">
|
|
80
82
|
<!-- cell renderer -->
|
|
81
83
|
<template v-if="!!$slots[column.field + 'Node']">
|
|
82
84
|
<slot
|
|
@@ -90,14 +92,52 @@
|
|
|
90
92
|
</div>
|
|
91
93
|
</td>
|
|
92
94
|
</template>
|
|
95
|
+
<!-- Row Contextmenu Button -->
|
|
96
|
+
<td
|
|
97
|
+
v-if="customContextMenu?.length"
|
|
98
|
+
:class="{
|
|
99
|
+
'row-contextmenu': true,
|
|
100
|
+
'non-border': !!borderStyle,
|
|
101
|
+
}"
|
|
102
|
+
:style="{
|
|
103
|
+
position: 'sticky',
|
|
104
|
+
right: 0,
|
|
105
|
+
width: '30px',
|
|
106
|
+
height: `${rowHeight}px`,
|
|
107
|
+
'min-width': '30px',
|
|
108
|
+
'line-height': `${rowHeight}px`,
|
|
109
|
+
}"
|
|
110
|
+
>
|
|
111
|
+
<template v-if="$slots.contextmenuIconNode">
|
|
112
|
+
<span
|
|
113
|
+
class="row-contextmenu__btn"
|
|
114
|
+
@click="onContextMenu($event)"
|
|
115
|
+
@click.prevent="menuRef.show"
|
|
116
|
+
>
|
|
117
|
+
<slot
|
|
118
|
+
name="contextmenuIconNode"
|
|
119
|
+
/>
|
|
120
|
+
</span>
|
|
121
|
+
</template>
|
|
122
|
+
<template v-else>
|
|
123
|
+
<grid-option-button
|
|
124
|
+
icon="ev-icon-warning2"
|
|
125
|
+
class="row-contextmenu__btn"
|
|
126
|
+
@click="onContextMenu($event)"
|
|
127
|
+
@click.prevent="menuRef.show"
|
|
128
|
+
/>
|
|
129
|
+
</template>
|
|
130
|
+
</td>
|
|
93
131
|
</tr>
|
|
94
132
|
</template>
|
|
95
133
|
|
|
96
134
|
<script>
|
|
97
135
|
import { computed } from 'vue';
|
|
136
|
+
import GridOptionButton from '@/components/grid/grid.optionButton.vue';
|
|
98
137
|
|
|
99
138
|
export default {
|
|
100
139
|
name: 'TreeGridNode',
|
|
140
|
+
components: { GridOptionButton },
|
|
101
141
|
props: {
|
|
102
142
|
dataIndex: {
|
|
103
143
|
type: Number,
|
|
@@ -135,6 +175,14 @@ export default {
|
|
|
135
175
|
type: String,
|
|
136
176
|
default: '',
|
|
137
177
|
},
|
|
178
|
+
customContextMenu: {
|
|
179
|
+
type: [Array],
|
|
180
|
+
default: () => [],
|
|
181
|
+
},
|
|
182
|
+
menuRef: {
|
|
183
|
+
type: Object,
|
|
184
|
+
default: null,
|
|
185
|
+
},
|
|
138
186
|
rowHeight: {
|
|
139
187
|
type: Number,
|
|
140
188
|
default: 35,
|
|
@@ -157,6 +205,7 @@ export default {
|
|
|
157
205
|
'expand-tree-data': null,
|
|
158
206
|
'click-tree-data': null,
|
|
159
207
|
'dbl-click-tree-data': null,
|
|
208
|
+
'context-menu': null,
|
|
160
209
|
},
|
|
161
210
|
setup(props, { emit }) {
|
|
162
211
|
const onCheck = ($event, data) => {
|
|
@@ -171,6 +220,9 @@ export default {
|
|
|
171
220
|
const onDblClick = ($event, data) => {
|
|
172
221
|
emit('dbl-click-tree-data', $event, data);
|
|
173
222
|
};
|
|
223
|
+
const onContextMenu = ($event) => {
|
|
224
|
+
emit('context-menu', $event);
|
|
225
|
+
};
|
|
174
226
|
const expandIconClasses = (node) => {
|
|
175
227
|
const expandIcon = props.expandIcon ? props.expandIcon : '';
|
|
176
228
|
const collapseIcon = props.expandIcon ? props.collapseIcon : '';
|
|
@@ -205,11 +257,13 @@ export default {
|
|
|
205
257
|
[column.align]: column.align,
|
|
206
258
|
'non-border': !!props.borderStyle,
|
|
207
259
|
});
|
|
208
|
-
const getColumnStyle = column => ({
|
|
260
|
+
const getColumnStyle = (column, cellIndex) => ({
|
|
209
261
|
width: `${column.width}px`,
|
|
210
262
|
height: `${props.rowHeight}px`,
|
|
211
263
|
'line-height': `${props.rowHeight}px`,
|
|
212
264
|
'min-width': `${props.minWidth}px`,
|
|
265
|
+
'border-right': props.orderedColumns.length - 1 === cellIndex
|
|
266
|
+
? 'none' : '1px solid #CFCFCF',
|
|
213
267
|
});
|
|
214
268
|
const getDepthStyle = (nodeLevel) => {
|
|
215
269
|
const depthSize = nodeLevel * 13;
|
|
@@ -228,6 +282,7 @@ export default {
|
|
|
228
282
|
onExpand,
|
|
229
283
|
onClick,
|
|
230
284
|
onDblClick,
|
|
285
|
+
onContextMenu,
|
|
231
286
|
expandIconClasses,
|
|
232
287
|
getRowClass,
|
|
233
288
|
getColumnClass,
|
|
@@ -279,7 +334,7 @@ export default {
|
|
|
279
334
|
background: url('./tree_icon.png') no-repeat -14px -35px;
|
|
280
335
|
}
|
|
281
336
|
}
|
|
282
|
-
.
|
|
337
|
+
.td-content {
|
|
283
338
|
position: relative;
|
|
284
339
|
flex: 1;
|
|
285
340
|
}
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
border-bottom: 1px solid evThemed('grid-bottom-border');
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
.row-contextmenu__btn {
|
|
32
|
+
display: none;
|
|
33
|
+
vertical-align: middle;
|
|
34
|
+
}
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
.column-list {
|
|
@@ -134,6 +138,15 @@
|
|
|
134
138
|
background: #5AB7FF;
|
|
135
139
|
color: #FFFFFF;
|
|
136
140
|
}
|
|
141
|
+
&:hover {
|
|
142
|
+
.row-contextmenu__btn {
|
|
143
|
+
display: grid;
|
|
144
|
+
&:hover {
|
|
145
|
+
cursor: pointer;
|
|
146
|
+
opacity: 0.6;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
137
150
|
}
|
|
138
151
|
|
|
139
152
|
.cell {
|
|
@@ -184,7 +197,7 @@
|
|
|
184
197
|
}
|
|
185
198
|
&.tree-td {
|
|
186
199
|
text-align: left !important;
|
|
187
|
-
.td-
|
|
200
|
+
.td-content__wrapper {
|
|
188
201
|
display: flex;
|
|
189
202
|
position: relative;
|
|
190
203
|
align-items: center;
|
|
@@ -178,6 +178,7 @@ export const resizeEvent = (params) => {
|
|
|
178
178
|
isRenderer,
|
|
179
179
|
updateVScroll,
|
|
180
180
|
updateHScroll,
|
|
181
|
+
contextInfo,
|
|
181
182
|
} = params;
|
|
182
183
|
/**
|
|
183
184
|
* 고정 너비, 스크롤 유무 등에 따른 컬럼 너비를 계산한다.
|
|
@@ -207,7 +208,7 @@ export const resizeEvent = (params) => {
|
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
return acc;
|
|
210
|
-
}, { totalWidth: 0, emptyCount: 0 });
|
|
211
|
+
}, { totalWidth: contextInfo.customContextMenu.length ? 30 : 0, emptyCount: 0 });
|
|
211
212
|
|
|
212
213
|
if (resizeInfo.rowHeight * store.length > elHeight - resizeInfo.scrollWidth) {
|
|
213
214
|
elWidth -= resizeInfo.scrollWidth;
|
|
@@ -373,7 +374,8 @@ export const clickEvent = (params) => {
|
|
|
373
374
|
let timer = null;
|
|
374
375
|
const onRowClick = (event, row) => {
|
|
375
376
|
if (event.target && event.target.parentElement
|
|
376
|
-
&& event.target.parentElement.classList.contains('row-checkbox-input')
|
|
377
|
+
&& (event.target.parentElement.classList.contains('row-checkbox-input')
|
|
378
|
+
|| event.target.closest('td')?.classList?.contains('row-contextmenu'))) {
|
|
377
379
|
return false;
|
|
378
380
|
}
|
|
379
381
|
clearTimeout(timer);
|