제품명·헤더 로고 이미지 통일, 덱 이름 변경·조회 수 제거, 썸네일 홀드 미리보기를 누르는 동안만 표시하도록 수정하고 Tailwind CSS를 재빌드했다. Co-authored-by: Cursor <cursoragent@cursor.com>
148 lines
3.7 KiB
JavaScript
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 };
|
|
})();
|