"use client"; import React, { useState, useEffect } from "react"; import { format } from "date-fns"; import { ko } from "date-fns/locale"; import { cn } from "@/lib/utils"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Switch } from "@/components/ui/switch"; import { Checkbox } from "@/components/ui/checkbox"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { PopComponentRegistry } from "@/lib/registry/PopComponentRegistry"; import { FontSize, FontWeight, TextAlign, ObjectFit, VerticalAlign, FONT_SIZE_LABELS, FONT_WEIGHT_LABELS, OBJECT_FIT_LABELS, FONT_SIZE_CLASSES, FONT_WEIGHT_CLASSES, TEXT_ALIGN_CLASSES, VERTICAL_ALIGN_LABELS, VERTICAL_ALIGN_CLASSES, JUSTIFY_CLASSES, } from "./types"; // ======================================== // 타입 정의 // ======================================== export type PopTextType = "text" | "datetime" | "image" | "title"; // datetime 빌더 설정 타입 export interface DateTimeBuilderConfig { // 날짜 요소 showYear?: boolean; showMonth?: boolean; showDay?: boolean; showWeekday?: boolean; // 시간 요소 showHour?: boolean; showMinute?: boolean; showSecond?: boolean; // 표기 방식 useKorean?: boolean; // true: 한글 (02월 04일), false: 숫자 (02/04) // 구분자 dateSeparator?: string; // "-", "/", "." } export interface PopTextConfig { textType: PopTextType; content?: string; dateFormat?: string; // 기존 호환용 (deprecated) dateTimeConfig?: DateTimeBuilderConfig; // 새로운 빌더 설정 isRealtime?: boolean; imageUrl?: string; objectFit?: ObjectFit; imageScale?: number; // 이미지 크기 조정 (10-100%) fontSize?: FontSize; fontWeight?: FontWeight; textAlign?: TextAlign; verticalAlign?: VerticalAlign; // 상하 정렬 } const TEXT_TYPE_LABELS: Record = { text: "일반 텍스트", datetime: "시간/날짜", image: "이미지", title: "제목", }; // ======================================== // datetime 포맷 빌드 함수 // ======================================== function buildDateTimeFormat(config?: DateTimeBuilderConfig): string { // 설정이 없으면 기본값 (시:분:초) if (!config) return "HH:mm:ss"; const sep = config.dateSeparator || "-"; const parts: string[] = []; // 날짜 부분 조합 const hasDateParts = config.showYear || config.showMonth || config.showDay; if (hasDateParts) { const dateParts: string[] = []; if (config.showYear) dateParts.push(config.useKorean ? "yyyy년" : "yyyy"); if (config.showMonth) dateParts.push(config.useKorean ? "MM월" : "MM"); if (config.showDay) dateParts.push(config.useKorean ? "dd일" : "dd"); // 한글 모드: 공백으로 연결, 숫자 모드: 구분자로 연결 parts.push(config.useKorean ? dateParts.join(" ") : dateParts.join(sep)); } // 요일 if (config.showWeekday) { parts.push(config.useKorean ? "(EEEE)" : "(EEE)"); } // 시간 부분 조합 const timeParts: string[] = []; if (config.showHour) timeParts.push(config.useKorean ? "HH시" : "HH"); if (config.showMinute) timeParts.push(config.useKorean ? "mm분" : "mm"); if (config.showSecond) timeParts.push(config.useKorean ? "ss초" : "ss"); if (timeParts.length > 0) { // 한글 모드: 공백으로 연결, 숫자 모드: 콜론으로 연결 parts.push(config.useKorean ? timeParts.join(" ") : timeParts.join(":")); } // 아무것도 선택 안 했으면 기본값 return parts.join(" ") || "HH:mm:ss"; } // ======================================== // 메인 컴포넌트 // ======================================== interface PopTextComponentProps { config?: PopTextConfig; label?: string; isDesignMode?: boolean; } export function PopTextComponent({ config, label, isDesignMode, }: PopTextComponentProps) { const textType = config?.textType || "text"; if (isDesignMode) { return (
); } // 실제 렌더링 switch (textType) { case "datetime": return ; case "image": return ; case "title": return ; default: return ; } } // 디자인 모드 미리보기 (실제 설정값 표시) function DesignModePreview({ config, label, }: { config?: PopTextConfig; label?: string; }) { const textType = config?.textType || "text"; // 공통 정렬 래퍼 클래스 (상하좌우 정렬) const alignWrapperClass = cn( "flex w-full h-full", VERTICAL_ALIGN_CLASSES[config?.verticalAlign || "center"], JUSTIFY_CLASSES[config?.textAlign || "left"] ); switch (textType) { case "datetime": // 실시간 시간 미리보기 return (
); case "image": // 이미지 미리보기 if (!config?.imageUrl) { return (
이미지 URL 없음
); } // 이미지도 정렬 래퍼 적용 return (
); case "title": // 제목 미리보기 return (
{config?.content || label || "제목"}
); default: // 일반 텍스트 미리보기 return (
{config?.content || label || "텍스트"}
); } } // 디자인 모드용 시간 미리보기 (실시간) function DateTimePreview({ config }: { config?: PopTextConfig }) { const [now, setNow] = useState(new Date()); useEffect(() => { // 디자인 모드에서도 실시간 업데이트 (간격 늘림) const timer = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(timer); }, []); // 빌더 설정 또는 기존 dateFormat 사용 (하위 호환) const dateFormat = config?.dateTimeConfig ? buildDateTimeFormat(config.dateTimeConfig) : config?.dateFormat || "HH:mm:ss"; return ( {format(now, dateFormat, { locale: ko })} ); } // 시간/날짜 (실시간 지원) function DateTimeDisplay({ config }: { config?: PopTextConfig }) { const [now, setNow] = useState(new Date()); useEffect(() => { if (!config?.isRealtime) return; const timer = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(timer); }, [config?.isRealtime]); // 빌더 설정 또는 기존 dateFormat 사용 (하위 호환) const dateFormat = config?.dateTimeConfig ? buildDateTimeFormat(config.dateTimeConfig) : config?.dateFormat || "HH:mm:ss"; // 정렬 래퍼 클래스 const alignWrapperClass = cn( "flex w-full h-full", VERTICAL_ALIGN_CLASSES[config?.verticalAlign || "center"], JUSTIFY_CLASSES[config?.textAlign || "left"] ); return (
{format(now, dateFormat, { locale: ko })}
); } // 이미지 function ImageDisplay({ config }: { config?: PopTextConfig }) { if (!config?.imageUrl) { return (
이미지 URL 필요
); } // 정렬 래퍼 클래스 const alignWrapperClass = cn( "flex w-full h-full", VERTICAL_ALIGN_CLASSES[config?.verticalAlign || "center"], JUSTIFY_CLASSES[config?.textAlign || "left"] ); return (
); } // 제목 function TitleDisplay({ config, label, }: { config?: PopTextConfig; label?: string; }) { const sizeClass = FONT_SIZE_CLASSES[config?.fontSize || "base"]; const weightClass = FONT_WEIGHT_CLASSES[config?.fontWeight || "normal"]; // 정렬 래퍼 클래스 const alignWrapperClass = cn( "flex w-full h-full", VERTICAL_ALIGN_CLASSES[config?.verticalAlign || "center"], JUSTIFY_CLASSES[config?.textAlign || "left"] ); return (
{config?.content || label || "제목"}
); } // 일반 텍스트 function TextDisplay({ config, label, }: { config?: PopTextConfig; label?: string; }) { const sizeClass = FONT_SIZE_CLASSES[config?.fontSize || "base"]; // 정렬 래퍼 클래스 const alignWrapperClass = cn( "flex w-full h-full", VERTICAL_ALIGN_CLASSES[config?.verticalAlign || "center"], JUSTIFY_CLASSES[config?.textAlign || "left"] ); return (
{config?.content || label || "텍스트"}
); } // ======================================== // 설정 패널 // ======================================== interface PopTextConfigPanelProps { config: PopTextConfig; onUpdate: (config: PopTextConfig) => void; } export function PopTextConfigPanel({ config, onUpdate, }: PopTextConfigPanelProps) { const textType = config?.textType || "text"; return (
{/* 텍스트 타입 선택 */}
{/* 서브타입별 설정 */} {textType === "text" && ( <>