131 lines
4.6 KiB
TypeScript
131 lines
4.6 KiB
TypeScript
/**
|
|
* 분할 패널 컴포넌트
|
|
* 좌측과 우측에 화면을 임베드합니다.
|
|
*
|
|
* 데이터 전달은 좌측 화면에 배치된 버튼의 transferData 액션으로 처리됩니다.
|
|
* 예: 좌측 화면에 TableListComponent + Button(transferData 액션) 배치
|
|
*/
|
|
|
|
"use client";
|
|
|
|
import React, { useState, useCallback } from "react";
|
|
import { EmbeddedScreen } from "./EmbeddedScreen";
|
|
import { Columns2 } from "lucide-react";
|
|
|
|
interface ScreenSplitPanelProps {
|
|
screenId?: number;
|
|
config?: any; // 설정 패널에서 오는 config (leftScreenId, rightScreenId, splitRatio, resizable)
|
|
}
|
|
|
|
/**
|
|
* 분할 패널 컴포넌트
|
|
* 순수하게 화면 분할 기능만 제공합니다.
|
|
*/
|
|
export function ScreenSplitPanel({ screenId, config }: ScreenSplitPanelProps) {
|
|
const [splitRatio, setSplitRatio] = useState(config?.splitRatio || 50);
|
|
|
|
// 설정 패널에서 오는 간단한 config를 임베딩 설정으로 변환
|
|
const leftEmbedding = config?.leftScreenId
|
|
? {
|
|
id: 1,
|
|
parentScreenId: screenId || 0,
|
|
childScreenId: config.leftScreenId,
|
|
position: "left" as const,
|
|
mode: "view" as const, // 기본 view 모드 (select는 테이블 자체 설정)
|
|
config: {},
|
|
companyCode: "*",
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
}
|
|
: null;
|
|
|
|
const rightEmbedding = config?.rightScreenId
|
|
? {
|
|
id: 2,
|
|
parentScreenId: screenId || 0,
|
|
childScreenId: config.rightScreenId,
|
|
position: "right" as const,
|
|
mode: "view" as const, // 기본 view 모드
|
|
config: {},
|
|
companyCode: "*",
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
}
|
|
: null;
|
|
|
|
/**
|
|
* 리사이저 드래그 핸들러
|
|
*/
|
|
const handleResize = useCallback((newRatio: number) => {
|
|
setSplitRatio(Math.max(20, Math.min(80, newRatio)));
|
|
}, []);
|
|
|
|
// config가 없거나 화면 설정이 안 된 경우 (디자이너 모드)
|
|
if (!config || !leftEmbedding || !rightEmbedding) {
|
|
return (
|
|
<div className="border-muted-foreground/25 flex h-full items-center justify-center rounded-lg border-2 border-dashed">
|
|
<div className="space-y-4 p-6 text-center">
|
|
<div className="flex items-center justify-center gap-3">
|
|
<div className="bg-muted flex h-16 w-16 items-center justify-center rounded-lg">
|
|
<Columns2 className="text-muted-foreground h-8 w-8" />
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p className="text-muted-foreground mb-2 text-base font-semibold">화면 분할 패널</p>
|
|
<p className="text-muted-foreground/60 mb-1 text-xs">좌우로 화면을 나눕니다</p>
|
|
<p className="text-muted-foreground/60 text-xs">
|
|
우측 속성 패널 → 상세 설정에서 좌측/우측 화면을 선택하세요
|
|
</p>
|
|
<p className="text-muted-foreground/60 mt-2 text-[10px]">
|
|
💡 데이터 전달: 좌측 화면에 버튼 배치 후 transferData 액션 설정
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex h-full">
|
|
{/* 좌측 패널 */}
|
|
<div style={{ width: `${splitRatio}%` }} className="flex-1 overflow-hidden border-r">
|
|
<EmbeddedScreen embedding={leftEmbedding} />
|
|
</div>
|
|
|
|
{/* 리사이저 */}
|
|
{config?.resizable !== false && (
|
|
<div
|
|
className="group bg-border hover:bg-primary/20 relative w-1 cursor-col-resize transition-colors"
|
|
onMouseDown={(e) => {
|
|
e.preventDefault();
|
|
const startX = e.clientX;
|
|
const startRatio = splitRatio;
|
|
const containerWidth = e.currentTarget.parentElement!.offsetWidth;
|
|
|
|
const handleMouseMove = (moveEvent: MouseEvent) => {
|
|
const deltaX = moveEvent.clientX - startX;
|
|
const deltaRatio = (deltaX / containerWidth) * 100;
|
|
handleResize(startRatio + deltaRatio);
|
|
};
|
|
|
|
const handleMouseUp = () => {
|
|
document.removeEventListener("mousemove", handleMouseMove);
|
|
document.removeEventListener("mouseup", handleMouseUp);
|
|
};
|
|
|
|
document.addEventListener("mousemove", handleMouseMove);
|
|
document.addEventListener("mouseup", handleMouseUp);
|
|
}}
|
|
>
|
|
<div className="bg-primary absolute inset-y-0 left-1/2 w-1 -translate-x-1/2 opacity-0 transition-opacity group-hover:opacity-100" />
|
|
</div>
|
|
)}
|
|
|
|
{/* 우측 패널 */}
|
|
<div style={{ width: `${100 - splitRatio}%` }} className="flex-1 overflow-hidden">
|
|
<EmbeddedScreen embedding={rightEmbedding} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|