45 lines
1.5 KiB
TypeScript
45 lines
1.5 KiB
TypeScript
"use client";
|
|
import * as React from "react";
|
|
import { twMerge } from "tailwind-merge";
|
|
|
|
type TabsContextValue = {
|
|
value: string;
|
|
setValue: (v: string) => void;
|
|
};
|
|
|
|
const TabsCtx = React.createContext<TabsContextValue | null>(null);
|
|
|
|
export function Tabs({ defaultValue, className, children }: { defaultValue: string; className?: string; children: React.ReactNode }) {
|
|
const [value, setValue] = React.useState(defaultValue);
|
|
return (
|
|
<div className={twMerge("w-full", className)}>
|
|
<TabsCtx.Provider value={{ value, setValue }}>{children}</TabsCtx.Provider>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function TabsList({ className, children }: { className?: string; children: React.ReactNode }) {
|
|
return <div className={twMerge("mb-3 inline-flex items-center gap-2 rounded-2xl border bg-slate-100 p-1", className)}>{children}</div>;
|
|
}
|
|
|
|
export function TabsTrigger({ value, children }: { value: string; children: React.ReactNode }) {
|
|
const ctx = React.useContext(TabsCtx)!;
|
|
const active = ctx.value === value;
|
|
return (
|
|
<button
|
|
onClick={() => ctx.setValue(value)}
|
|
className={twMerge(
|
|
"px-4 py-2 rounded-xl text-sm",
|
|
active ? "bg-white shadow border" : "text-slate-600 hover:text-slate-900"
|
|
)}
|
|
>
|
|
{children}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
export function TabsContent({ value, children }: { value: string; children: React.ReactNode }) {
|
|
const ctx = React.useContext(TabsCtx)!;
|
|
if (ctx.value !== value) return null;
|
|
return <div className="mt-2">{children}</div>;
|
|
} |