updated July 05, 202452 views

import { useRef, useState, Fragment } from "react";

const CodeBlock = (props: {
  className: string | undefined;
  children: React.ReactChildren;
}): JSX.Element => {
  const textInput = useRef(null);
  const [hovered, setHovered] = useState(false);
  const [copied, setCopied] = useState(false);

  const onEnter = () => {
    setHovered(true);
  };

  const onExit = () => {
    setHovered(false);
    setCopied(false);
  };

  const onCopy = () => {
    setCopied(true);
    navigator.clipboard.writeText(textInput.current.textContent);
    setTimeout(() => {
      setCopied(false);
    }, 3000);
  };

  return (
    <div
      ref={textInput}
      onMouseEnter={onEnter}
      onMouseLeave={onExit}
      className="relative"
    >
      {hovered && (
        <button
          aria-label="Copy code"
          type="button"
          className={`absolute right-3 top-3 w-7 h-7 p-1 rounded border-2 bg-gray-200 dark:bg-[#282e33] ${
            copied
              ? "focus:outline-none focus:border-green-500 border-green-500 dark:border-green-400 dark:focus:border-green-400"
              : "border-gray-100 dark:border-gray-300"
          }`}
          onClick={onCopy}
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            viewBox="0 0 24 24"
            stroke="currentColor"
            fill="none"
            className={
              copied
                ? "text-green-500 dark:text-green-400"
                : "text-gray-700 dark:text-gray-300"
            }
          >
            {copied ? (
              <Fragment>
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  ="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
                />
              </Fragment>
            ) : (
              <Fragment>
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  ="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
                />
              </Fragment>
            )}
          </svg>
        </button>
      )}

      <pre>{props.children}</pre>
    </div>
  );
};

export default CodeBlock;
import Link from "next/link";
import Image from "next/image";
import ImageWithTheme from "components/ImageWithTheme";
import CodeBlock from "components/CodeBlock";
import { Files } from "components/Files";

const CustomLink = (props) => {
  const href = props.href;
  const isInternalLink = href && (href.startsWith("/") || href.startsWith("#"));

  if (isInternalLink) {
    return (
      <Link href={href}>
        <a {...props}>{props.children}</a>
      </Link>
    );
  }

  return <a target="_blank" rel="noopener noreferrer" {...props} />;
};

function RoundedImage(props) {
  return <Image alt={props.alt} className="rounded-lg" {...props} />;
}

const MDXComponents = {
  ImageWithTheme,
  a: CustomLink,
  pre: CodeBlock,
  Files,
};

export default MDXComponents;