evui 3.4.17 → 3.4.19

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.17",
3
+ "version": "3.4.19",
4
4
  "description": "A EXEM Library project",
5
5
  "author": "exem <dev_client@ex-em.com>",
6
6
  "license": "MIT",
@@ -147,6 +147,5 @@ export function mobileCheck() {
147
147
  navigator.userAgent,
148
148
  )
149
149
  || 'ontouchstart' in window
150
- || navigator.maxTouchPoints
151
150
  );
152
151
  }
@@ -8,17 +8,15 @@
8
8
  <!-- Toolbar -->
9
9
  <toolbar>
10
10
  <template #toolbarWrapper>
11
- <!-- Filtering Column Items -->
11
+ <!-- Filtering Items -->
12
12
  <div
13
- class="filtering-column-items"
14
- :style="{ width: `${filteringItemsWidth + 40}px` }"
13
+ class="filtering"
14
+ :style="{ width: `${filteringItemsWidth}px` }"
15
15
  >
16
16
  <div
17
17
  v-if="isFiltering && Object.keys(filteringItemsByColumn).length"
18
18
  ref="filteringItemsRef"
19
- v-clickoutside="() => { if (!isShowColumnFilteringItems) onExpandFilteringItems(); }"
20
19
  class="filtering-items"
21
- :style="filteringItemsStyle"
22
20
  >
23
21
  <template
24
22
  v-for="(field, idx) in Object.keys(filteringItemsByColumn)"
@@ -27,13 +25,13 @@
27
25
  <template v-if="idx === 0">
28
26
  <div
29
27
  class="filtering-items__item filtering-items__item--filter"
30
- @click="onExpandFilteringItems"
28
+ @click.stop="onExpandFilteringItems"
31
29
  >
32
30
  <ev-icon
33
31
  icon="ev-icon-filter-list"
34
32
  class="filtering-items-expand"
35
33
  />
36
- <span class="filtering-items__item--title">
34
+ <span>
37
35
  Filter ({{ Object.keys(filteringItemsByColumn).length }})
38
36
  </span>
39
37
  <ev-icon
@@ -48,11 +46,12 @@
48
46
  v-if="idx === 1"
49
47
  v-model="columnOperator"
50
48
  :items="operatorItems"
51
- class="ev-grid-filter-setting__row--operator"
49
+ class="filtering-items__item--operator"
52
50
  @change="onChangeOperator"
53
51
  />
54
52
  <div
55
53
  class="filtering-items__item non-display"
54
+ :data-field="field"
56
55
  @click.stop="onClickFilteringItem({
57
56
  caption: getFilteringItemByField(field)?.caption,
58
57
  field: field,
@@ -61,13 +60,13 @@
61
60
  >
62
61
  <span class="filtering-items__item--title">
63
62
  {{ getFilteringItemByField(field)?.caption }}
63
+ {{ getFilteringItemByField(field)?.comparison }}
64
64
  </span>
65
65
  <span
66
66
  v-if="filteringItemsByColumn[field].length < 2"
67
67
  class="filtering-items__item--value"
68
68
  :title="getFilteringItemByField(field)?.value"
69
69
  >
70
- {{ getFilteringItemByField(field)?.comparison }}
71
70
  {{ getFilteringItemByField(field)?.value }}
72
71
  </span>
73
72
  <span
@@ -83,62 +82,61 @@
83
82
  />
84
83
  </div>
85
84
  </template>
85
+ <!-- +N Count-->
86
+ <div
87
+ v-if="isShowColumnFilteringItems && Object.keys(filteringItemsByColumn).length
88
+ && hiddenFilteringItemsCount > 0"
89
+ class="filtering-items__item filtering-items__item--count"
90
+ @click="onExpandFilteringItems"
91
+ >
92
+ + {{ hiddenFilteringItemsCount }}
93
+ </div>
86
94
  </div>
87
- <!-- +N Count-->
88
95
  <div
89
- v-if="isShowColumnFilteringItems
90
- && Object.keys(filteringItemsByColumn).length && hiddenFilteringItemsCount > 0"
91
- class="filtering-items filtering-items--count"
92
- @click="onExpandFilteringItems"
96
+ v-if="!isShowColumnFilteringItems && Object.keys(hiddenFilteringItemsByColumn).length"
97
+ ref="hiddenFilteringItemsRef"
98
+ v-clickoutside="() => { if (!isShowColumnFilteringItems) onExpandFilteringItems(); }"
99
+ class="filtering-items filtering-items__hidden-items"
100
+ :style="filteringItemsStyle"
93
101
  >
94
- + {{ hiddenFilteringItemsCount }}
95
- </div>
96
- </div>
97
- <!-- Filtering Items Box -->
98
- <template v-if="isFiltering && isShowFilteringItemsBox">
99
- <teleport to="#ev-grid-filtering-items-modal">
100
- <section
101
- v-clickoutside="() => { isShowFilteringItemsBox = false }"
102
- class="ev-grid-filtering-items"
103
- :style="{
104
- top: boxTop,
105
- left: boxLeft,
106
- }"
102
+ <template
103
+ v-for="(field, idx) in Object.keys(hiddenFilteringItemsByColumn)"
104
+ :key="idx"
107
105
  >
108
- <template
109
- v-for="(field, idx) in selectedFilteringItems"
110
- :key="idx"
106
+ <div
107
+ class="filtering-items__item"
108
+ @click.stop="onClickFilteringItem({
109
+ caption: getFilteringItemByField(field)?.caption,
110
+ field: field,
111
+ },
112
+ hiddenFilteringItemsByColumn[field])"
111
113
  >
112
- <div
113
- v-if="idx === 1"
114
- class="filtering-items__item filtering-items__item--operator"
114
+ <span class="filtering-items__item--title">
115
+ {{ getFilteringItemByField(field)?.caption }}
116
+ {{ getFilteringItemByField(field)?.comparison }}
117
+ </span>
118
+ <span
119
+ v-if="hiddenFilteringItemsByColumn[field].length < 2"
120
+ class="filtering-items__item--value"
121
+ :title="getFilteringItemByField(field)?.value"
115
122
  >
116
- <span class="filtering-items__item--value">
117
- {{ field.operator?.toUpperCase() }}
118
- </span>
119
- </div>
120
- <div class="filtering-items__item">
121
- <span class="filtering-items__item--title">
122
- {{ selectedFilteringColumn.caption }}
123
- </span>
124
- <span class="filtering-items__item--value">
125
- {{ field.comparison }}
126
- {{ field.value.toLocaleString('en') }}
127
- </span>
128
- <ev-icon
129
- class="filtering-items__item--remove"
130
- icon="ev-icon-s-close"
131
- @click="removeFiltering(
132
- {
133
- field: selectedFilteringColumn.field,
134
- idx,
135
- })"
136
- />
137
- </div>
138
- </template>
139
- </section>
140
- </teleport>
141
- </template>
123
+ {{ getFilteringItemByField(field)?.value }}
124
+ </span>
125
+ <span
126
+ v-else
127
+ class="filtering-items__item--value"
128
+ >
129
+ + {{ hiddenFilteringItemsByColumn[field].length }}
130
+ </span>
131
+ <ev-icon
132
+ class="filtering-items__item--remove"
133
+ icon="ev-icon-s-close"
134
+ @click="onApplyFilter(field, [], true)"
135
+ />
136
+ </div>
137
+ </template>
138
+ </div>
139
+ </div>
142
140
  <grid-option-button
143
141
  v-if="useColumnSetting"
144
142
  class="column-setting__icon"
@@ -501,6 +499,7 @@
501
499
  :show-page-info="showPageInfo"
502
500
  :order="order"
503
501
  />
502
+ <!-- Filter Setting -->
504
503
  <filter-setting
505
504
  v-model:is-show="isShowFilterSetting"
506
505
  v-model:items="filteringItemsByColumn"
@@ -522,9 +521,10 @@ import {
522
521
  nextTick,
523
522
  ref,
524
523
  provide,
525
- onBeforeMount,
524
+ onBeforeMount, onUnmounted,
526
525
  } from 'vue';
527
526
  import { clickoutside } from '@/directives/clickoutside';
527
+ import { cloneDeep } from 'lodash-es';
528
528
  import Toolbar from './grid.toolbar';
529
529
  import GridPagination from './grid.pagination';
530
530
  import GridSummary from './grid.summary';
@@ -620,6 +620,7 @@ export default {
620
620
  const borderStyle = computed(() => (props.option.style?.border || ''));
621
621
  const highlightIdx = computed(() => (props.option.style?.highlight ?? -1));
622
622
  const rowMinHeight = props.option.rowMinHeight || 35;
623
+ const filteringItemsWidth = ref(0);
623
624
  const elementInfo = reactive({
624
625
  body: null,
625
626
  header: null,
@@ -660,9 +661,9 @@ export default {
660
661
  movedColumns: [],
661
662
  originColumns: computed(() => props.columns.map((column, index) => ({ index, ...column }))),
662
663
  orderedColumns: computed(() => {
663
- const columns = stores.filteredColumns.length
664
- ? stores.filteredColumns : stores.originColumns;
665
- return stores.movedColumns.length ? stores.movedColumns : columns;
664
+ const columns = stores.movedColumns.length
665
+ ? stores.movedColumns : stores.originColumns;
666
+ return stores.filteredColumns.length ? stores.filteredColumns : columns;
666
667
  }),
667
668
  });
668
669
  const pageInfo = reactive({
@@ -868,17 +869,38 @@ export default {
868
869
 
869
870
  provide('toolbarWrapper', toolbarWrapper);
870
871
 
871
- const filteringItemsWidth = ref(0);
872
+ const onMouseWheel = (e) => {
873
+ if (e.type === 'wheel') {
874
+ contextInfo.menu?.hide(e);
875
+ }
876
+ if (e.type === 'scroll' && !e.target.classList?.contains('table-body')
877
+ && !e.target.offsetParent?.classList?.contains('ev-grid-column-setting')) {
878
+ contextInfo.columnMenu?.hide(e);
879
+ columnSettingInfo.isShowColumnSetting = false;
880
+ filterInfo.isShowFilterSetting = false;
881
+ }
882
+ };
883
+
872
884
  onMounted(() => {
873
885
  calculatedColumn();
874
886
  setStore(props.rows);
887
+ document.addEventListener('wheel', onMouseWheel, { capture: false });
888
+ document.addEventListener('scroll', onMouseWheel, { capture: true });
875
889
  });
890
+
891
+ onUnmounted(() => {
892
+ document.removeEventListener('wheel', onMouseWheel);
893
+ document.removeEventListener('scroll', onMouseWheel);
894
+ });
895
+
876
896
  onActivated(() => {
877
897
  onResize();
878
898
  });
899
+
879
900
  onUpdated(() => {
880
901
  filteringItemsWidth.value = elementInfo['grid-wrapper']?.offsetWidth / 1.5 || 0;
881
902
  });
903
+
882
904
  watch(
883
905
  () => props.columns,
884
906
  () => {
@@ -1062,6 +1084,8 @@ export default {
1062
1084
  const filteringItemsRef = ref(null);
1063
1085
  const isShowFilteringItemsBox = ref(false);
1064
1086
  const isShowColumnFilteringItems = ref(false);
1087
+ const hiddenFilteringItemsRef = ref(null);
1088
+ const hiddenFilteringItemsByColumn = ref({});
1065
1089
  const selectedFilteringColumn = reactive({
1066
1090
  caption: '',
1067
1091
  field: '',
@@ -1092,29 +1116,32 @@ export default {
1092
1116
  setStore([], false);
1093
1117
  };
1094
1118
 
1095
- const setColumnFilteringItems = (flag) => {
1096
- if (flag && isShowColumnFilteringItems.value) {
1119
+ const setColumnFilteringItems = async (isInit) => {
1120
+ if (isInit && isShowColumnFilteringItems.value) {
1097
1121
  hiddenFilteringItemsCount.value = 0;
1098
1122
  }
1099
1123
  const conditionItems = filteringItemsRef.value
1100
1124
  ?.getElementsByClassName('filtering-items__item');
1101
1125
  const hiddenItemList = [];
1126
+ let sumWidth = 0;
1102
1127
  if (conditionItems) {
1103
1128
  for (let i = 0; i < conditionItems.length; i++) {
1104
1129
  const itemEl = conditionItems[i];
1105
1130
  itemEl.classList.remove('non-display');
1106
- const boxTop = filteringItemsRef.value.getBoundingClientRect()?.top;
1107
- const boxScrollTop = filteringItemsRef.value.scrollTop;
1108
- const { top } = itemEl.getBoundingClientRect();
1109
- if (isShowColumnFilteringItems.value
1110
- && (top - boxTop > itemEl.offsetHeight - boxScrollTop)) {
1111
- if (flag) {
1112
- if (i === 0) {
1113
- hiddenFilteringItemsCount.value = 0;
1114
- }
1115
- hiddenFilteringItemsCount.value++;
1116
- }
1131
+ const boxWidth = filteringItemsRef.value.getBoundingClientRect()?.width;
1132
+ const { width } = itemEl.getBoundingClientRect();
1133
+ sumWidth += width + 10;
1134
+ if (boxWidth - 150 <= sumWidth
1135
+ && !itemEl.classList.contains('filtering-items__item--count')) {
1136
+ hiddenFilteringItemsCount.value++;
1117
1137
  hiddenItemList.push(itemEl);
1138
+ hiddenFilteringItemsByColumn.value = {
1139
+ ...hiddenFilteringItemsByColumn.value,
1140
+ [itemEl.dataset.field]:
1141
+ cloneDeep(filterInfo.filteringItemsByColumn[itemEl.dataset.field]),
1142
+ };
1143
+ } else {
1144
+ delete hiddenFilteringItemsByColumn.value[itemEl.dataset.field];
1118
1145
  }
1119
1146
  }
1120
1147
  conditionItems.forEach((item) => {
@@ -1126,6 +1153,7 @@ export default {
1126
1153
 
1127
1154
  const onApplyFilter = async (field, list) => {
1128
1155
  if (!list?.length) {
1156
+ delete hiddenFilteringItemsByColumn.value[field];
1129
1157
  delete filterInfo.filteringItemsByColumn[field];
1130
1158
  } else {
1131
1159
  filterInfo.filteringItemsByColumn[field] = list;
@@ -1138,41 +1166,29 @@ export default {
1138
1166
  setStore([], false);
1139
1167
  };
1140
1168
 
1169
+ let expandTimer = null;
1141
1170
  const onExpandFilteringItems = () => {
1142
- isShowColumnFilteringItems.value = !isShowColumnFilteringItems.value;
1143
- setColumnFilteringItems(isShowColumnFilteringItems.value);
1144
- };
1145
-
1146
- const removeFiltering = ({ field, idx }) => {
1147
- filterInfo.filteringItemsByColumn[field]?.splice(idx, 1);
1148
- if (!filterInfo.filteringItemsByColumn[field].length) {
1149
- delete filterInfo.filteringItemsByColumn[field];
1150
- isShowFilteringItemsBox.value = false;
1171
+ if (expandTimer) {
1172
+ clearTimeout(expandTimer);
1151
1173
  }
1152
- setColumnFilteringItems(true);
1153
- stores.filterStore = [];
1154
- setStore([], false);
1174
+ expandTimer = setTimeout(() => {
1175
+ isShowColumnFilteringItems.value = !isShowColumnFilteringItems.value;
1176
+ setColumnFilteringItems(isShowColumnFilteringItems.value);
1177
+ }, 150);
1155
1178
  };
1156
1179
 
1157
1180
  const removeAllFiltering = () => {
1158
1181
  filterInfo.filteringColumn = null;
1159
1182
  filterInfo.filteringItemsByColumn = {};
1183
+ hiddenFilteringItemsByColumn.value = {};
1160
1184
  stores.filterStore = [];
1161
1185
  setStore([], false);
1162
1186
  };
1163
1187
 
1164
- const filteringItemsStyle = computed(() => {
1165
- if (isShowColumnFilteringItems.value) {
1166
- return {
1167
- width: `${filteringItemsWidth.value}px`,
1168
- background: 'none',
1169
- };
1170
- }
1171
- return {
1172
- width: `${filteringItemsWidth.value}px`,
1173
- 'overflow-y': 'auto',
1174
- };
1175
- });
1188
+ const filteringItemsStyle = computed(() => ({
1189
+ width: `${filteringItemsWidth.value}px`,
1190
+ top: `${filteringItemsRef.value?.getBoundingClientRect().height || 0}px`,
1191
+ }));
1176
1192
 
1177
1193
  const getFilteringItemByField = (field) => {
1178
1194
  const filteringFieldInfo = filterInfo.filteringItemsByColumn[field];
@@ -1248,7 +1264,8 @@ export default {
1248
1264
  filteringItemsStyle,
1249
1265
  hiddenFilteringItemsCount,
1250
1266
  ...toRefs(filteringItemsBoxPosition),
1251
- removeFiltering,
1267
+ hiddenFilteringItemsRef,
1268
+ hiddenFilteringItemsByColumn,
1252
1269
  removeAllFiltering,
1253
1270
  onExpandFilteringItems,
1254
1271
  setColumnFilteringItems,
@@ -251,24 +251,32 @@
251
251
  text-align: center;
252
252
  }
253
253
 
254
- .filtering-column-items {
254
+ .filtering {
255
255
  position: relative;
256
256
  }
257
257
 
258
258
  .filtering-items {
259
259
  display: flex;
260
+ position: absolute;
261
+ width: inherit;
260
262
  flex-flow: row wrap;
261
263
  gap: 10px;
262
- position: absolute;
263
264
  padding: 8px;
264
265
  flex-direction: row;
265
266
  align-items: center;
266
267
  z-index: 1;
267
268
  border-radius: 4px;
268
- max-height: 106px;
269
+ max-height: 120px;
270
+ background: none;
269
271
 
270
- @include evThemify() {
271
- background: evThemed('grid-background');
272
+ &__hidden-items {
273
+ max-height: 106px;
274
+ overflow-y: auto;
275
+
276
+ @include evThemify() {
277
+ border: 1px solid evThemed('grid-bottom-border');
278
+ background: evThemed('grid-background');
279
+ }
272
280
  }
273
281
  &-expand {
274
282
  &:hover {
@@ -291,37 +299,41 @@
291
299
  }
292
300
  }
293
301
  &__item {
294
- padding: 4px;
302
+ height: 26px;
303
+ line-height: 26px;
304
+ padding: 0 10px;
305
+ border: 1px solid #CED4DA;
295
306
  border-radius: 4px;
296
307
  background: #EAF2FA;
308
+ font-size: 13px;
309
+ text-align: center;
297
310
  &:hover {
298
311
  cursor: pointer;
299
- padding: 3.5px;
300
312
  border: 1px solid #198FFF;
301
- box-sizing: border-box;
302
313
  }
303
314
  &--filter {
304
315
  font-weight: bold;
305
316
  border: 1px solid #198FFF;
317
+ color: #198FFF;
306
318
  i {
307
- color: #0077FF;
308
319
  vertical-align: middle;
309
320
  }
310
321
  }
311
322
  &--title {
312
- color: #0077FF;
313
323
  margin-right: 6px;
314
324
  vertical-align: middle;
315
325
  }
316
326
  &--value {
317
327
  display: inline-block;
318
328
  max-width: 80px;
329
+ color: #198FFF;
319
330
  text-overflow: ellipsis;
320
331
  overflow: hidden;
321
332
  vertical-align: middle;
322
333
  white-space: nowrap;
323
334
  }
324
335
  &--remove {
336
+ color: initial;
325
337
  vertical-align: middle;
326
338
  margin-left: 6px;
327
339
  &:hover {
@@ -329,9 +341,11 @@
329
341
  }
330
342
  }
331
343
  &--operator {
332
- width: 50px;
333
- background: #DCDFE6;
334
- text-align: center;
344
+ width: 80px;
345
+ height: 26px !important;
346
+ .ev-input {
347
+ height: 26px !important;
348
+ }
335
349
  &:hover {
336
350
  cursor: initial;
337
351
  margin: inherit;
@@ -1154,16 +1154,22 @@ export const columnSettingEvent = (params) => {
1154
1154
  }
1155
1155
  onResize();
1156
1156
  };
1157
- const onApplyColumn = (columns) => {
1158
- columnSettingInfo.hiddenColumn = '';
1157
+ const onApplyColumn = (columnNames) => {
1158
+ const columns = stores.orderedColumns.filter(col => !col.hide);
1159
+ const isSameColumn = columnNames.length === columns.length
1160
+ && columns.every(col => columnNames.includes(col.field));
1161
+
1162
+ if (isSameColumn) {
1163
+ return;
1164
+ }
1165
+
1159
1166
  stores.filteredColumns = stores.originColumns
1160
- .filter(col => columns.includes(col.field) || !col.caption);
1167
+ .filter(col => columnNames.includes(col.field) || !col.caption);
1168
+ columnSettingInfo.hiddenColumn = '';
1161
1169
  setFilteringColumn();
1162
1170
  };
1163
1171
  const setColumnHidden = (val) => {
1164
- const columns = (columnSettingInfo.isFilteringColumn
1165
- ? stores.filteredColumns : stores.originColumns)
1166
- .filter(col => !col.hide);
1172
+ const columns = stores.orderedColumns.filter(col => !col.hide);
1167
1173
 
1168
1174
  if (columns.length === 1) {
1169
1175
  return;
@@ -1180,10 +1186,18 @@ export const dragEvent = ({ stores }) => {
1180
1186
  const setColumnMoving = (currentIndex, droppedIndex) => {
1181
1187
  const oldIndex = parseInt(currentIndex, 10);
1182
1188
  const newPositionIndex = parseInt(droppedIndex, 10);
1183
- const columns = stores.filteredColumns.length ? stores.filteredColumns : stores.orderedColumns;
1189
+
1190
+ const columns = [...stores.orderedColumns];
1184
1191
  const movedColumn = columns[oldIndex];
1192
+
1185
1193
  columns.splice(oldIndex, 1);
1186
- stores.movedColumns = columns.splice(newPositionIndex, 0, movedColumn);
1194
+ columns.splice(newPositionIndex, 0, movedColumn);
1195
+
1196
+ if (stores.filteredColumns.length) {
1197
+ stores.filteredColumns = columns;
1198
+ } else {
1199
+ stores.movedColumns = columns;
1200
+ }
1187
1201
  };
1188
1202
  const onDragStart = (e) => {
1189
1203
  e.dataTransfer.setData('text/plain', e.currentTarget.dataset.index);
@@ -237,7 +237,18 @@
237
237
  </template>
238
238
 
239
239
  <script>
240
- import { reactive, toRefs, computed, watch, onMounted, onActivated, nextTick, ref, provide } from 'vue';
240
+ import {
241
+ reactive,
242
+ toRefs,
243
+ computed,
244
+ watch,
245
+ onActivated,
246
+ nextTick,
247
+ ref,
248
+ provide,
249
+ onMounted,
250
+ onUnmounted,
251
+ } from 'vue';
241
252
  import treeGridNode from './TreeGridNode';
242
253
  import Toolbar from './treeGrid.toolbar';
243
254
  import GridPagination from '../grid/grid.pagination';
@@ -545,9 +556,28 @@ export default {
545
556
 
546
557
  provide('toolbarWrapper', toolbarWrapper);
547
558
 
559
+ const onMouseWheel = (e) => {
560
+ if (e.type === 'wheel') {
561
+ contextInfo.menu?.hide(e);
562
+ }
563
+ if (e.type === 'scroll' && !e.target.classList?.contains('table-body')
564
+ && !e.target.offsetParent?.classList?.contains('ev-grid-column-setting')) {
565
+ contextInfo.columnMenu?.hide(e);
566
+ columnSettingInfo.isShowColumnSetting = false;
567
+ }
568
+ };
569
+
548
570
  onMounted(() => {
549
571
  stores.treeStore = setTreeNodeStore();
572
+ document.addEventListener('wheel', onMouseWheel, { capture: false });
573
+ document.addEventListener('scroll', onMouseWheel, { capture: true });
550
574
  });
575
+
576
+ onUnmounted(() => {
577
+ document.removeEventListener('wheel', onMouseWheel);
578
+ document.removeEventListener('scroll', onMouseWheel);
579
+ });
580
+
551
581
  onActivated(() => {
552
582
  onResize();
553
583
  });