شريط تحديد القيمة (Slider)
يتيح لك مكوّن شريط تحديد القيمة أن تختار قيمة، أو نطاقًا من القيم، بسحب مقبض على مسار أو بمفاتيح الأسهم. يأتي بثلاثة أحجام، ويمتلئ مساره من بداية السطر، فيتبع اتجاه القراءة في الصفحة.
التثبيت
npx @smicolon/fasla-ui@latest add sliderقبل إضافة أي مكوّن، شغّل الأمر init مرة واحدة في مشروعك. التفاصيل في صفحة التثبيت.
معاينة
الأحجام
تغيّر الأحجام الثلاثة المسار وحده: ارتفاعه في الحجم sm هو 6px، وفي الحجم md هو 8px، وفي الحجم lg هو 12px. أما المقبض فقطره 16px في كل الأحجام، فيبقى ارتفاع المكوّن 16px دائمًا. ويمتد المكوّن بعرض الحاوية التي يوضع فيها.
القيمة
يدل الجزء الممتلئ من المسار على القيمة، فهو يمتد من بداية المسار إلى المقبض. عند الحد الأدنى يقف المقبض في بداية المسار، وعند الحد الأقصى يقف في نهايته، ولا يتجاوز أيًّا من طرفيه. حدّد مدى القيم بالخاصية min والخاصية max، وحدّد مقدار الخطوة الواحدة بالخاصية step.
الحالات
التركيز حالة حقيقية، لا خاصية: انتقل إلى المقبض بمفتاح Tab. عند التركيز من لوحة المفاتيح تظهر حول المقبض حلقة بعرض 4px بدل ظله. أما الضغط أو السحب بالمؤشر فلا يُظهر الحلقة.
النطاق
مرّر مصفوفة إلى الخاصية value أو الخاصية defaultValue، مثل [25, 75]، فيظهر مقبض لكل قيمة ويمتلئ المسار بين المقبضين. وتعيد الدالة onValueChange عندئذٍ مصفوفة. وإذا سحبت أحد المقبضين إلى ما بعد الآخر دفعه أمامه، وتُبقي الخاصية minStepsBetweenValues مسافة بينهما.
التسمية والقيمة
لا يعرض مكوّن شريط تحديد القيمة أي نص. ضع فوقه تسمية ظاهرة واربطهما بالسمة aria-labelledby، واعرض القيمة الحالية بجانب التسمية، حتى لا يضطر القارئ إلى تقديرها من المسار.
إتاحة الوصول (accessibility)
- يحمل كل مقبض حقل نطاق حقيقيًا بالدور
slider، فيُعلن قارئ الشاشة اسمه وقيمته والحدين الأدنى والأقصى. ومع الخاصيةnameترسل حقول مخفية القيمة مع النموذج. - ينقلك مفتاح Tab إلى المقابض واحدًا بعد الآخر. تحرّك مفاتيح الأسهم المقبض خطوة واحدة، ويحرّكه مفتاح Page Up ومفتاح Page Down عشر خطوات، وهي قيمة الخاصية
largeStep. وينقله مفتاح Home إلى الحد الأدنى، ومفتاح End إلى الحد الأقصى. - سمِّ المكوّن دائمًا: بالسمة
aria-labelledbyمع تسمية ظاهرة، أو بالسمةaria-labelإن لم توجد تسمية. وفي النطاق، أعطِ كل مقبض اسمه الخاص بالدالةgetAriaLabel، مثل «أدنى سعر» و«أعلى سعر». - يسمع قارئ الشاشة الرقم وحده. وإذا احتاج الرقم إلى وحدة، كالعملة، فأعِد النص كاملًا من الدالة
getAriaValueText. - يتبع الاتجاه السمة
dirفي الصفحة، ولا توجد له خاصية. في الصفحات من اليمين إلى اليسار يمتلئ المسار من بداية السطر ويبدأ المقبض منها، ويحرّك كل سهم المقبض في الاتجاه الذي يشير إليه، فالسهم المتجه إلى نهاية السطر يرفع القيمة.
الخصائص (props)
Slider
| الخاصية | النوع | الافتراضي | الوصف |
|---|---|---|---|
value | number | number[] | لا يوجد | القيمة الحالية: رقم، أو مصفوفة في النطاق. وتُستخدم مع الدالة onValueChange. |
defaultValue | number | number[] | لا يوجد | القيمة عند العرض الأول. وتعطي المصفوفة مقبضًا لكل قيمة. |
onValueChange | (value) => void | لا يوجد | تُستدعى بالقيمة الجديدة أثناء تحرّك المقبض. |
onValueCommitted | (value) => void | لا يوجد | تُستدعى مرة واحدة بالقيمة النهائية، عند رفع المؤشر أو تغيير القيمة بمفتاح. |
size | "sm" | "md" | "lg" | "md" | ارتفاع المسار: 6 أو 8 أو 12px. وقطر المقبض 16px في كل الأحجام. |
min | number | 0 | أدنى قيمة. |
max | number | 100 | أعلى قيمة. |
step | number | 1 | مقدار الخطوة الواحدة، وتلتزم القيمة بمضاعفاته. |
largeStep | number | 10 | مقدار ما يحرّكه مفتاح Page Up ومفتاح Page Down. |
minStepsBetweenValues | number | 0 | في النطاق، أقل عدد من الخطوات بين المقبضين. |
aria-label | string | لا يوجد | الاسم الذي يقرؤه قارئ الشاشة حين لا توجد تسمية ظاهرة. |
aria-labelledby | string | لا يوجد | معرّف التسمية الظاهرة التي تسمّي المكوّن. |
getAriaLabel | (index) => string | لا يوجد | تسمّي كل مقبض في النطاق برقم ترتيبه: 0 للبداية، و1 للنهاية. |
getAriaValueText | (text, value, index) => string | لا يوجد | تعيد النص الذي يسمعه قارئ الشاشة لقيمة المقبض. |
name | string | لا يوجد | الاسم الذي تُرسل به القيمة مع النموذج. |
form | string | لا يوجد | معرّف النموذج الذي تُرسل معه القيمة، حين يكون المكوّن خارجه. |
className | string | لا يوجد | أصناف إضافية للمكوّن. |
الاستخدام
شريط تحديد القيمة البسيط
import { Slider } from "@/components/ui/slider"
export function VolumeSlider() {
return <Slider name="volume" defaultValue={60} aria-label="مستوى الصوت" />
}نطاق للسعر مع التحكّم بالقيمة
"use client"
import * as React from "react"
import { Slider } from "@/components/ui/slider"
export function PriceFilter() {
const [price, setPrice] = React.useState<readonly number[]>([200, 800])
return (
<Slider
value={price}
onValueChange={setPrice}
min={0}
max={1000}
step={50}
getAriaLabel={(index) => (index === 0 ? "أدنى سعر" : "أعلى سعر")}
/>
)
}مع تسمية ظاهرة وقيمة
"use client"
import * as React from "react"
import { Slider } from "@/components/ui/slider"
export function VolumeField() {
const id = React.useId()
const [volume, setVolume] = React.useState(40)
return (
<div className="flex flex-col gap-3">
<div className="flex items-baseline justify-between gap-4">
<span id={id} className="text-sm font-medium">
مستوى الصوت
</span>
<span className="text-sm tabular-nums text-muted-foreground">{volume}%</span>
</div>
<Slider value={volume} onValueChange={setVolume} aria-labelledby={id} />
</div>
)
}