src/features/ui/markdown/code-block.tsx (54 lines of code) (raw):

import { CheckIcon, ClipboardIcon } from "lucide-react"; import { FC, memo, useEffect, useState } from "react"; import { Prism } from "react-syntax-highlighter"; import { atomDark } from "react-syntax-highlighter/dist/esm/styles/prism"; import { Button } from "../button"; export const fence = { render: "CodeBlock", attributes: { language: { type: String, }, value: { type: String, }, }, }; interface Props { language: string; children: string; } export const CodeBlock: FC<Props> = memo(({ language, children }) => { const [isIconChecked, setIsIconChecked] = useState(false); const handleButtonClick = () => { navigator.clipboard.writeText(children); setIsIconChecked(true); }; useEffect(() => { const timeout = setTimeout(() => { setIsIconChecked(false); }, 2000); return () => clearTimeout(timeout); }, [isIconChecked]); return ( <div className="flex flex-col -mx-9"> <div className="flex items-center justify-end"> <Button variant={"ghost"} size={"sm"} title="Copy text" className="justify-right flex gap-2" onClick={handleButtonClick} > <span className="text-xs text-muted-foreground">Copy {language}</span> {isIconChecked ? ( <CheckIcon size={16} /> ) : ( <ClipboardIcon size={16} /> )} </Button> </div> <Prism language={language} style={atomDark} PreTag="pre" showLineNumbers> {children} </Prism> </div> ); }); CodeBlock.displayName = "CodeBlock";