Files
invyone/frontend/components/pop/designer/panels/ComponentPalette.tsx
T
SeongHyun Kim 52b217c180 feat(pop-search): 검색 컴포넌트 MVP 구현
- pop-search 컴포넌트 신규 추가 (Component, Config, types, index)
- 입력 타입: text, number, date, date-preset, select, multi-select, combo, modal-table, modal-card, modal-icon-grid, toggle
- 디자이너 팔레트, 레지스트리, 타입, 렌더러 라벨 등록
- 기본 그리드 크기 4x2, labelText/labelVisible 설정 지원
- filter_changed 이벤트 발행 (연결 시스템 미적용, 추후 dataFlow 기반으로 전환 예정)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-23 17:16:38 +09:00

135 lines
3.6 KiB
TypeScript

"use client";
import { useDrag } from "react-dnd";
import { cn } from "@/lib/utils";
import { PopComponentType } from "../types/pop-layout";
import { Square, FileText, MousePointer, BarChart3, LayoutGrid, MousePointerClick, List, Search } from "lucide-react";
import { DND_ITEM_TYPES } from "../constants";
// 컴포넌트 정의
interface PaletteItem {
type: PopComponentType;
label: string;
icon: React.ElementType;
description: string;
}
const PALETTE_ITEMS: PaletteItem[] = [
{
type: "pop-sample",
label: "샘플 박스",
icon: Square,
description: "크기 조정 테스트용",
},
{
type: "pop-text",
label: "텍스트",
icon: FileText,
description: "텍스트, 시간, 이미지 표시",
},
{
type: "pop-icon",
label: "아이콘",
icon: MousePointer,
description: "네비게이션 아이콘 (화면 이동, URL, 뒤로가기)",
},
{
type: "pop-dashboard",
label: "대시보드",
icon: BarChart3,
description: "KPI, 차트, 게이지, 통계 집계",
},
{
type: "pop-card-list",
label: "카드 목록",
icon: LayoutGrid,
description: "테이블 데이터를 카드 형태로 표시",
},
{
type: "pop-button",
label: "버튼",
icon: MousePointerClick,
description: "액션 버튼 (저장/삭제/API/모달)",
},
{
type: "pop-string-list",
label: "리스트 목록",
icon: List,
description: "테이블 데이터를 리스트/카드로 표시",
},
{
type: "pop-search",
label: "검색",
icon: Search,
description: "조건 입력 (텍스트/날짜/선택/모달)",
},
];
// 드래그 가능한 컴포넌트 아이템
function DraggablePaletteItem({ item }: { item: PaletteItem }) {
const [{ isDragging }, drag] = useDrag(
() => ({
type: DND_ITEM_TYPES.COMPONENT,
item: { type: DND_ITEM_TYPES.COMPONENT, componentType: item.type },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
}),
[item.type]
);
const Icon = item.icon;
return (
<div
ref={drag}
className={cn(
"flex cursor-grab items-center gap-3 rounded-md border bg-white p-3",
"transition-all hover:border-primary hover:shadow-sm",
isDragging && "opacity-50 cursor-grabbing"
)}
>
<div className="flex h-9 w-9 items-center justify-center rounded bg-muted">
<Icon className="h-4 w-4 text-muted-foreground" />
</div>
<div className="flex-1 min-w-0">
<div className="text-sm font-medium">{item.label}</div>
<div className="text-xs text-muted-foreground truncate">
{item.description}
</div>
</div>
</div>
);
}
// 컴포넌트 팔레트 패널
export default function ComponentPalette() {
return (
<div className="flex h-full flex-col bg-gray-50">
{/* 헤더 */}
<div className="border-b bg-white px-4 py-3">
<h3 className="text-sm font-semibold"></h3>
<p className="text-xs text-muted-foreground">
</p>
</div>
{/* 컴포넌트 목록 */}
<div className="flex-1 overflow-y-auto p-3">
<div className="space-y-2">
{PALETTE_ITEMS.map((item) => (
<DraggablePaletteItem key={item.type} item={item} />
))}
</div>
</div>
{/* 하단 안내 */}
<div className="border-t bg-white px-4 py-3">
<p className="text-xs text-muted-foreground">
Tip: 캔버스의
</p>
</div>
</div>
);
}