Files
loyaly-merchant/src/components/primitives/PageHeader.tsx
2026-08-05 18:34:37 +05:30

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>
);
}