ai-chatbot/components/home/floating-sidebar-shell.tsx

183 lines
5.1 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 dark:border-white/10 dark:bg-[#141416] dark:hover:bg-white/[0.04] 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 dark:bg-white dark:text-black">
{siteName.slice(0, 1).toUpperCase()}
</div>
)}
<span
className={cn(
"min-w-0 truncate font-semibold text-[#111827] leading-none tracking-tight dark:text-white",
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;
orientation?: "vertical" | "horizontal";
tone?: "dark" | "light";
};
export function FloatingSidebarRail({
children,
className,
orientation = "vertical",
tone = "dark",
}: FloatingSidebarRailProps) {
const isHorizontal = orientation === "horizontal";
const isLight = tone === "light";
return (
<div
className={cn(
"pointer-events-auto flex items-center gap-1 backdrop-blur-2xl",
isLight
? "border border-black/8 bg-white/96 text-[#111827] shadow-[0_20px_46px_rgba(15,23,42,0.12)]"
: "border border-white/8 bg-[#17181d]/94 text-white shadow-[0_22px_60px_rgba(6,8,14,0.34),inset_0_1px_0_rgba(255,255,255,0.05)]",
isHorizontal
? "h-[56px] w-auto flex-row overflow-hidden rounded-[22px] px-3 py-2"
: "w-[56px] max-w-[56px] min-w-0 flex-col overflow-x-hidden overflow-y-auto overscroll-contain rounded-[24px] px-2 py-2 [overflow-x:clip] [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
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 shrink-0 items-center justify-center rounded-xl text-[#4f566b] transition-colors hover:bg-black/5 hover:text-[#111827] disabled:cursor-not-allowed disabled:opacity-45 dark:text-white/60 dark:hover:bg-white/8 dark:hover:text-white",
active &&
"bg-[#111827] text-white shadow-sm hover:bg-[#1f2937] hover:text-white"
)}
disabled={disabled || loading}
onMouseDown={(event) => {
event.preventDefault();
event.stopPropagation();
}}
onPointerDown={(event) => {
event.preventDefault();
event.stopPropagation();
}}
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>
);
}