# رفع الملفات

> يضيف الناس الملفات بسحبها إلى منطقة الإفلات أو باختيارها. لكل ملف سطر باسمه وحجمه وتقدّم رفعه، يُلغى أثناء الرفع ويُزال بعد اكتماله. في العربية تنعكس الأسطر ويمتلئ شريط التقدّم من اليمين.

الصفحة: https://ritla.app/ar/ui/file-upload

## التثبيت

يُفتح التثبيت من السجل قريبًا، وهذه هي الأوامر التي ستشغّلها حينها.

```bash
npx ritla add file-upload
```

أو بأداة shadcn:

```bash
npx shadcn@latest add @ritla/file-upload
```

الاعتماديات: `@base-ui/react`، `@phosphor-icons/react`

## الاستخدام

```tsx
import { FileUpload, FileUploadItem, FileUploadList } from "@/components/ui/file-upload";

<FileUpload
  label="Receipts"
  accept=".pdf,.jpg"
  maxSize={10_000_000}
  helperText="PDF or JPG, up to 10 MB each"
  onFilesAdd={(accepted, rejected) => startUploads(accepted, rejected)}
>
  <FileUploadList>
    {files.map((file) => (
      <FileUploadItem
        key={file.id}
        name={file.name}
        size={file.size}
        status={file.status}
        progress={file.progress}
        onCancel={() => cancel(file.id)}
        onRemove={() => remove(file.id)}
      />
    ))}
  </FileUploadList>
</FileUpload>
```

## أمثلة

### حالات السطر

أثناء الرفع زر X يلغي (`status="uploading"`، و`progress` من 0 إلى 100، أو `null` حين لا يُعرف). بعد الاكتمال سلة المهملات تزيل والشريط ممتلئ. عند الفشل يظهر السبب تحت الاسم مع «إعادة المحاولة» (`onRetry`). `disabled` يخفت المنطقة والأسطر.

معاينة حيّة: https://ritla.app/_kanz/preview/arabic/file-upload-states

### ملف واحد، والكاميرا على الجوال

`multiple={false}` يأخذ ملفًا واحدًا، و`capture="environment"` يفتح الكاميرا الخلفية على الجوال حيث لا سحب ولا إفلات. اسم الملف يبقى كما سمّاه صاحبه، بأي لغة كان.

معاينة حيّة: https://ritla.app/_kanz/preview/arabic/file-upload-single

## مرجع الخصائص

### `FileUpload`

| الخاصية | النوع | الافتراضي | الوصف |
| --- | --- | --- | --- |
| `label` | `ReactNode` | لا يوجد | التسمية فوق منطقة الإفلات، باسم المستند المطلوب («الإيصالات»). |
| `required` | `boolean` | `false` | النجمة بعد التسمية. |
| `helperText` | `ReactNode` | لا يوجد | تحت المنطقة: الأنواع والحجم والعدد المسموح، قبل أن يحاول أحد. |
| `error` | `ReactNode` | لا يوجد | خطأ للرفع كله («أضف إيصالًا واحدًا على الأقل»)، يحل محل النص المساعد بالأحمر. |
| `accept` | `string` | لا يوجد | الأنواع المسموحة كما في `<input accept>`: ‏`.pdf,.jpg` أو `image/*`. يُفحص كل ملف، مُفلتًا أو مختارًا. |
| `maxSize` | `number` | لا يوجد | أكبر حجم للملف بالبايت. |
| `maxFiles` | `number` | لا يوجد | أكبر عدد للملفات في القائمة (الأسطر الفاشلة لا تُحسب). |
| `multiple` | `boolean` | `true` | أكثر من ملف في المرة الواحدة. |
| `capture` | `"environment" \| "user"` | لا يوجد | يفتح الكاميرا على الجوال بدل منتقي الملفات. |
| `disabled` | `boolean` | `false` | State = Disabled. |
| `name` | `string` | لا يوجد | اسم حقل الملف لنموذج يرسل الملفات بنفسه. |
| `onFilesAdd` | `(accepted: File[], rejected: FileRejection[]) => void` | لا يوجد | الملفات المختارة أو المُفلتة بعد فحصها: ابدأ رفع `accepted` واعرض كل مرفوض في سطر بسببه (`type` أو `size` أو `count`). المكوّن لا يرفع شيئًا بنفسه. |
| `dropText` | `ReactNode` | لا يوجد | سطر المنطقة. الافتراضي «اسحب الملفات وأفلتها هنا أو انقر لاختيارها.» بلغة الصفحة. |
| `browseLabel` | `ReactNode` | لا يوجد | نص الزر. الافتراضي «اختيار ملفات»، أو «اختيار ملف» لملف واحد. |

### `FileUploadItem`

| الخاصية | النوع | الافتراضي | الوصف |
| --- | --- | --- | --- |
| `name` | `string` | لا يوجد | اسم الملف كما سمّاه صاحبه، لا يُترجم ولا يُنقل حرفيًا. |
| `size` | `number` | لا يوجد | الحجم بالبايت، يُعرض بلغة الصفحة وأرقامها. |
| `status` | `"uploading" \| "done" \| "error"` | `"uploading"` | يحدد زر السطر: X يلغي أثناء الرفع، وسلة المهملات تزيل بعده. |
| `progress` | `number \| null` | `null` | التقدّم من 0 إلى 100 أثناء الرفع؛ `null` للشريط المخطط حين لا يُعرف. |
| `error` | `ReactNode` | لا يوجد | سبب الفشل بكلمات واضحة («الحجم أكبر من 10 ميغابايت»). |
| `onCancel / onRemove` | `() => void` | لا يوجد | زر السطر. بعد الإزالة ينتقل التركيز إلى زر السطر التالي، أو السابق، أو زر «اختيار ملفات». |
| `onRetry` | `() => void` | لا يوجد | يعرض «إعادة المحاولة» في السطر الفاشل. |
| `disabled` | `boolean` | `false` | State = Disabled. |
| `showFileSize / showIcon` | `boolean` | `true` | خاصيتا Show file size وShow icon. |

## إمكانية الوصول

لوحة المفاتيح (المفاتيح مكتوبة للاتجاه من اليسار إلى اليمين، ويتبادل السهمان في الاتجاه من اليمين إلى اليسار):

- `Tab`: إلى زر «اختيار ملفات»، ثم إلى زر كل سطر.
- `Enter`: على زر «اختيار ملفات» يفتح نافذة الملفات؛ على زر السطر يلغي أو يزيل.
- `Space`: مثل Enter.

ARIA:

- منطقة الإفلات اختصار فقط: زر «اختيار ملفات» زر حقيقي يفتح حقل ملف أصليًا، فيعمل كل شيء بلوحة المفاتيح وقارئ الشاشة وعلى الجوال.
- الرفع كله مجموعة باسم التسمية، وزر «اختيار ملفات» يُقرأ مع التسمية ويوصف بالنص المساعد، فيسمع قارئ الشاشة الأنواع والحدود قبل الاختيار.
- زر كل سطر يحمل اسم الملف: «إلغاء رفع hotel-invoice.pdf»، «إزالة taxi-receipt.jpg».
- منطقة حيّة (`aria-live="polite"`) تعلن اكتمال الرفع أو فشله، لا كل نسبة. كل شريط تقدّم `role="progressbar"` باسم ملفه.

## ملاحظات العربية والاتجاه

ينعكس:

- الأسطر: أيقونة النوع في البداية (اليمين)، ثم الاسم والحجم، والزر في النهاية (اليسار).
- شريط التقدّم يمتلئ من بداية السطر، أي من اليمين.

لا ينعكس:

- أيقونة النوع لا تنعكس، وامتدادها يبقى لاتينيًا (PDF، JPG).
- اسم الملف يبقى كما كُتب، معزولًا بـ`dir="auto"`؛ لا يُترجم ولا يُنقل حرفيًا.
- أيقونة السحابة والسلة وX لا تنعكس.

الأرقام: الحجم يتبع `numerals` ووحدته بلغة الصفحة: «2.3 MB» بالإنجليزية و«2.3 ميغابايت» أو «٢٫٣ ميغابايت» بالعربية.

الخط العربي: الاسم `body-16` بوزن Medium والحجم `body-12`، بأسطر عربية أطول؛ الاسم الطويل يُقصّ بنقاط ولا يلتف.
