דילוג לתוכן
uiify

Table

טבלה מובנית עם מתאם אינטראקטיבי אופציונלי.

דפדפנים
Chrome 123+Firefox 121+Safari 17.5+
שכבה
0
גודל חבילה דחוסה
617 B
מגבלת פריסה כפויה
0 ms
מגבלת Axe
0
תואם SSR
כן
חוזה מפורסם
סביבת בדיקות

מגרש ניסויים

שםתפקיד
אליסמנהל
בובעורך
Small
שםתפקיד
אליסמנהל
בובעורך
Medium
שםתפקיד
אליסמנהל
בובעורך
Large
שםתפקיד
אליסמנהל
בובעורך
solid
שםתפקיד
אליסמנהל
בובעורך
outline
שםתפקיד
אליסמנהל
בובעורך
ghost
שםתפקיד
אליסמנהל
בובעורך
import { Table } from "@gunkin/uiify";
const rows = [
  { id: "alice", name: "Alice Chen", role: "Admin", status: "Active" },
  { id: "bob", name: "Bob Smith", role: "Editor", status: "Active" },
  { id: "carol", name: "Carol Lee", role: "Viewer", status: "Invited" },
];
export default function Example() {
  return (
    <Table.Root className="" variant="outline" size="sm">
      <Table.Caption className="">Team members</Table.Caption>
      <Table.Header className="">
        <Table.Row className="" id="team-head">
          <Table.ColumnHeader className="" id="team-name">
            Name
          </Table.ColumnHeader>
          <Table.ColumnHeader className="" id="team-role">
            Role
          </Table.ColumnHeader>
          <Table.ColumnHeader className="" id="team-status">
            Status
          </Table.ColumnHeader>
        </Table.Row>
      </Table.Header>
      <Table.Body className="">
        {rows.map((row) => (
          <Table.Row className="" id={row.id} key={row.id}>
            <Table.RowHeader className="">{row.name}</Table.RowHeader>
            <Table.Cell className="">{row.role}</Table.Cell>
            <Table.Cell className="">{row.status}</Table.Cell>
          </Table.Row>
        ))}
      </Table.Body>
      <Table.Footer className="">
        <Table.Row className="" id="team-total">
          <Table.Cell className="" colSpan={3}>
            {rows.length} members · {rows.filter((row) => row.status === "Active").length} active
          </Table.Cell>
        </Table.Row>
      </Table.Footer>
    </Table.Root>
  );
}
@layer components {
  [data-uiify-table] {
    inline-size: 100%;
    border-collapse: separate;
    border-spacing: 0;
    background: var(--card);
    color: var(--card-foreground);
    font-size: var(--text-sm);
  }
  [data-uiify-table][data-layout="fixed"] {
    table-layout: fixed;
  }
  [data-uiify-table] :is(th, td) {
    padding-block: 0.75rem;
    padding-inline: 1rem;
    text-align: start;
    vertical-align: middle;
    border-block-end: 1px solid var(--border);
    background: inherit;
  }
  [data-uiify-table] :is(th, td)[data-align="center"] {
    text-align: center;
  }
  [data-uiify-table] :is(th, td)[data-align="end"] {
    text-align: end;
  }
  [data-uiify-table] :is(thead, tfoot) {
    background: var(--secondary);
    font-weight: 600;
  }
  [data-uiify-table] tbody tr {
    background: var(--card);
  }
  [data-uiify-table] tbody tr:hover {
    background: var(--secondary);
  }
  [data-uiify-table] tbody tr[data-selected] {
    background: var(--accent);
  }
  [data-uiify-table] caption {
    padding-block: 0.75rem;
    text-align: start;
    color: var(--muted-foreground);
  }
  [data-uiify-table][data-size="sm"] :is(th, td) {
    padding-block: 0.5rem;
    padding-inline: 0.75rem;
  }
  [data-uiify-table][data-size="lg"] :is(th, td) {
    padding-block: 1rem;
    padding-inline: 1.25rem;
  }
  [data-uiify-table][data-variant="outline"] {
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
  }
  [data-uiify-table][data-variant="ghost"] {
    background: transparent;
  }
  [data-uiify-table] :is(thead, tfoot, tr)[data-sticky] {
    position: sticky;
    inset-block-start: var(--uiify-table-sticky-offset, 0);
    z-index: 2;
  }
  [data-uiify-table] tfoot[data-sticky] {
    inset-block: auto var(--uiify-table-sticky-offset, 0);
  }
  [data-uiify-table] :is(th, td)[data-sticky] {
    position: sticky;
    z-index: 1;
  }
  [data-uiify-table] :is(th, td)[data-sticky="start"] {
    inset-inline-start: var(--uiify-table-sticky-offset, 0);
  }
  [data-uiify-table] :is(th, td)[data-sticky="end"] {
    inset-inline-end: var(--uiify-table-sticky-offset, 0);
  }
  [data-uiify-table] :is(thead, tfoot)[data-sticky] :is(th, td)[data-sticky] {
    z-index: 3;
  }
  [data-uiify-table] th > button {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
  }
  [data-uiify-table] th[aria-sort="ascending"] > button::after {
    content: "↑" / "";
  }
  [data-uiify-table] th[aria-sort="descending"] > button::after {
    content: "↓" / "";
  }
  [data-uiify-table] [data-part="expanded-row"] {
    background: var(--secondary);
  }
  [data-uiify-table] :is(button, tr):focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: -2px;
  }
}
import { Table } from "@gunkin/uiify";
const rows = [
  { id: "alice", name: "Alice Chen", role: "Admin", status: "Active" },
  { id: "bob", name: "Bob Smith", role: "Editor", status: "Active" },
  { id: "carol", name: "Carol Lee", role: "Viewer", status: "Invited" },
];
export default function Example() {
  return (
    <Table.Root
      className={
        "group/table [inline-size:100%] [border-collapse:separate] [border-spacing:0] bg-card text-card-foreground t" +
        "ext-sm data-[layout=fixed]:table-fixed data-[variant=outline]:[border-width:1px] data-[variant=outline]:bor" +
        "der-solid data-[variant=outline]:border-border data-[variant=outline]:rounded-lg data-[variant=ghost]:bg-tr" +
        "ansparent [&_:is(thead,tfoot)[data-sticky]_:is(th,td)[data-sticky]]:z-[3] [&_th[aria-sort=ascending]>button" +
        "::after]:content-[\"↑\"_/_\"\"] [&_th[aria-sort=descending]>button::after]:content-[\"↓\"_/_\"\"]"
      }
      variant="outline"
      size="sm"
    >
      <Table.Caption className="[padding-block:0.75rem] text-start text-muted-foreground">Team members</Table.Caption>
      <Table.Header
        className="
          bg-secondary [font-weight:600] data-[sticky]:sticky
          data-[sticky]:[inset-block-start:var(--uiify-table-sticky-offset,_0)] data-[sticky]:z-[2]
        "
      >
        <Table.Row
          className="
            group-data-[part=body]/table-body:bg-card group-data-[part=body]/table-body:hover:bg-secondary
            group-data-[part=body]/table-body:data-[selected]:bg-accent focus-visible:[outline:2px_solid_var(--ring)]
            focus-visible:[outline-offset:-2px] data-[sticky]:sticky
            data-[sticky]:[inset-block-start:var(--uiify-table-sticky-offset,_0)] data-[sticky]:z-[2]
          "
          id="team-head"
        >
          <Table.ColumnHeader
            className="
              [padding-block:0.75rem] [padding-inline:1rem] text-start [vertical-align:middle]
              [border-block-end-width:1px] [border-block-end-style:solid] [border-block-end-color:var(--border)]
              [background:inherit] data-[align=center]:text-center data-[align=end]:text-end
              group-data-[size=sm]/table:[padding-block:0.5rem] group-data-[size=sm]/table:[padding-inline:0.75rem]
              group-data-[size=lg]/table:[padding-block:1rem] group-data-[size=lg]/table:[padding-inline:1.25rem]
              data-[sticky]:sticky data-[sticky]:z-[1]
              data-[sticky=start]:[inset-inline-start:var(--uiify-table-sticky-offset,_0)]
              data-[sticky=end]:[inset-inline-end:var(--uiify-table-sticky-offset,_0)] [[data-sticky]_&]:z-[3]
            "
            id="team-name"
          >
            Name
          </Table.ColumnHeader>
          <Table.ColumnHeader
            className="
              [padding-block:0.75rem] [padding-inline:1rem] text-start [vertical-align:middle]
              [border-block-end-width:1px] [border-block-end-style:solid] [border-block-end-color:var(--border)]
              [background:inherit] data-[align=center]:text-center data-[align=end]:text-end
              group-data-[size=sm]/table:[padding-block:0.5rem] group-data-[size=sm]/table:[padding-inline:0.75rem]
              group-data-[size=lg]/table:[padding-block:1rem] group-data-[size=lg]/table:[padding-inline:1.25rem]
              data-[sticky]:sticky data-[sticky]:z-[1]
              data-[sticky=start]:[inset-inline-start:var(--uiify-table-sticky-offset,_0)]
              data-[sticky=end]:[inset-inline-end:var(--uiify-table-sticky-offset,_0)] [[data-sticky]_&]:z-[3]
            "
            id="team-role"
          >
            Role
          </Table.ColumnHeader>
          <Table.ColumnHeader
            className="
              [padding-block:0.75rem] [padding-inline:1rem] text-start [vertical-align:middle]
              [border-block-end-width:1px] [border-block-end-style:solid] [border-block-end-color:var(--border)]
              [background:inherit] data-[align=center]:text-center data-[align=end]:text-end
              group-data-[size=sm]/table:[padding-block:0.5rem] group-data-[size=sm]/table:[padding-inline:0.75rem]
              group-data-[size=lg]/table:[padding-block:1rem] group-data-[size=lg]/table:[padding-inline:1.25rem]
              data-[sticky]:sticky data-[sticky]:z-[1]
              data-[sticky=start]:[inset-inline-start:var(--uiify-table-sticky-offset,_0)]
              data-[sticky=end]:[inset-inline-end:var(--uiify-table-sticky-offset,_0)] [[data-sticky]_&]:z-[3]
            "
            id="team-status"
          >
            Status
          </Table.ColumnHeader>
        </Table.Row>
      </Table.Header>
      <Table.Body className="group/table-body">
        {rows.map((row) => (
          <Table.Row
            className="
              group-data-[part=body]/table-body:bg-card group-data-[part=body]/table-body:hover:bg-secondary
              group-data-[part=body]/table-body:data-[selected]:bg-accent focus-visible:[outline:2px_solid_var(--ring)]
              focus-visible:[outline-offset:-2px] data-[sticky]:sticky
              data-[sticky]:[inset-block-start:var(--uiify-table-sticky-offset,_0)] data-[sticky]:z-[2]
            "
            id={row.id}
            key={row.id}
          >
            <Table.RowHeader
              className="
                [padding-block:0.75rem] [padding-inline:1rem] text-start [vertical-align:middle]
                [border-block-end-width:1px] [border-block-end-style:solid] [border-block-end-color:var(--border)]
                [background:inherit] data-[align=center]:text-center data-[align=end]:text-end
                group-data-[size=sm]/table:[padding-block:0.5rem] group-data-[size=sm]/table:[padding-inline:0.75rem]
                group-data-[size=lg]/table:[padding-block:1rem] group-data-[size=lg]/table:[padding-inline:1.25rem]
                data-[sticky]:sticky data-[sticky]:z-[1]
                data-[sticky=start]:[inset-inline-start:var(--uiify-table-sticky-offset,_0)]
                data-[sticky=end]:[inset-inline-end:var(--uiify-table-sticky-offset,_0)] [[data-sticky]_&]:z-[3]
              "
            >
              {row.name}
            </Table.RowHeader>
            <Table.Cell
              className="
                [padding-block:0.75rem] [padding-inline:1rem] text-start [vertical-align:middle]
                [border-block-end-width:1px] [border-block-end-style:solid] [border-block-end-color:var(--border)]
                [background:inherit] data-[align=center]:text-center data-[align=end]:text-end
                group-data-[size=sm]/table:[padding-block:0.5rem] group-data-[size=sm]/table:[padding-inline:0.75rem]
                group-data-[size=lg]/table:[padding-block:1rem] group-data-[size=lg]/table:[padding-inline:1.25rem]
                data-[sticky]:sticky data-[sticky]:z-[1]
                data-[sticky=start]:[inset-inline-start:var(--uiify-table-sticky-offset,_0)]
                data-[sticky=end]:[inset-inline-end:var(--uiify-table-sticky-offset,_0)] [[data-sticky]_&]:z-[3]
              "
            >
              {row.role}
            </Table.Cell>
            <Table.Cell
              className="
                [padding-block:0.75rem] [padding-inline:1rem] text-start [vertical-align:middle]
                [border-block-end-width:1px] [border-block-end-style:solid] [border-block-end-color:var(--border)]
                [background:inherit] data-[align=center]:text-center data-[align=end]:text-end
                group-data-[size=sm]/table:[padding-block:0.5rem] group-data-[size=sm]/table:[padding-inline:0.75rem]
                group-data-[size=lg]/table:[padding-block:1rem] group-data-[size=lg]/table:[padding-inline:1.25rem]
                data-[sticky]:sticky data-[sticky]:z-[1]
                data-[sticky=start]:[inset-inline-start:var(--uiify-table-sticky-offset,_0)]
                data-[sticky=end]:[inset-inline-end:var(--uiify-table-sticky-offset,_0)] [[data-sticky]_&]:z-[3]
              "
            >
              {row.status}
            </Table.Cell>
          </Table.Row>
        ))}
      </Table.Body>
      <Table.Footer
        className="
          bg-secondary [font-weight:600] data-[sticky]:sticky
          data-[sticky]:[inset-block:auto_var(--uiify-table-sticky-offset,_0)] data-[sticky]:z-[2]
        "
      >
        <Table.Row
          className="
            group-data-[part=body]/table-body:bg-card group-data-[part=body]/table-body:hover:bg-secondary
            group-data-[part=body]/table-body:data-[selected]:bg-accent focus-visible:[outline:2px_solid_var(--ring)]
            focus-visible:[outline-offset:-2px] data-[sticky]:sticky
            data-[sticky]:[inset-block-start:var(--uiify-table-sticky-offset,_0)] data-[sticky]:z-[2]
          "
          id="team-total"
        >
          <Table.Cell
            className="
              [padding-block:0.75rem] [padding-inline:1rem] text-start [vertical-align:middle]
              [border-block-end-width:1px] [border-block-end-style:solid] [border-block-end-color:var(--border)]
              [background:inherit] data-[align=center]:text-center data-[align=end]:text-end
              group-data-[size=sm]/table:[padding-block:0.5rem] group-data-[size=sm]/table:[padding-inline:0.75rem]
              group-data-[size=lg]/table:[padding-block:1rem] group-data-[size=lg]/table:[padding-inline:1.25rem]
              data-[sticky]:sticky data-[sticky]:z-[1]
              data-[sticky=start]:[inset-inline-start:var(--uiify-table-sticky-offset,_0)]
              data-[sticky=end]:[inset-inline-end:var(--uiify-table-sticky-offset,_0)] [[data-sticky]_&]:z-[3]
            "
            colSpan={3}
          >
            {rows.length} members · {rows.filter((row) => row.status === "Active").length} active
          </Table.Cell>
        </Table.Row>
      </Table.Footer>
    </Table.Root>
  );
}

API

Table.Root (native)

מאפייןטיפוסברירת מחדלתיאור
layout"auto" | "fixed""auto"פריסת טבלה מובנית. בחירה, מיון והרחבה משתמשים בנקודת הכניסה המפורשת TableClient.
size"sm" | "md" | "lg"—ריווח התאים מתוך גיליון הסגנונות שנבחר.
variant"solid" | "outline" | "ghost"—גרסת המשטח מתוך גיליון הסגנונות שנבחר.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.Footer

מאפייןטיפוסברירת מחדלתיאור
stickyboolean—מצמיד את התחתית לסוף ציר הבלוק של מכל הגלילה.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.RowHeader / Table.Cell / Table.ColumnHeader

מאפייןטיפוסברירת מחדלתיאור
align"start" | "center" | "end""start"יישור הטקסט הלוגי לתא זה.
sticky"start" | "end"—מצמיד תא לקצה הלוגי בציר השורה. בתאים נצמדים סמוכים יש להגדיר --uiify-table-sticky-offset בכל תא.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

TableClient.ExpandTrigger / TableClient.ExpandedRow

מאפייןטיפוסברירת מחדלתיאור
rowId*string—מפתח שורה משותף המחבר את כפתור ההרחבה ל-<tr> המובנה שלו. יש להציב <td colSpan={columnCount}> בתוך השורה המורחבת.

TableClient.Root — @gunkin/uiify/components/table/client

מאפייןטיפוסברירת מחדלתיאור
aria-labelstring—שם נגיש לטבלה כשאין כיתוב או כותרת גלויים המציינים את שמה.
aria-labelledbystring—מזהה האלמנט המשמש כתווית הטבלה.
selectionMode"none" | "single" | "multiple""none"קובע אם ניתן לבחור שורות וכמה בו-זמנית; "none" מציג טבלה סטטית פשוטה ללא תפקיד grid.
selectionBehavior"toggle" | "replace"—קובע אם לחיצה על שורה מוסיפה אותה לבחירה או מחליפה את הבחירה הנוכחית.
defaultSelectedKeysIterable<string>—מזהי השורות שנבחרות בתחילה, כשהבחירה מנוהלת בתוך הרכיב ללא בקרה חיצונית.
selectedKeysIterable<string>—מזהי השורות שנבחרות כרגע, כשהבחירה מבוקרת על ידי הקוד הקורא.
onSelectionChange(selectedKeys: Set<string>) => void—נקראת עם קבוצת מזהי השורות הנבחרות החדשה בכל שינוי בבחירה.
defaultSortDescriptorSortDescriptor | null—העמודה וכיוון המיון הראשוניים, כשמצב המיון מנוהל בתוך הרכיב ללא בקרה חיצונית.
sortDescriptorSortDescriptor | null—העמודה וכיוון המיון הנוכחיים, ההופכים את המיון למבוקר.
onSortChange(descriptor: SortDescriptor | null) => void—נקראת עם מתאר המיון החדש, או null, בהפעלת כותרת עמודה הניתנת למיון.
expandedKeys / defaultExpandedKeysIterable<string>—מפתחות השורות המורחבות המבוקרים או הראשוניים.
onExpandedChange(keys: Set<string>) => void—מדווחת על בקשות הרחבה. שורות מבוקרות ממתינות למפתחות המעודכנים.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.Header

מאפייןטיפוסברירת מחדלתיאור
stickybooleanfalseמצמיד מקטע זה למכל הגלילה; Header משתמש בתחילת ציר הבלוק ו-Footer בסופו.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.Body

מאפייןטיפוסברירת מחדלתיאור
childrenReactNode—שורות גוף הטבלה.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.Row

מאפייןטיפוסברירת מחדלתיאור
id*string—מזהה שורה יציב המשמש כמפתח הבחירה וכמזהה האלמנט.
disabledbooleanfalseמונע בחירה בשורה או העברת מיקוד אליה באמצעות המקלדת.
selectedboolean—מסמן את השורה כנבחרת ב-HTML שנוצר בשרת, לפני שמאגר הבחירה מקבל שליטה.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.ColumnHeader

מאפייןטיפוסברירת מחדלתיאור
id*string—מזהה העמודה שמתארי המיון מפנים אליו.
allowsSortingbooleanfalseגורם לכותרת להחליף את מיון העמודה שלה ולחשוף aria-sort.
sortDirection"ascending" | "descending" | null—כיוון המיון המוצג לעמודה זו, המשמש ב-HTML הראשוני מהשרת כאשר מיון מותר.
onClickReact.MouseEventHandler<HTMLTableCellElement>—מופעלת לפני המיון; preventDefault() מונעת את שינוי המיון.
slotPropsTableClientColumnHeaderSlotProps—מאפיינים מובנים לכפתור המיון שנוצר ב-TableClient.ColumnHeader.
childrenReactNode—תווית הכותרת.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.Cell

מאפייןטיפוסברירת מחדלתיאור
childrenReactNode—תוכן התא.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.Caption

מאפייןטיפוסברירת מחדלתיאור
childrenReactNode—טקסט הכיתוב המתאר את הטבלה.
renderReactElement | ((props, state) => ReactElement)—החלפת האלמנט המוצג תוך שימור ההתנהגות וקישורי הנגישות.

Table.RowSelect

מאפייןטיפוסברירת מחדלתיאור
rowId*string—מזהה השורה שתיבת סימון זו בוחרת ומבטלת את בחירתה.
slotPropsTableSelectionSlotProps—מאפיינים מובנים לקלט תיבת הסימון ולתווית העוטפת שנוצרים.

Table.SelectAll

מאפייןטיפוסברירת מחדלתיאור
slotPropsTableSelectionSlotProps—מאפיינים מובנים לקלט תיבת הסימון ולתווית העוטפת שנוצרים.

מקלדת

מקשפעולה
Tabמיקוד פקדי המיון, הבחירה וההרחבה ב-TableClient
Enter / Spaceהפעלת כפתור מיון או הרחבה ממוקד; החלפת מצב תיבת סימון
Arrow keysמעבר בין שורות גוף שניתנות לבחירה ב-TableClient

נגישות

Table מרנדר כברירת מחדל אלמנטים מובנים של <table>, <caption>, <thead>, <tbody> ו-<tfoot> ללא JavaScript בצד הלקוח; השתמשו ב-ColumnHeader וב-RowHeader לסמנטיקת כותרות. נקודת הכניסה /client מוסיפה מיון, בחירה, ניווט מקלדת בין שורות והרחבה מבוקרת, הנחשפים באמצעות aria-sort, aria-expanded ו-aria-controls. ExpandedRow הוא <tr> מובנה; הניחו בתוכו Cell עם colSpan. הסינון וסדר הפריטים נשארים באחריות המשתמש בספרייה.

נקודות חיבור לעיצוב

data-uiify-tabledata-partdata-layoutdata-sizedata-variantdata-stickydata-selecteddata-disabled--uiify-table-sticky-offset
דף זה תורגם אוטומטית וטרם נבדק.