"use client"; import React from "react"; import { cn } from "@/lib/utils"; import { PopComponentDefinitionV5, PopGridPosition, GridMode, GRID_BREAKPOINTS, PopComponentType, } from "../types/pop-layout"; import { Settings, Database, Eye, Grid3x3, MoveHorizontal, MoveVertical, } from "lucide-react"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Checkbox } from "@/components/ui/checkbox"; import { PopComponentRegistry } from "@/lib/registry/PopComponentRegistry"; // ======================================== // Props // ======================================== interface ComponentEditorPanelProps { /** 선택된 컴포넌트 */ component: PopComponentDefinitionV5 | null; /** 현재 모드 */ currentMode: GridMode; /** 컴포넌트 업데이트 */ onUpdateComponent?: (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": "줄바꿈", }; // ======================================== // 컴포넌트 편집 패널 (v5 그리드 시스템) // ======================================== export default function ComponentEditorPanel({ component, currentMode, onUpdateComponent, className, }: ComponentEditorPanelProps) { const breakpoint = GRID_BREAKPOINTS[currentMode]; // 선택된 컴포넌트 없음 if (!component) { return (

속성

컴포넌트를 선택하세요
); } // 기본 모드 여부 const isDefaultMode = currentMode === "tablet_landscape"; return (
{/* 헤더 */}

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

{component.type}

{!isDefaultMode && (

기본 모드(태블릿 가로)에서만 위치 편집 가능

)}
{/* 탭 */} 위치 설정 표시 데이터 {/* 위치 탭 */} {/* 설정 탭 */} {/* 표시 탭 */} {/* 데이터 탭 */}
); } // ======================================== // 위치 편집 폼 // ======================================== interface PositionFormProps { component: PopComponentDefinitionV5; currentMode: GridMode; isDefaultMode: boolean; columns: number; onUpdate?: (updates: Partial) => void; } function PositionForm({ component, currentMode, isDefaultMode, columns, onUpdate }: PositionFormProps) { const { position } = component; const handlePositionChange = (field: keyof PopGridPosition, value: number) => { // 범위 체크 let clampedValue = Math.max(1, value); if (field === "col" || field === "colSpan") { clampedValue = Math.min(columns, clampedValue); } if (field === "colSpan" && position.col + clampedValue - 1 > columns) { clampedValue = columns - position.col + 1; } onUpdate?.({ position: { ...position, [field]: clampedValue, }, }); }; return (
{/* 그리드 정보 */}

현재 그리드: {GRID_BREAKPOINTS[currentMode].label}

최대 {columns}칸 × 무제한 행

{/* 열 위치 */}
handlePositionChange("col", parseInt(e.target.value) || 1)} disabled={!isDefaultMode} className="h-8 w-20 text-xs" /> (1~{columns})
{/* 행 위치 */}
handlePositionChange("row", parseInt(e.target.value) || 1)} disabled={!isDefaultMode} className="h-8 w-20 text-xs" /> (1~)
{/* 열 크기 */}
handlePositionChange("colSpan", parseInt(e.target.value) || 1)} disabled={!isDefaultMode} className="h-8 w-20 text-xs" /> 칸 (1~{columns})

{Math.round((position.colSpan / columns) * 100)}% 너비

{/* 행 크기 */}
handlePositionChange("rowSpan", parseInt(e.target.value) || 1)} disabled={!isDefaultMode} className="h-8 w-20 text-xs" />

높이: {position.rowSpan * GRID_BREAKPOINTS[currentMode].rowHeight}px

{/* 비활성화 안내 */} {!isDefaultMode && (

위치 편집은 기본 모드(태블릿 가로)에서만 가능합니다. 다른 모드에서는 자동으로 변환됩니다.

)}
); } // ======================================== // 설정 폼 // ======================================== interface ComponentSettingsFormProps { component: PopComponentDefinitionV5; onUpdate?: (updates: Partial) => void; } function ComponentSettingsForm({ component, onUpdate }: ComponentSettingsFormProps) { // PopComponentRegistry에서 configPanel 가져오기 const registeredComp = PopComponentRegistry.getComponent(component.type); const ConfigPanel = registeredComp?.configPanel; // config 업데이트 핸들러 const handleConfigUpdate = (newConfig: any) => { onUpdate?.({ config: newConfig }); }; return (
{/* 라벨 */}
onUpdate?.({ label: e.target.value })} placeholder="컴포넌트 이름" className="h-8 text-xs" />
{/* 컴포넌트 타입별 설정 패널 */} {ConfigPanel ? ( ) : (

{component.type} 전용 설정이 없습니다

)}
); } // ======================================== // 표시/숨김 폼 // ======================================== interface VisibilityFormProps { component: PopComponentDefinitionV5; onUpdate?: (updates: Partial) => void; } function VisibilityForm({ component, onUpdate }: VisibilityFormProps) { const modes: Array<{ key: GridMode; label: string }> = [ { key: "tablet_landscape", label: "태블릿 가로 (12칸)" }, { key: "tablet_portrait", label: "태블릿 세로 (8칸)" }, { key: "mobile_landscape", label: "모바일 가로 (6칸)" }, { key: "mobile_portrait", label: "모바일 세로 (4칸)" }, ]; const handleVisibilityChange = (mode: GridMode, visible: boolean) => { onUpdate?.({ visibility: { ...component.visibility, [mode]: visible, }, }); }; return (
{modes.map((mode) => { const isVisible = component.visibility?.[mode.key] !== false; return (
handleVisibilityChange(mode.key, checked === true) } />
); })}

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

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

데이터 바인딩

Phase 4에서 구현 예정

); }