101 lines
3.8 KiB
TypeScript
101 lines
3.8 KiB
TypeScript
import * as React from "react";
|
|
import { cn } from "~/lib/utils";
|
|
import { Button } from "./button";
|
|
import { Tabs as TabsComponent, TabsList, TabsTrigger } from "./tabs";
|
|
import { ButtonGroup as SubframeButtonGroup } from "~/ui/components/ButtonGroup";
|
|
import { Fragment, useState } from "react";
|
|
import { Link } from "react-router";
|
|
import HintTooltip from "./tooltips/HintTooltip";
|
|
|
|
interface ButtonGroupProps {
|
|
items: {
|
|
name: string;
|
|
href?: string;
|
|
target?: string;
|
|
component?: React.ReactNode;
|
|
hidden?: boolean;
|
|
onClick?: () => void;
|
|
active?: boolean;
|
|
disabled?: boolean;
|
|
}[];
|
|
variant?: "default" | "tabs";
|
|
size?: "xs" | "sm" | "default" | "lg";
|
|
className?: string;
|
|
}
|
|
export function ButtonGroup({ items, variant = "tabs", size = "sm", className }: ButtonGroupProps) {
|
|
let activeItems = items.filter((item) => !item.hidden);
|
|
const [value, setValue] = useState(activeItems.find((item) => item.active)?.name || (activeItems.length > 0 ? activeItems[0].name : ""));
|
|
if (variant === "tabs") {
|
|
return (
|
|
<TabsComponent value={value} onValueChange={(v) => setValue(v)} className={cn(className)}>
|
|
<TabsList className="w-full">
|
|
{activeItems.map((item) =>
|
|
item.href ? (
|
|
<TabsTrigger
|
|
key={item.name}
|
|
value={item.name}
|
|
onClick={item.onClick}
|
|
className={cn(item.active && "bg-secondary text-secondary-foreground")}
|
|
asChild
|
|
>
|
|
<Link to={item.href} target={item.target}>
|
|
{item.component || item.name}
|
|
</Link>
|
|
</TabsTrigger>
|
|
) : (
|
|
<TabsTrigger key={item.name} value={item.name} onClick={item.onClick} className={cn(item.active && "bg-secondary text-secondary-foreground")}>
|
|
{item.component || item.name}
|
|
</TabsTrigger>
|
|
)
|
|
)}
|
|
</TabsList>
|
|
</TabsComponent>
|
|
);
|
|
}
|
|
return (
|
|
// Subframe ButtonGroup `variant="split"` (gap-0) is the design-system container for the
|
|
// connected-button row. The original `isolate inline-flex rounded-md` look + width sizing
|
|
// are restored via className (neutralizing Subframe's `w-full`); per-button border/radius
|
|
// math stays on the buttons, unchanged.
|
|
<SubframeButtonGroup variant="split" align="start" className={cn("isolate inline-flex w-fit rounded-md", className)}>
|
|
{activeItems.map((item, idx) => (
|
|
<Fragment key={item.name}>
|
|
<Button
|
|
size={size}
|
|
variant="outline"
|
|
type="button"
|
|
onClick={(e) => {
|
|
// e.preventDefault();
|
|
e.stopPropagation();
|
|
item.onClick?.();
|
|
}}
|
|
className={cn(
|
|
item.active && "bg-secondary text-secondary-foreground",
|
|
idx > 0 && idx < activeItems.length - 1 && "rounded-none border-x-0",
|
|
idx === 0 && "rounded-l-md rounded-r-none border-r-0",
|
|
idx === activeItems.length - 1 && "rounded-l-none rounded-r-md border-l-0",
|
|
idx < activeItems.length - 1 && "border-r"
|
|
)}
|
|
asChild={item.href ? true : false}
|
|
disabled={item.disabled}
|
|
>
|
|
{item.href && !item.disabled ? (
|
|
<Link to={item.href} target={item.target}>
|
|
{item.component ? (
|
|
<HintTooltip text={<div className="overflow-hidden">{item.name}</div>}>
|
|
<span>{item.component || item.name}</span>
|
|
</HintTooltip>
|
|
) : (
|
|
<div>{item.name}</div>
|
|
)}
|
|
</Link>
|
|
) : (
|
|
<span>{item.component || item.name}</span>
|
|
)}
|
|
</Button>
|
|
</Fragment>
|
|
))}
|
|
</SubframeButtonGroup>
|
|
);
|
|
}
|