Live Editor 🤘
import { Button } from '@chakra-ui/react'
export default function Demo() {
return (
<Button m={2}>Button</Button>
);
}
const components = { | |
pre: CodeEditor, | |
}; | |
function MyApp({ Component, pageProps, ...rest }) { | |
return ( | |
<ChakraProvider theme={theme}> | |
<MDXProvider components={components}> | |
<Component {...pageProps} /> | |
</MDXProvider> | |
</ChakraProvider> | |
); | |
} | |
export default MyApp; |
export interface ICodeEditorProps { | |
children: ReactNode; | |
className?: string; | |
path?: string; | |
code?: string; | |
isLive?: boolean; | |
title?: string; | |
lang?: Language; | |
} | |
export const CodeEditor: FC<ICodeEditorProps> = ({ children, className, path, code, title, isLive, lang, ...rest }) => { | |
if (isLive) { | |
return <CodeRunner title={title} initialCode={code} {...rest} />; | |
} | |
return children; | |
}; |
import * as Chakra from '@chakra-ui/react'; | |
import { Textarea } from '@chakra-ui/react'; | |
import { FC, ReactNode, useState } from 'react'; | |
import { FaEye, FaCode } from 'react-icons/fa'; | |
import { useRunner } from 'react-runner'; | |
import { CodeMirror as CM } from 'react-runner-codemirror'; | |
const { chakra, Box, Button, ButtonGroup, Flex, Heading, Stack } = Chakra; | |
const scope = { | |
import: { | |
'@chakra-ui/react': Chakra, | |
}, | |
}; | |
const CodeMirror = chakra(CM); | |
export interface ICodeRunnerProps extends Omit<Chakra.HTMLChakraProps<'div'>, 'title'> { | |
title?: ReactNode; | |
initialCode?: string; | |
} | |
export const CodeRunner: FC<ICodeRunnerProps> = ({ title, initialCode = '', ...props }) => { | |
const [code, onChange] = useState(initialCode); | |
const { element, error } = useRunner({ code, scope }); | |
return ( | |
<Box ring="1" ringColor="gray.200" rounded="lg" {...props}> | |
<CodeMirror filename={`index.js`} defaultValue={code} onChange={(newCodes) => onChange(newCodes)} /> | |
<Box>{element}</Box> | |
{error && <pre>{error}</pre>} | |
</Box> | |
); | |
}; |
import { remarkMdxCodeMeta } from './plugins/remark/remark-mdx-code-meta.mjs'; | |
import nextMDX from '@next/mdx'; | |
const withMDX = nextMDX({ | |
extension: /\.mdx?$/, | |
/** @type {import('@mdx-js/loader').Options} */ | |
options: { | |
remarkPlugins: [remarkMdxCodeMeta], | |
providerImportSource: '@mdx-js/react', | |
}, | |
}); | |
/** | |
* @type {import('next').NextConfig} | |
*/ | |
const nextConfig = { | |
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'], | |
}; | |
export default withMDX(nextConfig); |
import { Parser } from 'acorn'; | |
import jsx from 'acorn-jsx'; | |
import { visit } from 'unist-util-visit'; | |
const parser = Parser.extend(jsx()); | |
export const transformer = (ast) => { | |
visit(ast, 'code', ({ meta, lang, value }, index, parent) => { | |
const code = JSON.stringify(`${value}\n`); | |
const langAttribute = lang ? `lang="${lang}"` : ''; | |
const codeAttribute = `code="${value}"`; | |
const codeProps = lang ? `className="language-${lang}"` : ''; | |
const template = `<pre ${meta} ${langAttribute} ${codeAttribute}><code ${codeProps}>{${code}}</code></pre>`; | |
const estree = parser.parse(template, { ecmaVersion: 'latest' }); | |
parent.children[index] = { type: 'mdxFlowExpression', template, data: { estree } }; | |
}); | |
}; | |
export const remarkMdxCodeMeta = () => transformer; |
import { Button } from '@chakra-ui/react'
export default function Demo() {
return (
<Button m={2}>Button</Button>
);
}