Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into feat/dashboard
This commit is contained in:
@@ -170,6 +170,10 @@ export function CanvasElement({
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const [dragStart, setDragStart] = useState({ x: 0, y: 0, elementX: 0, elementY: 0 });
|
||||
const dragStartRef = useRef({ x: 0, y: 0, elementX: 0, elementY: 0, initialScrollY: 0 }); // 🔥 스크롤 조정용 ref
|
||||
const autoScrollDirectionRef = useRef<"up" | "down" | null>(null); // 🔥 자동 스크롤 방향
|
||||
const autoScrollFrameRef = useRef<number | null>(null); // 🔥 requestAnimationFrame ID
|
||||
const lastMouseYRef = useRef<number>(window.innerHeight / 2); // 🔥 마지막 마우스 Y 위치 (초기값: 화면 중간)
|
||||
const [resizeStart, setResizeStart] = useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
@@ -211,12 +215,18 @@ export function CanvasElement({
|
||||
}
|
||||
|
||||
setIsDragging(true);
|
||||
setDragStart({
|
||||
const startPos = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
elementX: element.position.x,
|
||||
elementY: element.position.y,
|
||||
});
|
||||
initialScrollY: window.pageYOffset, // 🔥 드래그 시작 시점의 스크롤 위치
|
||||
};
|
||||
setDragStart(startPos);
|
||||
dragStartRef.current = startPos; // 🔥 ref에도 저장
|
||||
|
||||
// 🔥 드래그 시작 시 마우스 위치 초기화 (화면 중간)
|
||||
lastMouseYRef.current = window.innerHeight / 2;
|
||||
|
||||
// 🔥 다중 선택된 경우, 다른 위젯들의 오프셋 계산
|
||||
if (selectedElements.length > 1 && selectedElements.includes(element.id) && onMultiDragStart) {
|
||||
@@ -276,8 +286,26 @@ export function CanvasElement({
|
||||
const handleMouseMove = useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (isDragging) {
|
||||
const deltaX = e.clientX - dragStart.x;
|
||||
const deltaY = e.clientY - dragStart.y;
|
||||
// 🔥 자동 스크롤: 다중 선택 시 첫 번째 위젯에서만 처리
|
||||
const isFirstSelectedElement =
|
||||
!selectedElements || selectedElements.length === 0 || selectedElements[0] === element.id;
|
||||
|
||||
if (isFirstSelectedElement) {
|
||||
const scrollThreshold = 100;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const mouseY = e.clientY;
|
||||
|
||||
// 🔥 항상 마우스 위치 업데이트
|
||||
lastMouseYRef.current = mouseY;
|
||||
// console.log("🖱️ 마우스 위치 업데이트:", { mouseY, viewportHeight, top: scrollThreshold, bottom: viewportHeight - scrollThreshold });
|
||||
}
|
||||
|
||||
// 🔥 현재 스크롤 위치를 고려한 deltaY 계산
|
||||
const currentScrollY = window.pageYOffset;
|
||||
const scrollDelta = currentScrollY - dragStartRef.current.initialScrollY;
|
||||
|
||||
const deltaX = e.clientX - dragStartRef.current.x;
|
||||
const deltaY = e.clientY - dragStartRef.current.y + scrollDelta; // 🔥 스크롤 변화량 반영
|
||||
|
||||
// 임시 위치 계산
|
||||
let rawX = Math.max(0, dragStart.elementX + deltaX);
|
||||
@@ -363,6 +391,7 @@ export function CanvasElement({
|
||||
allElements,
|
||||
onUpdateMultiple,
|
||||
onMultiDragMove,
|
||||
// dragStartRef, autoScrollDirectionRef, autoScrollFrameRef는 ref라서 dependency 불필요
|
||||
],
|
||||
);
|
||||
|
||||
@@ -406,15 +435,18 @@ export function CanvasElement({
|
||||
return {
|
||||
id,
|
||||
updates: {
|
||||
position: newPosition,
|
||||
position: {
|
||||
x: Math.max(0, Math.min(canvasWidth - targetElement.size.width, finalX + relativeX)),
|
||||
y: Math.max(0, finalY + relativeY),
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.filter((update): update is { id: string; updates: { position: Position } } => update !== null);
|
||||
|
||||
if (updates.length > 0) {
|
||||
console.log("🔥 다중 선택 요소 함께 이동:", updates);
|
||||
onUpdateMultiple(updates as { id: string; updates: Partial<DashboardElement> }[]);
|
||||
// console.log("🔥 다중 선택 요소 함께 이동:", updates);
|
||||
onUpdateMultiple(updates);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -449,6 +481,13 @@ export function CanvasElement({
|
||||
|
||||
setIsDragging(false);
|
||||
setIsResizing(false);
|
||||
|
||||
// 🔥 자동 스크롤 정리
|
||||
autoScrollDirectionRef.current = null;
|
||||
if (autoScrollFrameRef.current) {
|
||||
cancelAnimationFrame(autoScrollFrameRef.current);
|
||||
autoScrollFrameRef.current = null;
|
||||
}
|
||||
}, [
|
||||
isDragging,
|
||||
isResizing,
|
||||
@@ -467,6 +506,60 @@ export function CanvasElement({
|
||||
dragStart.elementY,
|
||||
]);
|
||||
|
||||
// 🔥 자동 스크롤 루프 (requestAnimationFrame 사용)
|
||||
useEffect(() => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const scrollSpeed = 3; // 🔥 속도를 좀 더 부드럽게 (5 → 3)
|
||||
const scrollThreshold = 100;
|
||||
let animationFrameId: number;
|
||||
let lastTime = performance.now();
|
||||
|
||||
const autoScrollLoop = (currentTime: number) => {
|
||||
const viewportHeight = window.innerHeight;
|
||||
const lastMouseY = lastMouseYRef.current;
|
||||
|
||||
// 🔥 스크롤 방향 결정
|
||||
let shouldScroll = false;
|
||||
let scrollDirection = 0;
|
||||
|
||||
if (lastMouseY < scrollThreshold) {
|
||||
// 위쪽 영역
|
||||
shouldScroll = true;
|
||||
scrollDirection = -scrollSpeed;
|
||||
// console.log("⬆️ 위로 스크롤 조건 만족:", { lastMouseY, scrollThreshold });
|
||||
} else if (lastMouseY > viewportHeight - scrollThreshold) {
|
||||
// 아래쪽 영역
|
||||
shouldScroll = true;
|
||||
scrollDirection = scrollSpeed;
|
||||
// console.log("⬇️ 아래로 스크롤 조건 만족:", { lastMouseY, boundary: viewportHeight - scrollThreshold });
|
||||
}
|
||||
|
||||
// 🔥 프레임 간격 계산
|
||||
const deltaTime = currentTime - lastTime;
|
||||
|
||||
// 🔥 10ms 간격으로 스크롤
|
||||
if (shouldScroll && deltaTime >= 10) {
|
||||
window.scrollBy(0, scrollDirection);
|
||||
// console.log("✅ 스크롤 실행:", { scrollDirection, deltaTime });
|
||||
lastTime = currentTime;
|
||||
}
|
||||
|
||||
// 계속 반복
|
||||
animationFrameId = requestAnimationFrame(autoScrollLoop);
|
||||
};
|
||||
|
||||
// 루프 시작
|
||||
animationFrameId = requestAnimationFrame(autoScrollLoop);
|
||||
autoScrollFrameRef.current = animationFrameId;
|
||||
|
||||
return () => {
|
||||
if (animationFrameId) {
|
||||
cancelAnimationFrame(animationFrameId);
|
||||
}
|
||||
};
|
||||
}, [isDragging]);
|
||||
|
||||
// 전역 마우스 이벤트 등록
|
||||
React.useEffect(() => {
|
||||
if (isDragging || isResizing) {
|
||||
|
||||
Reference in New Issue
Block a user