react-markdown-preview icon indicating copy to clipboard operation
react-markdown-preview copied to clipboard

when using SyntaxHighlighter children is [object Object] instead of string

Open momozahara opened this issue 2 years ago • 5 comments

image

// react-markdown
<ReactMarkdown
  components={{
    code({ node, inline, className, children, ...props }) {
      const match = /language-(\w+)/.exec(className || "");
      return !inline && match ? (
        <SyntaxHighlighter
          language={match[1]}
          style={markdownTheme as any}
          PreTag="div"
          {...props}
        >
          {String(children).replace(/\n$/, "")} // <============
        </SyntaxHighlighter>
      ) : (
        <code
          className={className}
          {...props}
        >
          {children}
        </code>
      );
    },
  }}
>
  {markdownSource}
</ReactMarkdown>

// @uiw/react-markdown-preview
<MarkdownPreview
  source={markdownSource}
  components={{
    code({ node, inline, className, children, ...props }) {
      const match = /language-(\w+)/.exec(className || "");
      return !inline && match ? (
        <SyntaxHighlighter
          language={match[1]}
          style={markdownTheme as any}
          PreTag="div"
          {...props}
        >
          {String(children).replace(/\n$/, "")} //  <============
        </SyntaxHighlighter>
      ) : (
        <code
          className={className}
          {...props}
        >
          {children}
        </code>
      );
    },
  }}
/>

momozahara avatar Sep 23 '22 12:09 momozahara

oh never mind my bad its ReactNode and already rewrite by your setting

momozahara avatar Sep 23 '22 14:09 momozahara

oh never mind my bad its ReactNode and already rewrite my your setting

How did you fix it? @momozahara

GarliqBread avatar Sep 26 '22 08:09 GarliqBread

@MonsterCurry I switch to react-markdown but using this module css and do some work to imitate the element the main reason for that is im using nextjs and i dont want to install next-remove-imports which is broke my other module such as ace and i dont want to import markdown css in _app

my example: markdown editor

So I made this react-markdown-css module to bypass nextjs import outside of _app

import "react-markdown-css/styles/markdown.css";
import "react-markdown-css/styles/katex.min.css";
import ReactMarkdown from "react-markdown";

import remarkGfm from "remark-gfm";
import remarkMath from "remark-math";

import rehypeRaw from "rehype-raw";
import rehypeKatex from "rehype-katex";

<ReactMarkdown
  rehypePlugins={[rehypeRaw, rehypeKatex]}
  remarkPlugins={[remarkGfm, remarkMath]}
  components={Components}
>
  {markdownSource}
</ReactMarkdown>

Components

import { MouseEvent } from "react";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import highlightTheme from "react-syntax-highlighter/dist/esm/styles/prism/one-light";
import {
  CodeProps,
  HeadingProps,
  ReactMarkdownProps,
} from "react-markdown/lib/ast-to-react";

export function codeRenderer({
  inline,
  className,
  children,
  ...props
}: CodeProps) {
  const match = /language-(\w+)/.exec(className || "");
  return !inline && match ? (
    <div
      style={{
        position: "relative",
      }}
    >
      <SyntaxHighlighter
        language={match[1]}
        style={highlightTheme as any}
        PreTag="div"
        {...props}
      >
        {String(children).replace(/\n$/, "")}
      </SyntaxHighlighter>
      <CopyToClipboard text={String(children)}>
        <div
          data-code={String(children)}
          className="copied"
        >
          <svg
            className="octicon-copy"
            aria-hidden="true"
            viewBox="0 0 16 16"
            fill="currentColor"
            height="12"
            width="12"
          >
            <path
              fillRule="evenodd"
              d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"
            ></path>
            <path
              fillRule="evenodd"
              d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"
            ></path>
          </svg>
          <svg
            className="octicon-check"
            aria-hidden="true"
            viewBox="0 0 16 16"
            fill="currentColor"
            height="12"
            width="12"
          >
            <path
              fillRule="evenodd"
              d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"
            ></path>
          </svg>
        </div>
      </CopyToClipboard>
    </div>
  ) : (
    <code
      className={className}
      {...props}
    >
      {children}
    </code>
  );
}

type AnchorProps = ReactMarkdownProps & {
  href?: string;
};

function onAnchorClick(e: MouseEvent<HTMLElement>) {
  const parent = e.currentTarget!.parentElement!;
  const container = parent!.parentElement!;
  container.scrollBy({
    behavior: "smooth",
    top: parent.getBoundingClientRect().top,
  });
}

function Anchor(props: AnchorProps) {
  const { children, href } = props;

  return (
    <>
      <a
        className="anchor"
        aria-hidden={true}
        tabIndex={-1}
        href={href}
        onClick={onAnchorClick}
      >
        <svg
          className="octicon octicon-link"
          viewBox="0 0 16 16"
          version="1.1"
          width="16"
          height="16"
          aria-hidden="true"
        >
          <path
            fillRule="evenodd"
            d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"
          ></path>
        </svg>
      </a>
      {children}
    </>
  );
}

export function headingRenderer(props: HeadingProps) {
  const { node, level, children } = props;

  let href: string = children ? `#${children.toString().toLowerCase()}` : "#";
  const child = node.children[0];
  if (child && child.type === "element" && child.tagName === "a") {
    if (child.children[0].type === "text") {
      href = `#${child.children[0].value
        .replace(/[^a-zA-Z ]/g, "")
        .toLowerCase()}`;
    }
  }

  switch (level) {
    case 1: {
      return (
        <h1 id={href}>
          <Anchor
            href={href}
            {...props}
          />
        </h1>
      );
    }
    case 2: {
      return (
        <h2 id={href}>
          <Anchor
            href={href}
            {...props}
          />
        </h2>
      );
    }
    case 3: {
      return (
        <h3 id={href}>
          <Anchor
            href={href}
            {...props}
          />
        </h3>
      );
    }
    case 4: {
      return (
        <h4 id={href}>
          <Anchor
            href={href}
            {...props}
          />
        </h4>
      );
    }
    case 5: {
      return (
        <h5 id={href}>
          <Anchor
            href={href}
            {...props}
          />
        </h5>
      );
    }
    case 6: {
      return (
        <h6 id={href}>
          <Anchor
            href={href}
            {...props}
          />
        </h6>
      );
    }
  }

  return <>{children}</>;
}

const Components = {
  code: codeRenderer,
  h1: headingRenderer,
  h2: headingRenderer,
  h3: headingRenderer,
  h4: headingRenderer,
  h5: headingRenderer,
  h6: headingRenderer,
};

export default Components;

momozahara avatar Sep 27 '22 07:09 momozahara

@momozahara I see, thanks! But then, should we re-open this ticket? Since you didn't really find a solution, just used another library 😅

GarliqBread avatar Sep 27 '22 07:09 GarliqBread