Files
jobtrackingapp/job-tracker-ui/src/layout/PageLayout.tsx
T
2026-09-28 17:26:02 +02:00

51 lines
2.2 KiB
TypeScript

import React, { useId } from "react";
import { Box, Typography } from "@mui/material";
// Opt-in widths: the shell owns gutters; consumers must not add a second page gutter.
export type PageWidth = "reading" | "data" | "editor";
type HeaderProps = {
title: string;
description?: string;
actions?: React.ReactNode;
level?: "h1" | "h2" | "h3";
titleId?: string;
titleRef?: React.Ref<HTMLHeadingElement>;
};
export function PageHeader({ title, description, actions, level = "h1", titleId, titleRef }: HeaderProps) {
return (
<Box component="header" sx={{ display: "flex", flexWrap: "wrap", alignItems: "flex-start", gap: 2, minWidth: 0 }}>
<Box sx={{ flex: "1 1 16rem", minWidth: 0 }}>
<Typography ref={titleRef} tabIndex={titleRef ? -1 : undefined} id={titleId} component={level} variant={level === "h1" ? "pageTitle" : "sectionTitle"} sx={{ overflowWrap: "anywhere" }}>
{title}
</Typography>
{description ? <Typography variant="pageDescription" component="p" color="text.secondary" sx={{ mt: 1, maxWidth: "75ch", overflowWrap: "anywhere" }}>
{description}
</Typography> : null}
</Box>
{actions ? <Box sx={{ display: "flex", flexWrap: "wrap", alignItems: "center", gap: 1, minWidth: 0, maxWidth: "100%", "& > *": { maxWidth: "100%", whiteSpace: "normal", overflowWrap: "anywhere" } }}>
{actions}
</Box> : null}
</Box>
);
}
// Presentation only: domain components keep their loading, draft and mutation owners.
export function PageSection({ title, description, actions, children, level = "h2", surface = false }: Omit<HeaderProps, "titleId" | "level" | "titleRef"> & {
children?: React.ReactNode;
level?: "h2" | "h3";
surface?: boolean;
}) {
const titleId = useId();
return (
<Box component="section" aria-labelledby={titleId} sx={{
display: "grid", gap: 2, minWidth: 0, alignContent: "start",
...(surface ? { p: "var(--app-card-padding)", bgcolor: "background.paper", border: "1px solid", borderColor: "divider", borderRadius: 1 } : {}),
}}>
<PageHeader title={title} titleId={titleId} description={description} actions={actions} level={level} />
{children ? <Box sx={{ minWidth: 0 }}>{children}</Box> : null}
</Box>
);
}