"use client"; import React, { useState, useCallback } from "react"; import { BaseLayoutRenderer, LayoutRendererProps } from "./BaseLayoutRenderer"; export default class SplitLayoutRenderer extends BaseLayoutRenderer { render(): React.ReactElement { const { layout, isDesignMode, isSelected, onClick, className } = this.props; if (!layout.layoutConfig.split) { return
분할 레이아웃 설정이 없습니다.
; } return ( ); } } interface SplitLayoutComponentProps { layout: any; isDesignMode?: boolean; isSelected?: boolean; onClick?: (e: React.MouseEvent) => void; className?: string; renderer: SplitLayoutRenderer; } const SplitLayoutComponent: React.FC = ({ layout, isDesignMode, isSelected, onClick, className, renderer, }) => { const splitConfig = layout.layoutConfig.split; const [sizes, setSizes] = useState(splitConfig.ratio || [50, 50]); const [isDragging, setIsDragging] = useState(false); const containerStyle = renderer.getLayoutContainerStyle(); // 분할 컨테이너 스타일 const splitStyle: React.CSSProperties = { ...containerStyle, display: "flex", flexDirection: splitConfig.direction === "horizontal" ? "row" : "column", overflow: "hidden", }; // 디자인 모드 스타일 if (isDesignMode) { splitStyle.border = isSelected ? "2px solid #3b82f6" : "1px solid #e2e8f0"; splitStyle.borderRadius = "8px"; } // 스플리터 드래그 핸들러 const handleSplitterDrag = useCallback( (e: React.MouseEvent, index: number) => { if (!splitConfig.resizable || !isDesignMode) return; setIsDragging(true); const startPos = splitConfig.direction === "horizontal" ? e.clientX : e.clientY; const startSizes = [...sizes]; const handleMouseMove = (moveEvent: MouseEvent) => { const currentPos = splitConfig.direction === "horizontal" ? moveEvent.clientX : moveEvent.clientY; const delta = currentPos - startPos; const containerSize = splitConfig.direction === "horizontal" ? (e.currentTarget as HTMLElement).parentElement!.clientWidth : (e.currentTarget as HTMLElement).parentElement!.clientHeight; const deltaPercent = (delta / containerSize) * 100; const newSizes = [...startSizes]; newSizes[index] = Math.max(splitConfig.minSize?.[index] || 10, Math.min(90, startSizes[index] + deltaPercent)); newSizes[index + 1] = Math.max( splitConfig.minSize?.[index + 1] || 10, Math.min(90, startSizes[index + 1] - deltaPercent), ); setSizes(newSizes); }; const handleMouseUp = () => { setIsDragging(false); document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); }, [splitConfig, sizes, isDesignMode], ); return (
!isDragging && renderer.props.onDragStart?.(e)} onDragEnd={(e) => !isDragging && renderer.props.onDragEnd?.(e)} > {layout.zones.map((zone: any, index: number) => { const zoneChildren = renderer.getZoneChildren(zone.id); const isHorizontal = splitConfig.direction === "horizontal"; // 패널 크기 계산 const panelSize = sizes[index] || 100 / layout.zones.length; const panelStyle: React.CSSProperties = { [isHorizontal ? "width" : "height"]: `${panelSize}%`, [isHorizontal ? "height" : "width"]: "100%", overflow: "auto", }; return ( {/* 패널 */} {renderer.renderZone(zone, zoneChildren, { style: panelStyle, className: "split-panel", })} {/* 스플리터 (마지막 패널 제외) */} {index < layout.zones.length - 1 && (
handleSplitterDrag(e, index)} > {/* 스플리터 핸들 */}
)} ); })} {/* 디자인 모드에서 존이 없을 때 안내 메시지 */} {isDesignMode && layout.zones.length === 0 && (
분할 레이아웃에 존을 추가하세요
)}
); }; // React 컴포넌트로 래핑 export const SplitLayout: React.FC = (props) => { const renderer = new SplitLayoutRenderer(props); return renderer.render(); };