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

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
+76 -9
View File
@@ -1,21 +1,30 @@
let _grid, _gridContainer, _dragGhostEl;
let _state, _cb;
let _grid, _gridContainer, _dragGhostEl, _fileExplorerEl;
let _state, _cb, _onFolderDrop;
let dragMode = null;
let dragSrcIdx = null;
let _hoveredTreeRow = null;
let autoScrollRAF = null;
let autoScrollSpeed = 0;
let lastScrollTs = null;
export function init({ grid, gridContainer, dragGhostEl, state, callbacks }) {
_grid = grid;
_gridContainer = gridContainer;
_dragGhostEl = dragGhostEl;
_state = state;
_cb = callbacks;
export function init({ grid, gridContainer, dragGhostEl, state, callbacks, fileExplorerEl, onFolderDrop }) {
_grid = grid;
_gridContainer = gridContainer;
_dragGhostEl = dragGhostEl;
_state = state;
_cb = callbacks;
_fileExplorerEl = fileExplorerEl;
_onFolderDrop = onFolderDrop;
window.addEventListener('dragover', _onWindowDragOver);
if (fileExplorerEl) {
fileExplorerEl.addEventListener('dragover', _onExplorerDragOver);
fileExplorerEl.addEventListener('dragleave', _onExplorerDragLeave);
fileExplorerEl.addEventListener('drop', _onExplorerDrop);
}
}
// 카드 생성 시 호출해 드래그 이벤트를 부착
@@ -32,7 +41,7 @@ export function setupCard(card) {
function _onWindowDragOver(e) {
if (!dragMode) return;
if (dragMode === 'multi') _moveGhost(e);
_moveGhost(e);
_trackScroll(e.clientY);
}
@@ -97,6 +106,7 @@ function _onDrop(e) {
function _onDragEnd() {
_stopScroll();
_hideGhost();
_clearTreeHover();
document.querySelectorAll('.card').forEach(c =>
c.classList.remove('dragging', 'multi-dragging', 'drag-over')
);
@@ -104,6 +114,63 @@ function _onDragEnd() {
dragSrcIdx = null;
}
// ── 폴더 탐색기 드롭 핸들러 ─────────────────────────
function _onExplorerDragOver(e) {
if (!dragMode) return;
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
const row = e.target.closest('.tree-row');
if (row !== _hoveredTreeRow) {
_clearTreeHover();
_hoveredTreeRow = row;
if (_hoveredTreeRow) _hoveredTreeRow.classList.add('drop-target');
}
if (dragMode === 'single' && row && _dragGhostEl.style.display === 'none') {
_showGhost(e, '📁 1개 폴더로 이동');
}
if (dragMode === 'multi' && row) {
_dragGhostEl.textContent = `📁 ${_state.selectedIdxs.size}개 폴더로 이동`;
}
}
function _onExplorerDragLeave(e) {
if (!_fileExplorerEl.contains(e.relatedTarget)) {
_clearTreeHover();
if (dragMode === 'single') _hideGhost();
if (dragMode === 'multi') _dragGhostEl.textContent = `📦 ${_state.selectedIdxs.size}개 이동 중`;
}
}
function _onExplorerDrop(e) {
e.preventDefault();
const row = e.target.closest('.tree-row');
_clearTreeHover();
if (!row || !_onFolderDrop) return;
const folderNode = row.closest('.tree-node');
if (!folderNode) return;
const targetPath = folderNode.dataset.path;
const indices = _getDraggedFileIndices();
_onFolderDrop(targetPath, indices);
}
function _getDraggedFileIndices() {
if (dragMode === 'multi') return [..._state.selectedIdxs];
if (dragMode === 'single' && dragSrcIdx !== null) return [dragSrcIdx];
return [];
}
function _clearTreeHover() {
if (_hoveredTreeRow) {
_hoveredTreeRow.classList.remove('drop-target');
_hoveredTreeRow = null;
}
}
// ── 이동 로직 ─────────────────────────────────────
function _moveSingle(srcIdx, targetIdx) {