55 lines
1.4 KiB
TypeScript
55 lines
1.4 KiB
TypeScript
import type { ReactNode } from "react";
|
|
import { cx } from "./cx";
|
|
|
|
export interface TabItem<T extends string> {
|
|
id: T;
|
|
label: ReactNode;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
interface TabsProps<T extends string> {
|
|
items: ReadonlyArray<TabItem<T>>;
|
|
value: T;
|
|
onChange: (value: T) => void;
|
|
className?: string;
|
|
listClassName?: string;
|
|
triggerClassName?: string;
|
|
}
|
|
|
|
export default function Tabs<T extends string>({
|
|
items,
|
|
value,
|
|
onChange,
|
|
className,
|
|
listClassName,
|
|
triggerClassName,
|
|
}: TabsProps<T>) {
|
|
return (
|
|
<div className={cx("border-b border-[color:var(--app-divider)]", className)}>
|
|
<div className={cx("flex", listClassName)}>
|
|
{items.map((item) => {
|
|
const active = item.id === value;
|
|
return (
|
|
<button
|
|
key={item.id}
|
|
type="button"
|
|
aria-pressed={active}
|
|
disabled={item.disabled}
|
|
onClick={() => onChange(item.id)}
|
|
className={cx(
|
|
"flex-1 px-4 py-2.5 text-xs font-medium transition-colors",
|
|
active
|
|
? "border-b-2 border-[color:var(--app-accent)] app-text-primary"
|
|
: "app-text-soft hover:app-text-primary",
|
|
triggerClassName,
|
|
)}
|
|
>
|
|
{item.label}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|