DocDoeAI / src /components /shared /SharedSectionHeader.tsx
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
65d68f5
Raw History Blame Contribute Delete
4.87 kB
"use client";
import { HTMLAttributes } from "react";
import { clsx } from "clsx";
// =============================================================================
// SectionHeader Types
// =============================================================================
export interface SectionHeaderProps extends HTMLAttributes<HTMLDivElement> {
title: string;
subtitle?: string;
actionText?: string;
actionHref?: string;
actionOnClick?: () => void;
icon?: React.ReactNode;
titleSize?: "page" | "section" | "card" | "small";
showDivider?: boolean;
}
export interface PageHeaderProps extends SectionHeaderProps {
backHref?: string;
backText?: string;
actionSlot?: React.ReactNode;
}
// =============================================================================
// SharedSectionHeader Component
// =============================================================================
export function SharedSectionHeader({
title,
subtitle,
actionText,
actionHref,
actionOnClick,
icon,
titleSize = "section",
showDivider = false,
className,
...props
}: SectionHeaderProps) {
// Map title size to styles
const titleStyles = {
page: {
className: "text-[26px] font-bold tracking-[-0.02em] text-[var(--text-primary)]",
},
section: {
className: "text-[24px] font-bold tracking-[-0.02em] text-[var(--text-primary)]",
},
card: {
className: "text-[18px] font-bold text-[var(--text-primary)]",
},
small: {
className: "text-[15px] font-semibold text-[var(--text-primary)]",
},
};
const style = titleStyles[titleSize];
return (
<div className={clsx("space-y-4", className)} {...props}>
<div className="flex items-start justify-between gap-4">
<div className="flex items-center gap-3">
{icon && (
<div className="grid size-8 shrink-0 place-items-center rounded-md bg-[var(--accent-primary)]/10">
{icon}
</div>
)}
<div>
<h2 className={style.className}>{title}</h2>
{subtitle && (
<p className="mt-1 text-[14px] leading-relaxed text-[var(--text-secondary)]">
{subtitle}
</p>
)}
</div>
</div>
{(actionText || actionHref || actionOnClick) && (
<div className="flex items-center">
{actionHref ? (
<a
href={actionHref}
className="text-[13px] font-semibold text-[var(--accent-primary)] hover:underline"
>
{actionText}
</a>
) : actionOnClick ? (
<button
type="button"
onClick={actionOnClick}
className="text-[13px] font-semibold text-[var(--accent-primary)] hover:underline"
>
{actionText}
</button>
) : null}
</div>
)}
</div>
{showDivider ? <div className="h-px w-full bg-[var(--border-subtle)]" aria-hidden="true" /> : null}
</div>
);
}
// =============================================================================
// Section Label (uppercase small label)
// =============================================================================
export function SectionLabel({
children,
className,
accent = false,
...props
}: HTMLAttributes<HTMLParagraphElement> & { accent?: boolean }) {
return (
<p
className={clsx(
"text-[11px] font-bold uppercase tracking-wider",
accent ? "text-[var(--accent-primary)]" : "text-[var(--text-muted)]",
className,
)}
{...props}
>
{children}
</p>
);
}
// =============================================================================
// Page Header (with back link option)
// =============================================================================
export function PageHeader({
title,
subtitle,
backHref,
backText = "Back",
actionSlot,
className,
...props
}: PageHeaderProps) {
return (
<div className={clsx("space-y-2", className)} {...props}>
{backHref && (
<a
href={backHref}
className="inline-flex items-center gap-1.5 text-[12px] text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition"
>
← {backText}
</a>
)}
<div className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
<div>
<h2 className="text-[26px] font-bold tracking-[-0.02em] text-[var(--text-primary)]">{title}</h2>
{subtitle && (
<p className="mt-1 text-[14px] leading-relaxed text-[var(--text-secondary)]">
{subtitle}
</p>
)}
</div>
</div>
{actionSlot && <div className="flex gap-2">{actionSlot}</div>}
</div>
</div>
);
}