ai-chatbot/components/home/floating-sidebar-shell.tsx
Your Name 9a562db15a
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
fix: align flow board floating sidebar
2026-04-12 01:05:57 +08:00

163 lines
4.0 KiB
TypeScript

"use client";
import type { ReactNode } from "react";
import { Loader2, type LucideIcon } from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
type FloatingSidebarShellProps = {
children: ReactNode;
className?: string;
flowBoardControl?: boolean;
};
export function FloatingSidebarShell({
children,
className,
flowBoardControl = false,
}: FloatingSidebarShellProps) {
return (
<aside
className={cn(
"pointer-events-none flex flex-col items-center gap-2",
className
)}
data-flow-board-shell-control={flowBoardControl ? "true" : undefined}
>
<TooltipProvider delayDuration={120}>{children}</TooltipProvider>
</aside>
);
}
type FloatingLogoPillProps = {
siteName: string;
siteLogo?: string | null;
tooltip?: string | null;
onClick?: () => void;
variant?: "pill" | "plain";
};
export function FloatingLogoPill({
siteName,
siteLogo,
tooltip = siteName,
onClick,
variant = "pill",
}: FloatingLogoPillProps) {
const isPlain = variant === "plain";
const trigger = (
<button
aria-label={tooltip || siteName}
className={cn(
"pointer-events-auto flex items-center gap-2 text-left transition",
isPlain
? "h-8 hover:opacity-70"
: "h-[52px] w-[52px] justify-center rounded-full border border-black/10 bg-white px-2.5 shadow hover:bg-black/[0.03] hover:shadow-md lg:w-[164px] lg:justify-start lg:px-4"
)}
onClick={onClick}
type="button"
>
{siteLogo ? (
<img
alt={siteName}
className="h-8 w-8 shrink-0 rounded-full object-cover"
src={siteLogo}
/>
) : (
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-black font-bold text-white text-sm">
{siteName.slice(0, 1).toUpperCase()}
</div>
)}
<span
className={cn(
"min-w-0 truncate font-semibold text-[#111827] leading-none tracking-tight",
isPlain ? "text-[20px]" : "hidden text-[15px] lg:block"
)}
>
{siteName}
</span>
</button>
);
if (!tooltip) {
return trigger;
}
return (
<Tooltip>
<TooltipTrigger asChild>{trigger}</TooltipTrigger>
<TooltipContent side={isPlain ? "bottom" : "right"}>
{tooltip}
</TooltipContent>
</Tooltip>
);
}
type FloatingSidebarRailProps = {
children: ReactNode;
className?: string;
};
export function FloatingSidebarRail({
children,
className,
}: FloatingSidebarRailProps) {
return (
<div
className={cn(
"pointer-events-auto flex w-[56px] flex-col items-center gap-1 overflow-y-auto rounded-[28px] border border-black/10 bg-white px-1.5 py-2.5 shadow",
className
)}
>
{children}
</div>
);
}
type FloatingSidebarActionProps = {
label: string;
icon: LucideIcon;
active?: boolean;
disabled?: boolean;
loading?: boolean;
onClick?: () => void;
};
export function FloatingSidebarAction({
label,
icon: Icon,
active = false,
disabled = false,
loading = false,
onClick,
}: FloatingSidebarActionProps) {
return (
<Tooltip>
<TooltipTrigger asChild>
<button
className={cn(
"flex h-10 w-10 items-center justify-center rounded-full text-[#4f566b] transition-colors hover:bg-black/5 hover:text-[#111827] disabled:cursor-not-allowed disabled:opacity-45",
active &&
"bg-[#111827] text-white shadow-sm hover:bg-[#1f2937] hover:text-white"
)}
disabled={disabled || loading}
onClick={onClick}
type="button"
>
{loading ? (
<Loader2 className="h-[18px] w-[18px] animate-spin" />
) : (
<Icon className="h-[18px] w-[18px]" />
)}
</button>
</TooltipTrigger>
<TooltipContent side="right">{label}</TooltipContent>
</Tooltip>
);
}