import { CheckCircleOutlined, ExclamationCircleOutlined, InboxOutlined, LoadingOutlined, } from '@ant-design/icons'; import { Card, Flex, Skeleton, Typography } from 'antd'; import type { CardProps } from 'antd'; import type { ReactNode } from 'react'; import './DataStatePanel.css'; const { Paragraph, Text, Title } = Typography; export type DataStatePanelStatus = 'loading' | 'empty' | 'error' | 'ready'; export type DataStatePanelProps = { state: DataStatePanelStatus; title?: ReactNode; description?: ReactNode; actions?: ReactNode; children?: ReactNode; compact?: boolean; icon?: ReactNode; loadingRows?: number; className?: string; cardProps?: Omit; }; function getDefaultIcon(state: DataStatePanelStatus) { switch (state) { case 'loading': return ; case 'empty': return ; case 'error': return ; case 'ready': return ; default: return null; } } function getDefaultTitle(state: DataStatePanelStatus) { switch (state) { case 'loading': return '데이터를 불러오는 중입니다.'; case 'empty': return '표시할 데이터가 없습니다.'; case 'error': return '데이터를 불러오지 못했습니다.'; case 'ready': return '데이터를 확인할 수 있습니다.'; default: return undefined; } } function getDefaultDescription(state: DataStatePanelStatus) { switch (state) { case 'loading': return '응답을 정리하는 동안 패널 레이아웃을 유지합니다.'; case 'empty': return '조건을 조정하거나 새 항목을 추가하면 이 영역이 채워집니다.'; case 'error': return '네트워크 또는 권한 상태를 확인한 뒤 다시 시도하세요.'; case 'ready': return undefined; default: return undefined; } } function getSkeletonWidths(compact: boolean, rows: number) { if (compact) { return Array.from({ length: rows }, (_, index) => (index === rows - 1 ? '84%' : '100%')); } return Array.from({ length: rows }, (_, index) => { if (index === rows - 1) { return '74%'; } return '100%'; }); } function renderLoadingBody(compact: boolean, rows: number) { return (
); } export function DataStatePanel({ state, title, description, actions, children, compact = false, icon, loadingRows = compact ? 2 : 3, className, cardProps, }: DataStatePanelProps) { const resolvedTitle = title ?? getDefaultTitle(state); const resolvedDescription = description ?? getDefaultDescription(state); const panelClassName = [ 'data-state-panel', `data-state-panel--${state}`, compact ? 'data-state-panel--compact' : '', className ?? '', ] .filter(Boolean) .join(' '); return (
{resolvedTitle ? ( compact ? ( {resolvedTitle} ) : ( {resolvedTitle} ) ) : null} {resolvedDescription ? ( {resolvedDescription} ) : null}
{state === 'loading' ? renderLoadingBody(compact, loadingRows) : children ? (
{children}
) : null} {actions ?
{actions}
: null}
); }