Files
PM_test/views/main/jsm/archive/fileDrag.js
2026-06-12 17:14:03 +09:00

348 lines
15 KiB
JavaScript

import { vars } from './variable.js';
import {
targetFocus,
syncGroupStyle
} from './common.js';
import {
resetViewer,
} from './pageRenderer.js';
let archiveMain = document.querySelector('.archive-main');
let viewerContainer = archiveMain?.querySelector('.archive-main-right .viewer-container');
addFileDragEvent(document.querySelector('.archive-main-center .list-container .list-body'), 'file-area');
addFileDragEvent(document.querySelector('.recycle-bin-modal .list-container .list-body'), 'recycle-bin');
export function addFileDragEvent(listBody, target) {
let draggingStartTarget, draggingEndTarget;
let draggingStart = false, isDragging = false;
let isCtrl = false, isShift = false;
let processedItems = new Set();
let originalSelection = new Set();
let multiSelectBox = document.createElement('div');
multiSelectBox.style.position = 'absolute';
multiSelectBox.style.border = '1px solid rgba(0, 120, 215, 1)';
multiSelectBox.style.background = 'rgba(0, 120, 215, 0.2)';
multiSelectBox.style.zIndex = '9999';
multiSelectBox.style.pointerEvents = 'none';
multiSelectBox.style.display = 'none';
listBody.appendChild(multiSelectBox);
vars.preventNextClick = false;
let startX = 0;
let startY = 0;
let prevClientX = 0;
let prevClientY = 0;
let autoScrollDirection = null;
let autoScrollFrameId = null;
const scrollSpeed = 8;
const scrollMargin = 36;
function autoScrollLoop() {
if (!autoScrollDirection) return;
// 더 작으면 느리고 부드러움
if (autoScrollDirection === 'down') {
listBody.scrollTop += scrollSpeed;
} else if (autoScrollDirection === 'up') {
listBody.scrollTop -= scrollSpeed;
}
autoScrollFrameId = requestAnimationFrame(autoScrollLoop);
}
listBody.addEventListener('pointerdown', (e) => {
// 마우스 좌클릭이 아닌 경우 리턴
if (e.button !== 0) return;
// 갤러리 폴더 지도화면인 경우 리턴
if (target == 'file-area' && listBody.querySelector('.map-container').style.display === 'flex') return;
draggingStartTarget = e.target;
draggingStart = true;
isDragging = false;
isCtrl = e.ctrlKey;
isShift = e.shiftKey;
processedItems.clear();
if (target == 'file-area') originalSelection = new Set(vars.multiSelectListItemArr);
if (target == 'recycle-bin') originalSelection = new Set(vars.multiSelectListItemArr_bin);
vars.preventNextClick = false;
// if (isCtrl) {
// vars.lastListItem = e.target.closest('.list-item');
// } else {
// listBody.querySelectorAll('.list-item').forEach(listItem => {
// listItem.classList.remove('selected');
// listItem.classList.remove('group-style');
// })
// vars.multiSelectListItemArr = [];
// }
const dragStartListItem = e.target.closest('.list-item');
if (dragStartListItem && !isShift) {
if (target == 'file-area') vars.lastListItem = dragStartListItem;
if (target == 'recycle-bin') vars.lastListItem_bin = dragStartListItem;
}
if (!isCtrl) {
// Ctrl이 아닐 경우 기존 선택 모두 해제
listBody.querySelectorAll('.list-item').forEach(listItem => {
listItem.classList.remove('selected');
listItem.classList.remove('group-style');
});
if (target == 'file-area') vars.multiSelectListItemArr = [];
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin = [];
}
const bodyRect = listBody.getBoundingClientRect();
startX = e.clientX + listBody.scrollLeft - bodyRect.left;
startY = e.clientY + listBody.scrollTop - bodyRect.top;
prevClientX = e.clientX;
prevClientY = e.clientY;
Object.assign(multiSelectBox.style, {
left: `${startX}px`,
top: `${startY}px`,
width: `0px`,
height: `0px`,
display: 'none'
});
});
listBody.addEventListener('pointermove', (e) => {
// 마우스 좌클릭이 아닌 경우 리턴
if (e.buttons !== 1) return;
// list-body에서 마우스 드래그가 시작된게 아닌 경우 리턴
if (!draggingStart) return;
if (e.clientX === prevClientX && e.clientY === prevClientY) return;
prevClientX = e.clientX;
prevClientY = e.clientY;
const listBodyRect = listBody.getBoundingClientRect();
const currX = e.clientX + listBody.scrollLeft - listBodyRect.left;
const currY = e.clientY + listBody.scrollTop - listBodyRect.top;
const dx = currX - startX;
const dy = currY - startY;
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
isDragging = true;
vars.preventNextClick = true;
const left = Math.min(startX, currX);
const top = Math.min(startY, currY);
const width = Math.abs(dx);
const height = Math.abs(dy);
Object.assign(multiSelectBox.style, {
left: `${left}px`,
top: `${top}px`,
width: `${width}px`,
height: `${height}px`,
display: 'block'
});
const boxRect = multiSelectBox.getBoundingClientRect();
listBody.querySelectorAll('.list-item .wrap').forEach(elem => {
const rect = elem.getBoundingClientRect();
const overlaps = !(
rect.right < boxRect.left ||
rect.left > boxRect.right ||
rect.bottom < boxRect.top ||
rect.top > boxRect.bottom
);
let listItem = elem.parentElement;
if (isCtrl) {
if (overlaps && !processedItems.has(listItem)) {
processedItems.add(listItem);
// ✅ 토글: 원래 선택되어 있었으면 해제, 아니면 선택
if (originalSelection.has(listItem)) {
listItem.classList.remove('selected');
listItem.classList.remove('group-style');
if (target == 'file-area') vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item !== listItem);
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin = vars.multiSelectListItemArr_bin.filter(item => item !== listItem);
} else {
listItem.classList.add('selected');
listItem.classList.add('group-style');
if (target == 'file-area') vars.multiSelectListItemArr.push(listItem);
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin.push(listItem);
}
}
// ✅ 박스에서 벗어난 항목 복원
if (!overlaps && processedItems.has(listItem)) {
processedItems.delete(listItem);
if (originalSelection.has(listItem)) {
listItem.classList.add('selected');
listItem.classList.add('group-style');
if (target == 'file-area') {
if (!vars.multiSelectListItemArr.includes(listItem)) vars.multiSelectListItemArr.push(listItem);
}
if (target == 'recycle-bin') {
if (!vars.multiSelectListItemArr_bin.includes(listItem)) vars.multiSelectListItemArr_bin.push(listItem);
}
} else {
listItem.classList.remove('selected');
listItem.classList.remove('group-style');
if (target == 'file-area') vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item !== listItem);
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin = vars.multiSelectListItemArr_bin.filter(item => item !== listItem);
}
}
} else {
listItem.classList.toggle('selected', overlaps);
listItem.classList.toggle('group-style', overlaps);
if (target == 'file-area') {
if (overlaps && !vars.multiSelectListItemArr.includes(listItem)) vars.multiSelectListItemArr.push(listItem);
}
if (target == 'recycle-bin') {
if (overlaps && !vars.multiSelectListItemArr_bin.includes(listItem)) vars.multiSelectListItemArr_bin.push(listItem);
}
}
});
if (e.clientY > listBodyRect.bottom - scrollMargin) {
if (autoScrollDirection !== 'down') {
cancelAnimationFrame(autoScrollFrameId);
autoScrollDirection = 'down';
autoScrollLoop();
}
} else if (e.clientY < listBodyRect.top + scrollMargin) {
if (autoScrollDirection !== 'up') {
cancelAnimationFrame(autoScrollFrameId);
autoScrollDirection = 'up';
autoScrollLoop();
}
} else {
if (autoScrollDirection !== null) {
cancelAnimationFrame(autoScrollFrameId);
autoScrollDirection = null;
}
}
}
});
document.addEventListener('pointerup', async (e) => {
// list-body에서 마우스 드래그가 시작된게 아닌 경우 리턴
if (!draggingStart) return;
if (target == 'file-area') {
if (vars.lastHeaderBtn) targetFocus(vars.lastHeaderBtn);
if (vars.lastMainTreeItem) targetFocus(vars.lastMainTreeItem);
}
if (isShift && isDragging) {
const dragStartListItem = draggingStartTarget.closest('.list-item');
if (dragStartListItem) {
if (target == 'file-area') vars.lastListItem = dragStartListItem;
if (target == 'recycle-bin') vars.lastListItem_bin = dragStartListItem;
}
}
multiSelectBox.style.display = 'none';
draggingEndTarget = e.target;
draggingStart = false;
isDragging = false;
isCtrl = false;
isShift = false;
// 드래그가 아이템 바깥 영역에서만 진행되고, 드래그에 포함된 아이템이 하나도 없을 때 뷰어 초기화
if (
(
(draggingStartTarget.matches('.list-body') && draggingEndTarget.matches('.list-body'))
|| (draggingStartTarget.matches('.list-item-wrap') && draggingEndTarget.matches('.list-item-wrap'))
)
&&
(
(target == 'file-area' && vars.multiSelectListItemArr.length == 0)
|| (target == 'recycle-bin' && vars.multiSelectListItemArr_bin.length == 0)
)
)
{
// 모든 아이템에서 non-selected 클래스 삭제
document.querySelectorAll('.grid-item').forEach(gridItem => {
gridItem.classList.remove('non-selected')
})
// if (viewerContainer.style.display == 'flex') resetViewer();
resetViewer();
vars.lastContextTarget = undefined;
if (target == 'file-area') {
vars.lastListGroupTarget = undefined;
vars.lastListItem = undefined;
// vars.lastContextTarget = undefined;
}
if (target == 'recycle-bin') {
vars.lastListGroupTarget_bin = undefined;
vars.lastListItem_bin = undefined;
// vars.lastContextTarget_bin = undefined;
}
// userSelected 삭제
let me = JSON.parse(vars.userInfoString);
me.selected = undefined;
vars.socket.emit('fileSelect',{me : me});
} else {
if (target == 'file-area') {
// 모든 아이템에 non-selected 클래스 추가
document.querySelectorAll('.grid-item').forEach(gridItem => {
gridItem.classList.add('non-selected')
})
}
}
// 드래그 시작한 대상과 종료된 대상이 다르면 뷰어 초기화
if (draggingStartTarget != draggingEndTarget) {
if (target == 'file-area') {
vars.lastListItem = undefined;
vars.lastListGroupTarget = undefined;
}
if (target == 'recycle-bin') {
vars.lastListItem_bin = undefined;
vars.lastListGroupTarget_bin = undefined;
}
if (viewerContainer.style.display == 'flex') resetViewer();
}
// 드래그가 끝나고 pointerup 이벤트가 발생하는 시점에 selected 클래스가 있는 listItem만 필터링
if (target == 'file-area') vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item.classList.contains('selected'));
if (target == 'recycle-bin') vars.multiSelectListItemArr_bin = vars.multiSelectListItemArr_bin.filter(item => item.classList.contains('selected'));
// vars.multiSelectListItemArr/vars.multiSelectListItemArr_bin에 포함된 아이템에서 non-selected 클래스 삭제
if (target == 'file-area') {
vars.multiSelectListItemArr.forEach(item => {
item.classList.remove('non-selected');
})
}
if (target == 'recycle-bin') {
vars.multiSelectListItemArr_bin.forEach(item => {
item.classList.remove('non-selected');
})
}
await syncGroupStyle();
// 드래그 시작한 대상의 부모요소와 종료된 대상의 부모요소가 다르면 뷰어 초기화
// if (draggingStartTarget.parentElement != draggingEndTarget.parentElement) {
// if (viewerContainer.style.display == 'flex') resetViewer();
// }
if (target == 'file-area') vars.lastSelectTarget = vars.multiSelectListItemArr[0];
if (target == 'recycle-bin') vars.lastSelectTarget_bin = vars.multiSelectListItemArr_bin[0];
cancelAnimationFrame(autoScrollFrameId);
autoScrollDirection = null;
});
}