
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
<template>
<div v-show="isModalOpen" class="modal-wrapper" tabindex="-1" ref="modalContainer">
<div class="modal-container small-modal">
<div class="modal-title text-ct">
<h2>{{ modalTitle }}</h2>
</div>
<div class="modal-content-monthly">
<p class="alert-write text-ct" v-html="modalMessage">
</p>
<div v-if="modalType === 'radio'">
<p>원본파일 : </p>
<label></label>
<p>동작 : </p>
<label><input type="radio" value="move" v-model="moveOrCopy.type">덮어쓰기</label>
<label><input type="radio" value="cancel" v-model="moveOrCopy.type">건너뛰기</label>
<label><input type="checkbox" v-model="moveOrCopy.checkBox">항상 이 동작 사용</label>
</div>
</div>
<div class="modal-end flex justify-center" style="flex-wrap: nowrap;">
<button class="blue-btn large-btn" @click="onConfirm" ref="confirmBtn" @keydown="handleKeydown" v-if="isModalOpen">확인</button>
<button class="gray-btn large-btn" @click="onCancel" v-show="modalType === 'confirm'">취소</button>
<button class="gray-btn large-btn" @click="onRadioCancel" v-show="modalType === 'radio'">취소</button>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '모달 제목'
},
message: {
type: String,
default: '경고 메세지를 입력해주세요. <br /> 삭제,수정,추가 등등'
},
},
data() {
return {
isModalOpen: false,
modalType: 'alert', // 'alert', 'confirm', 'radio' 중 하나
modalTitle: this.title,
modalMessage: this.message,
moveOrCopy: {
type: "move",
checkBox: false
},
currentPromise: null,
keyboardEventHandler: null,
lastActiveElement: null
}
},
methods: {
// 키보드 이벤트 처리기
handleKeydown(event) {
// 모달 안에서 엔터키가 눌렸을 때
if (this.isModalOpen && event.key === 'Enter') {
event.preventDefault();
event.stopPropagation(); // 다른 요소로 이벤트 전파 방지
this.onConfirm(); // 확인 버튼 클릭 효과
return false;
}
},
// 모달 열릴 때 전역 이벤트 핸들러 추가
addGlobalKeyboardHandler() {
if (!this.keyboardEventHandler) {
this.keyboardEventHandler = (event) => {
// 모달이 열려있고 엔터키를 누를 때만 처리
if (this.isModalOpen && (event.key === 'Enter')) {
event.preventDefault();
event.stopPropagation();
if (event.key === 'Enter') {
this.onConfirm();
}
return false;
}
};
// 캡처 단계에서 이벤트를 잡아서 처리
document.addEventListener('keydown', this.keyboardEventHandler, true);
}
},
// 모달 닫힐 때 전역 이벤트 핸들러 제거
removeGlobalKeyboardHandler() {
if (this.keyboardEventHandler) {
document.removeEventListener('keydown', this.keyboardEventHandler, true);
this.keyboardEventHandler = null;
}
},
// 현재 활성 요소 저장
saveActiveElement() {
this.lastActiveElement = document.activeElement;
},
// 이전 활성 요소로 포커스 복원
restoreActiveElement() {
if (this.lastActiveElement && this.lastActiveElement.focus) {
setTimeout(() => {
try {
this.lastActiveElement.focus();
} catch (e) {
console.error('포커스 복원 중 오류:', e);
}
}, 100);
}
},
// 모달 닫기
closeModal() {
this.isModalOpen = false;
this.modalType = 'alert';
this.removeGlobalKeyboardHandler();
this.restoreActiveElement();
// 확실한 정리를 위한 추가 타이머
setTimeout(() => {
if (this.keyboardEventHandler) {
this.removeGlobalKeyboardHandler();
}
}, 500);
// 모달 닫힘 이벤트 발생 (선택적)
this.$emit('modal-closed');
},
// 일반 모달 표시
showModal() {
this.saveActiveElement();
this.modalType = 'alert';
this.isModalOpen = true;
this.addGlobalKeyboardHandler();
// 모달에 포커스 설정
this.$nextTick(() => {
if (this.$refs.confirmBtn) {
this.$refs.confirmBtn.focus();
} else if (this.$refs.modalContainer) {
this.$refs.modalContainer.focus();
}
});
// 모달 열림 이벤트 발생 (선택적)
this.$emit('modal-opened');
},
// 확인 버튼 클릭 핸들러
onConfirm() {
if (this.modalType === 'alert') {
// 단순 알림 모달은 그냥 닫기
this.closeModal();
} else if (this.modalType === 'confirm') {
// confirm 모달은 true 반환
this.resolvePromise(true);
} else if (this.modalType === 'radio') {
// radio 모달은 현재 선택된 값 반환
this.resolvePromise(this.moveOrCopy);
}
},
// 취소 버튼 클릭 핸들러 (일반 confirm용)
onCancel() {
this.resolvePromise(false);
},
// 취소 버튼 클릭 핸들러 (라디오 confirm용)
onRadioCancel() {
this.resolvePromise({
type: "cancel",
checkBox: false
});
},
// Promise 해결 및 모달 닫기
resolvePromise(value) {
if (this.currentPromise && this.currentPromise.resolve) {
this.currentPromise.resolve(value);
this.currentPromise = null;
}
this.closeModal();
},
// Promise 생성
createPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
this.currentPromise = { promise, resolve, reject };
return promise;
},
// confirm 모달 표시 (확인/취소)
async showConfirm() {
this.saveActiveElement();
this.modalType = 'confirm';
this.isModalOpen = true;
this.addGlobalKeyboardHandler();
// 모달에 포커스 설정
this.$nextTick(() => {
if (this.$refs.confirmBtn) {
this.$refs.confirmBtn.focus();
} else if (this.$refs.modalContainer) {
this.$refs.modalContainer.focus();
}
});
// 모달 열림 이벤트 발생 (선택적)
this.$emit('modal-opened');
return this.createPromise();
},
// radio confirm 모달 표시 (라디오 옵션)
async showRadioConfirm() {
this.saveActiveElement();
this.modalType = 'radio';
this.isModalOpen = true;
this.moveOrCopy.type = "move";
this.moveOrCopy.checkBox = false;
this.addGlobalKeyboardHandler();
// 모달에 포커스 설정
this.$nextTick(() => {
if (this.$refs.confirmBtn) {
this.$refs.confirmBtn.focus();
} else if (this.$refs.modalContainer) {
this.$refs.modalContainer.focus();
}
});
// 모달 열림 이벤트 발생 (선택적)
this.$emit('modal-opened');
return this.createPromise();
},
// 제목 설정
setTitle(title) {
this.modalTitle = title;
},
// 메시지 설정
setMessage(message) {
this.modalMessage = message;
},
},
watch: {
// props로 전달된 title이 변경되면 modalTitle 업데이트
title(newVal) {
this.modalTitle = newVal;
},
// props로 전달된 message가 변경되면 modalMessage 업데이트
message(newVal) {
this.modalMessage = newVal;
}
},
// 컴포넌트가 제거될 때 이벤트 리스너 정리
beforeDestroy() {
this.removeGlobalKeyboardHandler();
}
}
</script>