109 lines
3.5 KiB
TypeScript
109 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import { ChevronLeft, ChevronRight, Trophy } from "lucide-react";
|
|
import Link from "next/link";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { useSidebar } from "@/components/ui/sidebar";
|
|
import { useI18n } from "@/core/i18n/hooks";
|
|
import { env } from "@/env";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
function BrandMark({ className }: { className?: string }) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"bg-primary text-primary-foreground flex size-9 shrink-0 items-center justify-center rounded-lg text-sm font-bold shadow-sm",
|
|
className,
|
|
)}
|
|
aria-hidden
|
|
>
|
|
C
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function SidebarCollapseButton({ className }: { className?: string }) {
|
|
const { open, toggleSidebar } = useSidebar();
|
|
return (
|
|
<Button
|
|
data-sidebar="trigger"
|
|
data-slot="sidebar-trigger"
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
className={cn(
|
|
"text-muted-foreground hover:text-sidebar-foreground size-8 shrink-0",
|
|
className,
|
|
)}
|
|
onClick={() => toggleSidebar()}
|
|
>
|
|
{open ? (
|
|
<ChevronLeft className="size-4" strokeWidth={2} />
|
|
) : (
|
|
<ChevronRight className="size-4" strokeWidth={2} />
|
|
)}
|
|
<span className="sr-only">Toggle Sidebar</span>
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
export function WorkspaceHeader({ className }: { className?: string }) {
|
|
const { t } = useI18n();
|
|
const { state } = useSidebar();
|
|
|
|
const titleBlock = (
|
|
<div className="min-w-0 flex-1">
|
|
<div className="text-sidebar-foreground truncate text-sm font-semibold leading-tight tracking-tight">
|
|
{t.pages.appName}
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"group/workspace-header border-sidebar-border/80 border-b",
|
|
className,
|
|
)}
|
|
>
|
|
{state === "collapsed" ? (
|
|
<div className="group/workspace-header relative flex min-h-[3.75rem] items-center justify-center py-2">
|
|
<div className="rounded-[1.15rem] bg-white/92 p-1.5 shadow-[0_16px_32px_-24px_rgba(15,23,42,0.3)] ring-1 ring-black/4 transition-opacity group-hover/workspace-header:pointer-events-none group-hover/workspace-header:opacity-0">
|
|
<BrandMark className="size-10 rounded-[0.95rem] text-base" />
|
|
</div>
|
|
<div className="absolute inset-0 flex items-center justify-center opacity-0 transition-opacity group-hover/workspace-header:opacity-100">
|
|
<SidebarCollapseButton className="rounded-2xl bg-white/92 shadow-sm ring-1 ring-black/5" />
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="flex items-center gap-2 px-2.5 py-2">
|
|
<BrandMark />
|
|
{env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" ? (
|
|
<Link href="/page/workspace/chats/new" className="min-w-0 flex-1 outline-none">
|
|
{titleBlock}
|
|
</Link>
|
|
) : (
|
|
titleBlock
|
|
)}
|
|
{/* 用户排行榜入口:紧挨左上角的应用名,新标签打开免登录的公开排行榜页。 */}
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() =>
|
|
window.open("#/public/leaderboard", "_blank", "noopener")
|
|
}
|
|
title="打开用户排行榜公开页"
|
|
className="text-muted-foreground hover:text-sidebar-foreground h-8 shrink-0 gap-1 px-1.5 text-xs font-medium"
|
|
>
|
|
<Trophy className="size-4" />
|
|
<span>排行榜</span>
|
|
</Button>
|
|
<SidebarCollapseButton />
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|