선택한 이미지들을 드래그하고 폴더에 드롭하면 파일 이동 실행
This commit is contained in:
+103
-2
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user