evui 3.3.36 → 3.3.39

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 (141) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +40 -40
  3. package/dist/evui.common.js +1907 -1832
  4. package/dist/evui.common.js.map +1 -1
  5. package/dist/evui.umd.js +1907 -1832
  6. package/dist/evui.umd.js.map +1 -1
  7. package/dist/evui.umd.min.js +1 -1
  8. package/dist/evui.umd.min.js.map +1 -1
  9. package/dist/img/{EVUI.7f3588fb.svg → EVUI.b82ee81a.svg} +292 -292
  10. package/dist/img/{icon_mysql.7ea26d5d.svg → icon_mysql.1085fdc9.svg} +78 -78
  11. package/dist/img/{icon_oracle.9009b108.svg → icon_oracle.0572d3ee.svg} +13 -13
  12. package/dist/img/{icon_postgresql.f8fffba9.svg → icon_postgresql.ee12bde8.svg} +58 -58
  13. package/package.json +61 -61
  14. package/src/common/emitter.js +20 -20
  15. package/src/common/utils.debounce.js +223 -223
  16. package/src/common/utils.js +134 -134
  17. package/src/common/utils.table.js +78 -78
  18. package/src/common/utils.throttle.js +83 -83
  19. package/src/common/utils.tree.js +18 -18
  20. package/src/components/button/Button.vue +198 -198
  21. package/src/components/button/index.js +7 -7
  22. package/src/components/buttonGroup/ButtonGroup.vue +11 -11
  23. package/src/components/buttonGroup/index.js +7 -7
  24. package/src/components/calendar/Calendar.vue +661 -661
  25. package/src/components/calendar/index.js +7 -7
  26. package/src/components/calendar/uses.js +1272 -1272
  27. package/src/components/chart/Chart.vue +189 -192
  28. package/src/components/chart/chart.core.js +870 -870
  29. package/src/components/chart/element/element.bar.js +524 -524
  30. package/src/components/chart/element/element.bar.time.js +156 -156
  31. package/src/components/chart/element/element.heatmap.js +533 -533
  32. package/src/components/chart/element/element.line.js +339 -339
  33. package/src/components/chart/element/element.pie.js +197 -197
  34. package/src/components/chart/element/element.scatter.js +184 -184
  35. package/src/components/chart/element/element.tip.js +550 -542
  36. package/src/components/chart/helpers/helpers.canvas.js +265 -265
  37. package/src/components/chart/helpers/helpers.constant.js +206 -206
  38. package/src/components/chart/helpers/helpers.util.js +346 -338
  39. package/src/components/chart/index.js +9 -9
  40. package/src/components/chart/model/index.js +4 -4
  41. package/src/components/chart/model/model.series.js +93 -93
  42. package/src/components/chart/model/model.store.js +977 -967
  43. package/src/components/chart/plugins/plugins.interaction.js +769 -769
  44. package/src/components/chart/plugins/plugins.legend.gradient.js +602 -602
  45. package/src/components/chart/plugins/plugins.legend.js +1155 -1151
  46. package/src/components/chart/plugins/plugins.pie.js +254 -254
  47. package/src/components/chart/plugins/plugins.title.js +56 -56
  48. package/src/components/chart/plugins/plugins.tooltip.js +692 -692
  49. package/src/components/chart/scale/scale.js +848 -848
  50. package/src/components/chart/scale/scale.linear.js +38 -38
  51. package/src/components/chart/scale/scale.logarithmic.js +128 -128
  52. package/src/components/chart/scale/scale.step.js +336 -336
  53. package/src/components/chart/scale/scale.time.category.js +277 -277
  54. package/src/components/chart/scale/scale.time.js +48 -48
  55. package/src/components/chart/style/chart.scss +312 -312
  56. package/src/components/chart/uses.js +264 -252
  57. package/src/components/checkbox/Checkbox.vue +200 -200
  58. package/src/components/checkbox/index.js +7 -7
  59. package/src/components/checkboxGroup/CheckboxGroup.vue +44 -44
  60. package/src/components/checkboxGroup/index.js +7 -7
  61. package/src/components/contextMenu/ContextMenu.vue +80 -80
  62. package/src/components/contextMenu/MenuList.vue +149 -149
  63. package/src/components/contextMenu/index.js +7 -7
  64. package/src/components/contextMenu/uses.js +203 -203
  65. package/src/components/datePicker/DatePicker.vue +437 -437
  66. package/src/components/datePicker/index.js +7 -7
  67. package/src/components/datePicker/uses.js +419 -419
  68. package/src/components/grid/Grid.vue +827 -827
  69. package/src/components/grid/grid.filter.window.vue +493 -493
  70. package/src/components/grid/grid.pagination.vue +75 -75
  71. package/src/components/grid/grid.summary.vue +265 -265
  72. package/src/components/grid/grid.toolbar.vue +26 -26
  73. package/src/components/grid/index.js +11 -11
  74. package/src/components/grid/style/grid.scss +263 -263
  75. package/src/components/grid/uses.js +1002 -1007
  76. package/src/components/icon/Icon.vue +49 -49
  77. package/src/components/icon/index.js +8 -8
  78. package/src/components/inputNumber/InputNumber.vue +212 -212
  79. package/src/components/inputNumber/index.js +7 -7
  80. package/src/components/inputNumber/uses.js +217 -217
  81. package/src/components/loading/Loading.vue +125 -125
  82. package/src/components/loading/index.js +7 -7
  83. package/src/components/menu/Menu.vue +68 -68
  84. package/src/components/menu/MenuItem.vue +187 -187
  85. package/src/components/menu/index.js +7 -7
  86. package/src/components/message/Message.vue +223 -223
  87. package/src/components/message/index.js +31 -31
  88. package/src/components/messageBox/MessageBox.vue +358 -358
  89. package/src/components/messageBox/index.js +22 -22
  90. package/src/components/notification/Notification.vue +316 -316
  91. package/src/components/notification/index.js +49 -49
  92. package/src/components/pagination/Pagination.vue +271 -271
  93. package/src/components/pagination/index.js +7 -7
  94. package/src/components/pagination/pageButton.vue +30 -30
  95. package/src/components/progress/Progress.vue +139 -139
  96. package/src/components/progress/index.js +7 -7
  97. package/src/components/radio/Radio.vue +159 -159
  98. package/src/components/radio/index.js +7 -7
  99. package/src/components/radioGroup/RadioGroup.vue +41 -41
  100. package/src/components/radioGroup/index.js +7 -7
  101. package/src/components/scheduler/Scheduler.vue +149 -149
  102. package/src/components/scheduler/index.js +7 -7
  103. package/src/components/scheduler/uses.js +183 -183
  104. package/src/components/select/Select.vue +440 -440
  105. package/src/components/select/index.js +7 -7
  106. package/src/components/select/uses.js +270 -270
  107. package/src/components/slider/Slider.vue +505 -505
  108. package/src/components/slider/index.js +7 -7
  109. package/src/components/slider/uses.js +390 -390
  110. package/src/components/tabPanel/TabPanel.vue +74 -74
  111. package/src/components/tabPanel/index.js +7 -7
  112. package/src/components/tabs/Tabs.vue +517 -517
  113. package/src/components/tabs/index.js +7 -7
  114. package/src/components/textField/TextField.vue +375 -375
  115. package/src/components/textField/index.js +7 -7
  116. package/src/components/timePicker/TimePicker.vue +352 -352
  117. package/src/components/timePicker/index.js +7 -7
  118. package/src/components/toggle/Toggle.vue +115 -115
  119. package/src/components/toggle/index.js +7 -7
  120. package/src/components/tree/Tree.vue +313 -313
  121. package/src/components/tree/TreeNode.vue +293 -293
  122. package/src/components/tree/index.js +7 -7
  123. package/src/components/treeGrid/TreeGrid.vue +758 -758
  124. package/src/components/treeGrid/TreeGridNode.vue +275 -275
  125. package/src/components/treeGrid/index.js +9 -9
  126. package/src/components/treeGrid/style/treeGrid.scss +261 -261
  127. package/src/components/treeGrid/treeGrid.toolbar.vue +26 -26
  128. package/src/components/treeGrid/uses.js +867 -867
  129. package/src/components/window/Window.vue +329 -329
  130. package/src/components/window/index.js +7 -7
  131. package/src/components/window/uses.js +899 -899
  132. package/src/directives/clickoutside.js +90 -90
  133. package/src/main.js +116 -116
  134. package/src/style/components/input.scss +108 -108
  135. package/src/style/functions.scss +3 -3
  136. package/src/style/index.scss +6 -6
  137. package/src/style/lib/fonts/EVUI.svg +292 -292
  138. package/src/style/lib/icon.css +888 -888
  139. package/src/style/mixins.scss +94 -94
  140. package/src/style/themes.scss +67 -67
  141. package/src/style/variables.scss +22 -22
@@ -1,419 +1,419 @@
1
- import {
2
- ref, reactive, computed, watch,
3
- nextTick, getCurrentInstance,
4
- } from 'vue';
5
- import { getChangedValueByTimeFormat } from '../calendar/uses';
6
-
7
- const dateReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/);
8
- const dateTimeReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]/);
9
-
10
- export const useModel = () => {
11
- const { props, emit } = getCurrentInstance();
12
- const timeFormat = props.options?.timeFormat;
13
-
14
- // Select 컴포넌트의 v-model 값
15
- const mv = computed({
16
- get: () => {
17
- if (!props.modelValue) {
18
- return (props.mode === 'date' || props.mode === 'dateTime') ? '' : [];
19
- }
20
- if (['dateTime', 'dateTimeRange'].includes(props.mode) && timeFormat) {
21
- if (props.mode === 'dateTime') {
22
- return getChangedValueByTimeFormat(timeFormat, props.modelValue);
23
- } else if (props.modelValue.length) {
24
- const [fromTimeFormat, toTimeFormat] = timeFormat;
25
- return [
26
- getChangedValueByTimeFormat(fromTimeFormat, props.modelValue[0]),
27
- getChangedValueByTimeFormat(toTimeFormat, props.modelValue[1]),
28
- ];
29
- }
30
- }
31
- return props.modelValue;
32
- },
33
- set: value => emit('update:modelValue', value),
34
- });
35
-
36
- // mode: 'date' or 'dateTime'시 input box의 입력된 텍스트값
37
- let currentValue;
38
- if (['dateTimeRange', 'dateTime'].includes(props.mode) && timeFormat) {
39
- if (props.mode === 'dateTimeRange' && Array.isArray(props.modelValue) && props.modelValue.length === 2) {
40
- const [fromDate, toDate] = props.modelValue;
41
- const [fromTimeFormat, toTimeFormat] = timeFormat;
42
-
43
- props.modelValue = [
44
- getChangedValueByTimeFormat(fromTimeFormat, fromDate),
45
- getChangedValueByTimeFormat(toTimeFormat, toDate),
46
- ];
47
- currentValue = ref(props.modelValue);
48
- } else if (props.mode === 'dateTime' && props.modelValue) {
49
- currentValue = ref(getChangedValueByTimeFormat(timeFormat, props.modelValue));
50
- } else {
51
- currentValue = ref(props.modelValue);
52
- }
53
- } else {
54
- currentValue = ref(props.modelValue);
55
- }
56
-
57
- const validateValue = (curr) => {
58
- if (props.mode === 'date'
59
- && (curr.length !== 10 || !dateReg.exec(curr))
60
- ) {
61
- currentValue.value = mv.value;
62
- } else if (props.mode === 'dateTime'
63
- && (curr.length !== 19 || !dateTimeReg.exec(curr))
64
- ) {
65
- currentValue.value = mv.value;
66
- } else {
67
- mv.value = curr;
68
- }
69
- };
70
-
71
- // clearable 모드일 때, 항목(mv) 전체 삭제 아이콘 존재여부
72
- const isClearableIcon = computed(() => {
73
- if (props.mode === 'date' || props.mode === 'dateTime') {
74
- return mv.value;
75
- }
76
- return mv.value.length;
77
- });
78
-
79
- /**
80
- * clearable모드일 때 [x] 아이콘 클릭 시 mv값을 초기화
81
- */
82
- const removeAllMv = () => {
83
- if (props.mode === 'date' || props.mode === 'dateTime') {
84
- mv.value = null;
85
- } else {
86
- mv.value.splice(0);
87
- mv.value = [...mv.value];
88
- }
89
- };
90
-
91
- /**
92
- * mode: dateMulti, type: date인 경우 선택된 value를 mv에서 삭제하는 로직
93
- * @param val - tagWrapper에서 [x]클릭된 목록의 value
94
- */
95
- const removeMv = (val) => {
96
- if (!props.disabled) {
97
- const idx = mv.value.indexOf(val);
98
- mv.value.splice(idx, 1);
99
- mv.value = [...mv.value];
100
- }
101
- };
102
-
103
- /**
104
- * 해당 컴포넌트의 v-model값이 변경(change)되는 이벤트
105
- * @param e
106
- */
107
- const changeMv = async (e) => {
108
- await nextTick();
109
- emit('change', mv.value, e);
110
- };
111
-
112
- return {
113
- mv,
114
- currentValue,
115
- isClearableIcon,
116
- validateValue,
117
- removeAllMv,
118
- changeMv,
119
- removeMv,
120
- };
121
- };
122
-
123
- export const useDropdown = () => {
124
- const { props } = getCurrentInstance();
125
-
126
- const isDropbox = ref(false);
127
- const datePicker = ref(null);
128
- const datePickerWrapper = ref(null);
129
- const dropbox = ref(null);
130
- const itemWrapper = ref(null);
131
- const dropboxPosition = reactive({
132
- top: null,
133
- right: null,
134
- left: null,
135
- });
136
-
137
- /**
138
- * dropdown box 위치 변경하는 메소드
139
- */
140
- const changeDropboxPosition = async () => {
141
- await nextTick();
142
- const datePickerRect = datePickerWrapper.value?.getBoundingClientRect();
143
- const dropboxRect = dropbox.value?.getBoundingClientRect();
144
- const datePickerHeight = datePickerRect.height;
145
- const datePickerY = datePickerRect.y;
146
- const datePickerX = datePickerRect.x;
147
- const dropboxHeight = dropboxRect.height;
148
- const dropboxWidth = dropboxRect.width;
149
- const docHeight = document.documentElement.clientHeight;
150
- const docWidth = document.documentElement.clientWidth;
151
- if (docHeight < datePickerY + datePickerHeight + dropboxHeight) {
152
- // dropTop
153
- dropboxPosition.top = `-${dropboxHeight}px`;
154
- if (docWidth < datePickerX + dropboxWidth) {
155
- dropboxPosition.left = 'auto';
156
- dropboxPosition.right = '0px';
157
- } else {
158
- dropboxPosition.left = '0px';
159
- dropboxPosition.right = 'auto';
160
- }
161
- } else {
162
- // dropDown
163
- dropboxPosition.top = `${datePickerHeight}px`;
164
- if (docWidth < datePickerX + dropboxWidth) {
165
- dropboxPosition.left = 'auto';
166
- dropboxPosition.right = '0px';
167
- } else {
168
- dropboxPosition.left = '0px';
169
- dropboxPosition.right = 'auto';
170
- }
171
- }
172
- };
173
-
174
- /**
175
- * 인풋박스 클릭 이벤트
176
- * props로 받는 항목이 없는 경우 return처리
177
- * 인풋박스 위 클릭된 이벤트위치로 드롭박스의 사이즈, 위치를 계산
178
- */
179
- const clickSelectInput = async () => {
180
- if (!props.disabled) {
181
- isDropbox.value = !isDropbox.value;
182
- if (isDropbox.value) {
183
- await changeDropboxPosition();
184
- }
185
- }
186
- };
187
-
188
- /**
189
- * 드롭박스 외부 클릭 이벤트
190
- * filterable 모드인 경우는 필터링텍스트를 비운다.
191
- */
192
- const clickOutsideDropbox = () => {
193
- isDropbox.value = false;
194
- };
195
-
196
- return {
197
- isDropbox,
198
- datePicker,
199
- datePickerWrapper,
200
- dropbox,
201
- itemWrapper,
202
- dropboxPosition,
203
- clickSelectInput,
204
- clickOutsideDropbox,
205
- changeDropboxPosition,
206
- };
207
- };
208
-
209
- export const useShortcuts = (param) => {
210
- const { props } = getCurrentInstance();
211
- const { mv, currentValue, clickOutsideDropbox } = param;
212
-
213
- const usedShortcuts = reactive([]);
214
- props.shortcuts?.forEach(({ value, label, shortcutDate }) => {
215
- usedShortcuts.push({
216
- key: value,
217
- label,
218
- shortcutDate,
219
- isActive: false,
220
- });
221
- });
222
-
223
- /**
224
- * active 되어있는 shortcut 제거
225
- */
226
- const clearShortcuts = () => {
227
- const targetShortcut = usedShortcuts.find(shortcut => shortcut.isActive);
228
- if (targetShortcut) {
229
- targetShortcut.isActive = false;
230
- }
231
- };
232
-
233
- /**
234
- * targetKey에 해당하는 shortcut을 active
235
- * @param targetKey
236
- */
237
- const activeShortcut = (targetKey) => {
238
- const targetShortcut = usedShortcuts.find(shortcut => shortcut.key === targetKey);
239
- if (targetShortcut) {
240
- targetShortcut.isActive = true;
241
- }
242
- };
243
-
244
- /**
245
- * 월, 일을 두자리 숫자로 보정
246
- * @param num
247
- * @returns {string|*}
248
- */
249
- const lpadToTwoDigits = (num) => {
250
- if (num === null) {
251
- return '00';
252
- } else if (+num < 10) {
253
- return `0${num}`;
254
- }
255
- return num;
256
- };
257
-
258
- /**
259
- * 'YYYY-MM-DD' 형식으로 format
260
- * @param targetDate
261
- * @returns string
262
- */
263
- const formatDate = (targetDate) => {
264
- const dateValue = targetDate ? new Date(targetDate) : new Date();
265
- const year = dateValue.getFullYear();
266
- const month = dateValue.getMonth() + 1;
267
- const day = dateValue.getDate();
268
- return `${year}-${lpadToTwoDigits(month)}-${lpadToTwoDigits(day)}`;
269
- };
270
-
271
- /**
272
- * 'YYYY-MM-DD HH:mm:ss' 형식으로 format
273
- * @param targetDateTime
274
- * @returns string
275
- */
276
- const formatDateTime = (targetDateTime) => {
277
- const dateTimeValue = targetDateTime ? new Date(targetDateTime) : new Date();
278
- const hour = dateTimeValue.getHours();
279
- const min = dateTimeValue.getMinutes();
280
- const sec = dateTimeValue.getSeconds();
281
- return `${formatDate(dateTimeValue)} ${lpadToTwoDigits(hour)}:${lpadToTwoDigits(min)}:${lpadToTwoDigits(sec)}`;
282
- };
283
-
284
- /**
285
- * 초기 shortcut 세팅
286
- * 해당하는 날짜면 active
287
- */
288
- const setActiveShortcut = () => {
289
- clearShortcuts();
290
-
291
- const isRange = ['dateRange', 'dateTimeRange'].includes(props.mode);
292
-
293
- if (!usedShortcuts.length
294
- || (props.mode === 'dateMulti' && props.options?.multiType !== 'date')
295
- || (isRange && !mv.value.length)
296
- || (!isRange && !mv.value)
297
- ) {
298
- return;
299
- }
300
-
301
- let targetKey;
302
- if (isRange) {
303
- const timeFormat = props.options?.timeFormat;
304
- const [fromDate, toDate] = mv.value;
305
- let targetShortcut;
306
- if (props.mode === 'dateTimeRange' && timeFormat?.length) {
307
- targetShortcut = usedShortcuts.find(({ shortcutDate }) => {
308
- const [sFromDate, sToDate] = shortcutDate();
309
- const [fromTimeFormat, toTimeFormat] = timeFormat;
310
- const formatFromDate = getChangedValueByTimeFormat(
311
- fromTimeFormat,
312
- formatDateTime(sFromDate),
313
- );
314
- const formatToDate = getChangedValueByTimeFormat(
315
- toTimeFormat,
316
- formatDateTime(sToDate),
317
- );
318
- const isCorrectFromDate = formatFromDate === formatDateTime(fromDate);
319
- const isCorrectToDate = formatToDate === formatDateTime(toDate);
320
- return isCorrectFromDate && isCorrectToDate;
321
- });
322
- } else {
323
- targetShortcut = usedShortcuts.find(({ shortcutDate }) => {
324
- const [sFromDate, sToDate] = shortcutDate();
325
- const formatFunc = props.mode === 'dateTimeRange' ? formatDateTime : formatDate;
326
- const isCorrectFromDate = formatFunc(sFromDate) === formatFunc(fromDate);
327
- const isCorrectToDate = formatFunc(sToDate) === formatFunc(toDate);
328
- return isCorrectFromDate && isCorrectToDate;
329
- });
330
- }
331
- targetKey = targetShortcut?.key;
332
- } else {
333
- const formatFunc = props.mode === 'dateTime' ? formatDateTime : formatDate;
334
- const date = formatFunc(mv.value);
335
- const targetShortcut = usedShortcuts.find(({ shortcutDate }) => {
336
- const sDate = formatFunc(shortcutDate());
337
- return sDate === date;
338
- });
339
- targetKey = targetShortcut?.key;
340
- }
341
-
342
- if (targetKey) {
343
- activeShortcut(targetKey);
344
- }
345
- };
346
-
347
- /**
348
- * shortcut을 클릭했을 때 이벤트
349
- * @param targetKey
350
- */
351
- const clickShortcut = (targetKey) => {
352
- const isRange = ['dateRange', 'dateTimeRange'].includes(props.mode);
353
- const targetShortcut = usedShortcuts.find(({ key }) => key === targetKey);
354
-
355
- if (!targetShortcut) {
356
- return;
357
- }
358
-
359
- const shortcutDate = targetShortcut.shortcutDate;
360
- const timeFormat = props.options?.timeFormat;
361
-
362
- if (isRange) {
363
- const [fromDate, toDate] = shortcutDate();
364
- if (props.mode === 'dateTimeRange') {
365
- if (timeFormat?.length) {
366
- const [fromTimeFormat, toTimeFormat] = timeFormat;
367
-
368
- mv.value = [
369
- getChangedValueByTimeFormat(fromTimeFormat, formatDateTime(fromDate)),
370
- getChangedValueByTimeFormat(toTimeFormat, formatDateTime(toDate)),
371
- ];
372
- } else {
373
- mv.value = [formatDateTime(fromDate), formatDateTime(toDate)];
374
- }
375
- } else {
376
- mv.value = [formatDate(fromDate), formatDate(toDate)];
377
- }
378
- } else {
379
- const sDate = shortcutDate();
380
- mv.value = props.mode === 'dateTime'
381
- ? getChangedValueByTimeFormat(
382
- timeFormat,
383
- formatDateTime(sDate))
384
- : formatDate(sDate);
385
- }
386
-
387
- clearShortcuts();
388
- activeShortcut(targetKey);
389
- };
390
-
391
- watch(
392
- () => props.modelValue,
393
- (curr) => {
394
- setActiveShortcut();
395
- if (props.mode === 'dateMulti'
396
- && props?.options?.multiType === 'date'
397
- && props?.options?.multiDayLimit > curr.length
398
- ) {
399
- return;
400
- } else if (
401
- props.mode === 'dateRange'
402
- || props.mode === 'dateTime'
403
- || props.mode === 'dateTimeRange'
404
- ) {
405
- currentValue.value = curr;
406
- return;
407
- } else if (props.mode === 'date') {
408
- currentValue.value = curr;
409
- }
410
- clickOutsideDropbox();
411
- },
412
- );
413
-
414
- return {
415
- usedShortcuts,
416
- clickShortcut,
417
- setActiveShortcut,
418
- };
419
- };
1
+ import {
2
+ ref, reactive, computed, watch,
3
+ nextTick, getCurrentInstance,
4
+ } from 'vue';
5
+ import { getChangedValueByTimeFormat } from '../calendar/uses';
6
+
7
+ const dateReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/);
8
+ const dateTimeReg = new RegExp(/[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]/);
9
+
10
+ export const useModel = () => {
11
+ const { props, emit } = getCurrentInstance();
12
+ const timeFormat = props.options?.timeFormat;
13
+
14
+ // Select 컴포넌트의 v-model 값
15
+ const mv = computed({
16
+ get: () => {
17
+ if (!props.modelValue) {
18
+ return (props.mode === 'date' || props.mode === 'dateTime') ? '' : [];
19
+ }
20
+ if (['dateTime', 'dateTimeRange'].includes(props.mode) && timeFormat) {
21
+ if (props.mode === 'dateTime') {
22
+ return getChangedValueByTimeFormat(timeFormat, props.modelValue);
23
+ } else if (props.modelValue.length) {
24
+ const [fromTimeFormat, toTimeFormat] = timeFormat;
25
+ return [
26
+ getChangedValueByTimeFormat(fromTimeFormat, props.modelValue[0]),
27
+ getChangedValueByTimeFormat(toTimeFormat, props.modelValue[1]),
28
+ ];
29
+ }
30
+ }
31
+ return props.modelValue;
32
+ },
33
+ set: value => emit('update:modelValue', value),
34
+ });
35
+
36
+ // mode: 'date' or 'dateTime'시 input box의 입력된 텍스트값
37
+ let currentValue;
38
+ if (['dateTimeRange', 'dateTime'].includes(props.mode) && timeFormat) {
39
+ if (props.mode === 'dateTimeRange' && Array.isArray(props.modelValue) && props.modelValue.length === 2) {
40
+ const [fromDate, toDate] = props.modelValue;
41
+ const [fromTimeFormat, toTimeFormat] = timeFormat;
42
+
43
+ props.modelValue = [
44
+ getChangedValueByTimeFormat(fromTimeFormat, fromDate),
45
+ getChangedValueByTimeFormat(toTimeFormat, toDate),
46
+ ];
47
+ currentValue = ref(props.modelValue);
48
+ } else if (props.mode === 'dateTime' && props.modelValue) {
49
+ currentValue = ref(getChangedValueByTimeFormat(timeFormat, props.modelValue));
50
+ } else {
51
+ currentValue = ref(props.modelValue);
52
+ }
53
+ } else {
54
+ currentValue = ref(props.modelValue);
55
+ }
56
+
57
+ const validateValue = (curr) => {
58
+ if (props.mode === 'date'
59
+ && (curr.length !== 10 || !dateReg.exec(curr))
60
+ ) {
61
+ currentValue.value = mv.value;
62
+ } else if (props.mode === 'dateTime'
63
+ && (curr.length !== 19 || !dateTimeReg.exec(curr))
64
+ ) {
65
+ currentValue.value = mv.value;
66
+ } else {
67
+ mv.value = curr;
68
+ }
69
+ };
70
+
71
+ // clearable 모드일 때, 항목(mv) 전체 삭제 아이콘 존재여부
72
+ const isClearableIcon = computed(() => {
73
+ if (props.mode === 'date' || props.mode === 'dateTime') {
74
+ return mv.value;
75
+ }
76
+ return mv.value.length;
77
+ });
78
+
79
+ /**
80
+ * clearable모드일 때 [x] 아이콘 클릭 시 mv값을 초기화
81
+ */
82
+ const removeAllMv = () => {
83
+ if (props.mode === 'date' || props.mode === 'dateTime') {
84
+ mv.value = null;
85
+ } else {
86
+ mv.value.splice(0);
87
+ mv.value = [...mv.value];
88
+ }
89
+ };
90
+
91
+ /**
92
+ * mode: dateMulti, type: date인 경우 선택된 value를 mv에서 삭제하는 로직
93
+ * @param val - tagWrapper에서 [x]클릭된 목록의 value
94
+ */
95
+ const removeMv = (val) => {
96
+ if (!props.disabled) {
97
+ const idx = mv.value.indexOf(val);
98
+ mv.value.splice(idx, 1);
99
+ mv.value = [...mv.value];
100
+ }
101
+ };
102
+
103
+ /**
104
+ * 해당 컴포넌트의 v-model값이 변경(change)되는 이벤트
105
+ * @param e
106
+ */
107
+ const changeMv = async (e) => {
108
+ await nextTick();
109
+ emit('change', mv.value, e);
110
+ };
111
+
112
+ return {
113
+ mv,
114
+ currentValue,
115
+ isClearableIcon,
116
+ validateValue,
117
+ removeAllMv,
118
+ changeMv,
119
+ removeMv,
120
+ };
121
+ };
122
+
123
+ export const useDropdown = () => {
124
+ const { props } = getCurrentInstance();
125
+
126
+ const isDropbox = ref(false);
127
+ const datePicker = ref(null);
128
+ const datePickerWrapper = ref(null);
129
+ const dropbox = ref(null);
130
+ const itemWrapper = ref(null);
131
+ const dropboxPosition = reactive({
132
+ top: null,
133
+ right: null,
134
+ left: null,
135
+ });
136
+
137
+ /**
138
+ * dropdown box 위치 변경하는 메소드
139
+ */
140
+ const changeDropboxPosition = async () => {
141
+ await nextTick();
142
+ const datePickerRect = datePickerWrapper.value?.getBoundingClientRect();
143
+ const dropboxRect = dropbox.value?.getBoundingClientRect();
144
+ const datePickerHeight = datePickerRect.height;
145
+ const datePickerY = datePickerRect.y;
146
+ const datePickerX = datePickerRect.x;
147
+ const dropboxHeight = dropboxRect.height;
148
+ const dropboxWidth = dropboxRect.width;
149
+ const docHeight = document.documentElement.clientHeight;
150
+ const docWidth = document.documentElement.clientWidth;
151
+ if (docHeight < datePickerY + datePickerHeight + dropboxHeight) {
152
+ // dropTop
153
+ dropboxPosition.top = `-${dropboxHeight}px`;
154
+ if (docWidth < datePickerX + dropboxWidth) {
155
+ dropboxPosition.left = 'auto';
156
+ dropboxPosition.right = '0px';
157
+ } else {
158
+ dropboxPosition.left = '0px';
159
+ dropboxPosition.right = 'auto';
160
+ }
161
+ } else {
162
+ // dropDown
163
+ dropboxPosition.top = `${datePickerHeight}px`;
164
+ if (docWidth < datePickerX + dropboxWidth) {
165
+ dropboxPosition.left = 'auto';
166
+ dropboxPosition.right = '0px';
167
+ } else {
168
+ dropboxPosition.left = '0px';
169
+ dropboxPosition.right = 'auto';
170
+ }
171
+ }
172
+ };
173
+
174
+ /**
175
+ * 인풋박스 클릭 이벤트
176
+ * props로 받는 항목이 없는 경우 return처리
177
+ * 인풋박스 위 클릭된 이벤트위치로 드롭박스의 사이즈, 위치를 계산
178
+ */
179
+ const clickSelectInput = async () => {
180
+ if (!props.disabled) {
181
+ isDropbox.value = !isDropbox.value;
182
+ if (isDropbox.value) {
183
+ await changeDropboxPosition();
184
+ }
185
+ }
186
+ };
187
+
188
+ /**
189
+ * 드롭박스 외부 클릭 이벤트
190
+ * filterable 모드인 경우는 필터링텍스트를 비운다.
191
+ */
192
+ const clickOutsideDropbox = () => {
193
+ isDropbox.value = false;
194
+ };
195
+
196
+ return {
197
+ isDropbox,
198
+ datePicker,
199
+ datePickerWrapper,
200
+ dropbox,
201
+ itemWrapper,
202
+ dropboxPosition,
203
+ clickSelectInput,
204
+ clickOutsideDropbox,
205
+ changeDropboxPosition,
206
+ };
207
+ };
208
+
209
+ export const useShortcuts = (param) => {
210
+ const { props } = getCurrentInstance();
211
+ const { mv, currentValue, clickOutsideDropbox } = param;
212
+
213
+ const usedShortcuts = reactive([]);
214
+ props.shortcuts?.forEach(({ value, label, shortcutDate }) => {
215
+ usedShortcuts.push({
216
+ key: value,
217
+ label,
218
+ shortcutDate,
219
+ isActive: false,
220
+ });
221
+ });
222
+
223
+ /**
224
+ * active 되어있는 shortcut 제거
225
+ */
226
+ const clearShortcuts = () => {
227
+ const targetShortcut = usedShortcuts.find(shortcut => shortcut.isActive);
228
+ if (targetShortcut) {
229
+ targetShortcut.isActive = false;
230
+ }
231
+ };
232
+
233
+ /**
234
+ * targetKey에 해당하는 shortcut을 active
235
+ * @param targetKey
236
+ */
237
+ const activeShortcut = (targetKey) => {
238
+ const targetShortcut = usedShortcuts.find(shortcut => shortcut.key === targetKey);
239
+ if (targetShortcut) {
240
+ targetShortcut.isActive = true;
241
+ }
242
+ };
243
+
244
+ /**
245
+ * 월, 일을 두자리 숫자로 보정
246
+ * @param num
247
+ * @returns {string|*}
248
+ */
249
+ const lpadToTwoDigits = (num) => {
250
+ if (num === null) {
251
+ return '00';
252
+ } else if (+num < 10) {
253
+ return `0${num}`;
254
+ }
255
+ return num;
256
+ };
257
+
258
+ /**
259
+ * 'YYYY-MM-DD' 형식으로 format
260
+ * @param targetDate
261
+ * @returns string
262
+ */
263
+ const formatDate = (targetDate) => {
264
+ const dateValue = targetDate ? new Date(targetDate) : new Date();
265
+ const year = dateValue.getFullYear();
266
+ const month = dateValue.getMonth() + 1;
267
+ const day = dateValue.getDate();
268
+ return `${year}-${lpadToTwoDigits(month)}-${lpadToTwoDigits(day)}`;
269
+ };
270
+
271
+ /**
272
+ * 'YYYY-MM-DD HH:mm:ss' 형식으로 format
273
+ * @param targetDateTime
274
+ * @returns string
275
+ */
276
+ const formatDateTime = (targetDateTime) => {
277
+ const dateTimeValue = targetDateTime ? new Date(targetDateTime) : new Date();
278
+ const hour = dateTimeValue.getHours();
279
+ const min = dateTimeValue.getMinutes();
280
+ const sec = dateTimeValue.getSeconds();
281
+ return `${formatDate(dateTimeValue)} ${lpadToTwoDigits(hour)}:${lpadToTwoDigits(min)}:${lpadToTwoDigits(sec)}`;
282
+ };
283
+
284
+ /**
285
+ * 초기 shortcut 세팅
286
+ * 해당하는 날짜면 active
287
+ */
288
+ const setActiveShortcut = () => {
289
+ clearShortcuts();
290
+
291
+ const isRange = ['dateRange', 'dateTimeRange'].includes(props.mode);
292
+
293
+ if (!usedShortcuts.length
294
+ || (props.mode === 'dateMulti' && props.options?.multiType !== 'date')
295
+ || (isRange && !mv.value.length)
296
+ || (!isRange && !mv.value)
297
+ ) {
298
+ return;
299
+ }
300
+
301
+ let targetKey;
302
+ if (isRange) {
303
+ const timeFormat = props.options?.timeFormat;
304
+ const [fromDate, toDate] = mv.value;
305
+ let targetShortcut;
306
+ if (props.mode === 'dateTimeRange' && timeFormat?.length) {
307
+ targetShortcut = usedShortcuts.find(({ shortcutDate }) => {
308
+ const [sFromDate, sToDate] = shortcutDate();
309
+ const [fromTimeFormat, toTimeFormat] = timeFormat;
310
+ const formatFromDate = getChangedValueByTimeFormat(
311
+ fromTimeFormat,
312
+ formatDateTime(sFromDate),
313
+ );
314
+ const formatToDate = getChangedValueByTimeFormat(
315
+ toTimeFormat,
316
+ formatDateTime(sToDate),
317
+ );
318
+ const isCorrectFromDate = formatFromDate === formatDateTime(fromDate);
319
+ const isCorrectToDate = formatToDate === formatDateTime(toDate);
320
+ return isCorrectFromDate && isCorrectToDate;
321
+ });
322
+ } else {
323
+ targetShortcut = usedShortcuts.find(({ shortcutDate }) => {
324
+ const [sFromDate, sToDate] = shortcutDate();
325
+ const formatFunc = props.mode === 'dateTimeRange' ? formatDateTime : formatDate;
326
+ const isCorrectFromDate = formatFunc(sFromDate) === formatFunc(fromDate);
327
+ const isCorrectToDate = formatFunc(sToDate) === formatFunc(toDate);
328
+ return isCorrectFromDate && isCorrectToDate;
329
+ });
330
+ }
331
+ targetKey = targetShortcut?.key;
332
+ } else {
333
+ const formatFunc = props.mode === 'dateTime' ? formatDateTime : formatDate;
334
+ const date = formatFunc(mv.value);
335
+ const targetShortcut = usedShortcuts.find(({ shortcutDate }) => {
336
+ const sDate = formatFunc(shortcutDate());
337
+ return sDate === date;
338
+ });
339
+ targetKey = targetShortcut?.key;
340
+ }
341
+
342
+ if (targetKey) {
343
+ activeShortcut(targetKey);
344
+ }
345
+ };
346
+
347
+ /**
348
+ * shortcut을 클릭했을 때 이벤트
349
+ * @param targetKey
350
+ */
351
+ const clickShortcut = (targetKey) => {
352
+ const isRange = ['dateRange', 'dateTimeRange'].includes(props.mode);
353
+ const targetShortcut = usedShortcuts.find(({ key }) => key === targetKey);
354
+
355
+ if (!targetShortcut) {
356
+ return;
357
+ }
358
+
359
+ const shortcutDate = targetShortcut.shortcutDate;
360
+ const timeFormat = props.options?.timeFormat;
361
+
362
+ if (isRange) {
363
+ const [fromDate, toDate] = shortcutDate();
364
+ if (props.mode === 'dateTimeRange') {
365
+ if (timeFormat?.length) {
366
+ const [fromTimeFormat, toTimeFormat] = timeFormat;
367
+
368
+ mv.value = [
369
+ getChangedValueByTimeFormat(fromTimeFormat, formatDateTime(fromDate)),
370
+ getChangedValueByTimeFormat(toTimeFormat, formatDateTime(toDate)),
371
+ ];
372
+ } else {
373
+ mv.value = [formatDateTime(fromDate), formatDateTime(toDate)];
374
+ }
375
+ } else {
376
+ mv.value = [formatDate(fromDate), formatDate(toDate)];
377
+ }
378
+ } else {
379
+ const sDate = shortcutDate();
380
+ mv.value = props.mode === 'dateTime'
381
+ ? getChangedValueByTimeFormat(
382
+ timeFormat,
383
+ formatDateTime(sDate))
384
+ : formatDate(sDate);
385
+ }
386
+
387
+ clearShortcuts();
388
+ activeShortcut(targetKey);
389
+ };
390
+
391
+ watch(
392
+ () => props.modelValue,
393
+ (curr) => {
394
+ setActiveShortcut();
395
+ if (props.mode === 'dateMulti'
396
+ && props?.options?.multiType === 'date'
397
+ && props?.options?.multiDayLimit > curr.length
398
+ ) {
399
+ return;
400
+ } else if (
401
+ props.mode === 'dateRange'
402
+ || props.mode === 'dateTime'
403
+ || props.mode === 'dateTimeRange'
404
+ ) {
405
+ currentValue.value = curr;
406
+ return;
407
+ } else if (props.mode === 'date') {
408
+ currentValue.value = curr;
409
+ }
410
+ clickOutsideDropbox();
411
+ },
412
+ );
413
+
414
+ return {
415
+ usedShortcuts,
416
+ clickShortcut,
417
+ setActiveShortcut,
418
+ };
419
+ };