126 lines
3.4 KiB
TypeScript
126 lines
3.4 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() || "由壹点AI";
|
|
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}
|
|
/>
|
|
);
|
|
}
|