forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcode-showcase.tsx
More file actions
173 lines (164 loc) · 6.2 KB
/
Copy pathcode-showcase.tsx
File metadata and controls
173 lines (164 loc) · 6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
"use client";
import React, { Children, useState, type ReactNode } from "react";
import { cn } from "@/lib/utils";
import { Copy, Check, ChevronRight } from "lucide-react";
interface CodeShowcaseTab {
label: string;
icon: ReactNode;
filename: string;
}
interface CodeShowcaseProps {
tabs: CodeShowcaseTab[];
children: ReactNode;
}
/* Hide the entire fumadocs toolbar (icons + figcaption) and strip default spacing/rounding */
const codeOverrides =
"[&_figure]:!m-0 [&_figure]:!border-0 [&_figure]:!rounded-none [&_figure>div:first-child]:!hidden [&_pre]:!rounded-none [&_pre]:!border-0";
export function CodeShowcase({ tabs, children }: CodeShowcaseProps) {
const [activeIndex, setActiveIndex] = useState(0);
const [copied, setCopied] = useState(false);
const panels = Children.toArray(children);
const handleCopy = () => {
const codeEl = document.querySelector(
`[data-code-panel="${activeIndex}"] pre code`,
);
if (codeEl) {
navigator.clipboard.writeText(codeEl.textContent || "");
setCopied(true);
setTimeout(() => setCopied(false), 2000);
}
};
return (
<div className="not-prose mb-16">
{/* Desktop */}
<div className="hidden md:block rounded-2xl">
<div className="rounded-xl overflow-hidden border border-border/80 shadow-sm bg-card">
{/* Title bar */}
<div className="flex items-center px-4 py-2.5 bg-card border-b border-border/60">
<div className="flex items-center gap-1.5">
<div className="w-2.5 h-2.5 rounded-full bg-[#FF5F57]" />
<div className="w-2.5 h-2.5 rounded-full bg-[#FEBC2E]" />
<div className="w-2.5 h-2.5 rounded-full bg-[#28C840]" />
</div>
<div className="flex-1 text-center text-xs text-muted-foreground font-medium">
{tabs[activeIndex]?.filename}
</div>
<button
onClick={handleCopy}
className="text-muted-foreground/50 hover:text-muted-foreground transition-colors cursor-pointer"
aria-label="Copy code"
>
{copied ? (
<Check className="w-3.5 h-3.5" />
) : (
<Copy className="w-3.5 h-3.5" />
)}
</button>
</div>
{/* Content */}
<div className="flex">
{/* Sidebar */}
<div className="w-48 shrink-0 bg-card border-r border-border/60 py-3">
{tabs.map((tab, i) => (
<button
key={tab.label}
onClick={() => setActiveIndex(i)}
className={cn(
"flex items-center justify-between w-full px-4 py-2 text-sm font-medium transition-colors cursor-pointer",
i === activeIndex
? "text-primary bg-accent/50"
: "text-muted-foreground hover:text-foreground",
)}
>
<div className="flex items-center gap-2.5">
<span className="flex-shrink-0 [&_svg]:h-4 [&_svg]:w-4">
{tab.icon}
</span>
{tab.label}
</div>
{i === activeIndex && (
<ChevronRight className="w-3.5 h-3.5" />
)}
</button>
))}
</div>
{/* Code panel */}
<div className={cn("flex-1 min-w-0", codeOverrides)}>
{panels.map((panel, i) => (
<div
key={i}
data-code-panel={i}
className={i === activeIndex ? "block" : "hidden"}
>
{panel}
</div>
))}
</div>
</div>
</div>
</div>
{/* Mobile */}
<div className="md:hidden rounded-2xl bg-muted/50 p-2">
<div className="rounded-xl overflow-hidden border border-border/80 shadow-sm bg-card">
{/* Title bar */}
<div className="flex items-center px-3 py-2 bg-card border-b border-border/60">
<div className="flex items-center gap-1.5">
<div className="w-2 h-2 rounded-full bg-[#FF5F57]" />
<div className="w-2 h-2 rounded-full bg-[#FEBC2E]" />
<div className="w-2 h-2 rounded-full bg-[#28C840]" />
</div>
<div className="flex-1 text-center text-xs text-muted-foreground font-medium">
{tabs[activeIndex]?.filename}
</div>
<button
onClick={handleCopy}
className="text-muted-foreground/50 hover:text-muted-foreground transition-colors cursor-pointer"
aria-label="Copy code"
>
{copied ? (
<Check className="w-3.5 h-3.5" />
) : (
<Copy className="w-3.5 h-3.5" />
)}
</button>
</div>
{/* Tab bar */}
<div className="flex overflow-x-auto bg-card border-b border-border/60 px-1 py-1">
{tabs.map((tab, i) => (
<button
key={tab.label}
onClick={() => setActiveIndex(i)}
className={cn(
"flex items-center gap-2 px-3 py-1.5 text-xs font-medium whitespace-nowrap transition-colors rounded-md cursor-pointer",
i === activeIndex
? "text-primary bg-accent/50"
: "text-muted-foreground hover:text-foreground",
)}
>
<span className="flex-shrink-0 [&_svg]:h-3.5 [&_svg]:w-3.5">
{tab.icon}
</span>
{tab.label}
</button>
))}
</div>
{/* Code */}
<div className={codeOverrides}>
{panels.map((panel, i) => (
<div
key={i}
data-code-panel={i}
className={i === activeIndex ? "block" : "hidden"}
>
{panel}
</div>
))}
</div>
</div>
</div>
</div>
);
}
export function CodePanel({ children }: { children: ReactNode }) {
return <>{children}</>;
}