قالب صفحهٔ تنظیمات (SettingsPage)
تنظیمات در گروههای مستقل — هر گروه یک بخش با عنوان، ردیفهای برچسبکنارکنترل و نوار ذخیرهٔ خودش
معرفی
SettingsPage قالب صفحهای است که تنظیمات را در چند گروه مستقل نشان میدهد؛ الگوی تنظیمات سوپابیس، برای
راستبهچپ آینهشده. عرض باریک 768 پیکسل است و هر گروه یک SettingsSection است:
- عنوان و توضیح گروه؛
- ردیفها (
FormRow) در یک کارت، برچسب کنار کنترل (از عرضmd: ستون برچسب یکسوم، ستون کنترل دوسوم)،SwitchوCheckboxروی خط برچسب در انتهای آن؛ - یک نوار ذخیره برای هر بخش، پایین کارت همان بخش: نشانهٔ «تغییرات ذخیره نشده است» در ابتدای خط، «انصراف» و «ذخیره تغییرات» در انتهای آن.
قاعدهٔ ذخیره یکی است: هر بخش جدا ذخیره میشود، هیچوقت یک دکمهٔ ذخیره برای کل صفحه. صفحهای که یک فرم است و
یک بار ثبت میشود FormPage است.
چه زمانی استفاده کنیم:
- تنظیمات حساب، فضای کار یا محصول در چند گروه: اعلانها، اعضا، اتصالها.
- هر گروهی که مستقل از بقیه ذخیره میشود.
چه زمانی استفاده نکنیم:
- یک فرم با یک «ذخیره» (حتی اگر اسمش «تنظیمات هشدار» باشد):
FormPage. - تغییری که فوراً اعمال میشود و ذخیره ندارد: همان کنترل در یک
SettingsSectionبدونonSubmit.
استفاده
هر بخش فرم react-hook-form خودش را دارد؛ formState.isDirty نوار ذخیره را روشن میکند و reset تغییرات را کنار
میگذارد:
'use client'
import { useForm } from 'react-hook-form'
import { Input, Switch } from '@partodata/ui'
import { FormField } from '@partodata/ui/form'
import { FormRow, SettingsPage, SettingsSection } from '@partodata/ui/templates'
function EmailSettings({ save }: { save: (values: { weekly: boolean; address: string }) => Promise<void> }) {
const form = useForm({ defaultValues: { weekly: true, address: 'team@example.com' } })
return (
<SettingsSection
title="گزارش ایمیلی"
description="خلاصهٔ دورهای منشنها که به ایمیل تیم میرسد"
onSubmit={form.handleSubmit(async (values) => {
await save(values)
form.reset(values)
})}
onCancel={() => form.reset()}
dirty={form.formState.isDirty}
submitting={form.formState.isSubmitting}
>
<FormField
control={form.control}
name="weekly"
render={({ field }) => (
<FormRow label="گزارش هفتگی" description="هر شنبه صبح">
<Switch checked={field.value} onCheckedChange={field.onChange} />
</FormRow>
)}
/>
<FormField
control={form.control}
name="address"
rules={{ required: 'نشانی ایمیل را وارد کنید' }}
render={({ field, fieldState }) => (
<FormRow label="نشانی ایمیل" required error={fieldState.error?.message}>
<Input kind="email" {...field} />
</FormRow>
)}
/>
</SettingsSection>
)
}
export default function NotificationSettingsPage() {
return (
<SettingsPage title="تنظیمات اعلانها" description="گزارشها و هشدارهایی که دریافت میکنید">
<EmailSettings save={async () => {}} />
</SettingsPage>
)
}حالتها و انواع
نوار ذخیره و نشانهٔ تغییر
dirty | دکمهٔ ذخیره | «انصراف» | نشانهٔ «تغییرات ذخیره نشده است» |
|---|---|---|---|
false | غیرفعال | پنهان | پنهان |
true | فعال | پیدا | پیدا |
onSubmit، dirty و onCancel با هم میآیند: بخشی که ذخیره میکند هر سه را دارد و بخشی که ذخیره نمیکند هیچکدام
از ویژگیهای ذخیره را نمیگیرد (هر دو اشتباه خطای نوعاند). submitting دکمهٔ ذخیره را به حالت بارگذاری میبرد و
error خلاصهٔ خطای ذخیره را بالای کارت همان بخش نشان میدهد. اگر «انصراف» یا «ذخیره تغییرات» با صفحهکلید زده شود و
از صفحه برود یا غیرفعال شود، فوکوس روی فرم همان بخش میماند، نه <body>.
اقدامهای ثانوی بخش
«تأیید توکن»، «تست اتصال» یا «بازگردانی پیشفرضها»ی یک بخش secondaryActions همان SettingsSection است: در ابتدای
نوار ذخیره، پیش از نشانهٔ تغییر، هر کدام Button variant="default" که بخش را ذخیره نمیکند. هر بخش ناحیهٔ مستقل خودش است:
دکمهٔ ذخیرهاش اقدام اصلی همان بخش است. بخشی که ذخیره نمیشود اقدامهای ثانویاش را تنها در پاورقی نشان میدهد.
بخش بدون ذخیره
SettingsSection بدون onSubmit فقط ردیفهایش را نشان میدهد، بی فرم و بی نوار ذخیره: برای اطلاعات فقطخواندنی یا
دکمهای مثل «حذف فضای کار» (با variant="destructive" و یک AlertDialog تأیید).
بارگذاری
state روی SettingsPage (با pageState({ data: settings, isLoading, error, onRetry })) همهٔ بخشها را با اسکلت
ردیفها عوض میکند و سرِ صفحه میماند؛ state روی یک SettingsSection فقط کارت همان بخش را. تنظیمات حالت خالی
ندارند.
راهنمای استفاده
بکنید
- برای هر گروه مستقل یک
SettingsSectionبا فرم خودش بنویسید. dirtyوonCancelرا از فرم همان بخش بدهید (formState.isDirty،() => form.reset()).- فیلدها را با
FormRowمستقیم در بخش بنویسید؛ چیدمان کنار هم را بخش میدهد. گروه دیگری از تنظیمات، یکSettingsSectionدیگر است — نهFormSectionو نهCardداخل بخش (هر دو در حالت توسعه هشدار میدهند).
نکنید
- یک دکمهٔ «ذخیره» برای کل صفحه نگذارید؛ هر بخش نوار خودش را دارد.
- بخش را در
Cardخودتان نپیچید و برچسب را خودتان کنار کنترل نچینید. - برای تنظیمات زیرصفحه، زبانه در محتوا نسازید؛ زیرصفحهها سطح دوم منوی
ProductFrameاند (childrenآیتم منو).
Props
SettingsPage
SettingsSection
دسترسیپذیری
- هر بخش یک نشانهٔ نامدار دارد، نه دو: بخشی که ذخیره میکند فرمی (
form) به نام عنوانh2خودش است، و بخش بی ذخیره یک ناحیهٔ نامدار (region). - دکمهٔ ذخیرهٔ غیرفعال یعنی چیزی برای ذخیره نیست؛ نشانهٔ تغییر متن دارد، نه فقط رنگ.
- خلاصهٔ خطای ذخیره
role="alert"دارد و فوکوس میگیرد.
کامپوننتهای مرتبط
FormPage— یک فرم با یک ثبت.FormRow— هر فیلد؛ در بخش تنظیمات برچسبش کنار کنترل است.AlertDialog— تأیید کار برگشتناپذیر یک بخش بدون ذخیره.