import { useId, type ReactNode } from "react"; import { LogoTile } from "./logo-tile"; import { cn } from "../lib/cn"; /* ---------------------------------------------------------------------------- EmptyState — a designed empty, not an apology (doctrine D5). Every zero-data surface renders this: an optional icon, a short factual title (fragment, no period), ONE orienting sentence (what this area is for or what will appear here), and the primary action to change that — inside the empty state, not off in a distant header. Variants (design brief 7.14). Omitting `variant` keeps the original boxed look, so existing call sites don't change: - "page" (A) centred: 40px icon tile, title, one sentence, one action, 64px from the top. Hide the page header's action while it shows. Pass `templates` for C: 2-3 starter cards that prefill a form. - "inline" (B) one muted sentence and a link, for sections and no results: 'No matches for "aws".' + Clear search. -------------------------------------------------------------------------- */ export type EmptyStateVariant = "boxed" | "page" | "inline"; export function EmptyState({ icon, title, description, action, variant = "boxed", tone = "default", templates, className, }: { icon?: ReactNode | undefined; title: ReactNode; description?: ReactNode | undefined; action?: ReactNode | undefined; /** "boxed" (the original), "page" (A) or "inline" (B). */ variant?: EmptyStateVariant | undefined; /** "danger" colors the icon tile, for load errors. Page variant only. */ tone?: "default" | "danger" | undefined; /** Starter cards under the action (C), usually `EmptyStateTemplates`. Page variant only. */ templates?: ReactNode | undefined; className?: string | undefined; }) { if (variant === "page") { return (
{icon ? ( ) : null}

{title}

{description ? (

{description}

) : null} {action ? (
{action}
) : null} {templates ?
{templates}
: null}
); } if (variant === "inline") { return (

{title} {description ? <> {description} : null}

{action ?
{action}
: null}
); } return (
{icon ? ( {icon} ) : null}

{title}

{description ? (

{description}

) : null} {action ?
{action}
: null}
); } /** The quiet text action for inline empty states: "Clear search", "Add variable". */ export function EmptyStateLink({ children, onClick, className, }: { children: ReactNode; onClick?: (() => void) | undefined; className?: string | undefined; }) { return ( ); } /** Starter cards for the page empty state (C). */ export function EmptyStateTemplates({ label = "Or start from a template", children, className, }: { label?: ReactNode | undefined; children: ReactNode; className?: string | undefined; }) { const labelId = useId(); return (

{label}

); } /** One starter card. The whole card prefills the create form. */ export function EmptyStateTemplate({ icon, title, description, meta, onSelect, }: { icon?: ReactNode | undefined; title: ReactNode; description?: ReactNode | undefined; /** One quiet fact, for example the cadence: "Every weekday at 08:00". */ meta?: ReactNode | undefined; onSelect?: (() => void) | undefined; }) { const titleId = useId(); const descriptionId = useId(); return (
  • ); }