Files
uniare.sori.studio/script/page-help.js
zenn 2ecf6201c0 v4.0.0: 덱 UX·i18n·썸네일 저장 개선 및 413 수정
즐겨찾기·홀드 미리보기·페이지 도움말·3개국어 번역을 추가하고, 덱 저장 시 썸네일을 1200px로 통일하며 Nginx/API 업로드 제한을 상향했다.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 18:50:08 +09:00

97 lines
2.9 KiB
JavaScript

/**
* 페이지 제목 옆 ? 버튼 — 설명 툴팁
*/
(function initPageHelp() {
/** @type {HTMLElement|null} */
let activeTooltip = null;
/** @type {HTMLElement|null} */
let activeBtn = null;
function closeTooltip() {
if (activeTooltip) {
activeTooltip.remove();
activeTooltip = null;
}
if (activeBtn) {
activeBtn.setAttribute('aria-expanded', 'false');
activeBtn = null;
}
}
/**
* @param {HTMLElement} btn
* @param {string} text
*/
function openTooltip(btn, text) {
if (!text) return;
closeTooltip();
const tip = document.createElement('div');
tip.className = 'page-help-tooltip';
tip.setAttribute('role', 'tooltip');
tip.id = `page-help-tip-${Date.now()}`;
tip.textContent = text;
document.body.appendChild(tip);
btn.setAttribute('aria-expanded', 'true');
btn.setAttribute('aria-describedby', tip.id);
const rect = btn.getBoundingClientRect();
const margin = 12;
let left = rect.left + window.scrollX;
const top = rect.bottom + window.scrollY + 8;
const tipWidth = tip.offsetWidth;
if (left + tipWidth > window.innerWidth - margin) {
left = window.innerWidth - tipWidth - margin;
}
left = Math.max(margin, left);
tip.style.top = `${top}px`;
tip.style.left = `${left}px`;
activeTooltip = tip;
activeBtn = btn;
}
/**
* @param {object} [translations]
*/
function bindPageHelp(translations) {
const tFn = (key, fallback) => {
if (translations?.[key]) return translations[key];
if (window.UaPageI18n?.t) return window.UaPageI18n.t(key, fallback);
return fallback;
};
document.querySelectorAll('[data-i18n-tooltip]').forEach((btn) => {
const key = btn.dataset.i18nTooltip;
const text = tFn(key, '');
const helpLabel = tFn('pageHelpLabel', '페이지 안내');
btn.setAttribute('aria-label', text ? `${helpLabel}` : helpLabel);
if (btn.dataset.helpBound === '1') return;
btn.dataset.helpBound = '1';
btn.addEventListener('click', (e) => {
e.stopPropagation();
const current = tFn(key, '');
if (activeBtn === btn && activeTooltip) {
closeTooltip();
return;
}
openTooltip(btn, current);
});
});
}
document.addEventListener('click', closeTooltip);
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeTooltip();
});
window.addEventListener('scroll', closeTooltip, { passive: true });
window.addEventListener('resize', closeTooltip);
window.UaPageHelp = { bindPageHelp, closeTooltip };
})();