1
0
forked from baron/baron-sso
Files
baron-sso/common/core/components/page/PageHeader.tsx

69 lines
1.8 KiB
TypeScript

import type { ElementType, HTMLAttributes, ReactNode } from "react";
function cx(...classNames: Array<string | false | null | undefined>) {
return classNames.filter(Boolean).join(" ");
}
type PageHeaderProps = Omit<HTMLAttributes<HTMLElement>, "title"> & {
actions?: ReactNode;
icon?: ReactNode;
as?: ElementType;
description?: ReactNode;
eyebrow?: ReactNode;
sticky?: boolean;
title: ReactNode;
titleAs?: ElementType;
};
export function PageHeader({
actions,
as,
className,
description,
eyebrow,
icon,
sticky = false,
title,
titleAs,
...props
}: PageHeaderProps) {
const Root = as ?? "header";
const Title = titleAs ?? "h1";
return (
<Root
className={cx(
"flex flex-wrap items-start justify-between gap-4",
sticky &&
"sticky top-[-2.5rem] z-20 -mt-4 bg-background/95 pt-4 pb-2 backdrop-blur",
className,
)}
{...props}
>
<div className="flex min-w-0 items-start gap-3">
{icon ? (
<div className="mt-1 flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-primary/15 bg-primary/10 text-primary">
{icon}
</div>
) : null}
<div className="space-y-2">
{eyebrow ? (
<p className="text-xs uppercase tracking-[0.2em] text-muted-foreground">
{eyebrow}
</p>
) : null}
<Title className="text-3xl font-semibold tracking-tight">
{title}
</Title>
{description ? (
<p className="text-sm text-muted-foreground">{description}</p>
) : null}
</div>
</div>
{actions ? (
<div className="flex flex-wrap items-center gap-2">{actions}</div>
) : null}
</Root>
);
}