Skip to content

Commit

Permalink
add code block component
Browse files Browse the repository at this point in the history
  • Loading branch information
sajadevo committed Jan 31, 2025
1 parent 02aa780 commit 9bddf21
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 5 deletions.
5 changes: 5 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import "tailwindcss";
@plugin "@tailwindcss/typography";

@theme {
/* colors */
Expand Down Expand Up @@ -37,3 +38,7 @@
-ms-overflow-style: none;
scrollbar-width: none;
}

.code-block pre *::selection {
@apply !bg-primary !text-white;
}
82 changes: 82 additions & 0 deletions components/code-block.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"use client";

import React from "react";

// @components
import { Button } from "@/components/button";

// @icons
import { RiCheckLine, RiFileCopyLine } from "@remixicon/react";

// @hooks
import { useTheme } from "next-themes";
import { useCopyToClipboard } from "usehooks-ts";

// @utils
import prettier from "prettier/standalone";
import parserBabel from "prettier/plugins/babel";
import prettierPluginEstree from "prettier/plugins/estree";

export function CodeBlock({
children,
...props
}: React.ComponentPropsWithoutRef<"div">) {
const { resolvedTheme } = useTheme();
const [, copy] = useCopyToClipboard();
const codeRef = React.useRef<any>(null);

const [isClient, setIsClient] = React.useState(false);
const [isCopied, setIsCopied] = React.useState(false);

React.useEffect(() => {
setIsClient(true);
}, []);

if (!isClient) {
return null;
}

async function copyCode() {
setIsCopied(true);

const code = codeRef.current.innerText;
const cleanedCode = code
.split("\n")
.map((line: string) => line.replace(/^\d+\s*/, "")) // Removes leading numbers
.join("\n");

const formattedCode = await prettier.format(cleanedCode, {
parser: "babel",
plugins: [parserBabel, prettierPluginEstree],
});

copy(formattedCode);
}

function resetCopy() {
setIsCopied(false);
}

return (
<div
{...props}
ref={codeRef}
data-theme={resolvedTheme}
className="code-block border-secondary [&_pre_span[data-bright-ln]]:!text-foreground/50 relative rounded-2xl border [&_>_div]:!my-0 [&_pre]:!bg-transparent [&_pre]:!px-2 [&_pre]:!py-4 [&_pre]:font-mono [&_pre]:text-sm [&_pre]:leading-relaxed sm:[&_pre]:text-base [&_pre_span[data-bright-ln]]:!mr-4"
>
<Button
variant="secondary"
onClick={copyCode}
onMouseLeave={resetCopy}
className="absolute top-1 right-1 size-8 border-0 p-1.5 hover:bg-transparent"
>
{isCopied ? (
<RiCheckLine className="size-4" />
) : (
<RiFileCopyLine className="size-4" />
)}
</Button>
{children}
</div>
);
}
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,36 @@
"@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-tooltip": "1.1.6",
"@remixicon/react": "4.6.0",
"@tailwindcss/postcss": "next",
"@tailwindcss/postcss": "4.0.1",
"bright": "1.0.0",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"gray-matter": "4.0.3",
"motion": "12.0.0",
"next": "15.1.0",
"next-mdx-remote": "5.0.0",
"next-themes": "0.4.4",
"postcss": "8.5.1",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-fast-marquee": "1.6.5",
"react-hook-form": "7.54.1",
"remark-gfm": "4.0.0",
"resend": "4.0.1",
"tailwind-merge": "2.5.5",
"usehooks-ts": "3.1.0",
"zod": "3.24.1"
},
"devDependencies": {
"@tailwindcss/typography": "0.5.16",
"@types/node": "22.10.2",
"@types/react": "19",
"@types/react-dom": "19",
"eslint": "9.17.0",
"eslint-config-next": "15.1.0",
"prettier": "3.4.2",
"prettier-plugin-tailwindcss": "0.6.10",
"tailwindcss": "next",
"tailwindcss": "latest",
"typescript": "5"
}
}
4 changes: 1 addition & 3 deletions postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/** @type {import('postcss-load-config').Config} */
const config = {
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;

0 comments on commit 9bddf21

Please sign in to comment.