JSX
The JSX plugin allows you to process and associate custom editors with the JSX components in your markdown source - a capability enabled by MDX. The package includes a generic editor component, but you can also create your custom editors. The next example includes three JSX descriptors and an example of a custom editor that uses the NestedLexicalEditor
component to edit the markdown contents of a JSX component.
The JSX syntax also supports {}
as a way to embed JavaScript expressions in your markdown. Out of the box, the plugin will enable a simple inline editor for the expressions, too.
const jsxComponentDescriptors: JsxComponentDescriptor[] = [
{
name: 'MyLeaf',
kind: 'text', // 'text' for inline, 'flow' for block
// the source field is used to construct the import statement at the top of the markdown document.
// it won't be actually sourced.
source: './external',
// Used to construct the property popover of the generic editor
props: [
{ name: 'foo', type: 'string' },
{ name: 'bar', type: 'string' },
{ name: 'onClick', type: 'expression' }
],
// whether the component has children or not
hasChildren: true,
Editor: GenericJsxEditor
},
{
name: 'Marker',
kind: 'text',
source: './external',
props: [{ name: 'type', type: 'string' }],
hasChildren: false,
Editor: () => {
return (
<div style={{ border: '1px solid red', padding: 8, margin: 8, display: 'inline-block' }}>
<NestedLexicalEditor<MdxJsxTextElement>
getContent={(node) => node.children}
getUpdatedMdastNode={(mdastNode, children: any) => {
return { ...mdastNode, children }
}}
/>
</div>
)
}
},
{
name: 'BlockNode',
kind: 'flow',
source: './external',
props: [],
hasChildren: true,
Editor: GenericJsxEditor
}
]
// a toolbar button that will insert a JSX element into the editor.
const InsertMyLeaf = () => {
const insertJsx = usePublisher(insertJsx$)
return (
<Button
onClick={() =>
insertJsx({
name: 'MyLeaf',
kind: 'text',
props: { foo: 'bar', bar: 'baz', onClick: { type: 'expression', value: '() => console.log("Clicked")' } }
})
}
>
Leaf
</Button>
)
}
export const Example = () => {
return (
<MDXEditor
markdown={jsxMarkdown} // the contents of the file below
onChange={console.log}
plugins={[
jsxPlugin({ jsxComponentDescriptors }),
toolbarPlugin({
toolbarContents: () => (
<>
<InsertMyLeaf />
</>
)
})
]}
/>
)
}
import { MyLeaf, BlockNode } from './external';
A paragraph with inline jsx component <MyLeaf foo="bar" bar="baz" onClick={() => console.log("Clicked")}>Nested _markdown_</MyLeaf> more <Marker type="warning" />.
<BlockNode foo="fooValue">
Content *foo*
more Content
</BlockNode>
Types of properties
There are two types of properties - "textual" and "expressions" in JSX. You can define type in JsxComponentDescriptor
. jsxPlugin
will treat the value based on this setting. For example, this code:
const jsxComponentDescriptors: JsxComponentDescriptor[] = [
{
name: 'MyLeaf',
kind: 'text',
props: [
{ name: 'foo', type: 'string' } // Textual property type
],
hasChildren: true,
Editor: GenericJsxEditor
}
]
will produce component like the following:
<MyLeaf foo="bar">Some text...</MyLeaf>
While this descriptor:
const jsxComponentDescriptors: JsxComponentDescriptor[] = [
{
name: 'MyLeaf',
kind: 'text',
props: [
{ name: 'foo', type: 'expression' } // Expression property type
],
hasChildren: true,
Editor: GenericJsxEditor
}
]
will produce:
<MyLeaf foo={bar}>Some text...</MyLeaf>