126 lines
3.5 KiB
TypeScript
126 lines
3.5 KiB
TypeScript
"use client";
|
||
|
||
import type { ComponentProps, ReactNode } from "react";
|
||
import { Sparkles } from "lucide-react";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
type BrandTopBarProps = {
|
||
actions?: ReactNode;
|
||
children?: ReactNode;
|
||
className?: string;
|
||
contentClassName?: string;
|
||
logo?: string | null;
|
||
offsetTop?: number;
|
||
onLogoClick?: () => void;
|
||
position?: "fixed" | "sticky" | "static";
|
||
showBrand?: boolean;
|
||
siteName: string;
|
||
};
|
||
|
||
export function BrandTopBar({
|
||
actions,
|
||
children,
|
||
className,
|
||
contentClassName,
|
||
logo,
|
||
offsetTop = 0,
|
||
onLogoClick,
|
||
position = "fixed",
|
||
showBrand = true,
|
||
siteName,
|
||
}: BrandTopBarProps) {
|
||
const resolvedSiteName = String(siteName || "").trim() || "𝗕𝘂𝗬𝗶";
|
||
const positionClass =
|
||
position === "fixed"
|
||
? "fixed left-0 top-0 z-40 w-full"
|
||
: position === "sticky"
|
||
? "sticky top-0 z-40"
|
||
: "relative z-40";
|
||
|
||
return (
|
||
<header
|
||
className={cn(
|
||
positionClass,
|
||
"h-14 bg-white/90 backdrop-blur dark:bg-[#0a0a0c]/90",
|
||
className
|
||
)}
|
||
style={position === "fixed" ? { top: offsetTop } : undefined}
|
||
>
|
||
<div
|
||
className={cn(
|
||
"flex h-full items-center px-5",
|
||
showBrand ? "justify-between" : "justify-end",
|
||
contentClassName
|
||
)}
|
||
>
|
||
{showBrand ? (
|
||
<button
|
||
className="flex items-center gap-2 transition-opacity hover:opacity-80"
|
||
onClick={onLogoClick}
|
||
type="button"
|
||
>
|
||
{logo ? (
|
||
<img
|
||
alt={resolvedSiteName}
|
||
className="h-7 w-7 rounded-full object-cover"
|
||
src={logo}
|
||
/>
|
||
) : (
|
||
<div className="flex h-7 w-7 items-center justify-center rounded-full bg-black text-white dark:bg-white dark:text-black">
|
||
<Sparkles className="h-3.5 w-3.5" />
|
||
</div>
|
||
)}
|
||
<span className="text-[17px] font-semibold leading-none dark:text-zinc-100">
|
||
{resolvedSiteName}
|
||
</span>
|
||
</button>
|
||
) : null}
|
||
|
||
<div className="flex items-center gap-2">{actions || children}</div>
|
||
</div>
|
||
</header>
|
||
);
|
||
}
|
||
|
||
type BrandTopBarButtonProps = ComponentProps<"button"> & {
|
||
variant?: "default" | "primary";
|
||
};
|
||
|
||
export function BrandTopBarButton({
|
||
className,
|
||
type = "button",
|
||
variant = "default",
|
||
...props
|
||
}: BrandTopBarButtonProps) {
|
||
return (
|
||
<button
|
||
className={cn(
|
||
"inline-flex h-8 items-center justify-center gap-1.5 rounded-[14px] px-3 text-[12px] font-medium shadow-sm transition disabled:cursor-not-allowed disabled:opacity-45",
|
||
variant === "primary"
|
||
? "bg-[#1c1c1e] text-white hover:bg-black"
|
||
: "border border-black/10 bg-white text-black/70 hover:bg-black/5 hover:text-black dark:border-white/10 dark:bg-white/6 dark:text-white/72 dark:hover:bg-white/10 dark:hover:text-white",
|
||
className
|
||
)}
|
||
type={type}
|
||
{...props}
|
||
/>
|
||
);
|
||
}
|
||
|
||
export function BrandTopBarIconButton({
|
||
className,
|
||
type = "button",
|
||
...props
|
||
}: ComponentProps<"button">) {
|
||
return (
|
||
<button
|
||
className={cn(
|
||
"flex h-8 w-8 items-center justify-center rounded-full border border-black/10 bg-white text-black/65 shadow-sm transition hover:bg-black/5 hover:text-black dark:border-white/10 dark:bg-white/6 dark:text-white/72 dark:hover:bg-white/10 dark:hover:text-white",
|
||
className
|
||
)}
|
||
type={type}
|
||
{...props}
|
||
/>
|
||
);
|
||
}
|