حالت‌های خالی

راهنمای حالت‌های خالی — صفر نتیجه، فهرست خالی، صفحهٔ خالی و مسیر گم‌شده؛ هر کدام یک پاسخ دارد و در صفحه قالب آن را می‌کشد

معرفی

حالت‌های خالی زمانی ظاهر می‌شوند که محتوایی برای نمایش وجود ندارد. یک حالت خالی خوب سه کار انجام می‌دهد: وضعیت را توضیح می‌دهد، علت را بیان می‌کند، و راه ادامه را نشان می‌دهد.

در صفحه‌های اپلیکیشن این حالت‌ها را خودتان نمی‌سازید: هر کدام یک پاسخ دارد و قالب صفحه آن را می‌کشد. حالت خالی جای کل فهرست می‌نشیند؛ سرستون‌های جدول نمی‌مانند و ردیفی داخل جدول ساخته نمی‌شود.


نمونه بصری

صفر نتیجه — جست‌وجو یا فیلتری فعال است

نتیجه‌ای یافت نشد

نتیجه‌ای یافت نشد

عبارت جست‌وجو یا فیلترها را تغییر دهید.

فهرست خالی — بدون فیلتر

هنوز اینفلوئنسری ثبت نشده است

هنوز اینفلوئنسری ثبت نشده است


پیاده‌سازی

Empty از نسخهٔ 4.0 به‌اندازهٔ محتوایش است و حداقل ارتفاع ندارد (تا 3٫x چهارصد پیکسل بود). داخل کارت، جدول یا فهرست همین درست است. برای وضعیت خالیِ یک صفحهٔ کامل، UtilityPage kind="empty" (پایین همین صفحه) جایش را تعیین می‌کند؛ بیرون از قالب به آن صحنه بدهید: <Empty minHeight="400px">.

انواع حالت خالی و پاسخ هر کدام

نوعکجا پیش می‌آیدپاسخ
صفر نتیجهفهرستی که جست‌وجو یا فیلترش فعال استfiltered و onClearFilters قالب ListPage: «نتیجه‌ای یافت نشد» با «پاک کردن فیلترها»
فهرست خالیفهرستی که چیزی برنگرداند و فیلتری نداردemptyCopy: { title } در pageState — عنوانی که نام آنچه نیست را می‌برد
صفحهٔ خالی (اولیه)صفحه‌ای از منو که هنوز چیزی نداردUtilityPage با kind="empty"، title و description
مسیر گم‌شدهنشانی یا موجودیتی که وجود نداردUtilityPage با kind="404" (در Next.js در app/not-found.tsx)

صفر نتیجه و فهرست خالی: state قالب

فهرست یک صفحه ListPage است و حالت‌هایش state آن: pageState از فیلدهای useAsync یک مقدار می‌سازد و قالب، با filtered خود صفحه، تصمیم می‌گیرد کدام حالت خالی را نشان دهد.

import { DataTable, useAsync } from '@partodata/ui'
import { ListPage, pageState } from '@partodata/ui/templates'

const { data, isLoading, error, run } = useAsync<Paged<Influencer>>()

;<ListPage
  title="اینفلوئنسرها"
  search={search}
  filtered={query !== ''}
  onClearFilters={clear}
  state={pageState({
    data: data?.items,
    isLoading,
    error,
    onRetry: load,
    emptyCopy: { title: 'هنوز اینفلوئنسری ثبت نشده است' },
  })}
>
  <DataTable columns={columns} data={data?.items ?? []} />
</ListPage>
  • صفر نتیجه: تا filtered درست است، فهرست خالی همیشه «نتیجه‌ای یافت نشد» با «پاک کردن فیلترها» است (متن DS)، نه عنوان emptyCopy. دکمه همه را پاک می‌کند و به صفحهٔ 1 برمی‌گردد؛ اگر با صفحه‌کلید زده شود، فوکوس به جست‌وجو می‌رود.
  • فهرست خالی: عنوان emptyCopy نام آنچه نیست را می‌برد («هنوز اینفلوئنسری ثبت نشده است») و اقدامی ندارد: اقدام اصلی صفحه («افزودن اینفلوئنسر») همان بالا روی صفحه است.
  • بلوکی که جدا از صفحه بارگذاری می‌شود (نظرهای یک پست در صفحهٔ جزئیات): state همان DetailSection یا یک PageState؛ اگر فیلتر خودش را دارد، pageState({ …, filtered, onClearFilters }).

الگوهای رایج

صفحهٔ خالی: UtilityPage kind="empty"

صفحه‌ای از منو که قابلیتش هنوز چیزی ندارد (هنوز گزارشی ساخته نشده) یک UtilityPage است، نه ListPage با فهرست خالی. title وضعیت را می‌گوید، نه نام صفحه را:

import {  } from '@partodata/ui/templates'

export default function () {
  return (
    <
      ="empty"
      ="هنوز گزارشی ساخته نشده است"
      ="گزارش‌ها پس از راه‌اندازی این بخش، این‌جا فهرست می‌شوند."
    />
  )
}

action فقط وقتی هست که چیزی در محصول اولین مورد را می‌سازد، و آن‌وقت کاری انجام می‌دهد — پیوندی به آن یا یک onClick؛ هرگز دکمه‌ای بی‌عمل:

import Link from 'next/link'
import { Button } from '@partodata/ui'
import { UtilityPage } from '@partodata/ui/templates'

export default function SourcesPage() {
  return (
    <UtilityPage
      kind="empty"
      title="هنوز منبعی افزوده نشده است"
      description="با افزودن اولین منبع، منشن‌های آن این‌جا جمع می‌شوند."
      action={
        <Button asChild>
          <Link href="/sources/new">افزودن منبع</Link>
        </Button>
      }
    />
  )
}

مسیر گم‌شده: UtilityPage kind="404"

// app/not-found.tsx
import  from 'next/link'
import {  } from '@partodata/ui'
import {  } from '@partodata/ui/templates'

export default function () {
  return (
    <
      ="404"
      ={
        < >
          < ="/">بازگشت به صفحهٔ اصلی</>
        </>
      }
    />
  )
}

موجودیتی که درخواستش تمام شد و چیزی برنگرداند (isSuccess && !data از useAsync) هم به‌جای DetailPage یک UtilityPage kind="404" با title، description و پیوند بازگشت به فهرست است.

بیرون از قالب صفحه

کامپوننت ترکیبی Empty را فقط جایی خودتان بنویسید که قالبی حالت را نمی‌کشد: پنل کناری، Popover، Sheet یا محتوای یک CustomPage. اقدامش ثانوی (variant="default") است و کاری انجام می‌دهد:

;<>
  <>هنوز منبعی به این پنل افزوده نشده است</>
  <>منبعی را که می‌خواهید در این پنل ببینید از فهرست منبع‌ها انتخاب کنید.</>
  <>
    < ="default" ={}>
      انتخاب منبع
    </>
  </>
</>

بهترین روش‌ها + دام‌های رایج

  • عنوان حالت خالی نام آنچه نیست را می‌برد و راهنماست: «هنوز اینفلوئنسری ثبت نشده است»، نه «هیچ اینفلوئنسری وجود ندارد».
  • حالت خالی جای کل بلوک می‌نشیند و قالب ارتفاع کمینه‌اش را (--layout-state-min-height) نگه می‌دارد تا جابه‌جایی صفحه کم باشد.
  • اسکلت پیش از حالت خالی می‌آید و ترتیب بارگذاری → خطا → خالی → آماده را pageState تعیین می‌کند؛ خودتان سه‌تایی شرطی ننویسید.
  • اقدام حالت خالی ثانوی است و فقط وقتی هست که کاری انجام می‌دهد.

دام‌های رایج:

  • ردیف «نتیجه‌ای یافت نشد» داخل جدول (در emptyState جدول یا یک TableRow با colSpan): حالت خالی جای جدول است.
  • Empty یا Callout دست‌ساز برای صفحهٔ 404: UtilityPage kind="404".
  • ساختن صفحه‌ای که هنوز چیزی ندارد با ListPage و فهرست خالی: UtilityPage kind="empty".
  • دکمهٔ «افزودن …» بدون onClick یا پیوند.
  • ErrorState به‌جای حالت خالی برای نتیجهٔ صفر: این خطا نیست.

صفحات مرتبط