migration wip
This commit is contained in:
224
components/ui/Loading.tsx
Normal file
224
components/ui/Loading.tsx
Normal file
@@ -0,0 +1,224 @@
|
||||
import React, { forwardRef, HTMLAttributes } from 'react';
|
||||
import { cn } from '../../lib/utils';
|
||||
|
||||
// Loading sizes
|
||||
type LoadingSize = 'sm' | 'md' | 'lg' | 'xl';
|
||||
|
||||
// Loading variants
|
||||
type LoadingVariant = 'primary' | 'secondary' | 'neutral' | 'contrast';
|
||||
|
||||
// Loading props interface
|
||||
interface LoadingProps extends HTMLAttributes<HTMLDivElement> {
|
||||
size?: LoadingSize;
|
||||
variant?: LoadingVariant;
|
||||
overlay?: boolean;
|
||||
text?: string;
|
||||
fullscreen?: boolean;
|
||||
}
|
||||
|
||||
// Helper function to get size styles
|
||||
const getSizeStyles = (size: LoadingSize) => {
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
return 'w-4 h-4 border-2';
|
||||
case 'md':
|
||||
return 'w-8 h-8 border-4';
|
||||
case 'lg':
|
||||
return 'w-12 h-12 border-4';
|
||||
case 'xl':
|
||||
return 'w-16 h-16 border-4';
|
||||
default:
|
||||
return 'w-8 h-8 border-4';
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to get variant styles
|
||||
const getVariantStyles = (variant: LoadingVariant) => {
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
return 'border-primary';
|
||||
case 'secondary':
|
||||
return 'border-secondary';
|
||||
case 'neutral':
|
||||
return 'border-gray-300';
|
||||
case 'contrast':
|
||||
return 'border-white';
|
||||
default:
|
||||
return 'border-primary';
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to get text size
|
||||
const getTextSize = (size: LoadingSize) => {
|
||||
switch (size) {
|
||||
case 'sm':
|
||||
return 'text-sm';
|
||||
case 'md':
|
||||
return 'text-base';
|
||||
case 'lg':
|
||||
return 'text-lg';
|
||||
case 'xl':
|
||||
return 'text-xl';
|
||||
default:
|
||||
return 'text-base';
|
||||
}
|
||||
};
|
||||
|
||||
// Main Loading Component
|
||||
export const Loading = forwardRef<HTMLDivElement, LoadingProps>(
|
||||
({
|
||||
size = 'md',
|
||||
variant = 'primary',
|
||||
overlay = false,
|
||||
text,
|
||||
fullscreen = false,
|
||||
className = '',
|
||||
...props
|
||||
}, ref) => {
|
||||
const spinner = (
|
||||
<div
|
||||
className={cn(
|
||||
'animate-spin rounded-full',
|
||||
'border-t-transparent',
|
||||
getSizeStyles(size),
|
||||
getVariantStyles(variant),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
if (overlay) {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'fixed inset-0 z-50 flex items-center justify-center',
|
||||
'bg-black/50 backdrop-blur-sm',
|
||||
fullscreen && 'w-screen h-screen'
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
{spinner}
|
||||
{text && (
|
||||
<span className={cn('text-white font-medium', getTextSize(size))}>
|
||||
{text}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-col items-center justify-center gap-3',
|
||||
fullscreen && 'w-screen h-screen'
|
||||
)}
|
||||
>
|
||||
{spinner}
|
||||
{text && (
|
||||
<span className={cn('text-gray-700 font-medium', getTextSize(size))}>
|
||||
{text}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Loading.displayName = 'Loading';
|
||||
|
||||
// Loading Button Component
|
||||
interface LoadingButtonProps extends HTMLAttributes<HTMLDivElement> {
|
||||
size?: LoadingSize;
|
||||
variant?: LoadingVariant;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
export const LoadingButton = forwardRef<HTMLDivElement, LoadingButtonProps>(
|
||||
({ size = 'md', variant = 'primary', text = 'Loading...', className = '', ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-2 px-4 py-2 rounded-lg',
|
||||
'bg-gray-100 text-gray-700',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'animate-spin rounded-full border-t-transparent',
|
||||
getSizeStyles(size === 'sm' ? 'sm' : 'md'),
|
||||
getVariantStyles(variant)
|
||||
)}
|
||||
/>
|
||||
<span className="font-medium">{text}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
LoadingButton.displayName = 'LoadingButton';
|
||||
|
||||
// Loading Skeleton Component
|
||||
interface LoadingSkeletonProps extends HTMLAttributes<HTMLDivElement> {
|
||||
width?: string | number;
|
||||
height?: string | number;
|
||||
rounded?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const LoadingSkeleton = forwardRef<HTMLDivElement, LoadingSkeletonProps>(
|
||||
({ width = '100%', height = '1rem', rounded = false, className = '', ...props }, ref) => {
|
||||
// Convert numeric values to Tailwind width classes
|
||||
const getWidthClass = (width: string | number) => {
|
||||
if (typeof width === 'number') {
|
||||
if (width <= 32) return 'w-8';
|
||||
if (width <= 64) return 'w-16';
|
||||
if (width <= 128) return 'w-32';
|
||||
if (width <= 192) return 'w-48';
|
||||
if (width <= 256) return 'w-64';
|
||||
return 'w-full';
|
||||
}
|
||||
return width === '100%' ? 'w-full' : width;
|
||||
};
|
||||
|
||||
// Convert numeric values to Tailwind height classes
|
||||
const getHeightClass = (height: string | number) => {
|
||||
if (typeof height === 'number') {
|
||||
if (height <= 8) return 'h-2';
|
||||
if (height <= 16) return 'h-4';
|
||||
if (height <= 24) return 'h-6';
|
||||
if (height <= 32) return 'h-8';
|
||||
if (height <= 48) return 'h-12';
|
||||
if (height <= 64) return 'h-16';
|
||||
return 'h-auto';
|
||||
}
|
||||
return height === '1rem' ? 'h-4' : height;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'animate-pulse bg-gray-200',
|
||||
rounded && 'rounded-md',
|
||||
getWidthClass(width),
|
||||
getHeightClass(height),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
LoadingSkeleton.displayName = 'LoadingSkeleton';
|
||||
|
||||
// Export types for external use
|
||||
export type { LoadingProps, LoadingSize, LoadingVariant, LoadingButtonProps, LoadingSkeletonProps };
|
||||
Reference in New Issue
Block a user