mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-23 19:05:16 +02:00
132 lines
3.7 KiB
TypeScript
132 lines
3.7 KiB
TypeScript
"use client";
|
|
|
|
import type { LucideIcon } from "lucide-react";
|
|
import type React from "react";
|
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface SidebarButtonProps {
|
|
icon: LucideIcon;
|
|
label: string;
|
|
onClick?: () => void;
|
|
isCollapsed?: boolean;
|
|
isActive?: boolean;
|
|
badge?: React.ReactNode;
|
|
/** Overlay in the top-right corner of the collapsed icon (e.g. status badge) */
|
|
collapsedOverlay?: React.ReactNode;
|
|
/** Custom icon node for collapsed mode — overrides the default <Icon> rendering */
|
|
collapsedIconNode?: React.ReactNode;
|
|
/** Custom icon node for expanded mode — overrides the default <Icon> rendering */
|
|
expandedIconNode?: React.ReactNode;
|
|
/** Optional inline trailing content shown in expanded mode */
|
|
trailingContent?: React.ReactNode;
|
|
/** Optional tooltip content that replaces the default label tooltip */
|
|
tooltipContent?: React.ReactNode;
|
|
className?: string;
|
|
/** Extra attributes spread onto the inner <button> (e.g. data-joyride) */
|
|
buttonProps?: React.ButtonHTMLAttributes<HTMLButtonElement>;
|
|
}
|
|
|
|
const baseClassName = cn(
|
|
"group/sidebar-button relative flex h-9 items-center rounded-md mx-2 px-2 text-sm text-left",
|
|
"transition-colors hover:bg-accent hover:text-accent-foreground",
|
|
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
);
|
|
|
|
export function SidebarButton({
|
|
icon: Icon,
|
|
label,
|
|
onClick,
|
|
isCollapsed = false,
|
|
isActive = false,
|
|
badge,
|
|
collapsedOverlay,
|
|
collapsedIconNode,
|
|
expandedIconNode,
|
|
trailingContent,
|
|
tooltipContent,
|
|
className,
|
|
buttonProps,
|
|
}: SidebarButtonProps) {
|
|
const activeClassName = "bg-accent text-accent-foreground";
|
|
|
|
const iconNode = isCollapsed
|
|
? (collapsedIconNode ?? <Icon className="h-3.5 w-3.5" />)
|
|
: (expandedIconNode ?? <Icon className="h-3.5 w-3.5 shrink-0" />);
|
|
|
|
const button = (
|
|
<button
|
|
type="button"
|
|
onClick={onClick}
|
|
aria-label={isCollapsed ? label : undefined}
|
|
className={cn(baseClassName, isActive && activeClassName, className)}
|
|
{...buttonProps}
|
|
>
|
|
<span
|
|
className={cn(
|
|
"flex min-w-0 items-center translate-x-0.5 transition-transform duration-200 ease-out",
|
|
isCollapsed ? "shrink-0" : "flex-1"
|
|
)}
|
|
>
|
|
<span className="flex h-3.5 w-3.5 shrink-0 items-center justify-center">
|
|
{iconNode}
|
|
</span>
|
|
|
|
<span
|
|
className={cn(
|
|
"min-w-0 overflow-hidden whitespace-nowrap text-left",
|
|
"transition-[max-width,opacity,margin-left] duration-200 ease-out",
|
|
isCollapsed
|
|
? "max-w-0 opacity-0 ml-0"
|
|
: "max-w-[260px] flex-1 opacity-100 ml-2"
|
|
)}
|
|
>
|
|
<span className="block truncate">{label}</span>
|
|
</span>
|
|
</span>
|
|
|
|
{!isCollapsed && trailingContent}
|
|
{!isCollapsed && badge && typeof badge !== "string" ? badge : null}
|
|
{!isCollapsed && badge && typeof badge === "string" ? (
|
|
<span className="ml-1 inline-flex items-center justify-center min-w-4 h-4 px-1 rounded-full bg-red-500 text-white text-[10px] font-medium">
|
|
{badge}
|
|
</span>
|
|
) : null}
|
|
|
|
{collapsedOverlay && (
|
|
<span
|
|
aria-hidden={!isCollapsed}
|
|
className={cn(
|
|
"pointer-events-none absolute inset-0 transition-opacity duration-150",
|
|
isCollapsed ? "opacity-100" : "opacity-0"
|
|
)}
|
|
>
|
|
{collapsedOverlay}
|
|
</span>
|
|
)}
|
|
|
|
<span className="sr-only">{label}</span>
|
|
</button>
|
|
);
|
|
|
|
const renderTooltip = isCollapsed || !!tooltipContent;
|
|
if (!renderTooltip) {
|
|
return button;
|
|
}
|
|
|
|
return (
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>{button}</TooltipTrigger>
|
|
<TooltipContent side="right" className="max-w-xs">
|
|
{isCollapsed
|
|
? (tooltipContent ?? (
|
|
<>
|
|
{label}
|
|
{typeof badge === "string" && ` (${badge})`}
|
|
</>
|
|
))
|
|
: tooltipContent}
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
);
|
|
}
|