diff --git a/frontend/app/(main)/screens/[screenId]/page.tsx b/frontend/app/(main)/screens/[screenId]/page.tsx index f91831d5..7c15afff 100644 --- a/frontend/app/(main)/screens/[screenId]/page.tsx +++ b/frontend/app/(main)/screens/[screenId]/page.tsx @@ -10,8 +10,7 @@ import { useRouter } from "next/navigation"; import { toast } from "sonner"; import { initializeComponents } from "@/lib/registry/components"; import { EditModal } from "@/components/screen/EditModal"; -import { ResponsiveLayoutEngine } from "@/components/screen/ResponsiveLayoutEngine"; -import { useBreakpoint } from "@/hooks/useBreakpoint"; +import { RealtimePreview } from "@/components/screen/RealtimePreviewDynamic"; export default function ScreenViewPage() { const params = useParams(); @@ -22,13 +21,9 @@ export default function ScreenViewPage() { const [layout, setLayout] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [formData, setFormData] = useState>({}); - // 화면 너비에 따라 Y좌표 유지 여부 결정 - const [preserveYPosition, setPreserveYPosition] = useState(true); - - const breakpoint = useBreakpoint(); - // 편집 모달 상태 const [editModalOpen, setEditModalOpen] = useState(false); const [editModalConfig, setEditModalConfig] = useState<{ @@ -124,24 +119,6 @@ export default function ScreenViewPage() { } }, [screenId]); - // 윈도우 크기 변경 감지 - layout이 로드된 후에만 실행 - useEffect(() => { - if (!layout) return; - - const screenWidth = layout?.screenResolution?.width || 1200; - - const handleResize = () => { - const shouldPreserve = window.innerWidth >= screenWidth - 100; - setPreserveYPosition(shouldPreserve); - }; - - window.addEventListener("resize", handleResize); - // 초기 값도 설정 - handleResize(); - - return () => window.removeEventListener("resize", handleResize); - }, [layout]); - if (loading) { return (
@@ -172,39 +149,70 @@ export default function ScreenViewPage() { // 화면 해상도 정보가 있으면 해당 크기로, 없으면 기본 크기 사용 const screenWidth = layout?.screenResolution?.width || 1200; + const screenHeight = layout?.screenResolution?.height || 800; return ( -
-
- {/* 항상 반응형 모드로 렌더링 */} - {layout && layout.components.length > 0 ? ( - { - console.log("📝 page.tsx formData 업데이트:", fieldName, value); - setFormData((prev) => ({ ...prev, [fieldName]: value })); - }} - screenInfo={{ id: screenId, tableName: screen?.tableName }} - /> - ) : ( - // 빈 화면일 때 -
-
-
- 📄 -
-

화면이 비어있습니다

-

이 화면에는 아직 설계된 컴포넌트가 없습니다.

+
+ {/* 절대 위치 기반 렌더링 */} + {layout && layout.components.length > 0 ? ( +
+ {/* 최상위 컴포넌트들 렌더링 */} + {layout.components + .filter((component) => !component.parentId) + .map((component) => ( + {}} + > + {/* 자식 컴포넌트들 */} + {(component.type === "group" || component.type === "container" || component.type === "area") && + layout.components + .filter((child) => child.parentId === component.id) + .map((child) => { + // 자식 컴포넌트의 위치를 부모 기준 상대 좌표로 조정 + const relativeChildComponent = { + ...child, + position: { + x: child.position.x - component.position.x, + y: child.position.y - component.position.y, + z: child.position.z || 1, + }, + }; + + return ( + {}} + /> + ); + })} + + ))} +
+ ) : ( + // 빈 화면일 때 +
+
+
+ 📄
+

화면이 비어있습니다

+

이 화면에는 아직 설계된 컴포넌트가 없습니다.

- )} -
+
+ )} {/* 편집 모달 */} void; onUndo: () => void; onRedo: () => void; - onPreview: () => void; onTogglePanel: (panelId: string) => void; panelStates: Record; canUndo: boolean; @@ -45,7 +43,6 @@ export const DesignerToolbar: React.FC = ({ onSave, onUndo, onRedo, - onPreview, onTogglePanel, panelStates, canUndo, @@ -229,11 +226,6 @@ export const DesignerToolbar: React.FC = ({
- -
-
- + { + onTableDragStart={(e, table, column) => { const dragData = { type: column ? "column" : "table", table, @@ -3925,30 +3951,20 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD e.dataTransfer.setData("application/json", JSON.stringify(dragData)); }} selectedTableName={selectedScreen.tableName} + placedColumns={placedColumns} />
)} - {panelStates.components?.isOpen && ( -
-
-

컴포넌트

- -
-
- -
-
- )} - {panelStates.properties?.isOpen && ( -
-
-

속성

-
@@ -3962,85 +3978,49 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD currentTable={tables.length > 0 ? tables[0] : undefined} currentTableName={selectedScreen?.tableName} dragState={dragState} + onStyleChange={(style) => { + if (selectedComponent) { + updateComponentProperty(selectedComponent.id, "style", style); + } + }} + currentResolution={screenResolution} + onResolutionChange={handleResolutionChange} />
)} - {panelStates.styles?.isOpen && ( -
-
-

스타일

- -
-
- {selectedComponent ? ( - { - if (selectedComponent) { - updateComponentProperty(selectedComponent.id, "style", style); - } - }} - /> - ) : ( -
- 컴포넌트를 선택하여 스타일을 편집하세요 -
- )} -
-
- )} - - {panelStates.resolution?.isOpen && ( -
-
-

해상도

- -
-
- -
-
- )} + {/* 스타일과 해상도 패널은 속성 패널의 탭으로 통합됨 */} {/* 메인 캔버스 영역 (스크롤 가능한 컨테이너) - 좌우 최소화, 위아래 넉넉한 여유 */} -
+
{/* Pan 모드 안내 - 제거됨 */} {/* 줌 레벨 표시 */} -
+
🔍 {Math.round(zoomLevel * 100)}%
{/* 🔥 줌 적용 시 스크롤 영역 확보를 위한 래퍼 */}
{/* 실제 작업 캔버스 (해상도 크기) - 반응형 개선 + 줌 적용 */}
{ if (e.target === e.currentTarget && !selectionDrag.wasSelecting && !isPanMode) { setSelectedComponent(null); @@ -4067,14 +4047,13 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD {gridLines.map((line, index) => (
))} @@ -4286,15 +4265,12 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD {/* 드래그 선택 영역 */} {selectionDrag.isSelecting && (
)} @@ -4302,19 +4278,28 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD {/* 빈 캔버스 안내 */} {layout.components.length === 0 && (
-
- -

캔버스가 비어있습니다

-

좌측 패널에서 테이블/컬럼이나 템플릿을 드래그하여 화면을 설계하세요

-

- 단축키: T(테이블), M(템플릿), P(속성), S(스타일), R(격자), D(상세설정), E(해상도) -

-

- 편집: Ctrl+C(복사), Ctrl+V(붙여넣기), Ctrl+S(저장), Ctrl+Z(실행취소), Delete(삭제) -

-

- ⚠️ 브라우저 기본 단축키가 차단되어 애플리케이션 기능만 작동합니다 +

+
+ +
+

캔버스가 비어있습니다

+

+ 좌측 패널에서 테이블/컬럼이나 템플릿을 드래그하여 화면을 설계하세요

+
+

+ 단축키: T(테이블), M(템플릿), P(속성), S(스타일), + R(격자), D(상세설정), E(해상도) +

+

+ 편집: Ctrl+C(복사), Ctrl+V(붙여넣기), Ctrl+S(저장), + Ctrl+Z(실행취소), Delete(삭제) +

+

+ ⚠️ + 브라우저 기본 단축키가 차단되어 애플리케이션 기능만 작동합니다 +

+
)} @@ -4352,13 +4337,6 @@ export default function ScreenDesigner({ selectedScreen, onBackToList }: ScreenD screenId={selectedScreen.screenId} /> )} - {/* 반응형 미리보기 모달 */} - setShowResponsivePreview(false)} - components={layout.components} - screenWidth={screenResolution.width} - />
); } diff --git a/frontend/components/screen/StyleEditor.tsx b/frontend/components/screen/StyleEditor.tsx index 706ee8a1..ecd405d0 100644 --- a/frontend/components/screen/StyleEditor.tsx +++ b/frontend/components/screen/StyleEditor.tsx @@ -255,45 +255,45 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
-
-
-