feat(mui): fixed-width DataTable columns + restore Projects next-number hint
DataTable: optional per-column width; when any column sets one, the table uses table-layout:fixed with a colgroup so columns keep a stable size instead of reflowing as rows are filtered (overflow clipped with ellipsis). Tables without widths keep auto layout. Projects: assign column widths; restore the original '(přiděleno automaticky)' suffix on the next-number hint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -245,6 +245,7 @@ export default function Projects() {
|
|||||||
{
|
{
|
||||||
key: "project_number",
|
key: "project_number",
|
||||||
header: "Číslo",
|
header: "Číslo",
|
||||||
|
width: "12%",
|
||||||
sortKey: "project_number",
|
sortKey: "project_number",
|
||||||
mono: true,
|
mono: true,
|
||||||
render: (p) => p.project_number,
|
render: (p) => p.project_number,
|
||||||
@@ -252,6 +253,7 @@ export default function Projects() {
|
|||||||
{
|
{
|
||||||
key: "name",
|
key: "name",
|
||||||
header: "Název",
|
header: "Název",
|
||||||
|
width: "16%",
|
||||||
sortKey: "name",
|
sortKey: "name",
|
||||||
bold: true,
|
bold: true,
|
||||||
render: (p) => p.name || "—",
|
render: (p) => p.name || "—",
|
||||||
@@ -259,16 +261,19 @@ export default function Projects() {
|
|||||||
{
|
{
|
||||||
key: "customer",
|
key: "customer",
|
||||||
header: "Zákazník",
|
header: "Zákazník",
|
||||||
|
width: "13%",
|
||||||
render: (p) => p.customer_name || "—",
|
render: (p) => p.customer_name || "—",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "responsible",
|
key: "responsible",
|
||||||
header: "Zodpovědná osoba",
|
header: "Zodpovědná osoba",
|
||||||
|
width: "14%",
|
||||||
render: (p) => p.responsible_user_name || "—",
|
render: (p) => p.responsible_user_name || "—",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "status",
|
key: "status",
|
||||||
header: "Stav",
|
header: "Stav",
|
||||||
|
width: "10%",
|
||||||
sortKey: "status",
|
sortKey: "status",
|
||||||
render: (p) => (
|
render: (p) => (
|
||||||
<StatusChip
|
<StatusChip
|
||||||
@@ -280,6 +285,7 @@ export default function Projects() {
|
|||||||
{
|
{
|
||||||
key: "order",
|
key: "order",
|
||||||
header: "Objednávka",
|
header: "Objednávka",
|
||||||
|
width: "12%",
|
||||||
render: (p) =>
|
render: (p) =>
|
||||||
p.order_id ? (
|
p.order_id ? (
|
||||||
<Box
|
<Box
|
||||||
@@ -300,18 +306,21 @@ export default function Projects() {
|
|||||||
{
|
{
|
||||||
key: "start_date",
|
key: "start_date",
|
||||||
header: "Začátek",
|
header: "Začátek",
|
||||||
|
width: "8%",
|
||||||
mono: true,
|
mono: true,
|
||||||
render: (p) => formatDate(p.start_date),
|
render: (p) => formatDate(p.start_date),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "end_date",
|
key: "end_date",
|
||||||
header: "Konec",
|
header: "Konec",
|
||||||
|
width: "8%",
|
||||||
mono: true,
|
mono: true,
|
||||||
render: (p) => formatDate(p.end_date),
|
render: (p) => formatDate(p.end_date),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "actions",
|
key: "actions",
|
||||||
header: "Akce",
|
header: "Akce",
|
||||||
|
width: "7%",
|
||||||
align: "right",
|
align: "right",
|
||||||
render: (p) => (
|
render: (p) => (
|
||||||
<Box sx={{ display: "flex", gap: 0.5, justifyContent: "flex-end" }}>
|
<Box sx={{ display: "flex", gap: 0.5, justifyContent: "flex-end" }}>
|
||||||
@@ -443,7 +452,7 @@ export default function Projects() {
|
|||||||
nextNumberQuery.data?.number ??
|
nextNumberQuery.data?.number ??
|
||||||
nextNumberQuery.data?.next_number ??
|
nextNumberQuery.data?.next_number ??
|
||||||
"…"
|
"…"
|
||||||
}`}
|
} (přiděleno automaticky)`}
|
||||||
submitText="Vytvořit"
|
submitText="Vytvořit"
|
||||||
loading={creating}
|
loading={creating}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ export interface DataColumn<T> {
|
|||||||
bold?: boolean;
|
bold?: boolean;
|
||||||
/** If set, the header becomes a clickable sort control (requires onSort). */
|
/** If set, the header becomes a clickable sort control (requires onSort). */
|
||||||
sortKey?: string;
|
sortKey?: string;
|
||||||
|
/**
|
||||||
|
* Fixed column width (number = px, string = any CSS width incl. "%"). When
|
||||||
|
* ANY column sets a width the table switches to `table-layout: fixed`, so
|
||||||
|
* columns keep a stable size regardless of cell content (e.g. while
|
||||||
|
* filtering). Overflowing text is clipped with an ellipsis.
|
||||||
|
*/
|
||||||
|
width?: number | string;
|
||||||
render: (row: T) => ReactNode;
|
render: (row: T) => ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +54,28 @@ export default function DataTable<T>({
|
|||||||
console.warn("DataTable: columns with sortKey require an onSort prop.");
|
console.warn("DataTable: columns with sortKey require an onSort prop.");
|
||||||
}
|
}
|
||||||
if (rows.length === 0 && empty) return <>{empty}</>;
|
if (rows.length === 0 && empty) return <>{empty}</>;
|
||||||
|
// When any column declares a width, lock the table to fixed layout so column
|
||||||
|
// widths come from the colgroup (stable) instead of from cell content (which
|
||||||
|
// jumps as rows are filtered). Tables that set no widths keep auto layout.
|
||||||
|
const hasWidths = columns.some((c) => c.width != null);
|
||||||
|
const colWidth = (w: number | string | undefined) =>
|
||||||
|
w == null ? undefined : typeof w === "number" ? `${w}px` : w;
|
||||||
return (
|
return (
|
||||||
<TableContainer sx={{ borderRadius: 2 }}>
|
<TableContainer sx={{ borderRadius: 2, overflowX: "auto" }}>
|
||||||
<Table size="small" sx={{ "& td, & th": { borderColor: "divider" } }}>
|
<Table
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
...(hasWidths ? { tableLayout: "fixed", minWidth: 720 } : {}),
|
||||||
|
"& td, & th": { borderColor: "divider" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{hasWidths && (
|
||||||
|
<colgroup>
|
||||||
|
{columns.map((c) => (
|
||||||
|
<col key={c.key} style={{ width: colWidth(c.width) }} />
|
||||||
|
))}
|
||||||
|
</colgroup>
|
||||||
|
)}
|
||||||
<TableHead>
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
{columns.map((c) => (
|
{columns.map((c) => (
|
||||||
@@ -101,6 +127,13 @@ export default function DataTable<T>({
|
|||||||
...(c.mono
|
...(c.mono
|
||||||
? { fontFamily: "'DM Mono', Menlo, monospace" }
|
? { fontFamily: "'DM Mono', Menlo, monospace" }
|
||||||
: {}),
|
: {}),
|
||||||
|
...(hasWidths
|
||||||
|
? {
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
overflow: "hidden",
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{c.render(row)}
|
{c.render(row)}
|
||||||
|
|||||||
Reference in New Issue
Block a user