feat: separate code snippets and code blocks for copyable snippets

This commit is contained in:
Aarnav Tale
2026-03-26 12:57:30 -04:00
parent a1eb6d35db
commit 30bbcbab2a
5 changed files with 48 additions and 83 deletions
+34
View File
@@ -0,0 +1,34 @@
import { Copy } from "lucide-react";
import cn from "~/utils/cn";
import toast from "~/utils/toast";
export interface CodeBlockProps {
children: string;
className?: string;
}
export default function CodeBlock({ children, className }: CodeBlockProps) {
const text = children.trim();
return (
<button
type="button"
className={cn(
"w-full cursor-pointer rounded-md bg-mist-100 text-left dark:bg-mist-800",
"hover:bg-mist-200 dark:hover:bg-mist-700 transition-colors",
className,
)}
onClick={async () => {
await navigator.clipboard.writeText(text);
toast("Copied to clipboard");
}}
>
<code className="block px-3 pt-2 pb-1 text-sm break-all">{text}</code>
<span className="mt-0.5 flex items-center gap-1 px-3 pb-2 text-xs text-mist-500 dark:text-mist-400">
<Copy className="size-3" />
Click to copy
</span>
</button>
);
}
+4 -36
View File
@@ -1,51 +1,19 @@
import { Check, Copy } from "lucide-react";
import { HTMLProps } from "react";
import cn from "~/utils/cn";
import toast from "~/utils/toast";
export interface CodeProps extends HTMLProps<HTMLSpanElement> {
isCopyable?: boolean;
export interface CodeProps {
children: string | string[] | number;
className?: string;
}
export default function Code({ isCopyable, children, className }: CodeProps) {
export default function Code({ children, className }: CodeProps) {
return (
<code
className={cn(
"bg-mist-100 dark:bg-mist-800 px-1 py-0.5 font-mono",
"rounded-sm focus-within:outline-hidden focus-within:ring-2",
isCopyable && "relative pr-7",
"bg-mist-100 dark:bg-mist-800 px-1.5 py-0.5 font-mono rounded-sm text-[0.875em]",
className,
)}
>
{children}
{isCopyable && (
<button
className="absolute right-0 bottom-0"
onClick={async (event) => {
const text = Array.isArray(children) ? children.join("") : String(children);
const svgs = event.currentTarget.querySelectorAll("svg");
for (const svg of svgs) {
svg.toggleAttribute("data-copied", true);
}
await navigator.clipboard.writeText(text);
toast("Copied to clipboard");
setTimeout(() => {
for (const svg of svgs) {
svg.toggleAttribute("data-copied", false);
}
}, 1000);
}}
type="button"
>
<Check className="hidden h-4.5 w-4.5 p-1 data-copied:block" />
<Copy className="block h-4.5 w-4.5 p-1 data-copied:hidden" />
</button>
)}
</code>
);
}