borsa / nextjs-app /src /components /LoadingSpinner.tsx
veteroner's picture
feat: live position monitoring with charts + trading system production ready
656ac31
'use client'
interface LoadingSpinnerProps {
/** Size variant */
size?: 'sm' | 'md' | 'lg'
/** Optional label text */
label?: string
/** Full page centered */
fullPage?: boolean
/** Additional className */
className?: string
}
const sizeClasses = {
sm: 'w-5 h-5 border-2',
md: 'w-8 h-8 border-3',
lg: 'w-12 h-12 border-4',
}
export function LoadingSpinner({
size = 'md',
label,
fullPage = false,
className = '',
}: LoadingSpinnerProps) {
const spinner = (
<div className={`flex flex-col items-center justify-center gap-3 ${className}`}>
<div
className={`${sizeClasses[size]} border-gray-200 border-t-blue-600 rounded-full animate-spin`}
role="status"
aria-label={label || 'Yükleniyor'}
/>
{label && <p className="text-sm text-gray-500">{label}</p>}
</div>
)
if (fullPage) {
return (
<div className="min-h-[60vh] flex items-center justify-center">
{spinner}
</div>
)
}
return spinner
}
/** Skeleton placeholder for content loading */
export function Skeleton({
className = '',
lines = 1,
}: {
className?: string
lines?: number
}) {
return (
<div className={`animate-pulse space-y-2 ${className}`}>
{Array.from({ length: lines }).map((_, i) => (
<div
key={i}
className="h-4 bg-gray-200 rounded"
style={{ width: i === lines - 1 && lines > 1 ? '75%' : '100%' }}
/>
))}
</div>
)
}
/** Card-shaped skeleton for dashboard grids */
export function CardSkeleton({ count = 3 }: { count?: number }) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{Array.from({ length: count }).map((_, i) => (
<div key={i} className="bg-white border border-gray-200 rounded-xl p-4 animate-pulse">
<div className="h-4 bg-gray-200 rounded w-1/3 mb-3" />
<div className="h-6 bg-gray-200 rounded w-2/3 mb-2" />
<div className="h-4 bg-gray-200 rounded w-1/2" />
</div>
))}
</div>
)
}
/** Table row skeleton */
export function TableSkeleton({ rows = 5, cols = 4 }: { rows?: number; cols?: number }) {
return (
<div className="space-y-2">
{Array.from({ length: rows }).map((_, r) => (
<div key={r} className="flex gap-4 animate-pulse">
{Array.from({ length: cols }).map((_, c) => (
<div
key={c}
className="h-4 bg-gray-200 rounded flex-1"
style={{ maxWidth: c === 0 ? '120px' : undefined }}
/>
))}
</div>
))}
</div>
)
}