evui 3.1.53 → 3.1.57

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,23 +1,18 @@
1
1
  import {
2
- getCurrentInstance, ref, computed, reactive, watch, nextTick,
2
+ getCurrentInstance,
3
+ ref,
4
+ computed,
5
+ reactive,
6
+ watch,
7
+ nextTick,
8
+ onMounted,
9
+ onBeforeUnmount,
3
10
  } from 'vue';
4
11
 
5
12
  // 세로 스크롤 너비
6
13
  const getVScrollWidth = () => window.innerWidth - document.documentElement.clientWidth;
7
14
  // 가로 스크롤 너비
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
- );
15
+ // const getHScrollWidth = () => window.innerHeight - document.documentElement.clientHeight;
21
16
 
22
17
  const useModel = () => {
23
18
  const { props, emit } = getCurrentInstance();
@@ -89,6 +84,8 @@ const useModel = () => {
89
84
  }));
90
85
 
91
86
  const setBasePosition = () => {
87
+ basePosition.position = 'fixed';
88
+
92
89
  if (props.fullscreen) {
93
90
  basePosition.width = '100%';
94
91
  basePosition.height = '100%';
@@ -97,22 +94,32 @@ const useModel = () => {
97
94
  return;
98
95
  }
99
96
 
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;
97
+ const convertedWidth = removeUnit(props.width, 'horizontal');
98
+ const convertedMinWidth = removeUnit(props.minWidth, 'horizontal');
99
+ if (convertedWidth < convertedMinWidth) {
100
+ console.warn('Since width is less than min-width, it is replaced by min-width.');
101
+ basePosition.width = numberToUnit(props.minWidth);
102
+ } else {
103
+ basePosition.width = numberToUnit(props.width);
104
+ }
105
+
106
+ const convertedHeight = removeUnit(props.height, 'vertical');
107
+ const convertedMinHeight = removeUnit(props.minHeight, 'vertical');
108
+ if (convertedHeight < convertedMinHeight) {
109
+ console.warn('Since height is less than min-height, it is replaced by min-height.');
110
+ basePosition.height = numberToUnit(props.minHeight);
111
+ } else {
112
+ basePosition.height = numberToUnit(props.height);
113
+ }
104
114
 
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)`;
115
+ basePosition.top = `calc((100% - ${basePosition.height}) / 2)`;
116
+ basePosition.left = `calc((100% - ${basePosition.width}) / 2)`;
110
117
 
111
118
  if (removeUnit(props.width, 'horizontal') > window.innerWidth) {
112
119
  basePosition.left = 0;
113
120
  }
114
121
  if (removeUnit(props.height, 'vertical') > window.innerHeight) {
115
- basePosition.top = `${scrollTop}px`;
122
+ basePosition.top = 0;
116
123
  }
117
124
  };
118
125
 
@@ -125,61 +132,32 @@ const useModel = () => {
125
132
  };
126
133
 
127
134
  const changeBodyCls = (isVisible) => {
128
- const windowCnt = root?.getElementsByClassName('ev-window-wrapper')?.length;
129
135
  const hideScrollWindowCnt = root?.getElementsByClassName('scroll-lock')?.length;
130
- const allowScrollWindowCnt = root?.getElementsByClassName('scroll-allow')?.length;
131
136
  const bodyElem = document.body;
132
137
 
133
- if (isVisible) {
138
+ if (isVisible) { // window open
134
139
  if (props.hideScroll) {
135
- // props.hideScroll === true 시,
136
- // body 우측 padding 추가 & overflow hidden class 추가
140
+ // hideScroll 시, body 우측 padding 추가 & overflow hidden class 추가
137
141
  if (!hideScrollWindowCnt) {
138
142
  const scrollWidth = getVScrollWidth();
139
143
  bodyElem.style.paddingRight = `${scrollWidth}px`;
140
144
  }
141
145
  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
146
  }
147
+ } else if (props.hideScroll && hideScrollWindowCnt === 1) { // window close
148
+ bodyElem.style.removeProperty('padding-right');
149
+ bodyElem.classList.remove('ev-window-scroll-lock');
173
150
  }
174
151
  };
152
+
175
153
  setBasePosition();
176
154
 
177
155
  watch(
178
156
  () => props.visible,
179
- (newVal) => {
157
+ async (newVal) => {
180
158
  changeBodyCls(newVal);
181
159
  if (newVal) {
182
- nextTick(() => {
160
+ await nextTick(() => {
183
161
  setBasePosition();
184
162
  });
185
163
  }
@@ -211,8 +189,6 @@ const useMouseEvent = (param) => {
211
189
  const draggingMinSize = 50;
212
190
  const grabbingBorderSize = 5;
213
191
  const dragStyle = reactive({});
214
- const documentWidth = ref(0);
215
- const documentHeight = ref(0);
216
192
  const clickedInfo = reactive({
217
193
  state: '',
218
194
  pressedSpot: '',
@@ -417,6 +393,7 @@ const useMouseEvent = (param) => {
417
393
  // 브라우저 상하 위치 제약
418
394
  const getValidTop = (windowHeight, top) => {
419
395
  let tempTop = top;
396
+
420
397
  if (tempTop < 0) { // 상
421
398
  tempTop = 0;
422
399
  } else if (tempTop > windowHeight - draggingMinSize) { // 하
@@ -435,28 +412,6 @@ const useMouseEvent = (param) => {
435
412
  return tempLeft;
436
413
  };
437
414
 
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
415
  // mousedown > mousemove: 마우스 드래그
461
416
  const dragging = (e) => {
462
417
  e.preventDefault();
@@ -464,63 +419,14 @@ const useMouseEvent = (param) => {
464
419
 
465
420
  // window header를 통해 mouseMove 됐을 경우
466
421
  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
-
422
+ const windowWidth = document.documentElement.clientWidth;
423
+ const windowHeight = document.documentElement.clientHeight;
481
424
  const diffTop = e.clientY - clickedInfo.clientY;
482
425
  const diffLeft = e.clientX - clickedInfo.clientX;
483
426
 
484
427
  let tempTop = clickedInfo.top + diffTop;
485
428
  let tempLeft = clickedInfo.left + diffLeft;
486
429
 
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
430
  tempTop = getValidTop(windowHeight, tempTop);
525
431
  tempLeft = getValidLeft(windowWidth, tempLeft);
526
432
 
@@ -542,7 +448,6 @@ const useMouseEvent = (param) => {
542
448
 
543
449
  emit('mousedown-mouseup', e);
544
450
 
545
- window.removeEventListener('wheel', wheeling);
546
451
  window.removeEventListener('mousemove', dragging);
547
452
  window.removeEventListener('mouseup', endDrag);
548
453
  };
@@ -594,7 +499,6 @@ const useMouseEvent = (param) => {
594
499
 
595
500
  emit('mousedown', { ...clickedInfo });
596
501
 
597
- window.addEventListener('wheel', wheeling);
598
502
  window.addEventListener('mousemove', dragging);
599
503
  window.addEventListener('mouseup', endDrag);
600
504
  };
@@ -609,14 +513,7 @@ const useMouseEvent = (param) => {
609
513
  const clickExpandBtn = () => {
610
514
  isFullExpandWindow.value = !isFullExpandWindow.value;
611
515
  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 {
516
+ if (isFullExpandWindow.value) {
620
517
  beforeExpandPosInfo.top = windowRef.value.offsetTop;
621
518
  beforeExpandPosInfo.left = windowRef.value.offsetLeft;
622
519
  beforeExpandPosInfo.width = windowRef.value.offsetWidth;
@@ -628,7 +525,21 @@ const useMouseEvent = (param) => {
628
525
  width: document.body.clientWidth,
629
526
  height: document.body.clientHeight,
630
527
  });
528
+ } else {
529
+ setDragStyle({
530
+ top: beforeExpandPosInfo.top,
531
+ left: beforeExpandPosInfo.left,
532
+ width: beforeExpandPosInfo.width,
533
+ height: beforeExpandPosInfo.height,
534
+ });
631
535
  }
536
+
537
+ emit('expand', {
538
+ top: beforeExpandPosInfo.top,
539
+ left: beforeExpandPosInfo.left,
540
+ width: beforeExpandPosInfo.width,
541
+ height: beforeExpandPosInfo.height,
542
+ });
632
543
  });
633
544
  };
634
545
 
@@ -679,7 +590,308 @@ const useMouseEvent = (param) => {
679
590
  };
680
591
  };
681
592
 
593
+ const activeWindows = (() => {
594
+ let windows = [];
595
+ let sequence = 0;
596
+
597
+ return {
598
+ add(activeWindow) {
599
+ if (activeWindow === null || activeWindow === undefined) return;
600
+
601
+ activeWindow.sequence = sequence++;
602
+ windows.push(activeWindow);
603
+
604
+ // eslint-disable-next-line consistent-return
605
+ return activeWindow.sequence;
606
+ },
607
+ remove(inactiveWindow) {
608
+ if (inactiveWindow === null || inactiveWindow === undefined) return;
609
+ windows = windows.filter(activeWindow => activeWindow.sequence !== inactiveWindow.sequence);
610
+ },
611
+
612
+ get windows() {
613
+ return windows.slice();
614
+ },
615
+ getWindowBySequence(targetSequence) {
616
+ return windows.find(activeWindow => activeWindow.sequence === targetSequence);
617
+ },
618
+
619
+ isEmpty() {
620
+ return windows.length <= 0;
621
+ },
622
+ isFirstWindowOpen() {
623
+ return sequence === 1;
624
+ },
625
+ };
626
+ })();
627
+
628
+ const zIndexService = (() => {
629
+ const LOWER = 700;
630
+ const UPPER = 750;
631
+
632
+ const INCREMENT = 1;
633
+ const PADDING = INCREMENT * 2;
634
+
635
+ const UPPER_LIMIT = UPPER - PADDING;
636
+
637
+ let current = LOWER;
638
+
639
+ return {
640
+ getNext() {
641
+ if (current >= UPPER_LIMIT) {
642
+ return UPPER_LIMIT;
643
+ }
644
+ current += INCREMENT;
645
+ return current;
646
+ },
647
+ getNextOverLimit() {
648
+ return UPPER_LIMIT + INCREMENT;
649
+ },
650
+ getPrevFrom(index) {
651
+ const prev = current - (index * INCREMENT);
652
+
653
+ if (prev <= LOWER) return LOWER;
654
+ return prev;
655
+ },
656
+ isUpperLimitClose() {
657
+ return current >= UPPER_LIMIT;
658
+ },
659
+ resetToLower() {
660
+ current = LOWER;
661
+ },
662
+ getAllocableCount() {
663
+ return Math.floor((UPPER_LIMIT - LOWER) / INCREMENT);
664
+ },
665
+ };
666
+ })();
667
+
668
+ const getZIndexFromElement = (element) => {
669
+ const zIndex = window.getComputedStyle(element).getPropertyValue('z-index').trim();
670
+
671
+ if (!zIndex || isNaN(zIndex)) return 700; // window 초기 z-index 값
672
+
673
+ return parseInt(zIndex);
674
+ };
675
+
676
+ const getActiveWindowsOrderByZIndexAsc = () => {
677
+ // zIndex 클수록, 최근에 열린 것일수록(sequence 클수록) 뒤에 위치
678
+ const compareByZIndex = (window1, window2) => {
679
+ if (window1.zIndex > window2.zIndex) return 1;
680
+ if (window1.zIndex < window2.zIndex) return -1;
681
+
682
+ if (window1.sequence > window2.sequence) return 1;
683
+ return -1;
684
+ };
685
+
686
+ const activeWindowsSorted = Array.prototype.map.call(activeWindows.windows, activeWindow => ({
687
+ ...activeWindow,
688
+ zIndex: getZIndexFromElement(activeWindow.elem),
689
+ })).sort(compareByZIndex);
690
+
691
+ return activeWindowsSorted;
692
+ };
693
+
694
+ const useEscCloseAndFocusable = ({ closeWin, windowRef }) => {
695
+ const { props } = getCurrentInstance();
696
+
697
+ let sequence = null;
698
+ let timer = null;
699
+
700
+ // escClose 관련 로직 시작
701
+ const addActiveWindow = () => {
702
+ const windowSequence = activeWindows.add({
703
+ sequence,
704
+ closeWin,
705
+ elem: windowRef.value,
706
+ escClose: props.escClose,
707
+ });
708
+ return windowSequence;
709
+ };
710
+
711
+ const removeInactiveWindow = (inactiveWindow) => {
712
+ activeWindows.remove(inactiveWindow);
713
+ };
714
+
715
+ const keydownEsc = (event) => {
716
+ if (activeWindows.isEmpty()) return;
717
+
718
+ const { code } = event;
719
+ if (code !== 'Escape') return;
720
+
721
+ const activeWindowsSorted = getActiveWindowsOrderByZIndexAsc();
722
+ const topActiveWindow = activeWindowsSorted[activeWindowsSorted.length - 1];
723
+
724
+ // 예시 상황) Nested에서 외부 Window의 escClose는 true이고, 내부 Window의 escClose는 false인 경우,
725
+ // esc 눌러도 외부 Window는 닫히지 않고, 가장 상단에 있는 내부 Window가 수동으로 닫힌 후에 닫히도록 하기 위해
726
+ if (!topActiveWindow.escClose) return;
727
+
728
+ topActiveWindow.closeWin();
729
+ };
730
+
731
+ const setWindowActive = () => {
732
+ sequence = addActiveWindow();
733
+ // DOM의 dataset에 sequence 값 추가해 식별 가능하도록
734
+ windowRef.value.dataset.sequence = sequence;
735
+
736
+ if (activeWindows.isFirstWindowOpen()) {
737
+ document.addEventListener('keydown', keydownEsc);
738
+ }
739
+ };
740
+
741
+ const setWindowInactive = () => {
742
+ const inactiveWindow = activeWindows.getWindowBySequence(sequence);
743
+ removeInactiveWindow(inactiveWindow);
744
+ };
745
+ // escClose 관련 로직 끝
746
+
747
+
748
+ // focusable 관련 로직 시작
749
+ const setZIndexToWindow = ({ elem, zIndex }) => {
750
+ // 모달인 경우에는 dim layer도 같이 z-index 높여준다.
751
+ if (props.isModal) {
752
+ const dimLayerElem = elem.parentElement.getElementsByClassName('ev-window-dim-layer')[0];
753
+ dimLayerElem.style.zIndex = zIndex;
754
+ }
755
+
756
+ elem.style.zIndex = zIndex;
757
+ };
758
+
759
+ const assignZIndex = () => {
760
+ // Window가 1번째로 열릴 때, z-index 값을 시작값으로 설정
761
+ if (activeWindows.windows.length === 1) {
762
+ zIndexService.resetToLower();
763
+ }
764
+
765
+ const nextZIndex = zIndexService.getNext();
766
+ setZIndexToWindow({ elem: windowRef.value, zIndex: nextZIndex });
767
+ };
768
+
769
+ const sameAsCurrent = windowData => String(windowData.sequence)
770
+ === windowRef.value.dataset.sequence;
771
+
772
+ // 할당하려는 z-index 값이 상한일 때
773
+ const reassignZIndex = () => {
774
+ const activeWindowsZIndexAsc = getActiveWindowsOrderByZIndexAsc();
775
+
776
+ const overCountLimit = activeWindows.windows.length > zIndexService.getAllocableCount();
777
+ // 할당 가능한 z-index 수보다 많은 Window를 띄웠을 때
778
+ if (overCountLimit) {
779
+ const activeWindowsZIndexDesc = activeWindowsZIndexAsc.reverse();
780
+
781
+ // z-index 기준 내림차순으로 정렬한 Window의 z-index 값을 UPPER에서 LOWER로 1씩 감소한 값 할당
782
+ let interval = 0;
783
+ activeWindowsZIndexDesc.forEach((activeWindow) => {
784
+ if (sameAsCurrent(activeWindow)) return;
785
+
786
+ const prevZIndex = zIndexService.getPrevFrom(interval++);
787
+ setZIndexToWindow({ elem: activeWindow.elem, zIndex: prevZIndex });
788
+ });
789
+
790
+ // 가장 상단으로 와야하는 현재 Window의 z-index 값을 최대로
791
+ const nextZIndex = zIndexService.getNextOverLimit();
792
+ setZIndexToWindow({ elem: windowRef.value, zIndex: nextZIndex });
793
+ } else {
794
+ zIndexService.resetToLower();
795
+
796
+ activeWindowsZIndexAsc.forEach((activeWindow) => {
797
+ if (sameAsCurrent(activeWindow)) return;
798
+
799
+ const nextZIndex = zIndexService.getNext();
800
+ setZIndexToWindow({ elem: activeWindow.elem, zIndex: nextZIndex });
801
+ });
802
+
803
+ // 가장 상단으로 와야하는 현재 Window의 z-index 값을 최대로
804
+ const nextZIndex = zIndexService.getNext();
805
+ setZIndexToWindow({ elem: windowRef.value, zIndex: nextZIndex });
806
+ }
807
+ };
808
+
809
+ const checkLimitAndSetZIndex = () => {
810
+ if (zIndexService.isUpperLimitClose()) {
811
+ reassignZIndex();
812
+ } else {
813
+ assignZIndex();
814
+ }
815
+ };
816
+
817
+ const setFocus = () => {
818
+ // X 버튼을 클릭했을 때
819
+ if (!windowRef.value) return;
820
+
821
+ // focusable prop이 false인 경우에는 z-index를 높이지 않는다.
822
+ if (!props.focusable) return;
823
+
824
+ const activeWindowsSorted = getActiveWindowsOrderByZIndexAsc();
825
+ const topActiveWindow = activeWindowsSorted[activeWindowsSorted.length - 1];
826
+
827
+ const isAlreadyTop = sameAsCurrent(topActiveWindow);
828
+ if (isAlreadyTop) return;
829
+
830
+ checkLimitAndSetZIndex();
831
+ };
832
+ // focusable 관련 로직 끝
833
+
834
+
835
+ /*
836
+ 실행 시점(=Window 열림):
837
+ visible 초기값이 true일 때 watch를 실행시키지 않아 onMounted hook 사용
838
+ */
839
+ onMounted(() => {
840
+ if (props.visible) {
841
+ setWindowActive();
842
+ timer = setTimeout(checkLimitAndSetZIndex, 0);
843
+ }
844
+ });
845
+
846
+ /*
847
+ 실행 시점(=Window 닫힘):
848
+ 예시. 배열을 통해 Window 여러 개 띄울 때, 배열 원소 삭제로 인해 해당 Window가 닫히는 경우
849
+ unmount가 발생해서 watch를 실행히시키지 않아 onBeforeUnmount hook 사용
850
+ */
851
+ onBeforeUnmount(() => {
852
+ if (props.visible) {
853
+ setWindowInactive();
854
+ clearTimeout(timer);
855
+ }
856
+ });
857
+
858
+ /*
859
+ 실행 시점:
860
+ 1. visible 값이 false -> true로 변했을 때(=Window 열림)
861
+ 2. visible 값이 true -> false로 변했을 때(=Window 닫힙)
862
+ */
863
+ watch(
864
+ () => props.visible,
865
+ (visible) => {
866
+ nextTick(() => {
867
+ if (visible) {
868
+ // visible 값이 false -> true로 변경되었을 때
869
+ setWindowActive();
870
+ timer = setTimeout(checkLimitAndSetZIndex, 0);
871
+ } else {
872
+ // visible 값이 true -> false로 변경되었을 때
873
+ setWindowInactive();
874
+ clearTimeout(timer);
875
+ }
876
+ });
877
+ });
878
+
879
+ watch(
880
+ () => props.escClose,
881
+ (escClose) => {
882
+ if (!props.visible) return;
883
+ const currentWindow = activeWindows.getWindowBySequence(sequence);
884
+ if (currentWindow) currentWindow.escClose = escClose;
885
+ },
886
+ );
887
+
888
+ return {
889
+ setFocus,
890
+ };
891
+ };
892
+
682
893
  export {
683
894
  useModel,
684
895
  useMouseEvent,
896
+ useEscCloseAndFocusable,
685
897
  };