feat(pop-text): POP 텍스트 컴포넌트 추가

- pop-text 컴포넌트 구현 (텍스트/시간/이미지/제목 타입)
- PopComponentRegistry에 preview 속성 추가
- ComponentEditorPanel에서 configPanel 동적 렌더링
- PopRenderer에서 preview 컴포넌트 렌더링 지원
- ComponentPalette에 텍스트 컴포넌트 추가

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
shin
2026-02-06 17:07:56 +09:00
parent 40219fed08
commit 2dfc3cc681
9 changed files with 984 additions and 32 deletions

View File

@@ -12,6 +12,9 @@ import {
} from "@/components/ui/resizable";
import { toast } from "sonner";
// POP 컴포넌트 자동 등록 (반드시 다른 import보다 먼저)
import "@/lib/registry/pop-components";
import PopCanvas from "./PopCanvas";
import ComponentEditorPanel from "./panels/ComponentEditorPanel";
import ComponentPalette from "./panels/ComponentPalette";

View File

@@ -21,6 +21,7 @@ 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
@@ -315,6 +316,15 @@ interface ComponentSettingsFormProps {
}
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 (
<div className="space-y-4">
{/* 라벨 */}
@@ -329,12 +339,19 @@ function ComponentSettingsForm({ component, onUpdate }: ComponentSettingsFormPro
/>
</div>
{/* 컴포넌트 타입별 설정 (추후 구현) */}
<div className="rounded-lg bg-gray-50 p-3">
<p className="text-xs text-muted-foreground">
{component.type} Phase 4
</p>
</div>
{/* 컴포넌트 타입별 설정 패널 */}
{ConfigPanel ? (
<ConfigPanel
config={component.config || {}}
onUpdate={handleConfigUpdate}
/>
) : (
<div className="rounded-lg bg-gray-50 p-3">
<p className="text-xs text-muted-foreground">
{component.type}
</p>
</div>
)}
</div>
);
}

View File

@@ -3,7 +3,7 @@
import { useDrag } from "react-dnd";
import { cn } from "@/lib/utils";
import { PopComponentType } from "../types/pop-layout";
import { Square } from "lucide-react";
import { Square, FileText } from "lucide-react";
import { DND_ITEM_TYPES } from "../constants";
// 컴포넌트 정의
@@ -21,6 +21,12 @@ const PALETTE_ITEMS: PaletteItem[] = [
icon: Square,
description: "크기 조정 테스트용",
},
{
type: "pop-text",
label: "텍스트",
icon: FileText,
description: "텍스트, 시간, 이미지 표시",
},
];
// 드래그 가능한 컴포넌트 아이템

View File

@@ -19,6 +19,7 @@ import {
isOverlapping,
getAllEffectivePositions,
} from "../utils/gridUtils";
import { PopComponentRegistry } from "@/lib/registry/PopComponentRegistry";
// ========================================
// Props
@@ -500,7 +501,11 @@ interface ComponentContentProps {
function ComponentContent({ component, effectivePosition, isDesignMode, isSelected }: ComponentContentProps) {
const typeLabel = COMPONENT_TYPE_LABELS[component.type] || component.type;
// 디자인 모드: 플레이스홀더 표시
// PopComponentRegistry에서 등록된 컴포넌트 가져오기
const registeredComp = PopComponentRegistry.getComponent(component.type);
const PreviewComponent = registeredComp?.preview;
// 디자인 모드: 미리보기 컴포넌트 또는 플레이스홀더 표시
if (isDesignMode) {
return (
<div className="flex h-full w-full flex-col">
@@ -519,11 +524,15 @@ function ComponentContent({ component, effectivePosition, isDesignMode, isSelect
</span>
</div>
{/* 내용 */}
<div className="flex flex-1 items-center justify-center p-2">
<span className="text-xs text-gray-400">
{typeLabel}
</span>
{/* 내용: 등록된 preview 컴포넌트 또는 기본 플레이스홀더 */}
<div className="flex flex-1 items-center justify-center overflow-hidden">
{PreviewComponent ? (
<PreviewComponent config={component.config} />
) : (
<span className="text-xs text-gray-400 p-2">
{typeLabel}
</span>
)}
</div>
{/* 위치 정보 표시 (유효 위치 사용) */}

View File

@@ -9,7 +9,7 @@
/**
* POP 컴포넌트 타입
*/
export type PopComponentType = "pop-sample"; // 테스트용 샘플 박스
export type PopComponentType = "pop-sample" | "pop-text"; // 테스트용 샘플 박스, 텍스트 컴포넌트
/**
* 데이터 흐름 정의
@@ -341,6 +341,7 @@ export const isV5Layout = (layout: any): layout is PopLayoutDataV5 => {
*/
export const DEFAULT_COMPONENT_GRID_SIZE: Record<PopComponentType, { colSpan: number; rowSpan: number }> = {
"pop-sample": { colSpan: 2, rowSpan: 1 },
"pop-text": { colSpan: 3, rowSpan: 1 },
};
/**