"use client"; import React from "react"; import { cn } from "@/lib/utils"; import { PopComponentDefinitionV4, PopSizeConstraintV4, PopContainerV4, PopComponentType, } from "../types/pop-layout"; import { Settings, Database, Link2, MoveHorizontal, MoveVertical, Square, Maximize2, AlignCenter, Eye, } from "lucide-react"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; // ======================================== // Props 정의 // ======================================== interface ComponentEditorPanelV4Props { /** 선택된 컴포넌트 */ component: PopComponentDefinitionV4 | null; /** 선택된 컨테이너 */ container: PopContainerV4 | null; /** 현재 뷰포트 모드 */ currentViewportMode?: "mobile_portrait" | "mobile_landscape" | "tablet_portrait" | "tablet_landscape"; /** 컴포넌트 업데이트 */ onUpdateComponent?: (updates: Partial) => void; /** 컨테이너 업데이트 */ onUpdateContainer?: (updates: Partial) => void; /** 추가 className */ className?: string; } // ======================================== // 컴포넌트 타입별 라벨 // ======================================== const COMPONENT_TYPE_LABELS: Record = { "pop-field": "필드", "pop-button": "버튼", "pop-list": "리스트", "pop-indicator": "인디케이터", "pop-scanner": "스캐너", "pop-numpad": "숫자패드", "pop-spacer": "스페이서", "pop-break": "줄바꿈", }; // ======================================== // v4 컴포넌트 편집 패널 // // 핵심: // - 크기 제약 편집 (fixed/fill/hug) // - 반응형 숨김 설정 // - 개별 정렬 설정 // ======================================== export function ComponentEditorPanelV4({ component, container, currentViewportMode = "tablet_landscape", onUpdateComponent, onUpdateContainer, className, }: ComponentEditorPanelV4Props) { // 아무것도 선택되지 않은 경우 if (!component && !container) { return (

속성

컴포넌트 또는 컨테이너를 선택하세요
); } // 컨테이너가 선택된 경우 if (container) { const isNonDefaultMode = currentViewportMode !== "tablet_landscape"; return (

컨테이너 설정

{container.id}

{isNonDefaultMode && (

다른 모드에서는 드래그로 배치 변경 후 '고정' 버튼을 사용하세요

)}
); } // 컴포넌트가 선택된 경우 return (
{/* 헤더 */}

{component!.label || COMPONENT_TYPE_LABELS[component!.type]}

{component!.type}

{/* 탭 컨텐츠 */} 크기 설정 표시 데이터 {/* 크기 제약 탭 */} {/* 기본 설정 탭 */} {/* 모드별 표시 탭 */} {/* 데이터 바인딩 탭 */}
); } // ======================================== // 크기 제약 폼 // ======================================== interface SizeConstraintFormProps { component: PopComponentDefinitionV4; onUpdate?: (updates: Partial) => void; } function SizeConstraintForm({ component, onUpdate }: SizeConstraintFormProps) { const { size } = component; const handleSizeChange = ( field: keyof PopSizeConstraintV4, value: string | number | undefined ) => { onUpdate?.({ size: { ...size, [field]: value, }, }); }; return (
{/* 너비 설정 */}
handleSizeChange("width", "fixed")} label="고정" description="px" /> handleSizeChange("width", "fill")} label="채움" description="flex" /> handleSizeChange("width", "hug")} label="맞춤" description="auto" />
{/* 고정 너비 입력 */} {size.width === "fixed" && (
handleSizeChange( "fixedWidth", e.target.value ? Number(e.target.value) : undefined ) } placeholder="너비" /> px
)} {/* 채움일 때 최소/최대 */} {size.width === "fill" && (
handleSizeChange( "minWidth", e.target.value ? Number(e.target.value) : undefined ) } placeholder="최소" /> ~ handleSizeChange( "maxWidth", e.target.value ? Number(e.target.value) : undefined ) } placeholder="최대" /> px
)}
{/* 높이 설정 */}
handleSizeChange("height", "fixed")} label="고정" description="px" /> handleSizeChange("height", "fill")} label="채움" description="flex" /> handleSizeChange("height", "hug")} label="맞춤" description="auto" />
{/* 고정 높이 입력 */} {size.height === "fixed" && (
handleSizeChange( "fixedHeight", e.target.value ? Number(e.target.value) : undefined ) } placeholder="높이" /> px
)} {/* 채움일 때 최소 */} {size.height === "fill" && (
handleSizeChange( "minHeight", e.target.value ? Number(e.target.value) : undefined ) } placeholder="최소 높이" /> px
)}
{/* 개별 정렬 */}
); } // ======================================== // 크기 버튼 컴포넌트 // ======================================== interface SizeButtonProps { active: boolean; onClick: () => void; label: string; description: string; } function SizeButton({ active, onClick, label, description }: SizeButtonProps) { return ( ); } // ======================================== // 컨테이너 설정 폼 // ======================================== interface ContainerSettingsFormProps { container: PopContainerV4; currentViewportMode?: "mobile_portrait" | "mobile_landscape" | "tablet_portrait" | "tablet_landscape"; onUpdate?: (updates: Partial) => void; } function ContainerSettingsForm({ container, currentViewportMode = "tablet_landscape", onUpdate, }: ContainerSettingsFormProps) { const isNonDefaultMode = currentViewportMode !== "tablet_landscape"; return (
{/* 방향 */}
{isNonDefaultMode && (

드래그로 배치 변경 후 '고정' 버튼 클릭

)}
{/* 줄바꿈 */}
{/* 간격 */}
onUpdate?.({ gap: Number(e.target.value) || 0 })} disabled={isNonDefaultMode} /> px
{/* 패딩 */}
onUpdate?.({ padding: Number(e.target.value) || undefined }) } disabled={isNonDefaultMode} /> px
{/* 정렬 */}
); } // ======================================== // 컴포넌트 설정 폼 // ======================================== interface ComponentSettingsFormProps { component: PopComponentDefinitionV4; onUpdate?: (updates: Partial) => void; } function ComponentSettingsForm({ component, onUpdate, }: ComponentSettingsFormProps) { return (
{/* 라벨 입력 */}
onUpdate?.({ label: e.target.value })} placeholder="컴포넌트 라벨" />
{/* 타입별 설정 (TODO: 상세 구현) */}

{COMPONENT_TYPE_LABELS[component.type]} 상세 설정

(추후 구현 예정)

); } // ======================================== // 모드별 표시/숨김 폼 // ======================================== interface VisibilityFormProps { component: PopComponentDefinitionV4; onUpdate?: (updates: Partial) => void; } function VisibilityForm({ component, onUpdate }: VisibilityFormProps) { const modes = [ { key: "tablet_landscape" as const, label: "태블릿 가로 (1024×768)" }, { key: "tablet_portrait" as const, label: "태블릿 세로 (768×1024)" }, { key: "mobile_landscape" as const, label: "모바일 가로 (667×375)" }, { key: "mobile_portrait" as const, label: "모바일 세로 (375×667)" }, ]; return (

체크 해제하면 해당 모드에서 컴포넌트가 숨겨집니다

{modes.map(({ key, label }) => { const isChecked = component.visibility?.[key] !== false; return (
{ onUpdate?.({ visibility: { ...component.visibility, [key]: e.target.checked, }, }); }} className="h-4 w-4 rounded border-gray-300" /> {!isChecked && ( (숨김) )}
); })}
{/* 반응형 숨김 (픽셀 기반) */}
onUpdate?.({ hideBelow: e.target.value ? Number(e.target.value) : undefined, }) } placeholder="없음" /> px 이하에서 숨김

예: 500 입력 시 화면 너비가 500px 이하면 자동 숨김

); } // ======================================== // 데이터 바인딩 플레이스홀더 // ======================================== function DataBindingPlaceholder() { return (

데이터 바인딩 설정

테이블 선택 - 칼럼 선택 - 조인 설정

(추후 구현 예정)

); } export default ComponentEditorPanelV4;