@nadicodeai/ui 12.1.134 → 12.1.136
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-table.d.ts +1 -1
- package/dist/components/data-table.js +2 -5
- package/dist/components/record-identity.d.ts +1 -1
- package/dist/components/record-identity.d.ts.map +1 -1
- package/dist/components/record-identity.js +2 -4
- package/dist/components/record-list.d.ts +1 -1
- package/dist/components/record-list.js +1 -1
- package/dist/components/scope-switcher.d.ts +1 -1
- package/dist/components/scope-switcher.d.ts.map +1 -1
- package/dist/components/scope-switcher.js +5 -2
- package/dist/components/table-layouts.d.ts +7 -7
- package/dist/components/table-layouts.js +7 -7
- package/dist/components/table.d.ts +2 -2
- package/dist/components/table.d.ts.map +1 -1
- package/dist/components/table.js +4 -4
- package/dist/internal/table-compact-line.d.ts +4 -5
- package/dist/internal/table-compact-line.d.ts.map +1 -1
- package/dist/internal/table-compact-line.js +5 -6
- package/dist/showcase/usage.generated.js +5 -5
- package/package.json +2 -2
|
@@ -228,7 +228,7 @@ export declare function columnMenuLabel<TData extends RowData, TValue extends Ce
|
|
|
228
228
|
* @instead “For a row of descriptive content paired with a single action (membership row, integration row), use `Entity` instead.” (https://vercel.com/geist/table)
|
|
229
229
|
* “For a key/value metadata block on a detail page, use `Description`, not a two-column table.” (https://vercel.com/geist/table)
|
|
230
230
|
* Ours: Geist's `Entity` is a `RecordList` row. A registry of entities whose columns `RecordList` cannot hold, more than three facts beside a filter bar the product owns, as Vercel's Deployments list, is this table with `rows="entities"`, which draws the same rows: headings for assistive technology only, 72 px rows, a `RecordIdentity` in the identity column. Its `Description` is `ObjectFacts`. A list on a page of its own is a `CollectionPage`. A descriptive list is `ItemGroup`.
|
|
231
|
-
* @behaviour Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot
|
|
231
|
+
* @behaviour Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot (a `Tag` reads as its text there); entity facts wrap below the name so every value stays readable, while other rows end in an ellipsis unless a column `wraps`, or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` ("Page 2 of 7") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends "Deployment retention is enabled. Learn more" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries.
|
|
232
232
|
* “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table)
|
|
233
233
|
* “After an async filter change, wrap the region in `aria-live="polite"` so screen readers announce the new state.” (https://vercel.com/geist/empty-state)
|
|
234
234
|
* Ours: empty sits outside the body, in a polite live region that stays in place so a filter that empties the table is announced.
|
|
@@ -183,7 +183,7 @@ function DefaultToolbar({ actions }) {
|
|
|
183
183
|
* @instead “For a row of descriptive content paired with a single action (membership row, integration row), use `Entity` instead.” (https://vercel.com/geist/table)
|
|
184
184
|
* “For a key/value metadata block on a detail page, use `Description`, not a two-column table.” (https://vercel.com/geist/table)
|
|
185
185
|
* Ours: Geist's `Entity` is a `RecordList` row. A registry of entities whose columns `RecordList` cannot hold, more than three facts beside a filter bar the product owns, as Vercel's Deployments list, is this table with `rows="entities"`, which draws the same rows: headings for assistive technology only, 72 px rows, a `RecordIdentity` in the identity column. Its `Description` is `ObjectFacts`. A list on a page of its own is a `CollectionPage`. A descriptive list is `ItemGroup`.
|
|
186
|
-
* @behaviour Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot
|
|
186
|
+
* @behaviour Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot (a `Tag` reads as its text there); entity facts wrap below the name so every value stays readable, while other rows end in an ellipsis unless a column `wraps`, or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` ("Page 2 of 7") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends "Deployment retention is enabled. Learn more" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries.
|
|
187
187
|
* “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table)
|
|
188
188
|
* “After an async filter change, wrap the region in `aria-live="polite"` so screen readers announce the new state.” (https://vercel.com/geist/empty-state)
|
|
189
189
|
* Ours: empty sits outside the body, in a polite live region that stays in place so a filter that empties the table is announced.
|
|
@@ -291,8 +291,6 @@ function RecordsTable({ kind, columns, data, getRowId, filterColumnId, filterPla
|
|
|
291
291
|
wraps: cell.column.columnDef.meta?.wraps === true,
|
|
292
292
|
besideName: entities && cell.column.columnDef.meta?.compact === "name",
|
|
293
293
|
}));
|
|
294
|
-
// An entity row keeps its record on one 72 px row on a narrow card: the facts that leave the
|
|
295
|
-
// row join the line under the name, which `RecordIdentity` draws, instead of a row of their own.
|
|
296
294
|
// The line's provider stays in the tree when the card is wide enough for the columns and only
|
|
297
295
|
// its facts go, so a layout read after hydration never remounts the name a click is landing on.
|
|
298
296
|
const compactLine = entities && compactLayout && detailCells.length > 0
|
|
@@ -323,8 +321,7 @@ function RecordsTable({ kind, columns, data, getRowId, filterColumnId, filterPla
|
|
|
323
321
|
return _jsx(TableCell, { className: className }, cell.id);
|
|
324
322
|
}
|
|
325
323
|
const content = holdsCompactLine ? (_jsx(TableCompactLine, { ...compactLine, children: renderCell(cell) })) : (renderCell(cell));
|
|
326
|
-
|
|
327
|
-
return (_jsx(TableCell, { className: className, onFocus: titledWhenCut ? titleWhenCut : undefined, onPointerEnter: titledWhenCut ? titleWhenCut : undefined, children: isPrimaryLink
|
|
324
|
+
return (_jsx(TableCell, { className: className, onFocus: cutByEllipsis ? titleWhenCut : undefined, onPointerEnter: cutByEllipsis ? titleWhenCut : undefined, children: isPrimaryLink
|
|
328
325
|
? renderRowLink(primaryRowLinkProps({ href, label, wraps, content }))
|
|
329
326
|
: content }, cell.id));
|
|
330
327
|
}) }), rowEditor?.rowId === row.id ? (_jsx(TableRow, { "data-slot": "table-row-editor", className: "h-auto! [&>td]:bg-transparent!", children: _jsx(TableCell, { colSpan: visibleCells.length, className: "p-0 whitespace-normal", children: rowEditor.render(row.original) }) })) : null, detailRowShown ? (_jsx(TableDetailRow, { layout: compactLayout, colSpan: visibleCells.filter((cell) => !collapseClass(cell.column, primaryColumns, compactLayout)).length, facts: detailFacts() })) : null] }));
|
|
@@ -8,7 +8,7 @@ import { type IdentityMarkProps } from "./identity-mark.js";
|
|
|
8
8
|
* @family Status
|
|
9
9
|
* @when The first cell of every collection row: the mark, the name, and one muted line saying what the record is or where it comes from, so the row reads without a heading.
|
|
10
10
|
* @instead An Agent in a gallery is `AgentListRow`; a model is `ModelIdentity`. A descriptive item with prose and actions is `Item`.
|
|
11
|
-
* @behaviour The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot
|
|
11
|
+
* @behaviour The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot and wrap so every value stays readable, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.
|
|
12
12
|
* @words “Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity)
|
|
13
13
|
* Ours: the name as the record calls itself, and the detail in sentence case: kind, then relation, three to six words, "Company skill · every Agent has it".
|
|
14
14
|
* @access The name is the row link's text; the mark is decorative; the title carries the full name.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-identity.d.ts","sourceRoot":"","sources":["../../src/components/record-identity.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIlF;;;;;;;;;;;;GAYG;AACH,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAa,GACd,EAAE;IACD,IAAI,EAAE,SAAS,CAAC;IAChB,8FAA8F;IAC9F,IAAI,EAAE,iBAAiB,CAAC;IACxB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,+
|
|
1
|
+
{"version":3,"file":"record-identity.d.ts","sourceRoot":"","sources":["../../src/components/record-identity.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAIlF;;;;;;;;;;;;GAYG;AACH,iBAAS,cAAc,CAAC,EACtB,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAa,GACd,EAAE;IACD,IAAI,EAAE,SAAS,CAAC;IAChB,8FAA8F;IAC9F,IAAI,EAAE,iBAAiB,CAAC;IACxB,qFAAqF;IACrF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,+BAoCA;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -10,7 +10,7 @@ import { cn } from "../lib/utils.js";
|
|
|
10
10
|
* @family Status
|
|
11
11
|
* @when The first cell of every collection row: the mark, the name, and one muted line saying what the record is or where it comes from, so the row reads without a heading.
|
|
12
12
|
* @instead An Agent in a gallery is `AgentListRow`; a model is `ModelIdentity`. A descriptive item with prose and actions is `Item`.
|
|
13
|
-
* @behaviour The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot
|
|
13
|
+
* @behaviour The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot and wrap so every value stays readable, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.
|
|
14
14
|
* @words “Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity)
|
|
15
15
|
* Ours: the name as the record calls itself, and the detail in sentence case: kind, then relation, three to six words, "Company skill · every Agent has it".
|
|
16
16
|
* @access The name is the row link's text; the mark is decorative; the title carries the full name.
|
|
@@ -21,9 +21,7 @@ function RecordIdentity({ name, mark, hint, detail, wraps = false, }) {
|
|
|
21
21
|
return (_jsxs("span", { className: "flex min-w-0 items-center gap-4", "data-slot": "record-identity", children: [_jsx(IdentityMark, { ...mark, size: 32 }), _jsxs("span", { className: "grid min-w-0 in-[table]:w-0 in-[table]:flex-1", children: [_jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { className: cn("min-w-0 font-semibold text-foreground", wraps ? "break-words whitespace-pre-wrap" : "truncate"), title: wraps ? undefined : title, children: name }), _jsx(TableCompactNameFacts, {})] }), _jsxs("span", { className: cn(
|
|
22
22
|
// The line is the containing block of the facts' screen-reader labels, so what it cuts
|
|
23
23
|
// stays cut instead of widening the table.
|
|
24
|
-
"relative text-sm text-muted-foreground", wraps
|
|
25
|
-
? "break-words whitespace-normal"
|
|
26
|
-
: "truncate has-[[data-wraps]]:break-words has-[[data-wraps]]:whitespace-normal"), "data-slot": "record-identity-detail",
|
|
24
|
+
"relative text-sm text-muted-foreground", wraps ? "break-words whitespace-normal" : "truncate"), "data-slot": "record-identity-detail",
|
|
27
25
|
// oxlint-disable-next-line anti-slop/no-runtime-typeof -- detail is a ReactNode; only a string can be a title attribute.
|
|
28
26
|
title: !wraps && typeof detail === "string" ? detail : undefined, children: [detail, _jsx(TableCompactFacts, {})] })] })] }));
|
|
29
27
|
}
|
|
@@ -189,7 +189,7 @@ export type RecordListWithout<TRecord extends RowData, Key extends PropertyKey>
|
|
|
189
189
|
* @instead “For tabular data with sortable columns and shared row shape, use `Table` instead.” (https://vercel.com/geist/entity)
|
|
190
190
|
* “For a static key/value metadata block on a detail page, use `Description`.” (https://vercel.com/geist/entity)
|
|
191
191
|
* Ours: Geist's `Table` is a `DataTable`: this list has no visible headings and at most three facts, so a fourth fact is a table, and anything more than a few facts lives on the record's page. A screen gives `RecordList` the records and what each one says, never its own toolbar, table, rows or menus, and nothing else draws a record row. Its `Description` is `ObjectFacts`. Events in time are an `ActivityFeed`. Reusable things a person adds from a catalogue are a `LibraryShelf`. A descriptive row with prose is an `Item`.
|
|
192
|
-
* @behaviour One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout="line"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as
|
|
192
|
+
* @behaviour One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout="line"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as muted text joined by a middle dot that wraps, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that muted text, wrapping so every value stays readable, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list ("Deployment retention is enabled. Learn more", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message.
|
|
193
193
|
* “Pick the variant by what the user needs: no-results for a filtered list that returned zero rows, blank slate or informational for a resource the user hasn’t created, cleared for completed work, permission for role or tier denials, error for a failed load.” (https://vercel.com/geist/empty-state)
|
|
194
194
|
* Ours: `failed`, `noMatch` and `empty` are those variants, drawn inside the card.
|
|
195
195
|
* “The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`.” (https://vercel.com/geist/entity)
|
|
@@ -250,7 +250,7 @@ function RecordRows(props) {
|
|
|
250
250
|
* @instead “For tabular data with sortable columns and shared row shape, use `Table` instead.” (https://vercel.com/geist/entity)
|
|
251
251
|
* “For a static key/value metadata block on a detail page, use `Description`.” (https://vercel.com/geist/entity)
|
|
252
252
|
* Ours: Geist's `Table` is a `DataTable`: this list has no visible headings and at most three facts, so a fourth fact is a table, and anything more than a few facts lives on the record's page. A screen gives `RecordList` the records and what each one says, never its own toolbar, table, rows or menus, and nothing else draws a record row. Its `Description` is `ObjectFacts`. Events in time are an `ActivityFeed`. Reusable things a person adds from a catalogue are a `LibraryShelf`. A descriptive row with prose is an `Item`.
|
|
253
|
-
* @behaviour One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout="line"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as
|
|
253
|
+
* @behaviour One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout="line"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as muted text joined by a middle dot that wraps, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that muted text, wrapping so every value stays readable, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list ("Deployment retention is enabled. Learn more", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message.
|
|
254
254
|
* “Pick the variant by what the user needs: no-results for a filtered list that returned zero rows, blank slate or informational for a resource the user hasn’t created, cleared for completed work, permission for role or tier denials, error for a failed load.” (https://vercel.com/geist/empty-state)
|
|
255
255
|
* Ours: `failed`, `noMatch` and `empty` are those variants, drawn inside the card.
|
|
256
256
|
* “The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`.” (https://vercel.com/geist/entity)
|
|
@@ -31,7 +31,7 @@ export type ScopeSwitcherProps = {
|
|
|
31
31
|
* @family Navigation
|
|
32
32
|
* @when Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant="navigation"`.
|
|
33
33
|
* @instead A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.
|
|
34
|
-
* @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
|
|
34
|
+
* @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, wraps the selected name on phones instead of cutting it, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
|
|
35
35
|
* @words `copy.label` names what is chosen ("Scope") beside the current scope's name. Search and empty lines name the thing: "Find a scope", "No scope matches".
|
|
36
36
|
* @access The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.
|
|
37
37
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope-switcher.d.ts","sourceRoot":"","sources":["../../src/components/scope-switcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAgB/B,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,8DAA8D;IAC9D,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACjD,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CAClC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,IAAI,EACJ,MAAM,EACN,OAAiB,GAClB,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"scope-switcher.d.ts","sourceRoot":"","sources":["../../src/components/scope-switcher.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAgB/B,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,8DAA8D;IAC9D,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACjD,OAAO,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC;CAClC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,OAAO,EACP,aAAa,EACb,IAAI,EACJ,MAAM,EACN,OAAiB,GAClB,EAAE,kBAAkB,qBAsHpB"}
|
|
@@ -12,7 +12,7 @@ import { cn } from "../lib/utils.js";
|
|
|
12
12
|
* @family Navigation
|
|
13
13
|
* @when Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant="navigation"`.
|
|
14
14
|
* @instead A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.
|
|
15
|
-
* @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
|
|
15
|
+
* @behaviour A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, wraps the selected name on phones instead of cutting it, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.
|
|
16
16
|
* @words `copy.label` names what is chosen ("Scope") beside the current scope's name. Search and empty lines name the thing: "Find a scope", "No scope matches".
|
|
17
17
|
* @access The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.
|
|
18
18
|
*/
|
|
@@ -36,7 +36,10 @@ export function ScopeSwitcher({ value, options, onValueChange, copy, create, var
|
|
|
36
36
|
onValueChange(next);
|
|
37
37
|
};
|
|
38
38
|
const switchable = options.length > 1 || create !== undefined;
|
|
39
|
-
return (_jsxs("span", { "data-slot": "scope-switcher", className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { "aria-hidden": switchable ? "true" : undefined, className: cn("text-xs font-medium text-muted-foreground", variant === "navigation" && "sr-only"), children: copy.label }), switchable ? (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { variant: "ghost", className: "min-w-0 max-w-64 shrink",
|
|
39
|
+
return (_jsxs("span", { "data-slot": "scope-switcher", className: "flex min-w-0 items-center gap-2", children: [_jsx("span", { "aria-hidden": switchable ? "true" : undefined, className: cn("text-xs font-medium text-muted-foreground", variant === "navigation" && "sr-only"), children: copy.label }), switchable ? (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { variant: "ghost", className: cn("min-w-0 max-w-64 shrink", variant === "navigation" &&
|
|
40
|
+
"h-auto max-w-full py-1 md:h-control-height md:max-w-64 md:py-0"), prefix: current?.mark ? _jsx("span", { "aria-hidden": "true", children: current.mark }) : undefined, suffix: _jsx(ChevronsUpDown, { "aria-hidden": "true" }) }), children: [_jsxs("span", { className: "sr-only", children: [copy.label, ":"] }), " ", _jsx("span", { className: cn(variant === "navigation"
|
|
41
|
+
? "whitespace-normal break-words text-left md:truncate"
|
|
42
|
+
: "truncate"), children: currentLabel })] }), _jsxs(PopoverContent, { align: "start", className: cn("w-72 max-w-[calc(100vw-2rem)] p-0", variant === "navigation" && "w-96"), initialFocus: search, children: [_jsx(PopoverTitle, { className: "sr-only", children: copy.label }), _jsxs(Command, { label: copy.search, resultCount: copy.results, children: [_jsx(CommandInput, { ref: search, "aria-label": copy.search, placeholder: copy.search }), _jsxs(CommandList, { label: copy.label, children: [_jsx(CommandEmpty, { children: copy.empty }), groups.map(([group, items]) => (_jsx(CommandGroup, { heading: group, children: items.map((option) => (_jsxs(CommandItem, { value: option.value, keywords: [
|
|
40
43
|
option.label,
|
|
41
44
|
...(option.group ? [option.group] : []),
|
|
42
45
|
...(option.keywords ?? []),
|
|
@@ -6,7 +6,7 @@ export declare const tableCompactLayouts: {
|
|
|
6
6
|
readonly track: "hidden @md/table:table-column";
|
|
7
7
|
readonly row: "@max-md/table:[&>td]:border-b-0 @max-md/table:[&>td]:after:hidden @max-md/table:h-auto! @max-md/table:[&>td]:pb-1!";
|
|
8
8
|
readonly details: "@md/table:hidden";
|
|
9
|
-
readonly table: "@max-md/table:table-auto @max-md/table:[&_td]:whitespace-normal @max-md/table:[&_th:not(:first-child):not([data-identity])]:w-px @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child)]:pl-0 @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child):not(:last-child)]:pr-0";
|
|
9
|
+
readonly table: "@max-md/table:table-auto @max-md/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-md/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-md/table:[&_td]:whitespace-normal @max-md/table:[&_th:not(:first-child):not([data-identity])]:w-px @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child)]:pl-0 @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child):not(:last-child)]:pr-0";
|
|
10
10
|
};
|
|
11
11
|
readonly tight: {
|
|
12
12
|
readonly width: 576;
|
|
@@ -14,7 +14,7 @@ export declare const tableCompactLayouts: {
|
|
|
14
14
|
readonly track: "hidden @xl/table:table-column";
|
|
15
15
|
readonly row: "@max-xl/table:[&>td]:border-b-0 @max-xl/table:[&>td]:after:hidden @max-xl/table:h-auto! @max-xl/table:[&>td]:pb-1!";
|
|
16
16
|
readonly details: "@xl/table:hidden";
|
|
17
|
-
readonly table: "@max-xl/table:table-auto @max-xl/table:[&_td]:whitespace-normal @max-xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
17
|
+
readonly table: "@max-xl/table:table-auto @max-xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-xl/table:[&_td]:whitespace-normal @max-xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
18
18
|
};
|
|
19
19
|
readonly standard: {
|
|
20
20
|
readonly width: 768;
|
|
@@ -22,7 +22,7 @@ export declare const tableCompactLayouts: {
|
|
|
22
22
|
readonly track: "hidden @3xl/table:table-column";
|
|
23
23
|
readonly row: "@max-3xl/table:[&>td]:border-b-0 @max-3xl/table:[&>td]:after:hidden @max-3xl/table:h-auto! @max-3xl/table:[&>td]:pb-1!";
|
|
24
24
|
readonly details: "@3xl/table:hidden";
|
|
25
|
-
readonly table: "@max-3xl/table:table-auto @max-3xl/table:[&_td]:whitespace-normal @max-3xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
25
|
+
readonly table: "@max-3xl/table:table-auto @max-3xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-3xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-3xl/table:[&_td]:whitespace-normal @max-3xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
26
26
|
};
|
|
27
27
|
readonly wide: {
|
|
28
28
|
readonly width: 1024;
|
|
@@ -30,7 +30,7 @@ export declare const tableCompactLayouts: {
|
|
|
30
30
|
readonly track: "hidden @5xl/table:table-column";
|
|
31
31
|
readonly row: "@max-5xl/table:[&>td]:border-b-0 @max-5xl/table:[&>td]:after:hidden @max-5xl/table:h-auto! @max-5xl/table:[&>td]:pb-1!";
|
|
32
32
|
readonly details: "@5xl/table:hidden";
|
|
33
|
-
readonly table: "@max-5xl/table:table-auto @max-5xl/table:[&_td]:whitespace-normal @max-5xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
33
|
+
readonly table: "@max-5xl/table:table-auto @max-5xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-5xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-5xl/table:[&_td]:whitespace-normal @max-5xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
34
34
|
};
|
|
35
35
|
readonly roomy: {
|
|
36
36
|
readonly width: 1088;
|
|
@@ -38,7 +38,7 @@ export declare const tableCompactLayouts: {
|
|
|
38
38
|
readonly track: "hidden @min-[68rem]/table:table-column";
|
|
39
39
|
readonly row: "@max-[68rem]/table:[&>td]:border-b-0 @max-[68rem]/table:[&>td]:after:hidden @max-[68rem]/table:h-auto! @max-[68rem]/table:[&>td]:pb-1!";
|
|
40
40
|
readonly details: "@min-[68rem]/table:hidden";
|
|
41
|
-
readonly table: "@max-[68rem]/table:table-auto @max-[68rem]/table:[&_td]:whitespace-normal @max-[68rem]/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
41
|
+
readonly table: "@max-[68rem]/table:table-auto @max-[68rem]/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-[68rem]/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-[68rem]/table:[&_td]:whitespace-normal @max-[68rem]/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
42
42
|
};
|
|
43
43
|
readonly spacious: {
|
|
44
44
|
readonly width: 1152;
|
|
@@ -46,14 +46,14 @@ export declare const tableCompactLayouts: {
|
|
|
46
46
|
readonly track: "hidden @6xl/table:table-column";
|
|
47
47
|
readonly row: "@max-6xl/table:[&>td]:border-b-0 @max-6xl/table:[&>td]:after:hidden @max-6xl/table:h-auto! @max-6xl/table:[&>td]:pb-1!";
|
|
48
48
|
readonly details: "@6xl/table:hidden";
|
|
49
|
-
readonly table: "@max-6xl/table:table-auto @max-6xl/table:[&_td]:whitespace-normal @max-6xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
49
|
+
readonly table: "@max-6xl/table:table-auto @max-6xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-6xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-6xl/table:[&_td]:whitespace-normal @max-6xl/table:[&_th:not(:first-child):not([data-identity])]:w-px";
|
|
50
50
|
};
|
|
51
51
|
};
|
|
52
52
|
export type TableCompactLayout = keyof typeof tableCompactLayouts;
|
|
53
53
|
/**
|
|
54
54
|
* The compact layout for columns of these sizes: the narrowest card width at which every sized
|
|
55
55
|
* column, and a floor for each column without a size, still fit. A card narrower than that
|
|
56
|
-
* leaves its secondary columns out of the row: each joins the
|
|
56
|
+
* leaves its secondary columns out of the row: each joins the muted text under the name, or is
|
|
57
57
|
* dropped.
|
|
58
58
|
*/
|
|
59
59
|
export declare function compactLayoutFor(sizes: readonly (number | undefined)[]): TableCompactLayout;
|
|
@@ -6,7 +6,7 @@ export const tableCompactLayouts = {
|
|
|
6
6
|
track: "hidden @md/table:table-column",
|
|
7
7
|
row: "@max-md/table:[&>td]:border-b-0 @max-md/table:[&>td]:after:hidden @max-md/table:h-auto! @max-md/table:[&>td]:pb-1!",
|
|
8
8
|
details: "@md/table:hidden",
|
|
9
|
-
table: "@max-md/table:table-auto @max-md/table:[&_td]:whitespace-normal @max-md/table:[&_th:not(:first-child):not([data-identity])]:w-px @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child)]:pl-0 @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child):not(:last-child)]:pr-0",
|
|
9
|
+
table: "@max-md/table:table-auto @max-md/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-md/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-md/table:[&_td]:whitespace-normal @max-md/table:[&_th:not(:first-child):not([data-identity])]:w-px @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child)]:pl-0 @max-md/table:[&_tr:not([data-slot=table-detail-row])>td:not(:first-child):not(:last-child)]:pr-0",
|
|
10
10
|
},
|
|
11
11
|
tight: {
|
|
12
12
|
width: 576,
|
|
@@ -14,7 +14,7 @@ export const tableCompactLayouts = {
|
|
|
14
14
|
track: "hidden @xl/table:table-column",
|
|
15
15
|
row: "@max-xl/table:[&>td]:border-b-0 @max-xl/table:[&>td]:after:hidden @max-xl/table:h-auto! @max-xl/table:[&>td]:pb-1!",
|
|
16
16
|
details: "@xl/table:hidden",
|
|
17
|
-
table: "@max-xl/table:table-auto @max-xl/table:[&_td]:whitespace-normal @max-xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
17
|
+
table: "@max-xl/table:table-auto @max-xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-xl/table:[&_td]:whitespace-normal @max-xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
18
18
|
},
|
|
19
19
|
standard: {
|
|
20
20
|
width: 768,
|
|
@@ -22,7 +22,7 @@ export const tableCompactLayouts = {
|
|
|
22
22
|
track: "hidden @3xl/table:table-column",
|
|
23
23
|
row: "@max-3xl/table:[&>td]:border-b-0 @max-3xl/table:[&>td]:after:hidden @max-3xl/table:h-auto! @max-3xl/table:[&>td]:pb-1!",
|
|
24
24
|
details: "@3xl/table:hidden",
|
|
25
|
-
table: "@max-3xl/table:table-auto @max-3xl/table:[&_td]:whitespace-normal @max-3xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
25
|
+
table: "@max-3xl/table:table-auto @max-3xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-3xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-3xl/table:[&_td]:whitespace-normal @max-3xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
26
26
|
},
|
|
27
27
|
wide: {
|
|
28
28
|
width: 1024,
|
|
@@ -30,7 +30,7 @@ export const tableCompactLayouts = {
|
|
|
30
30
|
track: "hidden @5xl/table:table-column",
|
|
31
31
|
row: "@max-5xl/table:[&>td]:border-b-0 @max-5xl/table:[&>td]:after:hidden @max-5xl/table:h-auto! @max-5xl/table:[&>td]:pb-1!",
|
|
32
32
|
details: "@5xl/table:hidden",
|
|
33
|
-
table: "@max-5xl/table:table-auto @max-5xl/table:[&_td]:whitespace-normal @max-5xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
33
|
+
table: "@max-5xl/table:table-auto @max-5xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-5xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-5xl/table:[&_td]:whitespace-normal @max-5xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
34
34
|
},
|
|
35
35
|
roomy: {
|
|
36
36
|
width: 1088,
|
|
@@ -38,7 +38,7 @@ export const tableCompactLayouts = {
|
|
|
38
38
|
track: "hidden @min-[68rem]/table:table-column",
|
|
39
39
|
row: "@max-[68rem]/table:[&>td]:border-b-0 @max-[68rem]/table:[&>td]:after:hidden @max-[68rem]/table:h-auto! @max-[68rem]/table:[&>td]:pb-1!",
|
|
40
40
|
details: "@min-[68rem]/table:hidden",
|
|
41
|
-
table: "@max-[68rem]/table:table-auto @max-[68rem]/table:[&_td]:whitespace-normal @max-[68rem]/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
41
|
+
table: "@max-[68rem]/table:table-auto @max-[68rem]/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-[68rem]/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-[68rem]/table:[&_td]:whitespace-normal @max-[68rem]/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
42
42
|
},
|
|
43
43
|
spacious: {
|
|
44
44
|
width: 1152,
|
|
@@ -46,7 +46,7 @@ export const tableCompactLayouts = {
|
|
|
46
46
|
track: "hidden @6xl/table:table-column",
|
|
47
47
|
row: "@max-6xl/table:[&>td]:border-b-0 @max-6xl/table:[&>td]:after:hidden @max-6xl/table:h-auto! @max-6xl/table:[&>td]:pb-1!",
|
|
48
48
|
details: "@6xl/table:hidden",
|
|
49
|
-
table: "@max-6xl/table:table-auto @max-6xl/table:[&_td]:whitespace-normal @max-6xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
49
|
+
table: "@max-6xl/table:table-auto @max-6xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:whitespace-normal @max-6xl/table:[&_[data-slot=record-identity-detail]:has([data-slot=table-detail-line])]:break-words @max-6xl/table:[&_td]:whitespace-normal @max-6xl/table:[&_th:not(:first-child):not([data-identity])]:w-px",
|
|
50
50
|
},
|
|
51
51
|
};
|
|
52
52
|
/** The least a column without a size needs, in pixels: below it the card lays out compact. */
|
|
@@ -61,7 +61,7 @@ const NARROWEST_FIRST = [
|
|
|
61
61
|
/**
|
|
62
62
|
* The compact layout for columns of these sizes: the narrowest card width at which every sized
|
|
63
63
|
* column, and a floor for each column without a size, still fit. A card narrower than that
|
|
64
|
-
* leaves its secondary columns out of the row: each joins the
|
|
64
|
+
* leaves its secondary columns out of the row: each joins the muted text under the name, or is
|
|
65
65
|
* dropped.
|
|
66
66
|
*/
|
|
67
67
|
export function compactLayoutFor(sizes) {
|
|
@@ -39,8 +39,8 @@ type TableName = AccessibleName | {
|
|
|
39
39
|
* it, one muted line joined by a middle dot that ends in an ellipsis, their labels read by assistive
|
|
40
40
|
* technology only, as Vercel's lists read on a phone; a `Tag` there reads as its plain text, and a
|
|
41
41
|
* fact that `wraps` lets the line wrap whole instead of ending in an ellipsis. An entity row
|
|
42
|
-
* (`density="comfortable"`) keeps its
|
|
43
|
-
* under the name that `RecordIdentity` draws (`TableCompactLine`),
|
|
42
|
+
* (`density="comfortable"`) keeps its facts in the identity cell instead: they join the text
|
|
43
|
+
* under the name that `RecordIdentity` draws (`TableCompactLine`), and wrap so every value stays readable. Colour is neutral throughout: headings and cells share
|
|
44
44
|
* the body grey and a heading differs by weight, the footer is ink, and the hue of a state lives in
|
|
45
45
|
* the `StatusBadge` of a cell, never in the row.
|
|
46
46
|
* “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/components/table.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../src/components/table.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,uGAAuG;AACvG,KAAK,SAAS,GAAG,cAAc,GAAG;IAAE,aAAa,EAAE,IAAI,CAAA;CAAE,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,iBAAS,KAAK,CAAC,EACb,SAAS,EACT,cAAc,EACd,OAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,GAC9B,SAAS,GAAG;IACV,cAAc,CAAC,EAAE,kBAAkB,CAAC;IACpC,OAAO,CAAC,EAAE,SAAS,GAAG,aAAa,CAAC;CACrC,qBAmBF;AAED,iBAAS,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,qBAE7D;AAED,iBAAS,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAEnD;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAQ1E;AAID,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,OAAe,EACf,QAAgB,EAChB,WAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG;IACjC,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uHAAuH;IACvH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iFAAiF;IACjF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,qBAeA;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,WAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG;IACjC,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,qBAcA;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAW1E;AAED,iBAAS,QAAQ,CAAC,EAChB,SAAS,EACT,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG;IAC9B,2HAA2H;IAC3H,YAAY,CAAC,EAAE,kBAAkB,CAAC;CACnC,qBAYA;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAWrE;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAWrE;AAED,iBAAS,cAAc,CAAC,EACtB,OAAO,EACP,KAAK,EACL,MAAmB,GACpB,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;CACnC,qBA0BA;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,qBAQ7E;AAED,OAAO,EACL,KAAK,EACL,aAAa,EACb,QAAQ,EACR,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,KAAK,kBAAkB,GACxB,CAAC"}
|
package/dist/components/table.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
|
-
import { DetailFacts } from "../internal/table-compact-line.js";
|
|
4
|
+
import { DetailFacts, detailFactsLine, detailFactsWrap, } from "../internal/table-compact-line.js";
|
|
5
5
|
import { tableCompactLayouts } from "./table-layouts.js";
|
|
6
6
|
import { titleWhenCut } from "../internal/title-when-cut.js";
|
|
7
7
|
/**
|
|
@@ -37,8 +37,8 @@ import { titleWhenCut } from "../internal/title-when-cut.js";
|
|
|
37
37
|
* it, one muted line joined by a middle dot that ends in an ellipsis, their labels read by assistive
|
|
38
38
|
* technology only, as Vercel's lists read on a phone; a `Tag` there reads as its plain text, and a
|
|
39
39
|
* fact that `wraps` lets the line wrap whole instead of ending in an ellipsis. An entity row
|
|
40
|
-
* (`density="comfortable"`) keeps its
|
|
41
|
-
* under the name that `RecordIdentity` draws (`TableCompactLine`),
|
|
40
|
+
* (`density="comfortable"`) keeps its facts in the identity cell instead: they join the text
|
|
41
|
+
* under the name that `RecordIdentity` draws (`TableCompactLine`), and wrap so every value stays readable. Colour is neutral throughout: headings and cells share
|
|
42
42
|
* the body grey and a heading differs by weight, the footer is ink, and the hue of a state lives in
|
|
43
43
|
* the `StatusBadge` of a cell, never in the row.
|
|
44
44
|
* “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table)
|
|
@@ -86,7 +86,7 @@ function TableCell({ className, ...props }) {
|
|
|
86
86
|
}
|
|
87
87
|
function TableDetailRow({ colSpan, facts, layout = "standard", }) {
|
|
88
88
|
const wraps = facts.some((fact) => fact.wraps);
|
|
89
|
-
return (_jsx(TableRow, { "data-slot": "table-detail-row", className: cn("h-auto!", tableCompactLayouts[layout].details), children: _jsx(TableCell, { colSpan: colSpan, className: "pt-0! pb-3! whitespace-normal text-muted-foreground last:text-left", children: _jsx("div", { className: cn("relative min-w-0 in-[[data-slot=table-record-group]:has([data-slot=record-identity])]:pl-12
|
|
89
|
+
return (_jsx(TableRow, { "data-slot": "table-detail-row", className: cn("h-auto!", tableCompactLayouts[layout].details), children: _jsx(TableCell, { colSpan: colSpan, className: "pt-0! pb-3! whitespace-normal text-muted-foreground last:text-left", children: _jsx("div", { className: cn("relative min-w-0 in-[[data-slot=table-record-group]:has([data-slot=record-identity])]:pl-12", detailFactsLine, wraps ? ["break-words", detailFactsWrap] : "w-0 min-w-full truncate"), "data-slot": "table-detail-line", onFocus: wraps ? undefined : titleWhenCut, onPointerEnter: wraps ? undefined : titleWhenCut, children: _jsx(DetailFacts, { facts: facts }) }) }) }));
|
|
90
90
|
}
|
|
91
91
|
function TableCaption({ className, ...props }) {
|
|
92
92
|
return (_jsx("caption", { "data-slot": "table-caption", className: cn("mt-4 text-sm text-muted-foreground", className), ...props }));
|
|
@@ -9,6 +9,10 @@ export type TableDetailFact = {
|
|
|
9
9
|
/** The value stands beside the record's name instead, which ends in an ellipsis before it: a status. */
|
|
10
10
|
besideName?: boolean;
|
|
11
11
|
};
|
|
12
|
+
/** A facts line reads a `Tag` as its text and a block value inline. */
|
|
13
|
+
export declare const detailFactsLine = "[&_[data-slot=tag]]:contents [&>span>:is(div,p,.block,.grid,.flex)]:inline";
|
|
14
|
+
/** A facts line that wraps breaks every value between its words, except a badge, which stands whole. */
|
|
15
|
+
export declare const detailFactsWrap = "[&_:not(.sr-only,[data-slot=badge],[data-slot=badge]_*,[data-slot=status-badge],[data-slot=status-badge]_*)]:whitespace-normal";
|
|
12
16
|
export declare function DetailFacts({ facts, leadingSeparator, }: {
|
|
13
17
|
facts: readonly TableDetailFact[];
|
|
14
18
|
leadingSeparator?: boolean;
|
|
@@ -17,11 +21,6 @@ type TableCompactLineValue = {
|
|
|
17
21
|
layout: TableCompactLayout;
|
|
18
22
|
facts: readonly TableDetailFact[];
|
|
19
23
|
};
|
|
20
|
-
/**
|
|
21
|
-
* The facts an entity row moves onto the line under its name on a card narrower than its
|
|
22
|
-
* columns, so the record stays one 72 px row: the name over one muted line. `RecordIdentity`
|
|
23
|
-
* draws them after its own line.
|
|
24
|
-
*/
|
|
25
24
|
export declare function TableCompactLine({ children, ...value }: TableCompactLineValue & {
|
|
26
25
|
children: React.ReactNode;
|
|
27
26
|
}): React.JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table-compact-line.d.ts","sourceRoot":"","sources":["../../src/internal/table-compact-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4HAA4H;IAC5H,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,gBAAwB,GACzB,EAAE;IACD,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,uBAQA;AAED,KAAK,qBAAqB,GAAG;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAC;AAI/F
|
|
1
|
+
{"version":3,"file":"table-compact-line.d.ts","sourceRoot":"","sources":["../../src/internal/table-compact-line.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAuB,KAAK,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,4HAA4H;IAC5H,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wGAAwG;IACxG,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,eAAe,+EACkD,CAAC;AAE/E,wGAAwG;AACxG,eAAO,MAAM,eAAe,mIACsG,CAAC;AAEnI,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,gBAAwB,GACzB,EAAE;IACD,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;IAClC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,uBAQA;AAED,KAAK,qBAAqB,GAAG;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;CAAE,CAAC;AAI/F,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,qBAEvD;AAED,4IAA4I;AAC5I,wBAAgB,iBAAiB,6BAahC;AAED,2KAA2K;AAC3K,wBAAgB,qBAAqB,6BAkBpC;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,qBAEA;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD"}
|
|
@@ -3,15 +3,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { tableCompactLayouts } from "../components/table-layouts.js";
|
|
5
5
|
import { cn } from "../lib/utils.js";
|
|
6
|
+
/** A facts line reads a `Tag` as its text and a block value inline. */
|
|
7
|
+
export const detailFactsLine = "[&_[data-slot=tag]]:contents [&>span>:is(div,p,.block,.grid,.flex)]:inline";
|
|
8
|
+
/** A facts line that wraps breaks every value between its words, except a badge, which stands whole. */
|
|
9
|
+
export const detailFactsWrap = "[&_:not(.sr-only,[data-slot=badge],[data-slot=badge]_*,[data-slot=status-badge],[data-slot=status-badge]_*)]:whitespace-normal";
|
|
6
10
|
export function DetailFacts({ facts, leadingSeparator = false, }) {
|
|
7
11
|
return facts.map((fact, index) => (_jsxs("span", { children: [index > 0 || leadingSeparator ? _jsx("span", { "aria-hidden": "true", children: " \u00B7 " }) : null, _jsxs("span", { className: "sr-only", children: [fact.label, ": "] }), fact.value] }, fact.id)));
|
|
8
12
|
}
|
|
9
13
|
const TableCompactLineContext = React.createContext(null);
|
|
10
|
-
/**
|
|
11
|
-
* The facts an entity row moves onto the line under its name on a card narrower than its
|
|
12
|
-
* columns, so the record stays one 72 px row: the name over one muted line. `RecordIdentity`
|
|
13
|
-
* draws them after its own line.
|
|
14
|
-
*/
|
|
15
14
|
export function TableCompactLine({ children, ...value }) {
|
|
16
15
|
return _jsx(TableCompactLineContext, { value: value, children: children });
|
|
17
16
|
}
|
|
@@ -23,7 +22,7 @@ export function TableCompactFacts() {
|
|
|
23
22
|
const facts = line.facts.filter((fact) => !fact.besideName);
|
|
24
23
|
if (facts.length === 0)
|
|
25
24
|
return null;
|
|
26
|
-
return (_jsx("span", { className: cn(
|
|
25
|
+
return (_jsx("span", { className: cn(detailFactsLine, detailFactsWrap, tableCompactLayouts[line.layout].details), "data-slot": "table-detail-line", children: _jsx(DetailFacts, { facts: facts, leadingSeparator: true }) }));
|
|
27
26
|
}
|
|
28
27
|
/** The compact facts that stand beside the name of the row this stands in, which no ellipsis cuts; nothing outside such a row or on a card wide enough for the columns. */
|
|
29
28
|
export function TableCompactNameFacts() {
|
|
@@ -186,7 +186,7 @@ export const USAGE = [
|
|
|
186
186
|
summary: "Entity rows: objects a person opens or acts on, one line each on one resting card: the identity mark and name over what it is, a few facts, a status only for the exception, when it happened, at most two row commands, and the three dots last.",
|
|
187
187
|
when: "“Use `<Entity>` for a row of descriptive content paired with one or two controls (member rows, integration rows, domain rows).” (https://vercel.com/geist/entity) Ours: the records are things a person opens or acts on and that name themselves: projects, deployments, domains, members, integrations, Agents, Skills, mailboxes. A list inside another page: an Overview's exceptions, an object page's \"Needs attention\". A page whose whole job is the list is a `CollectionPage`, which draws this. Values edited in place (a domain, an environment variable) are the same rows with an `editor`: the row expands into its form. Of the three kinds of list, entity rows and rows edited in place are this; records compared across columns are a `DataTable`.",
|
|
188
188
|
instead: "“For tabular data with sortable columns and shared row shape, use `Table` instead.” (https://vercel.com/geist/entity) “For a static key/value metadata block on a detail page, use `Description`.” (https://vercel.com/geist/entity) Ours: Geist's `Table` is a `DataTable`: this list has no visible headings and at most three facts, so a fourth fact is a table, and anything more than a few facts lives on the record's page. A screen gives `RecordList` the records and what each one says, never its own toolbar, table, rows or menus, and nothing else draws a record row. Its `Description` is `ObjectFacts`. Events in time are an `ActivityFeed`. Reusable things a person adds from a catalogue are a `LibraryShelf`. A descriptive row with prose is an `Item`.",
|
|
189
|
-
behaviour: "One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout=\"line\"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as
|
|
189
|
+
behaviour: "One bordered card, a 1 px rule between records and none after the last. A row is 72 px, with 16 px above and below, and its columns stand left to right at the widths the list declares, so a person reads every list the same way: the `select` box (40 px, only when the list is picked from, an optional column), the identity (the rest: the one column without a size), the facts (each its `size`), the status (`status.size`), the time (`time.size`, tabular), the `checklist` ring (52 px, only when the record has steps to be ready) and the commands (`commandsSize`; left out, 64 px for the three dots alone). A `checklist` puts the record's `ProgressChecklist` ring just before its commands, where Vercel ends a project's row with its Production Checklist; the ring opens its own drawer, never the row. Every row has its 32 px mark, its name (600) and, under it, one muted 14 px line; `name` and the mark are required; a record with nothing to say under its name leaves the line out and is the name alone, never a dash. Give every column a `size` in pixels; the name takes the rest. With `layout=\"line\"`, as on Vercel's Projects page, the facts and the status share one muted line, joined by a middle dot, in a column as wide as the name's, and the row stays one line (mark, name over its line, that summary, the time, the ring, the three dots) on a card from 448 px, and on a narrower card the summary and the time move under the name as muted text joined by a middle dot that wraps, in the same row, their labels read by assistive technology only. Rows of `columns` follow the same rule once the card is narrower than their columns: the facts and the time leave the row for that muted text, wrapping so every value stays readable, never a labelled list, and the status stands beside the name, which ends in an ellipsis before it, so the exception is never the part a phone's line cuts. `facts` takes at most three. A menu of one command that is not destructive, with no row command, is that command's button, except in a `line` row, which keeps it in the three dots as Vercel's project row does; without a `commandsSize` the column is as wide as the widest such button, so its label is never cut. Status is only for the exception, empty for the usual record, and its column stands only while a record in the list shows one, so the name and its line keep the room; the commands column likewise stands only while a record has a command, a menu or a message, or the rows open, and it widens for a failed action's next steps only while a row shows one. Never the three dots before the button, a decorative icon for the record's mark, a status on every row, or a destructive button on every row: a destructive command stays in the three dots. A `line` row's summary joins only the facts the record has, so it never ends in a dot. A failed or refused action of the record shows as its catalogue message inside its own row, never a notice under the row: it replaces the detail line, with its next steps beside its commands, or under the message on a card too narrow for the row's columns. With `open` the whole row is one target and takes the `accent` hover, and a row that only opens ends in a muted chevron; a row that opens nothing never reacts to hover. With `editor` the record's row opens its form under its line. `empty` is required: `null` is the choice to draw nothing for a list with no records, and any other value is the state it draws. A note about the whole list is `footer.note`: one muted line, an optional *Learn more* after it at the left and one secondary action at the right, in a strip of its own under the count and the paging, as Vercel ends its deployments list (\"Deployment retention is enabled. Learn more\", *View recently deleted*). Colour is neutral, the name in ink and every other cell in the body grey, except the status badge and a failed action's message. “Pick the variant by what the user needs: no-results for a filtered list that returned zero rows, blank slate or informational for a resource the user hasn’t created, cleared for completed work, permission for role or tier denials, error for a failed load.” (https://vercel.com/geist/empty-state) Ours: `failed`, `noMatch` and `empty` are those variants, drawn inside the card. “The right column holds at most one or two controls. If the row needs more, move secondary actions into a `Dots Menu`.” (https://vercel.com/geist/entity) Ours: one or two row commands (outline buttons of 32 px) stand before the three dots (36 px), the Dots Menu. “Render the Skeleton variant (`entity-with-skeleton`) during load instead of an empty row, and swap to real content once data resolves.” (https://vercel.com/geist/entity) Ours: three still skeleton rows hold the place, in a region that announces what replaces them; then the first that holds: the failed read, no match, the empty state, the rows.",
|
|
190
190
|
words: '“Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity) Ours: the name is the record\'s own and the labels around it are sentence case, not Title Case. “Keep right-column buttons Verb + Noun (`Remove Member`, `Resend Invite`). Bare verbs like `Remove` or `Confirm` lose context once the row scrolls offscreen.” (https://vercel.com/geist/entity) Ours: every string is the product\'s: `label` names the list, a time\'s `phrase` writes its sentence ("Updated {when}"), `missing` is what a screen reader hears for a record with no time, the menu\'s `label` reads "More for {record}". A row command names its result in sentence case ("Edit domain"). A status is the exception\'s word ("Needs attention"), never "Active" on every row.',
|
|
191
191
|
access: '“Wrap the loading region in `aria-busy="true"` and announce completion with `aria-live="polite"` on the destination container, not the skeleton itself.” (https://vercel.com/geist/skeleton) Ours: the loading rows sit in one `SkeletonRegion` that is busy while they stand and announces what replaces them. A table named by `label`, its column headings read by assistive technology only. The row link carries `open.label`; the three dots carry the menu\'s `label`, and the menu\'s first line names the record. The loading rows are a status named by `loadingLabel`, inside the one live region that announces what replaces them. “For multi-select rows, the leading `Checkbox` carries `aria-label="Select {entity name}"` so the row is selectable without relying on the visual label.” (https://vercel.com/geist/entity) Ours: each box is named by `select.label`, which reads "Select {record}".',
|
|
192
192
|
},
|
|
@@ -780,7 +780,7 @@ export const USAGE = [
|
|
|
780
780
|
summary: 'A record\'s identity in a row (DESIGN.md "Tables and missing values"): its mark at 32, its name, and under it one muted `detail` line saying what the record is or where it comes from, so the row reads without a column heading.',
|
|
781
781
|
when: "The first cell of every collection row: the mark, the name, and one muted line saying what the record is or where it comes from, so the row reads without a heading.",
|
|
782
782
|
instead: "An Agent in a gallery is `AgentListRow`; a model is `ModelIdentity`. A descriptive item with prose and actions is `Item`.",
|
|
783
|
-
behaviour: "The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot
|
|
783
|
+
behaviour: "The mark stands 16 px before the name. The name is semibold (600) ink and the detail a muted 14 px line, two lines with no gap between them, so the row is an entity row; `mark` is required; a record with nothing to say under its name leaves `detail` out and is the name alone, never a dash, which is a table cell's missing value and not a record's. The name holds one line and ends in an ellipsis, whole in its title, with a `hint` such as an ID read after it. The detail is one line too and ends in an ellipsis, whole in its title when it is text; in a row on a card narrower than its columns, the facts that leave the row join it after a middle dot and wrap so every value stays readable, and a status that leaves the row stands beside the name, which ends in an ellipsis before it. `wraps` only when the name is the whole record, such as a memory, and then the detail wraps too. The table owns the link.",
|
|
784
784
|
words: '“Lead the left column with a scannable identifier: an `Avatar` or icon, a Title Case label, then sentence-case secondary metadata (`Member since Mar 14, 2026`).” (https://vercel.com/geist/entity) Ours: the name as the record calls itself, and the detail in sentence case: kind, then relation, three to six words, "Company skill · every Agent has it".',
|
|
785
785
|
access: "The name is the row link's text; the mark is decorative; the title carries the full name.",
|
|
786
786
|
},
|
|
@@ -868,7 +868,7 @@ export const USAGE = [
|
|
|
868
868
|
summary: "Says which part of the account a page reads, and changes it.",
|
|
869
869
|
when: "Changing which part of the account a page reads, such as Analytics' scope, or switching between objects from the shell's top bar with `variant=\"navigation\"`.",
|
|
870
870
|
instead: "A viewer with one scope and nothing to create reads its name with no control, which this draws by itself. Finding anything is `CommandDialog`; narrowing rows is `CollectionFilters`.",
|
|
871
|
-
behaviour: "A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.",
|
|
871
|
+
behaviour: "A ghost trigger, at most 16rem, opens a 288 px popover with focus in its search. Navigation omits the visible prefix, wraps the selected name on phones instead of cutting it, carries each option's identity mark and opens a 384 px popover constrained to the viewport. Type to filter, Enter chooses and closes; choosing the current one changes nothing. The caller writes the choice to the URL in `onValueChange`, so sibling pages keep it. An optional create command sits last.",
|
|
872
872
|
words: '`copy.label` names what is chosen ("Scope") beside the current scope\'s name. Search and empty lines name the thing: "Find a scope", "No scope matches".',
|
|
873
873
|
access: 'The trigger reads "Scope: All customers"; the popover has a hidden title; options are a listbox with the current one checked.',
|
|
874
874
|
},
|
|
@@ -1022,7 +1022,7 @@ export const USAGE = [
|
|
|
1022
1022
|
summary: "Records compared across columns: the same facts for every record, read down a column and across a row, inside their `TableCard`.",
|
|
1023
1023
|
when: "“Use `<Table>` for tabular data where rows share the same shape and at least one column is sortable or comparable across rows.” (https://vercel.com/geist/table) Ours: the person compares: usage per Agent, statements, invoices, customers with their partner and count. The columns are fixed, each has a heading, and at least one is a figure or can be sorted. There are three kinds of list. Objects a person opens (a project, a deployment, a member, an integration) are entity rows: a `RecordList`. Values edited in place (a domain, an environment variable) are entity rows that expand into their form: a `RecordList`'s `editor`. Records compared across columns are this table.",
|
|
1024
1024
|
instead: "“For a row of descriptive content paired with a single action (membership row, integration row), use `Entity` instead.” (https://vercel.com/geist/table) “For a key/value metadata block on a detail page, use `Description`, not a two-column table.” (https://vercel.com/geist/table) Ours: Geist's `Entity` is a `RecordList` row. A registry of entities whose columns `RecordList` cannot hold, more than three facts beside a filter bar the product owns, as Vercel's Deployments list, is this table with `rows=\"entities\"`, which draws the same rows: headings for assistive technology only, 72 px rows, a `RecordIdentity` in the identity column. Its `Description` is `ObjectFacts`. A list on a page of its own is a `CollectionPage`. A descriptive list is `ItemGroup`.",
|
|
1025
|
-
behaviour: "Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot
|
|
1025
|
+
behaviour: "Every column has a `header`; a column without one does not compile. The headings are always visible, medium weight in the cells' body grey, 48 px high, with 12 px of room under the labels and then the rule, so the first row starts at the rule like every other; rows are 40 px. Each column is as wide as its definition's `size`, in pixels, and the columns without one share the rest, so a table gives a size to every column but the one that takes the spare width; a value too long for its column ends in an ellipsis, whole in its title (a record's name carries its own), unless the column `wraps`; a column marked `identifier` holds ids or paths and is cut in the middle instead. The last column trails unless it `wraps` or is `centered`; a `centered` column's heading and values sit in the middle of it. Records are divided by a 1 px rule, none after the last. The first and last columns stand in from the card's edges by the card's spacing, so the first lines up with the title. Records of several kinds stand in one table under `group` headings (Vercel's Usage page: *Delivery network*, *Functions*): a heading row that spans the columns, in ink, medium text, 36 px high with the same rule under it, one per group in the order the groups first appear, never a second table; a heading is the group's name and carries no figure of its own. A row that opens a record takes `accent` on hover, and any row on focus within and while its menu is open; a row that opens nothing never reacts to hover. A selected row takes `muted`; headers and footers never highlight and carry no fill. A row never scrolls sideways: on a card narrower than its columns (the sized ones and 160 px for each without a size) the row keeps its name and its `primaryColumns`, and every other column leaves it, its value joining the one muted line under the name, joined by a middle dot (a `Tag` reads as its text there); entity facts wrap below the name so every value stays readable, while other rows end in an ellipsis unless a column `wraps`, or `dropped` when its `compact` says so, or beside an entity row's name, which ends in an ellipsis before it, when it says `name`, as Vercel's lists read on a phone: no labels, no second line; an `optional` column's empty value is left out of the line. A table whose every column is in `primaryColumns`, a settings matrix, lays out by its content there instead, so the name wraps beside the columns. A document's table, such as a legal page's, is read whole instead: its columns take the `wraps` option, so their words wrap and the row grows, and it keeps its header row; in the compact layout every cell but the identity wraps between words. A bounded page's paging stands in the card's footer strip at its trailing edge, opposite `footer`, which stands at the leading edge: its `status` (\"Page 2 of 7\") before Previous and Next, which are the product's router links (`previousLink`, `nextLink`) when each page is its own address, and buttons otherwise; the one that leads nowhere stays, disabled. A note about the whole table is `footerNote`, in a strip of its own under that one: one muted line, an optional *Learn more* after it at the left and one secondary action at the right (Vercel's deployments list ends \"Deployment retention is enabled. Learn more\" with *View recently deleted*); it is the one place a table says a sentence. Colour is neutral: headings and cells in the body grey, no hue, except a status badge or a tone the cell itself carries. “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table) “After an async filter change, wrap the region in `aria-live=\"polite\"` so screen readers announce the new state.” (https://vercel.com/geist/empty-state) Ours: empty sits outside the body, in a polite live region that stays in place so a filter that empties the table is announced. “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table) Ours: the order has one owner, the app's `sorting`, which the URL holds, so no heading is a button and every page of a paged list follows one order. “Apply `tabular-nums` (or Geist Mono) to numeric columns so digits align across rows for comparison.” (https://vercel.com/geist/table) Ours: a numeric column trails in tabular figures.",
|
|
1026
1026
|
words: '“Column headers (`<Table.Head>`) are Title Case nouns or noun phrases: `Last Used`, `Requests (7d)`, `Created`, `Status`. Never sentences.” (https://vercel.com/geist/table) “Render `—` in cells where a value is unknown or not applicable. Don’t substitute `N/A`, `null`, or an empty string.” (https://vercel.com/geist/table) “Use the canonical short relative-time form in cells (`2m ago`, `5h ago`); switch to `Mar 14, 2026` past 7 days. See `Relative Time Card`.” (https://vercel.com/geist/table) “Pagination labels are `Previous` and `Next`. Page-count copy reads `Page 2 of 7` or `21–40 of 142` with an en-dash inside the range.” (https://vercel.com/geist/table) Ours: headings are sentence case nouns with their unit, "Cost (€)", "Tokens", where Geist writes Title Case. The words of a time and of Previous and Next are the product\'s catalogue, in both languages. The footer is a count or the paging; totals and paging each stand in a footer zone of their own, the paging at the trailing edge; the summary row is a labelled total. Italian headings run a third longer and never wrap.',
|
|
1027
1027
|
access: 'A native table named by `tableLabel`, which is required; `th scope="col"`; the record\'s name is the row\'s one link. A sorted column\'s heading carries `aria-sort`. Ours: a missing value is a hidden "—" with screen-reader text, never blank.',
|
|
1028
1028
|
},
|
|
@@ -1198,7 +1198,7 @@ export const USAGE = [
|
|
|
1198
1198
|
summary: "The native table every comparison is drawn with: a head row, figure rows of one line, and cells padded the same at every width.",
|
|
1199
1199
|
when: "“Use `<Table>` for tabular data where rows share the same shape and at least one column is sortable or comparable across rows.” (https://vercel.com/geist/table) Ours: a fixed table a composition draws itself: a summary inside a card, a list of plan limits, figures compared down a column. A table of records is `DataTable`, which draws this one, inside a `TableCard`. It does not virtualize its rows; that is a separate capability.",
|
|
1200
1200
|
instead: "“For a row of descriptive content paired with a single action (membership row, integration row), use `Entity` instead.” (https://vercel.com/geist/table) “For a key/value metadata block on a detail page, use `Description`, not a two-column table.” (https://vercel.com/geist/table) “When the underlying list is empty (filter cleared, never created), render `Empty State` outside the table rather than an empty `<Table.Body>`.” (https://vercel.com/geist/table) Ours: Geist's `Entity` is `Item`, or `RecordList` when the row names a record; its `Description` is `ObjectFacts`; its `Empty State` is `EmptyState`. Records with sorting, paging and an empty state are `DataTable`.",
|
|
1201
|
-
behaviour: 'A head row 48 px high, with 12 px of room under its labels and then a 1 px rule, so the first row starts at the rule like every other; body rows 40 px (a cell that holds a control, such as the three dots, is padded just enough to keep the row at 40), every cell padded 8 px sideways and 10 px above and below in the body, and headings at 14 px / 20 px, weight 500, visible because people compare by them. Body rows have no rule between them unless the body is `bordered`, which draws it straight across the row. In a body that is not `bordered`, the first and last cell of a row take a 6 px corner, so a stripe or a highlight reads as a rounded bar; a `bordered` row keeps square corners, so its rule meets the edge. The last column, its heading and its cells, aligns to the trailing edge; any other numeric column takes `text-right` and `tabular-nums` to match. `striped`, `bordered` and `interactive` belong to `TableBody`: stripes fill the odd rows with `muted` (a selected row there takes `accent`), `bordered` draws a rule between rows (not after the last) and no frame, and an interactive row takes `accent` on hover, on focus within and while its menu is open, over a stripe; a row that opens nothing is not `interactive`. Headers and footers never highlight and carry no fill, and a selected row (`data-state="selected"`) takes `muted`; a footer has a rule above it and ink, medium text. A column\'s width is a `TableCol` in a `TableColgroup`. The table has no fill or edge of its own: it takes its enclosing surface, whose `Card` owns the outer edge, and a visible header and every record but the last end in a quiet 1 px `border` rule. A table that opens nothing implies no action on hover. `density="comfortable"` is the entity row of a record that names itself. On a card narrower than its `compactDetails` layout a row never scrolls sideways: its secondary columns leave the row and their values stand in the `TableDetailRow` under it, one muted line joined by a middle dot that ends in an ellipsis, their labels read by assistive technology only, as Vercel\'s lists read on a phone; a `Tag` there reads as its plain text, and a fact that `wraps` lets the line wrap whole instead of ending in an ellipsis. An entity row (`density="comfortable"`) keeps its
|
|
1201
|
+
behaviour: 'A head row 48 px high, with 12 px of room under its labels and then a 1 px rule, so the first row starts at the rule like every other; body rows 40 px (a cell that holds a control, such as the three dots, is padded just enough to keep the row at 40), every cell padded 8 px sideways and 10 px above and below in the body, and headings at 14 px / 20 px, weight 500, visible because people compare by them. Body rows have no rule between them unless the body is `bordered`, which draws it straight across the row. In a body that is not `bordered`, the first and last cell of a row take a 6 px corner, so a stripe or a highlight reads as a rounded bar; a `bordered` row keeps square corners, so its rule meets the edge. The last column, its heading and its cells, aligns to the trailing edge; any other numeric column takes `text-right` and `tabular-nums` to match. `striped`, `bordered` and `interactive` belong to `TableBody`: stripes fill the odd rows with `muted` (a selected row there takes `accent`), `bordered` draws a rule between rows (not after the last) and no frame, and an interactive row takes `accent` on hover, on focus within and while its menu is open, over a stripe; a row that opens nothing is not `interactive`. Headers and footers never highlight and carry no fill, and a selected row (`data-state="selected"`) takes `muted`; a footer has a rule above it and ink, medium text. A column\'s width is a `TableCol` in a `TableColgroup`. The table has no fill or edge of its own: it takes its enclosing surface, whose `Card` owns the outer edge, and a visible header and every record but the last end in a quiet 1 px `border` rule. A table that opens nothing implies no action on hover. `density="comfortable"` is the entity row of a record that names itself. On a card narrower than its `compactDetails` layout a row never scrolls sideways: its secondary columns leave the row and their values stand in the `TableDetailRow` under it, one muted line joined by a middle dot that ends in an ellipsis, their labels read by assistive technology only, as Vercel\'s lists read on a phone; a `Tag` there reads as its plain text, and a fact that `wraps` lets the line wrap whole instead of ending in an ellipsis. An entity row (`density="comfortable"`) keeps its facts in the identity cell instead: they join the text under the name that `RecordIdentity` draws (`TableCompactLine`), and wrap so every value stays readable. Colour is neutral throughout: headings and cells share the body grey and a heading differs by weight, the footer is ink, and the hue of a state lives in the `StatusBadge` of a cell, never in the row. “Sortable column headers are buttons. The visible label stays Title Case; the sort-direction arrow is decorative and the button announces the next sort state to assistive tech.” (https://vercel.com/geist/table) Ours: no heading sorts the rows, so no heading is a button; the order is the page\'s, through a `CollectionSort` above the list, and only `aria-sort` marks the column it follows. “Apply `tabular-nums` (or Geist Mono) to numeric columns so digits align across rows for comparison.” (https://vercel.com/geist/table) Ours: `DataTable` sets it on a numeric column; in a `Table` a figure\'s cell takes `tabular-nums`.',
|
|
1202
1202
|
words: "“Column headers (`<Table.Head>`) are Title Case nouns or noun phrases: `Last Used`, `Requests (7d)`, `Created`, `Status`. Never sentences.” (https://vercel.com/geist/table) “Render `—` in cells where a value is unknown or not applicable. Don’t substitute `N/A`, `null`, or an empty string.” (https://vercel.com/geist/table) “Use the canonical short relative-time form in cells (`2m ago`, `5h ago`); switch to `Mar 14, 2026` past 7 days. See `Relative Time Card`.” (https://vercel.com/geist/table) Ours: headings are sentence case nouns with their unit, *Cost (€)*, *Tokens*, where Geist writes Title Case. Italian headings run a third longer and never wrap. A missing value is `TableValue`'s dash, read as its `missingLabel`. The table is always named: its `aria-label` says what it lists (*Usage by Agent*).",
|
|
1203
1203
|
access: 'A native `table` named by `aria-label` or `aria-labelledby`, which the types require, or `aria-hidden` when it is only the placeholder of a loading view; `th scope="col"`, with `aria-sort` on the column the rows are ordered by.',
|
|
1204
1204
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nadicodeai/ui",
|
|
3
|
-
"version": "12.1.
|
|
3
|
+
"version": "12.1.136",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@base-ui/react": "^1.9.0",
|
|
84
84
|
"@iconify-icons/logos": "^2.0.3",
|
|
85
85
|
"@iconify/react": "^6.0.2",
|
|
86
|
-
"@nadicodeai/design-system": "12.1.
|
|
86
|
+
"@nadicodeai/design-system": "12.1.136",
|
|
87
87
|
"@tanstack/react-table": "^9.3.1",
|
|
88
88
|
"@vgpu/wgsl": "0.6.0",
|
|
89
89
|
"class-variance-authority": "^0.7.1",
|