الگوهای بارگذاری
راهنمای طراحی حالتهای loading در پرتو — Skeleton، Spinner، و Progressive Loading
چه زمانی از چه الگویی استفاده کنیم؟
| حالت | ابزار | مثال |
|---|---|---|
| بارگذاری اولیه صفحه | Skeleton | لیست اینفلوئنسرها هنگام fetch اولیه |
| عملیات کوتاه (< ۲ ثانیه) | Spinner در دکمه | ذخیره فرم، اعمال فیلتر |
| بارگذاری نمودار | prop isLoading | نمودار روند |
| بارگذاری تدریجی | Skeleton per item | لیست بینهایت |
نمونه بصری
بارگذاری اولیه صفحه — Skeleton
بارگذاری لیست
عملیات کوتاه — Spinner در دکمه
Skeleton
کاربرد اصلی
import { Skeleton } from '@parto-system-design/ui'
// Skeleton ساده
<Skeleton className="h-4 w-48" />
// جایگزین کارت
<div className="bg-surface-100 p-4 rounded-lg border border-default space-y-3">
<div className="flex items-center gap-3">
<Skeleton className="h-10 w-10 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-3 w-24" />
</div>
</div>
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-3/4" />
</div>جایگزین لیست
function InfluencerListSkeleton() {
return (
<div className="space-y-3">
{Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="flex items-center gap-3 p-4 border border-default rounded-lg">
<Skeleton className="h-12 w-12 rounded-full" />
<div className="flex-1 space-y-2">
<Skeleton className="h-4 w-1/3" />
<Skeleton className="h-3 w-1/4" />
</div>
<Skeleton className="h-8 w-20" />
</div>
))}
</div>
)
}
// استفاده
{isLoading ? <InfluencerListSkeleton /> : <InfluencerList data={data} />}جایگزین MetricCard
function MetricCardSkeleton() {
return (
<div className="bg-surface-100 p-4 rounded-lg border border-default space-y-2">
<Skeleton className="h-3 w-24" />
<Skeleton className="h-8 w-32" />
<Skeleton className="h-3 w-16" />
</div>
)
}Spinner در دکمه
برای عملیاتهای کوتاه (submit، ذخیره)، Spinner را داخل دکمه قرار دهید:
import { Button, Spinner } from '@parto-system-design/ui'
<Button disabled={isSubmitting}>
{isSubmitting && <Spinner className="ms-2 h-4 w-4" />}
{isSubmitting ? 'در حال ذخیره...' : 'ذخیره'}
</Button>Skeleton نمودار
همه نمودارهای پرتو از isLoading پشتیبانی میکنند:
<LineChart
data={data}
isLoading={isLoading}
// وقتی isLoading=true، Skeleton با ابعاد نمودار نمایش داده میشود
/>Progressive Loading (بارگذاری تدریجی)
برای لیستهای بینهایت یا صفحهبندی:
const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = useInfiniteQuery(...)
return (
<div>
{/* دادههای بارگذاری شده */}
{data?.pages.map(page =>
page.items.map(item => <InfluencerCard key={item.id} data={item} />)
)}
{/* بارگذاری آیتمهای جدید */}
{isFetchingNextPage && (
<div className="space-y-3 mt-3">
{Array.from({ length: 3 }).map((_, i) => (
<InfluencerCardSkeleton key={i} />
))}
</div>
)}
{/* دکمه بارگذاری بیشتر */}
{hasNextPage && !isFetchingNextPage && (
<Button variant="outline" className="w-full mt-4" onClick={fetchNextPage}>
بارگذاری بیشتر
</Button>
)}
</div>
)اصول Skeleton خوب
- ابعاد واقعی — Skeleton باید شبیه محتوای واقعی باشد. اگر کارت ۸۰px ارتفاع دارد، Skeleton هم همین ارتفاع داشته باشد
- بدون پرجزئیاتی — Skeleton نباید هر جزء را شبیهسازی کند — فقط ساختار کلی
- انیمیشن یکنواخت — Skeleton پرتو انیمیشن شیمری دارد که نشان میدهد در حال بارگذاری است
- تعداد مناسب — ۳ تا ۵ item برای لیست، نه ۲۰
دسترسیپذیری
// اطلاعرسانی به screen reader
<div aria-busy={isLoading} aria-label="در حال بارگذاری...">
{isLoading ? (
<InfluencerListSkeleton />
) : (
<InfluencerList data={data} />
)}
</div>
// یا با aria-live
<div aria-live="polite">
{isLoading && <span className="sr-only">در حال بارگذاری دادهها...</span>}
</div>صفحات مرتبط
- الگوهای خطا — وقتی بارگذاری شکست میخورد
- دادهنمایی — prop
isLoadingدر نمودارها - دسترسیپذیری —
aria-busy،aria-live