Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to create custom component #509

Closed
SarkarKurdish opened this issue Apr 20, 2023 · 6 comments
Closed

Ability to create custom component #509

SarkarKurdish opened this issue Apr 20, 2023 · 6 comments

Comments

@SarkarKurdish
Copy link

hey, in EditerMarkdown there are components option that allow you to customize how each element should get rendered

like this

      <EditerMarkdown
        components={{
          div: (props) => {
            return <div style={{color: "red"}} {...props} />;
          },
        }}
        source={value}
      />

I tired to a custom component so in the editor I can use this like this


**Hello world**

<MyComponent />

also i've changed the editor code to this

      <EditerMarkdown
        components={{
          div: (props) => {
            return <div style={{ color: "red" }} {...props} />;
          },
          MyCustomComponent: <MyCustomComponent />,
        }}
        source={value}
      />

function MyCustomComponent(props) {
  return (
    <div {...props} style={{ width: 30, height: 30, background: "red" }}>
      12345
    </div>
  );
}

It neither worked nor have any error and in the preview nothing was rendered for the custom component, is there any way to define a custom component?

@jaywcjlove
Copy link
Member

It's a little complicated, but not impossible.

  1. During the process of converting markdown to html, it was found that <MyComponent /> stored it.
previewOptions={{
    rehypeRewrite={(node, index, parent) => {
      // here ....
    }}
}}
  1. found your mark
previewOptions={{
    div: (props) => {
      if (props['data-MyComponent']) {
        return <MyCustomComponent />
      }
      return <div style={{ color: "red" }} {...props} />;
    },
}}

@SarkarKurdish

const CodePreview: CodeComponent | ReactMarkdownNames = ({ inline, node, ...props }) => {
const $dom = useRef<HTMLDivElement>(null);
const { 'data-meta': meta, ...rest } = props as any;
useEffect(() => {
if ($dom.current) {
const parentElement = $dom.current.parentElement;
if (parentElement && parentElement.parentElement) {
parentElement.parentElement.replaceChild($dom.current, parentElement);
}
}
}, [$dom]);
if (inline || !isMeta(meta)) {
return <code {...props} />;
}
const line = node.position?.start.line;
const metaId = getMetaId(meta) || String(line);
const Child = data.components[`${metaId}`];
if (metaId && typeof Child === 'function') {
const code = data.data[metaId].value || '';
const param = getURLParameters(meta);
return (
<CodeLayout
ref={$dom}
style={{ marginBottom: 10 }}
toolbar={param.title || 'Example'}
code={<pre {...rest} />}
text={code}
>
<Child />
</CodeLayout>
);
}
return <code {...rest} />;
};

@SarkarKurdish
Copy link
Author

It's a little complicated, but not impossible.

  1. During the process of converting markdown to html, it was found that <MyComponent /> stored it.
previewOptions={{
    rehypeRewrite={(node, index, parent) => {
      // here ....
    }}
}}
  1. found your mark
previewOptions={{
    div: (props) => {
      if (props['data-MyComponent']) {
        return <MyCustomComponent />
      }
      return <div style={{ color: "red" }} {...props} />;
    },
}}

@SarkarKurdish

const CodePreview: CodeComponent | ReactMarkdownNames = ({ inline, node, ...props }) => {
const $dom = useRef<HTMLDivElement>(null);
const { 'data-meta': meta, ...rest } = props as any;
useEffect(() => {
if ($dom.current) {
const parentElement = $dom.current.parentElement;
if (parentElement && parentElement.parentElement) {
parentElement.parentElement.replaceChild($dom.current, parentElement);
}
}
}, [$dom]);
if (inline || !isMeta(meta)) {
return <code {...props} />;
}
const line = node.position?.start.line;
const metaId = getMetaId(meta) || String(line);
const Child = data.components[`${metaId}`];
if (metaId && typeof Child === 'function') {
const code = data.data[metaId].value || '';
const param = getURLParameters(meta);
return (
<CodeLayout
ref={$dom}
style={{ marginBottom: 10 }}
toolbar={param.title || 'Example'}
code={<pre {...rest} />}
text={code}
>
<Child />
</CodeLayout>
);
}
return <code {...rest} />;
};

Thanks for the answer, but I could not make any sense of it could you please explain a little more or better of give me an example?

@jaywcjlove
Copy link
Member

@SarkarKurdish https://codesandbox.io/embed/markdown-editor-for-react-https-github-com-uiwjs-react-md-editor-issues-509-4qqpc6?fontsize=14&hidenavigation=1&theme=dark

Wrote a basic example, hope you can improve it

import React from "react";
import ReactDOM from "react-dom";
import MDEditor from "@uiw/react-md-editor";

const MyComponent = () => {
  return <div>这是测试</div>;
};

const mkdStr = `
**Hello world**


<MyComponent />

<MyComponent></MyComponent>

test
`;

function App() {
  const [value, setValue] = React.useState(mkdStr);
  return (
    <div className="container">
      <MDEditor
        height={200}
        previewOptions={{
          rehypeRewrite: (node, index, parent) => {
            if (node.type === "root") {
              console.log("node:", node);
            }
            if (node.tagName === "mycomponent") {
              node.properties["data-mycomp"] = node.tagName;
              node.tagName = "div";

              if (node.children.lenght > 0) {
                // ---> <MyComponent />
              } else {
                // ==> <MyComponent></MyComponent>
              }
            }
          },
          components: {
            div: ({ node, ...props }) => {
              const { "data-mycomp": tagName } = props;
              console.log("node0:", tagName);
              if (tagName === "mycomponent") {
                return <MyComponent />;
              }
              console.log("node1:", node);
              return <div {...props} />;
            }
          }
        }}
        value={value}
        onChange={setValue}
      />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("container"));

@jaywcjlove
Copy link
Member

image

@SarkarKurdish
Copy link
Author

@jaywcjlove Thanks for your help, it's just too complicated to work with in production and as the project gets bigger I don't think this way will scale very well, I ended up using next-mdx-remote where the components support custom components without any isse (I expected this package to do that as well)

This is my code

          <MDXRemote
            {...mdxSource}
            components={{ SyntaxHighlighter, MyComponent }}
          />

and this is a markdown example

# The title

<MyComponent data={{ name: "Joe" }} />

The result is what I've expected the component get's rendered and there is no need to any hacky stuff

@aysegulkavakli
Copy link

@SarkarKurdish https://codesandbox.io/embed/markdown-editor-for-react-https-github-com-uiwjs-react-md-editor-issues-509-4qqpc6?fontsize=14&hidenavigation=1&theme=dark

Wrote a basic example, hope you can improve it

import React from "react";
import ReactDOM from "react-dom";
import MDEditor from "@uiw/react-md-editor";

const MyComponent = () => {
  return <div>这是测试</div>;
};

const mkdStr = `
**Hello world**


<MyComponent />

<MyComponent></MyComponent>

test
`;

function App() {
  const [value, setValue] = React.useState(mkdStr);
  return (
    <div className="container">
      <MDEditor
        height={200}
        previewOptions={{
          rehypeRewrite: (node, index, parent) => {
            if (node.type === "root") {
              console.log("node:", node);
            }
            if (node.tagName === "mycomponent") {
              node.properties["data-mycomp"] = node.tagName;
              node.tagName = "div";

              if (node.children.lenght > 0) {
                // ---> <MyComponent />
              } else {
                // ==> <MyComponent></MyComponent>
              }
            }
          },
          components: {
            div: ({ node, ...props }) => {
              const { "data-mycomp": tagName } = props;
              console.log("node0:", tagName);
              if (tagName === "mycomponent") {
                return <MyComponent />;
              }
              console.log("node1:", node);
              return <div {...props} />;
            }
          }
        }}
        value={value}
        onChange={setValue}
      />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("container"));

I did the same thing but in my example node.type coming "raw". And It doesn't have the attributes like tagName and properties

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants