تحكم مقسّم
اختيار واحد من خيارين إلى خمسة خيارات قصيرة، مرسوم كمسار واحد. هو مجموعة أزرار راديو: الأسهم تختار أثناء التنقل، وتنعكس من اليمين إلى اليسار.
client - الحجم لم يُقَس بعد
تحكم مقسّم
client - الحجم لم يُقَس بعداختيار واحد من خيارين إلى خمسة خيارات قصيرة، مرسوم كمسار واحد. هو مجموعة أزرار راديو: الأسهم تختار أثناء التنقل، وتنعكس من اليمين إلى اليسار.
جرّبه
size
fullWidth
invalid
الشيفرة
segmented-control.tsx
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
<SegmentedControlRoot aria-label="View" defaultValue="board">
<SegmentedControlItem value="board">Board</SegmentedControlItem>
<SegmentedControlItem value="table">Table</SegmentedControlItem>
<SegmentedControlItem value="calendar">Calendar</SegmentedControlItem>
</SegmentedControlRoot>الأسهم تنقل وتختار معًا، وتحت TopClassProvider باتجاه من اليمين إلى اليسار ينتقل السهم الأيمن إلى المقطع السابق.
كل الحالات، بدون تمرير الفأرة
4 حالاتأو استورده من الحزمة
topclassui/segmented-control
import { SegmentedControlRoot, SegmentedControlItem } from 'topclassui/segmented-control'ثبّت مرة واحدة:npm i topclassui@beta zod
أمثلة
- لاختيار واحد من خيارين إلى خمسة خيارات قصيرة: طريقة عرض، وضع، نطاق. يبقى مقطع واحد مختارًا دائمًا، وللخيارات الأطول تُقرأ
RadioGroupبشكل أفضل. - جزءان،
SegmentedControlRootوSegmentedControlItem، ويعمل في المتصفح. صفحة الخادم تمرّرdefaultValueوحدها، وvalueالمتحكَّم بها مكانها ملف عميل. - يحتاج المسار إلى اسم ومقطع مختار: مرّر
aria-labelأوaria-labelledby، وvalueأوdefaultValue. الأنواع ترفض مسارًا بدونهما. invalidيضعaria-invalidعلى المسار، فيتحول خطه إلى الأحمر ويزداد سمكًا. وfullWidthيملأ الحاوية ويقسمها بالتساوي، لعدد قليل من المقاطع القصيرة.- عندما لا تتسع المقاطع يتمرر المسار أفقيًا داخل نفسه ولا تتمرر الصفحة، والتسمية الأطول من مقطعها تنتهي بعلامة حذف.
iconعلى المقطع يُرسم قبل تسميته. هو للزينة فقط: التسمية هي ما يسمّي المقطع.
basic
basic.tsx
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
/** A view switch: no name and no form, one segment always selected. The track is named by aria-label, and each
* segment by its own text. */
export default function Demo() {
return (
<SegmentedControlRoot aria-label="View" defaultValue="board">
<SegmentedControlItem value="board">Board</SegmentedControlItem>
<SegmentedControlItem value="table">Table</SegmentedControlItem>
<SegmentedControlItem value="calendar">Calendar</SegmentedControlItem>
</SegmentedControlRoot>
)
}
with-icons
with-icons.tsx
import { monitor } from 'topclassui/icons/monitor'
import { moon } from 'topclassui/icons/moon'
import { sun } from 'topclassui/icons/sun'
import { Svg } from 'topclassui/svg'
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
/** The theme switcher of the docs: an icon before each label. The icon is decoration; the label names the segment. */
export default function Demo() {
return (
<SegmentedControlRoot aria-label="Theme" defaultValue="light">
<SegmentedControlItem value="light" icon={<Svg icon={sun} />}>Light</SegmentedControlItem>
<SegmentedControlItem value="dark" icon={<Svg icon={moon} />}>Dark</SegmentedControlItem>
<SegmentedControlItem value="system" icon={<Svg icon={monitor} />}>System</SegmentedControlItem>
</SegmentedControlRoot>
)
}
sizes
sizes.tsx
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
/** The five sizes, one track each, the first segment selected. A segment is never under 24 px, 44 under a finger. */
export default function Demo() {
return (
<div className="grid justify-items-start gap-4">
<SegmentedControlRoot size="xs" aria-label="Extra small" defaultValue="on">
<SegmentedControlItem value="on">Extra small</SegmentedControlItem>
<SegmentedControlItem value="off">Off</SegmentedControlItem>
</SegmentedControlRoot>
<SegmentedControlRoot size="sm" aria-label="Small" defaultValue="on">
<SegmentedControlItem value="on">Small</SegmentedControlItem>
<SegmentedControlItem value="off">Off</SegmentedControlItem>
</SegmentedControlRoot>
<SegmentedControlRoot size="md" aria-label="Medium" defaultValue="on">
<SegmentedControlItem value="on">Medium</SegmentedControlItem>
<SegmentedControlItem value="off">Off</SegmentedControlItem>
</SegmentedControlRoot>
<SegmentedControlRoot size="lg" aria-label="Large" defaultValue="on">
<SegmentedControlItem value="on">Large</SegmentedControlItem>
<SegmentedControlItem value="off">Off</SegmentedControlItem>
</SegmentedControlRoot>
<SegmentedControlRoot size="xl" aria-label="Extra large" defaultValue="on">
<SegmentedControlItem value="on">Extra large</SegmentedControlItem>
<SegmentedControlItem value="off">Off</SegmentedControlItem>
</SegmentedControlRoot>
</div>
)
}
full-width
Range
full-width.tsx
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
/** fullWidth: the track fills its parent and the segments share it equally. For a few short segments. */
export default function Demo() {
return (
<div className="grid max-w-sm gap-2">
<span id="segmented-control-full-label" className="text-sm text-body">Range</span>
<SegmentedControlRoot aria-labelledby="segmented-control-full-label" fullWidth defaultValue="week">
<SegmentedControlItem value="day">Day</SegmentedControlItem>
<SegmentedControlItem value="week">Week</SegmentedControlItem>
<SegmentedControlItem value="month">Month</SegmentedControlItem>
</SegmentedControlRoot>
</div>
)
}
form
form.tsx
import { Button } from 'topclassui/button'
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
/** With a name the track is a form control: it sends view=table, before any script runs and after. */
export default function Demo() {
return (
<form className="grid justify-items-start gap-3">
<span id="segmented-control-form-label" className="text-sm text-body">Default view</span>
<SegmentedControlRoot aria-labelledby="segmented-control-form-label" name="view" defaultValue="table">
<SegmentedControlItem value="board">Board</SegmentedControlItem>
<SegmentedControlItem value="table">Table</SegmentedControlItem>
<SegmentedControlItem value="calendar">Calendar</SegmentedControlItem>
</SegmentedControlRoot>
<Button type="submit">Save</Button>
</form>
)
}
many
many.tsx
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
const SECTIONS = ['Overview', 'Activity log', 'Team members', 'Billing and invoices', 'Integrations', 'Security settings', 'Notifications']
/** Seven long segments, more than a phone is wide: the track scrolls sideways inside itself and the page does not.
* An arrow key brings the next segment into view. */
export default function Demo() {
return (
<SegmentedControlRoot aria-label="Workspace" defaultValue="Overview">
{SECTIONS.map((section) => <SegmentedControlItem key={section} value={section}>{section}</SegmentedControlItem>)}
</SegmentedControlRoot>
)
}
long-content
long-content.tsx
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
/** A segment holds one line: a label wider than the track ends in an ellipsis, and the full text stays its name. */
export default function Demo() {
return (
<SegmentedControlRoot aria-label="Files" defaultValue="recent">
<SegmentedControlItem value="recent">Recently opened by anyone in this workspace</SegmentedControlItem>
<SegmentedControlItem value="starred">Starred</SegmentedControlItem>
<SegmentedControlItem value="shared">Shared with me</SegmentedControlItem>
</SegmentedControlRoot>
)
}
بالعربية، من اليمين إلى اليسار
طريقة العرض
rtl.tsx
import { TopClassProvider } from 'topclassui/provider'
import { SegmentedControlItem, SegmentedControlRoot } from 'topclassui/segmented-control'
/** Arabic, inside TopClassProvider as the root layout would mount it. The first segment is at the right, and
* ArrowRight goes to the segment before. */
export default function Demo() {
return (
<TopClassProvider direction="rtl" locale="ar">
<div dir="rtl" lang="ar" className="grid justify-items-start gap-2">
<span id="segmented-control-rtl-label" className="text-sm text-body">طريقة العرض</span>
<SegmentedControlRoot aria-labelledby="segmented-control-rtl-label" name="view" defaultValue="table">
<SegmentedControlItem value="board">لوحة</SegmentedControlItem>
<SegmentedControlItem value="table">جدول</SegmentedControlItem>
<SegmentedControlItem value="calendar">تقويم</SegmentedControlItem>
</SegmentedControlRoot>
</div>
</TopClassProvider>
)
}
الخصائص
SegmentedControlRoot
SegmentedControlItem
لوحة المفاتيح وإمكانية الوصول
- المسار
radiogroup، وكل مقطع<button>أصلي بالدورrole="radio"، يُسمّى بنصه. الأسهم تتخطى المقطع المعطّل وتلتف عند الطرفين، وEnter وHome وEnd لا تفعل شيئًا. - قبل الترطيب يكون المقطع المختار هو محطة Tab الوحيدة، فيصل إليه Tab منذ أول عرض.
- لا يقل ارتفاع كل مقطع عن 24 بكسل، و44 تحت مؤشر لمس. حلقة التركيز تُرسم داخل المقطع الذي عليه التركيز، وفي وضع الألوان القسرية يأخذ المقطع المختار لون التمييز من النظام.
من اليمين إلى اليسار
يقع المقطع الأول على اليمين، وتنعكس الأسهم مع الاتجاه الذي يمرّره TopClassProvider من التخطيط.
الخادم والمتصفح
مكوّن متصفح: يُعرض على الخادم ثم يُرطَّب في المتصفح.
الملفات: index.tsx, client.tsx.
على الشارة: client - الحجم لم يُقَس بعد.