evui 3.4.25 → 3.4.27

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.
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div
3
- v-if="$slots.toolbar || useColumnSetting"
4
- ref="toolbarWrapper"
3
+ v-if="$slots.toolbar || useGridSetting"
4
+ ref="toolbarRef"
5
5
  class="toolbar-wrapper"
6
6
  :style="`width: ${gridWidth};`"
7
7
  >
@@ -145,9 +145,9 @@
145
145
  </div>
146
146
  </div>
147
147
  <grid-option-button
148
- v-if="useColumnSetting"
149
- class="column-setting__icon"
150
- @click="setColumnSetting"
148
+ v-if="useGridSetting"
149
+ class="grid-setting__icon"
150
+ @click="setGridSetting($event)"
151
151
  />
152
152
  <slot
153
153
  name="toolbar"
@@ -155,12 +155,6 @@
155
155
  />
156
156
  </template>
157
157
  </toolbar>
158
- <column-setting
159
- v-model:is-show="isShowColumnSetting"
160
- :columns="$props.columns"
161
- :hidden-column="hiddenColumn"
162
- @apply-column="onApplyColumn"
163
- />
164
158
  </div>
165
159
  <div
166
160
  ref="grid-wrapper"
@@ -200,6 +194,7 @@
200
194
  v-if="useCheckbox.use"
201
195
  :class="{
202
196
  'column': true,
197
+ 'checkbox-all': true,
203
198
  'non-border': !!borderStyle,
204
199
  }"
205
200
  :style="{
@@ -210,9 +205,21 @@
210
205
  <ev-checkbox
211
206
  v-if="useCheckbox.use && useCheckbox.headerCheck && useCheckbox.mode !== 'single'"
212
207
  v-model="isHeaderChecked"
208
+ :disabled="isHeaderUncheckable"
213
209
  @change="onCheckAll"
214
210
  />
215
211
  </li>
212
+ <li
213
+ v-if="useRowDetail"
214
+ :class="{
215
+ 'column': true,
216
+ 'non-border': !!borderStyle,
217
+ }"
218
+ :style="{
219
+ width: `${minWidth}px`,
220
+ 'border-right': '1px solid #CFCFCF'
221
+ }"
222
+ />
216
223
  <!-- Column List -->
217
224
  <template
218
225
  v-for="(column, index) in orderedColumns"
@@ -347,114 +354,148 @@
347
354
  <table ref="table">
348
355
  <tbody>
349
356
  <!-- Row List -->
350
- <tr
357
+ <template
351
358
  v-for="(row, rowIndex) in viewStore"
352
359
  :key="rowIndex"
353
- :data-index="row[0]"
354
- :class="{
355
- row: true,
356
- selected: row[3],
357
- highlight: row[0] === highlightIdx,
358
- 'non-border': !!borderStyle && borderStyle !== 'rows',
359
- }"
360
- @click="onRowClick($event, row)"
361
- @contextmenu="onRowClick($event, row, true)"
362
- @dblclick="onRowDblClick($event, row)"
363
360
  >
364
- <!-- Row Checkbox -->
365
- <td
366
- v-if="useCheckbox.use"
361
+ <tr
362
+ :data-index="row[0]"
367
363
  :class="{
368
- cell: true,
369
- 'row-checkbox': true,
370
- 'non-border': !!borderStyle,
371
- }"
372
- :style="{
373
- width: `${minWidth}px`,
374
- height: `${rowHeight}px`,
375
- 'border-right': '1px solid #CFCFCF',
364
+ row: true,
365
+ selected: row[3],
366
+ highlight: row[0] === highlightIdx,
367
+ 'non-border': !!borderStyle && borderStyle !== 'rows',
376
368
  }"
369
+ @click="onRowClick($event, row)"
370
+ @contextmenu="onRowClick($event, row, true)"
371
+ @dblclick="onRowDblClick($event, row)"
377
372
  >
378
- <ev-checkbox
379
- v-model="row[1]"
380
- class="row-checkbox-input"
381
- @change="onCheck($event, row)"
382
- />
383
- </td>
384
- <!-- Cell -->
385
- <template
386
- v-for="(column, cellIndex) in orderedColumns"
387
- :key="cellIndex"
388
- >
373
+ <!-- Row Checkbox -->
389
374
  <td
390
- v-if="!column.hide && !column.hiddenDisplay"
391
- :data-name="column.field"
392
- :data-index="column.index"
375
+ v-if="useCheckbox.use"
393
376
  :class="{
394
377
  cell: true,
395
- render: isRenderer(column),
396
- [column.type]: column.type,
397
- [column.align]: column.align,
398
- [column.field]: column.field,
378
+ 'row-checkbox': true,
399
379
  'non-border': !!borderStyle,
400
380
  }"
401
381
  :style="{
402
- width: `${column.width}px`,
382
+ width: `${minWidth}px`,
403
383
  height: `${rowHeight}px`,
384
+ 'border-right': '1px solid #CFCFCF',
385
+ }"
386
+ >
387
+ <ev-checkbox
388
+ v-model="row[1]"
389
+ class="row-checkbox-input"
390
+ :disabled="row[5]"
391
+ @change="onCheck($event, row)"
392
+ />
393
+ </td>
394
+ <!-- Row Detail toggle -->
395
+ <td
396
+ v-if="useRowDetail"
397
+ :class="{
398
+ cell: true,
399
+ 'row-detail-toggle': true,
400
+ 'non-border': !!borderStyle,
401
+ 'row-detail-toggle--expanded': row[4],
402
+ }"
403
+ :style="{
404
+ width: `${minWidth}px`,
405
+ height: `${rowHeight}px`,
406
+ 'border-right': '1px solid #CFCFCF',
407
+ }"
408
+ >
409
+ <ev-icon
410
+ v-model="row[4]"
411
+ icon="ev-icon-s-play"
412
+ class="row-detail-toggle-icon"
413
+ @click.stop="onExpanded($event, row)"
414
+ />
415
+ </td>
416
+ <!-- Cell -->
417
+ <template
418
+ v-for="(column, cellIndex) in orderedColumns"
419
+ :key="cellIndex"
420
+ >
421
+ <td
422
+ v-if="!column.hide && !column.hiddenDisplay"
423
+ :data-name="column.field"
424
+ :data-index="column.index"
425
+ :class="{
426
+ cell: true,
427
+ render: isRenderer(column),
428
+ [column.type]: column.type,
429
+ [column.align]: column.align,
430
+ [column.field]: column.field,
431
+ 'non-border': !!borderStyle,
432
+ }"
433
+ :style="{
434
+ width: `${column.width}px`,
435
+ height: `${rowHeight}px`,
436
+ 'line-height': `${rowHeight}px`,
437
+ 'min-width': `${isRenderer(column) ? rendererMinWidth : minWidth}px`,
438
+ 'border-right': orderedColumns.length - 1 === cellIndex
439
+ ? 'none' : '1px solid #CFCFCF',
440
+ }"
441
+ >
442
+ <!-- Cell Renderer -->
443
+ <div v-if="!!$slots[column.field]">
444
+ <slot
445
+ :name="column.field"
446
+ :item="{ row, column }"
447
+ />
448
+ </div>
449
+ <!-- Cell Value -->
450
+ <template v-else>
451
+ <div :title="getConvertValue(column, row[2][column.index])">
452
+ {{ getConvertValue(column, row[2][column.index]) }}
453
+ </div>
454
+ </template>
455
+ </td>
456
+ </template>
457
+ <!-- Row Contextmenu Button -->
458
+ <td
459
+ v-if="$props.option.customContextMenu?.length"
460
+ :class="{
461
+ 'row-contextmenu': true,
462
+ 'non-border': !!borderStyle,
463
+ }"
464
+ :style="{
465
+ position: 'sticky',
466
+ right: 0,
467
+ width: '30px',
468
+ height: `${rowHeight}px`,
469
+ 'min-width': '30px',
404
470
  'line-height': `${rowHeight}px`,
405
- 'min-width': `${isRenderer(column) ? rendererMinWidth : minWidth}px`,
406
- 'border-right': orderedColumns.length - 1 === cellIndex
407
- ? 'none' : '1px solid #CFCFCF',
408
471
  }"
409
472
  >
410
- <!-- Cell Renderer -->
411
- <div v-if="!!$slots[column.field]">
412
- <slot
413
- :name="column.field"
414
- :item="{ row, column }"
415
- />
416
- </div>
417
- <!-- Cell Value -->
473
+ <template v-if="$slots.contextmenuIcon">
474
+ <span
475
+ class="row-contextmenu__btn"
476
+ @click="onContextMenu($event)"
477
+ >
478
+ <slot name="contextmenuIcon"></slot>
479
+ </span>
480
+ </template>
418
481
  <template v-else>
419
- <div :title="getConvertValue(column, row[2][column.index])">
420
- {{ getConvertValue(column, row[2][column.index]) }}
421
- </div>
482
+ <grid-option-button
483
+ icon="ev-icon-warning2"
484
+ class="row-contextmenu__btn"
485
+ @click="onContextMenu($event)"
486
+ />
422
487
  </template>
423
488
  </td>
424
- </template>
425
- <!-- Row Contextmenu Button -->
426
- <td
427
- v-if="$props.option.customContextMenu?.length"
428
- :class="{
429
- 'row-contextmenu': true,
430
- 'non-border': !!borderStyle,
431
- }"
432
- :style="{
433
- position: 'sticky',
434
- right: 0,
435
- width: '30px',
436
- height: `${rowHeight}px`,
437
- 'min-width': '30px',
438
- 'line-height': `${rowHeight}px`,
439
- }"
489
+ </tr>
490
+ <tr
491
+ v-if="useRowDetail && $slots?.rowDetail && row[4]"
440
492
  >
441
- <template v-if="$slots.contextmenuIcon">
442
- <span
443
- class="row-contextmenu__btn"
444
- @click="onContextMenu($event)"
445
- >
446
- <slot name="contextmenuIcon"></slot>
447
- </span>
448
- </template>
449
- <template v-else>
450
- <grid-option-button
451
- icon="ev-icon-warning2"
452
- class="row-contextmenu__btn"
453
- @click="onContextMenu($event)"
454
- />
455
- </template>
456
- </td>
457
- </tr>
493
+ <slot
494
+ name="rowDetail"
495
+ :item="{ row }"
496
+ />
497
+ </tr>
498
+ </template>
458
499
  <tr v-if="!viewStore.length">
459
500
  <td class="is-empty">No records</td>
460
501
  </tr>
@@ -474,6 +515,11 @@
474
515
  ref="columnMenu"
475
516
  :items="columnMenuItems"
476
517
  />
518
+ <ev-context-menu
519
+ ref="gridSettingMenu"
520
+ :items="gridSettingContextMenuItems"
521
+ :is-show-menu-on-click="isShowMenuOnClick"
522
+ />
477
523
  </div>
478
524
  <!-- Resize Line -->
479
525
  <div
@@ -514,6 +560,15 @@
514
560
  :position="filterSettingPosition"
515
561
  @apply-filtering="onApplyFilter"
516
562
  />
563
+ <!-- Column Setting -->
564
+ <column-setting
565
+ v-model:is-show="isShowColumnSetting"
566
+ v-model:is-show-menu-on-click="isShowMenuOnClick"
567
+ :columns="$props.columns"
568
+ :hidden-column="hiddenColumn"
569
+ :position="columnSettingPosition"
570
+ @apply-column="onApplyColumn"
571
+ />
517
572
  </template>
518
573
 
519
574
  <script>
@@ -527,7 +582,6 @@ import {
527
582
  onActivated,
528
583
  nextTick,
529
584
  ref,
530
- provide,
531
585
  onBeforeMount, onUnmounted,
532
586
  } from 'vue';
533
587
  import { clickoutside } from '@/directives/clickoutside';
@@ -545,6 +599,7 @@ import {
545
599
  resizeEvent,
546
600
  clickEvent,
547
601
  checkEvent,
602
+ expandEvent,
548
603
  sortEvent,
549
604
  filterEvent,
550
605
  contextMenuEvent,
@@ -593,6 +648,14 @@ export default {
593
648
  type: [Array],
594
649
  default: () => [],
595
650
  },
651
+ uncheckable: {
652
+ type: [Array],
653
+ default: () => [],
654
+ },
655
+ expanded: {
656
+ type: [Array],
657
+ default: () => [],
658
+ },
596
659
  option: {
597
660
  type: Object,
598
661
  default: () => ({}),
@@ -607,12 +670,15 @@ export default {
607
670
  'check-all': null,
608
671
  'page-change': null,
609
672
  'sort-column': null,
673
+ 'expand-row': null,
674
+ 'update:expanded': null,
610
675
  },
611
676
  setup(props) {
612
677
  // const ROW_INDEX = 0;
613
678
  const ROW_CHECK_INDEX = 1;
614
679
  const ROW_DATA_INDEX = 2;
615
680
  const ROW_SELECT_INDEX = 3;
681
+ const ROW_EXPAND_INDEX = 4;
616
682
  const {
617
683
  isRenderer,
618
684
  getComponentName,
@@ -620,9 +686,9 @@ export default {
620
686
  getColumnIndex,
621
687
  setPixelUnit,
622
688
  } = commonFunctions();
623
- const toolbarWrapper = ref(null);
689
+ const toolbarRef = ref(null);
690
+ const useGridSetting = computed(() => (props.option?.useGridSetting?.use || false));
624
691
  const showHeader = computed(() => (props.option.showHeader ?? true));
625
- const useColumnSetting = computed(() => (props.option?.useColumnSetting || false));
626
692
  const useSummary = computed(() => (props.option?.useSummary || false));
627
693
  const stripeStyle = computed(() => (props.option.style?.stripe || false));
628
694
  const borderStyle = computed(() => (props.option.style?.border || ''));
@@ -654,6 +720,11 @@ export default {
654
720
  isFilteringColumn: false, // hide된 컬럼이 있는지
655
721
  visibleColumnIdx: [], // 보여지는 컬럼의 인덱스 목록
656
722
  hiddenColumn: '',
723
+ columnSettingPosition: {
724
+ top: 0,
725
+ left: 0,
726
+ columnListMenuWidth: 0,
727
+ },
657
728
  });
658
729
  const stores = reactive({
659
730
  viewStore: [],
@@ -695,9 +766,14 @@ export default {
695
766
  const checkInfo = reactive({
696
767
  prevCheckedRow: [],
697
768
  isHeaderChecked: false,
769
+ isHeaderUncheckable: false,
698
770
  checkedRows: props.checked,
699
771
  useCheckbox: computed(() => (props.option.useCheckbox || {})),
700
772
  });
773
+ const expandedInfo = reactive({
774
+ expandedRows: props.expanded,
775
+ useRowDetail: computed(() => props.option?.rowDetail?.use ?? false),
776
+ });
701
777
  const scrollInfo = reactive({
702
778
  lastScroll: {
703
779
  top: 0,
@@ -726,9 +802,15 @@ export default {
726
802
  columnMenu: null,
727
803
  columnMenuItems: [],
728
804
  customContextMenu: props.option.customContextMenu || [],
805
+ gridSettingMenu: null,
806
+ gridSettingContextMenuItems: [],
807
+ customGridSettingContextMenu: computed(
808
+ () => props.option?.useGridSetting?.customContextMenu || [],
809
+ ),
810
+ isShowMenuOnClick: false,
729
811
  });
730
812
  const resizeInfo = reactive({
731
- minWidth: 80,
813
+ minWidth: 40,
732
814
  rendererMinWidth: 80,
733
815
  iconWidth: 42,
734
816
  showResizeLine: false,
@@ -753,6 +835,12 @@ export default {
753
835
  row[ROW_SELECT_INDEX] = false;
754
836
  });
755
837
  };
838
+ const clearExpandedInfo = () => {
839
+ stores.store.forEach((row) => {
840
+ row[ROW_EXPAND_INDEX] = false;
841
+ });
842
+ expandedInfo.expandedRows = [];
843
+ };
756
844
  const {
757
845
  getPagingData,
758
846
  updatePagingInfo,
@@ -777,6 +865,7 @@ export default {
777
865
  resizeInfo,
778
866
  pageInfo,
779
867
  summaryScroll,
868
+ expandedInfo,
780
869
  getPagingData,
781
870
  updatePagingInfo,
782
871
  });
@@ -786,6 +875,13 @@ export default {
786
875
  onCheckAll,
787
876
  } = checkEvent({ checkInfo, stores, pageInfo, getPagingData, updatePagingInfo });
788
877
 
878
+ const {
879
+ onExpanded,
880
+ } = expandEvent({
881
+ expandedInfo,
882
+ stores,
883
+ });
884
+
789
885
  const {
790
886
  onSort,
791
887
  setSort,
@@ -816,6 +912,7 @@ export default {
816
912
  sortInfo,
817
913
  elementInfo,
818
914
  filterInfo,
915
+ expandedInfo,
819
916
  setSort,
820
917
  updateVScroll,
821
918
  setFilter,
@@ -830,6 +927,7 @@ export default {
830
927
  resizeInfo,
831
928
  elementInfo,
832
929
  checkInfo,
930
+ expandedInfo,
833
931
  stores,
834
932
  filterInfo,
835
933
  isRenderer,
@@ -839,13 +937,14 @@ export default {
839
937
  });
840
938
 
841
939
  const {
842
- setColumnSetting,
940
+ setPositionColumnSetting,
843
941
  initColumnSettingInfo,
844
942
  onApplyColumn,
845
943
  setColumnHidden,
846
944
  } = columnSettingEvent({
847
945
  stores,
848
946
  columnSettingInfo,
947
+ contextInfo,
849
948
  onSearch,
850
949
  onResize,
851
950
  });
@@ -854,14 +953,16 @@ export default {
854
953
  setContextMenu,
855
954
  onContextMenu,
856
955
  onColumnContextMenu,
956
+ onGridSettingContextMenu,
857
957
  } = contextMenuEvent({
858
958
  contextInfo,
859
959
  stores,
860
960
  selectInfo,
861
- onSort,
862
- setColumnHidden,
863
- useColumnSetting,
961
+ useGridSetting,
864
962
  filterInfo,
963
+ columnSettingInfo,
964
+ setColumnHidden,
965
+ onSort,
865
966
  });
866
967
 
867
968
  const {
@@ -875,7 +976,14 @@ export default {
875
976
  onDrop,
876
977
  } = dragEvent({ stores });
877
978
 
878
- provide('toolbarWrapper', toolbarWrapper);
979
+ const setGridSetting = (e) => {
980
+ contextInfo.gridSettingContextMenuItems.length = 0;
981
+ if (contextInfo.customGridSettingContextMenu.length) {
982
+ onGridSettingContextMenu(e);
983
+ } else {
984
+ columnSettingInfo.isShowColumnSetting = true;
985
+ }
986
+ };
879
987
 
880
988
  const onMouseWheel = (e) => {
881
989
  if (e.type === 'wheel') {
@@ -885,9 +993,11 @@ export default {
885
993
  && !e.target.offsetParent?.classList?.contains('ev-select-dropbox')
886
994
  && !e.target.offsetParent?.classList?.contains('ev-grid-column-setting')
887
995
  && !e.target.offsetParent?.classList?.contains('ev-text-field-wrapper')) {
888
- contextInfo.columnMenu?.hide(e);
889
- columnSettingInfo.isShowColumnSetting = false;
890
996
  filterInfo.isShowFilterSetting = false;
997
+ columnSettingInfo.isShowColumnSetting = false;
998
+ contextInfo.isShowMenuOnClick = false;
999
+ contextInfo.columnMenu?.hide(e);
1000
+ contextInfo.gridSettingMenu?.hide();
891
1001
  }
892
1002
  };
893
1003
 
@@ -911,6 +1021,14 @@ export default {
911
1021
  filteringItemsWidth.value = elementInfo['grid-wrapper']?.offsetWidth / 1.5 || 0;
912
1022
  });
913
1023
 
1024
+ watch(() => columnSettingInfo.isShowColumnSetting, (isShowColumnSetting) => {
1025
+ if (!isShowColumnSetting) {
1026
+ contextInfo.gridSettingMenu?.hide();
1027
+ return;
1028
+ }
1029
+ setPositionColumnSetting(toolbarRef.value);
1030
+ });
1031
+
914
1032
  watch(
915
1033
  () => props.columns,
916
1034
  () => {
@@ -947,6 +1065,10 @@ export default {
947
1065
  onResize();
948
1066
  }, { deep: true },
949
1067
  );
1068
+ watch(() => props.uncheckable,
1069
+ () => {
1070
+ setStore(props.rows);
1071
+ }, { deep: true });
950
1072
  watch(
951
1073
  () => props.checked,
952
1074
  (value) => {
@@ -1032,9 +1154,18 @@ export default {
1032
1154
  }
1033
1155
  },
1034
1156
  );
1157
+ watch(
1158
+ () => props.expanded.length,
1159
+ (expendedSize) => {
1160
+ if (!expendedSize) {
1161
+ clearExpandedInfo();
1162
+ }
1163
+ },
1164
+ );
1035
1165
  watch(
1036
1166
  () => [props.option.columnWidth, resizeInfo.gridWidth],
1037
- () => {
1167
+ async () => {
1168
+ await nextTick();
1038
1169
  resizeInfo.columnWidth = props.option.columnWidth;
1039
1170
  const gridWrapper = elementInfo['grid-wrapper'];
1040
1171
  gridWrapper.style.width = resizeInfo.gridWidth;
@@ -1217,15 +1348,21 @@ export default {
1217
1348
 
1218
1349
  onBeforeMount(() => initWrapperDiv());
1219
1350
 
1351
+ const getWidth = (w) => {
1352
+ console.log(w);
1353
+ return w;
1354
+ };
1355
+
1220
1356
  return {
1357
+ getWidth,
1221
1358
  summaryScroll,
1222
1359
  showHeader,
1223
1360
  stripeStyle,
1224
1361
  borderStyle,
1225
1362
  highlightIdx,
1226
1363
  useSummary,
1227
- useColumnSetting,
1228
- toolbarWrapper,
1364
+ useGridSetting,
1365
+ toolbarRef,
1229
1366
  stores,
1230
1367
  ...toRefs(elementInfo),
1231
1368
  ...toRefs(stores),
@@ -1235,6 +1372,7 @@ export default {
1235
1372
  ...toRefs(resizeInfo),
1236
1373
  ...toRefs(selectInfo),
1237
1374
  ...toRefs(checkInfo),
1375
+ ...toRefs(expandedInfo),
1238
1376
  ...toRefs(sortInfo),
1239
1377
  ...toRefs(contextInfo),
1240
1378
  ...toRefs(columnSettingInfo),
@@ -1254,13 +1392,14 @@ export default {
1254
1392
  onRowDblClick,
1255
1393
  onCheck,
1256
1394
  onCheckAll,
1395
+ onExpanded,
1257
1396
  onSort,
1258
1397
  setSort,
1259
1398
  setStore,
1260
1399
  setContextMenu,
1261
1400
  onContextMenu,
1262
1401
  onSearch,
1263
- setColumnSetting,
1402
+ setGridSetting,
1264
1403
  onApplyColumn,
1265
1404
  onColumnContextMenu,
1266
1405
  // filtering