mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(ui): give the page-header description the full row on a phone
With three or four icon actions beside the title, the description was left a ~130px column that wrapped five or six times. Below sm it now spans the row under the title and actions; from sm up the actions span both rows exactly as before. A one-word German title wider than the column beside the actions ran underneath them; the heading hyphenates by the page language and breaks as a last resort. Notifications, whose two actions are text links, moves them under the description on a phone (stackActionsOnPhone).
This commit is contained in:
1 parent
24e6972f95
commit
dc38540573
2 files changed
+55
-15
No files matched your search
@@ -201,6 +201,7 @@ export default function NotificationsPage() {
|
||||
title={t("notifications.title")}
|
||||
description={t("notifications.subtitle")}
|
||||
actions={headerActions}
|
||||
stackActionsOnPhone
|
||||
/>
|
||||
<EmptyState
|
||||
className="max-w-2xl"
|
||||
@@ -227,6 +228,7 @@ export default function NotificationsPage() {
|
||||
title={t("notifications.title")}
|
||||
description={t("notifications.subtitle")}
|
||||
actions={headerActions}
|
||||
stackActionsOnPhone
|
||||
/>
|
||||
|
||||
{/* Desktop: table layout */}
|
||||
|
||||
@@ -26,6 +26,15 @@ import { cn } from "@/lib/utils";
|
||||
* shells hand-rolled the whole block to get the second copy, which is how
|
||||
* they drifted apart from every module page in the first place.
|
||||
*/
|
||||
/**
|
||||
* A one-word German title ("Krankheitstagebuch") is wider than the column a
|
||||
* phone leaves beside the actions, and without a break opportunity it ran
|
||||
* underneath them. `hyphens-auto` breaks it by the page's `lang`; the
|
||||
* `wrap-break-word` floor covers a word the hyphenation dictionary does not.
|
||||
*/
|
||||
const HEADING_CLASS =
|
||||
"text-2xl font-bold tracking-tight hyphens-auto wrap-break-word";
|
||||
|
||||
export function PageHeader({
|
||||
title,
|
||||
titleId,
|
||||
@@ -34,6 +43,7 @@ export function PageHeader({
|
||||
backLink,
|
||||
topSlot,
|
||||
actions,
|
||||
stackActionsOnPhone = false,
|
||||
className,
|
||||
}: {
|
||||
title: ReactNode;
|
||||
@@ -44,37 +54,65 @@ export function PageHeader({
|
||||
/** Optional block above the title (e.g. a hub back-link the shell owns). */
|
||||
topSlot?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
/**
|
||||
* Below `sm`, move the actions onto their own row under the description.
|
||||
* For text-labelled actions (two outline links, say): beside the title they
|
||||
* left the H1 a column narrower than its own word. Icon actions fit beside
|
||||
* the title and leave this off.
|
||||
*/
|
||||
stackActionsOnPhone?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
const heading =
|
||||
headingAs === "div" ? (
|
||||
<div
|
||||
id={titleId}
|
||||
role="heading"
|
||||
aria-level={1}
|
||||
className="text-2xl font-bold tracking-tight"
|
||||
>
|
||||
<div id={titleId} role="heading" aria-level={1} className={HEADING_CLASS}>
|
||||
{title}
|
||||
</div>
|
||||
) : (
|
||||
<h1 id={titleId} className="text-2xl font-bold tracking-tight">
|
||||
<h1 id={titleId} className={HEADING_CLASS}>
|
||||
{title}
|
||||
</h1>
|
||||
);
|
||||
|
||||
// Title, description and actions share one grid. On a phone the
|
||||
// description spans the full row under the title + actions: a header with
|
||||
// three or four icon actions otherwise left the sentence a ~130 px column
|
||||
// that wrapped five or six times. From `sm` up the actions span both rows,
|
||||
// so the description sits beside them exactly as before.
|
||||
return (
|
||||
<div className={cn("space-y-1.5", className)}>
|
||||
{backLink ? <BackLink {...backLink} /> : null}
|
||||
{topSlot}
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0 space-y-1.5">
|
||||
{heading}
|
||||
{description ? (
|
||||
<p className="text-muted-foreground text-sm">{description}</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
data-slot="page-header-row"
|
||||
className={cn(
|
||||
"grid items-start gap-x-3 gap-y-1.5",
|
||||
actions ? "grid-cols-[minmax(0,1fr)_auto]" : "grid-cols-1",
|
||||
actions && stackActionsOnPhone && "max-sm:grid-cols-1",
|
||||
)}
|
||||
>
|
||||
<div className="min-w-0">{heading}</div>
|
||||
{actions ? (
|
||||
<div className="flex shrink-0 items-center gap-2">{actions}</div>
|
||||
<div
|
||||
className={cn(
|
||||
"col-start-2 row-start-1 flex shrink-0 items-center gap-2",
|
||||
description && "sm:row-span-2",
|
||||
stackActionsOnPhone &&
|
||||
"max-sm:col-span-full max-sm:col-start-1 max-sm:row-start-3 max-sm:flex-wrap",
|
||||
)}
|
||||
>
|
||||
{actions}
|
||||
</div>
|
||||
) : null}
|
||||
{description ? (
|
||||
<p
|
||||
className={cn(
|
||||
"text-muted-foreground text-sm",
|
||||
actions && "col-span-full sm:col-span-1",
|
||||
)}
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user