55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
'use client';
|
|
|
|
import {VStack, HStack} from '@astryxdesign/core/Layout';
|
|
import {Heading, Text} from '@astryxdesign/core/Text';
|
|
|
|
/**
|
|
* Every workspace page opens the same way: title, one line of orientation,
|
|
* then the filters and actions that belong to THIS page. Centralising it is
|
|
* what keeps five modules from drifting into five different header treatments.
|
|
*
|
|
* Two trailing slots, deliberately distinct:
|
|
* `controls` — filters that scope the page's data (store, period, compare).
|
|
* Own row beneath the title, because a page can have several
|
|
* and they would otherwise crowd the heading and wrap badly.
|
|
* `actions` — page-level verbs (export, add store). Trailing edge of the
|
|
* title row, where a primary action is conventionally found.
|
|
*
|
|
* `eyebrow` is the small line ABOVE the title — a greeting, a breadcrumb-ish
|
|
* context line. It sits inside the heading block rather than above the whole
|
|
* header so it cannot drift away from the title it belongs to.
|
|
*/
|
|
export function PageHeader({
|
|
eyebrow,
|
|
title,
|
|
description,
|
|
controls,
|
|
actions,
|
|
}: {
|
|
eyebrow?: string;
|
|
title: string;
|
|
description?: string;
|
|
controls?: React.ReactNode;
|
|
actions?: React.ReactNode;
|
|
}) {
|
|
return (
|
|
<HStack hAlign="between" vAlign="center" gap={4} wrap="wrap">
|
|
<VStack gap={1}>
|
|
{eyebrow ? (
|
|
<Text size="sm" color="secondary">
|
|
{eyebrow}
|
|
</Text>
|
|
) : null}
|
|
<Heading level={1}>{title}</Heading>
|
|
{description ? <Text color="secondary">{description}</Text> : null}
|
|
</VStack>
|
|
{controls || actions ? (
|
|
<HStack gap={2} vAlign="center" wrap="wrap">
|
|
{controls}
|
|
{actions}
|
|
</HStack>
|
|
) : null}
|
|
</HStack>
|
|
);
|
|
}
|