From fc0bc3e5c8f6052b18073e78d6ab99e768f4ee85 Mon Sep 17 00:00:00 2001 From: leeheejin Date: Wed, 22 Oct 2025 11:23:38 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9E=90=EB=8F=99=EC=8A=A4=ED=81=AC=EB=A1=A4,?= =?UTF-8?q?=20=EB=8B=A4=EC=A4=91=EC=84=A0=ED=83=9D=20=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=ED=9C=A0=EB=A1=9C=20=EC=9C=84=EC=95=84=EB=9E=98=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EA=B0=80=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/dashboard/CanvasElement.tsx | 101 +++++++++++++++-- .../admin/dashboard/DashboardCanvas.tsx | 103 ++++++++++++++---- .../admin/dashboard/DashboardDesigner.tsx | 24 ++-- 3 files changed, 189 insertions(+), 39 deletions(-) diff --git a/frontend/components/admin/dashboard/CanvasElement.tsx b/frontend/components/admin/dashboard/CanvasElement.tsx index 7bd4165e..55d45480 100644 --- a/frontend/components/admin/dashboard/CanvasElement.tsx +++ b/frontend/components/admin/dashboard/CanvasElement.tsx @@ -172,6 +172,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(null); // ๐Ÿ”ฅ requestAnimationFrame ID + const lastMouseYRef = useRef(window.innerHeight / 2); // ๐Ÿ”ฅ ๋งˆ์ง€๋ง‰ ๋งˆ์šฐ์Šค Y ์œ„์น˜ (์ดˆ๊ธฐ๊ฐ’: ํ™”๋ฉด ์ค‘๊ฐ„) const [resizeStart, setResizeStart] = useState({ x: 0, y: 0, @@ -213,12 +217,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) { @@ -269,8 +279,25 @@ 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); @@ -356,6 +383,7 @@ export function CanvasElement({ allElements, onUpdateMultiple, onMultiDragMove, + // dragStartRef, autoScrollDirectionRef, autoScrollFrameRef๋Š” ref๋ผ์„œ dependency ๋ถˆํ•„์š” ], ); @@ -393,7 +421,6 @@ export function CanvasElement({ position: { x: Math.max(0, Math.min(canvasWidth - targetElement.size.width, finalX + relativeX)), y: Math.max(0, finalY + relativeY), - z: targetElement.position.z, }, }, }; @@ -401,7 +428,7 @@ export function CanvasElement({ .filter((update): update is { id: string; updates: Partial } => update !== null); if (updates.length > 0) { - console.log("๐Ÿ”ฅ ๋‹ค์ค‘ ์„ ํƒ ์š”์†Œ ํ•จ๊ป˜ ์ด๋™:", updates); + // console.log("๐Ÿ”ฅ ๋‹ค์ค‘ ์„ ํƒ ์š”์†Œ ํ•จ๊ป˜ ์ด๋™:", updates); onUpdateMultiple(updates); } } @@ -437,6 +464,13 @@ export function CanvasElement({ setIsDragging(false); setIsResizing(false); + + // ๐Ÿ”ฅ ์ž๋™ ์Šคํฌ๋กค ์ •๋ฆฌ + autoScrollDirectionRef.current = null; + if (autoScrollFrameRef.current) { + cancelAnimationFrame(autoScrollFrameRef.current); + autoScrollFrameRef.current = null; + } }, [ isDragging, isResizing, @@ -455,6 +489,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) { @@ -586,7 +674,6 @@ export function CanvasElement({ const displayPosition = tempPosition || (multiDragOffset && !isDragging ? { x: element.position.x + multiDragOffset.x, y: element.position.y + multiDragOffset.y, - z: element.position.z, } : element.position); const displaySize = tempSize || element.size; diff --git a/frontend/components/admin/dashboard/DashboardCanvas.tsx b/frontend/components/admin/dashboard/DashboardCanvas.tsx index f586df57..1c2414c1 100644 --- a/frontend/components/admin/dashboard/DashboardCanvas.tsx +++ b/frontend/components/admin/dashboard/DashboardCanvas.tsx @@ -57,9 +57,14 @@ export const DashboardCanvas = forwardRef( } | null>(null); const [isSelecting, setIsSelecting] = useState(false); const [justSelected, setJustSelected] = useState(false); // ๐Ÿ”ฅ ๋ฐฉ๊ธˆ ์„ ํƒํ–ˆ๋Š”์ง€ ํ”Œ๋ž˜๊ทธ + const [isDraggingAny, setIsDraggingAny] = useState(false); // ๐Ÿ”ฅ ํ˜„์žฌ ๋“œ๋ž˜๊ทธ ์ค‘์ธ์ง€ ํ”Œ๋ž˜๊ทธ // ๐Ÿ”ฅ ๋‹ค์ค‘ ์„ ํƒ๋œ ์œ„์ ฏ๋“ค์˜ ์ž„์‹œ ์œ„์น˜ (๋“œ๋ž˜๊ทธ ์ค‘ ์‹œ๊ฐ์  ํ”ผ๋“œ๋ฐฑ) const [multiDragOffsets, setMultiDragOffsets] = useState>({}); + + // ๐Ÿ”ฅ ์„ ํƒ ๋ฐ•์Šค ๋“œ๋ž˜๊ทธ ์ค‘ ์ž๋™ ์Šคํฌ๋กค + const lastMouseYForSelectionRef = React.useRef(window.innerHeight / 2); + const selectionAutoScrollFrameRef = React.useRef(null); // ํ˜„์žฌ ์บ”๋ฒ„์Šค ํฌ๊ธฐ์— ๋งž๋Š” ๊ทธ๋ฆฌ๋“œ ์„ค์ • ๊ณ„์‚ฐ const gridConfig = useMemo(() => calculateGridConfig(canvasWidth), [canvasWidth]); @@ -207,11 +212,11 @@ export const DashboardCanvas = forwardRef( const isWidget = target.closest("[data-element-id]"); if (isWidget) { - console.log("๐Ÿšซ ์œ„์ ฏ ๋‚ด๋ถ€ ํด๋ฆญ - ์„ ํƒ ๋ฐ•์Šค ์‹œ์ž‘ ์•ˆํ•จ"); + // console.log("๐Ÿšซ ์œ„์ ฏ ๋‚ด๋ถ€ ํด๋ฆญ - ์„ ํƒ ๋ฐ•์Šค ์‹œ์ž‘ ์•ˆํ•จ"); return; } - console.log("โœ… ๋นˆ ๊ณต๊ฐ„ ํด๋ฆญ - ์„ ํƒ ๋ฐ•์Šค ์‹œ์ž‘"); + // console.log("โœ… ๋นˆ ๊ณต๊ฐ„ ํด๋ฆญ - ์„ ํƒ ๋ฐ•์Šค ์‹œ์ž‘"); if (!ref || typeof ref === "function") return; const rect = ref.current?.getBoundingClientRect(); @@ -246,7 +251,7 @@ export const DashboardCanvas = forwardRef( const minY = Math.min(selectionBox.startY, selectionBox.endY); const maxY = Math.max(selectionBox.startY, selectionBox.endY); - console.log("๐Ÿ” ์„ ํƒ ๋ฐ•์Šค:", { minX, maxX, minY, maxY }); + // console.log("๐Ÿ” ์„ ํƒ ๋ฐ•์Šค:", { minX, maxX, minY, maxY }); // ์„ ํƒ ๋ฐ•์Šค ์•ˆ์— ์žˆ๋Š” ์š”์†Œ๋“ค ์ฐพ๊ธฐ (70% ์ด์ƒ ๊ฒน์น˜๋ฉด ์„ ํƒ) const selectedIds = elements @@ -276,18 +281,18 @@ export const DashboardCanvas = forwardRef( // 70% ์ด์ƒ ๊ฒน์น˜๋ฉด ์„ ํƒ const overlapPercentage = overlapArea / elementArea; - console.log(`๐Ÿ“ฆ ์š”์†Œ ${el.id}:`, { - position: el.position, - size: el.size, - overlapPercentage: (overlapPercentage * 100).toFixed(1) + "%", - selected: overlapPercentage >= 0.7, - }); + // console.log(`๐Ÿ“ฆ ์š”์†Œ ${el.id}:`, { + // position: el.position, + // size: el.size, + // overlapPercentage: (overlapPercentage * 100).toFixed(1) + "%", + // selected: overlapPercentage >= 0.7, + // }); return overlapPercentage >= 0.7; }) .map((el) => el.id); - console.log("โœ… ์„ ํƒ๋œ ์š”์†Œ:", selectedIds); + // console.log("โœ… ์„ ํƒ๋œ ์š”์†Œ:", selectedIds); if (selectedIds.length > 0) { onSelectMultiple(selectedIds); @@ -313,30 +318,33 @@ export const DashboardCanvas = forwardRef( const x = e.clientX - rect.left + (ref.current?.scrollLeft || 0); const y = e.clientY - rect.top + (ref.current?.scrollTop || 0); - console.log("๐Ÿ–ฑ๏ธ ๋งˆ์šฐ์Šค ์ด๋™:", { x, y, startX: selectionBox.startX, startY: selectionBox.startY, isSelecting }); + // ๐Ÿ”ฅ ์ž๋™ ์Šคํฌ๋กค์„ ์œ„ํ•œ ๋งˆ์šฐ์Šค Y ์œ„์น˜ ์ €์žฅ + lastMouseYForSelectionRef.current = e.clientY; + + // console.log("๐Ÿ–ฑ๏ธ ๋งˆ์šฐ์Šค ์ด๋™:", { x, y, startX: selectionBox.startX, startY: selectionBox.startY, isSelecting }); // ๐Ÿ”ฅ selectionBox๊ฐ€ ์žˆ์ง€๋งŒ ์•„์ง isSelecting์ด false์ธ ๊ฒฝ์šฐ (๋“œ๋ž˜๊ทธ ์‹œ์ž‘ ๋Œ€๊ธฐ) if (!isSelecting) { const deltaX = Math.abs(x - selectionBox.startX); const deltaY = Math.abs(y - selectionBox.startY); - console.log("๐Ÿ“ ์ด๋™ ๊ฑฐ๋ฆฌ:", { deltaX, deltaY }); + // console.log("๐Ÿ“ ์ด๋™ ๊ฑฐ๋ฆฌ:", { deltaX, deltaY }); // ๐Ÿ”ฅ 5px ์ด์ƒ ์›€์ง์ด๋ฉด ์„ ํƒ ๋ฐ•์Šค ํ™œ์„ฑํ™” (์œ„์ ฏ ๋“œ๋ž˜๊ทธ์™€ ๊ตฌ๋ถ„) if (deltaX > 5 || deltaY > 5) { - console.log("๐ŸŽฏ ์„ ํƒ ๋ฐ•์Šค ํ™œ์„ฑํ™” (5px ์ด์ƒ ์ด๋™)"); + // console.log("๐ŸŽฏ ์„ ํƒ ๋ฐ•์Šค ํ™œ์„ฑํ™” (5px ์ด์ƒ ์ด๋™)"); setIsSelecting(true); } return; } // ๐Ÿ”ฅ ์„ ํƒ ๋ฐ•์Šค ์—…๋ฐ์ดํŠธ - console.log("๐Ÿ“ฆ ์„ ํƒ ๋ฐ•์Šค ์—…๋ฐ์ดํŠธ:", { startX: selectionBox.startX, startY: selectionBox.startY, endX: x, endY: y }); + // console.log("๐Ÿ“ฆ ์„ ํƒ ๋ฐ•์Šค ์—…๋ฐ์ดํŠธ:", { startX: selectionBox.startX, startY: selectionBox.startY, endX: x, endY: y }); setSelectionBox((prev) => (prev ? { ...prev, endX: x, endY: y } : null)); }; const handleDocumentMouseUp = () => { - console.log("๐Ÿ–ฑ๏ธ ๋งˆ์šฐ์Šค ์—… - handleMouseUp ํ˜ธ์ถœ"); + // console.log("๐Ÿ–ฑ๏ธ ๋งˆ์šฐ์Šค ์—… - handleMouseUp ํ˜ธ์ถœ"); handleMouseUp(); }; @@ -349,24 +357,77 @@ export const DashboardCanvas = forwardRef( }; }, [selectionBox, isSelecting, ref, handleMouseUp]); + // ๐Ÿ”ฅ ์„ ํƒ ๋ฐ•์Šค ๋“œ๋ž˜๊ทธ ์ค‘ ์ž๋™ ์Šคํฌ๋กค + useEffect(() => { + if (!isSelecting) { + // console.log("โŒ ์ž๋™ ์Šคํฌ๋กค ๋น„ํ™œ์„ฑํ™”: isSelecting =", isSelecting); + return; + } + + // console.log("โœ… ์ž๋™ ์Šคํฌ๋กค ํ™œ์„ฑํ™”: isSelecting =", isSelecting); + + const scrollSpeed = 3; + const scrollThreshold = 100; + let animationFrameId: number; + let lastTime = performance.now(); + + const autoScrollLoop = (currentTime: number) => { + const viewportHeight = window.innerHeight; + const lastMouseY = lastMouseYForSelectionRef.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; + + if (shouldScroll && deltaTime >= 10) { + window.scrollBy(0, scrollDirection); + // console.log("โœ… ์Šคํฌ๋กค ์‹คํ–‰ (์„ ํƒ ๋ฐ•์Šค):", { scrollDirection, deltaTime }); + lastTime = currentTime; + } + + animationFrameId = requestAnimationFrame(autoScrollLoop); + }; + + animationFrameId = requestAnimationFrame(autoScrollLoop); + selectionAutoScrollFrameRef.current = animationFrameId; + + return () => { + if (animationFrameId) { + cancelAnimationFrame(animationFrameId); + } + // console.log("๐Ÿ›‘ ์ž๋™ ์Šคํฌ๋กค ์ •๋ฆฌ"); + }; + }, [isSelecting]); + // ์บ”๋ฒ„์Šค ํด๋ฆญ ์‹œ ์„ ํƒ ํ•ด์ œ const handleCanvasClick = useCallback( (e: React.MouseEvent) => { - // ๐Ÿ”ฅ ๋ฐฉ๊ธˆ ์„ ํƒํ–ˆ์œผ๋ฉด ํด๋ฆญ ์ด๋ฒคํŠธ ๋ฌด์‹œ (์„ ํƒ ํ•ด์ œ ๋ฐฉ์ง€) - if (justSelected) { - console.log("๐Ÿšซ ๋ฐฉ๊ธˆ ์„ ํƒํ–ˆ์œผ๋ฏ€๋กœ ํด๋ฆญ ์ด๋ฒคํŠธ ๋ฌด์‹œ"); + // ๐Ÿ”ฅ ๋ฐฉ๊ธˆ ์„ ํƒํ–ˆ๊ฑฐ๋‚˜ ๋“œ๋ž˜๊ทธ ์ค‘์ด๋ฉด ํด๋ฆญ ์ด๋ฒคํŠธ ๋ฌด์‹œ (์„ ํƒ ํ•ด์ œ ๋ฐฉ์ง€) + if (justSelected || isDraggingAny) { + // console.log("๐Ÿšซ ๋ฐฉ๊ธˆ ์„ ํƒํ–ˆ๊ฑฐ๋‚˜ ๋“œ๋ž˜๊ทธ ์ค‘์ด๋ฏ€๋กœ ํด๋ฆญ ์ด๋ฒคํŠธ ๋ฌด์‹œ"); return; } if (e.target === e.currentTarget) { - console.log("โœ… ๋นˆ ๊ณต๊ฐ„ ํด๋ฆญ - ์„ ํƒ ํ•ด์ œ"); + // console.log("โœ… ๋นˆ ๊ณต๊ฐ„ ํด๋ฆญ - ์„ ํƒ ํ•ด์ œ"); onSelectElement(null); if (onSelectMultiple) { onSelectMultiple([]); } } }, - [onSelectElement, onSelectMultiple, justSelected], + [onSelectElement, onSelectMultiple, justSelected, isDraggingAny], ); // ๋™์  ๊ทธ๋ฆฌ๋“œ ํฌ๊ธฐ ๊ณ„์‚ฐ @@ -462,6 +523,7 @@ export const DashboardCanvas = forwardRef( onMultiDragStart={(draggedId, initialOffsets) => { // ๐Ÿ”ฅ ๋‹ค์ค‘ ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ - ์ดˆ๊ธฐ ์˜คํ”„์…‹ ์ €์žฅ setMultiDragOffsets(initialOffsets); + setIsDraggingAny(true); }} onMultiDragMove={(draggedElement, tempPosition) => { // ๐Ÿ”ฅ ๋‹ค์ค‘ ๋“œ๋ž˜๊ทธ ์ค‘ - ๋‹ค๋ฅธ ์œ„์ ฏ๋“ค์˜ ์œ„์น˜ ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ @@ -486,6 +548,7 @@ export const DashboardCanvas = forwardRef( onMultiDragEnd={() => { // ๐Ÿ”ฅ ๋‹ค์ค‘ ๋“œ๋ž˜๊ทธ ์ข…๋ฃŒ - ์˜คํ”„์…‹ ์ดˆ๊ธฐํ™” setMultiDragOffsets({}); + setIsDraggingAny(false); }} onRemove={onRemoveElement} onSelect={onSelectElement} diff --git a/frontend/components/admin/dashboard/DashboardDesigner.tsx b/frontend/components/admin/dashboard/DashboardDesigner.tsx index a73e6a47..2d482ab3 100644 --- a/frontend/components/admin/dashboard/DashboardDesigner.tsx +++ b/frontend/components/admin/dashboard/DashboardDesigner.tsx @@ -9,7 +9,7 @@ import { ListWidgetConfigModal } from "./widgets/ListWidgetConfigModal"; import { YardWidgetConfigModal } from "./widgets/YardWidgetConfigModal"; import { DashboardSaveModal } from "./DashboardSaveModal"; import { DashboardElement, ElementType, ElementSubtype } from "./types"; -import { GRID_CONFIG, snapToGrid, snapSizeToGrid, calculateCellSize } from "./gridUtils"; +import { GRID_CONFIG, snapToGrid, snapSizeToGrid, calculateCellSize, calculateGridConfig } from "./gridUtils"; import { Resolution, RESOLUTIONS, detectScreenResolution } from "./ResolutionSelector"; import { DashboardProvider } from "@/contexts/DashboardContext"; import { useMenu } from "@/contexts/MenuContext"; @@ -214,22 +214,22 @@ export default function DashboardDesigner({ dashboardId: initialDashboardId }: D return; } - // ๊ธฐ๋ณธ ํฌ๊ธฐ ์„ค์ • - let defaultCells = { width: 2, height: 2 }; // ๊ธฐ๋ณธ ์œ„์ ฏ ํฌ๊ธฐ + // ๊ธฐ๋ณธ ํฌ๊ธฐ ์„ค์ • (์„œ๋ธŒ๊ทธ๋ฆฌ๋“œ ๊ธฐ์ค€) + const gridConfig = calculateGridConfig(canvasConfig.width); + const subGridSize = gridConfig.SUB_GRID_SIZE; + + // ์„œ๋ธŒ๊ทธ๋ฆฌ๋“œ ๊ธฐ์ค€ ๊ธฐ๋ณธ ํฌ๊ธฐ (ํ”ฝ์…€) + let defaultWidth = subGridSize * 10; // ๊ธฐ๋ณธ ์œ„์ ฏ: ์„œ๋ธŒ๊ทธ๋ฆฌ๋“œ 10์นธ + let defaultHeight = subGridSize * 10; // ๊ธฐ๋ณธ ์œ„์ ฏ: ์„œ๋ธŒ๊ทธ๋ฆฌ๋“œ 10์นธ if (type === "chart") { - defaultCells = { width: 4, height: 3 }; // ์ฐจํŠธ + defaultWidth = subGridSize * 20; // ์ฐจํŠธ: ์„œ๋ธŒ๊ทธ๋ฆฌ๋“œ 20์นธ + defaultHeight = subGridSize * 15; // ์ฐจํŠธ: ์„œ๋ธŒ๊ทธ๋ฆฌ๋“œ 15์นธ } else if (type === "widget" && subtype === "calendar") { - defaultCells = { width: 2, height: 3 }; // ๋‹ฌ๋ ฅ ์ตœ์†Œ ํฌ๊ธฐ + defaultWidth = subGridSize * 10; // ๋‹ฌ๋ ฅ: ์„œ๋ธŒ๊ทธ๋ฆฌ๋“œ 10์นธ + defaultHeight = subGridSize * 15; // ๋‹ฌ๋ ฅ: ์„œ๋ธŒ๊ทธ๋ฆฌ๋“œ 15์นธ } - // ํ˜„์žฌ ํ•ด์ƒ๋„์— ๋งž๋Š” ์…€ ํฌ๊ธฐ ๊ณ„์‚ฐ - const cellSize = Math.floor((canvasConfig.width + GRID_CONFIG.GAP) / GRID_CONFIG.COLUMNS) - GRID_CONFIG.GAP; - const cellWithGap = cellSize + GRID_CONFIG.GAP; - - const defaultWidth = defaultCells.width * cellWithGap - GRID_CONFIG.GAP; - const defaultHeight = defaultCells.height * cellWithGap - GRID_CONFIG.GAP; - // ํฌ๊ธฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ if (isNaN(defaultWidth) || isNaN(defaultHeight) || defaultWidth <= 0 || defaultHeight <= 0) { // console.error("Invalid size calculated:", {