صفحهٔ سفارشی (CustomPage)

راه خروج برای صفحه‌ای که در هیچ قالبی جا نمی‌شود — همان سرِ صفحه، عرض و ریتم، با محتوای خودتان و یک DS-GAP ثبت‌شده

معرفی

CustomPage برای صفحه‌ای است که در هیچ‌کدام از قالب‌ها جا نمی‌شود. سرِ صفحه (و با آن تنها h1، راه بازگشت، اقدام‌ها و زبانه‌ها)، عرض نام‌دار و ریتم صفحه را نگه می‌دارد و ناحیهٔ محتوا را به شما می‌دهد. dsGap الزامی است: پیش از ساختن صفحه، نیاز را به‌عنوان یک DS-GAP برای مسئول طراحی ثبت کنید و شناسه‌اش را این‌جا بنویسید؛ روی ریشهٔ صفحه data-ds-gap می‌نشیند تا هر صفحهٔ سفارشی پیدا و شمرده شود. وقتی همان نیاز دو بار تکرار شد، قالب تازه می‌شود.

چه زمانی استفاده کنیم:

  • صفحه‌ای که واقعاً هیچ قالبی ندارد: ویرایشگر، خط زمانی رویدادها، نقشه.

چه زمانی استفاده نکنیم:

  • هر صفحه‌ای که فهرست، جزئیات، فرم، تنظیمات، داشبورد یا صفحهٔ کمکی است: قالب خودش را به کار ببرید (انتخاب قالب صفحه).
  • برای گریز از یک قاعدهٔ قالب (جای اقدام، عرض): قاعده را در DS-GAP مطرح کنید.

استفاده

محتوا PageSectionهاست (از @partodata/ui) یا DetailSectionها، که ریتم صفحه را نگه می‌دارند:

'use client'
import { Button, PageSection } from '@partodata/ui'
import { CustomPage } from '@partodata/ui/templates'

const steps = [
  { id: 's1', title: 'اعلام کمپین تخفیف فصلی', time: 'شنبه، ساعت 10' },
  { id: 's2', title: 'اولین موج منشن‌ها در اینستاگرام', time: 'شنبه، ساعت 14' },
]

export default function CampaignTimelinePage() {
  return (
    <CustomPage
      dsGap="DS-GAP-12: خط زمانی رویدادهای کمپین"
      title="خط زمانی کمپین"
      secondaryActions={<Button variant="default">خروجی</Button>}
    >
      <PageSection>
        <ol className="flex flex-col gap-layout-block-gap border-s border-border ps-6">
          {steps.map((step) => (
            <li key={step.id} className="flex flex-col gap-1">
              <span className="text-sm font-medium text-foreground">{step.title}</span>
              <span className="text-sm text-foreground-light">{step.time}</span>
            </li>
          ))}
        </ol>
      </PageSection>
    </CustomPage>
  )
}

حالت‌ها و انواع

ثبت DS-GAP

  1. نیاز را بنویسید: صفحه چه چیزی دارد که هیچ قالبی ندارد، و کدام قالب نزدیک‌ترین است.
  2. آن را برای مسئول طراحی ثبت کنید و شناسه‌اش را در dsGap بگذارید.
  3. جزء محلی با نام سیستم طراحی نسازید؛ کمترین ترکیب ممکن از اجزای موجود را داخل CustomPage بسازید.

صفحه‌ای که ارتفاع را پر می‌کند: layout="fill"

فضای کاری رسانه (پخش زنده کنار متن گفتار)، نقشه، پنل‌های کنار هم، کنسول یا سندی در iframe صفحهٔ بخش‌هایی نیست که پیمایش شود: layout="fill" ناحیهٔ محتوا را به اندازهٔ کل ارتفاع ناحیهٔ محتوای قاب زیر سرِ صفحه می‌کند. فرزندانش آن را پر می‌کنند — PagePaneهایی کنار هم (از 56rem محتوا؛ پایین‌تر روی هم، هر کدام سهمی از ارتفاع)، یا یک عنصر تمام‌اندازه — و هر کدام درون خودش پیمایش می‌شود؛ خود صفحه هرگز پیمایش نمی‌شود.

  • این شکلِ مجاز است، نه کمبود: صفحهٔ fill به dsGap نیاز ندارد و با data-layout="fill" جدا شمرده می‌شود؛ شناسهٔ DS-GAP فقط وقتی لازم است که خودِ فضای کاری چیزی کم دارد.
  • inset="none" محتوا را لبه‌به‌لبه و به عرض کامل ناحیهٔ محتوای قاب می‌کند، هر widthی که داده شده باشد (بی حاشیهٔ صفحه و فاصلهٔ پایین): نقشه، دیوار ویدیو. دیوار اپراتور کروم قاب را هم با chrome={useKioskChrome()} روی ProductFrame برمی‌دارد.
  • titleHidden فقط در همین حالت: عنوان هنوز تنها h1 صفحه است ولی فقط برای صفحه‌خوان — برای صفحه‌ای که محتوایش خودش کروم است (دیوار اپراتور، نقشهٔ تمام‌صفحه با کنترل‌های خودش). سرِ صفحه دیگر نیست، پس نه اقدام، نه زبانه، نه بازگشت.
  • PagePane یک ناحیهٔ عنوان‌دار است با اقدام‌های خودش — و اقدام اصلی خودش: هر پنل ناحیهٔ مستقلی است (دکمهٔ «منطقهٔ تازه»ی پنل فهرست و «ذخیره»ی پنل فرم هر کدام اقدام اصلی پنل خودشان‌اند). width="aside" پنلی به عرض --layout-aside-width است (فهرستی کنار جزئیاتش)؛ بقیه فضای باقی را می‌گیرند. پنلی که محتوایش با PageToolbar شروع می‌شود اقدام اصلی‌اش را یا در ردیف عنوان پنل دارد یا در primaryAction نوارابزار — هرگز هر دو. PagePane فقط فرزند CustomPage layout="fill" است (بیرون از آن هشدار توسعه). بدنهٔ پنل با صفحه‌کلید فوکوس و پیمایش می‌شود.

صفحهٔ تغییرات: pattern="changelog"

صفحهٔ کاملِ تغییرات محصول (فهرست نسخه‌ها و تغییرات هر کدام) قالب جدا ندارد و کمبود هم نیست: CustomPage با pattern="changelog" و یک ReleaseTimeline به‌عنوان محتوا. به dsGap نیاز ندارد و با data-pattern="changelog" جدا شمرده می‌شود. پنل «آخرین تغییرات» در سرِ صفحه همچنان WhatsNewPanel است.

// app/(app)/changelog/page.tsx
import { CustomPage } from '@partodata/ui/templates'
import { ReleaseTimeline } from '@partodata/ui/release-timeline'

import { whatsNewFeed } from '@/content/whats-new'

export default function ChangelogPage() {
  return (
    <CustomPage pattern="changelog" title="تغییرات">
      <ReleaseTimeline notes={whatsNewFeed.notes} />
    </CustomPage>
  )
}

عرض

width یکی از عرض‌های نام‌دار است: narrow 768، default 1200 (پیش‌فرض)، wide 1600، full بی‌سقف.

راهنمای استفاده

بکنید

  • پیش از CustomPage مطمئن شوید هیچ قالبی جا نمی‌شود.
  • محتوا را در PageSectionها بگذارید تا ریتم 48 پیکسلی صفحه بماند.

نکنید

  • CustomPage را برای صفحه‌ای که قالب دارد به کار نبرید.
  • h1 یا سرِ صفحهٔ دیگری داخل محتوا نسازید.
  • dsGap را خالی یا ساختگی نگذارید؛ نوعش فقط شناسه‌ای مثل DS-GAP-12: خط زمانی را می‌پذیرد.

Props

CustomPage

Prop

Type

PagePane

Prop

Type

دسترسی‌پذیری

  • سرِ صفحه همان سرِ صفحهٔ قالب‌هاست: تنها h1، پیوند بازگشت نام‌دار، زبانه‌های نشانی‌دار.
  • دسترس‌پذیری محتوای سفارشی با خود شماست: سرعنوان‌های بخش‌ها از h2 شروع شوند.

کامپوننت‌های مرتبط