Unified loading states.

This commit is contained in:
Mike Cao 2025-06-13 21:13:11 -07:00
parent 7b5591a3ce
commit da8c7e99c5
52 changed files with 506 additions and 364 deletions

View file

@ -1,32 +1,59 @@
import { ReactNode } from 'react';
import { Spinner, Dots, Column, type ColumnProps } from '@umami/react-zen';
import { Loading, Column, type ColumnProps } from '@umami/react-zen';
import { ErrorMessage } from '@/components/common/ErrorMessage';
import { Empty } from '@/components/common/Empty';
export interface LoadingPanelProps extends ColumnProps {
data?: any;
error?: Error;
isEmpty?: boolean;
isLoading?: boolean;
isFetching?: boolean;
loadingIcon?: 'dots' | 'spinner';
renderEmpty?: () => ReactNode;
children: ReactNode;
}
export function LoadingPanel({
data,
error,
isEmpty,
isFetched,
isLoading,
isFetching,
loadingIcon = 'dots',
renderEmpty = () => <Empty />,
children,
...props
}: {
error?: Error;
isEmpty?: boolean;
isFetched?: boolean;
isLoading?: boolean;
loadingIcon?: 'dots' | 'spinner';
renderEmpty?: () => ReactNode;
children: ReactNode;
} & ColumnProps) {
}: LoadingPanelProps) {
const empty = isEmpty ?? checkEmpty(data);
return (
<Column {...props}>
{isLoading && !isFetched && (loadingIcon === 'dots' ? <Dots /> : <Spinner />)}
<Column position="relative" flexGrow={1} {...props}>
{/* Show loading spinner only if no data exists */}
{(isLoading || isFetching) && !data && <Loading icon={loadingIcon} position="page" />}
{/* Show error */}
{error && <ErrorMessage />}
{!error && !isLoading && isEmpty && renderEmpty()}
{!error && !isLoading && !isEmpty && children}
{/* Show empty state (once loaded) */}
{!error && !isLoading && !isFetching && empty && renderEmpty()}
{/* Show main content when data exists */}
{!error && !empty && children}
</Column>
);
}
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;
}