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 (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; } });