خانة الاختيار (Checkbox)
يتيح لك مكوّن خانة الاختيار (Checkbox) تحديد خيار أو إلغاء تحديده.
التثبيت
npx @smicolon/fasla-ui@latest add checkboxقبل إضافة أي مكوّن، شغّل الأمر init مرة واحدة في مشروعك. التفاصيل في صفحة التثبيت.
معاينة
الأنماط (variant)
تأتي خانة الاختيار بنمطين (variant) كما في Figma. النمط (variant) default هو الافتراضي، ويعرض المربع مع تسميته فقط. أما النمط (variant) layout فيضعهما داخل بطاقة بإطار، ويمكن النقر في أي مكان منها، وتتلوّن خلفيتها عند حالة التمرير (hover).
defaultlayoutالأحجام
تتوفر خانة الاختيار بثلاثة أحجام، والحجم الافتراضي هو md. يبلغ طول ضلع المربع 16 أو 20 أو 24px، ويبقى حجم التسمية ثابتًا.
defaultlayoutالحالات
تظهر علامة الصح عند التحديد، وتظهر الشرطة عند التحديد الجزئي (indeterminate)، ولا تنعكس أيّ منهما في الاتجاه من اليمين إلى اليسار. تأتي حالتا التمرير (hover) والتركيز (focus) من CSS مباشرة، لا من خاصية. مرّر المؤشر فوق خانة الاختيار، أو انتقل إليها بمفتاح Tab، لتراهما. وعند التعطيل يظهر المكوّن كله باهتًا، بما فيه التسمية والوصف.
defaultlayoutإتاحة الوصول (accessibility)
- تعمل خانة الاختيار مع لوحة المفاتيح تلقائيًا: تتبدّل حالتها بمفتاح المسافة، وتُرسَل مع النموذج دون شيفرة إضافية، لأنها مبنية على عنصر
<input type="checkbox">الأصلي. - تضبط الخاصية
indeterminateحالة العنصر نفسه، فيعلن قارئ الشاشة أن الخانة محددة جزئيًا. وتزول هذه الحالة عند النقر كما في المتصفح، لذا حدّث الخاصية من حالة تطبيقك. - يأخذ قارئ الشاشة اسم الخانة من نص
labelوحده، ويقرأ نصdescriptionبعده لأنه مرتبط بالخانة عبرaria-describedby. أما المربع المرسوم فمخفي عن التقنيات المساعدة. - أضف
aria-labelلخانة الاختيار التي تظهر بلاlabel، ليعرف قارئ الشاشة ما تتحكم فيه. - تتبع خانة الاختيار اتجاه الصفحة المحدد في السمة (attribute)
dir، فيبقى المربع في بداية السطر في الاتجاهين.
الخصائص (props)
| الخاصية | النوع | الافتراضي | الوصف |
|---|---|---|---|
variant | "default" | "layout" | "default" | المربع وحده، أو داخل بطاقة بإطار. |
size | "sm" | "md" | "lg" | "md" | حجم خانة الاختيار. |
checked | boolean | لا يوجد | حالة التحديد، وتُستخدم مع onChange. |
defaultChecked | boolean | false | الحالة الأولى عندما لا تتحكم بالقيمة. |
indeterminate | boolean | false | يعرض شرطة لمجموعة محددة جزئيًا. |
disabled | boolean | false | يعطّل الخانة ويُظهرها باهتة. |
label | string | لا يوجد | نص التسمية. |
description | string | لا يوجد | سطر ثانوي تحت التسمية. |
name | string | لا يوجد | الاسم الذي يُرسَل مع النموذج. |
value | string | لا يوجد | القيمة التي تُرسَل عند التحديد. |
onChange | (event) => void | لا يوجد | تُستدعى عند تغيير حالة الخانة. |
className | string | لا يوجد | أصناف (class) إضافية للمكوّن. |
الاستخدام
خانة اختيار بسيطة
"use client"
import { Checkbox } from "@/components/ui/checkbox"
export function TermsCheckbox() {
return <Checkbox name="terms" aria-label="أوافق على الشروط والأحكام" />
}مع تسمية
"use client"
import { Checkbox } from "@/components/ui/checkbox"
export function TermsCheckbox() {
return <Checkbox name="terms" label="أوافق على الشروط والأحكام" />
}مع وصف
"use client"
import { Checkbox } from "@/components/ui/checkbox"
export function MarketingCheckbox() {
return (
<Checkbox
name="marketing"
label="رسائل العروض"
description="تصلك رسائل عن المنتجات والميزات الجديدة"
/>
)
}داخل بطاقة بإطار
"use client"
import { Checkbox } from "@/components/ui/checkbox"
export function SecurityCheckbox() {
return <Checkbox name="security" variant="layout" label="تنبيهات الأمان" />
}أحجام مختلفة
"use client"
import { Checkbox } from "@/components/ui/checkbox"
export function CheckboxSizes() {
return (
<div className="flex flex-col gap-3">
<Checkbox size="sm" label="صغير" />
<Checkbox size="md" label="متوسط" />
<Checkbox size="lg" label="كبير" />
</div>
)
}تحديد الكل مع خانة رئيسية محددة جزئيًا
"use client"
import { useState } from "react"
import { Checkbox } from "@/components/ui/checkbox"
const files = ["التقرير السنوي", "الفواتير", "العقد"]
export function SelectAllFiles() {
const [selected, setSelected] = useState<string[]>([])
const all = selected.length === files.length
return (
<div className="flex flex-col gap-3">
<Checkbox
label="تحديد كل الملفات"
checked={all}
indeterminate={selected.length > 0 && !all}
onChange={(e) => setSelected(e.target.checked ? files : [])}
/>
<div className="flex flex-col gap-3 ps-8">
{files.map((file) => (
<Checkbox
key={file}
size="sm"
label={file}
checked={selected.includes(file)}
onChange={(e) =>
setSelected((prev) =>
e.target.checked ? [...prev, file] : prev.filter((f) => f !== file)
)
}
/>
))}
</div>
</div>
)
}عدد الملفات المحددة: 1