چیدمان آیتم فرم (FormItemLayout)
داربست ردیف فرم — برچسب، توضیح و خطا؛ با چهار حالت چیدمان شامل ردیف toggle
معرفی
FormItemLayout عنصرِ پایهٔ چیدمان یک ردیف فرم است: داربست برچسب/توضیح/خطا را میسازد و کنترل (input، switch، select) را بهعنوان children جای میدهد. برگرفته از FormLayout واقعی Supabase و برای RTL بازطراحی شده. عمداً مستقل است و هیچ وابستگیای به react-hook-form ندارد، پس در بارل امن میماند. فیلدی که با react-hook-form اعتبارسنجی میشود FormRow است، از render در FormField.
برای فیلدهای صفحه و دیالوگ: FormRow
فیلد یک صفحه یا دیالوگ FormRow است، که روی همین FormItemLayout ساخته شده و
چیدمانش را از ظرف میگیرد (برچسب بالا در صفحهٔ فرم و دیالوگ، کنار کنترل در بخش تنظیمات). FormItemLayout جزء
سطح پایینتر است، برای جایی که چیدمان ردیف را خودتان باید انتخاب کنید.
چه زمانی استفاده کنیم:
- برای یک ردیف فرم استاندارد با برچسب، توضیح و پیام خطا
- وقتی به شکلهای مختلف چیدمان نیاز دارید (عمودی، افقی، ردیف toggle)
- برای یکدستکردن فاصلهگذاری و ترتیب برچسب/کنترل/خطا در کل فرمها
چه زمانی استفاده نکنیم:
- برای مدیریت state و اعتبارسنجی فرم — از
Form(react-hook-form) استفاده کنید وFormItemLayoutرا داخل آن بهعنوان چیدمان به کار ببرید - برای فقط یک برچسب بدون کنترل — از
Labelاستفاده کنید - برای سربرگ یک بخش فرم — از
FormHeaderاستفاده کنید
نامی که در گزارشها نمایش داده میشود.
آدرس سایت برند را بدون https وارد کنید.
هر هفته یک خلاصه عملکرد ایمیل میشود.
استفاده
import { FormItemLayout, Input } from '@partodata/ui'
export default function CampaignNameField() {
return (
<FormItemLayout label="نام کمپین" description="نامی که در گزارشها نمایش داده میشود." id="campaign-name">
<Input id="campaign-name" placeholder="کمپین تخفیف فصلی" />
</FormItemLayout>
)
}id (یا name) روی FormItemLayout بهعنوان htmlFor برچسب استفاده میشود؛ همان مقدار را روی کنترل هم بگذارید تا کلیک روی برچسب کنترل را فوکوس کند.
حالتها و انواع
prop layout
چهار شکل ردیف، دقیقاً مطابق FormLayout واقعی Supabase:
vertical(پیشفرض) — برچسب بالای کنترل، بهصورت پشته.horizontal— گرید 12 ستونی، برچسب 4 ستون / کنترل 8 ستون.flex— ستون برچسب+توضیح در کنار کنترل.flex-row-reverse— شکل «ردیف toggle»: برچسب در ابتدای خط، کنترل در انتهای خط، روی یک خط.
عمودی (پیشفرض)
<FormItemLayout label="نام کمپین" description="نامی که در گزارشها نمایش داده میشود." id="name">
<Input id="name" placeholder="کمپین تخفیف فصلی" />
</FormItemLayout>افقی
<FormItemLayout
layout="horizontal"
label="دامنه برند"
labelOptional="(اختیاری)"
description="آدرس سایت برند را بدون https وارد کنید."
id="domain"
>
<Input id="domain" dir="ltr" placeholder="brand.example.com" />
</FormItemLayout>ردیف toggle با flex-row-reverse
این حالت برای ردیفهای سوییچ/چکباکس است: برچسب و توضیح در ابتدای خط، کنترل در انتهای خط.
<FormItemLayout
layout="flex-row-reverse"
label="اعلان گزارش هفتگی"
description="هر هفته یک خلاصه عملکرد ایمیل میشود."
id="weekly"
>
<Switch id="weekly" defaultChecked />
</FormItemLayout>چرا نام flex-row-reverse ولی بدون reverse فیزیکی؟
دستور اصلی Supabase در LTR، ردیف را بهصورت فیزیکی معکوس میکند تا برچسب اول و سوییچ آخر بیاید. ما بهجای آن از
جریان منطقی استفاده میکنیم: بلوک متن اول در DOM رندر میشود و کنترل دوم، با یک ردیف justify-between طبیعی. چون
flex-row ساده جهت نوشتار را رعایت میکند، برچسب در ابتدای خط و کنترل در انتهای خط در هر دو جهت LTR و RTL قرار
میگیرد — بدون reverse فیزیکی و بدون left/right. نام prop برای سازگاری drop-in با API سوپابیس، همان flex-row-reverse
نگه داشته شده است.
نمایش خطا
با ستکردن error، پیام در تینت destructive رندر میشود. با hideMessage میتوانید اسلات خطا را حتی وقتی error ست است پنهان کنید.
<FormItemLayout label="ایمیل" error="ایمیل واردشده معتبر نیست." id="email">
<Input id="email" dir="ltr" />
</FormItemLayout>aria-invalid و aria-describedby را دستی ست نکنید — وقتی کنترل تنها فرزندِ ردیف باشد، FormItemLayout خودش پیام خطا و توضیح را با id پایدار رندر میکند، آنها را روی کنترل به aria-describedby میبندد و در حالت خطا aria-invalid میگذارد. اگر خودتان aria-describedby بدهید، حفظ و با شناسههای ردیف ادغام میشود؛ aria-invalid صریح شما هم بازنویسی نمیشود.
برچسبهای کمکی
<FormItemLayout
label="کلید API"
labelOptional="(اختیاری)"
beforeLabel={<Lock className="size-3.5" />}
afterLabel="— فقط خواندنی"
id="api-key"
>
<Input id="api-key" dir="ltr" />
</FormItemLayout>راهنمای استفاده
بکنید
id/nameرا رویFormItemLayoutو روی کنترل یکسان بگذارید تا برچسب به کنترل متصل شود - برای ردیفهای سوییچ/چکباکس ازlayout="flex-row-reverse"استفاده کنید نه چیدمان دستی - در فرمهای react-hook-form،errorرا ازfieldStateصریح پاس دهید
نکنید
- انتظار نداشته باشید
FormItemLayoutخودش state فرم را مدیریت کند — فقط چیدمان است - برای معکوسکردن ردیف toggle از کلاسهای فیزیکیflex-row-reverse/left/rightاستفاده نکنید؛ proplayoutرا به کار ببرید - توضیح و خطا را همزمان بهعنوان دو پیام متناقض نمایش ندهید؛ هنگام خطا معمولاً پیام خطا کافی است
جدول ویژگیها
FormItemLayout
دسترسیپذیری
- برچسب یک
Labelواقعی باhtmlForاست؛ اگرid/nameرا روی کنترل هم بگذارید، کلیک روی برچسب کنترل را فوکوس میکند - توضیح و پیام خطا هرکدام
idپایدار میگیرند (<fieldId>-descriptionو<fieldId>-message) و بهصورت خودکار باaria-describedbyبه کنترل بسته میشوند؛ اگرid/nameندهید، یک شناسه داخلی تولید میشود - در حالت خطا، کنترل
aria-invalidمیگیرد و پیام خطا باrole="alert"رندر میشود، پس صفحهخوان همان چیزی را اعلام میکند که کاربر بینا میبیند - این اتصال فقط وقتی انجام میشود که کنترل تنها فرزند ردیف باشد؛ با چند فرزند، اتصال بر عهدهٔ خودتان است.
aria-describedbyوaria-invalidکه خودتان بدهید حفظ میشوند (اولی ادغام میشود) - با
hideMessageمتن خطا پنهان میشود ولیaria-invalidروی کنترل باقی میماند - چیدمان
flex-row-reverseاز جریان منطقی استفاده میکند، پس ترتیب خواندن صفحهخوان (برچسب → توضیح → کنترل) در هر دو جهت درست میماند
کامپوننتهای مرتبط
- Form — برای مدیریت state و اعتبارسنجی (react-hook-form)، از Form استفاده کنید و FormItemLayout را داخلش برای چیدمان به کار ببرید
- Field — جایگزین سبکتر برای ردیفهای فرم ساده بدون حالتهای چیدمان چندگانه
- FormHeader — برای سربرگ یک بخش از فرم (عنوان + توضیح + اقدامات)، از FormHeader استفاده کنید
- Label — اگر فقط یک برچسب مستقل میخواهید، از Label استفاده کنید