النماذج

يرسل كل عنصر كما يرسل الحقل الأصلي.

عندما تعطي العنصر اسماً عبر ⁦name⁩، يرسل ضمن ⁦FormData⁩، وهي الصيغة التي يقرؤها النموذج العادي و⁦<Form>⁩ في ⁦Inertia⁩ وإجراءات ⁦React Router⁩ وإجراءات الخادم. لا حاجة إلى ربط ⁦onChange⁩ ولا إلى حقول مخفية إضافية.

الإرسال الأصلي في ⁦DatePicker⁩ و⁦DateRangePicker⁩ و⁦TagInput⁩ و⁦FileUpload⁩ و⁦Dropdown⁩ و⁦Rating⁩، والدالة ⁦parseDateOnly⁩، يتطلب ⁦@dev-dga/react 1.4.0⁩ أو أحدث.

ما يرسله كل عنصر

القواعد مطابقة للحقول الأصلية: العنصر المعطل لا يرسل، و⁦required⁩ يمنع الإرسال ما دام العنصر فارغاً، وإعادة تعيين النموذج ترجع القيمة الأولى.

العنصريرسلملاحظات
⁦TextInput⁩ و⁦Textarea⁩ و⁦SearchBox⁩النصحقل أصلي، وكل خصائص الحقل تمر إليه.
NumberInputرقم عشري بلا تنسيق، مثل ⁦"1234.5"⁩بلا فواصل آلاف، وبأرقام لاتينية دائماً. الفارغ يرسل ⁦""⁩.
Selectالقيمة المختارةالفارغ يرسل ⁦""⁩.
Dropdownالقيمة المختارةمع ⁦multiple⁩ يرسل قيداً لكل قيمة بالاسم نفسه. الاختيار الواحد الفارغ يرسل ⁦""⁩.
Checkboxقيمة ⁦value⁩ أو ⁦"on"⁩غير المحدد لا يرسل. أعط المجموعة اسماً واحداً واقرأها بـ ⁦getAll⁩.
RadioGroupالقيمة المختارةلا يرسل شيء بلا اختيار.
Switch"on"المطفأ لا يرسل.
Sliderالقيمةيرسل النطاق ⁦name[]⁩ مرة لكل مقبض، ويضيف ⁦Radix⁩ القوسين.
Ratingرقم نصي، مثل ⁦"4"⁩ أو ⁦"3.5"⁩بلا تقييم يرسل ⁦"0"⁩.
TagInputقيداً لكل وسملا يرسل شيء بلا وسوم، ولا يرسل النص المكتوب قبل اعتماده أبداً.
InputOTPالرمز المدخلحقل أصلي تحت الخانات.
DatePickerتاريخ ميلادي ⁦YYYY-MM-DD⁩حتى عند عرض الهجري. الفارغ يرسل ⁦""⁩.
DateRangePickerتاريخ ميلادي ⁦YYYY-MM-DD⁩ باسمي ⁦startName⁩ و⁦endName⁩يرسل كل طرف وحده.
FileUploadكل ملف في القائمة (⁦multipart⁩)الحقل الأصلي يعكس ⁦files⁩، بما فيها الملفات المسحوبة. إعادة التعيين تترك ⁦files⁩ لك.
⁦ContentSwitcher⁩ و⁦Toggle⁩لا شيءهذه مبدلات للعرض فقط. للاختيار داخل نموذج استخدم ⁦RadioGroup⁩ أو ⁦Checkbox⁩.

القيم المتعددة

يرسل ⁦TagInput⁩ و⁦Dropdown⁩ مع ⁦multiple⁩ ومجموعات ⁦Checkbox⁩ قيداً واحداً في ⁦FormData⁩ لكل قيمة، بالاسم الذي تمرره كما هو، مثل ⁦<select multiple>⁩ الأصلي. اقرأها بـ ⁦formData.getAll(name⁩). لا تبني ⁦PHP⁩ و⁦Laravel⁩ مصفوفة إلا من أسماء تنتهي بـ ⁦[]⁩، لذا مرر ⁦name="tags[]"⁩ هناك.

// Next.js server action or React Router actionformData.getAll('skills'); // ["react", "laravel"]// Laravel reads arrays only from names ending in []<TagInput label="Skills" name="skills[]" /><Dropdown multiple label="Cities" name="cities[]">…</Dropdown>// Laravel: $request->validate(['skills' => ['array'], 'skills.*' => ['string']]);

النماذج العادية

ضع العناصر داخل ⁦<form>⁩ واقرأ ⁦FormData⁩ في معالج الإرسال. هذا هو العقد الذي تقوم عليه كل التكاملات التالية.

<form  onSubmit={(event) => {    event.preventDefault();    const data = new FormData(event.currentTarget);    data.get('fullName'); // "Noura"    data.get('visitDate'); // "2026-10-20"    data.getAll('skills'); // ["react", "laravel"]  }}>  <TextInput label="Full name" name="fullName" required />  <DatePicker label="Visit date" name="visitDate" required />  <TagInput label="Skills" name="skills" />  <Button type="submit">Submit</Button></form>

⁦<Form>⁩ في ⁦Inertia⁩

تبني ⁦Inertia⁩ بياناتها من ⁦new FormData(form⁩)، لذا لا تحتاج العناصر المسماة إلى ربط إضافي. وتمرر أخطاؤها مباشرة إلى خاصيتي ⁦error⁩ و⁦errorMessage⁩ في كل عنصر.

import { Form } from '@inertiajs/react';import { Button, DatePicker, Select, SelectItem, TextInput, parseDateOnly } from '@dev-dga/react';export default function EditVisit({ visit }: { visit: Visit }) {  return (    <Form action={`/visits/${visit.id}`} method="put">      {({ errors, processing }) => (        <>          <TextInput            label="Name"            name="name"            defaultValue={visit.name}            error={!!errors.name}            errorMessage={errors.name}          />          <Select label="City" name="city" defaultValue={visit.city} error={!!errors.city} errorMessage={errors.city}>            <SelectItem value="riyadh">Riyadh</SelectItem>            <SelectItem value="jeddah">Jeddah</SelectItem>          </Select>          <DatePicker            label="Visit date"            name="date"            defaultValue={visit.date ? parseDateOnly(visit.date) : null}            error={!!errors.date}            errorMessage={errors.date}          />          <Button type="submit" disabled={processing}>            Save          </Button>        </>      )}    </Form>  );}

تطلق الدالة ⁦reset(⁩) الكاملة حدث ⁦reset⁩ الذي تستمع إليه العناصر، بينما لا تطلق إعادة تعيين حقول بعينها، مثل ⁦resetOnSuccess={["name"]}⁩، أي حدث. لذا تحتفظ العناصر التي تدير حالتها بنفسها (منتقيا التاريخ و⁦TagInput⁩ و⁦Dropdown⁩ و⁦Rating⁩ و⁦NumberInput⁩ و⁦InputOTP⁩) بقيمتها الحالية. أعد تعيين النموذج كله، أو تحكم في هذه الحقول بنفسك.

إجراءات ⁦React Router⁩

يرسل ⁦<Form>⁩ في ⁦React Router⁩ بيانات ⁦FormData⁩ نفسها إلى إجراء المسار. اقرأها بـ ⁦request.formData(⁩) وأعد أخطاء التحقق من الإجراء.

import { Form, useActionData, type ActionFunctionArgs } from 'react-router';import { Button, DatePicker } from '@dev-dga/react';export async function action({ request }: ActionFunctionArgs) {  const data = await request.formData();  const date = data.get('date'); // "2026-10-20", or "" when empty  if (!date) return { errors: { date: 'Pick a visit date.' } };  // save…  return { errors: {} };}export default function NewVisit() {  const result = useActionData<typeof action>();  return (    <Form method="post">      <DatePicker        label="Visit date"        name="date"        error={!!result?.errors.date}        errorMessage={result?.errors.date}      />      <Button type="submit">Save</Button>    </Form>  );}

إجراءات الخادم

مرر إجراء الخادم إلى خاصية ⁦action⁩ في النموذج. ترجع ⁦useActionState⁩ نتيجة الإجراء، مما يعيد أخطاء الحقول من الخادم إلى العناصر.

// app/visits/actions.ts'use server';type State = { errors: { date?: string } };export async function saveVisit(_prev: State, formData: FormData): Promise<State> {  const date = formData.get('date');  if (typeof date !== 'string' || date === '') return { errors: { date: 'Pick a visit date.' } };  // save…  return { errors: {} };}// app/visits/visit-form.tsx'use client';import { useActionState } from 'react';import { Button, DatePicker } from '@dev-dga/react';import { saveVisit } from './actions';export function VisitForm() {  const [state, formAction, pending] = useActionState(saveVisit, { errors: {} });  return (    <form action={formAction}>      <DatePicker        label="Visit date"        name="date"        error={!!state.errors.date}        errorMessage={state.errors.date}      />      <Button type="submit" disabled={pending}>        Save      </Button>    </form>  );}

أخطاء التحقق من الخادم

يقبل كل حقل ⁦error (⁩حالة الخطأ) و⁦errorMessage (⁩النص تحت الحقل). ويضبطان معاً ⁦aria-invalid⁩ ويربطان ⁦aria-describedby⁩ بالرسالة، مما يجعل قارئات الشاشة تقرأ رسالة الخادم مع الحقل.

<TextInput  label="National ID"  name="nationalId"  error={!!errors.nationalId}  errorMessage={errors.nationalId}/><Dropdown label="City" name="city" error={!!errors.city} errorMessage={errors.city}>  …</Dropdown>

اضبط ⁦error⁩ و⁦errorMessage⁩ معاً. تبقى الرسالة دون ⁦error⁩ مخفية، بينما يعلّم ⁦error⁩ وحده الحقل دون أن يخبر المستخدم بالسبب.

التواريخ القادمة من الخادم

يرسل الخادم التواريخ نصوصاً بصيغة ⁦YYYY-MM-DD⁩. حولها بـ ⁦parseDateOnly⁩ التي تبني ⁦Date⁩ عند منتصف الليل بالتوقيت المحلي وتطلق ⁦RangeError⁩ لأي صيغة أخرى. في المقابل، تقرأ ⁦new Date("2026-10-20"⁩) النص بتوقيت ⁦UTC⁩، مما يظهر ⁦19⁩ أكتوبر في أي منطقة غرب ⁦UTC⁩.

import { DatePicker, parseDateOnly } from '@dev-dga/react';// visit.date arrives as "2026-10-20" (or null)const visitDate = visit.date ? parseDateOnly(visit.date) : null;<DatePicker label="Visit date" name="visitDate" defaultValue={visitDate} />;// Avoid: new Date(visit.date) parses as UTC and shows October 19 west of UTC.