115 lines
2.5 KiB
TypeScript
115 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
type TabsContextValue = {
|
|
value: string;
|
|
setValue: (value: string) => void;
|
|
};
|
|
|
|
const TabsContext = React.createContext<TabsContextValue | null>(null);
|
|
|
|
function useTabsContext() {
|
|
const context = React.useContext(TabsContext);
|
|
|
|
if (!context) {
|
|
throw new Error("Tabs components must be used within Tabs.");
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
type TabsProps = {
|
|
defaultValue: string;
|
|
children: React.ReactNode;
|
|
className?: string;
|
|
};
|
|
|
|
function Tabs({ defaultValue, children, className }: TabsProps) {
|
|
const [value, setValue] = React.useState(defaultValue);
|
|
|
|
return (
|
|
<TabsContext.Provider value={{ value, setValue }}>
|
|
<div className={cn("w-full", className)}>{children}</div>
|
|
</TabsContext.Provider>
|
|
);
|
|
}
|
|
|
|
function TabsList({
|
|
className,
|
|
...props
|
|
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"inline-flex h-auto flex-wrap items-center gap-2 rounded-surface border border-border/80 bg-muted/40 p-1.5",
|
|
className,
|
|
)}
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
type TabsTriggerProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
value: string;
|
|
};
|
|
|
|
function TabsTrigger({
|
|
className,
|
|
value,
|
|
onClick,
|
|
...props
|
|
}: TabsTriggerProps) {
|
|
const { value: activeValue, setValue } = useTabsContext();
|
|
const isActive = activeValue === value;
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
className={cn(
|
|
"inline-flex items-center justify-center rounded-nested px-3 py-2 text-sm font-medium text-muted-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-50",
|
|
isActive && "bg-background text-foreground shadow-sm",
|
|
className,
|
|
)}
|
|
onClick={(event) => {
|
|
setValue(value);
|
|
onClick?.(event);
|
|
}}
|
|
{...props}
|
|
/>
|
|
);
|
|
}
|
|
|
|
type TabsContentProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
value: string;
|
|
};
|
|
|
|
function TabsContent({
|
|
className,
|
|
value,
|
|
children,
|
|
...props
|
|
}: TabsContentProps) {
|
|
const { value: activeValue } = useTabsContext();
|
|
|
|
if (activeValue !== value) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"mt-6 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
className,
|
|
)}
|
|
{...props}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export { Tabs, TabsContent, TabsList, TabsTrigger };
|