@crossangle-org/cs-ui 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion/cs-accordion.js +116 -0
- package/dist/components/accordion/cs-accordion.js.map +1 -0
- package/dist/components/alert-dialog/cs-alert-dialog.js +148 -0
- package/dist/components/alert-dialog/cs-alert-dialog.js.map +1 -0
- package/dist/components/avatar/cs-avatar.js +44 -0
- package/dist/components/avatar/cs-avatar.js.map +1 -0
- package/dist/components/badge/cs-badge.js +40 -0
- package/dist/components/badge/cs-badge.js.map +1 -0
- package/dist/components/box/cs-box.js +37 -0
- package/dist/components/box/cs-box.js.map +1 -0
- package/dist/components/button/cs-button.js +91 -0
- package/dist/components/button/cs-button.js.map +1 -0
- package/dist/components/calendar/cs-calendar.js +199 -0
- package/dist/components/calendar/cs-calendar.js.map +1 -0
- package/dist/components/card/cs-card.js +95 -0
- package/dist/components/card/cs-card.js.map +1 -0
- package/dist/components/chart/cs-chart.js +88 -0
- package/dist/components/chart/cs-chart.js.map +1 -0
- package/dist/components/checkbox/cs-checkbox.js +55 -0
- package/dist/components/checkbox/cs-checkbox.js.map +1 -0
- package/dist/components/code-block/cs-code-block.js +39 -0
- package/dist/components/code-block/cs-code-block.js.map +1 -0
- package/dist/components/code-block/cs-code-highlighter.js +59 -0
- package/dist/components/code-block/cs-code-highlighter.js.map +1 -0
- package/dist/components/collapsible/cs-collapsible.js +36 -0
- package/dist/components/collapsible/cs-collapsible.js.map +1 -0
- package/dist/components/date-picker/cs-date-picker.js +25 -0
- package/dist/components/date-picker/cs-date-picker.js.map +1 -0
- package/dist/components/dialog/cs-dialog.js +131 -0
- package/dist/components/dialog/cs-dialog.js.map +1 -0
- package/dist/components/drawer/cs-drawer.js +131 -0
- package/dist/components/drawer/cs-drawer.js.map +1 -0
- package/dist/components/dropdown-menu/cs-dropdown-menu.js +247 -0
- package/dist/components/dropdown-menu/cs-dropdown-menu.js.map +1 -0
- package/dist/components/dropzone/cs-dropzone.js +147 -0
- package/dist/components/dropzone/cs-dropzone.js.map +1 -0
- package/dist/components/empty/cs-empty.js +107 -0
- package/dist/components/empty/cs-empty.js.map +1 -0
- package/dist/components/field/cs-field.js +218 -0
- package/dist/components/field/cs-field.js.map +1 -0
- package/dist/components/input/cs-input-group.js +207 -0
- package/dist/components/input/cs-input-group.js.map +1 -0
- package/dist/components/input/cs-input.js +40 -0
- package/dist/components/input/cs-input.js.map +1 -0
- package/dist/components/label/cs-label.js +26 -0
- package/dist/components/label/cs-label.js.map +1 -0
- package/dist/components/navigation-menu/cs-navigation-menu.js +214 -0
- package/dist/components/navigation-menu/cs-navigation-menu.js.map +1 -0
- package/dist/components/pagination/cs-pagination.js +124 -0
- package/dist/components/pagination/cs-pagination.js.map +1 -0
- package/dist/components/popover/cs-popover.js +60 -0
- package/dist/components/popover/cs-popover.js.map +1 -0
- package/dist/components/progress/cs-progress.js +62 -0
- package/dist/components/progress/cs-progress.js.map +1 -0
- package/dist/components/scroll-area/cs-scroll-area.js +61 -0
- package/dist/components/scroll-area/cs-scroll-area.js.map +1 -0
- package/dist/components/select/cs-select.js +195 -0
- package/dist/components/select/cs-select.js.map +1 -0
- package/dist/components/select/cs-simple-select.js +32 -0
- package/dist/components/select/cs-simple-select.js.map +1 -0
- package/dist/components/separator/cs-separator.js +28 -0
- package/dist/components/separator/cs-separator.js.map +1 -0
- package/dist/components/sheet/cs-sheet.js +128 -0
- package/dist/components/sheet/cs-sheet.js.map +1 -0
- package/dist/components/sidebar/cs-sidebar.js +657 -0
- package/dist/components/sidebar/cs-sidebar.js.map +1 -0
- package/dist/components/skeleton/cs-skeleton.js +32 -0
- package/dist/components/skeleton/cs-skeleton.js.map +1 -0
- package/dist/components/sonner/cs-sonner.js +76 -0
- package/dist/components/sonner/cs-sonner.js.map +1 -0
- package/dist/components/spinner/cs-spinner.js +34 -0
- package/dist/components/spinner/cs-spinner.js.map +1 -0
- package/dist/components/switch/cs-switch.js +38 -0
- package/dist/components/switch/cs-switch.js.map +1 -0
- package/dist/components/table/cs-data-base-table.js +108 -0
- package/dist/components/table/cs-data-base-table.js.map +1 -0
- package/dist/components/table/cs-data-table.js +32 -0
- package/dist/components/table/cs-data-table.js.map +1 -0
- package/dist/components/table/cs-skeleton-table.js +41 -0
- package/dist/components/table/cs-skeleton-table.js.map +1 -0
- package/dist/components/table/cs-table.js +120 -0
- package/dist/components/table/cs-table.js.map +1 -0
- package/dist/components/tabs/cs-simple-tabs.js +24 -0
- package/dist/components/tabs/cs-simple-tabs.js.map +1 -0
- package/dist/components/tabs/cs-tabs.js +114 -0
- package/dist/components/tabs/cs-tabs.js.map +1 -0
- package/dist/components/toggle/cs-toggle-group.js +65 -0
- package/dist/components/toggle/cs-toggle-group.js.map +1 -0
- package/dist/components/toggle/cs-toggle.js +46 -0
- package/dist/components/toggle/cs-toggle.js.map +1 -0
- package/dist/components/tooltip/cs-simple-tooltip.js +16 -0
- package/dist/components/tooltip/cs-simple-tooltip.js.map +1 -0
- package/dist/components/tooltip/cs-tooltip.js +72 -0
- package/dist/components/tooltip/cs-tooltip.js.map +1 -0
- package/dist/constants/cs-chart-option.constant.js +105 -0
- package/dist/constants/cs-chart-option.constant.js.map +1 -0
- package/dist/cs-ui.css +73 -108
- package/dist/hooks/use-accordion.js +54 -0
- package/dist/hooks/use-accordion.js.map +1 -0
- package/dist/hooks/use-infinite-scroll.js +40 -0
- package/dist/hooks/use-infinite-scroll.js.map +1 -0
- package/dist/hooks/use-laptop.js +20 -0
- package/dist/hooks/use-laptop.js.map +1 -0
- package/dist/hooks/use-mobile.js +20 -0
- package/dist/hooks/use-mobile.js.map +1 -0
- package/dist/index.d.ts +19 -6
- package/dist/index.js +287 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/chart.util.js +48 -0
- package/dist/lib/chart.util.js.map +1 -0
- package/dist/lib/style.util.js +19 -0
- package/dist/lib/style.util.js.map +1 -0
- package/dist/lib/utils.js +27 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +4 -5
- package/dist/index.cjs.js +0 -147659
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.es.js +0 -147624
- package/dist/index.es.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs-sidebar.js","sources":["../../../src/components/sidebar/cs-sidebar.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { ChevronDownIcon, PanelLeftIcon } from \"lucide-react\"\n\nimport { useIsMobile } from \"../../hooks/use-mobile\"\nimport { cn } from \"../../lib/utils\"\nimport { CsButton } from \"../button\"\nimport { CsCollapsible, CsCollapsibleContent, CsCollapsibleTrigger } from \"../collapsible\"\nimport { CsInput } from \"../input\"\nimport { csNavigationMenuLinkVariants } from \"../navigation-menu\"\nimport { CsSeparator } from \"../separator\"\nimport {\n CsSheet,\n CsSheetContent,\n CsSheetDescription,\n CsSheetHeader,\n CsSheetTitle,\n} from \"../sheet\"\nimport { CsSkeleton } from \"../skeleton\"\nimport {\n CsTooltip,\n CsTooltipContent,\n CsTooltipProvider,\n CsTooltipTrigger,\n} from \"../tooltip\"\nimport { csNavigationMenuTriggerStyle } from \"../navigation-menu\"\n\n/**\n * CS Design System 사이드바 컴포넌트\n *\n * 애플리케이션의 주요 네비게이션을 제공하는 반응형 사이드바.\n * 대시보드, 관리자 패널 등에 사용되며, 데스크탑에서는 고정/접힘 모드, 모바일에서는 Sheet로 표시됩니다.\n *\n * ## 사용 시나리오\n * - 대시보드 네비게이션: 주요 메뉴와 하위 메뉴 표시\n * - 관리자 패널: 여러 관리 기능 접근\n * - 앱 내비게이션: 페이지 간 이동, 섹션 전환\n * - 접히는 사이드바: 아이콘만 표시하여 공간 절약 (collapsible=\"icon\")\n * - 모바일: Sheet로 변환되어 필요시에만 표시\n *\n * ## 유사 컴포넌트와의 차이\n * - **CsSheet**: 임시 패널 (설정, 필터) - 데스크탑/모바일 동일\n * - Sidebar는 항상 표시되는 네비게이션, Sheet는 임시 패널\n *\n * ## 주요 컴포넌트\n * | 컴포넌트 | 역할 |\n * |----------|------|\n * | CsSidebarProvider | Context 제공 (필수) |\n * | CsSidebar | 사이드바 컨테이너 |\n * | CsSidebarContent | 스크롤 가능한 본문 영역 |\n * | CsSidebarGroup | 메뉴 그룹 |\n * | CsSidebarMenu | 메뉴 리스트 |\n * | CsSidebarMenuButton | 메뉴 버튼 (tooltip 지원) |\n * | CsSidebarTrigger | 토글 버튼 |\n *\n * ## Variant (메뉴 버튼 스타일)\n * CsNavigationMenu 컴포넌트의 스타일 시스템 재사용:\n * - `csNavigationMenuTriggerStyle` - 1차 메뉴 버튼 (solid-ghost | bottom-border | gradient)\n * - `csNavigationMenuLinkVariants` - 2차 메뉴 아이템 (item variant)\n *\n * ## CSS Variables (NavigationMenu 토큰 참조)\n * ```css\n * --sidebar-width: 16rem (데스크탑)\n * --sidebar-width-icon: 3rem (접힌 상태)\n * --color-bg-surface-default (배경)\n * --color-bg-overlay-default (모바일 오버레이)\n * ```\n *\n * @example 기본 사용\n * ```tsx\n * <CsSidebarProvider>\n * <CsSidebar>\n * <CsSidebarContent>\n * <CsSidebarGroup>\n * <CsSidebarMenu>\n * <CsSidebarMenuItem>\n * <CsSidebarMenuButton>메뉴 1</CsSidebarMenuButton>\n * </CsSidebarMenuItem>\n * </CsSidebarMenu>\n * </CsSidebarGroup>\n * </CsSidebarContent>\n * </CsSidebar>\n * <CsSidebarInset>\n * <main>메인 컨텐츠</main>\n * </CsSidebarInset>\n * </CsSidebarProvider>\n * ```\n *\n * @example 접히는 사이드바 (아이콘만 표시)\n * ```tsx\n * <CsSidebar collapsible=\"icon\">...</CsSidebar>\n * ```\n */\nconst SIDEBAR_COOKIE_NAME = \"sidebar_state\"\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7\nconst SIDEBAR_WIDTH = \"16rem\"\nconst SIDEBAR_WIDTH_MOBILE = \"18rem\"\nconst SIDEBAR_WIDTH_ICON = \"3rem\"\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\"\n\ntype SidebarVariant = \"solid-ghost\" | \"bottom-border\" | \"gradient\"\n\nconst SidebarGroupContext = React.createContext<boolean>(false)\n\ntype SidebarContextProps = {\n state: \"expanded\" | \"collapsed\"\n open: boolean\n setOpen: (open: boolean) => void\n openMobile: boolean\n setOpenMobile: (open: boolean) => void\n isMobile: boolean\n toggleSidebar: () => void\n variant: SidebarVariant\n}\n\nconst SidebarContext = React.createContext<SidebarContextProps | null>(null)\n\nfunction useSidebar() {\n const context = React.useContext(SidebarContext)\n if (!context) {\n throw new Error(\"useSidebar must be used within a SidebarProvider.\")\n }\n\n return context\n}\n\n/**\n * Sidebar Context Provider\n *\n * @param defaultOpen - 초기 열림 상태. 기본값: true\n * @param open - 제어 모드용 열림 상태\n * @param onOpenChange - 상태 변경 콜백\n * @param variant - 메뉴 버튼 스타일 (solid-ghost | bottom-border | gradient)\n */\nfunction CsSidebarProvider({\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n variant = \"solid-ghost\",\n className,\n style,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n defaultOpen?: boolean\n open?: boolean\n onOpenChange?: (open: boolean) => void\n variant?: SidebarVariant\n}) {\n const isMobile = useIsMobile()\n const [openMobile, setOpenMobile] = React.useState(false)\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = React.useState(defaultOpen)\n const open = openProp ?? _open\n const setOpen = React.useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === \"function\" ? value(open) : value\n if (setOpenProp) {\n setOpenProp(openState)\n } else {\n _setOpen(openState)\n }\n\n // This sets the cookie to keep the sidebar state.\n document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`\n },\n [setOpenProp, open]\n )\n\n // Helper to toggle the sidebar.\n const toggleSidebar = React.useCallback(() => {\n return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)\n }, [isMobile, setOpen, setOpenMobile])\n\n // Adds a keyboard shortcut to toggle the sidebar.\n React.useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (\n event.key === SIDEBAR_KEYBOARD_SHORTCUT &&\n (event.metaKey || event.ctrlKey)\n ) {\n event.preventDefault()\n toggleSidebar()\n }\n }\n\n window.addEventListener(\"keydown\", handleKeyDown)\n return () => window.removeEventListener(\"keydown\", handleKeyDown)\n }, [toggleSidebar])\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? \"expanded\" : \"collapsed\"\n\n const contextValue = React.useMemo<SidebarContextProps>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n variant,\n }),\n [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar, variant]\n )\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <CsTooltipProvider delayDuration={0}>\n <div\n data-slot=\"sidebar-wrapper\"\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH,\n \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n ...style,\n } as React.CSSProperties\n }\n className={cn(\n \"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n </CsTooltipProvider>\n </SidebarContext.Provider>\n )\n}\n\n/**\n * Sidebar 컨테이너\n *\n * @param side - 사이드바 위치 (left | right). 기본값: left\n * @param variant - 레이아웃 스타일 (sidebar | floating | inset)\n * @param collapsible - 접힘 모드 (offcanvas | icon | none)\n */\nfunction CsSidebar({\n side = \"left\",\n variant = \"sidebar\",\n collapsible = \"offcanvas\",\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & {\n side?: \"left\" | \"right\"\n variant?: \"sidebar\" | \"floating\" | \"inset\"\n collapsible?: \"offcanvas\" | \"icon\" | \"none\"\n}) {\n const { isMobile, state, openMobile, setOpenMobile } = useSidebar()\n\n if (collapsible === \"none\") {\n return (\n <div\n data-slot=\"sidebar\"\n className={cn(\n \"flex h-full w-(--sidebar-width) flex-col\",\n className\n )}\n {...props}\n >\n {children}\n </div>\n )\n }\n\n if (isMobile) {\n return (\n <CsSheet open={openMobile} onOpenChange={setOpenMobile} {...props}>\n <CsSheetContent\n data-sidebar=\"sidebar\"\n data-slot=\"sidebar\"\n data-mobile=\"true\"\n className=\"bg-(--color-bg-overlay-default) w-(--sidebar-width) p-0 [&>button]:hidden\"\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH_MOBILE,\n } as React.CSSProperties\n }\n side={side}\n >\n <CsSheetHeader className=\"sr-only\">\n <CsSheetTitle>Sidebar</CsSheetTitle>\n <CsSheetDescription>Displays the mobile sidebar.</CsSheetDescription>\n </CsSheetHeader>\n <div className=\"flex h-full w-full flex-col p-layout-md gap-layout-md bg-(--color-bg-surface-default) boxshadow-lg\">{children}</div>\n </CsSheetContent>\n </CsSheet>\n )\n }\n\n return (\n <div\n className=\"group peer text-sidebar-foreground hidden mobile:block\"\n data-state={state}\n data-collapsible={state === \"collapsed\" ? collapsible : \"\"}\n data-variant={variant}\n data-side={side}\n data-slot=\"sidebar\"\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n data-slot=\"sidebar-gap\"\n className={cn(\n \"relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear\",\n \"group-data-[collapsible=offcanvas]:w-0\",\n \"group-data-[side=right]:rotate-180\",\n variant === \"floating\" || variant === \"inset\"\n ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]\"\n : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon)\"\n )}\n />\n <div\n data-slot=\"sidebar-container\"\n className={cn(\n \"fixed shadow-none inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear mobile:flex\",\n side === \"left\"\n ? \"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]\"\n : \"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]\",\n variant === \"floating\" || variant === \"inset\"\n ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]\"\n : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon)\",\n className\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n data-slot=\"sidebar-inner\"\n className=\"bg-(--color-bg-surface-default) flex h-full w-full flex-col p-layout-md gap-layout-md boxshadow-lg\"\n >\n {children}\n </div>\n </div>\n </div>\n )\n}\n\n/** 사이드바 토글 버튼 */\nfunction CsSidebarTrigger({\n className,\n onClick,\n children,\n ...props\n}: React.ComponentProps<typeof CsButton>) {\n const { toggleSidebar } = useSidebar()\n\n return (\n <CsButton\n data-sidebar=\"trigger\"\n data-slot=\"sidebar-trigger\"\n variant=\"ghost\"\n className={className}\n onClick={(event) => {\n onClick?.(event)\n toggleSidebar()\n }}\n {...props}\n >\n {children ?? <PanelLeftIcon />}\n <span className=\"sr-only\">Toggle Sidebar</span>\n </CsButton>\n )\n}\n\n/** 사이드바 레일 (드래그로 토글) */\nfunction CsSidebarRail({ className, ...props }: React.ComponentProps<\"button\">) {\n const { toggleSidebar } = useSidebar()\n\n return (\n <button\n data-sidebar=\"rail\"\n data-slot=\"sidebar-rail\"\n aria-label=\"Toggle Sidebar\"\n tabIndex={-1}\n onClick={toggleSidebar}\n title=\"Toggle Sidebar\"\n className={cn(\n \"hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] mobile:flex\",\n \"in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize\",\n \"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize\",\n \"hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full\",\n \"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2\",\n \"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2\",\n className\n )}\n {...props}\n />\n )\n}\n\n/** 사이드바 옆 메인 콘텐츠 영역 */\nfunction CsSidebarInset({ className, ...props }: React.ComponentProps<\"main\">) {\n return (\n <main\n data-slot=\"sidebar-inset\"\n className={cn(\n \"bg-background relative flex w-full flex-1 flex-col\",\n \"mobile:peer-data-[variant=inset]:m-2 mobile:peer-data-[variant=inset]:ml-0 mobile:peer-data-[variant=inset]:rounded-xl mobile:peer-data-[variant=inset]:shadow-sm mobile:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2\",\n className\n )}\n {...props}\n />\n )\n}\n\n/** 사이드바 내 검색 입력 필드 */\nfunction CsSidebarInput({\n className,\n ...props\n}: React.ComponentProps<typeof CsInput>) {\n return (\n <CsInput\n data-slot=\"sidebar-input\"\n data-sidebar=\"input\"\n className={cn(\"bg-background h-8 w-full shadow-none\", className)}\n {...props}\n />\n )\n}\n\n/** 사이드바 상단 헤더 영역 */\nfunction CsSidebarHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sidebar-header\"\n data-sidebar=\"header\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n )\n}\n\n/** 사이드바 하단 푸터 영역 */\nfunction CsSidebarFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sidebar-footer\"\n data-sidebar=\"footer\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n )\n}\n\n/** 사이드바 구분선 */\nfunction CsSidebarSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof CsSeparator>) {\n return (\n <CsSeparator\n data-slot=\"sidebar-separator\"\n data-sidebar=\"separator\"\n className={cn(\"bg-sidebar-border mx-2 w-auto\", className)}\n {...props}\n />\n )\n}\n\n/** 사이드바 스크롤 가능한 메인 콘텐츠 */\nfunction CsSidebarContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sidebar-content\"\n data-sidebar=\"content\"\n className={cn(\n \"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden\",\n className\n )}\n {...props}\n />\n )\n}\n\n/** 메뉴 아이템 그룹 컨테이너 */\nfunction CsSidebarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sidebar-group\"\n data-sidebar=\"group\"\n className={cn(\"relative flex w-full min-w-0 flex-col\", className)}\n {...props}\n />\n )\n}\n\n/** 접을 수 있는 메뉴 그룹 (Collapsible 포함) */\nfunction CsSidebarGroupCollapsible({\n title,\n icon,\n children,\n className,\n variant = \"solid-ghost\",\n ...props\n}: {\n title: string\n icon?: React.ReactNode\n children: React.ReactNode\n className?: string\n variant?: \"solid-ghost\" | \"bottom-border\" | \"gradient\"\n}) {\n return (\n <CsCollapsible className=\"group/collapsible\">\n <CsSidebarGroup className={cn(\"gap-layout-sm\", className)} {...props}>\n <CsCollapsibleTrigger\n className={cn(\n csNavigationMenuTriggerStyle({ variant }),\n \"w-full justify-start\",\n className\n )}\n >\n {icon}\n {title}\n <ChevronDownIcon className=\"ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-180\" />\n </CsCollapsibleTrigger>\n <CsCollapsibleContent>\n <CsSidebarGroupContent className=\"flex flex-col gap-layout-sm\">\n {children}\n </CsSidebarGroupContent>\n </CsCollapsibleContent>\n </CsSidebarGroup>\n </CsCollapsible>\n )\n}\n\n/** 그룹 제목 라벨 */\nfunction CsSidebarGroupLabel({\n className,\n asChild = false,\n ...props\n}: React.ComponentProps<\"div\"> & { asChild?: boolean }) {\n const Comp = asChild ? Slot : \"div\"\n\n return (\n <Comp\n data-slot=\"sidebar-group-label\"\n data-sidebar=\"group-label\"\n className={cn(\n \"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0\",\n className\n )}\n {...props}\n />\n )\n}\n\n/** 그룹 우측 액션 버튼 */\nfunction CsSidebarGroupAction({\n className,\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> & { asChild?: boolean }) {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"sidebar-group-action\"\n data-sidebar=\"group-action\"\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 mobile:after:hidden\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n )\n}\n\n/** 그룹 내부 콘텐츠 영역 */\nfunction CsSidebarGroupContent({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n <SidebarGroupContext.Provider value={true}>\n <div\n data-slot=\"sidebar-group-content\"\n data-sidebar=\"group-content\"\n className={cn(\"w-full px-layout-sm\", className)}\n {...props}\n />\n </SidebarGroupContext.Provider>\n )\n}\n\n/** 메뉴 리스트 컨테이너 */\nfunction CsSidebarMenu({ className, ...props }: React.ComponentProps<\"ul\">) {\n return (\n <ul\n data-slot=\"sidebar-menu\"\n data-sidebar=\"menu\"\n className={cn(\"flex w-full min-w-0 flex-col gap-1\", className)}\n {...props}\n />\n )\n}\n\n/** 개별 메뉴 아이템 */\nfunction CsSidebarMenuItem({ className, ...props }: React.ComponentProps<\"li\">) {\n return (\n <li\n data-slot=\"sidebar-menu-item\"\n data-sidebar=\"menu-item\"\n className={cn(\"group/menu-item relative\", className)}\n {...props}\n />\n )\n}\n\nconst SIDEBAR_MENU_BUTTON_BASE =\n \"peer/menu-button w-full justify-start overflow-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate\"\n\n/**\n * 사이드바 메뉴 버튼\n *\n * NavigationMenu 스타일 시스템 재사용:\n * - 1차 메뉴 (Group 외부): `csNavigationMenuTriggerStyle` 적용\n * - 2차 메뉴 (Group 내부): `csNavigationMenuLinkVariants({ variant: \"item\" })` 적용\n *\n * 접힌 상태(collapsible=\"icon\")에서 tooltip 지원.\n *\n * @param asChild - Slot 패턴 사용 여부\n * @param isActive - 활성 상태\n * @param tooltip - 접힌 상태에서 표시할 툴팁\n */\nfunction CsSidebarMenuButton({\n asChild = false,\n isActive = false,\n tooltip,\n className,\n onClick,\n ...props\n}: React.ComponentProps<\"button\"> & {\n asChild?: boolean\n isActive?: boolean\n tooltip?: string | React.ComponentProps<typeof CsTooltipContent>\n}) {\n const Comp = asChild ? Slot : \"button\"\n const { isMobile, state, variant, setOpenMobile } = useSidebar()\n const isInsideGroup = React.useContext(SidebarGroupContext)\n\n // 모바일에서 메뉴 클릭 시 사이드바 닫기\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n if (isMobile) {\n setOpenMobile(false)\n }\n onClick?.(e)\n }\n\n const button = (\n <Comp\n data-slot=\"sidebar-menu-button\"\n data-sidebar=\"menu-button\"\n data-active={isActive}\n className={cn(\n SIDEBAR_MENU_BUTTON_BASE,\n isInsideGroup\n ? csNavigationMenuLinkVariants({ variant: \"item\" })\n : csNavigationMenuTriggerStyle({ variant }),\n \"w-full justify-start\",\n isActive && isInsideGroup && \"bg-(--navigation-item-hover-bg) text-(--navigation-item-hover-font)\",\n className\n )}\n onClick={handleClick}\n {...props}\n />\n )\n\n if (!tooltip) {\n return button\n }\n\n if (typeof tooltip === \"string\") {\n tooltip = {\n children: tooltip,\n }\n }\n\n return (\n <CsTooltip>\n <CsTooltipTrigger asChild>{button}</CsTooltipTrigger>\n <CsTooltipContent\n side=\"right\"\n align=\"center\"\n hidden={state !== \"collapsed\" || isMobile}\n {...tooltip}\n />\n </CsTooltip>\n )\n}\n\n/** 메뉴 아이템 우측 액션 버튼 */\nfunction CsSidebarMenuAction({\n className,\n asChild = false,\n showOnHover = false,\n ...props\n}: React.ComponentProps<\"button\"> & {\n asChild?: boolean\n showOnHover?: boolean\n}) {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"sidebar-menu-action\"\n data-sidebar=\"menu-action\"\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"after:absolute after:-inset-2 mobile:after:hidden\",\n \"group-data-[collapsible=icon]:hidden\",\n showOnHover &&\n \"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 mobile:opacity-0\",\n className\n )}\n {...props}\n />\n )\n}\n\n/** 메뉴 아이템 뱃지 (알림 카운트 등) */\nfunction CsSidebarMenuBadge({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sidebar-menu-badge\"\n data-sidebar=\"menu-badge\"\n className={cn(\n \"text-sidebar-foreground pointer-events-none absolute top-1.5 right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none\",\n \"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n )\n}\n\n/** 메뉴 로딩 스켈레톤 */\nfunction CsSidebarMenuSkeleton({\n className,\n showIcon = false,\n ...props\n}: React.ComponentProps<\"div\"> & {\n showIcon?: boolean\n}) {\n // Random width between 50 to 90%.\n const width = React.useMemo(() => {\n return `${Math.floor(Math.random() * 40) + 50}%`\n }, [])\n\n return (\n <div\n data-slot=\"sidebar-menu-skeleton\"\n data-sidebar=\"menu-skeleton\"\n className={cn(\"flex h-8 items-center gap-2 rounded-md px-2\", className)}\n {...props}\n >\n {showIcon && (\n <CsSkeleton\n className=\"size-4 rounded-md\"\n data-sidebar=\"menu-skeleton-icon\"\n />\n )}\n <CsSkeleton\n className=\"h-4 max-w-(--skeleton-width) flex-1\"\n data-sidebar=\"menu-skeleton-text\"\n style={\n {\n \"--skeleton-width\": width,\n } as React.CSSProperties\n }\n />\n </div>\n )\n}\n\n/** 하위 메뉴 리스트 */\nfunction CsSidebarMenuSub({ className, ...props }: React.ComponentProps<\"ul\">) {\n return (\n <ul\n data-slot=\"sidebar-menu-sub\"\n data-sidebar=\"menu-sub\"\n className={cn(\n \"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n )\n}\n\n/** 하위 메뉴 개별 아이템 */\nfunction CsSidebarMenuSubItem({\n className,\n ...props\n}: React.ComponentProps<\"li\">) {\n return (\n <li\n data-slot=\"sidebar-menu-sub-item\"\n data-sidebar=\"menu-sub-item\"\n className={cn(\"group/menu-sub-item relative\", className)}\n {...props}\n />\n )\n}\n\n/** 하위 메뉴 버튼 (링크) */\nfunction CsSidebarMenuSubButton({\n asChild = false,\n size = \"md\",\n isActive = false,\n className,\n ...props\n}: React.ComponentProps<\"a\"> & {\n asChild?: boolean\n size?: \"sm\" | \"md\"\n isActive?: boolean\n}) {\n const Comp = asChild ? Slot : \"a\"\n\n return (\n <Comp\n data-slot=\"sidebar-menu-sub-button\"\n data-sidebar=\"menu-sub-button\"\n data-size={size}\n data-active={isActive}\n className={cn(\n \"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n \"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground\",\n size === \"sm\" && \"text-xs\",\n size === \"md\" && \"text-sm\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n CsSidebar,\n CsSidebarContent,\n CsSidebarFooter,\n CsSidebarGroup,\n CsSidebarGroupAction,\n CsSidebarGroupCollapsible,\n CsSidebarGroupContent,\n CsSidebarGroupLabel,\n CsSidebarHeader,\n CsSidebarInput,\n CsSidebarInset,\n CsSidebarMenu,\n CsSidebarMenuAction,\n CsSidebarMenuBadge,\n CsSidebarMenuButton,\n CsSidebarMenuItem,\n CsSidebarMenuSkeleton,\n CsSidebarMenuSub,\n CsSidebarMenuSubButton,\n CsSidebarMenuSubItem,\n CsSidebarProvider,\n CsSidebarRail,\n CsSidebarSeparator,\n CsSidebarTrigger,\n useSidebar,\n type SidebarVariant,\n}\n"],"names":["open"],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
const csSkeletonVariants = cva(
|
|
5
|
+
"bg-(--skeleton-fill) animate-pulse",
|
|
6
|
+
{
|
|
7
|
+
variants: {
|
|
8
|
+
radius: {
|
|
9
|
+
default: "rounded-(--skeleton-radius)",
|
|
10
|
+
max: "rounded-(--skeleton-radius-full)"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
defaultVariants: {
|
|
14
|
+
radius: "default"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
function CsSkeleton({ className, radius, ...props }) {
|
|
19
|
+
return /* @__PURE__ */ jsx(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
"data-slot": "skeleton",
|
|
23
|
+
className: cn(csSkeletonVariants({ radius }), className),
|
|
24
|
+
...props
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
CsSkeleton,
|
|
30
|
+
csSkeletonVariants
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=cs-skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs-skeleton.js","sources":["../../../src/components/skeleton/cs-skeleton.tsx"],"sourcesContent":["import { cva } from \"class-variance-authority\"\nimport { cn } from \"../../lib/utils\"\n\n/**\n * CsSkeleton 스타일 시스템 (Design Token Reference)\n *\n * ## CSS Variables\n * ```css\n * --skeleton-fill | radius | radius-full\n * ```\n */\nconst csSkeletonVariants = cva(\n \"bg-(--skeleton-fill) animate-pulse\",\n {\n variants: {\n radius: {\n default: \"rounded-(--skeleton-radius)\",\n max: \"rounded-(--skeleton-radius-full)\",\n }\n },\n defaultVariants: {\n radius: \"default\",\n }\n }\n)\n\n/**\n * CsSkeleton Props\n */\ntype CsSkeletonProps = React.ComponentProps<\"div\"> & {\n /**\n * 모서리 둥글기\n * - `default`: 기본 radius - 텍스트, 카드 등\n * - `max`: 완전 둥글게 - 아바타, 원형 요소\n * @default 'default'\n */\n radius?: 'default' | 'max'\n}\n\n/**\n * CS Design System 스켈레톤 컴포넌트\n *\n * 콘텐츠 로딩 중 표시할 플레이스홀더. pulse 애니메이션 적용.\n * 실제 콘텐츠와 유사한 형태로 배치하여 로딩 경험을 개선합니다.\n * className으로 크기와 형태를 지정해야 합니다.\n *\n * ## 사용 시나리오\n * - 텍스트 로딩: `className=\"h-4 w-full\"` - 제목, 본문 라인 표시\n * - 카드 로딩: 여러 스켈레톤 조합 - 이미지 + 제목 + 설명 영역\n * - 리스트 로딩: 반복된 스켈레톤 - 목록 아이템 형태\n * - 아바타 로딩: `radius=\"max\"` + `className=\"size-10\"` - 원형 프로필\n * - 테이블 로딩: 행/열 구조로 배치 - 데이터 테이블 형태\n * - 이미지 로딩: `className=\"h-40 w-full\"` - 썸네일, 배너 영역\n *\n * ## 유사 컴포넌트와의 차이\n * - **CsSpinner**: 불확정적 로딩 - 회전 아이콘\n * - Skeleton은 콘텐츠 형태 표시, Spinner는 로딩 중 표시만\n *\n * @example 텍스트 라인\n * ```tsx\n * <CsSkeleton className=\"h-4 w-full\" />\n * <CsSkeleton className=\"h-4 w-3/4\" />\n * ```\n *\n * @example 원형 (아바타)\n * ```tsx\n * <CsSkeleton radius=\"max\" className=\"size-10\" />\n * ```\n *\n * @example 카드 스켈레톤\n * ```tsx\n * <CsCard>\n * <CsSkeleton className=\"h-40 w-full\" />\n * <CsSkeleton className=\"h-4 w-2/3 mt-4\" />\n * <CsSkeleton className=\"h-4 w-1/2 mt-2\" />\n * </CsCard>\n * ```\n */\nfunction CsSkeleton({ className, radius, ...props }: CsSkeletonProps) {\n return (\n <div\n data-slot=\"skeleton\"\n className={cn(csSkeletonVariants({ radius }), className)}\n {...props}\n />\n )\n}\n\nexport { CsSkeleton, csSkeletonVariants, type CsSkeletonProps }\n"],"names":[],"mappings":";;;AAWA,MAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,KAAK;AAAA,MAAA;AAAA,IACP;AAAA,IAEF,iBAAiB;AAAA,MACf,QAAQ;AAAA,IAAA;AAAA,EACV;AAEJ;AAsDA,SAAS,WAAW,EAAE,WAAW,QAAQ,GAAG,SAA0B;AACpE,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,mBAAmB,EAAE,OAAA,CAAQ,GAAG,SAAS;AAAA,MACtD,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { OctagonXIcon, TriangleAlertIcon, InfoIcon, CircleCheckIcon } from "lucide-react";
|
|
4
|
+
import { useTheme } from "next-themes";
|
|
5
|
+
import { Toaster } from "sonner";
|
|
6
|
+
import { cn } from "../../lib/utils.js";
|
|
7
|
+
import { CsSpinner } from "../spinner/cs-spinner.js";
|
|
8
|
+
const CsSonner = ({ className, ...props }) => {
|
|
9
|
+
const { theme = "system" } = useTheme();
|
|
10
|
+
return /* @__PURE__ */ jsx(
|
|
11
|
+
Toaster,
|
|
12
|
+
{
|
|
13
|
+
theme,
|
|
14
|
+
className: cn("toaster group", className),
|
|
15
|
+
icons: {
|
|
16
|
+
success: /* @__PURE__ */ jsx(CircleCheckIcon, { className: "size-4" }),
|
|
17
|
+
info: /* @__PURE__ */ jsx(InfoIcon, { className: "size-4" }),
|
|
18
|
+
warning: /* @__PURE__ */ jsx(TriangleAlertIcon, { className: "size-4" }),
|
|
19
|
+
error: /* @__PURE__ */ jsx(OctagonXIcon, { className: "size-4" }),
|
|
20
|
+
loading: /* @__PURE__ */ jsx(CsSpinner, {})
|
|
21
|
+
},
|
|
22
|
+
style: {
|
|
23
|
+
// Default toast
|
|
24
|
+
"--normal-bg": "var(--toast-common-bg)",
|
|
25
|
+
"--normal-text": "var(--toast-default-title)",
|
|
26
|
+
"--normal-border": "var(--toast-common-border)",
|
|
27
|
+
"--border-radius": "var(--toast-common-radius)",
|
|
28
|
+
// Success toast
|
|
29
|
+
"--success-bg": "var(--toast-success-bg)",
|
|
30
|
+
"--success-border": "var(--toast-success-border)",
|
|
31
|
+
"--success-text": "var(--toast-success-title)",
|
|
32
|
+
// Info toast
|
|
33
|
+
"--info-bg": "var(--toast-info-bg)",
|
|
34
|
+
"--info-border": "var(--toast-info-border)",
|
|
35
|
+
"--info-text": "var(--toast-info-title)",
|
|
36
|
+
// Warning toast
|
|
37
|
+
"--warning-bg": "var(--toast-warning-bg)",
|
|
38
|
+
"--warning-border": "var(--toast-warning-border)",
|
|
39
|
+
"--warning-text": "var(--toast-warning-title)",
|
|
40
|
+
// Error toast
|
|
41
|
+
"--error-bg": "var(--toast-error-bg)",
|
|
42
|
+
"--error-border": "var(--toast-error-border)",
|
|
43
|
+
"--error-text": "var(--toast-error-title)"
|
|
44
|
+
},
|
|
45
|
+
toastOptions: {
|
|
46
|
+
classNames: {
|
|
47
|
+
toast: cn(
|
|
48
|
+
"group/toast boxshadow-lg! px-(--toast-common-padding-x)! py-(--toast-common-padding-y)! gap-(--toast-common-gap)! border-(length:--toast-common-border-width)!"
|
|
49
|
+
),
|
|
50
|
+
icon: cn(
|
|
51
|
+
"flex items-center! justify-center! mx-0! size-(--toast-common-icon-size)! [&_svg]:size-(--toast-common-icon-size)!",
|
|
52
|
+
"[&_svg]:text-(--toast-default-icon)",
|
|
53
|
+
"group-data-[type=success]/toast:[&_svg]:text-(--toast-success-icon)!",
|
|
54
|
+
"group-data-[type=error]/toast:[&_svg]:text-(--toast-error-icon)!",
|
|
55
|
+
"group-data-[type=warning]/toast:[&_svg]:text-(--toast-warning-icon)!",
|
|
56
|
+
"group-data-[type=info]/toast:[&_svg]:text-(--toast-info-icon)!"
|
|
57
|
+
),
|
|
58
|
+
content: "typo-body-sm!",
|
|
59
|
+
description: cn(
|
|
60
|
+
"text-(--toast-default-description)!",
|
|
61
|
+
"group-data-[type=success]/toast:text-(--toast-success-description)!",
|
|
62
|
+
"group-data-[type=error]/toast:text-(--toast-error-description)!",
|
|
63
|
+
"group-data-[type=warning]/toast:text-(--toast-warning-description)!",
|
|
64
|
+
"group-data-[type=info]/toast:text-(--toast-info-description)!"
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
richColors: true,
|
|
69
|
+
...props
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
export {
|
|
74
|
+
CsSonner
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=cs-sonner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs-sonner.js","sources":["../../../src/components/sonner/cs-sonner.tsx"],"sourcesContent":["\"use client\"\n\nimport {\n CircleCheckIcon,\n InfoIcon,\n Loader2Icon,\n OctagonXIcon,\n TriangleAlertIcon,\n} from \"lucide-react\"\nimport { useTheme } from \"next-themes\"\nimport { Toaster as Sonner, type ToasterProps } from \"sonner\"\n\nimport { cn } from \"../../lib/utils\"\nimport { CsSpinner } from \"../spinner\"\n\n/**\n * CsSonner 스타일 시스템 (Design Token Reference)\n *\n * ## Figma Token → Props 매핑\n * | Figma 토큰 패턴 | toast 타입 |\n * |----------------|-----------|\n * | `toast/default/*` | `toast(\"메시지\")` |\n * | `toast/success/*` | `toast.success()` |\n * | `toast/error/*` | `toast.error()` |\n * | `toast/warning/*` | `toast.warning()` |\n * | `toast/info/*` | `toast.info()` |\n *\n * ## 토스트 타입별 토큰\n * | 타입 | Token prefix |\n * |------|--------------|\n * | default | toast-default-* |\n * | success | toast-success-* |\n * | error | toast-error-* |\n * | warning | toast-warning-* |\n * | info | toast-info-* |\n *\n * ## CSS Variables\n * ```css\n * --toast-common-bg | border | radius | padding-x | padding-y | gap | icon-size | border-width\n * --toast-{type}-bg | border | title | description | icon\n * ```\n *\n * @see {@link https://sonner.emilkowal.ski/ | Sonner}\n */\n\n/**\n * CsSonner Props\n */\ntype CsSonnerProps = ToasterProps\n\n/**\n * CS Design System 토스트 컴포넌트\n *\n * 사용자에게 짧은 알림 메시지를 표시하는 토스트 컴포넌트.\n * 성공, 오류, 경고, 정보 등 다양한 타입을 지원하며, 자동으로 사라집니다.\n * 앱 루트에 `<CsSonner />`를 한 번만 배치하고, `toast()` 함수로 호출합니다.\n * Sonner 라이브러리 기반.\n *\n * ## 사용 시나리오\n * - 성공 메시지: `toast.success(\"저장되었습니다\")` - 저장, 전송, 완료 알림\n * - 오류 알림: `toast.error(\"오류가 발생했습니다\")` - 실패, 에러, 경고\n * - 정보 표시: `toast.info(\"새 메시지가 있습니다\")` - 알림, 공지\n * - 경고 메시지: `toast.warning(\"권한이 필요합니다\")` - 주의, 확인 필요\n * - 로딩 알림: `toast.loading(\"처리 중...\")` - 진행 중 표시\n * - 기본 메시지: `toast(\"복사되었습니다\")` - 일반 알림\n *\n * ## 유사 컴포넌트와의 차이\n * - **CsAlertDialog**: 사용자 확인 필요 - 모달 다이얼로그\n * - Sonner는 알림만 표시하고 자동 사라짐, AlertDialog는 사용자 응답 필요\n *\n * @example 설정\n * ```tsx\n * // layout.tsx\n * <CsSonner />\n * ```\n *\n * @example 사용\n * ```tsx\n * import { toast } from \"sonner\"\n *\n * toast(\"기본 메시지\")\n * toast.success(\"성공!\")\n * toast.error(\"오류 발생\")\n * toast.info(\"정보\")\n * toast.warning(\"경고\")\n * toast.loading(\"로딩 중...\")\n * ```\n */\nconst CsSonner = ({ className, ...props }: ToasterProps) => {\n const { theme = \"system\" } = useTheme()\n\n return (\n <Sonner\n theme={theme as ToasterProps[\"theme\"]}\n className={cn(\"toaster group\", className)}\n icons={{\n success: <CircleCheckIcon className=\"size-4\" />,\n info: <InfoIcon className=\"size-4\" />,\n warning: <TriangleAlertIcon className=\"size-4\" />,\n error: <OctagonXIcon className=\"size-4\" />,\n loading: <CsSpinner />,\n }}\n style={\n {\n // Default toast\n \"--normal-bg\": \"var(--toast-common-bg)\",\n \"--normal-text\": \"var(--toast-default-title)\",\n \"--normal-border\": \"var(--toast-common-border)\",\n \"--border-radius\": \"var(--toast-common-radius)\",\n \n // Success toast\n \"--success-bg\": \"var(--toast-success-bg)\",\n \"--success-border\": \"var(--toast-success-border)\",\n \"--success-text\": \"var(--toast-success-title)\",\n \n // Info toast\n \"--info-bg\": \"var(--toast-info-bg)\",\n \"--info-border\": \"var(--toast-info-border)\",\n \"--info-text\": \"var(--toast-info-title)\",\n \n // Warning toast\n \"--warning-bg\": \"var(--toast-warning-bg)\",\n \"--warning-border\": \"var(--toast-warning-border)\",\n \"--warning-text\": \"var(--toast-warning-title)\",\n \n // Error toast\n \"--error-bg\": \"var(--toast-error-bg)\",\n \"--error-border\": \"var(--toast-error-border)\",\n \"--error-text\": \"var(--toast-error-title)\",\n } as React.CSSProperties\n }\n toastOptions={{\n classNames: {\n toast: cn(\n \"group/toast boxshadow-lg! px-(--toast-common-padding-x)! py-(--toast-common-padding-y)! gap-(--toast-common-gap)! border-(length:--toast-common-border-width)!\",\n ),\n icon: cn(\n \"flex items-center! justify-center! mx-0! size-(--toast-common-icon-size)! [&_svg]:size-(--toast-common-icon-size)!\",\n \"[&_svg]:text-(--toast-default-icon)\",\n \"group-data-[type=success]/toast:[&_svg]:text-(--toast-success-icon)!\",\n \"group-data-[type=error]/toast:[&_svg]:text-(--toast-error-icon)!\",\n \"group-data-[type=warning]/toast:[&_svg]:text-(--toast-warning-icon)!\",\n \"group-data-[type=info]/toast:[&_svg]:text-(--toast-info-icon)!\",\n ),\n content: \"typo-body-sm!\",\n description: cn(\n \"text-(--toast-default-description)!\",\n \"group-data-[type=success]/toast:text-(--toast-success-description)!\",\n \"group-data-[type=error]/toast:text-(--toast-error-description)!\",\n \"group-data-[type=warning]/toast:text-(--toast-warning-description)!\",\n \"group-data-[type=info]/toast:text-(--toast-info-description)!\",\n ),\n },\n }}\n richColors\n {...props}\n />\n )\n}\n\nexport { CsSonner, type CsSonnerProps }\n"],"names":[],"mappings":";;;;;;;AAyFE,MAAA,WAAQ,CAAQ,aAAa,SAAA,MAAS;AAEtC,2BACE,IAAA,SAAA;AAAA,SAAC;AAAA,IAAA;AAAA,IAAA;AAAA,MAEC;AAAA,MACA,WAAO,GAAA,iBAAA,SAAA;AAAA,MAAA,OACL;AAAA,QACA,SAAM,oBAAC,iBAAS,EAAA,WAAmB,SAAA,CAAA;AAAA,QACnC,MAAA,oBAAS,UAAC,EAAA,WAAA,SAAkB,CAAA;AAAA,QAC5B,SAAO,oBAAC,mBAAa,EAAA,WAAU,SAAS,CAAA;AAAA,QACxC,OAAA,kCAAU,EAAU,WAAA,SAAA,CAAA;AAAA,QACtB,SAAA,oBAAA,WAAA,CAAA,CAAA;AAAA,MACA;AAAA,MACE,OAAA;AAAA;AAAA,QAGE,eAAA;AAAA,QACA,iBAAA;AAAA,QACA,mBAAmB;AAAA,QAAA,mBAAA;AAAA;AAAA,QAInB,gBAAA;AAAA,QACA,oBAAkB;AAAA,QAAA,kBAAA;AAAA;AAAA,QAIlB,aAAA;AAAA,QACA,iBAAe;AAAA,QAAA,eAAA;AAAA;AAAA,QAIf,gBAAA;AAAA,QACA,oBAAkB;AAAA,QAAA,kBAAA;AAAA;AAAA,QAIlB,cAAA;AAAA,QACA,kBAAgB;AAAA,QAClB,gBAAA;AAAA,MAEF;AAAA,MAAc,cACA;AAAA,QAAA,YACH;AAAA,UAAA,OACL;AAAA,YACF;AAAA,UACA;AAAA,UAAM,MACJ;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACF;AAAA,UACA;AAAA,UACA,SAAA;AAAA,UAAa,aACX;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YAAA;AAAA,UACF;AAAA,QAEJ;AAAA,MACA;AAAA,MACC,YAAG;AAAA,MAAA,GAAA;AAAA,IACN;AAAA,EAEJ;;"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Loader2Icon } from "lucide-react";
|
|
3
|
+
import { cn } from "../../lib/utils.js";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
const csSpinnerVariants = cva(
|
|
6
|
+
"size-4 animate-spin",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
base: "text-(--spinner-fill-base)",
|
|
11
|
+
sub: "text-(--spinner-fill-sub)"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: {
|
|
15
|
+
variant: "base"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
function CsSpinner({ className, variant, ...props }) {
|
|
20
|
+
return /* @__PURE__ */ jsx(
|
|
21
|
+
Loader2Icon,
|
|
22
|
+
{
|
|
23
|
+
role: "status",
|
|
24
|
+
"aria-label": "Loading",
|
|
25
|
+
className: cn(csSpinnerVariants({ variant }), className),
|
|
26
|
+
...props
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
CsSpinner,
|
|
32
|
+
csSpinnerVariants
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=cs-spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs-spinner.js","sources":["../../../src/components/spinner/cs-spinner.tsx"],"sourcesContent":["import { Loader2Icon } from \"lucide-react\"\n\nimport { cn } from \"../../lib/utils\"\nimport { cva } from \"class-variance-authority\"\n\n/**\n * CsSpinner 스타일 시스템 (Design Token Reference)\n *\n * ## Figma Token → Props 매핑\n * | Figma 토큰 패턴 | Props |\n * |----------------|-------|\n * | `spinner/base` | `variant=\"base\"` (기본) |\n * | `spinner/sub` | `variant=\"sub\"` |\n *\n * ## CSS Variables\n * ```css\n * --spinner-fill-base\n * --spinner-fill-sub\n * ```\n */\nconst csSpinnerVariants = cva(\n \"size-4 animate-spin\",\n {\n variants: {\n variant: {\n base: \"text-(--spinner-fill-base)\",\n sub: \"text-(--spinner-fill-sub)\",\n }\n },\n defaultVariants: {\n variant: \"base\",\n }\n }\n)\n\n/**\n * CsSpinner Props\n */\ntype CsSpinnerProps = React.ComponentProps<\"svg\"> & {\n /**\n * 스피너 색상\n * - `base`: 기본 색상 - 일반 로딩 상태\n * - `sub`: 보조 색상 - 밝은 배경, 버튼 내부 등\n * @default 'base'\n */\n variant?: 'base' | 'sub'\n}\n\n/**\n * CS Design System 스피너 컴포넌트\n *\n * 불확정적 로딩 상태를 표시하는 회전 아이콘. Loader2Icon 기반.\n * 언제 완료될지 모르는 작업에 사용하며, aria-label=\"Loading\"이 자동 적용됩니다.\n * 버튼 내부, 전체 화면 로딩, 인라인 로딩 등 다양한 위치에 배치 가능합니다.\n *\n * ## 사용 시나리오\n * - 버튼 내부 로딩: `variant=\"sub\"` - 제출 중, 처리 중 버튼\n * - 전체 화면 로딩: `variant=\"base\"` - 페이지 로딩, 초기화 중\n * - 인라인 로딩: 작은 크기 - 데이터 로드 중, 검색 중 표시\n * - API 요청 중: 폼 제출, 데이터 저장 등 서버 응답 대기\n * - 무한 스크롤: 다음 페이지 로드 중 표시\n * - 초기 로딩: 앱 시작, 인증 확인 등\n *\n * ## 유사 컴포넌트와의 차이\n * - **CsProgress**: 정량적 진행률 (0-100%) - 프로그레스 바\n * - **CsSkeleton**: 콘텐츠 플레이스홀더 - 로딩될 영역 형태 표시\n * - Spinner는 불확정적 로딩, Progress는 진행률, Skeleton은 구조 표시\n *\n * @example 기본\n * ```tsx\n * <CsSpinner />\n * ```\n *\n * @example sub 스타일 (밝은 배경용)\n * ```tsx\n * <CsSpinner variant=\"sub\" />\n * ```\n *\n * @example 버튼 내부\n * ```tsx\n * <CsButton disabled>\n * <CsSpinner variant=\"sub\" /> 처리 중...\n * </CsButton>\n * ```\n */\nfunction CsSpinner({ className, variant, ...props }: CsSpinnerProps) {\n return (\n <Loader2Icon\n role=\"status\"\n aria-label=\"Loading\"\n className={cn(csSpinnerVariants({ variant }), className)}\n {...props}\n />\n )\n}\n\nexport { CsSpinner, csSpinnerVariants, type CsSpinnerProps }\n"],"names":[],"mappings":";;;;AAoBA,MAAM,oBAAoB;AAAA,EACxB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,MAAM;AAAA,QACN,KAAK;AAAA,MAAA;AAAA,IACP;AAAA,IAEF,iBAAiB;AAAA,MACf,SAAS;AAAA,IAAA;AAAA,EACX;AAEJ;AAoDA,SAAS,UAAU,EAAE,WAAW,SAAS,GAAG,SAAyB;AACnE,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAW;AAAA,MACX,WAAW,GAAG,kBAAkB,EAAE,QAAA,CAAS,GAAG,SAAS;AAAA,MACtD,GAAG;AAAA,IAAA;AAAA,EAAA;AAGV;"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as SwitchPrimitive from "@radix-ui/react-switch";
|
|
4
|
+
import { cn } from "../../lib/utils.js";
|
|
5
|
+
function CsSwitch({
|
|
6
|
+
className,
|
|
7
|
+
...props
|
|
8
|
+
}) {
|
|
9
|
+
return /* @__PURE__ */ jsx(
|
|
10
|
+
SwitchPrimitive.Root,
|
|
11
|
+
{
|
|
12
|
+
"data-slot": "switch",
|
|
13
|
+
className: cn(
|
|
14
|
+
"peer inline-flex shrink-0 items-center outline-none transition-all disabled:cursor-not-allowed disabled:opacity-(--opacity-state-disabled)",
|
|
15
|
+
"w-[calc(2rem+var(--switch-common-padding-x)*2)] h-[calc(1rem+var(--switch-common-padding-y)*2)]",
|
|
16
|
+
"px-(--switch-common-padding-x) py-(--switch-common-padding-y) rounded-(--switch-common-radius)",
|
|
17
|
+
"data-[state=unchecked]:bg-(--switch-off-bg) data-[state=checked]:bg-(--switch-on-bg)",
|
|
18
|
+
className
|
|
19
|
+
),
|
|
20
|
+
...props,
|
|
21
|
+
children: /* @__PURE__ */ jsx(
|
|
22
|
+
SwitchPrimitive.Thumb,
|
|
23
|
+
{
|
|
24
|
+
"data-slot": "switch-thumb",
|
|
25
|
+
className: cn(
|
|
26
|
+
"pointer-events-none block size-4 ring-0 transition-transform",
|
|
27
|
+
"rounded-(--switch-common-radius) data-[state=checked]:bg-(--switch-on-thumb) data-[state=unchecked]:bg-(--switch-off-thumb)",
|
|
28
|
+
"data-[state=checked]:translate-x-[1rem] data-[state=unchecked]:translate-x-0"
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
CsSwitch
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=cs-switch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs-switch.js","sources":["../../../src/components/switch/cs-switch.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport * as SwitchPrimitive from \"@radix-ui/react-switch\"\n\nimport { cn } from \"../../lib/utils\"\n\n/**\n * CsSwitch 스타일 시스템 (Design Token Reference)\n *\n * ## Figma Token → Props 매핑\n * | Figma 토큰 패턴 | 상태 |\n * |----------------|------|\n * | `switch/off/*` | `checked={false}` (기본) |\n * | `switch/on/*` | `checked={true}` |\n *\n * ## State 매핑\n * | UI 상태 | data 속성 | 스타일 변화 |\n * |---------|----------|------------|\n * | OFF | `data-state=\"unchecked\"` | switch-off-bg, switch-off-thumb |\n * | ON | `data-state=\"checked\"` | switch-on-bg, switch-on-thumb, translate-x |\n * | 비활성 | `disabled` | opacity 감소 |\n *\n * ## CSS Variables\n * ```css\n * --switch-common-padding-x | padding-y | radius\n * --switch-off-bg | thumb\n * --switch-on-bg | thumb\n * ```\n *\n * @see {@link https://www.radix-ui.com/primitives/docs/components/switch | Radix Switch}\n */\n\n/**\n * CsSwitch Props\n */\ntype CsSwitchProps = React.ComponentProps<typeof SwitchPrimitive.Root> & {\n /**\n * 스위치 ON/OFF 상태\n * controlled 모드에서 사용\n */\n checked?: boolean\n /**\n * 상태 변경 핸들러\n * @param checked - 변경된 상태\n */\n onCheckedChange?: (checked: boolean) => void\n /**\n * 비활성 상태\n * 상호작용 불가, opacity 감소\n */\n disabled?: boolean\n}\n\n/**\n * CS Design System 스위치 컴포넌트\n *\n * ON/OFF 상태를 즉시 전환하는 토글 스위치.\n * 설정 화면에서 기능을 켜거나 끄는 데 사용되며, 토글 즉시 변경사항이 반영됩니다.\n * Radix UI 기반입니다.\n *\n * ## 사용 시나리오\n * - 설정 토글: 알림 켜기/끄기, 다크모드, 자동 재생 등\n * - 기능 활성화: 실시간으로 반영되는 옵션 (2FA, 공개/비공개)\n * - 테이블 필터: 즉시 필터링되는 토글 옵션\n * - 대시보드 위젯: 위젯 표시/숨김 토글\n *\n * ## 유사 컴포넌트와의 차이\n * - **CsCheckbox**: 폼 제출 시 반영되는 선택 (약관 동의, 다중 선택)\n * - Switch는 토글 즉시 반영, Checkbox는 폼 제출 전까지 임시 상태\n * - **CsToggle**: 버튼 형태의 토글 (필터 칩, 포맷 변경 등)\n *\n * @example 기본\n * ```tsx\n * <CsSwitch checked={isOn} onCheckedChange={setIsOn} />\n * ```\n *\n * @example 라벨과 함께\n * ```tsx\n * <CsLabel className=\"flex items-center gap-2\">\n * <CsSwitch checked={enabled} onCheckedChange={setEnabled} />\n * 알림 받기\n * </CsLabel>\n * ```\n *\n * @example 비활성\n * ```tsx\n * <CsSwitch disabled checked={false} />\n * ```\n */\nfunction CsSwitch({\n className,\n ...props\n}: CsSwitchProps) {\n return (\n <SwitchPrimitive.Root\n data-slot=\"switch\"\n className={cn(\n \"peer inline-flex shrink-0 items-center outline-none transition-all disabled:cursor-not-allowed disabled:opacity-(--opacity-state-disabled)\",\n \"w-[calc(2rem+var(--switch-common-padding-x)*2)] h-[calc(1rem+var(--switch-common-padding-y)*2)]\",\n \"px-(--switch-common-padding-x) py-(--switch-common-padding-y) rounded-(--switch-common-radius)\",\n \"data-[state=unchecked]:bg-(--switch-off-bg) data-[state=checked]:bg-(--switch-on-bg)\",\n className\n )}\n {...props}\n >\n <SwitchPrimitive.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n \"pointer-events-none block size-4 ring-0 transition-transform\",\n \"rounded-(--switch-common-radius) data-[state=checked]:bg-(--switch-on-thumb) data-[state=unchecked]:bg-(--switch-off-thumb)\",\n \"data-[state=checked]:translate-x-[1rem] data-[state=unchecked]:translate-x-0\"\n )}\n />\n </SwitchPrimitive.Root>\n )\n}\n\nexport { CsSwitch, type CsSwitchProps }\n"],"names":[],"mappings":";;;;AA0FkB,SAChB,SAAA;AAAA,EACA;AAAA,EACF,GAAkB;AAChB;AACE,SAAiB;AAAA,IAAhB,gBAAA;AAAA,IAAA;AAAA,MAEC,aAAW;AAAA,MAAA,WACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACF;AAAA,MACC;AAAA,MAED,GAAA;AAAA,MAAA,UAAiB;AAAA,QAAhB,gBAAA;AAAA,QAAA;AAAA,UAEC,aAAW;AAAA,UAAA,WACT;AAAA,YACA;AAAA,YACA;AAAA,YAAA;AAAA,UACF;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EAEJ;;"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment } from "react";
|
|
3
|
+
import { getExpandedRowModel, getCoreRowModel, useReactTable, flexRender } from "@tanstack/react-table";
|
|
4
|
+
import { CsTable, CsTableHeader, CsTableBody, CsTableRow, CsTableCell, CsTableHead } from "./cs-table.js";
|
|
5
|
+
import { useInfiniteScroll } from "../../hooks/use-infinite-scroll.js";
|
|
6
|
+
import { CsSpinner } from "../spinner/cs-spinner.js";
|
|
7
|
+
import { CsScrollArea, CsScrollBar } from "../scroll-area/cs-scroll-area.js";
|
|
8
|
+
function CsDataBaseTable(props) {
|
|
9
|
+
const {
|
|
10
|
+
columns,
|
|
11
|
+
rows = [],
|
|
12
|
+
expandable,
|
|
13
|
+
subRowsName,
|
|
14
|
+
EmptyFallback,
|
|
15
|
+
hasNextPage,
|
|
16
|
+
fetchNextPage,
|
|
17
|
+
isFetchingNextPage,
|
|
18
|
+
...rest
|
|
19
|
+
} = props;
|
|
20
|
+
const initialColumnVisibility = columns.reduce(
|
|
21
|
+
(acc, cur) => {
|
|
22
|
+
if ("accessorKey" in cur) {
|
|
23
|
+
acc[cur.accessorKey] = cur.meta?.initialVisible ?? true;
|
|
24
|
+
}
|
|
25
|
+
if (cur.id) {
|
|
26
|
+
acc[cur.id] = cur.meta?.initialVisible ?? true;
|
|
27
|
+
}
|
|
28
|
+
return acc;
|
|
29
|
+
},
|
|
30
|
+
{}
|
|
31
|
+
);
|
|
32
|
+
const tableOption = {
|
|
33
|
+
getCoreRowModel: getCoreRowModel(),
|
|
34
|
+
getExpandedRowModel: expandable ? getExpandedRowModel() : void 0,
|
|
35
|
+
enableExpanding: expandable,
|
|
36
|
+
manualSorting: false,
|
|
37
|
+
columns,
|
|
38
|
+
data: rows,
|
|
39
|
+
getSubRows(originalRow) {
|
|
40
|
+
if (!subRowsName) return [];
|
|
41
|
+
return originalRow[subRowsName];
|
|
42
|
+
},
|
|
43
|
+
initialState: { columnVisibility: initialColumnVisibility }
|
|
44
|
+
};
|
|
45
|
+
const tanstackTable = useReactTable(tableOption);
|
|
46
|
+
const renderTableHead = () => {
|
|
47
|
+
return tanstackTable.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(CsTableRow, { children: headerGroup.headers.map((header) => {
|
|
48
|
+
const headerContent = flexRender(
|
|
49
|
+
header.column.columnDef.header,
|
|
50
|
+
header.getContext()
|
|
51
|
+
);
|
|
52
|
+
return /* @__PURE__ */ jsx(
|
|
53
|
+
CsTableHead,
|
|
54
|
+
{
|
|
55
|
+
colSpan: header.colSpan,
|
|
56
|
+
className: header.column.columnDef.meta?.thClassName,
|
|
57
|
+
style: {
|
|
58
|
+
width: header.column.columnDef.meta?.width?.toString(),
|
|
59
|
+
minWidth: header.column.columnDef.meta?.minWidth?.toString() ?? header.column.columnDef.meta?.width?.toString()
|
|
60
|
+
},
|
|
61
|
+
children: headerContent
|
|
62
|
+
},
|
|
63
|
+
header.id
|
|
64
|
+
);
|
|
65
|
+
}) }, headerGroup.id));
|
|
66
|
+
};
|
|
67
|
+
const renderTableBody = () => {
|
|
68
|
+
if (EmptyFallback && !tanstackTable.getRowModel().rows.length) {
|
|
69
|
+
return /* @__PURE__ */ jsx(CsTableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(CsTableCell, { colSpan: columns.length, children: EmptyFallback }) });
|
|
70
|
+
}
|
|
71
|
+
return tanstackTable.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(CsTableRow, { children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(
|
|
72
|
+
CsTableCell,
|
|
73
|
+
{
|
|
74
|
+
className: cell.column.columnDef.meta?.tdClassName,
|
|
75
|
+
children: flexRender(cell.column.columnDef.cell, cell.getContext())
|
|
76
|
+
},
|
|
77
|
+
cell.id
|
|
78
|
+
)) }) }, row.id));
|
|
79
|
+
};
|
|
80
|
+
const { scrollContainerRef } = useInfiniteScroll({
|
|
81
|
+
fetchMore: fetchNextPage ?? (() => {
|
|
82
|
+
}),
|
|
83
|
+
hasMore: Boolean(hasNextPage),
|
|
84
|
+
isFetching: Boolean(isFetchingNextPage),
|
|
85
|
+
threshold: 200
|
|
86
|
+
});
|
|
87
|
+
return /* @__PURE__ */ jsxs(CsScrollArea, { children: [
|
|
88
|
+
/* @__PURE__ */ jsxs(CsTable, { ...rest, children: [
|
|
89
|
+
/* @__PURE__ */ jsx(CsTableHeader, { children: renderTableHead() }),
|
|
90
|
+
/* @__PURE__ */ jsxs(CsTableBody, { children: [
|
|
91
|
+
renderTableBody(),
|
|
92
|
+
hasNextPage && /* @__PURE__ */ jsx(
|
|
93
|
+
CsTableRow,
|
|
94
|
+
{
|
|
95
|
+
ref: scrollContainerRef,
|
|
96
|
+
className: "px-(--table-item-common-padding-x) py-(--table-item-common-padding-y)",
|
|
97
|
+
children: /* @__PURE__ */ jsx(CsTableCell, { colSpan: columns.length, children: /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center", children: /* @__PURE__ */ jsx(CsSpinner, {}) }) })
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
] })
|
|
101
|
+
] }),
|
|
102
|
+
/* @__PURE__ */ jsx(CsScrollBar, { className: "sticky bottom-0", orientation: "horizontal" })
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
export {
|
|
106
|
+
CsDataBaseTable
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=cs-data-base-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs-data-base-table.js","sources":["../../../src/components/table/cs-data-base-table.tsx"],"sourcesContent":["\nimport { Fragment, type ReactNode, type ComponentType } from 'react'\nimport {\n getCoreRowModel,\n getExpandedRowModel,\n type VisibilityState,\n type TableOptions,\n useReactTable,\n flexRender,\n} from '@tanstack/react-table'\n\nimport {\n CsTable,\n CsTableBody,\n CsTableCell,\n CsTableHead,\n CsTableHeader,\n CsTableRow,\n} from './cs-table'\n\nimport { useInfiniteScroll } from '../../hooks/use-infinite-scroll'\nimport { CsSpinner } from '../spinner/cs-spinner'\nimport {\n TableColumnDef,\n TableHeader,\n CsTableMeta,\n} from '../../types/table.type'\nimport { CsScrollArea, CsScrollBar } from '../scroll-area/cs-scroll-area'\n\n/**\n * CsDataBaseTable Props\n */\ninterface CsDataBaseTableProps<T> {\n columns: TableColumnDef<T>[]\n rows?: T[]\n expandable?: boolean\n className?: string\n subRowsName?: string\n EmptyFallback?: ReactNode\n defaultHeaderComp?: ComponentType<{ column: any; children: React.ReactNode }>\n hasNextPage?: boolean\n fetchNextPage?: () => void\n isFetchingNextPage?: boolean\n}\n\n/**\n * CS Design System 데이터 테이블 기본 컴포넌트 (Headless)\n *\n * 로딩, 에러 상태를 내부에서 처리하지 않고 순수하게 데이터만 렌더링하는 컴포넌트입니다.\n * Suspense 패턴이나 외부에서 로딩을 제어해야 할 때 사용합니다.\n *\n * ## 주요 기능\n * - TanStack Table 기반 데이터 바인딩\n * - 무한 스크롤 (useInfiniteScroll)\n * - 계층 구조 확장 (expandable)\n *\n * @example\n * ```tsx\n * // Suspense와 함께 사용\n * <Suspense fallback={<CsSkeletonTable ... />}>\n * <CsDataBaseTable columns={columns} rows={data} ... />\n * </Suspense>\n * ```\n */\nfunction CsDataBaseTable<T>(props: CsDataBaseTableProps<T>) {\n const {\n columns,\n rows = [],\n expandable,\n subRowsName,\n EmptyFallback,\n hasNextPage,\n fetchNextPage,\n isFetchingNextPage,\n ...rest\n } = props\n\n const initialColumnVisibility = columns.reduce<VisibilityState>(\n (acc, cur) => {\n if ('accessorKey' in cur) {\n acc[cur.accessorKey as string] = cur.meta?.initialVisible ?? true\n }\n if (cur.id) {\n acc[cur.id] = cur.meta?.initialVisible ?? true\n }\n return acc\n },\n {}\n )\n\n const tableOption: TableOptions<T> = {\n getCoreRowModel: getCoreRowModel<T>(),\n getExpandedRowModel: expandable ? getExpandedRowModel<T>() : undefined,\n enableExpanding: expandable,\n manualSorting: false,\n columns,\n data: rows,\n getSubRows(originalRow: T) {\n if (!subRowsName) return []\n // @ts-ignore\n return (originalRow as any)[subRowsName]\n },\n initialState: { columnVisibility: initialColumnVisibility },\n }\n\n const tanstackTable = useReactTable<T>(tableOption)\n\n const renderTableHead = () => {\n return tanstackTable.getHeaderGroups().map((headerGroup) => (\n <CsTableRow key={headerGroup.id}>\n {headerGroup.headers.map((header: TableHeader<T>) => {\n const headerContent = flexRender(\n header.column.columnDef.header,\n header.getContext()\n )\n return (\n <CsTableHead\n key={header.id}\n colSpan={header.colSpan}\n className={header.column.columnDef.meta?.thClassName}\n style={{\n width: header.column.columnDef.meta?.width?.toString(),\n minWidth:\n header.column.columnDef.meta?.minWidth?.toString() ??\n header.column.columnDef.meta?.width?.toString(),\n }}\n >\n {headerContent}\n </CsTableHead>\n )\n })}\n </CsTableRow>\n ))\n }\n\n const renderTableBody = () => {\n if (EmptyFallback && !tanstackTable.getRowModel().rows.length) {\n return (\n <CsTableRow className=\"hover:bg-transparent\">\n <CsTableCell colSpan={columns.length}>{EmptyFallback}</CsTableCell>\n </CsTableRow>\n )\n }\n \n return tanstackTable.getRowModel().rows.map((row) => (\n <Fragment key={row.id}>\n <CsTableRow>\n {row.getVisibleCells().map((cell) => (\n <CsTableCell\n key={cell.id}\n className={\n (cell.column.columnDef.meta as CsTableMeta)?.tdClassName\n }\n >\n {flexRender(cell.column.columnDef.cell, cell.getContext())}\n </CsTableCell>\n ))}\n </CsTableRow>\n </Fragment>\n ))\n }\n\n const { scrollContainerRef } = useInfiniteScroll<HTMLTableRowElement>({\n fetchMore: fetchNextPage ?? (() => {}),\n hasMore: Boolean(hasNextPage),\n isFetching: Boolean(isFetchingNextPage),\n useWindowScroll: false,\n threshold: 200,\n })\n\n return (\n <CsScrollArea>\n <CsTable {...rest}>\n <CsTableHeader>{renderTableHead()}</CsTableHeader>\n <CsTableBody>\n {renderTableBody()}\n {hasNextPage && (\n <CsTableRow\n ref={scrollContainerRef}\n className=\"px-(--table-item-common-padding-x) py-(--table-item-common-padding-y)\"\n >\n <CsTableCell colSpan={columns.length}>\n <div className=\"flex w-full items-center justify-center\">\n <CsSpinner />\n </div>\n </CsTableCell>\n </CsTableRow>\n )}\n </CsTableBody>\n </CsTable>\n <CsScrollBar className=\"sticky bottom-0\" orientation=\"horizontal\" />\n </CsScrollArea>\n )\n}\n\nexport { CsDataBaseTable, type CsDataBaseTableProps }\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { CsSkeletonTable } from "./cs-skeleton-table.js";
|
|
3
|
+
import { CsTable, CsTableBody, CsTableRow, CsTableCell } from "./cs-table.js";
|
|
4
|
+
import { CsDataBaseTable } from "./cs-data-base-table.js";
|
|
5
|
+
function CsDataTable(props) {
|
|
6
|
+
const {
|
|
7
|
+
columns,
|
|
8
|
+
isLoading,
|
|
9
|
+
isError,
|
|
10
|
+
defaultRowSize = 25,
|
|
11
|
+
ErrorFallback,
|
|
12
|
+
...rest
|
|
13
|
+
} = props;
|
|
14
|
+
if (isLoading) {
|
|
15
|
+
return /* @__PURE__ */ jsx(
|
|
16
|
+
CsSkeletonTable,
|
|
17
|
+
{
|
|
18
|
+
columns,
|
|
19
|
+
rowCount: defaultRowSize,
|
|
20
|
+
className: props.className
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
if (isError && ErrorFallback) {
|
|
25
|
+
return /* @__PURE__ */ jsx(CsTable, { className: props.className, children: /* @__PURE__ */ jsx(CsTableBody, { children: /* @__PURE__ */ jsx(CsTableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(CsTableCell, { colSpan: columns.length, children: ErrorFallback }) }) }) });
|
|
26
|
+
}
|
|
27
|
+
return /* @__PURE__ */ jsx(CsDataBaseTable, { columns, ...rest });
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
CsDataTable
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=cs-data-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs-data-table.js","sources":["../../../src/components/table/cs-data-table.tsx"],"sourcesContent":["// \"use client\"\nimport { type ReactNode } from 'react'\nimport { CsSkeletonTable } from './cs-skeleton-table'\nimport { CsTable, CsTableBody, CsTableCell, CsTableRow } from './cs-table'\nimport { CsDataBaseTable, CsDataBaseTableProps } from './cs-data-base-table'\n\n/**\n * CsDataTable 스타일 시스템 (Design Token Reference)\n *\n * CsTable과 동일한 토큰 사용.\n * 상세한 토큰 매핑은 CsTable 문서 참조.\n *\n * ## 기능\n * | 기능 | Props |\n * |------|-------|\n * | 데이터 바인딩 | `columns`, `rows` |\n * | 로딩 상태 | `isLoading`, `defaultRowSize` |\n * | 무한 스크롤 | `hasNextPage`, `fetchNextPage`, `isFetchingNextPage` |\n * | 계층 구조 | `expandable`, `subRowsName` |\n * | 빈 상태 | `EmptyFallback` |\n * | 에러 상태 | `ErrorFallback` |\n *\n * ## CSS Variables\n * CsTable 컴포넌트의 토큰 사용\n * ```css\n * --table-item-common-padding-x | padding-y\n * ```\n *\n * @see CsTable - 토큰 매핑 상세 정보\n * @see {@link https://tanstack.com/table/latest | TanStack Table}\n */\n\n/**\n * CS Design System 데이터 테이블 컴포넌트\n *\n * CsTable 위에 데이터 바인딩, 로딩, 무한 스크롤 기능을 추가한 고급 테이블.\n * 정렬, 필터링, 페이지네이션, 계층 구조 등 복잡한 데이터 표시에 사용됩니다.\n * TanStack Table 기반으로 강력한 데이터 조작 기능을 제공합니다.\n *\n * ## 사용 시나리오\n * - 관리자 대시보드: 정렬/필터링이 필요한 대량 데이터 목록\n * - 무한 스크롤 리스트: 트랜잭션, 로그, 활동 내역 등\n * - 계층 구조 데이터: 폴더 트리, 조직도, 중첩 카테고리\n * - 로딩 상태 표시: 스켈레톤 UI로 데이터 로딩 중 표시\n * - 에러 핸들링: 데이터 로드 실패 시 ErrorFallback 표시\n * - 빈 상태: EmptyFallback으로 데이터 없음 표시\n *\n * ## 유사 컴포넌트와의 차이\n * - **CsTable**: 정적 마크업 테이블 - 단순 데이터 표시만\n * - CsDataTable은 TanStack Table + 무한 스크롤 + 로딩/에러 처리 포함\n *\n * @example 기본\n * ```tsx\n * <CsDataTable\n * columns={columns}\n * rows={data}\n * />\n * ```\n *\n * @example 로딩 상태\n * ```tsx\n * <CsDataTable\n * columns={columns}\n * rows={data}\n * isLoading={isLoading}\n * defaultRowSize={10}\n * />\n * ```\n *\n * @example 무한 스크롤\n * ```tsx\n * <CsDataTable\n * columns={columns}\n * rows={data}\n * hasNextPage={hasNextPage}\n * fetchNextPage={fetchNextPage}\n * isFetchingNextPage={isFetchingNextPage}\n * />\n * ```\n *\n * @example 계층 구조 (expandable)\n * ```tsx\n * <CsDataTable\n * columns={columns}\n * rows={treeData}\n * expandable\n * subRowsName=\"children\"\n * />\n * ```\n */\n/**\n * CsDataTable Props\n */\ninterface CsDataTableProps<T> extends CsDataBaseTableProps<T> {\n /**\n * 로딩 상태\n * true일 때 스켈레톤 표시\n * @default false\n */\n isLoading?: boolean\n /**\n * 에러 상태\n * true일 때 ErrorFallback 표시\n * @default false\n */\n isError?: boolean\n /**\n * 로딩 시 표시할 스켈레톤 행 개수\n * @default 25\n */\n defaultRowSize?: number\n /**\n * 에러 발생 시 표시할 컴포넌트\n */\n ErrorFallback?: ReactNode\n}\n\n/**\n * CS Design System 데이터 테이블 컴포넌트 (High-level Wrapper)\n *\n * CsDataBaseTable(데이터 렌더링)와 CsSkeletonTable(로딩 상태)을 조합하여\n * 로딩, 에러, 데이터 표시를 통합적으로 관리하는 컴포넌트입니다.\n *\n * 내부적으로 상태에 따라 로딩 스켈레톤이나 에러 UI를 표시합니다.\n * Suspense를 사용하지 않는 기존 페이지나 간단한 사용 사례에 적합합니다.\n *\n * ## 유사 컴포넌트 비교\n * - **CsDataTable**: 로딩/에러 상태 관리 포함 (All-in-one)\n * - **CsDataBaseTable**: 순수 데이터 렌더링 (Headless, Suspense용)\n * - **CsTable**: 저수준 마크업 테이블\n *\n * @example\n * ```tsx\n * <CsDataTable\n * isLoading={isLoading}\n * isError={isError}\n * columns={columns}\n * rows={data}\n * />\n * ```\n */\nfunction CsDataTable<T>(props: CsDataTableProps<T>) {\n const {\n columns,\n isLoading,\n isError,\n defaultRowSize = 25,\n ErrorFallback,\n ...rest\n } = props\n\n if (isLoading) {\n return (\n <CsSkeletonTable\n columns={columns}\n rowCount={defaultRowSize}\n className={props.className}\n />\n )\n }\n\n // Handle Error state specifically here or pass to Base?\n // Current implementation in Base doesn't handle ErrorFallback.\n // We can wrap Base with Error logic if needed, but previously it was inside renderTableBody.\n // To handle it here we might need to check IsError.\n\n if (isError && ErrorFallback) {\n return (\n <CsTable className={props.className}>\n <CsTableBody>\n <CsTableRow className=\"hover:bg-transparent\">\n <CsTableCell colSpan={columns.length}>{ErrorFallback}</CsTableCell>\n </CsTableRow>\n </CsTableBody>\n </CsTable>\n )\n }\n\n return <CsDataBaseTable columns={columns} {...rest} />\n}\n\nexport { CsDataTable, type CsDataTableProps }\n"],"names":[],"mappings":";;;;AA6IA,SAAS,YAAe,OAA4B;AAClD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,IACjB;AAAA,IACA,GAAG;AAAA,EAAA,IACD;AAEJ,MAAI,WAAW;AACb,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,UAAU;AAAA,QACV,WAAW,MAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAGvB;AAOA,MAAI,WAAW,eAAe;AAC5B,+BACG,SAAA,EAAQ,WAAW,MAAM,WACxB,UAAA,oBAAC,eACC,UAAA,oBAAC,YAAA,EAAW,WAAU,wBACpB,UAAA,oBAAC,eAAY,SAAS,QAAQ,QAAS,UAAA,cAAA,CAAc,EAAA,CACvD,GACF,EAAA,CACF;AAAA,EAEJ;AAEA,SAAO,oBAAC,iBAAA,EAAgB,SAAmB,GAAG,KAAA,CAAM;AACtD;"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { CsTable, CsTableHeader, CsTableRow, CsTableHead, CsTableBody, CsTableCell } from "./cs-table.js";
|
|
3
|
+
import { CsSkeleton } from "../skeleton/cs-skeleton.js";
|
|
4
|
+
import { useReactTable, getCoreRowModel, flexRender } from "@tanstack/react-table";
|
|
5
|
+
function CsSkeletonTable({
|
|
6
|
+
columns,
|
|
7
|
+
rowCount = 25,
|
|
8
|
+
className
|
|
9
|
+
}) {
|
|
10
|
+
const table = useReactTable({
|
|
11
|
+
data: [],
|
|
12
|
+
columns,
|
|
13
|
+
getCoreRowModel: getCoreRowModel()
|
|
14
|
+
});
|
|
15
|
+
return /* @__PURE__ */ jsxs(CsTable, { className, children: [
|
|
16
|
+
/* @__PURE__ */ jsx(CsTableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(CsTableRow, { children: headerGroup.headers.map((header) => {
|
|
17
|
+
const headerContent = flexRender(
|
|
18
|
+
header.column.columnDef.header,
|
|
19
|
+
header.getContext()
|
|
20
|
+
);
|
|
21
|
+
return /* @__PURE__ */ jsx(
|
|
22
|
+
CsTableHead,
|
|
23
|
+
{
|
|
24
|
+
colSpan: header.colSpan,
|
|
25
|
+
className: header.column.columnDef.meta?.thClassName,
|
|
26
|
+
style: {
|
|
27
|
+
width: header.column.columnDef.meta?.width?.toString(),
|
|
28
|
+
minWidth: header.column.columnDef.meta?.minWidth?.toString() ?? header.column.columnDef.meta?.width?.toString()
|
|
29
|
+
},
|
|
30
|
+
children: headerContent
|
|
31
|
+
},
|
|
32
|
+
header.id
|
|
33
|
+
);
|
|
34
|
+
}) }, headerGroup.id)) }),
|
|
35
|
+
/* @__PURE__ */ jsx(CsTableBody, { children: Array.from({ length: rowCount }).map((_, rowIndex) => /* @__PURE__ */ jsx(CsTableRow, { children: table.getHeaderGroups()[0].headers.map((header) => /* @__PURE__ */ jsx(CsTableCell, { children: /* @__PURE__ */ jsx(CsSkeleton, { className: "h-4 w-full" }) }, header.id)) }, rowIndex)) })
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
CsSkeletonTable
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=cs-skeleton-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cs-skeleton-table.js","sources":["../../../src/components/table/cs-skeleton-table.tsx"],"sourcesContent":["import {\n CsTable,\n CsTableBody,\n CsTableCell,\n CsTableHead,\n CsTableHeader,\n CsTableRow,\n} from './cs-table'\nimport { CsSkeleton } from '../skeleton/cs-skeleton'\nimport { TableColumnDef, TableHeader } from '../../types/table.type'\nimport { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'\n\n/**\n * CsSkeletonTable Props\n */\ninterface CsSkeletonTableProps<T> {\n columns: TableColumnDef<T>[]\n rowCount?: number\n className?: string\n}\n\n/**\n * CS Design System 스켈레톤 테이블 컴포넌트\n *\n * 데이터 로딩 중 테이블의 구조(헤더, 컬럼 폭)를 유지하면서\n * 로딩 인디케이터(스켈레톤)를 표시하는 컴포넌트입니다.\n *\n * CsDataTable 내부에서 사용되거나, Suspense의 fallback으로 사용됩니다.\n */\nfunction CsSkeletonTable<T>({\n columns,\n rowCount = 25,\n className,\n}: CsSkeletonTableProps<T>) {\n // Use useReactTable to render headers correctly based on column definitions\n // We provide empty data just to use the header generation logic\n const table = useReactTable({\n data: [],\n columns,\n getCoreRowModel: getCoreRowModel(),\n })\n\n return (\n <CsTable className={className}>\n <CsTableHeader>\n {table.getHeaderGroups().map((headerGroup) => (\n <CsTableRow key={headerGroup.id}>\n {headerGroup.headers.map((header: TableHeader<T>) => {\n const headerContent = flexRender(\n header.column.columnDef.header,\n header.getContext()\n )\n return (\n <CsTableHead\n key={header.id}\n colSpan={header.colSpan}\n className={header.column.columnDef.meta?.thClassName}\n style={{\n width: header.column.columnDef.meta?.width?.toString(),\n minWidth:\n header.column.columnDef.meta?.minWidth?.toString() ??\n header.column.columnDef.meta?.width?.toString(),\n }}\n >\n {headerContent}\n </CsTableHead>\n )\n })}\n </CsTableRow>\n ))}\n </CsTableHeader>\n <CsTableBody>\n {Array.from({ length: rowCount }).map((_, rowIndex) => (\n <CsTableRow key={rowIndex}>\n {table.getHeaderGroups()[0].headers.map((header) => (\n <CsTableCell key={header.id}>\n <CsSkeleton className=\"h-4 w-full\" />\n </CsTableCell>\n ))}\n </CsTableRow>\n ))}\n </CsTableBody>\n </CsTable>\n )\n}\n\nexport { CsSkeletonTable, type CsSkeletonTableProps }\n"],"names":[],"mappings":";;;;AA6BA,SAAS,gBAAmB;AAAA,EAC1B;AAAA,EACA,WAAW;AAAA,EACX;AACF,GAA4B;AAG1B,QAAM,QAAQ,cAAc;AAAA,IAC1B,MAAM,CAAA;AAAA,IACN;AAAA,IACA,iBAAiB,gBAAA;AAAA,EAAgB,CAClC;AAED,SACE,qBAAC,WAAQ,WACP,UAAA;AAAA,IAAA,oBAAC,eAAA,EACE,UAAA,MAAM,gBAAA,EAAkB,IAAI,CAAC,gBAC5B,oBAAC,YAAA,EACE,UAAA,YAAY,QAAQ,IAAI,CAAC,WAA2B;AACnD,YAAM,gBAAgB;AAAA,QACpB,OAAO,OAAO,UAAU;AAAA,QACxB,OAAO,WAAA;AAAA,MAAW;AAEpB,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UAEC,SAAS,OAAO;AAAA,UAChB,WAAW,OAAO,OAAO,UAAU,MAAM;AAAA,UACzC,OAAO;AAAA,YACL,OAAO,OAAO,OAAO,UAAU,MAAM,OAAO,SAAA;AAAA,YAC5C,UACE,OAAO,OAAO,UAAU,MAAM,UAAU,SAAA,KACxC,OAAO,OAAO,UAAU,MAAM,OAAO,SAAA;AAAA,UAAS;AAAA,UAGjD,UAAA;AAAA,QAAA;AAAA,QAVI,OAAO;AAAA,MAAA;AAAA,IAalB,CAAC,EAAA,GArBc,YAAY,EAsB7B,CACD,EAAA,CACH;AAAA,wBACC,aAAA,EACE,UAAA,MAAM,KAAK,EAAE,QAAQ,UAAU,EAAE,IAAI,CAAC,GAAG,aACxC,oBAAC,YAAA,EACE,gBAAM,kBAAkB,CAAC,EAAE,QAAQ,IAAI,CAAC,WACvC,oBAAC,eACC,UAAA,oBAAC,YAAA,EAAW,WAAU,aAAA,CAAa,KADnB,OAAO,EAEzB,CACD,EAAA,GALc,QAMjB,CACD,EAAA,CACH;AAAA,EAAA,GACF;AAEJ;"}
|