Files
uniare.sori.studio/script/deck-preview-hold.js
zenn a281d731a1 v4.0.0: UNIARE 리브랜딩·레이아웃·덱 UX 개선
제품명·헤더 로고 이미지 통일, 덱 이름 변경·조회 수 제거, 썸네일 홀드 미리보기를 누르는 동안만 표시하도록 수정하고 Tailwind CSS를 재빌드했다.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 11:19:07 +09:00

148 lines
3.7 KiB
JavaScript

/**
* 썸네일 미리보기 확대 (길게 누르는 동안만 표시)
*/
(function initDeckPreviewHold() {
/** @type {HTMLElement|null} */
let overlay = null;
/** @type {number|null} */
let holdTimer = null;
/** @type {number} */
const HOLD_THRESHOLD_MS = 280;
/**
* @returns {HTMLElement}
*/
function ensureOverlay() {
if (!overlay) {
overlay = document.createElement('div');
overlay.className = 'deck-preview-hold-overlay hidden';
overlay.setAttribute('role', 'dialog');
overlay.setAttribute('aria-modal', 'true');
overlay.innerHTML =
'<div class="deck-preview-hold-box"><img alt="" draggable="false" /></div>';
document.body.appendChild(overlay);
}
return overlay;
}
/**
* @param {string} src
*/
function show(src) {
if (!src) return;
const el = ensureOverlay();
const img = el.querySelector('img');
if (img) img.src = src;
el.classList.remove('hidden');
}
function clearHoldTimer() {
if (holdTimer !== null) {
window.clearTimeout(holdTimer);
holdTimer = null;
}
}
function hide() {
clearHoldTimer();
if (overlay) overlay.classList.add('hidden');
}
/**
* @param {Event} e
* @returns {HTMLElement|null}
*/
function findHoldTarget(e) {
const target = e.target;
if (!(target instanceof Element)) return null;
return target.closest('[data-hold-preview]');
}
/**
* @param {HTMLElement} hold
* @returns {string}
*/
function getPreviewSrc(hold) {
return hold.dataset.previewSrc || '';
}
/**
* @param {PointerEvent} e
* @param {HTMLElement|null} hold
*/
function releaseCapture(e, hold) {
if (hold?.releasePointerCapture && e.pointerId !== undefined) {
try {
hold.releasePointerCapture(e.pointerId);
} catch {
/* ignore */
}
}
}
function initGlobal() {
if (document.documentElement.dataset.deckHoldInit === '1') return;
document.documentElement.dataset.deckHoldInit = '1';
document.addEventListener(
'pointerdown',
(e) => {
const hold = findHoldTarget(e);
if (!hold) return;
const src = getPreviewSrc(hold);
if (!src) return;
clearHoldTimer();
holdTimer = window.setTimeout(() => {
holdTimer = null;
show(src);
}, HOLD_THRESHOLD_MS);
if (hold.setPointerCapture && e.pointerId !== undefined) {
try {
hold.setPointerCapture(e.pointerId);
} catch {
/* ignore */
}
}
},
true,
);
document.addEventListener(
'pointerup',
(e) => {
const hold = findHoldTarget(e);
releaseCapture(e, hold);
hide();
},
true,
);
document.addEventListener(
'pointercancel',
() => {
hide();
},
true,
);
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') hide();
});
}
/**
* @param {HTMLElement} _container
*/
function bindHoldPreview(_container) {
initGlobal();
}
initGlobal();
window.UaDeckPreviewHold = { bindHoldPreview, hide };
})();