Skip to content

Commit

Permalink
feat: copy button for code blocks in docs (#103)
Browse files Browse the repository at this point in the history
closes #89
  • Loading branch information
skylarmb authored Aug 6, 2024
1 parent 701928f commit be8bdde
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 71 deletions.
38 changes: 38 additions & 0 deletions website/components/CodeBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Code } from "bright";
import { IBM_Plex_Mono } from "next/font/google";
import { ComponentProps, ReactElement } from "react";

import CodeCopyButton from "@/components/CodeCopyButton";

const fontMono = IBM_Plex_Mono({
subsets: ["latin"],
display: "swap",
variable: "--font-mono",
weight: "400",
});

interface Props extends ComponentProps<typeof Code> {
children: ReactElement;
}

const CodeBlock = (props: Props) => {
return (
<div style={{ position: "relative" }}>
<CodeCopyButton text={props.children.props.children} />
<Code
{...props}
style={{
maxWidth: "calc(100vw - 32px)",
margin: 0,
borderRadius: "8px",
padding: 10, // 24px - 1em = 10px
backgroundColor: "var(--gray-bg)",
}}
theme={"github-dark"}
className={fontMono.className}
/>
</div>
);
};

export default CodeBlock;
88 changes: 88 additions & 0 deletions website/components/CodeCopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use client";

import { useEffect, useState } from "react";
import { BiCheckDouble, BiCopy } from "react-icons/bi";
import styled from "styled-components";

interface Props {
text: string;
}

const CodeCopyButton = ({ text }: Props) => {
const [isCopied, setIsCopied] = useState(false);
useEffect(() => {
if (isCopied) {
const timeout = setTimeout(() => {
setIsCopied(false);
}, 2000);
return () => clearTimeout(timeout);
}
}, [isCopied]);

const handleCopy = () => {
if (navigator.clipboard == null) {
console.error("Clipboard API not available");
return;
}
setIsCopied(true);
navigator.clipboard
.writeText(text.trim())
.then(() => {
console.log("Text copied to clipboard");
})
.catch((err) => {
console.error("Failed to copy text: ", err);
});
};

return (
<S.CodeCopyButton
aria-label="Copy to clipboard"
onClick={handleCopy}
$isCopied={isCopied}
>
{isCopied ? (
<BiCheckDouble
size={24}
style={{ pointerEvents: "none" }}
role="presentation"
/>
) : (
<BiCopy
size={24}
style={{ pointerEvents: "none" }}
role="presentation"
/>
)}
</S.CodeCopyButton>
);
};

namespace S {
export const CodeCopyButton = styled.button<{ $isCopied: boolean }>`
display: flex;
position: absolute;
right: 8px;
top: 8px;
color: white;
background-color: ${(props) =>
props.$isCopied
? "rgba(200, 255, 200, 0.5)"
: "rgba(255, 255, 255, 0.1)"};
border: none;
outline: none;
cursor: pointer;
border-radius: 4px;
padding: 4px;
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: ${(props) =>
props.$isCopied
? "rgba(200, 255, 200, 0.5)"
: "rgba(255, 255, 255, 0.2)"};
}
`;
}

export default CodeCopyButton;
44 changes: 0 additions & 44 deletions website/lib/bright-extensions.tsx

This file was deleted.

30 changes: 3 additions & 27 deletions website/mdx-components.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import { Code } from "bright";
import type { MDXComponents } from "mdx/types";
import { IBM_Plex_Mono } from "next/font/google";
import Image from "next/image";
import Link from "next/link";

import { titleBar } from "./lib/bright-extensions";

const fontMono = IBM_Plex_Mono({
subsets: ["latin"],
display: "swap",
variable: "--font-mono",
weight: "400",
});
import CodeBlock from "@/components/CodeBlock";

export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
Expand Down Expand Up @@ -48,23 +39,8 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
/>
);
},
pre: (props) => {
return (
<Code
{...props}
style={{
maxWidth: "calc(100vw - 32px)",
margin: 0,
borderRadius: "8px",
padding: 10, // 24px - 1em = 10px
backgroundColor: "var(--gray-bg)",
}}
theme={"github-dark"}
className={fontMono.className}
extensions={[titleBar]}
/>
);
},
// @ts-expect-error children will not be undefined
pre: CodeBlock,
Vimeo: ({ id }: { id: string }) => {
return (
<div style={{ padding: "56% 0 0 0 ", position: "relative" }}>
Expand Down

0 comments on commit be8bdde

Please sign in to comment.