리펙토링 - 전체 프로젝트 코드 (Codex)

This commit is contained in:
2026-05-17 22:18:01 +09:00
parent 5337ea221b
commit 0f72f4a0f0
16 changed files with 508 additions and 356 deletions
+18 -75
View File
@@ -1,25 +1,27 @@
let _grid, _gridContainer, _dragGhostEl, _fileExplorerEl;
import { createAutoScroller } from './drag/auto-scroll.js';
import { createDragGhost } from './drag/ghost.js';
let _grid, _gridContainer, _fileExplorerEl;
let _state, _cb, _onFolderDrop;
let _dropSeparator = null;
let _originPlaceholders = [];
let _dropTargetIdx = null;
let _autoScroller = null;
let _ghost = null;
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, fileExplorerEl, onFolderDrop }) {
_grid = grid;
_gridContainer = gridContainer;
_dragGhostEl = dragGhostEl;
_state = state;
_cb = callbacks;
_fileExplorerEl = fileExplorerEl;
_onFolderDrop = onFolderDrop;
_autoScroller = createAutoScroller(_gridContainer, () => Boolean(dragMode));
_ghost = createDragGhost(dragGhostEl);
_dropSeparator = document.createElement('div');
_dropSeparator.className = 'drop-separator';
@@ -49,8 +51,8 @@ export function setupCard(card) {
function _onWindowDragOver(e) {
if (!dragMode) return;
_moveGhost(e);
_trackScroll(e.clientY);
_ghost.move(e);
_autoScroller.track(e.clientY);
}
// ── 카드 드래그 핸들러 ─────────────────────────────
@@ -68,7 +70,7 @@ function _onDragStart(e) {
const img = new Image();
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
e.dataTransfer.setDragImage(img, 0, 0);
_showGhost(e, `📦 ${count}개 이동 중`);
_ghost.show(e, `📦 ${count}개 이동 중`);
// 브라우저가 드래그 이미지를 캡처한 뒤 카드 숨기고 원본 위치 표시
requestAnimationFrame(() => {
@@ -136,8 +138,8 @@ function _performDrop() {
}
function _onDragEnd() {
_stopScroll();
_hideGhost();
_autoScroller.stop();
_ghost.hide();
_hideDropSeparator();
_clearTreeHover();
_clearOriginPlaceholders();
@@ -163,19 +165,19 @@ function _onExplorerDragOver(e) {
if (_hoveredTreeRow) _hoveredTreeRow.classList.add('drop-target');
}
if (dragMode === 'single' && row && _dragGhostEl.style.display === 'none') {
_showGhost(e, '📁 1개 폴더로 이동');
if (dragMode === 'single' && row && _ghost.isHidden()) {
_ghost.show(e, '📁 1개 폴더로 이동');
}
if (dragMode === 'multi' && row) {
_dragGhostEl.textContent = `📁 ${_state.selectedIdxs.size}개 폴더로 이동`;
_ghost.setText(`📁 ${_state.selectedIdxs.size}개 폴더로 이동`);
}
}
function _onExplorerDragLeave(e) {
if (!_fileExplorerEl.contains(e.relatedTarget)) {
_clearTreeHover();
if (dragMode === 'single') _hideGhost();
if (dragMode === 'multi') _dragGhostEl.textContent = `📦 ${_state.selectedIdxs.size}개 이동 중`;
if (dragMode === 'single') _ghost.hide();
if (dragMode === 'multi') _ghost.setText(`📦 ${_state.selectedIdxs.size}개 이동 중`);
}
}
@@ -287,65 +289,6 @@ function _moveMulti(targetIdx) {
_cb.pushHistory?.(movedFiles.map(f => f.path));
}
// ── 자동 스크롤 ───────────────────────────────────
function _trackScroll(clientY) {
const vh = window.innerHeight;
const topLine = vh * 0.1;
const botLine = vh * 0.9;
if (clientY < topLine) {
const ratio = clientY / topLine;
_startScroll(-vh * (1 - 0.8 * ratio));
} else if (clientY > botLine) {
const ratio = (clientY - botLine) / (vh * 0.1);
_startScroll(vh * (0.2 + 0.8 * ratio));
} else {
_stopScroll();
}
}
function _startScroll(speed) {
autoScrollSpeed = speed;
if (!autoScrollRAF) {
lastScrollTs = performance.now();
autoScrollRAF = requestAnimationFrame(_tickScroll);
}
}
function _stopScroll() {
autoScrollSpeed = 0;
if (autoScrollRAF) {
cancelAnimationFrame(autoScrollRAF);
autoScrollRAF = null;
}
}
function _tickScroll(now) {
if (!dragMode) { _stopScroll(); return; }
const dt = (now - lastScrollTs) / 1000;
lastScrollTs = now;
_gridContainer.scrollTop += autoScrollSpeed * dt;
autoScrollRAF = requestAnimationFrame(_tickScroll);
}
// ── 드래그 고스트 ─────────────────────────────────
function _showGhost(e, text) {
_dragGhostEl.textContent = text;
_dragGhostEl.style.display = 'block';
_moveGhost(e);
}
function _moveGhost(e) {
_dragGhostEl.style.left = (e.clientX + 16) + 'px';
_dragGhostEl.style.top = (e.clientY - 14) + 'px';
}
function _hideGhost() {
_dragGhostEl.style.display = 'none';
}
function _getCardEl(idx) {
return _grid.querySelector(`.card[data-idx="${idx}"]`);
}
+51
View File
@@ -0,0 +1,51 @@
export function createAutoScroller(container, isActive) {
let frameId = null;
let speed = 0;
let lastTs = null;
function track(clientY) {
const vh = window.innerHeight;
const topLine = vh * 0.1;
const botLine = vh * 0.9;
if (clientY < topLine) {
const ratio = clientY / topLine;
start(-vh * (1 - 0.8 * ratio));
} else if (clientY > botLine) {
const ratio = (clientY - botLine) / (vh * 0.1);
start(vh * (0.2 + 0.8 * ratio));
} else {
stop();
}
}
function start(nextSpeed) {
speed = nextSpeed;
if (!frameId) {
lastTs = performance.now();
frameId = requestAnimationFrame(tick);
}
}
function stop() {
speed = 0;
if (frameId) {
cancelAnimationFrame(frameId);
frameId = null;
}
}
function tick(now) {
if (!isActive()) {
stop();
return;
}
const dt = (now - lastTs) / 1000;
lastTs = now;
container.scrollTop += speed * dt;
frameId = requestAnimationFrame(tick);
}
return { track, stop };
}
+26
View File
@@ -0,0 +1,26 @@
export function createDragGhost(el) {
function show(e, text) {
el.textContent = text;
el.style.display = 'block';
move(e);
}
function move(e) {
el.style.left = `${e.clientX + 16}px`;
el.style.top = `${e.clientY - 14}px`;
}
function hide() {
el.style.display = 'none';
}
function setText(text) {
el.textContent = text;
}
function isHidden() {
return el.style.display === 'none';
}
return { hide, isHidden, move, setText, show };
}