선택한 이미지들을 드래그하고 폴더에 드롭하면 파일 이동 실행

This commit is contained in:
2026-05-15 16:31:58 +09:00
parent 595ceac26b
commit 604c1aa806
6 changed files with 283 additions and 11 deletions
+103 -2
View File
@@ -30,6 +30,13 @@ const gridContainer = document.getElementById('grid-container');
const dragGhostEl = document.getElementById('drag-ghost');
const thumbSizeHint = document.getElementById('thumb-size-hint');
const btnPreviewToggle = document.getElementById('btn-preview-toggle');
const moveModal = document.getElementById('move-modal');
const movePrefixInput = document.getElementById('move-prefix');
const moveDigitsInput = document.getElementById('move-digits');
const moveCancelBtn = document.getElementById('move-cancel');
const moveConfirmBtn = document.getElementById('move-confirm');
const moveErrorModal = document.getElementById('move-error-modal');
const moveErrorCloseBtn = document.getElementById('move-error-close');
// ── 핸들러 초기화 ─────────────────────────────────
const callbacks = {
@@ -39,12 +46,13 @@ const callbacks = {
updateNewNameLabels,
};
const fileExplorerContainer = document.querySelector('.file-explorer');
pinchZoom.init({ gridContainer, thumbSizeHint });
selection.init({ gridContainer, rubberBand, grid, state, callbacks });
dragDrop.init({ grid, gridContainer, dragGhostEl, state, callbacks });
dragDrop.init({ grid, gridContainer, dragGhostEl, state, callbacks, fileExplorerEl: fileExplorerContainer, onFolderDrop: handleFolderDrop });
hoverPreview.init();
const fileExplorerContainer = document.querySelector('.file-explorer');
fileExplorer.init(fileExplorerContainer, loadFolder);
window.api.onFullscreenChange((isFullscreen) => {
@@ -280,6 +288,99 @@ async function doRename() {
}
}
// ── 폴더로 파일 이동 ─────────────────────────────
let _pendingMoveTarget = null;
let _pendingMoveFiles = null;
async function handleFolderDrop(targetPath, fileIndices) {
if (!fileIndices.length || !state.currentFolder) return;
if (targetPath === state.currentFolder) return;
const sorted = [...fileIndices].sort((a, b) => a - b);
const selectedFiles = sorted.map(i => state.files[i]).filter(Boolean);
if (!selectedFiles.length) return;
const names = selectedFiles.map(f => f.name);
const conflicts = await window.api.checkFilesExist(targetPath, names);
if (!conflicts.length) {
await _doMoveFiles(targetPath, selectedFiles);
} else {
_pendingMoveTarget = targetPath;
_pendingMoveFiles = selectedFiles;
movePrefixInput.value = 'temp';
moveModal.style.display = 'flex';
}
}
moveCancelBtn.addEventListener('click', () => {
moveModal.style.display = 'none';
_pendingMoveTarget = null;
_pendingMoveFiles = null;
});
moveConfirmBtn.addEventListener('click', async () => {
const prefix = movePrefixInput.value.trim() || 'photo';
const digits = Math.max(1, Math.min(6, parseInt(moveDigitsInput.value) || 4));
const newNames = _pendingMoveFiles.map((f, i) => {
const ext = f.name.split('.').pop().toLowerCase();
return `${prefix}_${String(i + 1).padStart(digits, '0')}.${ext}`;
});
const conflicts = await window.api.checkFilesExist(_pendingMoveTarget, newNames);
if (conflicts.length) {
moveModal.style.display = 'none';
moveErrorModal.style.display = 'flex';
return;
}
moveModal.style.display = 'none';
const targetPath = _pendingMoveTarget;
const filesToMove = _pendingMoveFiles.map((f, i) => ({ ...f, newName: newNames[i] }));
_pendingMoveTarget = null;
_pendingMoveFiles = null;
await _doMoveFiles(targetPath, filesToMove);
});
moveErrorCloseBtn.addEventListener('click', () => {
moveErrorModal.style.display = 'none';
});
async function _doMoveFiles(targetPath, files) {
showLoading('파일 이동 중...');
try {
const result = await window.api.moveFiles({ targetFolder: targetPath, files });
hideLoading();
if (result.errors.length) {
alert('일부 오류:\n' + result.errors.join('\n'));
} else {
showToast(`${result.moved.length}개 파일 이동 완료!`);
setStatus(`${result.moved.length}개 파일 이동 완료`, true);
}
const newList = await window.api.listImages(state.currentFolder);
state.files.length = 0;
state.files.push(...newList);
state.selectedIdxs.clear();
if (!newList.length) {
grid.innerHTML = '';
grid.style.display = 'none';
emptyState.style.display = '';
fileCount.textContent = '0개 파일';
updateSelCount();
btnRename.disabled = true;
setStatus('이미지 파일 없음', false);
} else {
renderGrid();
setStatus(`${state.files.length}개 파일 로드됨`, true);
}
} catch (e) {
hideLoading();
alert('오류 발생: ' + e.message);
}
}
// ── UI 업데이트 ──────────────────────────────────
function refreshCardSelection() {