"use client"; /** * Display 메타 컴포넌트 렌더러 * - config.displayType에 따라: * - text:

텍스트 * - heading:

~

* - divider:
구분선 * - badge: shadcn Badge * - alert: shadcn Alert * - stat: 통계 카드 (라벨 + 숫자) * - config.dataBinding이 있으면 formData에서 값 바인딩 * - 디자인 모드: 대체 텍스트 표시 */ import React from "react"; import { cn } from "@/lib/utils"; import { Separator } from "@/components/ui/separator"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { TrendingUp, TrendingDown } from "lucide-react"; interface DisplayRendererProps { id: string; config: { displayType: "text" | "heading" | "divider" | "badge" | "alert" | "stat"; text?: { content: string; size?: "xs" | "sm" | "md" | "lg" | "xl"; weight?: "normal" | "medium" | "semibold" | "bold"; align?: "left" | "center" | "right"; }; heading?: { content: string; level: 1 | 2 | 3 | 4 | 5 | 6; }; badge?: { text: string; variant?: "default" | "secondary" | "outline" | "destructive"; }; alert?: { title?: string; message: string; variant?: "default" | "destructive"; }; stat?: { label: string; value: string | number; change?: string; changeType?: "increase" | "decrease" | "neutral"; }; dataBinding?: string; // formData에서 값 바인딩 시 키 }; formData?: Record; isDesignMode?: boolean; className?: string; } export function DisplayRenderer({ id, config, formData, isDesignMode = false, className, }: DisplayRendererProps) { const { displayType } = config; // 데이터 바인딩 처리 const getBoundValue = (defaultValue: any) => { if (config.dataBinding && formData) { return formData[config.dataBinding] ?? defaultValue; } return defaultValue; }; // text:

+ 크기/굵기/정렬 if (displayType === "text" && config.text) { const { content, size = "md", weight = "normal", align = "left" } = config.text; const boundContent = getBoundValue(content); const sizeClass = { xs: "text-xs", sm: "text-sm", md: "text-base", lg: "text-lg", xl: "text-xl", }[size]; const weightClass = { normal: "font-normal", medium: "font-medium", semibold: "font-semibold", bold: "font-bold", }[weight]; const alignClass = { left: "text-left", center: "text-center", right: "text-right", }[align]; return (

{isDesignMode && !boundContent ? "(텍스트)" : boundContent}

); } // heading:

~

if (displayType === "heading" && config.heading) { const { content, level } = config.heading; const boundContent = getBoundValue(content); const Tag = `h${level}` as keyof JSX.IntrinsicElements; const sizeClass = { 1: "text-3xl", 2: "text-2xl", 3: "text-xl", 4: "text-lg", 5: "text-base", 6: "text-sm", }[level]; return ( {isDesignMode && !boundContent ? "(제목)" : boundContent} ); } // divider: shadcn Separator if (displayType === "divider") { return ; } // badge: shadcn Badge if (displayType === "badge" && config.badge) { const { text, variant = "default" } = config.badge; const boundText = getBoundValue(text); return ( {isDesignMode && !boundText ? "(뱃지)" : boundText} ); } // alert: shadcn Alert if (displayType === "alert" && config.alert) { const { title, message, variant = "default" } = config.alert; const boundMessage = getBoundValue(message); return ( {title && {title}} {isDesignMode && !boundMessage ? "(알림 메시지)" : boundMessage} ); } // stat: 통계 카드 if (displayType === "stat" && config.stat) { const { label, value, change, changeType = "neutral" } = config.stat; const boundValue = getBoundValue(value); const icon = changeType === "increase" ? ( ) : changeType === "decrease" ? ( ) : null; return (
{label} {change && (
{icon} {change}
)}
{isDesignMode && !boundValue ? "0" : boundValue}
); } return (
Unknown display type: {displayType}
); }