Table
عرض شاملimport { SortDirection, Table, TableAlign, TableBody, TableCell, TableFooter } from '@nili/ui';هذه صفحة العرض الشامل الخاصة بالمكتبة، معروضة حيّة. غيّر اللغة أو السمة من الأعلى وسَتتبعك.
أساسي
عنصر جدول حقيقي، لا عناصر div بأدوار صفوف. فقارئات الشاشة تمنح الجدول الحقيقي وضع تنقّل — الانتقال خلية خلية وسماع ترويسة العمود مع كل قفزة — ولا ينجو شيء من ذلك في إعادة الكتابة. وlabel يسمّيه؛ فجدول بلا اسم يُعلَن بوصفه «جدول» لا أكثر.
| العميل | المبلغ | الحالة |
|---|---|---|
| Acme Co. | 1,200 | مدفوعة |
| Globex | 480 | معلّقة |
| Initech | 3,150 | متأخّرة |
| Umbrella | 90 | مدفوعة |
import {
Table, TableHeader, TableBody, TableRow, TableHead, TableCell, Badge,
} from "@nili/ui"
export function Example() {
return (
<Table label="Invoices">
<TableHeader>
<TableRow>
<TableHead>Customer</TableHead>
<TableHead align="end">Amount</TableHead>
<TableHead>Status</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{rows.map((row) => (
<TableRow key={row.id}>
<TableCell header>{row.customer}</TableCell>
<TableCell align="end" numeric>{row.amount}</TableCell>
<TableCell>
<Badge appearance="light" color="green" dot>Paid</Badge>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}ارتفاع الصف والتعليق
ارتفاعان للصف — X-Large (٦٤) وLarge (٤٨). ويسمّي caption الظاهر الجدولَ، فلا حاجة إلى label أيضًا.
| العميل | المبلغ |
|---|---|
| Acme Co. | 1,200 |
| Globex | 480 |
| Initech | 3,150 |
import { Table } from "@nili/ui"
export function Example() {
return (
<>
<Table size="xl" label="Invoices">…</Table>
<Table size="lg" caption={t('sizes.caption')}>…</Table>
</>
)
}أولوية الخلية
مدى علوّ صوت العمود. فـleading هوية الصف، وpassive بيانات وصفية يتجاوزها البصر، وnone يُسقط تنسيق الخط كليًا كي تستضيف الخلية عنصر تحكّم.
| العميل | البريد | آخر تحديث | الإجراءات |
|---|---|---|---|
A | billing@acme.com | قبل يومين | |
G | ap@globex.io | قبل يومين | |
I | finance@initech.dev | قبل يومين |
import { Table, TableCell } from "@nili/ui"
export function Example() {
return (
<TableRow>
<TableCell priority="leading" header>Acme Co.</TableCell>
<TableCell priority="regular">billing@acme.com</TableCell>
<TableCell priority="passive">Updated 2 days ago</TableCell>
<TableCell priority="none">
<CompactButton aria-label="More"><Icon icon={RiMore2Fill} /></CompactButton>
</TableCell>
</TableRow>
)
}الفرز
يرسم sortable زرّ الفرز ويدير aria-sort على الخلية. ويقع على العمود المفروز فقط — فـ«none» على كل عمود آخر ضجيج يقرأه قارئ الشاشة بصوت عالٍ.
| Initech | 3,150 |
|---|---|
| Acme Co. | 1,200 |
| Globex | 480 |
| Umbrella | 90 |
import { TableHead } from "@nili/ui"
export function Example() {
const [sort, setSort] = useState({ key: "amount", direction: "desc" })
return (
<TableHead
align="end"
sortable
sortDirection={sort.key === "amount" ? sort.direction : null}
sortLabel={t('sorting.byAmount')}
onSort={(direction) => setSort({ key: "amount", direction })}
>
Amount
</TableHead>
)
}التحديد والترويسة الملتصقة والتذييل
يبرز selected صفًّا ويضيف aria-selected. أما interactive فمجرّد دلالة تحويم — فصفٌّ يفتح سجلًّا بـonClick غير مرئي لقارئ الشاشة، لذا ضع رابطًا أو زرًّا حقيقيًا في خلية ودع إبراز الصف يتبعه.
| العميل | المبلغ | |
|---|---|---|
| Acme Co. | 1,200 | |
| Globex | 480 | |
| Initech | 3,150 | |
| Umbrella | 90 | |
| تم تحديد 1 من 4 | 4,920 | |
import { Table, TableRow, TableHead, TableCell, Checkbox } from "@nili/ui"
export function Example() {
return (
<Table label="Invoices" stickyHeader>
<TableHeader sticky>
<TableRow>
<TableHead state="empty"><Checkbox aria-label="Select all" /></TableHead>
<TableHead>Customer</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow selected interactive>
<TableCell priority="none"><Checkbox aria-label="Select row" checked /></TableCell>
<TableCell header>Globex</TableCell>
</TableRow>
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={2}>4 invoices</TableCell>
</TableRow>
</TableFooter>
</Table>
)
}Table
extends ComponentPropsWithoutRef<'table'>
باقي الخصائص
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| size | xl | lg | xl | |
| label | string | — | Accessible name. A table with no name is announced as "table" and nothing else, which is useless on a page with three of them. |
| caption | ReactNode | — | Visible caption, rendered above the table. Also names it. |
| stickyHeader | boolean | false | Keeps the header visible while the body scrolls. |
| scrollable | boolean | true | Wrap in a scrollable, keyboard-focusable region. On by default: a table that overflows horizontally is unreachable by keyboard otherwise. |
| scrollClassName | string | — | Class for the scroll wrapper. Use `className` for the `<table>`. |
TableCaption
داخليTableCell
extends Omit< ComponentPropsWithoutRef<'td'>, 'align' >
محاور التنويع — من تعريف cva للمكوّن
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| align | start | center | end | start | |
| priority | leading | regular | passive | none | regular | Figma's `Priority` — how loudly the column reads. `leading` is the row's identity, `passive` is metadata the eye should skip, `none` drops the type styling so the cell can host a control. |
| numeric | boolean | false | Tabular figures, so a column of numbers lines up. |
| size | xl | lg | xl |
باقي الخصائص
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| header | boolean | false | Makes the cell a row header (`<th scope="row">`). The first column of a data table usually is one, and marking it lets a screen reader say which row it is reading. |
TableHead
extends Omit< ComponentPropsWithoutRef<'th'>, 'align' >
محاور التنويع — من تعريف cva للمكوّن
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| align | start | center | end | start | |
| state | default | disabled | empty | default | Figma's header `State`. `empty` marks a header with no label — the checkbox column — so it is not announced as naming its column. |
| size | xl | lg | xl |
باقي الخصائص
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| sortable | boolean | false | Renders a sort button and manages `aria-sort` on the cell. |
| sortDirection | desc | asc | — | Current direction, or `null` when this column is not the sort key. |
| onSort | (direction: SortDirection) => void | — | |
| sortLabel | string | — | Accessible name for the sort button, e.g. `` (c) => `Sort by ${c}` ``. |
TableHeader
محاور التنويع — من تعريف cva للمكوّن
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| sticky | 'true' | 'false' | false |
TableRow
extends ComponentPropsWithoutRef<'tr'>
محاور التنويع — من تعريف cva للمكوّن
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| selected | boolean | false | Highlights the row and adds `aria-selected`. |
| interactive | boolean | false | Hover affordance for a row that navigates. The click target must still be a real control *inside* the row — see the note in the component. |
باقي الخصائص
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — |
TableScroll
داخليTableSortButton
داخليمحاور التنويع — من تعريف cva للمكوّن
| الخاصيّة | النوع | الافتراضي | ملاحظات |
|---|---|---|---|
| align | start | center | end | start | |
| sorted | 'true' | 'false' | false |