-
Notifications
You must be signed in to change notification settings - Fork 55
/
live-code.js
93 lines (86 loc) · 2.69 KB
/
live-code.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import {Absolute, BorderBox, Flex, Relative, Text} from '@primer/components'
import htmlReactParser from 'html-react-parser'
import githubTheme from '../github'
import React, {useState} from 'react'
import reactElementToJsxString from 'react-element-to-jsx-string'
import {LiveEditor, LiveError, LivePreview, LiveProvider} from 'react-live'
import {ThemeContext} from 'styled-components'
import scope from '../live-code-scope'
import ClipboardCopy from './clipboard-copy'
import LivePreviewWrapper from './live-preview-wrapper'
const languageTransformers = {
html: (html) => htmlToJsx(html),
jsx: (jsx) => wrapWithFragment(jsx),
}
function htmlToJsx(html) {
try {
const reactElement = htmlReactParser(removeNewlines(html))
// The output of htmlReactParser could be a single React element
// or an array of React elements. reactElementToJsxString does not accept arrays
// so we have to wrap the output in React fragment.
return reactElementToJsxString(<>{reactElement}</>)
} catch (error) {
return wrapWithFragment(html)
}
}
function removeNewlines(string) {
return string.replace(/(\r\n|\n|\r)/gm, '')
}
function wrapWithFragment(jsx) {
return `<React.Fragment>${jsx}</React.Fragment>`
}
function LiveCode({code, language, noinline}) {
const theme = React.useContext(ThemeContext)
const [liveCode, setLiveCode] = useState(code)
const handleChange = (updatedLiveCode) => setLiveCode(updatedLiveCode)
return (
<Flex flexDirection="column" mb={3}>
<LiveProvider
scope={scope}
code={liveCode}
transformCode={languageTransformers[language]}
noInline={noinline}
>
<Flex
sx={{
border: '1px solid',
borderColor: 'border.gray',
borderTopRightRadius: 2,
borderTopLeftRadius: 2,
}}
>
<LivePreviewWrapper>
<LivePreview />
</LivePreviewWrapper>
</Flex>
<Relative>
<LiveEditor
onChange={handleChange}
theme={githubTheme}
ignoreTabKey={true}
padding={theme.space[3]}
style={{
fontFamily: theme.fonts.mono,
fontSize: '85%',
borderBottomLeftRadius: theme.radii[2],
borderBottomRightRadius: theme.radii[2],
}}
/>
<Absolute top={0} right={0} p={2}>
<ClipboardCopy value={liveCode} />
</Absolute>
</Relative>
<Text
as={LiveError}
m={0}
p={3}
fontFamily="mono"
fontSize={1}
color="white"
bg="red.5"
/>
</LiveProvider>
</Flex>
)
}
export default LiveCode