"use client"; /** * UnifiedLayout * * 통합 레이아웃 컴포넌트 * - grid: 그리드 레이아웃 * - split: 분할 레이아웃 * - flex: 플렉스 레이아웃 * - divider: 구분선 * - screen-embed: 화면 임베딩 */ import React, { forwardRef, useCallback, useRef, useState } from "react"; import { cn } from "@/lib/utils"; import { UnifiedLayoutProps } from "@/types/unified-components"; import { GripVertical, GripHorizontal } from "lucide-react"; /** * 그리드 레이아웃 컴포넌트 (12컬럼 시스템) * * 사용법: * - columns: 컬럼 수 (기본 12, 전통적 그리드) * - colSpan: 자식 요소별 span 지정 시 사용 * - Tailwind의 grid-cols-12 기반 */ const GridLayout = forwardRef(({ columns = 12, gap = "16px", children, className, use12Column = true }, ref) => { // 12컬럼 그리드 클래스 매핑 const gridColsClass: Record = { 1: "grid-cols-1", 2: "grid-cols-2", 3: "grid-cols-3", 4: "grid-cols-4", 5: "grid-cols-5", 6: "grid-cols-6", 7: "grid-cols-7", 8: "grid-cols-8", 9: "grid-cols-9", 10: "grid-cols-10", 11: "grid-cols-11", 12: "grid-cols-12", }; // 12컬럼 시스템 사용 시 if (use12Column) { return (
{children}
); } // 기존 방식 (동적 컬럼 수) return (
{children}
); }); GridLayout.displayName = "GridLayout"; /** * 분할 레이아웃 컴포넌트 (리사이즈 가능) */ const SplitLayout = forwardRef(({ direction = "horizontal", splitRatio = [50, 50], gap = "8px", children, className }, ref) => { const containerRef = useRef(null); const [ratio, setRatio] = useState(splitRatio); const [isDragging, setIsDragging] = useState(false); const childArray = React.Children.toArray(children); const isHorizontal = direction === "horizontal"; // 리사이저 드래그 시작 const handleMouseDown = useCallback((e: React.MouseEvent) => { e.preventDefault(); setIsDragging(true); const startPos = isHorizontal ? e.clientX : e.clientY; const startRatio = [...ratio]; const container = containerRef.current; const handleMouseMove = (moveEvent: MouseEvent) => { if (!container) return; const containerSize = isHorizontal ? container.offsetWidth : container.offsetHeight; const currentPos = isHorizontal ? moveEvent.clientX : moveEvent.clientY; const delta = currentPos - startPos; const deltaPercent = (delta / containerSize) * 100; const newFirst = Math.max(10, Math.min(90, startRatio[0] + deltaPercent)); const newSecond = 100 - newFirst; setRatio([newFirst, newSecond]); }; const handleMouseUp = () => { setIsDragging(false); document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); }, [isHorizontal, ratio]); return (
{ (containerRef as React.MutableRefObject).current = node; if (typeof ref === "function") ref(node); else if (ref) ref.current = node; }} className={cn( "flex", isHorizontal ? "flex-row" : "flex-col", className )} style={{ gap }} > {/* 첫 번째 패널 */}
{childArray[0]}
{/* 리사이저 */}
{isHorizontal ? ( ) : ( )}
{/* 두 번째 패널 */}
{childArray[1]}
); }); SplitLayout.displayName = "SplitLayout"; /** * 플렉스 레이아웃 컴포넌트 */ const FlexLayout = forwardRef(({ direction = "horizontal", gap = "16px", wrap = false, justify = "start", align = "stretch", children, className }, ref) => { const justifyMap = { start: "flex-start", center: "center", end: "flex-end", between: "space-between", around: "space-around", }; const alignMap = { start: "flex-start", center: "center", end: "flex-end", stretch: "stretch", }; return (
{children}
); }); FlexLayout.displayName = "FlexLayout"; /** * 구분선 컴포넌트 */ const DividerLayout = forwardRef(({ direction = "horizontal", className }, ref) => { return (
); }); DividerLayout.displayName = "DividerLayout"; /** * 화면 임베딩 컴포넌트 */ const ScreenEmbedLayout = forwardRef(({ screenId, className }, ref) => { if (!screenId) { return (
화면을 선택하세요
); } // TODO: 실제 화면 임베딩 로직 구현 // InteractiveScreenViewer와 연동 필요 return (
임베딩된 화면 (ID: {screenId})
{/* 여기에 InteractiveScreenViewer 렌더링 */} 화면 내용이 여기에 표시됩니다
); }); ScreenEmbedLayout.displayName = "ScreenEmbedLayout"; /** * 메인 UnifiedLayout 컴포넌트 */ export const UnifiedLayout = forwardRef( (props, ref) => { const { id, style, size, config: configProp, children, } = props; // config가 없으면 기본값 사용 const config = configProp || { type: "grid" as const, columns: 2 }; // 타입별 레이아웃 렌더링 const renderLayout = () => { const layoutType = config.type || "grid"; switch (layoutType) { case "grid": return ( {children} ); case "split": return ( {children} ); case "flex": return ( {children} ); case "divider": return ( ); case "screen-embed": return ( ); default: return ( {children} ); } }; const componentWidth = size?.width || style?.width; const componentHeight = size?.height || style?.height; return (
{renderLayout()}
); } ); UnifiedLayout.displayName = "UnifiedLayout"; export default UnifiedLayout;