Skip to content

Commit

Permalink
refactor(codeblock): ♻️ refactor codeblock and clipboard button (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
navin-moorthy authored Jul 20, 2022
2 parents 2a01916 + d6bc457 commit 3c326e1
Show file tree
Hide file tree
Showing 47 changed files with 1,104 additions and 478 deletions.
25 changes: 25 additions & 0 deletions components/ClipBoardCopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useClipboard } from "@chakra-ui/hooks";

import { CopyButton } from "./CopyButton";
import { Tooltip, TooltipProps } from "./Tooltip";

export type ClipboardCopyButtonProps = TooltipProps & {
content: string;
};

export const ClipboardCopyButton: React.FC<ClipboardCopyButtonProps> = ({
content,
...props
}) => {
const { hasCopied, onCopy } = useClipboard(content);

return (
<Tooltip
withArrow
placement="left"
anchor={<CopyButton hasCopied={hasCopied} onCopy={onCopy} />}
content={hasCopied ? "Copied!" : "Copy"}
{...props}
/>
);
};
162 changes: 0 additions & 162 deletions components/Codeblock.tsx

This file was deleted.

17 changes: 17 additions & 0 deletions components/Codeblock/CopyCodeBlockButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { ClipboardCopyButton } from "../ClipBoardCopyButton";

export type CopyCodeBlockButtonProps = {
code: string;
};

export const CopyCodeBlockButton: React.FC<
CopyCodeBlockButtonProps
> = props => {
const { code } = props;

return (
<span className="absolute right-2 top-2">
<ClipboardCopyButton content={code} />
</span>
);
};
61 changes: 61 additions & 0 deletions components/Codeblock/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from "react";
import { LiveEditor, LiveError, LivePreview, LiveProvider } from "react-live";
import { AdaptUIProvider } from "@adaptui/react-tailwind";
import { useTheme } from "next-themes";
import darkTheme from "prism-react-renderer/themes/vsDark";
import lightTheme from "prism-react-renderer/themes/vsLight";

import { scope } from "../../utils";

import { CopyCodeBlockButton } from "./CopyCodeBlockButton";
import { transformer } from "./transformer";

export type CodeblockProps = {
code: string;
noCopy?: boolean;
noInline?: boolean;
children?: React.ReactNode | any;
};

export const Codeblock = (props: CodeblockProps) => {
const { code, noCopy, noInline, children, ...rest } = props;
const _code = children?.props?.children?.props?.children ?? code;

const [editorCode, setEditorCode] = React.useState(_code.trim());
const handleChange = React.useCallback((code: string) => {
setEditorCode(code.trim());
}, []);

const { theme } = useTheme();

const liveProviderProps = {
theme: theme === "dark" ? darkTheme : lightTheme,
code: editorCode,
scope,
noInline,
...rest,
};

return (
<AdaptUIProvider>
<LiveProvider
transformCode={rawCode => transformer(rawCode, noInline)}
{...liveProviderProps}
>
<div className="mt-6 rounded-md border border-[#ededed] bg-transparent">
<LivePreview className="rounded-t-md bg-white-900 p-6" />
<div className="relative">
<LiveEditor
onChange={handleChange}
className="dark:!bg-prime-300 rounded-b-md !bg-slate-100 !font-mono text-sm leading-6 tracking-tighter dark:!bg-opacity-10"
/>
<CopyCodeBlockButton code={editorCode} />
</div>
</div>
<LiveError className="mt-2 rounded-md bg-red-100 text-xs text-red-800" />
</LiveProvider>
</AdaptUIProvider>
);
};

export * from "./CopyCodeBlockButton";
15 changes: 15 additions & 0 deletions components/Codeblock/transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export const transformer = (rawCode: any, noInline: any) => {
const code = rawCode
// remove imports
.replace(/((^|)import[^;]+[; ]+)+/gi, "")
// replace `export default => {*};` with `render(() => {*});`
.replace(/export default \(\) => {((.|\n)*)};/, "render(() => {$1});")
// replace `export default => (*);` with `render(*);`
.replace(/export default \(\) => \(((.|\n)*)\);/, "render($1);")
// replace `export default => *;` with `render(*);`
.replace(/export default \(\) => ((.|\n)*);/, "render($1);")
.replace(/export const App = ((.|\n)*);/, "render($1);")
.replace(/export default ((.|\n)*);/, "render($1);");

return !noInline ? `<>${code}</>` : code;
};
37 changes: 21 additions & 16 deletions components/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { Button } from "@adaptui/react-tailwind";
import { useClipboard } from "@chakra-ui/hooks";
import React, { forwardRef } from "react";
import { Button, ButtonProps } from "@adaptui/react-tailwind";

export type CopyButtonProps = {
code: string;
};

export const CopyButton: React.FC<CopyButtonProps> = ({ code }) => {
const { hasCopied, onCopy } = useClipboard(code);
import { CopiedIcon, CopyIcon } from "./Icons";

return (
<span className="absolute right-0 -top-2 -translate-x-2 translate-y-4 transform">
<Button size="sm" onClick={onCopy}>
{hasCopied ? "Copied!" : "Copy"}
</Button>
</span>
);
export type CopyButtonProps = ButtonProps & {
hasCopied: boolean;
onCopy: () => void;
};

export default CopyButton;
export const CopyButton = forwardRef<HTMLButtonElement, CopyButtonProps>(
(props, ref) => {
const { onCopy, hasCopied, ...rest } = props;

return (
<Button
ref={ref}
size="sm"
onClick={onCopy}
iconOnly={hasCopied ? <CopiedIcon /> : <CopyIcon />}
{...rest}
/>
);
},
);
20 changes: 20 additions & 0 deletions components/Icons/CopiedIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createIcon } from "@adaptui/react-tailwind";

export const CopiedIcon = createIcon({
displayName: "CopiedIcon",
viewBox: "0 0 15 15",
path: (
<>
<path
d="M5 2V1h5v1H5Zm-.25-2A.75.75 0 0 0 4 .75V1h-.5A1.5 1.5 0 0 0 2 2.5v10A1.5 1.5 0 0 0 3.5 14H7v-1H3.5a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5H4v.25c0 .414.336.75.75.75h5.5a.75.75 0 0 0 .75-.75V2h.5a.5.5 0 0 1 .5.5V7h1V2.5A1.5 1.5 0 0 0 11.5 1H11V.75a.75.75 0 0 0-.75-.75h-5.5Z"
clipRule="evenodd"
fill="currentColor"
fillRule="evenodd"
/>
<path
d="M9.5 8C8.5 8 8 8.5 8 9.5v4c0 1 .5 1.5 1.5 1.5h4c1 0 1.5-.5 1.5-1.5v-4c0-1-.5-1.5-1.5-1.5h-4Z"
fill="currentColor"
/>
</>
),
});
14 changes: 14 additions & 0 deletions components/Icons/CopyIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { createIcon } from "@adaptui/react-tailwind";

export const CopyIcon = createIcon({
displayName: "CopyIcon",
viewBox: "0 0 15 15",
path: (
<path
d="M5 2V1h5v1H5Zm-.25-2A.75.75 0 0 0 4 .75V1h-.5A1.5 1.5 0 0 0 2 2.5v10A1.5 1.5 0 0 0 3.5 14H7v-1H3.5a.5.5 0 0 1-.5-.5v-10a.5.5 0 0 1 .5-.5H4v.25c0 .414.336.75.75.75h5.5a.75.75 0 0 0 .75-.75V2h.5a.5.5 0 0 1 .5.5V7h1V2.5A1.5 1.5 0 0 0 11.5 1H11V.75a.75.75 0 0 0-.75-.75h-5.5ZM9 8.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm1.5.5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1Zm2.5-.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm1.5.5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1Zm.5 1.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm-.5 2.5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1Zm0 2a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1Zm-6-4a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1Zm.5 1.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0ZM8.5 15a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1Zm2.5-.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0Zm1.5.5a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
/>
),
});
2 changes: 2 additions & 0 deletions components/Icons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./CopiedIcon";
export * from "./CopyIcon";
4 changes: 2 additions & 2 deletions components/InteractiveCodeblock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import lightTheme from "prism-react-renderer/themes/vsLight";
import { setup, tw } from "twind";
import * as colors from "twind/colors";

import CopyButton from "./CopyButton";
import { CopyCodeBlockButton } from "./Codeblock/CopyCodeBlockButton";

setup({
preflight: false, // do not include base style reset (default: use tailwind preflight)
Expand Down Expand Up @@ -135,7 +135,7 @@ export const InteractiveCodeblock = (props: InteractiveCodeblockProps) => {
<LivePreview className="p-6" />
<div className="relative">
<LiveEditor className="dark:!bg-prime-300 rounded-md rounded-t-none !bg-slate-100 !font-mono text-sm leading-6 tracking-tighter dark:!bg-opacity-10" />
<CopyButton code={code} />
<CopyCodeBlockButton code={code} />
</div>
</div>
<LiveError className="mt-0 rounded-md rounded-t-none bg-red-100 text-xs text-red-500" />
Expand Down
14 changes: 14 additions & 0 deletions components/Pre.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { CopyCodeBlockButton } from "./Codeblock/CopyCodeBlockButton";

export const Pre = (props: { children?: React.ReactNode }) => {
const { children, ...rest } = props;

return (
<div className="relative">
<pre {...rest}>
<>{children}</>
</pre>
<CopyCodeBlockButton code={"copy"} />
</div>
);
};
Loading

1 comment on commit 3c326e1

@vercel
Copy link

@vercel vercel bot commented on 3c326e1 Jul 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.