133 lines
4.3 KiB
JavaScript

(function () {
// ✅ 스크립트가 실수로 2번 로드돼도 바인딩 1번만
if (window.__ADMIN_UI_JS_BOUND__) return;
window.__ADMIN_UI_JS_BOUND__ = true;
// -----------------------
// click handlers
// -----------------------
document.addEventListener('click', (e) => {
const t = e.target;
// password toggle
if (t && t.matches('[data-toggle="password"]')) {
const pw = document.getElementById('password');
if (!pw) return;
const isPw = pw.type === 'password';
pw.type = isPw ? 'text' : 'password';
t.textContent = isPw ? '숨기기' : '보기';
return;
}
});
// -----------------------
// ✅ data-confirm (ONLY ONCE)
// - capture 단계에서 먼저 실행되어
// 취소 시 다른 submit 리스너(버튼 disable 등) 실행 안 됨
// -----------------------
document.addEventListener('submit', (e) => {
const form = e.target;
if (!(form instanceof HTMLFormElement)) return;
const raw = form.getAttribute('data-confirm');
if (!raw) return;
// 이미 confirm 통과한 submit이면 다시 confirm 금지
if (form.dataset.confirmed === '1') return;
// "\n" 문자, "
" 엔티티 모두 줄바꿈 처리
const msg = String(raw)
.replace(/\\n/g, '\n')
.replace(/
/g, '\n');
if (!window.confirm(msg)) {
e.preventDefault();
e.stopImmediatePropagation();
return;
}
form.dataset.confirmed = '1';
// confirm 통과 -> 그대로 진행 (다른 submit 리스너 정상 실행)
}, true);
// -----------------------
// login submit UI (disable + text)
// -----------------------
// document.addEventListener('submit', (e) => {
// const form = e.target;
// if (!form || !form.matches('[data-form="login"]')) return;
//
// const btn = form.querySelector('[data-submit]');
// if (btn) {
// btn.disabled = true;
// btn.dataset.original = btn.textContent;
// btn.textContent = '처리 중...';
// }
// });
// -----------------------
// showMsg (toast)
// -----------------------
if (typeof window.showMsg !== 'function') {
function ensureWrap() {
let wrap = document.getElementById('a-toast-wrap');
if (!wrap) {
wrap = document.createElement('div');
wrap.id = 'a-toast-wrap';
wrap.className = 'a-toast-wrap';
document.body.appendChild(wrap);
}
return wrap;
}
window.showMsg = function (message, opt = {}) {
const type = opt.type || 'info'; // success | info | warn | danger
const title = opt.title || '';
const wrap = ensureWrap();
const toast = document.createElement('div');
toast.className = `a-toast a-toast--${type}`;
const t = document.createElement('div');
t.className = 'a-toast__title';
t.textContent = title || (type === 'success' ? '완료' : type === 'danger' ? '오류' : '안내');
const m = document.createElement('div');
m.className = 'a-toast__msg';
m.textContent = String(message || '');
toast.appendChild(t);
toast.appendChild(m);
wrap.appendChild(toast);
const ttl = Number(opt.ttl || 3000);
window.setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateY(-4px)';
toast.style.transition = 'all .15s ease';
window.setTimeout(() => toast.remove(), 180);
}, ttl);
toast.addEventListener('click', () => toast.remove());
return Promise.resolve();
};
}
// flash 자동 표시
document.addEventListener('DOMContentLoaded', async () => {
const list = window.__adminFlash;
if (!Array.isArray(list) || list.length === 0) return;
for (const f of list) {
await window.showMsg(f.message, { type: f.type, title: f.title, ttl: 3200 });
}
window.__adminFlash = [];
});
})();