리팩토링: renderer.js 구성 요소들을 독립적인 파일로 나누기
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { state } from './state.js';
|
||||
import * as dragDrop from './handlers/drag-drop.js';
|
||||
import * as hoverPreview from './handlers/image-preview.js';
|
||||
import * as selection from './handlers/selection.js';
|
||||
|
||||
const grid = document.getElementById('grid');
|
||||
const emptyState = document.getElementById('empty-state');
|
||||
const gridContainer = document.getElementById('grid-container');
|
||||
const fileCount = document.getElementById('file-count');
|
||||
const selCount = document.getElementById('sel-count');
|
||||
const inputPrefix = document.getElementById('input-prefix');
|
||||
const inputDigits = document.getElementById('input-digits');
|
||||
|
||||
let _cardObserver = null;
|
||||
|
||||
export function renderGrid() {
|
||||
if (_cardObserver) { _cardObserver.disconnect(); _cardObserver = null; }
|
||||
|
||||
grid.innerHTML = '';
|
||||
emptyState.style.display = 'none';
|
||||
grid.style.display = 'grid';
|
||||
fileCount.textContent = `${state.files.length}개 파일`;
|
||||
updateSelCount();
|
||||
|
||||
state.files.forEach((file, idx) => grid.appendChild(createCard(file, idx)));
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(async (entry) => {
|
||||
if (!entry.isIntersecting) return;
|
||||
observer.unobserve(entry.target);
|
||||
const img = entry.target.querySelector('img.card-thumb');
|
||||
if (!img || img.src) return;
|
||||
const dataUrl = await window.api.readImage(entry.target.dataset.filepath);
|
||||
if (dataUrl) img.src = dataUrl;
|
||||
});
|
||||
}, { root: gridContainer, rootMargin: '400px 0px', threshold: 0 });
|
||||
|
||||
Array.from(grid.children).forEach(card => observer.observe(card));
|
||||
_cardObserver = observer;
|
||||
|
||||
refreshCardSelection();
|
||||
updateNumberBadges();
|
||||
updateNewNameLabels();
|
||||
}
|
||||
|
||||
export function createCard(file, idx) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card';
|
||||
card.dataset.idx = idx;
|
||||
card.dataset.filepath = file.path;
|
||||
card.draggable = true;
|
||||
|
||||
const numBadge = document.createElement('div');
|
||||
numBadge.className = 'card-num';
|
||||
numBadge.textContent = `#${idx + 1}`;
|
||||
|
||||
const checkBadge = document.createElement('div');
|
||||
checkBadge.className = 'card-check';
|
||||
checkBadge.textContent = '✓';
|
||||
|
||||
// src 없이 생성 — IntersectionObserver가 뷰포트 진입 시 로딩
|
||||
const thumb = document.createElement('img');
|
||||
thumb.className = 'card-thumb';
|
||||
thumb.alt = file.name;
|
||||
thumb.draggable = false;
|
||||
|
||||
const body = document.createElement('div');
|
||||
body.className = 'card-body';
|
||||
const nameEl = document.createElement('div');
|
||||
nameEl.className = 'card-name';
|
||||
nameEl.title = file.name;
|
||||
nameEl.textContent = file.name;
|
||||
const newNameEl = document.createElement('div');
|
||||
newNameEl.className = 'card-new-name';
|
||||
|
||||
body.appendChild(nameEl);
|
||||
body.appendChild(newNameEl);
|
||||
card.appendChild(numBadge);
|
||||
card.appendChild(checkBadge);
|
||||
card.appendChild(thumb);
|
||||
card.appendChild(body);
|
||||
|
||||
dragDrop.setupCard(card);
|
||||
hoverPreview.setupCard(card);
|
||||
card.addEventListener('click', selection.onCardClick);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
export function refreshCardSelection() {
|
||||
Array.from(grid.children).forEach((card, i) => {
|
||||
card.classList.toggle('selected', state.selectedIdxs.has(i));
|
||||
});
|
||||
}
|
||||
|
||||
export function updateNumberBadges() {
|
||||
Array.from(grid.children).forEach((card, i) => {
|
||||
const badge = card.querySelector('.card-num');
|
||||
if (badge) badge.textContent = `#${i + 1}`;
|
||||
card.dataset.idx = i;
|
||||
});
|
||||
}
|
||||
|
||||
export function updateNewNameLabels() {
|
||||
const prefix = inputPrefix.value.trim() || 'photo';
|
||||
const digits = Math.max(1, Math.min(6, parseInt(inputDigits.value) || 3));
|
||||
Array.from(grid.children).forEach((card, i) => {
|
||||
const el = card.querySelector('.card-new-name');
|
||||
const file = state.files[i];
|
||||
if (!el || !file) return;
|
||||
const ext = file.name.split('.').pop().toLowerCase();
|
||||
el.textContent = `→ ${prefix}_${String(i + 1).padStart(digits, '0')}.${ext}`;
|
||||
});
|
||||
}
|
||||
|
||||
export function updateSelCount() {
|
||||
const n = state.selectedIdxs.size;
|
||||
if (n > 0) {
|
||||
selCount.textContent = `${n}개 선택됨`;
|
||||
selCount.classList.add('visible');
|
||||
} else {
|
||||
selCount.textContent = '';
|
||||
selCount.classList.remove('visible');
|
||||
}
|
||||
}
|
||||
|
||||
export function showEmptyGrid() {
|
||||
grid.innerHTML = '';
|
||||
grid.style.display = 'none';
|
||||
emptyState.style.display = '';
|
||||
fileCount.textContent = '0개 파일';
|
||||
updateSelCount();
|
||||
}
|
||||
Reference in New Issue
Block a user