2.3 KB107 lines
Blame
1"use client";
2
3import { createContext, useContext } from "react";
4import Link from "next/link";
5
6interface TabsContextValue {
7 value: string;
8 onValueChange?: (value: string) => void;
9}
10
11const TabsContext = createContext<TabsContextValue>({ value: "" });
12
13export interface TabsProps {
14 value: string;
15 onValueChange?: (value: string) => void;
16 children: React.ReactNode;
17 className?: string;
18}
19
20export function Tabs({ value, onValueChange, children, className = "" }: TabsProps) {
21 return (
22 <TabsContext.Provider value={{ value, onValueChange }}>
23 <div className={className}>{children}</div>
24 </TabsContext.Provider>
25 );
26}
27
28export interface TabListProps {
29 children: React.ReactNode;
30 className?: string;
31}
32
33export function TabList({ children, className = "" }: TabListProps) {
34 return (
35 <div
36 className={`flex gap-0 text-sm mb-6 ${className}`}
37 style={{ borderBottom: "1px solid var(--border)" }}
38 role="tablist"
39 >
40 {children}
41 </div>
42 );
43}
44
45export interface TabProps {
46 value: string;
47 children: React.ReactNode;
48 href?: string;
49}
50
51export function Tab({ value, children, href }: TabProps) {
52 const ctx = useContext(TabsContext);
53 const isActive = ctx.value === value;
54
55 const tabStyle: React.CSSProperties = {
56 color: isActive ? "var(--text-primary)" : "var(--text-muted)",
57 borderBottom: isActive
58 ? "2px solid var(--accent)"
59 : "2px solid transparent",
60 };
61
62 if (href) {
63 return (
64 <Link
65 href={href}
66 className="px-3 py-2 -mb-px transition-colors"
67 style={tabStyle}
68 role="tab"
69 aria-selected={isActive}
70 >
71 {children}
72 </Link>
73 );
74 }
75
76 return (
77 <button
78 onClick={() => ctx.onValueChange?.(value)}
79 className="px-3 py-2 -mb-px transition-colors"
80 style={{
81 ...tabStyle,
82 background: "none",
83 border: "none",
84 borderBottom: tabStyle.borderBottom,
85 cursor: "pointer",
86 font: "inherit",
87 fontSize: "inherit",
88 }}
89 role="tab"
90 aria-selected={isActive}
91 >
92 {children}
93 </button>
94 );
95}
96
97export interface TabPanelProps {
98 value: string;
99 children: React.ReactNode;
100}
101
102export function TabPanel({ value, children }: TabPanelProps) {
103 const ctx = useContext(TabsContext);
104 if (ctx.value !== value) return null;
105 return <div role="tabpanel">{children}</div>;
106}
107