Optimize Playground

This commit is contained in:
2024-01-25 15:35:52 +08:00
parent 34034ec59b
commit 96acaddf77
12 changed files with 58 additions and 33 deletions

View File

@@ -110,6 +110,10 @@ const FileSelector = ({
} }
const handleOnValidateTab = (newFileName: string, oldFileName: string) => { const handleOnValidateTab = (newFileName: string, oldFileName: string) => {
if (newFileName.length > 40) {
onError?.('File name is too long, maximum 40 characters.')
}
if (!/\.(jsx|tsx|js|ts|css|json)$/.test(newFileName)) { if (!/\.(jsx|tsx|js|ts|css|json)$/.test(newFileName)) {
onError?.('Playground only supports *.jsx, *.tsx, *.js, *.ts, *.css, *.json files.') onError?.('Playground only supports *.jsx, *.tsx, *.js, *.ts, *.css, *.json files.')
@@ -219,4 +223,6 @@ const FileSelector = ({
) )
} }
FileSelector.Item = Item
export default FileSelector export default FileSelector

View File

@@ -160,4 +160,7 @@ const CodeEditor = ({
) )
} }
CodeEditor.Editor = Editor
CodeEditor.FileSelector = FileSelector
export default CodeEditor export default CodeEditor

View File

@@ -1,3 +1,4 @@
import '@/components/Playground/Output/Preview/render.scss'
import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw' import iframeRaw from '@/components/Playground/Output/Preview/iframe.html?raw'
interface RenderProps { interface RenderProps {
@@ -66,6 +67,7 @@ const Render = ({ iframeKey, compiledCode, onError }: RenderProps) => {
return ( return (
<iframe <iframe
data-component={'playground-output-preview-render'}
key={iframeKey} key={iframeKey}
ref={iframeRef} ref={iframeRef}
src={iframeUrl} src={iframeUrl}

View File

@@ -48,11 +48,6 @@
}); });
</script> </script>
<script type="module" id="appSrc"></script> <script type="module" id="appSrc"></script>
<div id="root"> <div id="root"></div>
<div
style="position:absolute;top: 0;left:0;width:100%;height:100%;display: flex;justify-content: center;align-items: center;">
Loading...
</div>
</div>
</body> </body>
</html> </html>

View File

@@ -1,16 +1,16 @@
import '@/components/Playground/Output/Preview/preview.scss' import '@/components/Playground/Output/Preview/preview.scss'
import { IFiles, IImportMap } from '@/components/Playground/shared' import { IFiles, IImportMap } from '@/components/Playground/shared'
import Compiler from '@/components/Playground/compiler' import Compiler from '@/components/Playground/compiler'
import { ENTRY_FILE_NAME } from '@/components/Playground/files'
import Render from '@/components/Playground/Output/Preview/Render' import Render from '@/components/Playground/Output/Preview/Render'
interface PreviewProps { interface PreviewProps {
iframeKey: string iframeKey: string
files: IFiles files: IFiles
importMap: IImportMap importMap: IImportMap
entryPoint: string
} }
const Preview = ({ iframeKey, files, importMap }: PreviewProps) => { const Preview = ({ iframeKey, files, importMap, entryPoint }: PreviewProps) => {
const [errorMsg, setErrorMsg] = useState('') const [errorMsg, setErrorMsg] = useState('')
const [compiledCode, setCompiledCode] = useState('') const [compiledCode, setCompiledCode] = useState('')
@@ -19,7 +19,7 @@ const Preview = ({ iframeKey, files, importMap }: PreviewProps) => {
} }
useEffect(() => { useEffect(() => {
Compiler.compile(files, importMap, [ENTRY_FILE_NAME]) Compiler.compile(files, importMap, entryPoint)
.then((result) => { .then((result) => {
setCompiledCode(result.outputFiles[0].text) setCompiledCode(result.outputFiles[0].text)
}) })
@@ -36,4 +36,6 @@ const Preview = ({ iframeKey, files, importMap }: PreviewProps) => {
) )
} }
Preview.Render = Render
export default Preview export default Preview

View File

@@ -3,9 +3,4 @@
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
iframe {
border: none;
flex: 1;
}
} }

View File

@@ -0,0 +1,6 @@
[data-component=playground-output-preview-render] {
border: none;
height: 100%;
width: 100%;
flex: 1;
}

View File

@@ -1,6 +1,6 @@
import FlexBox from '@/components/common/FlexBox' import FlexBox from '@/components/common/FlexBox'
import { IFiles, IImportMap } from '@/components/Playground/shared' import { IFiles, IImportMap } from '@/components/Playground/shared'
import FileSelector from '@/components/Playground/CodeEditor/FileSelector' import Playground from '@/components/Playground'
import Transform from '@/components/Playground/Output/Transform' import Transform from '@/components/Playground/Output/Transform'
import Preview from '@/components/Playground/Output/Preview' import Preview from '@/components/Playground/Output/Preview'
@@ -8,14 +8,15 @@ interface OutputProps {
files: IFiles files: IFiles
selectedFileName: string selectedFileName: string
importMap: IImportMap importMap: IImportMap
entryPoint: string
} }
const Output = ({ files, selectedFileName, importMap }: OutputProps) => { const Output = ({ files, selectedFileName, importMap, entryPoint }: OutputProps) => {
const [selectedTab, setSelectedTab] = useState('Preview') const [selectedTab, setSelectedTab] = useState('Preview')
return ( return (
<FlexBox data-component={'playground-code-output'}> <FlexBox data-component={'playground-code-output'}>
<FileSelector <Playground.CodeEditor.FileSelector
files={{ files={{
Preview: { name: 'Preview', language: 'json', value: '' }, Preview: { name: 'Preview', language: 'json', value: '' },
Transform: { name: 'Transform', language: 'json', value: '' } Transform: { name: 'Transform', language: 'json', value: '' }
@@ -29,6 +30,7 @@ const Output = ({ files, selectedFileName, importMap }: OutputProps) => {
iframeKey={JSON.stringify(importMap)} iframeKey={JSON.stringify(importMap)}
files={files} files={files}
importMap={importMap} importMap={importMap}
entryPoint={entryPoint}
/> />
)} )}
{selectedTab === 'Transform' && <Transform file={files[selectedFileName]} />} {selectedTab === 'Transform' && <Transform file={files[selectedFileName]} />}

View File

@@ -2,7 +2,7 @@ import esbuild, { Loader, OnLoadArgs, Plugin, PluginBuild } from 'esbuild-wasm'
import localforage from 'localforage' import localforage from 'localforage'
import axios from 'axios' import axios from 'axios'
import { IFiles, IImportMap } from '@/components/Playground/shared' import { IFiles, IImportMap } from '@/components/Playground/shared'
import { cssToJs, ENTRY_FILE_NAME, jsonToJs, addReactImport } from '@/components/Playground/files' import { cssToJs, jsonToJs, addReactImport } from '@/components/Playground/files'
class Compiler { class Compiler {
private init = false private init = false
@@ -42,7 +42,7 @@ class Compiler {
return esbuild.transform(code, { loader }) return esbuild.transform(code, { loader })
}) })
compile = (files: IFiles, importMap: IImportMap, entryPoints: string[]) => compile = (files: IFiles, importMap: IImportMap, entryPoint: string) =>
new Promise<void>((resolve) => { new Promise<void>((resolve) => {
if (this.init) { if (this.init) {
resolve() resolve()
@@ -57,11 +57,11 @@ class Compiler {
}).then(() => { }).then(() => {
return esbuild.build({ return esbuild.build({
bundle: true, bundle: true,
entryPoints: entryPoints, entryPoints: [entryPoint],
format: 'esm', format: 'esm',
metafile: true, metafile: true,
write: false, write: false,
plugins: [this.fileResolverPlugin(files, importMap, entryPoints)] plugins: [this.fileResolverPlugin(files, importMap, entryPoint)]
}) })
}) })
@@ -72,13 +72,13 @@ class Compiler {
private fileResolverPlugin = ( private fileResolverPlugin = (
files: IFiles, files: IFiles,
importMap: IImportMap, importMap: IImportMap,
entryPoints: string[] entryPoint: string
): Plugin => { ): Plugin => {
return { return {
name: 'file-resolver-plugin', name: 'file-resolver-plugin',
setup: (build: PluginBuild) => { setup: (build: PluginBuild) => {
build.onResolve({ filter: /.*/ }, (args: esbuild.OnResolveArgs) => { build.onResolve({ filter: /.*/ }, (args: esbuild.OnResolveArgs) => {
if (entryPoints.includes(args.path)) { if (entryPoint === args.path) {
return { return {
namespace: 'OxygenToolbox', namespace: 'OxygenToolbox',
path: args.path path: args.path
@@ -165,10 +165,10 @@ class Compiler {
}) })
build.onLoad({ filter: /.*/ }, async (args: OnLoadArgs) => { build.onLoad({ filter: /.*/ }, async (args: OnLoadArgs) => {
if (args.path === ENTRY_FILE_NAME) { if (entryPoint === args.path) {
return { return {
loader: 'tsx', loader: 'tsx',
contents: addReactImport(files[ENTRY_FILE_NAME].value) contents: addReactImport(files[entryPoint].value)
} }
} }

View File

@@ -1,6 +1,7 @@
import '@/components/Playground/playground.scss' import '@/components/Playground/playground.scss'
import { IFiles, IImportMap, ITsconfig } from '@/components/Playground/shared' import { IFiles, IImportMap, ITsconfig } from '@/components/Playground/shared'
import { import {
ENTRY_FILE_NAME,
IMPORT_MAP_FILE_NAME, IMPORT_MAP_FILE_NAME,
MAIN_FILE_NAME, MAIN_FILE_NAME,
TS_CONFIG_FILE_NAME TS_CONFIG_FILE_NAME
@@ -13,9 +14,15 @@ interface PlaygroundProps {
initFiles: IFiles initFiles: IFiles
initImportMapRaw: string initImportMapRaw: string
initTsconfigRaw: string initTsconfigRaw: string
entryPoint?: string
} }
const Playground = ({ initFiles, initImportMapRaw, initTsconfigRaw }: PlaygroundProps) => { const Playground = ({
initFiles,
initImportMapRaw,
initTsconfigRaw,
entryPoint = ENTRY_FILE_NAME
}: PlaygroundProps) => {
const [files, setFiles] = useState(initFiles) const [files, setFiles] = useState(initFiles)
const [selectedFileName, setSelectedFileName] = useState(MAIN_FILE_NAME) const [selectedFileName, setSelectedFileName] = useState(MAIN_FILE_NAME)
const [importMapRaw, setImportMapRaw] = useState<string>(initImportMapRaw) const [importMapRaw, setImportMapRaw] = useState<string>(initImportMapRaw)
@@ -93,7 +100,12 @@ const Playground = ({ initFiles, initImportMapRaw, initTsconfigRaw }: Playground
onChangeFileContent={handleOnChangeFileContent} onChangeFileContent={handleOnChangeFileContent}
onSelectedFileChange={setSelectedFileName} onSelectedFileChange={setSelectedFileName}
/> />
<Output files={files} selectedFileName={selectedFileName} importMap={importMap!} /> <Output
files={files}
selectedFileName={selectedFileName}
importMap={importMap!}
entryPoint={entryPoint}
/>
</FlexBox> </FlexBox>
) )
} }

View File

@@ -32,8 +32,8 @@ import FitFullscreen from '@/components/common/FitFullscreen'
import FlexBox from '@/components/common/FlexBox' import FlexBox from '@/components/common/FlexBox'
import HideScrollbar from '@/components/common/HideScrollbar' import HideScrollbar from '@/components/common/HideScrollbar'
import Card from '@/components/common/Card' import Card from '@/components/common/Card'
import CodeEditor from '@/components/Playground/CodeEditor'
import Permission from '@/components/common/Permission' import Permission from '@/components/common/Permission'
import Playground from '@/components/Playground'
const Base = () => { const Base = () => {
const blocker = useBlocker( const blocker = useBlocker(
@@ -283,9 +283,11 @@ const Base = () => {
duration: 0 duration: 0
}) })
void compiler void compiler
.compile(files, importMap, [ .compile(
files,
importMap,
compileForm.getFieldValue('entryFileName') as string compileForm.getFieldValue('entryFileName') as string
]) )
.then((result) => { .then((result) => {
void message.destroy('compiling') void message.destroy('compiling')
void message.loading({ void message.loading({
@@ -982,7 +984,7 @@ const Base = () => {
</Card> </Card>
{editingFileName && ( {editingFileName && (
<Card> <Card>
<CodeEditor <Playground.CodeEditor
files={editingFiles[editingBaseId]} files={editingFiles[editingBaseId]}
selectedFileName={editingFileName} selectedFileName={editingFileName}
onSelectedFileChange={() => {}} onSelectedFileChange={() => {}}

View File

@@ -30,8 +30,8 @@ import FitFullscreen from '@/components/common/FitFullscreen'
import FlexBox from '@/components/common/FlexBox' import FlexBox from '@/components/common/FlexBox'
import HideScrollbar from '@/components/common/HideScrollbar' import HideScrollbar from '@/components/common/HideScrollbar'
import Card from '@/components/common/Card' import Card from '@/components/common/Card'
import CodeEditor from '@/components/Playground/CodeEditor'
import Permission from '@/components/common/Permission' import Permission from '@/components/common/Permission'
import Playground from '@/components/Playground'
const Template = () => { const Template = () => {
const blocker = useBlocker( const blocker = useBlocker(
@@ -866,7 +866,7 @@ const Template = () => {
</Card> </Card>
{editingFileName && ( {editingFileName && (
<Card> <Card>
<CodeEditor <Playground.CodeEditor
files={editingFiles[editingTemplateId]} files={editingFiles[editingTemplateId]}
selectedFileName={editingFileName} selectedFileName={editingFileName}
onSelectedFileChange={() => {}} onSelectedFileChange={() => {}}