evui 3.1.50 → 3.1.54

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.
@@ -162,7 +162,7 @@
162
162
  </template>
163
163
 
164
164
  <script>
165
- import { reactive, toRefs, computed, watch, nextTick } from 'vue';
165
+ import { reactive, toRefs, computed, watch } from 'vue';
166
166
  import treeGridNode from './TreeGridNode';
167
167
  import Toolbar from './treeGrid.toolbar';
168
168
  import {
@@ -227,9 +227,8 @@ export default {
227
227
  'update:checked': null,
228
228
  'check-row': null,
229
229
  'check-all': null,
230
- 'update-tree-data': null,
231
230
  },
232
- setup(props, { emit }) {
231
+ setup(props) {
233
232
  const {
234
233
  isRenderer,
235
234
  getComponentName,
@@ -246,7 +245,8 @@ export default {
246
245
  const stores = reactive({
247
246
  treeStore: [],
248
247
  viewStore: [],
249
- treeRows: computed(() => JSON.parse(JSON.stringify(props.rows))),
248
+ filterStore: [],
249
+ treeRows: props.rows,
250
250
  showTreeStore: computed(() => stores.treeStore.filter(item => item.show)),
251
251
  orderedColumns: computed(() =>
252
252
  props.columns.map((column, index) => ({ index, ...column }))),
@@ -324,7 +324,7 @@ export default {
324
324
  } = contextMenuEvent({ contextInfo, stores, selectInfo });
325
325
 
326
326
  const {
327
- setTreeStore,
327
+ setTreeNodeStore,
328
328
  handleExpand,
329
329
  } = treeEvent({ stores, onResize });
330
330
 
@@ -359,17 +359,16 @@ export default {
359
359
  checkInfo.isHeaderChecked = false;
360
360
  },
361
361
  );
362
+ stores.treeStore = setTreeNodeStore();
363
+
362
364
  watch(
363
365
  () => props.rows,
364
- () => {
365
- nextTick(() => {
366
- stores.treeStore = [];
367
- calculatedColumn();
368
- setTreeStore(stores.treeRows, 0, true);
369
- updateVScroll();
370
- emit('update-tree-data', stores.treeRows);
371
- });
372
- }, { deep: true, immediate: true },
366
+ (newData) => {
367
+ stores.treeRows = newData;
368
+ stores.treeStore = setTreeNodeStore();
369
+ onResize();
370
+ updateVScroll();
371
+ }, { deep: true },
373
372
  );
374
373
  watch(
375
374
  () => [props.width, props.height, resizeInfo.adjust, props.option.columnWidth],
@@ -550,35 +550,73 @@ export const contextMenuEvent = (params) => {
550
550
 
551
551
  export const treeEvent = (params) => {
552
552
  const { stores, onResize } = params;
553
- // tree data init
554
- let index = 0;
555
- const filterObj = (keys, obj) => {
556
- const newObj = {};
557
- Object.keys(obj).forEach((key) => {
558
- if (!keys.includes(key)) {
559
- newObj[key] = obj[key];
560
- }
561
- });
562
- return newObj;
563
- };
564
- const setTreeStore = (rows, count, isShow, parent) => {
565
- rows.forEach((nodeObj) => {
566
- const node = nodeObj;
567
- const dataObj = filterObj('children', nodeObj);
568
- node.data = dataObj;
569
- node.level = count;
570
- node.expand = node.expand === undefined ? true : node.expand;
571
- node.show = isShow;
572
- node.checked = false;
573
- node.index = index++;
574
- node.parent = parent;
575
- node.isFilter = false;
576
- stores.treeStore.push(node);
577
- if (node.children && node.children.length > 0) {
578
- node.hasChild = true;
579
- setTreeStore(node.children, node.level + 1, node.show && node.expand, node);
553
+ const setTreeNodeStore = () => {
554
+ let nodeIndex = 0;
555
+ const nodeList = [];
556
+
557
+ function getDataObj(nodeObj) {
558
+ const newObj = {};
559
+ Object.keys(nodeObj).forEach((key) => {
560
+ if (key !== 'children') {
561
+ newObj[key] = nodeObj[key];
562
+ }
563
+ });
564
+ return newObj;
565
+ }
566
+
567
+ function setNodeData(nodeInfo) {
568
+ const { node, level, isShow, parent } = nodeInfo;
569
+ if (node !== null && typeof node === 'object') {
570
+ node.index = nodeIndex++;
571
+ node.level = level;
572
+
573
+ if (!Object.hasOwnProperty.call(node, 'checked')) {
574
+ node.checked = false;
575
+ }
576
+
577
+ if (!Object.hasOwnProperty.call(node, 'show')) {
578
+ node.show = isShow;
579
+ }
580
+
581
+ if (!Object.hasOwnProperty.call(node, 'expand')) {
582
+ node.expand = true;
583
+ }
584
+
585
+ if (!Object.hasOwnProperty.call(node, 'isFilter')) {
586
+ node.isFilter = false;
587
+ }
588
+
589
+ if (!Object.hasOwnProperty.call(node, 'data')) {
590
+ node.data = getDataObj(node);
591
+ }
592
+
593
+ nodeList.push(node);
594
+
595
+ if (typeof parent !== 'undefined') {
596
+ node.parent = parent;
597
+ }
598
+ if (node.children) {
599
+ node.hasChild = true;
600
+ node.children.forEach(child =>
601
+ setNodeData({
602
+ node: child,
603
+ level: level + 1,
604
+ isShow: node.show && node.expand,
605
+ parent: node,
606
+ }),
607
+ );
608
+ }
580
609
  }
610
+ }
611
+ stores.treeRows.forEach((root) => {
612
+ setNodeData({
613
+ node: root,
614
+ level: 0,
615
+ isShow: true,
616
+ parent: undefined,
617
+ });
581
618
  });
619
+ return nodeList;
582
620
  };
583
621
  const setExpandNode = (children, isShow, isFilter) => {
584
622
  children.forEach((nodeObj) => {
@@ -595,7 +633,7 @@ export const treeEvent = (params) => {
595
633
  setExpandNode(data.children, data.expand, data.isFilter);
596
634
  onResize();
597
635
  };
598
- return { setTreeStore, handleExpand };
636
+ return { setTreeNodeStore, handleExpand };
599
637
  };
600
638
 
601
639
  export const filterEvent = (params) => {
@@ -84,7 +84,7 @@
84
84
  </template>
85
85
 
86
86
  <script>
87
- import { useModel, useMouseEvent } from './uses';
87
+ import { useEscKeydownEvent, useModel, useMouseEvent } from './uses';
88
88
 
89
89
  export default {
90
90
  name: 'EvWindow',
@@ -149,6 +149,10 @@ export default {
149
149
  type: Boolean,
150
150
  default: false,
151
151
  },
152
+ escClose: {
153
+ type: Boolean,
154
+ default: false,
155
+ },
152
156
  },
153
157
  emits: [
154
158
  'update:visible',
@@ -156,6 +160,7 @@ export default {
156
160
  'mousedown-mouseup',
157
161
  'mousedown-mousemove',
158
162
  'resize',
163
+ 'expand',
159
164
  ],
160
165
  setup() {
161
166
  const {
@@ -182,6 +187,8 @@ export default {
182
187
  removeUnit,
183
188
  });
184
189
 
190
+ useEscKeydownEvent({ closeWin, windowRef });
191
+
185
192
  return {
186
193
  windowRef,
187
194
  headerRef,
@@ -1,23 +1,11 @@
1
1
  import {
2
- getCurrentInstance, ref, computed, reactive, watch, nextTick,
2
+ getCurrentInstance, ref, computed, reactive, watch, nextTick, onMounted,
3
3
  } from 'vue';
4
4
 
5
5
  // 세로 스크롤 너비
6
6
  const getVScrollWidth = () => window.innerWidth - document.documentElement.clientWidth;
7
7
  // 가로 스크롤 너비
8
- const getHScrollWidth = () => window.innerHeight - document.documentElement.clientHeight;
9
- // 전체 문서 너비
10
- const getDocumentWidth = () => Math.max(
11
- document.body.scrollWidth, document.documentElement.scrollWidth,
12
- document.body.offsetWidth, document.documentElement.offsetWidth,
13
- document.body.clientWidth, document.documentElement.clientWidth,
14
- );
15
- // 전체 문서 높이
16
- const getDocumentHeight = () => Math.max(
17
- document.body.scrollHeight, document.documentElement.scrollHeight,
18
- document.body.offsetHeight, document.documentElement.offsetHeight,
19
- document.body.clientHeight, document.documentElement.clientHeight,
20
- );
8
+ // const getHScrollWidth = () => window.innerHeight - document.documentElement.clientHeight;
21
9
 
22
10
  const useModel = () => {
23
11
  const { props, emit } = getCurrentInstance();
@@ -89,6 +77,8 @@ const useModel = () => {
89
77
  }));
90
78
 
91
79
  const setBasePosition = () => {
80
+ basePosition.position = 'fixed';
81
+
92
82
  if (props.fullscreen) {
93
83
  basePosition.width = '100%';
94
84
  basePosition.height = '100%';
@@ -97,22 +87,32 @@ const useModel = () => {
97
87
  return;
98
88
  }
99
89
 
100
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop
101
- || document.body.scrollTop || 0;
102
- const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft
103
- || document.body.scrollLeft || 0;
90
+ const convertedWidth = removeUnit(props.width, 'horizontal');
91
+ const convertedMinWidth = removeUnit(props.minWidth, 'horizontal');
92
+ if (convertedWidth < convertedMinWidth) {
93
+ console.warn('Since width is less than min-width, it is replaced by min-width.');
94
+ basePosition.width = numberToUnit(props.minWidth);
95
+ } else {
96
+ basePosition.width = numberToUnit(props.width);
97
+ }
104
98
 
105
- basePosition.width = numberToUnit(props.width);
106
- basePosition.height = numberToUnit(props.height);
107
- basePosition.position = 'absolute';
108
- basePosition.top = `calc(((100vh - ${basePosition.height}) / 2) + ${scrollTop}px)`;
109
- basePosition.left = `calc(((100vw - ${basePosition.width}) / 2) + ${scrollLeft}px)`;
99
+ const convertedHeight = removeUnit(props.height, 'vertical');
100
+ const convertedMinHeight = removeUnit(props.minHeight, 'vertical');
101
+ if (convertedHeight < convertedMinHeight) {
102
+ console.warn('Since height is less than min-height, it is replaced by min-height.');
103
+ basePosition.height = numberToUnit(props.minHeight);
104
+ } else {
105
+ basePosition.height = numberToUnit(props.height);
106
+ }
107
+
108
+ basePosition.top = `calc((100% - ${basePosition.height}) / 2)`;
109
+ basePosition.left = `calc((100% - ${basePosition.width}) / 2)`;
110
110
 
111
111
  if (removeUnit(props.width, 'horizontal') > window.innerWidth) {
112
112
  basePosition.left = 0;
113
113
  }
114
114
  if (removeUnit(props.height, 'vertical') > window.innerHeight) {
115
- basePosition.top = `${scrollTop}px`;
115
+ basePosition.top = 0;
116
116
  }
117
117
  };
118
118
 
@@ -125,61 +125,32 @@ const useModel = () => {
125
125
  };
126
126
 
127
127
  const changeBodyCls = (isVisible) => {
128
- const windowCnt = root?.getElementsByClassName('ev-window-wrapper')?.length;
129
128
  const hideScrollWindowCnt = root?.getElementsByClassName('scroll-lock')?.length;
130
- const allowScrollWindowCnt = root?.getElementsByClassName('scroll-allow')?.length;
131
129
  const bodyElem = document.body;
132
130
 
133
- if (isVisible) {
131
+ if (isVisible) { // window open
134
132
  if (props.hideScroll) {
135
- // props.hideScroll === true 시,
136
- // body 우측 padding 추가 & overflow hidden class 추가
133
+ // hideScroll 시, body 우측 padding 추가 & overflow hidden class 추가
137
134
  if (!hideScrollWindowCnt) {
138
135
  const scrollWidth = getVScrollWidth();
139
136
  bodyElem.style.paddingRight = `${scrollWidth}px`;
140
137
  }
141
138
  bodyElem.classList.add('ev-window-scroll-lock');
142
- } else if (!props.hideScroll && !props.isModal) {
143
- // !props.hideScroll && !props.isModal 시,
144
- // body에 position: relative 추가, 스크롤 여부에 따라 overflow-x or y hidden 처리
145
- const vScrollWidth = getVScrollWidth();
146
- const hScrollWidth = getHScrollWidth();
147
- const bodyClassName = ['ev-window-scroll-allow'];
148
- if (vScrollWidth > 0 && hScrollWidth === 0) {
149
- // 세로 스크롤만 있을 경우 - 가로 스크롤 막음
150
- bodyClassName.push('horizontal-hide');
151
- } else if (vScrollWidth === 0 && hScrollWidth > 0) {
152
- // 가로 스크롤만 있을 경우 - 세로 스크롤 막음
153
- bodyClassName.push('vertical-hide');
154
- } else if (vScrollWidth === 0 && hScrollWidth === 0) {
155
- // 둘다 없을 경우 - 가로&세로 스크롤 막음
156
- bodyClassName.push('hide');
157
- }
158
-
159
- bodyElem.classList.add(...bodyClassName);
160
- }
161
- bodyElem.classList.add('ev-window-open');
162
- } else {
163
- if (hideScrollWindowCnt === 1) {
164
- bodyElem.style.removeProperty('padding-right');
165
- bodyElem.classList.remove('ev-window-scroll-lock');
166
- }
167
- if (allowScrollWindowCnt === 1) {
168
- bodyElem.classList.remove('ev-window-scroll-allow', 'horizontal-hide', 'vertical-hide', 'hide');
169
- }
170
- if (windowCnt === 1) {
171
- bodyElem.classList.remove('ev-window-open');
172
139
  }
140
+ } else if (props.hideScroll && hideScrollWindowCnt === 1) { // window close
141
+ bodyElem.style.removeProperty('padding-right');
142
+ bodyElem.classList.remove('ev-window-scroll-lock');
173
143
  }
174
144
  };
145
+
175
146
  setBasePosition();
176
147
 
177
148
  watch(
178
149
  () => props.visible,
179
- (newVal) => {
150
+ async (newVal) => {
180
151
  changeBodyCls(newVal);
181
152
  if (newVal) {
182
- nextTick(() => {
153
+ await nextTick(() => {
183
154
  setBasePosition();
184
155
  });
185
156
  }
@@ -211,8 +182,6 @@ const useMouseEvent = (param) => {
211
182
  const draggingMinSize = 50;
212
183
  const grabbingBorderSize = 5;
213
184
  const dragStyle = reactive({});
214
- const documentWidth = ref(0);
215
- const documentHeight = ref(0);
216
185
  const clickedInfo = reactive({
217
186
  state: '',
218
187
  pressedSpot: '',
@@ -417,6 +386,7 @@ const useMouseEvent = (param) => {
417
386
  // 브라우저 상하 위치 제약
418
387
  const getValidTop = (windowHeight, top) => {
419
388
  let tempTop = top;
389
+
420
390
  if (tempTop < 0) { // 상
421
391
  tempTop = 0;
422
392
  } else if (tempTop > windowHeight - draggingMinSize) { // 하
@@ -435,28 +405,6 @@ const useMouseEvent = (param) => {
435
405
  return tempLeft;
436
406
  };
437
407
 
438
- // mousedown > wheel: 마우스 휠
439
- const wheeling = (e) => {
440
- const scrollTop = document.documentElement.scrollTop;
441
- if (e.deltaY < 0 && scrollTop <= 0) {
442
- return;
443
- }
444
-
445
- let tempTop = removeUnit(dragStyle.top) || clickedInfo.top;
446
- tempTop += e.deltaY;
447
-
448
- const windowHeight = props.hideScroll || props.isModal
449
- ? document.documentElement.clientHeight : documentHeight.value;
450
- tempTop = getValidTop(windowHeight, tempTop);
451
-
452
- clickedInfo.top = tempTop;
453
-
454
- setDragStyle({
455
- top: `${tempTop}px`,
456
- left: dragStyle.left,
457
- });
458
- };
459
-
460
408
  // mousedown > mousemove: 마우스 드래그
461
409
  const dragging = (e) => {
462
410
  e.preventDefault();
@@ -464,63 +412,14 @@ const useMouseEvent = (param) => {
464
412
 
465
413
  // window header를 통해 mouseMove 됐을 경우
466
414
  if (props.draggable && clickedInfo.pressedSpot === 'header') {
467
- // 전체 문서 너비&높이 세팅 - mount 때와 드래그 시 수치 다르게 측정되기 때문에 드래그 초기 한번만 세팅
468
- if (!documentWidth.value) {
469
- documentWidth.value = getDocumentWidth();
470
- }
471
- if (!documentHeight.value) {
472
- documentHeight.value = getDocumentHeight();
473
- }
474
-
475
- // 스크롤 있을 경우 전체 문서 기준, 없을 경우 clientWidth, Height 기준
476
- const windowWidth = props.hideScroll || props.isModal
477
- ? document.documentElement.clientWidth : documentWidth.value;
478
- const windowHeight = props.hideScroll || props.isModal
479
- ? document.documentElement.clientHeight : documentHeight.value;
480
-
415
+ const windowWidth = document.documentElement.clientWidth;
416
+ const windowHeight = document.documentElement.clientHeight;
481
417
  const diffTop = e.clientY - clickedInfo.clientY;
482
418
  const diffLeft = e.clientX - clickedInfo.clientX;
483
419
 
484
420
  let tempTop = clickedInfo.top + diffTop;
485
421
  let tempLeft = clickedInfo.left + diffLeft;
486
422
 
487
- // 스크롤 허용 & 드래그 시 브라우저 상하단 위치에서 스크롤 이동
488
- if (!props.hideScroll && !props.isModal) {
489
- let [x, y] = [0, 0];
490
- const moveScrollSize = 10;
491
- const scrollTop = document.documentElement.scrollTop;
492
-
493
- if (e.clientY < draggingMinSize && scrollTop > 0) { // 상
494
- tempTop = draggedInfo.top || tempTop;
495
- clickedInfo.clientY = e.clientY;
496
- clickedInfo.top = tempTop;
497
- y = -moveScrollSize;
498
- tempTop -= moveScrollSize;
499
- } else if (e.clientY > document.documentElement.clientHeight - draggingMinSize) { // 하
500
- tempTop = draggedInfo.top || tempTop;
501
- clickedInfo.clientY = e.clientY;
502
- clickedInfo.top = tempTop;
503
- y = moveScrollSize;
504
- tempTop += moveScrollSize;
505
- } else if (e.clientX < draggingMinSize) { // 좌
506
- tempLeft = draggedInfo.left || tempLeft;
507
- clickedInfo.clientX = e.clientX;
508
- clickedInfo.left = tempLeft;
509
- x = -moveScrollSize;
510
- tempLeft -= moveScrollSize;
511
- } else if (e.clientX > document.documentElement.clientWidth - draggingMinSize) { // 우
512
- tempLeft = draggedInfo.left || tempLeft;
513
- clickedInfo.clientX = e.clientX;
514
- clickedInfo.left = tempLeft;
515
- x = moveScrollSize;
516
- tempLeft += moveScrollSize;
517
- }
518
-
519
- document.documentElement.scrollBy(x, y);
520
- draggedInfo.top = tempTop;
521
- draggedInfo.left = tempLeft;
522
- }
523
-
524
423
  tempTop = getValidTop(windowHeight, tempTop);
525
424
  tempLeft = getValidLeft(windowWidth, tempLeft);
526
425
 
@@ -542,7 +441,6 @@ const useMouseEvent = (param) => {
542
441
 
543
442
  emit('mousedown-mouseup', e);
544
443
 
545
- window.removeEventListener('wheel', wheeling);
546
444
  window.removeEventListener('mousemove', dragging);
547
445
  window.removeEventListener('mouseup', endDrag);
548
446
  };
@@ -594,7 +492,6 @@ const useMouseEvent = (param) => {
594
492
 
595
493
  emit('mousedown', { ...clickedInfo });
596
494
 
597
- window.addEventListener('wheel', wheeling);
598
495
  window.addEventListener('mousemove', dragging);
599
496
  window.addEventListener('mouseup', endDrag);
600
497
  };
@@ -609,14 +506,7 @@ const useMouseEvent = (param) => {
609
506
  const clickExpandBtn = () => {
610
507
  isFullExpandWindow.value = !isFullExpandWindow.value;
611
508
  nextTick(() => {
612
- if (!isFullExpandWindow.value) {
613
- setDragStyle({
614
- top: beforeExpandPosInfo.top,
615
- left: beforeExpandPosInfo.left,
616
- width: beforeExpandPosInfo.width,
617
- height: beforeExpandPosInfo.height,
618
- });
619
- } else {
509
+ if (isFullExpandWindow.value) {
620
510
  beforeExpandPosInfo.top = windowRef.value.offsetTop;
621
511
  beforeExpandPosInfo.left = windowRef.value.offsetLeft;
622
512
  beforeExpandPosInfo.width = windowRef.value.offsetWidth;
@@ -628,7 +518,21 @@ const useMouseEvent = (param) => {
628
518
  width: document.body.clientWidth,
629
519
  height: document.body.clientHeight,
630
520
  });
521
+ } else {
522
+ setDragStyle({
523
+ top: beforeExpandPosInfo.top,
524
+ left: beforeExpandPosInfo.left,
525
+ width: beforeExpandPosInfo.width,
526
+ height: beforeExpandPosInfo.height,
527
+ });
631
528
  }
529
+
530
+ emit('expand', {
531
+ top: beforeExpandPosInfo.top,
532
+ left: beforeExpandPosInfo.left,
533
+ width: beforeExpandPosInfo.width,
534
+ height: beforeExpandPosInfo.height,
535
+ });
632
536
  });
633
537
  };
634
538
 
@@ -679,7 +583,136 @@ const useMouseEvent = (param) => {
679
583
  };
680
584
  };
681
585
 
586
+ const activeWindows = (() => {
587
+ let windows = [];
588
+ let sequence = 0;
589
+
590
+ return {
591
+ add(activeWindow) {
592
+ if (activeWindow === null || activeWindow === undefined) return;
593
+
594
+ activeWindow.sequence = sequence++;
595
+ windows.push(activeWindow);
596
+
597
+ // eslint-disable-next-line consistent-return
598
+ return activeWindow.sequence;
599
+ },
600
+ remove(inactiveWindow) {
601
+ if (inactiveWindow === null || inactiveWindow === undefined) return;
602
+ windows = windows.filter(activeWindow => activeWindow.sequence !== inactiveWindow.sequence);
603
+ },
604
+ get windows() {
605
+ return windows.slice();
606
+ },
607
+ getWindowBySequence(targetSequence) {
608
+ return windows.find(activeWindow => activeWindow.sequence === targetSequence);
609
+ },
610
+ isEmpty() {
611
+ return windows.length <= 0;
612
+ },
613
+ isFirstWindowOpen() {
614
+ return windows.length === 1;
615
+ },
616
+ isLastWindowClose() {
617
+ return windows.length === 0;
618
+ },
619
+ };
620
+ })();
621
+
622
+ const getZIndexFromElement = (element) => {
623
+ const zIndex = window.getComputedStyle(element).getPropertyValue('z-index').trim();
624
+
625
+ if (!zIndex || isNaN(zIndex)) return 700; // window 초기 z-index 값
626
+
627
+ return parseInt(zIndex);
628
+ };
629
+
630
+ const useEscKeydownEvent = ({ closeWin, windowRef }) => {
631
+ const { props } = getCurrentInstance();
632
+
633
+ let sequence = null;
634
+
635
+ const addActiveWindow = () => {
636
+ sequence = activeWindows.add({
637
+ sequence,
638
+ closeWin,
639
+ elem: windowRef.value,
640
+ escClose: props.escClose,
641
+ });
642
+ };
643
+
644
+ const removeInactiveWindow = (inactiveWindow) => {
645
+ activeWindows.remove(inactiveWindow);
646
+ };
647
+
648
+ const keydownEsc = (event) => {
649
+ if (activeWindows.isEmpty()) return;
650
+
651
+ const { code } = event;
652
+ if (code !== 'Escape') return;
653
+
654
+ // zIndex 클수록, 최근에 열린 것일수록(sequence 클수록) 앞에 위치
655
+ const compare = (window1, window2) => {
656
+ if (window1.zIndex > window2.zIndex) return -1;
657
+ if (window1.zIndex < window2.zIndex) return 1;
658
+
659
+ if (window1.sequence > window2.sequence) return -1;
660
+ return 1;
661
+ };
662
+
663
+ const activeWindowSorted = Array.prototype.map.call(activeWindows.windows, activeWindow => ({
664
+ ...activeWindow,
665
+ zIndex: getZIndexFromElement(activeWindow.elem),
666
+ })).sort(compare);
667
+
668
+ const topActiveWindow = activeWindowSorted[0];
669
+
670
+ // 예시 상황) Nested에서 외부 Window의 escClose는 true이고, 내부 Window의 escClose는 false인 경우,
671
+ // esc 눌러도 외부 Window는 닫히지 않고, 가장 상단에 있는 내부 Window가 수동으로 닫힌 후에 닫히도록 하기 위해
672
+ if (topActiveWindow.escClose === false) return;
673
+
674
+ topActiveWindow.closeWin();
675
+ };
676
+
677
+ const addKeydownEvtHandler = () => {
678
+ if (activeWindows.isFirstWindowOpen()) {
679
+ document.addEventListener('keydown', keydownEsc);
680
+ }
681
+ };
682
+
683
+ const removeKeydownEvtHandler = () => {
684
+ if (activeWindows.isLastWindowClose()) {
685
+ document.removeEventListener('keydown', keydownEsc);
686
+ }
687
+ };
688
+
689
+ onMounted(() => {
690
+ // visible 초기값이 true
691
+ if (props.visible) {
692
+ addActiveWindow();
693
+ addKeydownEvtHandler();
694
+ }
695
+ });
696
+
697
+ watch(
698
+ () => props.visible,
699
+ (newVal) => {
700
+ nextTick(() => {
701
+ if (newVal) {
702
+ addActiveWindow();
703
+ addKeydownEvtHandler();
704
+ } else {
705
+ const inactiveWindow = activeWindows.getWindowBySequence(sequence);
706
+ removeInactiveWindow(inactiveWindow);
707
+ removeKeydownEvtHandler();
708
+ }
709
+ });
710
+ },
711
+ );
712
+ };
713
+
682
714
  export {
683
715
  useModel,
684
716
  useMouseEvent,
717
+ useEscKeydownEvent,
685
718
  };