mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-10 05:53:05 +09:00
fix(ui): stack DataTable rows into cards on phones for Servers, Fleet proxy, Incidents, Jobs
This commit is contained in:
1 parent
eeb2792d06
commit
33febcaf1b
6 files changed
+68
-2
No files matched your search
@@ -105,6 +105,9 @@ export function DataTable({
|
||||
className,
|
||||
rowClassName,
|
||||
tableClassName,
|
||||
// Below 640px each row becomes a card: first column is the title, the rest
|
||||
// are label/value pairs (CSS-only, see .sk-dtable-wrap--stack).
|
||||
stackOnMobile = false,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const internal = useTableSort({
|
||||
@@ -365,10 +368,14 @@ export function DataTable({
|
||||
/>
|
||||
</TableCell>
|
||||
)}
|
||||
{visibleColumns.map((column) => (
|
||||
{visibleColumns.map((column, columnIndex) => (
|
||||
<TableCell
|
||||
key={`${key}-${column.key}`}
|
||||
className={column.cellClassName}
|
||||
data-label={stackOnMobile && typeof column.header === 'string'
|
||||
? translateLabel(t, column, 'header') || undefined
|
||||
: undefined}
|
||||
data-primary={stackOnMobile && columnIndex === 0 ? '' : undefined}
|
||||
>
|
||||
{column.render
|
||||
? column.render(row)
|
||||
@@ -380,7 +387,7 @@ export function DataTable({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('sk-dtable-wrap', className)} ref={wrapRef}>
|
||||
<div className={cn('sk-dtable-wrap', stackOnMobile && 'sk-dtable-wrap--stack', className)} ref={wrapRef}>
|
||||
<Table className={cn('sk-dtable', tableClassName)}>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
|
||||
@@ -400,6 +400,7 @@ const FleetProxy = () => {
|
||||
|
||||
<div className="fleet-proxy__table">
|
||||
<DataTable
|
||||
stackOnMobile
|
||||
loading={loading}
|
||||
columns={chrome.columns}
|
||||
data={rows}
|
||||
|
||||
@@ -533,6 +533,7 @@ export default function Incidents() {
|
||||
/>
|
||||
) : (
|
||||
<DataTable
|
||||
stackOnMobile
|
||||
{...chrome.tableProps}
|
||||
columns={chrome.columns}
|
||||
data={shown}
|
||||
|
||||
@@ -482,6 +482,7 @@ export default function Jobs() {
|
||||
/>
|
||||
) : scheduledView ? (
|
||||
<DataTable
|
||||
stackOnMobile
|
||||
columns={scheduledColumns}
|
||||
data={scheduled}
|
||||
keyField="id"
|
||||
@@ -531,6 +532,7 @@ export default function Jobs() {
|
||||
/>
|
||||
) : (
|
||||
<DataTable
|
||||
stackOnMobile
|
||||
columns={chrome.columns}
|
||||
data={jobs}
|
||||
keyField="id"
|
||||
|
||||
@@ -447,6 +447,7 @@ const Servers = () => {
|
||||
/>
|
||||
) : (
|
||||
<DataTable
|
||||
stackOnMobile
|
||||
columns={chrome.columns}
|
||||
data={filteredServers}
|
||||
keyField="id"
|
||||
|
||||
@@ -510,6 +510,60 @@ $sk-tones: ('accent', 'green', 'cyan', 'amber', 'red', 'violet');
|
||||
border-radius: $radius-card;
|
||||
}
|
||||
|
||||
// Opt-in (DataTable `stackOnMobile`): phones get one card per row instead of a
|
||||
// sideways-scrolling grid. Header is hidden; each cell is "label value", the
|
||||
// first column is the title and an unlabelled cell (actions) sits on the right.
|
||||
@media (max-width: $breakpoint-sm - 1px) {
|
||||
.sk-dtable-wrap--stack {
|
||||
overflow-x: visible;
|
||||
|
||||
.sk-dtable,
|
||||
.sk-dtable tbody { display: block; }
|
||||
.sk-dtable thead { display: none; }
|
||||
|
||||
.sk-dtable tbody tr {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $space-1;
|
||||
padding: $space-3 $space-4;
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
}
|
||||
.sk-dtable tbody tr:last-child { border-bottom: none; }
|
||||
|
||||
.sk-dtable tbody td,
|
||||
.sk-dtable tbody td:first-child,
|
||||
.sk-dtable tbody td:last-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $space-3;
|
||||
height: auto;
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
text-align: right;
|
||||
}
|
||||
.sk-dtable tbody td[data-label]::before {
|
||||
content: attr(data-label);
|
||||
flex-shrink: 0;
|
||||
font-size: $text-xs;
|
||||
color: var(--text-faint);
|
||||
}
|
||||
.sk-dtable tbody td:not([data-label]) { justify-content: flex-end; }
|
||||
.sk-dtable tbody td[data-primary] { justify-content: flex-start; text-align: left; font-size: $text-md; }
|
||||
.sk-dtable tbody td[data-primary]::before { content: none; }
|
||||
.sk-dtable tbody td:empty { display: none; }
|
||||
// A cell that is only a "open" chevron rides the card's top-right corner
|
||||
// instead of costing a row of its own.
|
||||
.sk-dtable tbody tr { position: relative; }
|
||||
.sk-dtable tbody td:has(> svg:only-child) {
|
||||
position: absolute;
|
||||
inset-block-start: $space-3;
|
||||
inset-inline-end: $space-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A table that is one section of a titled card/panel runs flush inside it:
|
||||
// two frames a pixel apart is the seam this rule removes. A page-specific
|
||||
// panel that frames its own header + table opts in with --flush.
|
||||
|
||||
Reference in new issue
Block a user