Complete main UI #37
77
src/components/Playground/Output/Preview/Render.tsx
Normal file
77
src/components/Playground/Output/Preview/Render.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw'
|
||||
|
||||
interface RenderProps {
|
||||
iframeKey: string
|
||||
compiledCode: string
|
||||
onError?: (errorMsg: string) => void
|
||||
}
|
||||
|
||||
interface IMessage {
|
||||
type: 'LOADED' | 'ERROR' | 'UPDATE' | 'DONE'
|
||||
msg: string
|
||||
data: {
|
||||
compiledCode: string
|
||||
}
|
||||
}
|
||||
|
||||
const getIframeUrl = (iframeRaw: string) => {
|
||||
const shimsUrl = '//unpkg.com/es-module-shims/dist/es-module-shims.js'
|
||||
// 判断浏览器是否支持esm ,不支持esm就引入es-module-shims
|
||||
const newIframeRaw =
|
||||
typeof import.meta === 'undefined'
|
||||
? iframeRaw.replace(
|
||||
'<!-- es-module-shims -->',
|
||||
`<script async src="${shimsUrl}"></script>`
|
||||
)
|
||||
: iframeRaw
|
||||
return URL.createObjectURL(new Blob([newIframeRaw], { type: 'text/html' }))
|
||||
}
|
||||
|
||||
const iframeUrl = getIframeUrl(iframeRaw)
|
||||
|
||||
const Render = ({ iframeKey, compiledCode, onError }: RenderProps) => {
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null)
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
|
||||
const handleMessage = ({ data }: { data: IMessage }) => {
|
||||
const { type, msg } = data
|
||||
switch (type) {
|
||||
case 'LOADED':
|
||||
setLoaded(true)
|
||||
break
|
||||
case 'ERROR':
|
||||
onError?.(msg)
|
||||
break
|
||||
case 'DONE':
|
||||
onError?.('')
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', handleMessage)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', handleMessage)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (loaded) {
|
||||
iframeRef.current?.contentWindow?.postMessage({
|
||||
type: 'UPDATE',
|
||||
data: { compiledCode }
|
||||
} as IMessage)
|
||||
}
|
||||
}, [compiledCode, loaded])
|
||||
|
||||
return (
|
||||
<iframe
|
||||
key={iframeKey}
|
||||
ref={iframeRef}
|
||||
src={iframeUrl}
|
||||
sandbox="allow-popups-to-escape-sandbox allow-scripts allow-popups allow-forms allow-pointer-lock allow-top-navigation allow-modals allow-same-origin"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Render
|
||||
@@ -1,8 +1,8 @@
|
||||
import '@/components/Playground/Output/Preview/preview.scss'
|
||||
import { IFiles, IImportMap } from '@/components/Playground/shared'
|
||||
import Compiler from '@/components/Playground/compiler'
|
||||
import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw'
|
||||
import { ENTRY_FILE_NAME } from '@/components/Playground/files.ts'
|
||||
import { ENTRY_FILE_NAME } from '@/components/Playground/files'
|
||||
import Render from '@/components/Playground/Output/Preview/Render'
|
||||
|
||||
interface PreviewProps {
|
||||
iframeKey: string
|
||||
@@ -10,79 +10,27 @@ interface PreviewProps {
|
||||
importMap: IImportMap
|
||||
}
|
||||
|
||||
interface IMessage {
|
||||
type: 'LOADED' | 'ERROR' | 'UPDATE' | 'DONE'
|
||||
msg: string
|
||||
data: {
|
||||
compiledCode: string
|
||||
}
|
||||
}
|
||||
|
||||
const getIframeUrl = (iframeRaw: string) => {
|
||||
const shimsUrl = '//unpkg.com/es-module-shims@1.8.0/dist/es-module-shims.js'
|
||||
// 判断浏览器是否支持esm ,不支持esm就引入es-module-shims
|
||||
const newIframeRaw =
|
||||
typeof import.meta === 'undefined'
|
||||
? iframeRaw.replace(
|
||||
'<!-- es-module-shims -->',
|
||||
`<script async src="${shimsUrl}"></script>`
|
||||
)
|
||||
: iframeRaw
|
||||
return URL.createObjectURL(new Blob([newIframeRaw], { type: 'text/html' }))
|
||||
}
|
||||
|
||||
const iframeUrl = getIframeUrl(iframeRaw)
|
||||
|
||||
const Preview = ({ iframeKey, files, importMap }: PreviewProps) => {
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null)
|
||||
const [errorMsg, setErrorMsg] = useState('')
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
const [compiledCode, setCompiledCode] = useState('')
|
||||
|
||||
const handleMessage = ({ data }: { data: IMessage }) => {
|
||||
const { type, msg } = data
|
||||
switch (type) {
|
||||
case 'LOADED':
|
||||
setLoaded(true)
|
||||
break
|
||||
case 'ERROR':
|
||||
setErrorMsg(msg)
|
||||
break
|
||||
case 'DONE':
|
||||
setErrorMsg('')
|
||||
const handleOnError = (errorMsg: string) => {
|
||||
setErrorMsg(errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', handleMessage)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('message', handleMessage)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
Compiler.compile(files, importMap, [ENTRY_FILE_NAME])
|
||||
.then((result) => {
|
||||
if (loaded) {
|
||||
iframeRef.current?.contentWindow?.postMessage({
|
||||
type: 'UPDATE',
|
||||
data: { compiledCode: result.outputFiles[0].text }
|
||||
} as IMessage)
|
||||
}
|
||||
setCompiledCode(result.outputFiles[0].text)
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
setErrorMsg(`编译失败:${e.message}`)
|
||||
})
|
||||
}, [files, Compiler, loaded])
|
||||
}, [files, Compiler])
|
||||
|
||||
return (
|
||||
<div data-component={'playground-preview'}>
|
||||
<iframe
|
||||
key={iframeKey}
|
||||
ref={iframeRef}
|
||||
src={iframeUrl}
|
||||
sandbox="allow-popups-to-escape-sandbox allow-scripts allow-popups allow-forms allow-pointer-lock allow-top-navigation allow-modals allow-same-origin"
|
||||
/>
|
||||
<Render iframeKey={iframeKey} compiledCode={compiledCode} onError={handleOnError} />
|
||||
{errorMsg && <div className={'playground-error-message'}>{errorMsg}</div>}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -14,7 +14,10 @@ class Compiler {
|
||||
constructor() {
|
||||
try {
|
||||
void esbuild
|
||||
.initialize({ worker: true, wasmURL: 'https://esm.sh/esbuild-wasm/esbuild.wasm' })
|
||||
.initialize({
|
||||
worker: true,
|
||||
wasmURL: 'https://esm.sh/esbuild-wasm@0.19.12/esbuild.wasm'
|
||||
})
|
||||
.finally(() => {
|
||||
this.init = true
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user