Segment table
canon
Listing table: sortable header, frozen checkbox and identity columns, lazy loading by 50 and an optional summary row in its own tfoot.
Engine lives in the app (components/segment/SegmentScreen.tsx — SegmentTable); the lite variant strips checkbox, column menu, summary and frozen identity. Row height is --row-h (37px) — the 44/52 densities belong to Item list, not here. The registry previously listed .sgb-summary, which does not exist: the summary row is the table tfoot and .sgb-sum is the per-column control.
Used in 23 files · 22 areas · 23× — pages/AccountingInvoicesPage, pages/AccountingOpenItemsPage, pages/AccountingReceivedInvoicesPage, pages/AiZamestnanciPage, pages/DokumentyPage, pages/FirmyPage …
Default
export const Default = () => {
const [sel, setSel] = useState<Set<string>>(new Set());
return (
<Table
sel={sel}
onToggle={(id) =>
setSel((s) => {
const n = new Set(s);
if (n.has(id)) n.delete(id);
else n.add(id);
return n;
})
}
summary
sorted="asc"
/>
);
};
Variants
full — checkbox column, sortable header, summary row in the table's own tfoot (so the columns line up for free) | Firma | Město | Obchody | Objem | Vlastník |
|---|
| BTBrno Tech a.s. | Brno | 3 | 1 250 000 | Štěpán Svoboda |
| ACAtol Capital s.r.o. | Praha | 1 | 480 000 | Petr Smutný |
| MIModřice Invest | Modřice | 5 | 2 100 000 | Eva Dvořáková |
lite — the same engine without the chrome: no checkbox, no column menu, no summary; for tables inside a recordwithout the summary — the row is opt-in per listing, not a default
export const Variants = () => (
<div style={{ display: 'grid', gap: 'var(--sp-5)' }}>
<Labeled text="full — checkbox column, sortable header, summary row in the table's own tfoot (so the columns line up for free)">
<Table summary sorted="asc" />
</Labeled>
<Labeled text="lite — the same engine without the chrome: no checkbox, no column menu, no summary; for tables inside a record">
<Table lite />
</Labeled>
<Labeled text="without the summary — the row is opt-in per listing, not a default">
<Table />
</Labeled>
</div>
);
States
selected rows — the checkbox appears on hover and stays once the row is picked; the bulk bar belongs to the screen, not to the table loading — skeleton lines in the cells, so the columns keep their width and nothing jumps when data lands column overflow — when the table scrolls sideways, the frozen identity column casts a shadow so the reader knows what is pinned empty — this is what a listing with no rows shows today: the header and nothing under it. There is no empty state in the engine (see the review).
export const States = () => {
const [sel, setSel] = useState<Set<string>>(new Set(['1']));
return (
<div style={{ display: 'grid', gap: 'var(--sp-5)' }}>
<Labeled text="selected rows — the checkbox appears on hover and stays once the row is picked; the bulk bar belongs to the screen, not to the table">
<Table
sel={sel}
onToggle={(id) =>
setSel((s) => {
const n = new Set(s);
if (n.has(id)) n.delete(id);
else n.add(id);
return n;
})
}
/>
</Labeled>
<Labeled text="loading — skeleton lines in the cells, so the columns keep their width and nothing jumps when data lands">
<Table>
{[0, 1, 2].map((i) => (
<tr key={i}>
<td className="col-ck" />
<td>
<span className="cell-id">
<span className="sk-av square" />
<span className="sk sk-line" style={{ width: 120 }} />
</span>
</td>
{[72, 40, 80, 96].map((w, j) => (
<td key={j}>
<span className="sk sk-line" style={{ width: w }} />
</td>
))}
</tr>
))}
</Table>
</Labeled>
<Labeled text="column overflow — when the table scrolls sideways, the frozen identity column casts a shadow so the reader knows what is pinned">
<div style={{ maxWidth: 520, overflow: 'hidden' }}>
<Table scrolledX summary />
</div>
</Labeled>
<Labeled text="empty — this is what a listing with no rows shows today: the header and nothing under it. There is no empty state in the engine (see the review).">
<Table>{null}</Table>
</Labeled>
</div>
);
};
With icon & kbd
header icons mark the kind of column; the sort mark appears on the column that is sorted The table carries no shortcut chips. Rows can be made keyboard-operable by the screen (`rowKeyboard`), and then Enter or Space opens the row — the row stays a `tr`, so the table semantics survive for screen readers.
export const WithIconAndKbd = () => (
<div style={{ display: 'grid', gap: 'var(--sp-5)' }}>
<Labeled text="header icons mark the kind of column; the sort mark appears on the column that is sorted">
<Table sorted="desc" />
</Labeled>
<span style={label}>
The table carries no shortcut chips. Rows can be made keyboard-operable by the screen
(`rowKeyboard`), and then Enter or Space opens the row — the row stays a `tr`, so the table
semantics survive for screen readers.
</span>
</div>
);
Composition
export const Composition = () => {
const [sel, setSel] = useState<Set<string>>(new Set());
return (
<div
style={{
border: '1px solid var(--border)',
borderRadius: 'var(--radius-lg)',
overflow: 'hidden',
background: 'var(--bg-canvas)',
}}
>
<div className="tbar">
<div className="tbar-id">
<span className="tbar-h">Firmy</span>
</div>
<span className="vspace" />
<button type="button" className="tchip">
<Icon name="filter" />
<span className="k">Filtr</span>
</button>
</div>
<div className="fb">
<button type="button" className="fb-chip">
<Icon name="map-pin" />
<span className="k">Kraj</span>
<span className="v">Praha</span>
<Icon name="chevron-down" className="chev" />
</button>
<Button variant="ghost">
<Icon name="plus" />
Filtr
</Button>
</div>
<Table
sel={sel}
onToggle={(id) =>
setSel((s) => {
const n = new Set(s);
if (n.has(id)) n.delete(id);
else n.add(id);
return n;
})
}
summary
sorted="asc"
/>
</div>
);
};
„+“ at the end of the header row — the only entry to the column set open: search · visible (table order) · separator · hidden; identity not listed
export const ColumnPanel = () => (
<div
style={{
display: 'grid',
gridTemplateColumns: 'minmax(0, 1fr) auto',
gap: 'var(--sp-5)',
alignItems: 'start',
}}
>
<Labeled text="„+“ at the end of the header row — the only entry to the column set">
<Table addCol />
</Labeled>
<Labeled text="open: search · visible (table order) · separator · hidden; identity not listed">
<StaticPopover width={260}>
<label className="acrm-recordselect-search">
<Icon name="search" />
<input placeholder="Hledat sloupec…" aria-label="Hledat sloupec…" />
</label>
<div className="cm-list cm-reorder acrm-recordselect-list">
<CheckItem icon="map-pin" checked>
Město
</CheckItem>
<CheckItem icon="briefcase" checked>
Obchody
</CheckItem>
<CheckItem icon="cash" checked>
Objem
</CheckItem>
<CheckItem icon="user" checked>
Vlastník
</CheckItem>
<div className="acrm-popover-sep" role="separator" />
<CheckItem icon="hash">IČO</CheckItem>
<CheckItem icon="tag">Štítky</CheckItem>
</div>
</StaticPopover>
</Labeled>
</div>
);