244 lines
7.4 KiB
JavaScript
244 lines
7.4 KiB
JavaScript
class UiDialog {
|
|
constructor(rootId = "uiDialog") {
|
|
this.root = document.getElementById(rootId);
|
|
if (!this.root) return;
|
|
|
|
this.titleEl = this.root.querySelector("#uiDialogTitle");
|
|
this.msgEl = this.root.querySelector("#uiDialogMessage");
|
|
this.okBtn = this.root.querySelector("#uiDialogOk");
|
|
this.cancelBtn = this.root.querySelector("#uiDialogCancel");
|
|
|
|
this._resolver = null;
|
|
this._type = "alert";
|
|
this._lastFocus = null;
|
|
|
|
// close triggers
|
|
this.root.addEventListener("click", (e) => {
|
|
if (!e.target?.dataset?.uidialogClose) return;
|
|
|
|
// 어떤 close인지 구분 (backdrop / x)
|
|
const isBackdrop = !!e.target.closest(".ui-dialog__backdrop");
|
|
const isX = !!e.target.closest(".ui-dialog__x");
|
|
|
|
// 정책에 따라 차단 (기본: 모두 차단)
|
|
if (isBackdrop && !this._closePolicy?.backdrop) return;
|
|
if (isX && !this._closePolicy?.x) return;
|
|
|
|
this._resolve(false);
|
|
});
|
|
|
|
|
|
// keyboard
|
|
document.addEventListener("keydown", (e) => {
|
|
if (!this.isOpen()) return;
|
|
|
|
if (e.key === "Escape") {
|
|
if (this._closePolicy?.esc) this._resolve(false);
|
|
return;
|
|
}
|
|
if (e.key === "Enter") {
|
|
// Enter는 OK로 처리 (기본 true)
|
|
if (this._closePolicy?.enter !== false) this._resolve(true);
|
|
return;
|
|
}
|
|
});
|
|
this.okBtn.addEventListener("click", () => this._resolve(true));
|
|
this.cancelBtn.addEventListener("click", () => this._resolve(false));
|
|
}
|
|
|
|
isOpen() {
|
|
return !!this.root && this.root.classList.contains("is-open");
|
|
}
|
|
|
|
alert(message, options = {}) {
|
|
return this._open("alert", message, options);
|
|
}
|
|
|
|
confirm(message, options = {}) {
|
|
return this._open("confirm", message, options);
|
|
}
|
|
|
|
_open(type, message, options) {
|
|
if (!this.root) return Promise.resolve(false);
|
|
|
|
const {
|
|
title = type === "confirm" ? "확인" : "알림",
|
|
okText = "확인",
|
|
cancelText = "취소",
|
|
dangerous = false,
|
|
|
|
// ✅ 기본: 밖 클릭/닫기(X)/ESC로 닫기 금지
|
|
closeOnBackdrop = false,
|
|
closeOnX = false,
|
|
closeOnEsc = false,
|
|
closeOnEnter = true,
|
|
} = options;
|
|
|
|
this._closePolicy = {
|
|
backdrop: !!closeOnBackdrop,
|
|
x: !!closeOnX,
|
|
esc: !!closeOnEsc,
|
|
enter: closeOnEnter !== false,
|
|
};
|
|
|
|
this._type = type;
|
|
this._lastFocus = document.activeElement;
|
|
|
|
this.titleEl.textContent = title;
|
|
this.msgEl.textContent = message ?? "";
|
|
|
|
this.okBtn.textContent = okText;
|
|
this.cancelBtn.textContent = cancelText;
|
|
|
|
// alert면 cancel 숨김
|
|
if (type === "alert") {
|
|
this.cancelBtn.style.display = "none";
|
|
} else {
|
|
this.cancelBtn.style.display = "";
|
|
}
|
|
|
|
// danger 스타일
|
|
this.okBtn.classList.toggle("ui-dialog__btn--danger", !!dangerous);
|
|
|
|
// open
|
|
this.root.classList.add("is-open");
|
|
this.root.setAttribute("aria-hidden", "false");
|
|
document.documentElement.style.overflow = "hidden";
|
|
|
|
// focus
|
|
setTimeout(() => this.okBtn.focus(), 0);
|
|
|
|
return new Promise((resolve) => {
|
|
this._resolver = resolve;
|
|
});
|
|
}
|
|
|
|
_resolve(ok) {
|
|
if (!this.isOpen()) return;
|
|
|
|
// close
|
|
this.root.classList.remove("is-open");
|
|
this.root.setAttribute("aria-hidden", "true");
|
|
document.documentElement.style.overflow = "";
|
|
|
|
const r = this._resolver;
|
|
this._resolver = null;
|
|
|
|
// focus restore
|
|
try { this._lastFocus?.focus?.(); } catch (e) {}
|
|
|
|
if (typeof r === "function") r(!!ok);
|
|
}
|
|
}
|
|
|
|
// 전역으로 노출 (모든 페이지에서 바로 호출)
|
|
window.uiDialog = new UiDialog();
|
|
|
|
|
|
// ======================================================
|
|
// ✅ Global showMsg / clearMsg (공통 사용)
|
|
// - 페이지에서는 showMsg/clearMsg만 호출
|
|
// - await showMsg(...) 하면 버튼 클릭 전까지 다음 코드 진행이 멈춤
|
|
// ======================================================
|
|
(function () {
|
|
let cachedHelpEl = null;
|
|
|
|
function getHelpEl(opt = {}) {
|
|
// opt.helpId를 주면 그걸 우선 사용
|
|
if (opt.helpId) {
|
|
const el = document.getElementById(opt.helpId);
|
|
if (el) return el;
|
|
}
|
|
// 기본 fallback id (너가 쓰는 reg_phone_help)
|
|
if (cachedHelpEl && document.contains(cachedHelpEl)) return cachedHelpEl;
|
|
cachedHelpEl = document.getElementById("reg_phone_help");
|
|
return cachedHelpEl;
|
|
}
|
|
|
|
/**
|
|
* showMsg(msg, opt)
|
|
* opt: {
|
|
* type:'alert'|'confirm',
|
|
* title, okText, cancelText, dangerous,
|
|
* redirect, helpId
|
|
* }
|
|
* return: Promise<boolean> (confirm: true/false, alert: true)
|
|
*/
|
|
window.showMsg = async function (msg, opt = {}) {
|
|
const d = Object.assign({
|
|
type: "alert",
|
|
title: "알림",
|
|
okText: "확인",
|
|
cancelText: "취소",
|
|
dangerous: false,
|
|
redirect: "",
|
|
helpId: "",
|
|
}, opt || {});
|
|
|
|
// ✅ 모달이 있으면 모달로 (여기서 await로 멈춤)
|
|
if (window.uiDialog && typeof window.uiDialog[d.type] === "function") {
|
|
const ok = await window.uiDialog[d.type](msg || "", d);
|
|
|
|
if (d.redirect) {
|
|
// alert: OK(true)일 때만 / confirm: OK(true)일 때만
|
|
if (ok) {
|
|
window.location.href = d.redirect;
|
|
}
|
|
}
|
|
|
|
return d.type === "confirm" ? !!ok : true;
|
|
}
|
|
|
|
// ✅ fallback (모달이 없으면 help 영역에 표시)
|
|
const helpEl = getHelpEl(d);
|
|
if (helpEl) {
|
|
helpEl.style.display = "block";
|
|
helpEl.textContent = msg || "";
|
|
return true;
|
|
}
|
|
|
|
// 마지막 fallback
|
|
alert(msg || "");
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* clearMsg(helpId?)
|
|
*/
|
|
window.clearMsg = function (helpId = "") {
|
|
const el = helpId ? document.getElementById(helpId) : getHelpEl({});
|
|
if (!el) return;
|
|
el.style.display = "none";
|
|
el.textContent = "";
|
|
};
|
|
})();
|
|
|
|
|
|
// ======================================================
|
|
// 서버 flash 자동 실행 (기존 유지)
|
|
// ======================================================
|
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
const flashEl = document.getElementById("uiDialogFlash");
|
|
if (!flashEl || !window.uiDialog) return;
|
|
|
|
let payload = null;
|
|
try { payload = JSON.parse(flashEl.textContent || "{}"); } catch (e) {}
|
|
|
|
if (!payload) return;
|
|
|
|
/**
|
|
* payload 예시:
|
|
* { type:'alert'|'confirm', message:'...', title:'...', okText:'...', cancelText:'...', redirect:'/...' }
|
|
*/
|
|
const type = payload.type || "alert";
|
|
const ok = await window.uiDialog[type](payload.message || "", payload);
|
|
|
|
// confirm이고 OK일 때만 redirect 같은 후속 처리
|
|
if (type === "confirm") {
|
|
if (ok && payload.redirect) window.location.href = payload.redirect;
|
|
} else {
|
|
if (payload.redirect) window.location.href = payload.redirect;
|
|
}
|
|
});
|
|
|