"use client"; /** * V2Group * * 통합 그룹 컴포넌트 * - tabs: 탭 그룹 * - accordion: 아코디언 그룹 * - section: 섹션 그룹 * - card-section: 카드 섹션 * - modal: 모달 그룹 * - form-modal: 폼 모달 그룹 */ import React, { forwardRef, useState, useCallback } from "react"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { cn } from "@/lib/utils"; import { V2GroupProps, TabItem } from "@/types/v2-components"; import { ChevronDown, ChevronRight, X } from "lucide-react"; /** * 탭 그룹 컴포넌트 */ const TabsGroup = forwardRef void; children?: React.ReactNode; className?: string; }>(({ tabs = [], activeTab, onTabChange, children, className }, ref) => { const [internalActiveTab, setInternalActiveTab] = useState(activeTab || tabs[0]?.id || ""); const currentTab = activeTab || internalActiveTab; const handleTabChange = useCallback((tabId: string) => { setInternalActiveTab(tabId); onTabChange?.(tabId); }, [onTabChange]); // 탭 정보가 있으면 탭 사용, 없으면 children 그대로 렌더링 if (tabs.length === 0) { return (
{children}
); } return ( {tabs.map((tab) => ( {tab.title} ))} {tabs.map((tab) => ( {tab.content || children} ))} ); }); TabsGroup.displayName = "TabsGroup"; /** * 아코디언 그룹 컴포넌트 */ const AccordionGroup = forwardRef(({ title, collapsible = true, defaultExpanded = true, children, className }, ref) => { const [isOpen, setIsOpen] = useState(defaultExpanded); if (!collapsible) { return (
{title && (

{title}

)}
{children}
); } return (

{title || "그룹"}

{isOpen ? ( ) : ( )}
{children}
); }); AccordionGroup.displayName = "AccordionGroup"; /** * 섹션 그룹 컴포넌트 */ const SectionGroup = forwardRef(({ title, description, collapsible = false, defaultExpanded = true, children, className }, ref) => { const [isOpen, setIsOpen] = useState(defaultExpanded); if (collapsible) { return (
{title &&

{title}

} {description &&

{description}

}
{isOpen ? ( ) : ( )}
{children}
); } return (
{(title || description) && (
{title &&

{title}

} {description &&

{description}

}
)} {children}
); }); SectionGroup.displayName = "SectionGroup"; /** * 카드 섹션 그룹 컴포넌트 */ const CardSectionGroup = forwardRef(({ title, description, collapsible = false, defaultExpanded = true, children, className }, ref) => { const [isOpen, setIsOpen] = useState(defaultExpanded); if (collapsible) { return (
{title && {title}} {description && {description}}
{isOpen ? ( ) : ( )}
{children}
); } return ( {(title || description) && ( {title && {title}} {description && {description}} )} {children} ); }); CardSectionGroup.displayName = "CardSectionGroup"; /** * 모달 그룹 컴포넌트 */ const ModalGroup = forwardRef void; modalSize?: "sm" | "md" | "lg" | "xl"; children?: React.ReactNode; className?: string; }>(({ title, description, open = false, onOpenChange, modalSize = "md", children, className }, ref) => { const sizeClasses = { sm: "max-w-sm", md: "max-w-md", lg: "max-w-lg", xl: "max-w-xl", }; return ( {title || description ? ( {title && {title}} {description && {description}} ) : ( 모달 )} {children} ); }); ModalGroup.displayName = "ModalGroup"; /** * 폼 모달 그룹 컴포넌트 */ const FormModalGroup = forwardRef void; modalSize?: "sm" | "md" | "lg" | "xl"; onSubmit?: () => void; onCancel?: () => void; submitLabel?: string; cancelLabel?: string; children?: React.ReactNode; className?: string; }>(({ title, description, open = false, onOpenChange, modalSize = "md", onSubmit, onCancel, submitLabel = "저장", cancelLabel = "취소", children, className }, ref) => { const sizeClasses = { sm: "max-w-sm", md: "max-w-md", lg: "max-w-lg", xl: "max-w-xl", }; const handleCancel = useCallback(() => { onCancel?.(); onOpenChange?.(false); }, [onCancel, onOpenChange]); const handleSubmit = useCallback(() => { onSubmit?.(); }, [onSubmit]); return ( {title || description ? ( {title && {title}} {description && {description}} ) : ( 폼 모달 )}
{children}
); }); FormModalGroup.displayName = "FormModalGroup"; /** * 메인 V2Group 컴포넌트 */ export const V2Group = forwardRef( (props, ref) => { const { id, style, size, config: configProp, children, open, onOpenChange, } = props; // config가 없으면 기본값 사용 const config = configProp || { type: "section" as const, tabs: [] }; // 타입별 그룹 렌더링 const renderGroup = () => { const groupType = config.type || "section"; switch (groupType) { case "tabs": return ( {children} ); case "accordion": return ( {children} ); case "section": return ( {children} ); case "card-section": return ( {children} ); case "modal": return ( {children} ); case "form-modal": return ( {children} ); default: return ( {children} ); } }; const componentWidth = size?.width || style?.width; const componentHeight = size?.height || style?.height; return (
{renderGroup()}
); } ); V2Group.displayName = "V2Group"; export default V2Group;