146 lines
3.1 KiB
JavaScript
146 lines
3.1 KiB
JavaScript
const HIDE_DELAY = 100;
|
|
|
|
let _el = null;
|
|
let _imgEl = null;
|
|
let _hideTimer = null;
|
|
let _visible = false;
|
|
let _activeCard = null;
|
|
let _hoverCard = null;
|
|
let _enabled = true;
|
|
let _showSeq = 0;
|
|
|
|
export function init() {
|
|
_el = document.createElement('div');
|
|
_el.className = 'hover-preview';
|
|
|
|
_imgEl = document.createElement('img');
|
|
_el.appendChild(_imgEl);
|
|
document.body.appendChild(_el);
|
|
}
|
|
|
|
export function setEnabled(v) {
|
|
_enabled = v;
|
|
if (!v) {
|
|
clearTimeout(_hideTimer); _hideTimer = null;
|
|
_hideNow();
|
|
return;
|
|
}
|
|
|
|
if (_hoverCard?.isConnected) {
|
|
_showForCard(_hoverCard);
|
|
}
|
|
}
|
|
|
|
export function setupCard(card) {
|
|
card.addEventListener('mouseenter', _onEnter);
|
|
card.addEventListener('mouseleave', _onLeave);
|
|
}
|
|
|
|
function _onEnter() {
|
|
const card = this;
|
|
_hoverCard = card;
|
|
if (!_enabled) return;
|
|
|
|
_activeCard = card;
|
|
|
|
if (_hideTimer) {
|
|
clearTimeout(_hideTimer);
|
|
_hideTimer = null;
|
|
}
|
|
|
|
if (_visible) {
|
|
_switchTo(card);
|
|
return;
|
|
}
|
|
|
|
_showForCard(card);
|
|
}
|
|
|
|
function _onLeave() {
|
|
if (_hoverCard === this) _hoverCard = null;
|
|
|
|
if (!_visible) {
|
|
return;
|
|
}
|
|
|
|
_hideTimer = setTimeout(() => {
|
|
_hideTimer = null;
|
|
_hideNow();
|
|
}, HIDE_DELAY);
|
|
}
|
|
|
|
// ── 팝업 내용 전환 (이미 열린 상태에서 카드 변경) ──
|
|
|
|
async function _showForCard(card) {
|
|
const seq = ++_showSeq;
|
|
_activeCard = card;
|
|
|
|
const src = await _loadSrc(card);
|
|
if (!src || !_enabled || _activeCard !== card || seq !== _showSeq) return;
|
|
|
|
_imgEl.src = src;
|
|
_visible = true;
|
|
_show(card);
|
|
}
|
|
|
|
async function _switchTo(card) {
|
|
const seq = ++_showSeq;
|
|
const src = await _loadSrc(card);
|
|
if (!src || !_enabled || _activeCard !== card || seq !== _showSeq) return;
|
|
|
|
_imgEl.src = src;
|
|
_show(card);
|
|
}
|
|
|
|
// ── 이미지 소스 반환 (카드 썸네일 캐시 우선) ──
|
|
|
|
async function _loadSrc(card) {
|
|
const thumb = card.querySelector('img.card-thumb');
|
|
let src = thumb?.src || '';
|
|
if (!src) {
|
|
src = await window.api.readImage(card.dataset.filepath) || '';
|
|
if (src && thumb) thumb.src = src;
|
|
}
|
|
return src;
|
|
}
|
|
|
|
function _hideNow() {
|
|
_showSeq++;
|
|
_activeCard = null;
|
|
_visible = false;
|
|
_el.style.display = 'none';
|
|
_el.style.visibility = 'hidden';
|
|
}
|
|
|
|
// ── 이미지 팝업 위치 계산 및 표시 ────────────────
|
|
|
|
function _show(card) {
|
|
_el.style.display = 'block';
|
|
_el.style.visibility = 'hidden';
|
|
_el.style.left = '0px';
|
|
_el.style.top = '0px';
|
|
|
|
requestAnimationFrame(() => {
|
|
if (!_visible) return;
|
|
|
|
const popup = _el.getBoundingClientRect();
|
|
const cardRect = card.getBoundingClientRect();
|
|
const vw = window.innerWidth;
|
|
const vh = window.innerHeight;
|
|
const gap = 16;
|
|
|
|
let left = cardRect.right + gap;
|
|
if (left + popup.width > vw - 8) {
|
|
left = cardRect.left - popup.width - gap;
|
|
}
|
|
left = Math.max(8, Math.min(left, vw - popup.width - 8));
|
|
|
|
let top = cardRect.top + (cardRect.height - popup.height) / 2;
|
|
top = Math.max(8, Math.min(top, vh - popup.height - 8));
|
|
|
|
_el.style.left = left + 'px';
|
|
_el.style.top = top + 'px';
|
|
_el.style.visibility = 'visible';
|
|
});
|
|
}
|