ObjectsGrid
Source: client/lib/theme/vueda-tailwind/objects-grid/index.js
The outer scroll surface and card fill for the grid. It owns the density attribute, tabular number rendering, and
data-flushedge merging used when the grid is embedded in a parent surface. The radius and inset hairline apply in table layout only, where the root is the surface the rows sit on and its edge closes the last row. Card layout drops the edge, because each card already carries one through ObjectsGrid.bodyRow and an edge here would sit 4 px outside every card edge. The fill stays in both layouts, so the gutter between cards keeps the same tone as the chrome a consumer wraps around the grid.data-flushremoves the embedded grid's inset hairline and radius; the parent owns the outer edge and any separators around the grid.
Vue component: ObjectsGrid
| Slot | Default classes | Description |
|---|---|---|
| root | callback | The outer scroll surface and card fill for the grid. It owns the density attribute, tabular number rendering, and data-flush edge merging used when the grid is embedded in a parent surface. The radius and inset hairline apply in table layout only, where the root is the surface the rows sit on and its edge closes the last row. Card layout drops the edge, because each card already carries one through ObjectsGrid.bodyRow and an edge here would sit 4 px outside every card edge. The fill stays in both layouts, so the gutter between cards keeps the same tone as the chrome a consumer wraps around the grid. data-flush removes the embedded grid's inset hairline and radius; the parent owns the outer edge and any separators around the grid. |
| table | callback | The inner table-shaped container. It switches on the table display contract only when the active breakpoint resolves to table layout, and fills the root so row dividers reach both card edges instead of stopping at the last column. The separated border model keeps cell padding free of collapsed edges, so row dividers sit on cells rather than on rows. |
| headerRowGroup | callback | The header row group for table layout. It is hidden in card layout because card labels come from ObjectsGridCardCell.header. |
| headerRow | callback | The table-layout header row wrapper. It becomes a table row only when ObjectsGrid.table is in table mode, and carries the divider that separates the header band from the first data row. The divider sits on the header cells because the separated border model does not paint borders on rows. |
| headerCell | callback | The table-layout column header cell. It aligns numeric columns with data-numeric so sortable columns keep their right edge stable. |
| emptyText | text-center | The empty-state cell wrapper. It centers the empty content inside the single row the grid renders when it has no data. That row spans the full width in both layouts, but by different means: in table layout the cell is a td carrying colspan, because a CSS table gives a non-cell child one column; in card layout it is a div inside a row that spans the card grid. A recipe here should therefore set no display or column utilities of its own. |
| emptyContent | 4 classes | The empty-state content stack. It styles the icon, title, and variant-driven loading or error treatment used by the default empty slot. |
| bodyRowGroup | callback | The body row group for loading, empty, and data rows. It is a responsive card grid in card layout and a table row group in table layout. In table layout it removes the last row's divider from that row's cells so the enclosing ObjectsGrid.root edge remains the closing edge. |
| bodyRow | callback | The shared row wrapper for table rows and card rows. It carries hover, selected, and marked-destroy states while preserving action controls. Selected rows use a low-primary tint plus a leading primary rail so selection stays distinct from hover. In table layout each row also divides from the next; that divider sits on the row's cells, because the separated border model does not paint borders on rows. In card layout the row is the card: it carries the card fill, radius, and edge, so it reads the same on any ground and is the only edge in that layout. |
| cardContainer | 3 classes | The card-layout field grid inside each row card. It aligns all ObjectsGridCardCell.header and ObjectsGridCardCell.value fragments into label and value columns. |
| rowActions | Row-action classes exposed for consumers that compose inline row controls next to object rows. Use rowActions.root for the hidden wrapper and rowActions.action for compact icon buttons. |
root
The outer scroll surface and card fill for the grid. It owns the density attribute, tabular number rendering, and
data-flushedge merging used when the grid is embedded in a parent surface. The radius and inset hairline apply in table layout only, where the root is the surface the rows sit on and its edge closes the last row. Card layout drops the edge, because each card already carries one through ObjectsGrid.bodyRow and an edge here would sit 4 px outside every card edge. The fill stays in both layouts, so the gutter between cards keeps the same tone as the chrome a consumer wraps around the grid.data-flushremoves the embedded grid's inset hairline and radius; the parent owns the outer edge and any separators around the grid.
Callback:
({ isTable }) => [
"max-w-full overflow-x-auto",
"bg-card text-foreground text-body",
"[font-variant-numeric:tabular-nums_slashed-zero]",
{
"rounded-vueda-card hairline hairline-border": isTable,
},
// Flush variant: an ancestor stamps `data-flush="true"` (e.g. FieldSetTabularInline.body)
// to merge the grid into a parent card without doubling borders. Drop the rounded edge
// and inset hairline; the parent owns the outer edge and feedback-panel separator.
"[[data-flush]_&]:rounded-none [[data-flush]_&]:shadow-none",
]Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:18
table
The inner table-shaped container. It switches on the table display contract only when the active breakpoint resolves to table layout, and fills the root so row dividers reach both card edges instead of stopping at the last column. The separated border model keeps cell padding free of collapsed edges, so row dividers sit on cells rather than on rows.
Callback:
({ isTable }) => [
{
"!table w-full border-separate border-spacing-0": isTable,
},
]Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:33
headerRowGroup
The header row group for table layout. It is hidden in card layout because card labels come from ObjectsGridCardCell.header.
Callback:
({ isTable }) => [
"gap-1 2xs:gap-2 2xl:gap-4",
{
"!table-header-group": isTable,
hidden: !isTable,
},
]Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:41
headerRow
The table-layout header row wrapper. It becomes a table row only when ObjectsGrid.table is in table mode, and carries the divider that separates the header band from the first data row. The divider sits on the header cells because the separated border model does not paint borders on rows.
Callback:
({ isTable }) => [{ "!table-row": isTable, "[&>*]:border-b-hairline": isTable }]Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:51
headerCell
The table-layout column header cell. It aligns numeric columns with
data-numericso sortable columns keep their right edge stable.
Callback:
({ isTable }) => [
"align-bottom",
"font-semibold",
"select-none",
// Match the body cells and TableHead: a label wider than its column's values (a
// "Unit Price" header over "0.38") widens the column instead of wrapping.
"whitespace-nowrap",
"data-[numeric]:text-right",
{
"!table-cell": isTable,
},
]Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:55
emptyText
The empty-state cell wrapper. It centers the empty content inside the single row the grid renders when it has no data. That row spans the full width in both layouts, but by different means: in table layout the cell is a
tdcarryingcolspan, because a CSS table gives a non-cell child one column; in card layout it is adivinside a row that spans the card grid. A recipe here should therefore set no display or column utilities of its own.
Default classes:
text-center
Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:70
emptyContent
The empty-state content stack. It styles the icon, title, and variant-driven loading or error treatment used by the default empty slot.
Default classes:
flex flex-col items-center justify-center gap-2.5 py-10 text-center text-muted-foreground
[&>[data-slot=icon]]:text-xl [&>[data-slot=icon]]:text-muted-foreground/70
data-[variant=loading]:[&>[data-slot=icon]]:animate-spin
data-[variant=error]:[&>[data-slot=icon]]:text-destructive/80Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:74
bodyRowGroup
The body row group for loading, empty, and data rows. It is a responsive card grid in card layout and a table row group in table layout. In table layout it removes the last row's divider from that row's cells so the enclosing ObjectsGrid.root edge remains the closing edge.
Callback:
({ isTable }) => [
"print:block",
{
// 1 / 2 / 3 column responsive default; projects override via theme as needed.
"grid grid-flow-row grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 p-1 2xs:p-2 2xl:p-4 gap-1 2xs:gap-2 2xl:gap-4":
!isTable,
"!table-row-group [&>*:last-child>*]:border-b-0": isTable,
},
]Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:84
bodyRow
The shared row wrapper for table rows and card rows. It carries hover, selected, and marked-destroy states while preserving action controls. Selected rows use a low-primary tint plus a leading primary rail so selection stays distinct from hover. In table layout each row also divides from the next; that divider sits on the row's cells, because the separated border model does not paint borders on rows. In card layout the row is the card: it carries the card fill, radius, and edge, so it reads the same on any ground and is the only edge in that layout.
Callback:
({ isTable }) => {
return [
"group/row transition-colors",
"hover:bg-accent/50 active:bg-accent",
// Selected row state.
"data-[state=selected]:bg-primary/[0.06]",
"data-[state=selected]:hover:bg-primary/[0.09]",
"data-[state=selected]:active:bg-primary/[0.12]",
"data-[state=selected]:[box-shadow:inset_2px_0_0_0_var(--primary)]",
// marked-destroy: 4% destructive tint and a strikethrough on every cell whose
// `data-field` / `data-card` / `data-card-header` is not the action column. Action
// controls are excluded so the user can still click "undo" on the destroy mark.
"data-[state=marked-destroy]:bg-destructive/[0.04]",
"data-[state=marked-destroy]:[&_[data-field]:not([data-field=item-action-bar])]:line-through",
"data-[state=marked-destroy]:[&_[data-card]:not([data-card=item-action-bar])]:line-through",
"data-[state=marked-destroy]:[&_[data-card-header]:not([data-card-header=item-action-bar])]:line-through",
// you can't tell how many cards are on a row, so we must treat them all the same.
// first and last don't help us here.
{
"!table-row [&>*]:border-b-hairline": isTable,
"p-1 2xs:p-2 2xl:p-4 rounded-vueda-card hairline hairline-border bg-card overflow-y-auto":
!isTable,
},
];
}Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:96
cardContainer
The card-layout field grid inside each row card. It aligns all ObjectsGridCardCell.header and ObjectsGridCardCell.value fragments into label and value columns.
Default classes:
p-1 2xs:p-2 2xl:p-4 gap-1 2xs:gap-2 2xl:gap-4 mb-1 mt-2
grid grid-cols-[minmax(96px,max-content)_1fr] items-baseline
[&>*]:min-w-0Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:126
rowActions
Row-action classes exposed for consumers that compose inline row controls next to object rows. Use
rowActions.rootfor the hidden wrapper androwActions.actionfor compact icon buttons.
Source: client/lib/theme/vueda-tailwind/objects-grid/ObjectsGrid.theme.js:139