Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | 57x | import { Menu } from "lucide-react";
import type { ReactNode, RefObject } from "react";
import { cn } from "../../lib/utils";
import { LogoIcon } from "../ui/logo-icon";
import { ReleaseBadge } from "../ui/release-badge";
/**
* The 48px top bar (plan §1.2 / `.context/mockups/shell.html`):
*
* ▣ INTERIORING v2.31 ⌕ ⚲ 3 ◑ SR ▾
*
* A hairline under it. No shadow, no second row — the shell's whole vertical
* budget above the content is three rem.
*
* The mockup's global search is NOT here: there is no cross-entity search in
* the pro portal to wire it to (the only search surface is the admin page at
* `/admin/search`). A box that returns nothing is worse than no box, so the
* slot is left to the `children` the layouts actually have.
*/
export type TopBarProps = {
/** Opens the rail drawer. Omit on surfaces with no drawer. */
onOpenNav?: () => void;
navOpen?: boolean;
menuButtonRef?: RefObject<HTMLButtonElement | null>;
/**
* Where the hamburger shows. Defaults to "below the static rail". The pro
* shell narrows it to tablets, because phones navigate from the bottom.
*/
menuClassName?: string;
/** A `.label` beside the brand: "Admin". */
label?: string;
/** The right-hand controls: bell, theme, whatever the surface has. */
children?: ReactNode;
};
export function TopBar({
onOpenNav,
navOpen,
menuButtonRef,
menuClassName = "lg:hidden",
label,
children,
}: TopBarProps) {
return (
<div className="flex h-12 items-center gap-2 border-b border-border-default bg-background-elevated px-3 sm:px-4">
{onOpenNav && (
<button
ref={menuButtonRef}
type="button"
onClick={onOpenNav}
aria-label="Open navigation"
aria-expanded={navOpen ?? false}
className={cn(
"-ml-1 flex size-8 items-center justify-center text-foreground-muted transition-colors hover:text-foreground-default pointer-coarse:size-11",
menuClassName,
)}
>
<Menu className="size-5" aria-hidden="true" />
</button>
)}
<span className="flex min-w-0 items-center gap-1.5">
<LogoIcon size={24} className="flex-none text-foreground-default" />
<span className="truncate text-sm font-semibold text-foreground-default">
Interioring
</span>
{label && (
<span className="text-xs font-semibold uppercase tracking-wide text-foreground-muted">
{label}
</span>
)}
<ReleaseBadge />
</span>
<span className="ml-auto flex items-center gap-1">{children}</span>
</div>
);
}
|