v4.0.0: 덱 UX·i18n·썸네일 저장 개선 및 413 수정
즐겨찾기·홀드 미리보기·페이지 도움말·3개국어 번역을 추가하고, 덱 저장 시 썸네일을 1200px로 통일하며 Nginx/API 업로드 제한을 상향했다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
96
script/page-help.js
Normal file
96
script/page-help.js
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* 페이지 제목 옆 ? 버튼 — 설명 툴팁
|
||||
*/
|
||||
(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 };
|
||||
})();
|
||||
Reference in New Issue
Block a user