파일 삭제 기능 추가, undo / redo 기능에 적용
This commit is contained in:
+64
-19
@@ -7,6 +7,7 @@ import {
|
||||
} from './grid.js';
|
||||
import { doRename } from './rename.js';
|
||||
import { handleFolderDrop } from './move.js';
|
||||
import * as deleteHandler from './delete.js';
|
||||
import * as pinchZoom from './handlers/pinch-zoom.js';
|
||||
import * as selection from './handlers/selection.js';
|
||||
import * as dragDrop from './handlers/drag-drop.js';
|
||||
@@ -39,13 +40,14 @@ history.setOnUpdate(updateHistoryButtons);
|
||||
|
||||
const callbacks = {
|
||||
refreshCardSelection, updateSelCount, updateNumberBadges, updateNewNameLabels,
|
||||
pushHistory: (affectedPaths) => history.push(state.files, affectedPaths),
|
||||
pushHistory: (affectedPaths) => history.pushReorder(state.files, affectedPaths),
|
||||
};
|
||||
|
||||
pinchZoom.init({ gridContainer, thumbSizeHint });
|
||||
selection.init({ gridContainer, rubberBand, grid, state, callbacks });
|
||||
dragDrop.init({ grid, gridContainer, dragGhostEl, state, callbacks, fileExplorerEl: fileExplorerContainer, onFolderDrop: handleFolderDrop });
|
||||
hoverPreview.init();
|
||||
deleteHandler.init(grid);
|
||||
fileExplorer.init(fileExplorerContainer, loadFolder);
|
||||
|
||||
window.api.onFullscreenChange((isFullscreen) => {
|
||||
@@ -93,8 +95,10 @@ document.addEventListener('keydown', (e) => {
|
||||
refreshCardSelection();
|
||||
updateSelCount();
|
||||
}
|
||||
if (e.code === 'KeyU') performUndo();
|
||||
if (e.code === 'KeyR') performRedo();
|
||||
if (e.metaKey && !e.shiftKey && e.code === 'KeyZ') { e.preventDefault(); performUndo(); }
|
||||
if (e.metaKey && e.shiftKey && e.code === 'KeyZ') { e.preventDefault(); performRedo(); }
|
||||
if (e.code === 'KeyP') btnPreviewToggle.click();
|
||||
if (e.code === 'Backspace' || e.code === 'Delete') deleteHandler.deleteSelected();
|
||||
});
|
||||
|
||||
// ── 폴더 열기 (다이얼로그) ───────────────────────
|
||||
@@ -145,7 +149,7 @@ async function loadFolder(folder) {
|
||||
updatePreview();
|
||||
btnRename.disabled = false;
|
||||
history.reset();
|
||||
history.push(state.files);
|
||||
history.pushReorder(state.files);
|
||||
}
|
||||
|
||||
// ── 정렬 ─────────────────────────────────────────
|
||||
@@ -158,7 +162,7 @@ async function sortFiles(mode) {
|
||||
updateSortButtons();
|
||||
await renderGrid();
|
||||
setStatus(`${mode === 'name' ? '이름순' : '날짜순'}으로 정렬됨`, true);
|
||||
history.push(state.files);
|
||||
history.pushReorder(state.files);
|
||||
}
|
||||
|
||||
function updateSortButtons() {
|
||||
@@ -167,43 +171,84 @@ function updateSortButtons() {
|
||||
}
|
||||
|
||||
// ── Undo / Redo ───────────────────────────────────
|
||||
let _historyBusy = false;
|
||||
|
||||
function updateHistoryButtons() {
|
||||
btnUndo.disabled = !history.canUndo();
|
||||
btnRedo.disabled = !history.canRedo();
|
||||
}
|
||||
|
||||
function performUndo() {
|
||||
if (!history.canUndo()) {
|
||||
showToast('더 이상 되돌릴 수 없습니다');
|
||||
async function performUndo() {
|
||||
if (_historyBusy || !history.canUndo()) {
|
||||
if (!history.canUndo()) showToast('더 이상 되돌릴 수 없습니다');
|
||||
return;
|
||||
}
|
||||
_applyHistorySnapshot(history.undo());
|
||||
_historyBusy = true;
|
||||
try {
|
||||
const { entry, targetFiles } = history.undo();
|
||||
if (entry.type === 'delete') {
|
||||
const { restored, errors } = await window.api.restoreStagedFiles(entry.deletedEntries);
|
||||
if (errors.length) showToast('일부 파일 복원 실패: ' + errors[0]);
|
||||
_applyHistorySnapshot(targetFiles, new Set(restored));
|
||||
if (restored.length) setStatus(`${restored.length}개 파일 복원됨`, true);
|
||||
} else {
|
||||
_applyHistorySnapshot(targetFiles, entry.affectedPaths);
|
||||
}
|
||||
} finally {
|
||||
_historyBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
function performRedo() {
|
||||
if (!history.canRedo()) {
|
||||
showToast('마지막 작업 상태입니다');
|
||||
async function performRedo() {
|
||||
if (_historyBusy || !history.canRedo()) {
|
||||
if (!history.canRedo()) showToast('마지막 작업 상태입니다');
|
||||
return;
|
||||
}
|
||||
_applyHistorySnapshot(history.redo());
|
||||
_historyBusy = true;
|
||||
try {
|
||||
const nextEntry = history.peekRedo();
|
||||
if (nextEntry.type === 'delete') {
|
||||
const pathsToRedo = nextEntry.deletedEntries.map(e => e.originalPath);
|
||||
const { staged, errors } = await window.api.stageDeleteFiles(pathsToRedo);
|
||||
if (errors.length) showToast('일부 파일 삭제 실패: ' + errors[0]);
|
||||
const stagedByPath = new Map(staged.map(s => [s.originalPath, s.stagingPath]));
|
||||
nextEntry.deletedEntries.forEach(e => {
|
||||
if (stagedByPath.has(e.originalPath)) e.stagingPath = stagedByPath.get(e.originalPath);
|
||||
});
|
||||
const { targetFiles } = history.redo();
|
||||
_applyHistorySnapshot(targetFiles, null);
|
||||
if (staged.length) setStatus(`${staged.length}개 파일 삭제됨`, true);
|
||||
} else {
|
||||
const { entry, targetFiles } = history.redo();
|
||||
_applyHistorySnapshot(targetFiles, entry.affectedPaths);
|
||||
}
|
||||
} finally {
|
||||
_historyBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
function _applyHistorySnapshot({ files, affectedPaths }) {
|
||||
function _applyHistorySnapshot(targetFiles, affectedPaths = null) {
|
||||
state.files.length = 0;
|
||||
state.files.push(...files);
|
||||
state.files.push(...targetFiles);
|
||||
state.selectedIdxs.clear();
|
||||
|
||||
if (affectedPaths) {
|
||||
if (affectedPaths && affectedPaths.size > 0) {
|
||||
const pathToIdx = new Map(state.files.map((f, i) => [f.path, i]));
|
||||
affectedPaths.forEach(path => {
|
||||
const idx = pathToIdx.get(path);
|
||||
affectedPaths.forEach(p => {
|
||||
const idx = pathToIdx.get(p);
|
||||
if (idx !== undefined) state.selectedIdxs.add(idx);
|
||||
});
|
||||
}
|
||||
|
||||
state.sortMode = null;
|
||||
updateSortButtons();
|
||||
renderGrid();
|
||||
if (!state.files.length) {
|
||||
showEmptyGrid();
|
||||
btnRename.disabled = true;
|
||||
} else {
|
||||
renderGrid();
|
||||
btnRename.disabled = false;
|
||||
}
|
||||
updateHistoryButtons();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user