diff --git a/.cursorrules b/.cursorrules index 3b0c3833..cf9eaae9 100644 --- a/.cursorrules +++ b/.cursorrules @@ -1,5 +1,22 @@ # Cursor Rules for ERP-node Project +## ๐Ÿ”ฅ ํ•„์ˆ˜ ํ™•์ธ ๊ทœ์น™ (์ž‘์—… ์‹œ์ž‘ ์ „ & ์™„๋ฃŒ ํ›„) + +**AI ์—์ด์ „ํŠธ๋Š” ๋ชจ๋“  ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „๊ณผ ์™„๋ฃŒํ•œ ํ›„์— ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ ํŒŒ์ผ์„ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:** +- [AI-๊ฐœ๋ฐœ์ž ํ˜‘์—… ์ž‘์—… ์ˆ˜์น™](.cursor/rules/ai-developer-collaboration-rules.mdc) + +**ํ•ต์‹ฌ 3์›์น™:** +1. **ํ™•์ธ ์šฐ์„ ** ๐Ÿ” - ์ถ”์ธกํ•˜์ง€ ๋ง๊ณ , ํ•ญ์ƒ ํ™•์ธํ•˜๊ณ  ์ž‘์—… +2. **ํ•œ ๋ฒˆ์— ํ•˜๋‚˜** ๐ŸŽฏ - ์—ฌ๋Ÿฌ ๋ฌธ์ œ๋ฅผ ๋™์‹œ์— ํ•ด๊ฒฐํ•˜๋ ค ํ•˜์ง€ ๋ง๊ธฐ +3. **์ฒ ์ €ํ•œ ๋งˆ๋ฌด๋ฆฌ** โœจ - ๋กœ๊ทธ ์ œ๊ฑฐ, ํ…Œ์ŠคํŠธ, ๋ช…ํ™•ํ•œ ์„ค๋ช… + +**์ ˆ๋Œ€ ๊ธˆ์ง€:** +- โŒ ํ™•์ธ ์—†์ด "์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค" ๋งํ•˜๊ธฐ +- โŒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปฌ๋Ÿผ๋ช… ์ถ”์ธกํ•˜๊ธฐ (๋ฐ˜๋“œ์‹œ MCP๋กœ ํ™•์ธ) +- โŒ ๋””๋ฒ„๊น… ๋กœ๊ทธ๋ฅผ ๋‚จ๊ฒจ๋‘” ์ฑ„ ์ž‘์—… ์ข…๋ฃŒ + +--- + ## ๐Ÿšจ ์ตœ์šฐ์„  ๋ณด์•ˆ ๊ทœ์น™: ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ **๋ชจ๋“  ์ฝ”๋“œ ์ž‘์„ฑ/์ˆ˜์ • ์™„๋ฃŒ ํ›„ ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ ํŒŒ์ผ์„ ํ™•์ธํ•˜์„ธ์š”:** diff --git a/.gitignore b/.gitignore index a771d2c9..e6e30135 100644 --- a/.gitignore +++ b/.gitignore @@ -286,4 +286,5 @@ uploads/ *.hwp *.hwpx -claude.md \ No newline at end of file +claude.md +.cursor/rules/ai-developer-collaboration-rules.mdc diff --git a/frontend/components/screen/RealtimePreview.tsx b/frontend/components/screen/RealtimePreview.tsx index ab8cc3ae..86a2f357 100644 --- a/frontend/components/screen/RealtimePreview.tsx +++ b/frontend/components/screen/RealtimePreview.tsx @@ -57,7 +57,7 @@ interface RealtimePreviewProps { isSelected?: boolean; isDesignMode?: boolean; onClick?: (e?: React.MouseEvent) => void; - onDragStart?: (e: React.DragEvent) => void; + onDragStart?: (e: React.MouseEvent | React.DragEvent) => void; // MouseEvent๋„ ํ—ˆ์šฉ onDragEnd?: () => void; onGroupToggle?: (groupId: string) => void; // ๊ทธ๋ฃน ์ ‘๊ธฐ/ํŽผ์น˜๊ธฐ children?: React.ReactNode; // ๊ทธ๋ฃน ๋‚ด ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋“ค @@ -247,6 +247,13 @@ export const RealtimePreviewDynamic: React.FC = ({ }) => { const { user } = useAuth(); const { type, id, position, size, style = {} } = component; + + // ๐Ÿ” [๋””๋ฒ„๊น…] ๋ Œ๋”๋ง ์‹œ ํฌ๊ธฐ ๋กœ๊ทธ + console.log("๐ŸŽจ [RealtimePreview] ๋ Œ๋”๋ง", { + componentId: id, + size, + position, + }); const [fileUpdateTrigger, setFileUpdateTrigger] = useState(0); const [actualHeight, setActualHeight] = useState(null); const contentRef = React.useRef(null); @@ -458,7 +465,17 @@ export const RealtimePreviewDynamic: React.FC = ({ onClick?.(e); }; + const handleMouseDown = (e: React.MouseEvent) => { + // ๋””์ž์ธ ๋ชจ๋“œ์—์„œ๋งŒ ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ (์บ”๋ฒ„์Šค ๋‚ด ์ด๋™์šฉ) + if (isDesignMode && onDragStart) { + e.stopPropagation(); + // MouseEvent๋ฅผ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌ + onDragStart(e); + } + }; + const handleDragStart = (e: React.DragEvent) => { + // HTML5 Drag API (ํŒ”๋ ˆํŠธ์—์„œ ์บ”๋ฒ„์Šค๋กœ ๋“œ๋ž˜๊ทธ์šฉ) e.stopPropagation(); onDragStart?.(e); }; @@ -473,8 +490,9 @@ export const RealtimePreviewDynamic: React.FC = ({ className="absolute cursor-pointer" style={{ ...componentStyle, ...selectionStyle }} onClick={handleClick} - draggable - onDragStart={handleDragStart} + onMouseDown={isDesignMode ? handleMouseDown : undefined} + draggable={!isDesignMode} // ๋””์ž์ธ ๋ชจ๋“œ๊ฐ€ ์•„๋‹ ๋•Œ๋งŒ draggable (ํŒ”๋ ˆํŠธ์šฉ) + onDragStart={!isDesignMode ? handleDragStart : undefined} onDragEnd={handleDragEnd} > {/* ์ปดํฌ๋„ŒํŠธ ํƒ€์ž…๋ณ„ ๋ Œ๋”๋ง */} diff --git a/frontend/components/screen/RealtimePreviewDynamic.tsx b/frontend/components/screen/RealtimePreviewDynamic.tsx index 679ed5a8..80d577d6 100644 --- a/frontend/components/screen/RealtimePreviewDynamic.tsx +++ b/frontend/components/screen/RealtimePreviewDynamic.tsx @@ -264,6 +264,9 @@ export const RealtimePreviewDynamic: React.FC = ({ height: getHeight(), zIndex: component.type === "layout" ? 1 : position.z || 2, ...componentStyle, + // ๐Ÿ”ฅ ์ค‘์š”: componentStyle.width๋ฅผ ๋ฎ์–ด์“ฐ๊ธฐ ์œ„ํ•ด ๋‹ค์‹œ ์„ค์ • + width: getWidth(), // size.width ๊ธฐ๋ฐ˜ ํ”ฝ์…€ ๊ฐ’์œผ๋กœ ๊ฐ•์ œ + height: getHeight(), // size.height ๊ธฐ๋ฐ˜ ํ”ฝ์…€ ๊ฐ’์œผ๋กœ ๊ฐ•์ œ right: undefined, }; diff --git a/frontend/components/screen/ScreenDesigner.tsx b/frontend/components/screen/ScreenDesigner.tsx index 7db03da6..5c5e4dd2 100644 --- a/frontend/components/screen/ScreenDesigner.tsx +++ b/frontend/components/screen/ScreenDesigner.tsx @@ -29,13 +29,9 @@ import { snapToGrid, snapSizeToGrid, generateGridLines, - updateSizeFromGridColumns, - adjustGridColumnsFromSize, alignGroupChildrenToGrid, calculateOptimalGroupSize, normalizeGroupChildPositions, - calculateWidthFromColumns, - GridSettings as GridUtilSettings, } from "@/lib/utils/gridUtils"; import { GroupingToolbar } from "./GroupingToolbar"; import { screenApi, tableTypeApi } from "@/lib/api/screen"; @@ -107,11 +103,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const [layout, setLayout] = useState({ components: [], gridSettings: { - columns: 12, - gap: 16, - padding: 0, - snapToGrid: true, - showGrid: false, // ๊ธฐ๋ณธ๊ฐ’ false๋กœ ๋ณ€๊ฒฝ + snapToGrid: true, // ๊ฒฉ์ž ์Šค๋ƒ… ON + showGrid: false, // ๊ฒฉ์ž ํ‘œ์‹œ OFF gridColor: "#d1d5db", gridOpacity: 0.5, }, @@ -540,107 +533,31 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD gridInfo && newComp.type !== "group" ) { - // ํ˜„์žฌ ํ•ด์ƒ๋„์— ๋งž๋Š” ๊ฒฉ์ž ์ •๋ณด๋กœ ์Šค๋ƒ… ์ ์šฉ - const currentGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, { - columns: prevLayout.gridSettings.columns, - gap: prevLayout.gridSettings.gap, - padding: prevLayout.gridSettings.padding, - snapToGrid: prevLayout.gridSettings.snapToGrid || false, - }); - const snappedSize = snapSizeToGrid( - newComp.size, - currentGridInfo, - prevLayout.gridSettings as GridUtilSettings, + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž๋กœ ์Šค๋ƒ… + const currentGridInfo = calculateGridInfo( + screenResolution.width, + screenResolution.height, + prevLayout.gridSettings, ); + const snappedSize = snapSizeToGrid(newComp.size, currentGridInfo, prevLayout.gridSettings); newComp.size = snappedSize; - - // ํฌ๊ธฐ ๋ณ€๊ฒฝ ์‹œ gridColumns๋„ ์ž๋™ ์กฐ์ • - const adjustedColumns = adjustGridColumnsFromSize( - newComp, - currentGridInfo, - prevLayout.gridSettings as GridUtilSettings, - ); - if (newComp.gridColumns !== adjustedColumns) { - newComp.gridColumns = adjustedColumns; - } } - // gridColumns ๋ณ€๊ฒฝ ์‹œ ํฌ๊ธฐ๋ฅผ ๊ฒฉ์ž์— ๋งž๊ฒŒ ์ž๋™ ์กฐ์ • - if (path === "gridColumns" && prevLayout.gridSettings?.snapToGrid && newComp.type !== "group") { - const currentGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, { - columns: prevLayout.gridSettings.columns, - gap: prevLayout.gridSettings.gap, - padding: prevLayout.gridSettings.padding, - snapToGrid: prevLayout.gridSettings.snapToGrid || false, - }); - - // gridColumns์— ๋งž๋Š” ์ •ํ™•ํ•œ ๋„ˆ๋น„ ๊ณ„์‚ฐ - const newWidth = calculateWidthFromColumns( - newComp.gridColumns, - currentGridInfo, - prevLayout.gridSettings as GridUtilSettings, - ); - newComp.size = { - ...newComp.size, - width: newWidth, - }; - } + // ๐Ÿ—‘๏ธ gridColumns ๋กœ์ง ์ œ๊ฑฐ: 10px ๊ณ ์ • ๊ฒฉ์ž์—์„œ๋Š” ๋ถˆํ•„์š” // ์œ„์น˜ ๋ณ€๊ฒฝ ์‹œ ๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ (๊ทธ๋ฃน ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ ํฌํ•จ) if ( (path === "position.x" || path === "position.y" || path === "position") && layout.gridSettings?.snapToGrid ) { - // ํ˜„์žฌ ํ•ด์ƒ๋„์— ๋งž๋Š” ๊ฒฉ์ž ์ •๋ณด ๊ณ„์‚ฐ - const currentGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, { - columns: layout.gridSettings.columns, - gap: layout.gridSettings.gap, - padding: layout.gridSettings.padding, - snapToGrid: layout.gridSettings.snapToGrid || false, - }); - - // ๊ทธ๋ฃน ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ์ธ ๊ฒฝ์šฐ ํŒจ๋”ฉ์„ ๊ณ ๋ คํ•œ ๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ - if (newComp.parentId && currentGridInfo) { - const { columnWidth } = currentGridInfo; - const { gap } = layout.gridSettings; - - // ๊ทธ๋ฃน ๋‚ด๋ถ€ ํŒจ๋”ฉ ๊ณ ๋ คํ•œ ๊ฒฉ์ž ์ •๋ ฌ - const padding = 16; - const effectiveX = newComp.position.x - padding; - const columnIndex = Math.round(effectiveX / (columnWidth + (gap || 16))); - const snappedX = padding + columnIndex * (columnWidth + (gap || 16)); - - // Y ์ขŒํ‘œ๋Š” 10px ๋‹จ์œ„๋กœ ์Šค๋ƒ… - const effectiveY = newComp.position.y - padding; - const rowIndex = Math.round(effectiveY / 10); - const snappedY = padding + rowIndex * 10; - - // ํฌ๊ธฐ๋„ ์™ธ๋ถ€ ๊ฒฉ์ž์™€ ๋™์ผํ•˜๊ฒŒ ์Šค๋ƒ… - const fullColumnWidth = columnWidth + (gap || 16); // ์™ธ๋ถ€ ๊ฒฉ์ž์™€ ๋™์ผํ•œ ํฌ๊ธฐ - const widthInColumns = Math.max(1, Math.round(newComp.size.width / fullColumnWidth)); - const snappedWidth = widthInColumns * fullColumnWidth - (gap || 16); // gap ์ œ๊ฑฐํ•˜์—ฌ ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ ํฌ๊ธฐ - // ๋†’์ด๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ (์Šค๋ƒ… ์ œ๊ฑฐ) - const snappedHeight = Math.max(10, newComp.size.height); - - newComp.position = { - x: Math.max(padding, snappedX), // ํŒจ๋”ฉ๋งŒํผ ์ตœ์†Œ ์—ฌ๋ฐฑ ํ™•๋ณด - y: Math.max(padding, snappedY), - z: newComp.position.z || 1, - }; - - newComp.size = { - width: snappedWidth, - height: snappedHeight, - }; - } else if (newComp.type !== "group") { - // ๊ทธ๋ฃน์ด ์•„๋‹Œ ์ผ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ๋งŒ ๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ - const snappedPosition = snapToGrid( - newComp.position, - currentGridInfo, - layout.gridSettings as GridUtilSettings, - ); - newComp.position = snappedPosition; - } + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž + const currentGridInfo = calculateGridInfo( + screenResolution.width, + screenResolution.height, + layout.gridSettings, + ); + const snappedPosition = snapToGrid(newComp.position, currentGridInfo, layout.gridSettings); + newComp.position = snappedPosition; } return newComp; @@ -903,20 +820,21 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const { convertLayoutComponents } = await import("@/lib/utils/webTypeConfigConverter"); const convertedComponents = convertLayoutComponents(layoutToUse.components); - // ๊ธฐ๋ณธ ๊ฒฉ์ž ์„ค์ • ๋ณด์žฅ (๊ฒฉ์ž ํ‘œ์‹œ์™€ ์Šค๋ƒ… ๊ธฐ๋ณธ ํ™œ์„ฑํ™”) + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž ์‹œ์Šคํ…œ์œผ๋กœ ์ž๋™ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ + // ์ด์ „ columns, gap, padding ์„ค์ •์„ ์ œ๊ฑฐํ•˜๊ณ  ์ƒˆ ์‹œ์Šคํ…œ์œผ๋กœ ๋ณ€ํ™˜ const layoutWithDefaultGrid = { ...layoutToUse, components: convertedComponents, // ๋ณ€ํ™˜๋œ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ gridSettings: { - columns: layoutToUse.gridSettings?.columns || 12, // DB ๊ฐ’ ์šฐ์„ , ์—†์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’ 12 - gap: layoutToUse.gridSettings?.gap ?? 16, // DB ๊ฐ’ ์šฐ์„ , ์—†์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’ 16 - padding: 0, // padding์€ ํ•ญ์ƒ 0์œผ๋กœ ๊ฐ•์ œ + // ๐Ÿ—‘๏ธ ์ œ๊ฑฐ: columns, gap, padding (๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ) snapToGrid: layoutToUse.gridSettings?.snapToGrid ?? true, // DB ๊ฐ’ ์šฐ์„  showGrid: layoutToUse.gridSettings?.showGrid ?? false, // DB ๊ฐ’ ์šฐ์„  gridColor: layoutToUse.gridSettings?.gridColor || "#d1d5db", gridOpacity: layoutToUse.gridSettings?.gridOpacity ?? 0.5, }, }; + + console.log("โœ… ๊ฒฉ์ž ์„ค์ • ๋กœ๋“œ (10px ๊ณ ์ •):", layoutWithDefaultGrid.gridSettings); // ์ €์žฅ๋œ ํ•ด์ƒ๋„ ์ •๋ณด๊ฐ€ ์žˆ์œผ๋ฉด ์ ์šฉ, ์—†์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ if (layoutToUse.screenResolution) { @@ -1074,51 +992,12 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD }; }, [MIN_ZOOM, MAX_ZOOM]); - // ๊ฒฉ์ž ์„ค์ • ์—…๋ฐ์ดํŠธ ๋ฐ ์ปดํฌ๋„ŒํŠธ ์ž๋™ ์Šค๋ƒ… + // ๊ฒฉ์ž ์„ค์ • ์—…๋ฐ์ดํŠธ (10px ๊ณ ์ • ๊ฒฉ์ž - ์ž๋™ ์Šค๋ƒ… ์ œ๊ฑฐ) const updateGridSettings = useCallback( (newGridSettings: GridSettings) => { + // ๋‹จ์ˆœํžˆ ๊ฒฉ์ž ์„ค์ •๋งŒ ์—…๋ฐ์ดํŠธ (์ปดํฌ๋„ŒํŠธ ์ž๋™ ์ด๋™ ์—†์Œ) const newLayout = { ...layout, gridSettings: newGridSettings }; - - // ๊ฒฉ์ž ์Šค๋ƒ…์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ, ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒˆ๋กœ์šด ๊ฒฉ์ž์— ๋งž๊ฒŒ ์กฐ์ • - if (newGridSettings.snapToGrid && screenResolution.width > 0) { - // ์ƒˆ๋กœ์šด ๊ฒฉ์ž ์„ค์ •์œผ๋กœ ๊ฒฉ์ž ์ •๋ณด ์žฌ๊ณ„์‚ฐ (ํ•ด์ƒ๋„ ๊ธฐ์ค€) - const newGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, { - columns: newGridSettings.columns, - gap: newGridSettings.gap, - padding: newGridSettings.padding, - snapToGrid: newGridSettings.snapToGrid || false, - }); - - const gridUtilSettings = { - columns: newGridSettings.columns, - gap: newGridSettings.gap, - padding: newGridSettings.padding, - snapToGrid: newGridSettings.snapToGrid, - }; - - const adjustedComponents = layout.components.map((comp) => { - const snappedPosition = snapToGrid(comp.position, newGridInfo, gridUtilSettings); - const snappedSize = snapSizeToGrid(comp.size, newGridInfo, gridUtilSettings); - - // gridColumns๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ์ž๋™ ์กฐ์ • - let adjustedGridColumns = comp.gridColumns; - if (!adjustedGridColumns || adjustedGridColumns < 1 || adjustedGridColumns > newGridSettings.columns) { - adjustedGridColumns = adjustGridColumnsFromSize({ size: snappedSize }, newGridInfo, gridUtilSettings); - } - - return { - ...comp, - position: snappedPosition, - size: snappedSize, - gridColumns: adjustedGridColumns, // gridColumns ์†์„ฑ ์ถ”๊ฐ€/์กฐ์ • - }; - }); - - newLayout.components = adjustedComponents; - // console.log("๊ฒฉ์ž ์„ค์ • ๋ณ€๊ฒฝ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ ์œ„์น˜ ๋ฐ ํฌ๊ธฐ ์ž๋™ ์กฐ์ •:", adjustedComponents.length, "๊ฐœ"); - // console.log("์ƒˆ๋กœ์šด ๊ฒฉ์ž ์ •๋ณด:", newGridInfo); - } - + setLayout(newLayout); saveToHistory(newLayout); }, @@ -1215,18 +1094,13 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const snappedPosition = snapToGrid(comp.position, newGridInfo, gridUtilSettings); const snappedSize = snapSizeToGrid(comp.size, newGridInfo, gridUtilSettings); - // gridColumns ์žฌ๊ณ„์‚ฐ - const adjustedGridColumns = adjustGridColumnsFromSize({ size: snappedSize }, newGridInfo, gridUtilSettings); - return { ...comp, position: snappedPosition, size: snappedSize, - gridColumns: adjustedGridColumns, }; }); - console.log("๐Ÿงฒ ๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ ์™„๋ฃŒ"); } const updatedLayout = { @@ -1285,17 +1159,10 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const snappedPosition = snapToGrid(comp.position, currentGridInfo, gridUtilSettings); const snappedSize = snapSizeToGrid(comp.size, currentGridInfo, gridUtilSettings); - // gridColumns๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ์ž๋™ ์กฐ์ • - let adjustedGridColumns = comp.gridColumns; - if (!adjustedGridColumns || adjustedGridColumns < 1 || adjustedGridColumns > layout.gridSettings!.columns) { - adjustedGridColumns = adjustGridColumnsFromSize({ size: snappedSize }, currentGridInfo, gridUtilSettings); - } - return { ...comp, position: snappedPosition, size: snappedSize, - gridColumns: adjustedGridColumns, }; }); @@ -1454,24 +1321,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD : { x: absoluteX, y: absoluteY, z: 1 }; if (templateComp.type === "container") { - // ๊ทธ๋ฆฌ๋“œ ์ปฌ๋Ÿผ ๊ธฐ๋ฐ˜ ํฌ๊ธฐ ๊ณ„์‚ฐ - const gridColumns = - typeof templateComp.size.width === "number" && templateComp.size.width <= 12 ? templateComp.size.width : 4; // ๊ธฐ๋ณธ 4์ปฌ๋Ÿผ - - const calculatedSize = - currentGridInfo && layout.gridSettings?.snapToGrid - ? (() => { - const newWidth = calculateWidthFromColumns( - gridColumns, - currentGridInfo, - layout.gridSettings as GridUtilSettings, - ); - return { - width: newWidth, - height: templateComp.size.height, - }; - })() - : { width: 400, height: templateComp.size.height }; // ํด๋ฐฑ ํฌ๊ธฐ + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž: ๊ธฐ๋ณธ ๋„ˆ๋น„ ์‚ฌ์šฉ + const calculatedSize = { width: 400, height: templateComp.size.height }; return { id: componentId, @@ -1495,21 +1346,11 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD // ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ const gridColumns = 6; // ๊ธฐ๋ณธ๊ฐ’: 6์ปฌ๋Ÿผ (50% ๋„ˆ๋น„) - // gridColumns์— ๋งž๋Š” ํฌ๊ธฐ ๊ณ„์‚ฐ - const calculatedSize = - currentGridInfo && layout.gridSettings?.snapToGrid - ? (() => { - const newWidth = calculateWidthFromColumns( - gridColumns, - currentGridInfo, - layout.gridSettings as GridUtilSettings, - ); - return { - width: newWidth, - height: templateComp.size.height, // ๋†’์ด๋Š” ํ…œํ”Œ๋ฆฟ ๊ฐ’ ์œ ์ง€ - }; - })() - : templateComp.size; + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž: ๊ธฐ๋ณธ ํฌ๊ธฐ ์‚ฌ์šฉ + const calculatedSize = { + width: 800, // ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” ๊ธฐ๋ณธ ๋„ˆ๋น„ + height: templateComp.size.height, + }; console.log("๐Ÿ“Š ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” ์ƒ์„ฑ ์‹œ ํฌ๊ธฐ ๊ณ„์‚ฐ:", { gridColumns, @@ -1574,20 +1415,11 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD // ํŒŒ์ผ ์ฒจ๋ถ€ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ const gridColumns = 6; // ๊ธฐ๋ณธ๊ฐ’: 6์ปฌ๋Ÿผ - const calculatedSize = - currentGridInfo && layout.gridSettings?.snapToGrid - ? (() => { - const newWidth = calculateWidthFromColumns( - gridColumns, - currentGridInfo, - layout.gridSettings as GridUtilSettings, - ); - return { - width: newWidth, - height: templateComp.size.height, - }; - })() - : templateComp.size; + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž + const calculatedSize = { + width: 400, + height: templateComp.size.height, + }; return { id: componentId, @@ -1625,20 +1457,11 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD // ์˜์—ญ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ const gridColumns = 6; // ๊ธฐ๋ณธ๊ฐ’: 6์ปฌ๋Ÿผ (50% ๋„ˆ๋น„) - const calculatedSize = - currentGridInfo && layout.gridSettings?.snapToGrid - ? (() => { - const newWidth = calculateWidthFromColumns( - gridColumns, - currentGridInfo, - layout.gridSettings as GridUtilSettings, - ); - return { - width: newWidth, - height: templateComp.size.height, - }; - })() - : templateComp.size; + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž + const calculatedSize = { + width: 600, // ์˜์—ญ ๊ธฐ๋ณธ ๋„ˆ๋น„ + height: templateComp.size.height, + }; return { id: componentId, @@ -1760,7 +1583,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const widgetSize = currentGridInfo && layout.gridSettings?.snapToGrid ? { - width: calculateWidthFromColumns(1, currentGridInfo, layout.gridSettings as GridUtilSettings), + width: 200, height: templateComp.size.height, } : templateComp.size; @@ -2131,23 +1954,9 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD }); } - // ๊ทธ๋ฆฌ๋“œ ์‹œ์Šคํ…œ์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ gridColumns์— ๋งž์ถฐ ๋„ˆ๋น„ ์žฌ๊ณ„์‚ฐ - if (layout.gridSettings?.snapToGrid && gridInfo) { - // gridColumns์— ๋งž๋Š” ์ •ํ™•ํ•œ ๋„ˆ๋น„ ๊ณ„์‚ฐ - const calculatedWidth = calculateWidthFromColumns( - gridColumns, - gridInfo, - layout.gridSettings as GridUtilSettings, - ); - - // ์ปดํฌ๋„ŒํŠธ๋ณ„ ์ตœ์†Œ ํฌ๊ธฐ ๋ณด์žฅ - const minWidth = isTableList ? 120 : isCardDisplay ? 400 : component.defaultSize.width; - - componentSize = { - ...component.defaultSize, - width: Math.max(calculatedWidth, minWidth), - }; - } + // ๐Ÿ—‘๏ธ 10px ๊ณ ์ • ๊ฒฉ์ž: gridColumns ๋กœ์ง ์ œ๊ฑฐ + // ๊ธฐ๋ณธ ํฌ๊ธฐ๋งŒ ์‚ฌ์šฉ + componentSize = component.defaultSize; console.log("๐ŸŽจ ์ตœ์ข… ์ปดํฌ๋„ŒํŠธ ํฌ๊ธฐ:", { componentId: component.id, @@ -2247,15 +2056,12 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD e.preventDefault(); const dragData = e.dataTransfer.getData("application/json"); - // console.log("๐ŸŽฏ ๋“œ๋กญ ์ด๋ฒคํŠธ:", { dragData }); if (!dragData) { - // console.log("โŒ ๋“œ๋ž˜๊ทธ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค"); return; } try { const parsedData = JSON.parse(dragData); - // console.log("๐Ÿ“‹ ํŒŒ์‹ฑ๋œ ๋ฐ์ดํ„ฐ:", parsedData); // ํ…œํ”Œ๋ฆฟ ๋“œ๋ž˜๊ทธ์ธ ๊ฒฝ์šฐ if (parsedData.type === "template") { @@ -2309,34 +2115,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD }; } else if (type === "column") { // console.log("๐Ÿ”„ ์ปฌ๋Ÿผ ๋“œ๋กญ ์ฒ˜๋ฆฌ:", { webType: column.widgetType, columnName: column.columnName }); - // ํ˜„์žฌ ํ•ด์ƒ๋„์— ๋งž๋Š” ๊ฒฉ์ž ์ •๋ณด๋กœ ๊ธฐ๋ณธ ํฌ๊ธฐ ๊ณ„์‚ฐ - const currentGridInfo = layout.gridSettings - ? calculateGridInfo(screenResolution.width, screenResolution.height, { - columns: layout.gridSettings.columns, - gap: layout.gridSettings.gap, - padding: layout.gridSettings.padding, - snapToGrid: layout.gridSettings.snapToGrid || false, - }) - : null; - - // ๊ฒฉ์ž ์Šค๋ƒ…์ด ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ์ •ํ™•ํ•œ ๊ฒฉ์ž ํฌ๊ธฐ๋กœ ์ƒ์„ฑ, ์•„๋‹ˆ๋ฉด ๊ธฐ๋ณธ๊ฐ’ - const defaultWidth = - currentGridInfo && layout.gridSettings?.snapToGrid - ? calculateWidthFromColumns(1, currentGridInfo, layout.gridSettings as GridUtilSettings) - : 200; - - console.log("๐ŸŽฏ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ ์‹œ ํฌ๊ธฐ ๊ณ„์‚ฐ:", { - screenResolution: `${screenResolution.width}x${screenResolution.height}`, - gridSettings: layout.gridSettings, - currentGridInfo: currentGridInfo - ? { - columnWidth: currentGridInfo.columnWidth.toFixed(2), - totalWidth: currentGridInfo.totalWidth, - } - : null, - defaultWidth: defaultWidth.toFixed(2), - snapToGrid: layout.gridSettings?.snapToGrid, - }); + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž ์‹œ์Šคํ…œ: ๊ฐ„๋‹จํ•œ ๊ธฐ๋ณธ ๋„ˆ๋น„ ์‚ฌ์šฉ + const defaultWidth = 200; // ๊ธฐ๋ณธ ๋„ˆ๋น„ 200px // ์›นํƒ€์ž…๋ณ„ ๊ธฐ๋ณธ ๊ทธ๋ฆฌ๋“œ ์ปฌ๋Ÿผ ์ˆ˜ ๊ณ„์‚ฐ const getDefaultGridColumns = (widgetType: string): number => { @@ -2375,7 +2155,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD }; const defaultColumns = widthMap[widgetType] || 3; // ๊ธฐ๋ณธ๊ฐ’ 3 (1/4, 25%) - console.log("๐ŸŽฏ [ScreenDesigner] getDefaultGridColumns:", { widgetType, defaultColumns }); return defaultColumns; }; @@ -2388,7 +2167,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD file: 240, // ํŒŒ์ผ ์—…๋กœ๋“œ (40 * 6) }; - return heightMap[widgetType] || 40; // ๊ธฐ๋ณธ๊ฐ’ 40 + return heightMap[widgetType] || 30; // ๊ธฐ๋ณธ๊ฐ’ 30px๋กœ ๋ณ€๊ฒฝ }; // ์›นํƒ€์ž…๋ณ„ ๊ธฐ๋ณธ ์„ค์ • ์ƒ์„ฑ @@ -2547,22 +2326,9 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD // ์›นํƒ€์ž…๋ณ„ ์ ์ ˆํ•œ gridColumns ๊ณ„์‚ฐ const calculatedGridColumns = getDefaultGridColumns(column.widgetType); - // gridColumns์— ๋งž๋Š” ์‹ค์ œ ๋„ˆ๋น„ ๊ณ„์‚ฐ - const componentWidth = - currentGridInfo && layout.gridSettings?.snapToGrid - ? calculateWidthFromColumns( - calculatedGridColumns, - currentGridInfo, - layout.gridSettings as GridUtilSettings, - ) - : defaultWidth; + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž: ๊ฐ„๋‹จํ•œ ๋„ˆ๋น„ ๊ณ„์‚ฐ + const componentWidth = defaultWidth; - console.log("๐ŸŽฏ ํผ ์ปจํ…Œ์ด๋„ˆ ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ:", { - widgetType: column.widgetType, - calculatedGridColumns, - componentWidth, - defaultWidth, - }); newComponent = { id: generateComponentId(), @@ -2583,7 +2349,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD codeCategory: column.codeCategory, }), style: { - labelDisplay: false, // ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์˜ ๊ธฐ๋ณธ ๋ผ๋ฒจ ํ‘œ์‹œ๋ฅผ false๋กœ ์„ค์ • + labelDisplay: false, // ๋ผ๋ฒจ ์ˆจ๊น€ (placeholder ์‚ฌ์šฉ) labelFontSize: "12px", labelColor: "#212121", labelFontWeight: "500", @@ -2595,6 +2361,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD webType: column.widgetType, // ์›๋ณธ ์›นํƒ€์ž… ๋ณด์กด inputType: column.inputType, // โœ… input_type ์ถ”๊ฐ€ (category ๋“ฑ) ...getDefaultWebTypeConfig(column.widgetType), + placeholder: column.columnLabel || column.columnName, // placeholder์— ์ปฌ๋Ÿผ ๋ผ๋ฒจ๋ช… ํ‘œ์‹œ // ์ฝ”๋“œ ํƒ€์ž…์ธ ๊ฒฝ์šฐ ์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ ์ •๋ณด ์ถ”๊ฐ€ ...(column.widgetType === "code" && column.codeCategory && { @@ -2613,22 +2380,9 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD // ์›นํƒ€์ž…๋ณ„ ์ ์ ˆํ•œ gridColumns ๊ณ„์‚ฐ const calculatedGridColumns = getDefaultGridColumns(column.widgetType); - // gridColumns์— ๋งž๋Š” ์‹ค์ œ ๋„ˆ๋น„ ๊ณ„์‚ฐ - const componentWidth = - currentGridInfo && layout.gridSettings?.snapToGrid - ? calculateWidthFromColumns( - calculatedGridColumns, - currentGridInfo, - layout.gridSettings as GridUtilSettings, - ) - : defaultWidth; + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž: ๊ฐ„๋‹จํ•œ ๋„ˆ๋น„ ๊ณ„์‚ฐ + const componentWidth = defaultWidth; - console.log("๐ŸŽฏ ์บ”๋ฒ„์Šค ์ปดํฌ๋„ŒํŠธ ์ƒ์„ฑ:", { - widgetType: column.widgetType, - calculatedGridColumns, - componentWidth, - defaultWidth, - }); // ๐Ÿ” ์ด๋ฏธ์ง€ ํƒ€์ž… ๋“œ๋ž˜๊ทธ์•ค๋“œ๋กญ ๋””๋ฒ„๊น… // if (column.widgetType === "image") { @@ -2658,7 +2412,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD codeCategory: column.codeCategory, }), style: { - labelDisplay: true, // ํ…Œ์ด๋ธ” ํŒจ๋„์—์„œ ๋“œ๋ž˜๊ทธํ•œ ์ปดํฌ๋„ŒํŠธ๋Š” ๋ผ๋ฒจ์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ‘œ์‹œ + labelDisplay: false, // ๋ผ๋ฒจ ์ˆจ๊น€ (placeholder ์‚ฌ์šฉ) labelFontSize: "14px", labelColor: "#000000", // ์ˆœ์ˆ˜ํ•œ ๊ฒ€์ • labelFontWeight: "500", @@ -2670,6 +2424,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD webType: column.widgetType, // ์›๋ณธ ์›นํƒ€์ž… ๋ณด์กด inputType: column.inputType, // โœ… input_type ์ถ”๊ฐ€ (category ๋“ฑ) ...getDefaultWebTypeConfig(column.widgetType), + placeholder: column.columnLabel || column.columnName, // placeholder์— ์ปฌ๋Ÿผ ๋ผ๋ฒจ๋ช… ํ‘œ์‹œ // ์ฝ”๋“œ ํƒ€์ž…์ธ ๊ฒฝ์šฐ ์ฝ”๋“œ ์นดํ…Œ๊ณ ๋ฆฌ ์ •๋ณด ์ถ”๊ฐ€ ...(column.widgetType === "code" && column.codeCategory && { @@ -2701,21 +2456,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD newComponent.position = snapToGrid(newComponent.position, currentGridInfo, gridUtilSettings); newComponent.size = snapSizeToGrid(newComponent.size, currentGridInfo, gridUtilSettings); - console.log("๐Ÿงฒ ์ƒˆ ์ปดํฌ๋„ŒํŠธ ๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ:", { - type: newComponent.type, - resolution: `${screenResolution.width}x${screenResolution.height}`, - snappedPosition: newComponent.position, - snappedSize: newComponent.size, - columnWidth: currentGridInfo.columnWidth, - }); - } - - if (newComponent.type === "group") { - console.log("๐Ÿ”“ ๊ทธ๋ฃน ์ปดํฌ๋„ŒํŠธ๋Š” ๊ฒฉ์ž ์Šค๋ƒ… ์ œ์™ธ:", { - type: newComponent.type, - position: newComponent.position, - size: newComponent.size, - }); } const newLayout = { @@ -2889,27 +2629,11 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD componentsToMove = [...componentsToMove, ...additionalComponents]; } - // console.log("๋“œ๋ž˜๊ทธ ์‹œ์ž‘:", component.id, "์ด๋™ํ•  ์ปดํฌ๋„ŒํŠธ ์ˆ˜:", componentsToMove.length); - console.log("๋งˆ์šฐ์Šค ์œ„์น˜ (์คŒ ๋ณด์ •):", { - zoomLevel, - clientX: event.clientX, - clientY: event.clientY, - rectLeft: rect.left, - rectTop: rect.top, - mouseRaw: { x: event.clientX - rect.left, y: event.clientY - rect.top }, - mouseZoomCorrected: { x: relativeMouseX, y: relativeMouseY }, - componentX: component.position.x, - componentY: component.position.y, - grabOffsetX: relativeMouseX - component.position.x, - grabOffsetY: relativeMouseY - component.position.y, - }); - - console.log("๐Ÿš€ ๋“œ๋ž˜๊ทธ ์‹œ์ž‘:", { - componentId: component.id, - componentType: component.type, - initialPosition: { x: component.position.x, y: component.position.y }, - }); - + const finalGrabOffset = { + x: relativeMouseX - component.position.x, + y: relativeMouseY - component.position.y, + }; + setDragState({ isDragging: true, draggedComponent: component, // ์ฃผ ๋“œ๋ž˜๊ทธ ์ปดํฌ๋„ŒํŠธ (๋งˆ์šฐ์Šค ์œ„์น˜ ๊ธฐ์ค€) @@ -2924,10 +2648,7 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD y: component.position.y, z: (component.position as Position).z || 1, }, - grabOffset: { - x: relativeMouseX - component.position.x, - y: relativeMouseY - component.position.y, - }, + grabOffset: finalGrabOffset, justFinishedDrag: false, }); }, @@ -2955,34 +2676,24 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const rawX = relativeMouseX - dragState.grabOffset.x; const rawY = relativeMouseY - dragState.grabOffset.y; + // ๐Ÿ”ฅ ๊ฒฝ๊ณ„ ์ œํ•œ ๋กœ์ง ์ œ๊ฑฐ: ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์„ ๋ฒ—์–ด๋‚˜๋„ ๋˜๊ฒŒ ํ•จ + // ์ด์œ : + // 1. ํฐ ์ปดํฌ๋„ŒํŠธ(884px)๋ฅผ ์ž‘์€ ์˜์—ญ(16px)์—๋งŒ ์ œํ•œํ•˜๋Š” ๊ฒƒ์€ ์‚ฌ์šฉ์„ฑ ๋ฌธ์ œ + // 2. ์‚ฌ์šฉ์ž๊ฐ€ ์ž์œ ๋กญ๊ฒŒ ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ + // 3. ์ตœ์†Œ ์œ„์น˜๋งŒ 0 ์ด์ƒ์œผ๋กœ ์ œํ•œ (์Œ์ˆ˜ ์ขŒํ‘œ ๋ฐฉ์ง€) + const newPosition = { - x: Math.max(0, Math.min(rawX, screenResolution.width - componentWidth)), - y: Math.max(0, Math.min(rawY, screenResolution.height - componentHeight)), + x: Math.max(0, rawX), + y: Math.max(0, rawY), z: (dragState.draggedComponent.position as Position).z || 1, }; // ๋“œ๋ž˜๊ทธ ์ƒํƒœ ์—…๋ฐ์ดํŠธ - console.log("๐Ÿ”ฅ ScreenDesigner updateDragPosition (์คŒ ๋ณด์ •):", { - zoomLevel, - draggedComponentId: dragState.draggedComponent.id, - mouseRaw: { x: event.clientX - rect.left, y: event.clientY - rect.top }, - mouseZoomCorrected: { x: relativeMouseX, y: relativeMouseY }, - oldPosition: dragState.currentPosition, - newPosition: newPosition, - }); - setDragState((prev) => { const newState = { ...prev, currentPosition: { ...newPosition }, // ์ƒˆ๋กœ์šด ๊ฐ์ฒด ์ƒ์„ฑ }; - console.log("๐Ÿ”„ ScreenDesigner dragState ์—…๋ฐ์ดํŠธ:", { - prevPosition: prev.currentPosition, - newPosition: newState.currentPosition, - stateChanged: - prev.currentPosition.x !== newState.currentPosition.x || - prev.currentPosition.y !== newState.currentPosition.y, - }); return newState; }); @@ -3000,15 +2711,8 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD const draggedComponent = layout.components.find((c) => c.id === dragState.draggedComponent); let finalPosition = dragState.currentPosition; - // ํ˜„์žฌ ํ•ด์ƒ๋„์— ๋งž๋Š” ๊ฒฉ์ž ์ •๋ณด ๊ณ„์‚ฐ - const currentGridInfo = layout.gridSettings - ? calculateGridInfo(screenResolution.width, screenResolution.height, { - columns: layout.gridSettings.columns, - gap: layout.gridSettings.gap, - padding: layout.gridSettings.padding, - snapToGrid: layout.gridSettings.snapToGrid || false, - }) - : null; + // ๐Ÿ”ฅ 10px ๊ณ ์ • ๊ฒฉ์ž ์‹œ์Šคํ…œ: calculateGridInfo๋Š” columns, gap, padding์„ ๋ฌด์‹œํ•จ + const currentGridInfo = calculateGridInfo(screenResolution.width, screenResolution.height, layout.gridSettings); // ์ผ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ ๋ฐ ํ”Œ๋กœ์šฐ ๋ฒ„ํŠผ ๊ทธ๋ฃน์— ๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ (์ผ๋ฐ˜ ๊ทธ๋ฃน ์ œ์™ธ) if (draggedComponent?.type !== "group" && layout.gridSettings?.snapToGrid && currentGridInfo) { @@ -3019,21 +2723,9 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD z: dragState.currentPosition.z ?? 1, }, currentGridInfo, - { - columns: layout.gridSettings.columns, - gap: layout.gridSettings.gap, - padding: layout.gridSettings.padding, - snapToGrid: layout.gridSettings.snapToGrid || false, - }, + layout.gridSettings, ); - console.log("๐ŸŽฏ ๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ๋จ:", { - componentType: draggedComponent?.type, - resolution: `${screenResolution.width}x${screenResolution.height}`, - originalPosition: dragState.currentPosition, - snappedPosition: finalPosition, - columnWidth: currentGridInfo.columnWidth, - }); } // ์Šค๋ƒ…์œผ๋กœ ์ธํ•œ ์ถ”๊ฐ€ ์ด๋™ ๊ฑฐ๋ฆฌ ๊ณ„์‚ฐ @@ -3098,28 +2790,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD height: snappedHeight, }; - console.log("๐ŸŽฏ ๋“œ๋ž˜๊ทธ ์ข…๋ฃŒ ์‹œ ๊ทธ๋ฃน ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ ๊ฒฉ์ž ์Šค๋ƒ… (ํŒจ๋”ฉ ๊ณ ๋ ค):", { - componentId: comp.id, - parentId: comp.parentId, - beforeSnap: { - x: originalComponent.position.x + totalDeltaX, - y: originalComponent.position.y + totalDeltaY, - }, - calculation: { - effectiveX, - effectiveY, - columnIndex, - rowIndex, - columnWidth, - fullColumnWidth, - widthInColumns, - gap: gap || 16, - padding, - }, - afterSnap: newPosition, - afterSizeSnap: newSize, - }); - return { ...comp, position: newPosition as Position, @@ -3142,11 +2812,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD if (selectedComponent && dragState.draggedComponents.some((c) => c.id === selectedComponent.id)) { const updatedSelectedComponent = updatedComponents.find((c) => c.id === selectedComponent.id); if (updatedSelectedComponent) { - console.log("๐Ÿ”„ ScreenDesigner: ์„ ํƒ๋œ ์ปดํฌ๋„ŒํŠธ ์œ„์น˜ ์—…๋ฐ์ดํŠธ", { - componentId: selectedComponent.id, - oldPosition: selectedComponent.position, - newPosition: updatedSelectedComponent.position, - }); setSelectedComponent(updatedSelectedComponent); } } diff --git a/frontend/components/screen/panels/GridPanel.tsx b/frontend/components/screen/panels/GridPanel.tsx index f33cc601..34d324f8 100644 --- a/frontend/components/screen/panels/GridPanel.tsx +++ b/frontend/components/screen/panels/GridPanel.tsx @@ -1,335 +1,79 @@ -"use client"; - import React from "react"; import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; -import { Button } from "@/components/ui/button"; -import { Separator } from "@/components/ui/separator"; -import { Slider } from "@/components/ui/slider"; -import { Grid3X3, RotateCcw, Eye, EyeOff, Zap, RefreshCw } from "lucide-react"; -import { GridSettings, ScreenResolution } from "@/types/screen"; -import { calculateGridInfo } from "@/lib/utils/gridUtils"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Grid3X3 } from "lucide-react"; +import { GridSettings } from "@/types/screen-management"; interface GridPanelProps { gridSettings: GridSettings; onGridSettingsChange: (settings: GridSettings) => void; - onResetGrid: () => void; - onForceGridUpdate?: () => void; // ๊ฐ•์ œ ๊ฒฉ์ž ์žฌ์กฐ์ • ์ถ”๊ฐ€ - screenResolution?: ScreenResolution; // ํ•ด์ƒ๋„ ์ •๋ณด ์ถ”๊ฐ€ } -export const GridPanel: React.FC = ({ - gridSettings, - onGridSettingsChange, - onResetGrid, - onForceGridUpdate, - screenResolution, -}) => { - const updateSetting = (key: keyof GridSettings, value: any) => { +/** + * ๊ฒฉ์ž ์„ค์ • ํŒจ๋„ (10px ๊ณ ์ • ๊ฒฉ์ž) + * + * ์‚ฌ์šฉ์ž ์„ค์ •: + * - ๊ฒฉ์ž ํ‘œ์‹œ ON/OFF + * - ๊ฒฉ์ž ์Šค๋ƒ… ON/OFF + * + * ์ž๋™ ์„ค์ • (๋ณ€๊ฒฝ ๋ถˆ๊ฐ€): + * - ๊ฒฉ์ž ํฌ๊ธฐ: 10px ๊ณ ์ • + * - ๊ฒฉ์ž ๊ฐ„๊ฒฉ: 10px ๊ณ ์ • + */ +export function GridPanel({ gridSettings, onGridSettingsChange }: GridPanelProps) { + const updateSetting = (key: K, value: GridSettings[K]) => { onGridSettingsChange({ ...gridSettings, [key]: value, }); }; - // ์ตœ๋Œ€ ์ปฌ๋Ÿผ ์ˆ˜ ๊ณ„์‚ฐ (์ตœ์†Œ ์ปฌ๋Ÿผ ๋„ˆ๋น„ 30px ๊ธฐ์ค€) - const MIN_COLUMN_WIDTH = 30; - const maxColumns = screenResolution - ? Math.floor((screenResolution.width - gridSettings.padding * 2 + gridSettings.gap) / (MIN_COLUMN_WIDTH + gridSettings.gap)) - : 24; - const safeMaxColumns = Math.max(1, Math.min(maxColumns, 100)); // ์ตœ๋Œ€ 100๊ฐœ๋กœ ์ œํ•œ - - // ์‹ค์ œ ๊ฒฉ์ž ์ •๋ณด ๊ณ„์‚ฐ - const actualGridInfo = screenResolution - ? calculateGridInfo(screenResolution.width, screenResolution.height, { - columns: gridSettings.columns, - gap: gridSettings.gap, - padding: gridSettings.padding, - snapToGrid: gridSettings.snapToGrid || false, - }) - : null; - - // ์‹ค์ œ ํ‘œ์‹œ๋˜๋Š” ์ปฌ๋Ÿผ ์ˆ˜ ๊ณ„์‚ฐ (ํ•ญ์ƒ ์„ค์ •๋œ ๊ฐœ์ˆ˜๋ฅผ ํ‘œ์‹œํ•˜๋˜, ๋„ˆ๋น„๊ฐ€ ๋„ˆ๋ฌด ์ž‘์œผ๋ฉด ๊ฒฝ๊ณ ) - const actualColumns = gridSettings.columns; - - // ์ปฌ๋Ÿผ์ด ๋„ˆ๋ฌด ์ž‘์€์ง€ ํ™•์ธ - const isColumnsTooSmall = - screenResolution && actualGridInfo - ? actualGridInfo.columnWidth < MIN_COLUMN_WIDTH - : false; - return ( -
- {/* ํ—ค๋” */} -
-
-
- -

๊ฒฉ์ž ์„ค์ •

-
- -
- {onForceGridUpdate && ( - - )} - - -
+ + +
+ + ๊ฒฉ์ž ์„ค์ • +
+
+ + {/* ๊ฒฉ์ž ํ‘œ์‹œ */} +
+ + updateSetting("showGrid", checked as boolean)} + />
- {/* ์ฃผ์š” ํ† ๊ธ€๋“ค */} -
-
-
- {gridSettings.showGrid ? ( - - ) : ( - - )} - -
- updateSetting("showGrid", checked)} - /> -
- -
-
- - -
- updateSetting("snapToGrid", checked)} - /> -
-
-
- - {/* ์„ค์ • ์˜์—ญ */} -
- {/* ๊ฒฉ์ž ๊ตฌ์กฐ */} -
-

๊ฒฉ์ž ๊ตฌ์กฐ

- -
- -
- { - const value = parseInt(e.target.value, 10); - if (!isNaN(value) && value >= 1 && value <= safeMaxColumns) { - updateSetting("columns", value); - } - }} - className="h-8 text-xs" - /> - / {safeMaxColumns} -
- updateSetting("columns", value)} - className="w-full" - /> -
- 1์—ด - {safeMaxColumns}์—ด -
- {isColumnsTooSmall && ( -

- โš ๏ธ ์ปฌ๋Ÿผ ๋„ˆ๋น„๊ฐ€ ๋„ˆ๋ฌด ์ž‘์Šต๋‹ˆ๋‹ค (์ตœ์†Œ {MIN_COLUMN_WIDTH}px ๊ถŒ์žฅ) -

- )} -
- -
- - updateSetting("gap", value)} - className="w-full" - /> -
- 0px - 40px -
-
- -
- - updateSetting("padding", value)} - className="w-full" - /> -
- 0px - 60px -
-
+ {/* ๊ฒฉ์ž ์Šค๋ƒ… */} +
+ + updateSetting("snapToGrid", checked as boolean)} + />
- - - {/* ๊ฒฉ์ž ์Šคํƒ€์ผ */} -
-

๊ฒฉ์ž ์Šคํƒ€์ผ

- -
- -
- updateSetting("gridColor", e.target.value)} - className="h-8 w-12 rounded border p-1" - /> - updateSetting("gridColor", e.target.value)} - placeholder="#d1d5db" - className="flex-1" - /> -
-
- -
- - updateSetting("gridOpacity", value)} - className="w-full" - /> -
- 10% - 100% -
-
+ {/* ๊ฒฉ์ž ์ •๋ณด (์ฝ๊ธฐ ์ „์šฉ) */} +
+

๐Ÿ”ง ๊ฒฉ์ž ์‹œ์Šคํ…œ

+
    +
  • โ€ข ๊ฒฉ์ž ํฌ๊ธฐ: 10px ๊ณ ์ •
  • +
  • โ€ข ์ปดํฌ๋„ŒํŠธ๋Š” 10px ๋‹จ์œ„๋กœ ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค
  • +
  • โ€ข ๊ฒฉ์ž ์Šค๋ƒ…์„ ๋„๋ฉด ์ž์œ ๋กญ๊ฒŒ ๋ฐฐ์น˜ ๊ฐ€๋Šฅ
  • +
- - - - {/* ๋ฏธ๋ฆฌ๋ณด๊ธฐ */} -
-

๋ฏธ๋ฆฌ๋ณด๊ธฐ

- -
-
- ์ปดํฌ๋„ŒํŠธ ์˜ˆ์‹œ -
-
-
-
- - {/* ํ‘ธํ„ฐ */} -
-
๐Ÿ’ก ๊ฒฉ์ž ์„ค์ •์€ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์บ”๋ฒ„์Šค์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค
- - {/* ํ•ด์ƒ๋„ ๋ฐ ๊ฒฉ์ž ์ •๋ณด */} - {screenResolution && actualGridInfo && ( - <> - -
-

๊ฒฉ์ž ์ •๋ณด

- -
-
- ํ•ด์ƒ๋„: - - {screenResolution.width} ร— {screenResolution.height} - -
- -
- ์ปฌ๋Ÿผ ๋„ˆ๋น„: - - {actualGridInfo.columnWidth.toFixed(1)}px - {isColumnsTooSmall && " (๋„ˆ๋ฌด ์ž‘์Œ)"} - -
- -
- ์‚ฌ์šฉ ๊ฐ€๋Šฅ ๋„ˆ๋น„: - - {(screenResolution.width - gridSettings.padding * 2).toLocaleString()}px - -
- - {isColumnsTooSmall && ( -
- ๐Ÿ’ก ์ปฌ๋Ÿผ์ด ๋„ˆ๋ฌด ์ž‘์Šต๋‹ˆ๋‹ค. ์ปฌ๋Ÿผ ์ˆ˜๋ฅผ ์ค„์ด๊ฑฐ๋‚˜ ๊ฐ„๊ฒฉ์„ ์ค„์—ฌ๋ณด์„ธ์š”. -
- )} -
-
- - )} -
-
+ + ); -}; - -export default GridPanel; +} diff --git a/frontend/components/screen/panels/TablesPanel.tsx b/frontend/components/screen/panels/TablesPanel.tsx index abeff8d6..46bf55f8 100644 --- a/frontend/components/screen/panels/TablesPanel.tsx +++ b/frontend/components/screen/panels/TablesPanel.tsx @@ -53,12 +53,16 @@ export const TablesPanel: React.FC = ({ onDragStart, placedColumns = new Set(), }) => { - // ์ด๋ฏธ ๋ฐฐ์น˜๋œ ์ปฌ๋Ÿผ์„ ์ œ์™ธํ•œ ํ…Œ์ด๋ธ” ์ •๋ณด ์ƒ์„ฑ + // ์ˆจ๊ธธ ๊ธฐ๋ณธ ์ปฌ๋Ÿผ ๋ชฉ๋ก (id, created_date, updated_date, writer, company_code) + const hiddenColumns = new Set(['id', 'created_date', 'updated_date', 'writer', 'company_code']); + + // ์ด๋ฏธ ๋ฐฐ์น˜๋œ ์ปฌ๋Ÿผ + ๊ธฐ๋ณธ ์ปฌ๋Ÿผ์„ ์ œ์™ธํ•œ ํ…Œ์ด๋ธ” ์ •๋ณด ์ƒ์„ฑ const tablesWithAvailableColumns = tables.map((table) => ({ ...table, columns: table.columns.filter((col) => { const columnKey = `${table.tableName}.${col.columnName}`; - return !placedColumns.has(columnKey); + // ๊ธฐ๋ณธ ์ปฌ๋Ÿผ ๋˜๋Š” ์ด๋ฏธ ๋ฐฐ์น˜๋œ ์ปฌ๋Ÿผ์€ ์ œ์™ธ + return !hiddenColumns.has(col.columnName) && !placedColumns.has(columnKey); }), })); diff --git a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx b/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx index 6d063640..c37f0a85 100644 --- a/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx +++ b/frontend/components/screen/panels/UnifiedPropertiesPanel.tsx @@ -125,6 +125,23 @@ export const UnifiedPropertiesPanel: React.FC = ({ } }, [selectedComponent?.size?.height, selectedComponent?.id]); + // ๐Ÿ”ฅ ํ›…์€ ํ•ญ์ƒ ์ตœ์ƒ๋‹จ์— (early return ์ด์ „) + // ํฌ๊ธฐ ์ž…๋ ฅ ํ•„๋“œ์šฉ ๋กœ์ปฌ ์ƒํƒœ + const [localSize, setLocalSize] = useState({ + width: selectedComponent?.size?.width || 100, + height: selectedComponent?.size?.height || 40, + }); + + // ์„ ํƒ๋œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋กœ์ปฌ ์ƒํƒœ ๋™๊ธฐํ™” + useEffect(() => { + if (selectedComponent) { + setLocalSize({ + width: selectedComponent.size?.width || 100, + height: selectedComponent.size?.height || 40, + }); + } + }, [selectedComponent?.id, selectedComponent?.size?.width, selectedComponent?.size?.height]); + // ๊ฒฉ์ž ์„ค์ • ์—…๋ฐ์ดํŠธ ํ•จ์ˆ˜ (early return ์ด์ „์— ์ •์˜) const updateGridSetting = (key: string, value: any) => { if (onGridSettingsChange && gridSettings) { @@ -135,17 +152,10 @@ export const UnifiedPropertiesPanel: React.FC = ({ } }; - // ๊ฒฉ์ž ์„ค์ • ๋ Œ๋”๋ง (early return ์ด์ „์— ์ •์˜) + // ๊ฒฉ์ž ์„ค์ • ๋ Œ๋”๋ง (10px ๊ณ ์ • ๊ฒฉ์ž) const renderGridSettings = () => { if (!gridSettings || !onGridSettingsChange) return null; - // ์ตœ๋Œ€ ์ปฌ๋Ÿผ ์ˆ˜ ๊ณ„์‚ฐ - const MIN_COLUMN_WIDTH = 30; - const maxColumns = currentResolution - ? Math.floor((currentResolution.width - gridSettings.padding * 2 + gridSettings.gap) / (MIN_COLUMN_WIDTH + gridSettings.gap)) - : 24; - const safeMaxColumns = Math.max(1, Math.min(maxColumns, 100)); // ์ตœ๋Œ€ 100๊ฐœ๋กœ ์ œํ•œ - return (
@@ -154,7 +164,7 @@ export const UnifiedPropertiesPanel: React.FC = ({
- {/* ํ† ๊ธ€๋“ค */} + {/* ๊ฒฉ์ž ํ‘œ์‹œ */}
{gridSettings.showGrid ? ( @@ -168,11 +178,12 @@ export const UnifiedPropertiesPanel: React.FC = ({
updateGridSetting("showGrid", checked)} />
+ {/* ๊ฒฉ์ž ์Šค๋ƒ… */}
@@ -187,65 +198,14 @@ export const UnifiedPropertiesPanel: React.FC = ({ />
- {/* ์ปฌ๋Ÿผ ์ˆ˜ */} -
- -
- { - const value = parseInt(e.target.value, 10); - if (!isNaN(value) && value >= 1 && value <= safeMaxColumns) { - updateGridSetting("columns", value); - } - }} - className="h-6 px-2 py-0 text-xs" - style={{ fontSize: "12px" }} - placeholder={`1~${safeMaxColumns}`} - /> -
-

- ์ตœ๋Œ€ {safeMaxColumns}๊ฐœ๊นŒ์ง€ ์„ค์ • ๊ฐ€๋Šฅ (์ตœ์†Œ ์ปฌ๋Ÿผ ๋„ˆ๋น„ {MIN_COLUMN_WIDTH}px) -

-
- - {/* ๊ฐ„๊ฒฉ */} -
- - updateGridSetting("gap", value)} - className="w-full" - /> -
- - {/* ์—ฌ๋ฐฑ */} -
- - updateGridSetting("padding", value)} - className="w-full" - /> + {/* ๊ฒฉ์ž ์ •๋ณด (์ฝ๊ธฐ ์ „์šฉ) */} +
+

๐Ÿ”ง ๊ฒฉ์ž ์‹œ์Šคํ…œ

+
    +
  • โ€ข ๊ฒฉ์ž ํฌ๊ธฐ: 10px ๊ณ ์ •
  • +
  • โ€ข ์ปดํฌ๋„ŒํŠธ๋Š” 10px ๋‹จ์œ„๋กœ ๋ฐฐ์น˜๋ฉ๋‹ˆ๋‹ค
  • +
  • โ€ข ๊ฒฉ์ž ์Šค๋ƒ…์„ ๋„๋ฉด ์ž์œ ๋กญ๊ฒŒ ๋ฐฐ์น˜ ๊ฐ€๋Šฅ
  • +
@@ -455,47 +415,90 @@ export const UnifiedPropertiesPanel: React.FC = ({
)} - {/* Grid Columns + Z-Index (๊ฐ™์€ ํ–‰) */} + {/* Z-Index */} +
+ + handleUpdate("position.z", parseInt(e.target.value) || 1)} + className="h-6 w-full px-2 py-0 text-xs" + style={{ fontSize: "12px" }} + /> +
+ + {/* ํฌ๊ธฐ (๋„ˆ๋น„/๋†’์ด) */}
- {(selectedComponent as any).gridColumns !== undefined && ( -
- -
- { - const value = parseInt(e.target.value, 10); - const maxColumns = gridSettings?.columns || 12; - if (!isNaN(value) && value >= 1 && value <= maxColumns) { - handleUpdate("gridColumns", value); - - // width๋ฅผ ํผ์„ผํŠธ๋กœ ๊ณ„์‚ฐํ•˜์—ฌ ์—…๋ฐ์ดํŠธ - const widthPercent = (value / maxColumns) * 100; - handleUpdate("style.width", `${widthPercent}%`); - } - }} - className="h-6 w-full px-2 py-0 text-xs" - style={{ fontSize: "12px" }} - /> - - /{gridSettings?.columns || 12} - -
-
- )}
- + handleUpdate("position.z", parseInt(e.target.value) || 1)} + value={localSize.width} + onChange={(e) => { + // ์ž…๋ ฅ ์ค‘์—๋Š” ๋กœ์ปฌ ์ƒํƒœ๋งŒ ์—…๋ฐ์ดํŠธ + const value = e.target.value === "" ? "" : parseInt(e.target.value); + setLocalSize((prev) => ({ ...prev, width: value as number })); + }} + onBlur={(e) => { + // ํฌ์ปค์Šค ์•„์›ƒ ์‹œ ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ ์—…๋ฐ์ดํŠธ + const rawValue = e.target.value; + const parsedValue = parseInt(rawValue); + const newWidth = Math.max(10, parsedValue || 10); + + // ๋กœ์ปฌ ์ƒํƒœ๋„ ์ตœ์ข…๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ + setLocalSize((prev) => ({ ...prev, width: newWidth })); + + // size.width ๊ฒฝ๋กœ๋กœ ์—…๋ฐ์ดํŠธ (๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ๋จ) + handleUpdate("size.width", newWidth); + }} + onKeyDown={(e) => { + // Enter ํ‚ค๋กœ๋„ ์ฆ‰์‹œ ์ ์šฉ + if (e.key === "Enter") { + const newWidth = Math.max(10, parseInt((e.target as HTMLInputElement).value) || 10); + setLocalSize((prev) => ({ ...prev, width: newWidth })); + handleUpdate("size.width", newWidth); + (e.target as HTMLInputElement).blur(); + } + }} className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }} + /> +
+
+ + { + // ์ž…๋ ฅ ์ค‘์—๋Š” ๋กœ์ปฌ ์ƒํƒœ๋งŒ ์—…๋ฐ์ดํŠธ + const value = e.target.value === "" ? "" : parseInt(e.target.value); + setLocalSize((prev) => ({ ...prev, height: value as number })); + }} + onBlur={(e) => { + // ํฌ์ปค์Šค ์•„์›ƒ ์‹œ ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ ์—…๋ฐ์ดํŠธ + const rawValue = e.target.value; + const parsedValue = parseInt(rawValue); + const newHeight = Math.max(10, parsedValue || 10); + + // ๋กœ์ปฌ ์ƒํƒœ๋„ ์ตœ์ข…๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธ + setLocalSize((prev) => ({ ...prev, height: newHeight })); + + // size.height ๊ฒฝ๋กœ๋กœ ์—…๋ฐ์ดํŠธ (๊ฒฉ์ž ์Šค๋ƒ… ์ ์šฉ๋จ) + handleUpdate("size.height", newHeight); + }} + onKeyDown={(e) => { + // Enter ํ‚ค๋กœ๋„ ์ฆ‰์‹œ ์ ์šฉ + if (e.key === "Enter") { + const newHeight = Math.max(10, parseInt((e.target as HTMLInputElement).value) || 10); + setLocalSize((prev) => ({ ...prev, height: newHeight })); + handleUpdate("size.height", newHeight); + (e.target as HTMLInputElement).blur(); + } + }} + className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }} />
diff --git a/frontend/lib/utils/gridUtils.ts b/frontend/lib/utils/gridUtils.ts index 7ea3f6b4..d9f8316f 100644 --- a/frontend/lib/utils/gridUtils.ts +++ b/frontend/lib/utils/gridUtils.ts @@ -1,205 +1,77 @@ import { Position, Size } from "@/types/screen"; import { GridSettings } from "@/types/screen-management"; +// ๐ŸŽฏ 10px ๊ณ ์ • ๊ฒฉ์ž ์‹œ์Šคํ…œ +const GRID_SIZE = 10; // ๊ณ ์ •๊ฐ’ + export interface GridInfo { - columnWidth: number; + gridSize: number; // ํ•ญ์ƒ 10px totalWidth: number; totalHeight: number; } /** - * ๊ฒฉ์ž ์ •๋ณด ๊ณ„์‚ฐ + * ๊ฒฉ์ž ์ •๋ณด ๊ณ„์‚ฐ (๋‹จ์ˆœํ™”) */ export function calculateGridInfo( containerWidth: number, containerHeight: number, - gridSettings: GridSettings, + _gridSettings?: GridSettings, // ํ˜ธํ™˜์„ฑ ์œ ์ง€์šฉ (์‚ฌ์šฉ ์•ˆ ํ•จ) ): GridInfo { - const { gap, padding } = gridSettings; - let { columns } = gridSettings; - - // ๐Ÿ”ฅ ์ตœ์†Œ ์ปฌ๋Ÿผ ๋„ˆ๋น„๋ฅผ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ตœ๋Œ€ ์ปฌ๋Ÿผ ์ˆ˜ ๊ณ„์‚ฐ - const MIN_COLUMN_WIDTH = 30; // ์ตœ์†Œ ์ปฌ๋Ÿผ ๋„ˆ๋น„ 30px - const availableWidth = containerWidth - padding * 2; - const maxPossibleColumns = Math.floor((availableWidth + gap) / (MIN_COLUMN_WIDTH + gap)); - - // ์„ค์ •๋œ ์ปฌ๋Ÿผ ์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์œผ๋ฉด ์ž๋™์œผ๋กœ ์ œํ•œ - if (columns > maxPossibleColumns) { - console.warn( - `โš ๏ธ ๊ฒฉ์ž ์ปฌ๋Ÿผ ์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ๋งŽ์Šต๋‹ˆ๋‹ค. ${columns}๊ฐœ โ†’ ${maxPossibleColumns}๊ฐœ๋กœ ์ž๋™ ์กฐ์ •๋จ (์ตœ์†Œ ์ปฌ๋Ÿผ ๋„ˆ๋น„: ${MIN_COLUMN_WIDTH}px)`, - ); - columns = Math.max(1, maxPossibleColumns); - } - - // ๊ฒฉ์ž ๊ฐ„๊ฒฉ์„ ๊ณ ๋ คํ•œ ์ปฌ๋Ÿผ ๋„ˆ๋น„ ๊ณ„์‚ฐ - const totalGaps = (columns - 1) * gap; - const columnWidth = (availableWidth - totalGaps) / columns; - return { - columnWidth: Math.max(columnWidth, MIN_COLUMN_WIDTH), + gridSize: GRID_SIZE, totalWidth: containerWidth, totalHeight: containerHeight, }; } /** - * ์œ„์น˜๋ฅผ ๊ฒฉ์ž์— ๋งž์ถค + * ์œ„์น˜๋ฅผ 10px ๊ฒฉ์ž์— ๋งž์ถค */ -export function snapToGrid(position: Position, gridInfo: GridInfo, gridSettings: GridSettings): Position { +export function snapToGrid(position: Position, _gridInfo: GridInfo, gridSettings: GridSettings): Position { if (!gridSettings.snapToGrid) { return position; } - const { columnWidth } = gridInfo; - const { gap, padding } = gridSettings; - - // ๊ฒฉ์ž ์…€ ํฌ๊ธฐ (์ปฌ๋Ÿผ ๋„ˆ๋น„ + ๊ฐ„๊ฒฉ์„ ํ•˜๋‚˜์˜ ๊ฒฉ์ž ๋‹จ์œ„๋กœ ๊ณ„์‚ฐ) - const cellWidth = columnWidth + gap; - const cellHeight = 10; // ํ–‰ ๋†’์ด 10px ๋‹จ์œ„๋กœ ๊ณ ์ • - - // ํŒจ๋”ฉ์„ ์ œ์™ธํ•œ ์ƒ๋Œ€ ์œ„์น˜ - const relativeX = position.x - padding; - const relativeY = position.y - padding; - - // ๊ฒฉ์ž ๊ธฐ์ค€์œผ๋กœ ์œ„์น˜ ๊ณ„์‚ฐ (๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ฒฉ์ž์ ์œผ๋กœ ์Šค๋ƒ…) - const gridX = Math.round(relativeX / cellWidth); - const gridY = Math.round(relativeY / cellHeight); - - // ์‹ค์ œ ํ”ฝ์…€ ์œ„์น˜๋กœ ๋ณ€ํ™˜ - const snappedX = Math.max(padding, padding + gridX * cellWidth); - const snappedY = Math.max(padding, padding + gridY * cellHeight); - return { - x: snappedX, - y: snappedY, + x: Math.round(position.x / GRID_SIZE) * GRID_SIZE, + y: Math.round(position.y / GRID_SIZE) * GRID_SIZE, z: position.z, }; } /** - * ํฌ๊ธฐ๋ฅผ ๊ฒฉ์ž์— ๋งž์ถค + * ํฌ๊ธฐ๋ฅผ 10px ๊ฒฉ์ž์— ๋งž์ถค */ -export function snapSizeToGrid(size: Size, gridInfo: GridInfo, gridSettings: GridSettings): Size { +export function snapSizeToGrid(size: Size, _gridInfo: GridInfo, gridSettings: GridSettings): Size { if (!gridSettings.snapToGrid) { return size; } - const { columnWidth } = gridInfo; - const { gap } = gridSettings; - - // ๊ฒฉ์ž ๋‹จ์œ„๋กœ ๋„ˆ๋น„ ๊ณ„์‚ฐ - // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฐจ์ง€ํ•˜๋Š” ์ปฌ๋Ÿผ ์ˆ˜๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ณ„์‚ฐ - let gridColumns = 1; - - // ํ˜„์žฌ ๋„ˆ๋น„์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ฒฉ์ž ์ปฌ๋Ÿผ ์ˆ˜ ์ฐพ๊ธฐ - for (let cols = 1; cols <= gridSettings.columns; cols++) { - const targetWidth = cols * columnWidth + (cols - 1) * gap; - if (size.width <= targetWidth + (columnWidth + gap) / 2) { - gridColumns = cols; - break; - } - gridColumns = cols; - } - - const snappedWidth = gridColumns * columnWidth + (gridColumns - 1) * gap; - - // ๋†’์ด๋Š” 10px ๋‹จ์œ„๋กœ ์Šค๋ƒ… - const rowHeight = 10; - const snappedHeight = Math.max(10, Math.round(size.height / rowHeight) * rowHeight); - - console.log( - `๐Ÿ“ ํฌ๊ธฐ ์Šค๋ƒ…: ${size.width}px โ†’ ${snappedWidth}px (${gridColumns}์ปฌ๋Ÿผ, ์ปฌ๋Ÿผ๋„ˆ๋น„:${columnWidth}px, ๊ฐ„๊ฒฉ:${gap}px)`, - ); - return { - width: Math.max(columnWidth, snappedWidth), - height: snappedHeight, + width: Math.max(GRID_SIZE, Math.round(size.width / GRID_SIZE) * GRID_SIZE), + height: Math.max(GRID_SIZE, Math.round(size.height / GRID_SIZE) * GRID_SIZE), }; } /** - * ๊ฒฉ์ž ์ปฌ๋Ÿผ ์ˆ˜๋กœ ๋„ˆ๋น„ ๊ณ„์‚ฐ - */ -export function calculateWidthFromColumns(columns: number, gridInfo: GridInfo, gridSettings: GridSettings): number { - const { columnWidth } = gridInfo; - const { gap } = gridSettings; - - return columns * columnWidth + (columns - 1) * gap; -} - -/** - * gridColumns ์†์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ปดํฌ๋„ŒํŠธ ํฌ๊ธฐ ์—…๋ฐ์ดํŠธ - */ -export function updateSizeFromGridColumns( - component: { gridColumns?: number; size: Size }, - gridInfo: GridInfo, - gridSettings: GridSettings, -): Size { - if (!component.gridColumns || component.gridColumns < 1) { - return component.size; - } - - const newWidth = calculateWidthFromColumns(component.gridColumns, gridInfo, gridSettings); - - return { - width: newWidth, - height: component.size.height, // ๋†’์ด๋Š” ์œ ์ง€ - }; -} - -/** - * ์ปดํฌ๋„ŒํŠธ์˜ gridColumns๋ฅผ ์ž๋™์œผ๋กœ ํฌ๊ธฐ์— ๋งž๊ฒŒ ์กฐ์ • - */ -export function adjustGridColumnsFromSize( - component: { size: Size }, - gridInfo: GridInfo, - gridSettings: GridSettings, -): number { - const columns = calculateColumnsFromWidth(component.size.width, gridInfo, gridSettings); - return Math.min(Math.max(1, columns), gridSettings.columns); // 1-12 ๋ฒ”์œ„๋กœ ์ œํ•œ -} - -/** - * ๋„ˆ๋น„์—์„œ ๊ฒฉ์ž ์ปฌ๋Ÿผ ์ˆ˜ ๊ณ„์‚ฐ - */ -export function calculateColumnsFromWidth(width: number, gridInfo: GridInfo, gridSettings: GridSettings): number { - const { columnWidth } = gridInfo; - const { gap } = gridSettings; - - return Math.max(1, Math.round((width + gap) / (columnWidth + gap))); -} - -/** - * ๊ฒฉ์ž ๊ฐ€์ด๋“œ๋ผ์ธ ์ƒ์„ฑ + * ๊ฒฉ์ž ๊ฐ€์ด๋“œ๋ผ์ธ ์ƒ์„ฑ (10px ๊ฐ„๊ฒฉ) */ export function generateGridLines( containerWidth: number, containerHeight: number, - gridSettings: GridSettings, + _gridSettings?: GridSettings, ): { verticalLines: number[]; horizontalLines: number[]; } { - const { columns, gap, padding } = gridSettings; - const gridInfo = calculateGridInfo(containerWidth, containerHeight, gridSettings); - const { columnWidth } = gridInfo; - - // ๊ฒฉ์ž ์…€ ํฌ๊ธฐ (์Šค๋ƒ… ๋กœ์ง๊ณผ ๋™์ผํ•˜๊ฒŒ) - const cellWidth = columnWidth + gap; - const cellHeight = 10; // ํ–‰ ๋†’์ด 10px ๋‹จ์œ„๋กœ ๊ณ ์ • - - // ์„ธ๋กœ ๊ฒฉ์ž์„  const verticalLines: number[] = []; - for (let i = 0; i <= columns; i++) { - const x = padding + i * cellWidth; - if (x <= containerWidth) { - verticalLines.push(x); - } + for (let x = 0; x <= containerWidth; x += GRID_SIZE) { + verticalLines.push(x); } - // ๊ฐ€๋กœ ๊ฒฉ์ž์„  const horizontalLines: number[] = []; - for (let y = padding; y < containerHeight; y += cellHeight) { + for (let y = 0; y <= containerHeight; y += GRID_SIZE) { horizontalLines.push(y); } @@ -242,46 +114,21 @@ export function alignGroupChildrenToGrid( ): any[] { if (!gridSettings.snapToGrid || children.length === 0) return children; - console.log("๐Ÿ”ง alignGroupChildrenToGrid ์‹œ์ž‘:", { - childrenCount: children.length, - groupPosition, - gridInfo, - gridSettings, - }); - - return children.map((child, index) => { - console.log(`๐Ÿ“ ์ž์‹ ${index + 1} ์ฒ˜๋ฆฌ ์ค‘:`, { - childId: child.id, - originalPosition: child.position, - originalSize: child.size, - }); - - const { columnWidth } = gridInfo; - const { gap } = gridSettings; - - // ๊ทธ๋ฃน ๋‚ด๋ถ€ ํŒจ๋”ฉ ๊ณ ๋ คํ•œ ๊ฒฉ์ž ์ •๋ ฌ + return children.map((child) => { const padding = 16; - const effectiveX = child.position.x - padding; - const columnIndex = Math.round(effectiveX / (columnWidth + gap)); - const snappedX = padding + columnIndex * (columnWidth + gap); + + // 10px ๋‹จ์œ„๋กœ ์Šค๋ƒ… + const snappedX = Math.max(padding, Math.round((child.position.x - padding) / GRID_SIZE) * GRID_SIZE + padding); + const snappedY = Math.max(padding, Math.round((child.position.y - padding) / GRID_SIZE) * GRID_SIZE + padding); + + const snappedWidth = Math.max(GRID_SIZE, Math.round(child.size.width / GRID_SIZE) * GRID_SIZE); + const snappedHeight = Math.max(GRID_SIZE, Math.round(child.size.height / GRID_SIZE) * GRID_SIZE); - // Y ์ขŒํ‘œ๋Š” 10px ๋‹จ์œ„๋กœ ์Šค๋ƒ… - const rowHeight = 10; - const effectiveY = child.position.y - padding; - const rowIndex = Math.round(effectiveY / rowHeight); - const snappedY = padding + rowIndex * rowHeight; - - // ํฌ๊ธฐ๋Š” ์™ธ๋ถ€ ๊ฒฉ์ž์™€ ๋™์ผํ•˜๊ฒŒ ์Šค๋ƒ… (columnWidth + gap ์‚ฌ์šฉ) - const fullColumnWidth = columnWidth + gap; // ์™ธ๋ถ€ ๊ฒฉ์ž์™€ ๋™์ผํ•œ ํฌ๊ธฐ - const widthInColumns = Math.max(1, Math.round(child.size.width / fullColumnWidth)); - const snappedWidth = widthInColumns * fullColumnWidth - gap; // gap ์ œ๊ฑฐํ•˜์—ฌ ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ ํฌ๊ธฐ - const snappedHeight = Math.max(10, Math.round(child.size.height / rowHeight) * rowHeight); - - const snappedChild = { + return { ...child, position: { - x: Math.max(padding, snappedX), // ํŒจ๋”ฉ๋งŒํผ ์ตœ์†Œ ์—ฌ๋ฐฑ ํ™•๋ณด - y: Math.max(padding, snappedY), + x: snappedX, + y: snappedY, z: child.position.z || 1, }, size: { @@ -289,26 +136,6 @@ export function alignGroupChildrenToGrid( height: snappedHeight, }, }; - - console.log(`โœ… ์ž์‹ ${index + 1} ๊ฒฉ์ž ์ •๋ ฌ ์™„๋ฃŒ:`, { - childId: child.id, - calculation: { - effectiveX, - effectiveY, - columnIndex, - rowIndex, - widthInColumns, - originalX: child.position.x, - snappedX: snappedChild.position.x, - padding, - }, - snappedPosition: snappedChild.position, - snappedSize: snappedChild.size, - deltaX: snappedChild.position.x - child.position.x, - deltaY: snappedChild.position.y - child.position.y, - }); - - return snappedChild; }); } @@ -317,19 +144,13 @@ export function alignGroupChildrenToGrid( */ export function calculateOptimalGroupSize( children: Array<{ position: Position; size: Size }>, - gridInfo: GridInfo, - gridSettings: GridSettings, + _gridInfo?: GridInfo, + _gridSettings?: GridSettings, ): Size { if (children.length === 0) { - return { width: gridInfo.columnWidth * 2, height: 10 * 4 }; + return { width: GRID_SIZE * 20, height: GRID_SIZE * 10 }; } - console.log("๐Ÿ“ calculateOptimalGroupSize ์‹œ์ž‘:", { - childrenCount: children.length, - children: children.map((c) => ({ pos: c.position, size: c.size })), - }); - - // ๋ชจ๋“  ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จํ•˜๋Š” ์ตœ์†Œ ๊ฒฝ๊ณ„ ๊ณ„์‚ฐ const bounds = children.reduce( (acc, child) => ({ minX: Math.min(acc.minX, child.position.x), @@ -340,61 +161,38 @@ export function calculateOptimalGroupSize( { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity }, ); - console.log("๐Ÿ“ ๊ฒฝ๊ณ„ ๊ณ„์‚ฐ:", bounds); - const contentWidth = bounds.maxX - bounds.minX; const contentHeight = bounds.maxY - bounds.minY; + const padding = 16; - // ๊ทธ๋ฃน์€ ๊ฒฉ์ž ์Šค๋ƒ… ์—†์ด ์ปจํ…์ธ ์— ๋งž๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ํฌ๊ธฐ - const padding = 16; // ๊ทธ๋ฃน ๋‚ด๋ถ€ ์—ฌ๋ฐฑ - const groupSize = { + return { width: contentWidth + padding * 2, height: contentHeight + padding * 2, }; - - console.log("โœ… ์ž์—ฐ์Šค๋Ÿฌ์šด ๊ทธ๋ฃน ํฌ๊ธฐ:", { - contentSize: { width: contentWidth, height: contentHeight }, - withPadding: groupSize, - strategy: "๊ทธ๋ฃน์€ ๊ฒฉ์ž ์Šค๋ƒ… ์—†์ด, ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ๋งŒ ๊ฒฉ์ž์— ๋งž์ถค", - }); - - return groupSize; } /** * ๊ทธ๋ฃน ๋‚ด ์ƒ๋Œ€ ์ขŒํ‘œ๋ฅผ ๊ฒฉ์ž ๊ธฐ์ค€์œผ๋กœ ์ •๊ทœํ™” */ -export function normalizeGroupChildPositions(children: any[], gridSettings: GridSettings): any[] { - if (!gridSettings.snapToGrid || children.length === 0) return children; +export function normalizeGroupChildPositions(children: any[], _gridSettings?: GridSettings): any[] { + if (children.length === 0) return children; - console.log("๐Ÿ”„ normalizeGroupChildPositions ์‹œ์ž‘:", { - childrenCount: children.length, - originalPositions: children.map((c) => ({ id: c.id, pos: c.position })), - }); - - // ๋ชจ๋“  ์ž์‹์˜ ์ตœ์†Œ ์œ„์น˜ ์ฐพ๊ธฐ const minX = Math.min(...children.map((child) => child.position.x)); const minY = Math.min(...children.map((child) => child.position.y)); - - console.log("๐Ÿ“ ์ตœ์†Œ ์œ„์น˜:", { minX, minY }); - - // ๊ทธ๋ฃน ๋‚ด์—์„œ ์‹œ์ž‘์ ์„ ํŒจ๋”ฉ๋งŒํผ ๋–จ์–ด๋œจ๋ฆผ (์ž์—ฐ์Šค๋Ÿฌ์šด ์—ฌ๋ฐฑ) const padding = 16; - const startX = padding; - const startY = padding; - const normalizedChildren = children.map((child) => ({ + return children.map((child) => ({ ...child, position: { - x: child.position.x - minX + startX, - y: child.position.y - minY + startY, + x: child.position.x - minX + padding, + y: child.position.y - minY + padding, z: child.position.z || 1, }, })); - - console.log("โœ… ์ •๊ทœํ™” ์™„๋ฃŒ:", { - normalizedPositions: normalizedChildren.map((c) => ({ id: c.id, pos: c.position })), - }); - - return normalizedChildren; } + +// ๐Ÿ—‘๏ธ ์ œ๊ฑฐ๋œ ํ•จ์ˆ˜๋“ค (๋” ์ด์ƒ ํ•„์š” ์—†์Œ) +// - calculateWidthFromColumns +// - updateSizeFromGridColumns +// - adjustGridColumnsFromSize +// - calculateColumnsFromWidth diff --git a/frontend/types/screen-management.ts b/frontend/types/screen-management.ts index d83a6354..75c5d4d2 100644 --- a/frontend/types/screen-management.ts +++ b/frontend/types/screen-management.ts @@ -561,21 +561,22 @@ export interface LayoutData { } /** - * ๊ฒฉ์ž ์„ค์ • + * ๊ฒฉ์ž ์„ค์ • (10px ๊ณ ์ • ๊ฒฉ์ž) */ export interface GridSettings { - enabled: boolean; - size: number; - color: string; - opacity: number; - snapToGrid: boolean; - // gridUtils์—์„œ ํ•„์š”ํ•œ ์†์„ฑ๋“ค ์ถ”๊ฐ€ - columns: number; - gap: number; - padding: number; - showGrid?: boolean; - gridColor?: string; - gridOpacity?: number; + snapToGrid: boolean; // ๊ฒฉ์ž ์Šค๋ƒ… ON/OFF + showGrid?: boolean; // ๊ฒฉ์ž ํ‘œ์‹œ ์—ฌ๋ถ€ + gridColor?: string; // ๊ฒฉ์ž ์„  ์ƒ‰์ƒ + gridOpacity?: number; // ๊ฒฉ์ž ์„  ํˆฌ๋ช…๋„ + + // ๐Ÿ—‘๏ธ ์ œ๊ฑฐ๋œ ์†์„ฑ๋“ค (10px ๊ณ ์ •์œผ๋กœ ๋” ์ด์ƒ ํ•„์š” ์—†์Œ) + // - columns: ์ž๋™ ๊ณ„์‚ฐ (ํ•ด์ƒ๋„ รท 10px) + // - gap: 10px ๊ณ ์ • + // - padding: 0px ๊ณ ์ • + // - size: 10px ๊ณ ์ • + // - enabled: showGrid๋กœ ๋Œ€์ฒด + // - color: gridColor๋กœ ๋Œ€์ฒด + // - opacity: gridOpacity๋กœ ๋Œ€์ฒด } /**