evui 3.3.7 → 3.3.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.
Files changed (33) hide show
  1. package/dist/evui.common.js +2973 -989
  2. package/dist/evui.common.js.map +1 -1
  3. package/dist/evui.umd.js +2973 -989
  4. package/dist/evui.umd.js.map +1 -1
  5. package/dist/evui.umd.min.js +1 -1
  6. package/dist/evui.umd.min.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/components/chart/Chart.vue +16 -4
  9. package/src/components/chart/chart.core.js +39 -14
  10. package/src/components/chart/element/element.bar.js +9 -3
  11. package/src/components/chart/element/element.heatmap.js +213 -0
  12. package/src/components/chart/element/element.pie.js +104 -13
  13. package/src/components/chart/element/element.tip.js +125 -4
  14. package/src/components/chart/helpers/helpers.constant.js +22 -0
  15. package/src/components/chart/helpers/helpers.util.js +8 -0
  16. package/src/components/chart/model/model.series.js +4 -0
  17. package/src/components/chart/model/model.store.js +207 -30
  18. package/src/components/chart/plugins/plugins.interaction.js +91 -12
  19. package/src/components/chart/plugins/plugins.legend.js +212 -42
  20. package/src/components/chart/plugins/plugins.pie.js +54 -46
  21. package/src/components/chart/plugins/plugins.tooltip.js +87 -1
  22. package/src/components/chart/scale/scale.js +12 -2
  23. package/src/components/chart/scale/scale.step.js +12 -5
  24. package/src/components/chart/scale/scale.time.category.js +12 -2
  25. package/src/components/chart/uses.js +28 -3
  26. package/src/components/grid/Grid.vue +201 -108
  27. package/src/components/grid/grid.filter.window.vue +1 -0
  28. package/src/components/grid/grid.pagination.vue +75 -0
  29. package/src/components/grid/uses.js +105 -24
  30. package/src/components/pagination/Pagination.vue +271 -0
  31. package/src/components/pagination/index.js +7 -0
  32. package/src/components/pagination/pageButton.vue +31 -0
  33. package/src/main.js +3 -0
@@ -68,19 +68,31 @@ export const commonFunctions = () => {
68
68
  };
69
69
 
70
70
  export const scrollEvent = (params) => {
71
- const { scrollInfo, stores, elementInfo, resizeInfo } = params;
71
+ const {
72
+ scrollInfo,
73
+ stores,
74
+ elementInfo,
75
+ resizeInfo,
76
+ pageInfo,
77
+ getPagingData,
78
+ updatePagingInfo,
79
+ } = params;
72
80
  /**
73
81
  * 수직 스크롤의 위치 계산 후 적용한다.
74
82
  */
75
- const updateVScroll = () => {
83
+ const updateVScroll = (isScroll) => {
76
84
  const bodyEl = elementInfo.body;
77
85
  const rowHeight = resizeInfo.rowHeight;
78
86
  if (bodyEl) {
87
+ let store = stores.store;
88
+ if (pageInfo.isClientPaging) {
89
+ store = getPagingData();
90
+ }
79
91
  const rowCount = bodyEl.clientHeight > rowHeight
80
- ? Math.ceil(bodyEl.clientHeight / rowHeight) : stores.store.length;
81
- const totalScrollHeight = stores.store.length * rowHeight;
92
+ ? Math.ceil(bodyEl.clientHeight / rowHeight) : store.length;
93
+ const totalScrollHeight = store.length * rowHeight;
82
94
  let firstVisibleIndex = Math.floor(bodyEl.scrollTop / rowHeight);
83
- if (firstVisibleIndex > stores.store.length - 1) {
95
+ if (firstVisibleIndex > store.length - 1) {
84
96
  firstVisibleIndex = 0;
85
97
  }
86
98
 
@@ -88,11 +100,17 @@ export const scrollEvent = (params) => {
88
100
  const firstIndex = Math.max(firstVisibleIndex, 0);
89
101
  const lastIndex = lastVisibleIndex;
90
102
 
91
- stores.viewStore = stores.store.slice(firstIndex, lastIndex);
92
- scrollInfo.hasVerticalScrollBar = rowCount < stores.store.length;
103
+ stores.viewStore = store.slice(firstIndex, lastIndex);
104
+ scrollInfo.hasVerticalScrollBar = rowCount < store.length;
93
105
  scrollInfo.vScrollTopHeight = firstIndex * rowHeight;
94
106
  scrollInfo.vScrollBottomHeight = totalScrollHeight - (stores.viewStore.length * rowHeight)
95
107
  - scrollInfo.vScrollTopHeight;
108
+ if (isScroll && pageInfo.isInfinite && scrollInfo.vScrollBottomHeight === 0) {
109
+ pageInfo.prevPage = pageInfo.currentPage;
110
+ pageInfo.currentPage = Math.ceil(lastIndex / pageInfo.perPage) + 1;
111
+ pageInfo.startIndex = lastIndex;
112
+ updatePagingInfo({ onScrollEnd: true });
113
+ }
96
114
  }
97
115
  };
98
116
  /**
@@ -117,7 +135,7 @@ export const scrollEvent = (params) => {
117
135
  const isVertical = !(scrollTop === lastTop);
118
136
 
119
137
  if (isVertical && bodyEl?.clientHeight) {
120
- updateVScroll();
138
+ updateVScroll(true);
121
139
  }
122
140
 
123
141
  if (isHorizontal) {
@@ -372,7 +390,7 @@ export const clickEvent = (params) => {
372
390
  };
373
391
 
374
392
  export const checkEvent = (params) => {
375
- const { checkInfo, stores } = params;
393
+ const { checkInfo, stores, pageInfo, getPagingData } = params;
376
394
  const { emit } = getCurrentInstance();
377
395
  /**
378
396
  * row에 대한 체크 상태를 해제한다.
@@ -408,7 +426,10 @@ export const checkEvent = (params) => {
408
426
  }
409
427
  checkInfo.checkedIndex.add(row[ROW_INDEX]);
410
428
 
411
- const store = stores.store;
429
+ let store = stores.store;
430
+ if (pageInfo.isClientPaging) {
431
+ store = getPagingData();
432
+ }
412
433
  const isAllChecked = store.every(d => d[ROW_CHECK_INDEX]);
413
434
  if (store.length && isAllChecked) {
414
435
  checkInfo.isHeaderChecked = true;
@@ -434,7 +455,10 @@ export const checkEvent = (params) => {
434
455
  */
435
456
  const onCheckAll = (event) => {
436
457
  const isHeaderChecked = checkInfo.isHeaderChecked;
437
- const store = stores.store;
458
+ let store = stores.store;
459
+ if (pageInfo.isClientPaging) {
460
+ store = getPagingData();
461
+ }
438
462
  store.forEach((row) => {
439
463
  if (isHeaderChecked) {
440
464
  if (!checkInfo.checkedRows.includes(row[ROW_DATA_INDEX])) {
@@ -456,7 +480,7 @@ export const checkEvent = (params) => {
456
480
  };
457
481
 
458
482
  export const sortEvent = (params) => {
459
- const { sortInfo, stores, getColumnIndex } = params;
483
+ const { sortInfo, stores, getColumnIndex, updatePagingInfo } = params;
460
484
  const { props } = getCurrentInstance();
461
485
  function OrderQueue() {
462
486
  this.orders = ['asc', 'desc', 'init'];
@@ -480,6 +504,7 @@ export const sortEvent = (params) => {
480
504
  order.enqueue(sortInfo.sortOrder);
481
505
 
482
506
  sortInfo.isSorting = true;
507
+ updatePagingInfo({ onSort: true });
483
508
  }
484
509
  };
485
510
  /**
@@ -537,9 +562,12 @@ export const filterEvent = (params) => {
537
562
  filterInfo,
538
563
  stores,
539
564
  checkInfo,
565
+ pageInfo,
540
566
  getColumnIndex,
541
567
  getConvertValue,
542
568
  updateVScroll,
569
+ getPagingData,
570
+ updatePagingInfo,
543
571
  } = params;
544
572
  /**
545
573
  * 해당 컬럼에 대한 필터 팝업을 보여준다.
@@ -763,6 +791,12 @@ export const filterEvent = (params) => {
763
791
  } else {
764
792
  checkInfo.isHeaderChecked = false;
765
793
  }
794
+ if (!searchWord && pageInfo.isClientPaging && pageInfo.prevPage) {
795
+ pageInfo.currentPage = 1;
796
+ stores.pagingStore = getPagingData();
797
+ }
798
+
799
+ updatePagingInfo({ onSearch: true });
766
800
  updateVScroll();
767
801
  }, 500);
768
802
  };
@@ -890,18 +924,65 @@ export const storeEvent = (params) => {
890
924
  updateVScroll();
891
925
  }
892
926
  };
893
- /**
894
- * 컴포넌트의 변경 데이터를 store에 업데이트한다.
895
- *
896
- * @param {number} rowIndex - row 인덱스
897
- * @param {number} cellIndex - cell 인덱스
898
- * @param {number|string} newValue - 데이터
899
- */
900
- const updateData = (rowIndex, cellIndex, newValue) => {
901
- const row = stores.store.filter(data => data[0] === rowIndex);
902
- if (row) {
903
- row[0][ROW_DATA_INDEX][cellIndex] = newValue;
927
+ return { setStore };
928
+ };
929
+
930
+ export const pagingEvent = (params) => {
931
+ const { emit } = getCurrentInstance();
932
+ const {
933
+ stores,
934
+ pageInfo,
935
+ sortInfo,
936
+ filterInfo,
937
+ elementInfo,
938
+ clearCheckInfo,
939
+ } = params;
940
+ const getPagingData = () => {
941
+ const start = (pageInfo.currentPage - 1) * pageInfo.perPage;
942
+ const end = parseInt(start, 10) + parseInt(pageInfo.perPage, 10);
943
+ return stores.store.slice(start, end);
944
+ };
945
+ const updatePagingInfo = (eventName) => {
946
+ emit('page-change', {
947
+ eventName,
948
+ pageInfo: {
949
+ currentPage: pageInfo.currentPage,
950
+ prevPage: pageInfo.prevPage,
951
+ startIndex: pageInfo.startIndex,
952
+ total: pageInfo.total,
953
+ perPage: pageInfo.perPage,
954
+ },
955
+ sortInfo: {
956
+ field: sortInfo.sortField,
957
+ order: sortInfo.sortOrder,
958
+ },
959
+ searchInfo: {
960
+ searchWord: filterInfo.searchWord,
961
+ searchColumns: stores.orderedColumns
962
+ .filter(c => !c.hide && (c?.searchable === undefined || c?.searchable))
963
+ .map(d => d.field),
964
+ },
965
+ });
966
+ if (pageInfo.isInfinite && (eventName?.onSearch || eventName?.onSort)) {
967
+ pageInfo.currentPage = 1;
968
+ elementInfo.body.scrollTop = 0;
969
+ clearCheckInfo();
970
+ }
971
+ };
972
+ const changePage = (beforeVal) => {
973
+ if (pageInfo.isClientPaging) {
974
+ pageInfo.prevPage = beforeVal;
975
+ if (stores.store.length <= pageInfo.perPage) {
976
+ stores.pagingStore = stores.store;
977
+ } else {
978
+ const start = (pageInfo.currentPage - 1) * pageInfo.perPage;
979
+ const end = parseInt(start, 10) + parseInt(pageInfo.perPage, 10);
980
+ stores.pagingStore = stores.store.slice(start, end);
981
+ elementInfo.body.scrollTop = 0;
982
+ pageInfo.startIndex = start;
983
+ }
904
984
  }
985
+ updatePagingInfo({ onChangePage: true });
905
986
  };
906
- return { setStore, updateData };
987
+ return { getPagingData, updatePagingInfo, changePage };
907
988
  };
@@ -0,0 +1,271 @@
1
+ <template>
2
+ <nav class="pagination">
3
+ <!-- Page Info -->
4
+ <small v-if="showPageInfo" class="pagination-info">
5
+ <template v-if="perPage === 1">
6
+ {{ firstData }} / {{ total }}
7
+ </template>
8
+ <template v-else>
9
+ {{ firstData }} - {{ Math.min(current * perPage, total) }}
10
+ / {{ total }}
11
+ </template>
12
+ </small>
13
+ <ul class="pagination-list" :style="listClasses">
14
+ <!-- Previous -->
15
+ <li :class="{'is-disabled': !hasPrev}">
16
+ <page-button
17
+ class="pagination-previous"
18
+ :disabled="!hasPrev"
19
+ :page="getPage(current - 1)"
20
+ >
21
+ <ev-icon icon="ev-icon-s-arrow-left"/>
22
+ </page-button>
23
+ </li>
24
+
25
+ <!--Pages-->
26
+ <template v-for="(page, i) in pagesInRange" :key="i">
27
+ <li v-if="page.number === -1" class="pagination-ellipsis">
28
+ <span>&hellip;</span>
29
+ </li>
30
+ <li v-else :class="{ 'is-current': page.isCurrent, 'is-page': true }" @click="page.click">
31
+ <page-button :page="page"/>
32
+ </li>
33
+ </template>
34
+
35
+ <!-- Next -->
36
+ <li :class="{'is-disabled': !hasNext}">
37
+ <page-button
38
+ class="pagination-next"
39
+ :disabled="!hasNext"
40
+ :page="getPage(current + 1)"
41
+ >
42
+ <ev-icon icon="ev-icon-s-arrow-right"/>
43
+ </page-button>
44
+ </li>
45
+ </ul>
46
+ </nav>
47
+ </template>
48
+
49
+ <script>
50
+ import { computed, nextTick, watch } from 'vue';
51
+ import EvIcon from '@/components/icon/Icon';
52
+ import pageButton from './pageButton';
53
+
54
+ export default {
55
+ name: 'EvPagination',
56
+ components: {
57
+ EvIcon,
58
+ pageButton,
59
+ },
60
+ props: {
61
+ total: {
62
+ type: [Number, String],
63
+ default: 0,
64
+ },
65
+ visiblePage: {
66
+ type: [Number, String],
67
+ default: 8,
68
+ },
69
+ perPage: {
70
+ type: [Number, String],
71
+ default: 20,
72
+ },
73
+ modelValue: {
74
+ type: [Number, String],
75
+ default: 1,
76
+ },
77
+ showPageInfo: {
78
+ type: Boolean,
79
+ default: false,
80
+ },
81
+ order: {
82
+ type: String,
83
+ default: 'left',
84
+ validator: val => ['left', 'right', 'center'].includes(val),
85
+ },
86
+ },
87
+ emits: {
88
+ 'update:modelValue': null,
89
+ change: null,
90
+ },
91
+ setup(props, { emit }) {
92
+ const visiblePage = computed(() => (props.visiblePage > 7 ? props.visiblePage : 7));
93
+ const current = computed(() => props.modelValue);
94
+ const pageCount = computed(() => (props.total === 0
95
+ ? 1 : Math.ceil(props.total / props.perPage)));
96
+ const hasPrev = computed(() => current.value > 1);
97
+ const hasNext = computed(() => current.value < pageCount.value);
98
+ const firstData = computed(() => {
99
+ const item = current.value * props.perPage - props.perPage + 1;
100
+ return item >= 0 ? item : 0;
101
+ });
102
+ const changePage = (num, event) => {
103
+ if (current.value === num || num < 1 || num > pageCount.value) return;
104
+ emit('update:modelValue', num);
105
+ emit('change', num);
106
+ if (event && event.target) {
107
+ nextTick(() => event.target.focus());
108
+ }
109
+ };
110
+ const getPage = (num, options = {}) => ({
111
+ number: num,
112
+ isCurrent: current.value === num,
113
+ click: event => changePage(num, event),
114
+ input: (event, inputNum) => changePage(+inputNum, event),
115
+ disabled: options.disabled || false,
116
+ class: options.class || '',
117
+ });
118
+ const onRange = (from, to) => {
119
+ const range = [];
120
+ const f = from > 0 ? from : 1;
121
+
122
+ for (let i = f; i <= to; i++) {
123
+ range.push(getPage(i));
124
+ }
125
+
126
+ return range;
127
+ };
128
+ const pagesInRange = computed(() => {
129
+ const totalVisible = parseInt(visiblePage.value, 10);
130
+
131
+ if (totalVisible === 0) {
132
+ return [];
133
+ }
134
+
135
+ const maxLength = Math.min(
136
+ Math.max(0, totalVisible) || pageCount.value,
137
+ pageCount.value,
138
+ );
139
+ if (pageCount.value <= maxLength) {
140
+ return onRange(1, pageCount.value);
141
+ }
142
+ const even = maxLength % 2 === 0 ? 1 : 0;
143
+ const left = Math.floor(maxLength / 2);
144
+ const right = pageCount.value - left + 1 + even;
145
+
146
+ if (current.value > left && current.value < right) {
147
+ const firstItem = 1;
148
+ const lastItem = pageCount.value;
149
+ const start = current.value - left + 2;
150
+ const end = current.value + left - 2 - even;
151
+ const secondItem = start - 1 === firstItem + 1 ? 2 : -1;
152
+ const beforeLastItem = end + 1 === lastItem - 1 ? end + 1 : -1;
153
+ return [
154
+ getPage(1),
155
+ getPage(secondItem),
156
+ ...onRange(start, end),
157
+ getPage(beforeLastItem),
158
+ getPage(pageCount.value),
159
+ ];
160
+ } else if (current.value === left) {
161
+ const end = current.value + left - 1 - even;
162
+ return [...onRange(1, end), getPage(-1), getPage(pageCount.value)];
163
+ } else if (current.value === right) {
164
+ const start = current.value - left + 1;
165
+ return [getPage(1), getPage(-1), ...onRange(start, pageCount.value)];
166
+ }
167
+ return [
168
+ ...onRange(1, left),
169
+ getPage(-1),
170
+ ...onRange(right, pageCount.value),
171
+ ];
172
+ });
173
+ const listClasses = computed(() => ({ 'justify-content': props.order, flex: '3 1 0%' }));
174
+
175
+ watch(
176
+ () => pageCount.value,
177
+ (value) => {
178
+ if (current.value > value) {
179
+ changePage(value);
180
+ }
181
+ },
182
+ );
183
+ watch(
184
+ () => current.value,
185
+ (after, before) => {
186
+ if (pageCount.value < after) {
187
+ changePage(before);
188
+ }
189
+ },
190
+ );
191
+ return {
192
+ pageCount,
193
+ pagesInRange,
194
+ hasPrev,
195
+ hasNext,
196
+ listClasses,
197
+ firstData,
198
+ current,
199
+ changePage,
200
+ getPage,
201
+ };
202
+ },
203
+ };
204
+ </script>
205
+
206
+ <style lang="scss">
207
+ .pagination {
208
+ display: flex;
209
+ .pagination-next,
210
+ .pagination-previous {
211
+ margin: 0;
212
+ padding-left: 10px;
213
+ padding-right: 10px;
214
+ background: center center no-repeat;
215
+ background-size: 16px;
216
+ cursor: pointer;
217
+ &:hover {
218
+ color: #1A6AFE;
219
+ }
220
+ }
221
+ &-info {
222
+ order: 2;
223
+ line-height: 32px;
224
+ }
225
+ .is-current {
226
+ pointer-events: none;
227
+ cursor: not-allowed;
228
+ background-color: #1A6AFE;
229
+ color: #FFFFFF;
230
+ border-radius: 4px;
231
+ }
232
+ .is-page {
233
+ &:hover {
234
+ color: #1A6AFE;
235
+ }
236
+ }
237
+ .pagination-ellipsis {
238
+ pointer-events: none;
239
+ cursor: not-allowed;
240
+ }
241
+ .is-disabled {
242
+ pointer-events: none;
243
+ cursor: not-allowed;
244
+ opacity: 0.5;
245
+ color: #C0C4CC;
246
+ }
247
+ &-list {
248
+ display: flex;
249
+ user-select: none;
250
+ list-style: none;
251
+ font-size: 0;
252
+ padding: 0;
253
+ margin: 0;
254
+ align-items: center;
255
+ flex-wrap: wrap;
256
+ li {
257
+ display: flex;
258
+ height: 32px;
259
+ padding: 0 4px;
260
+ justify-content: center;
261
+ align-items: center;
262
+ font-size: 14px;
263
+ min-width: 32px;
264
+ line-height: 32px;
265
+ cursor: pointer;
266
+ box-sizing: border-box;
267
+ text-align: center;
268
+ }
269
+ }
270
+ }
271
+ </style>
@@ -0,0 +1,7 @@
1
+ import EvPagination from './Pagination';
2
+
3
+ EvPagination.install = (app) => {
4
+ app.component(EvPagination.name, EvPagination);
5
+ };
6
+
7
+ export default EvPagination;
@@ -0,0 +1,31 @@
1
+ <template>
2
+ <span
3
+ :class="{
4
+ 'is-current': page.isCurrent,
5
+ 'is-disabled': disabled || page.disabled,
6
+ [page.class]: true
7
+ }"
8
+ v-bind="$attrs"
9
+ :aria-current="page.isCurrent"
10
+ @click.prevent="page.click"
11
+ >
12
+ <slot>{{ page.number }}</slot>
13
+ </span>
14
+ </template>
15
+
16
+ <script>
17
+
18
+ export default {
19
+ name: 'PageButton',
20
+ props: {
21
+ page: {
22
+ type: Object,
23
+ required: true,
24
+ },
25
+ disabled: {
26
+ type: Boolean,
27
+ default: false,
28
+ },
29
+ },
30
+ };
31
+ </script>
package/src/main.js CHANGED
@@ -28,6 +28,7 @@ import EvTimePicker from '@/components/timePicker/';
28
28
  import EvGrid from '@/components/grid/';
29
29
  import EvChart from '@/components/chart/';
30
30
  import EvTreeGrid from '@/components/treeGrid/';
31
+ import EvPagination from '@/components/pagination/';
31
32
  import { version } from '../package.json';
32
33
 
33
34
  const components = [
@@ -61,6 +62,7 @@ const components = [
61
62
  EvNotification,
62
63
  EvMessageBox,
63
64
  EvTreeGrid,
65
+ EvPagination,
64
66
  ];
65
67
 
66
68
  const install = (app) => {
@@ -108,6 +110,7 @@ export {
108
110
  EvNotification,
109
111
  EvMessageBox,
110
112
  EvTreeGrid,
113
+ EvPagination,
111
114
  };
112
115
 
113
116
  export default EVUI;