diff options
| author | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
| commit | 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch) | |
| tree | b9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /src/components/common/LoadingPanel.tsx | |
| download | umami-396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b.tar.xz umami-396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b.zip | |
Created from https://vercel.com/new
Diffstat (limited to 'src/components/common/LoadingPanel.tsx')
| -rw-r--r-- | src/components/common/LoadingPanel.tsx | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/src/components/common/LoadingPanel.tsx b/src/components/common/LoadingPanel.tsx new file mode 100644 index 0000000..fb37e14 --- /dev/null +++ b/src/components/common/LoadingPanel.tsx @@ -0,0 +1,71 @@ +import { Column, type ColumnProps, Loading } from '@umami/react-zen'; +import type { ReactNode } from 'react'; +import { Empty } from '@/components/common/Empty'; +import { ErrorMessage } from '@/components/common/ErrorMessage'; + +export interface LoadingPanelProps extends ColumnProps { + data?: any; + error?: unknown; + isEmpty?: boolean; + isLoading?: boolean; + isFetching?: boolean; + loadingIcon?: 'dots' | 'spinner'; + loadingPlacement?: 'center' | 'absolute' | 'inline'; + renderEmpty?: () => ReactNode; + children: ReactNode; +} + +export function LoadingPanel({ + data, + error, + isEmpty, + isLoading, + isFetching, + loadingIcon = 'dots', + loadingPlacement = 'absolute', + renderEmpty = () => <Empty />, + children, + ...props +}: LoadingPanelProps): ReactNode { + const empty = isEmpty ?? checkEmpty(data); + + // Show loading spinner only if no data exists + if (isLoading || isFetching) { + return ( + <Column position="relative" height="100%" width="100%" {...props}> + <Loading icon={loadingIcon} placement={loadingPlacement} /> + </Column> + ); + } + + // Show error + if (error) { + return <ErrorMessage />; + } + + // Show empty state (once loaded) + if (!error && !isLoading && !isFetching && empty) { + return renderEmpty(); + } + + // Show main content when data exists + if (!isLoading && !isFetching && !error && !empty) { + return children; + } + + return null; +} + +function checkEmpty(data: any) { + if (!data) return false; + + if (Array.isArray(data)) { + return data.length <= 0; + } + + if (typeof data === 'object') { + return Object.keys(data).length <= 0; + } + + return !!data; +} |