Optimize Playground
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -160,4 +160,7 @@ const CodeEditor = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CodeEditor.Editor = Editor
|
||||||
|
CodeEditor.FileSelector = FileSelector
|
||||||
|
|
||||||
export default CodeEditor
|
export default CodeEditor
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -3,9 +3,4 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
iframe {
|
|
||||||
border: none;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
6
src/components/Playground/Output/Preview/render.scss
Normal file
6
src/components/Playground/Output/Preview/render.scss
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[data-component=playground-output-preview-render] {
|
||||||
|
border: none;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
@@ -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]} />}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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={() => {}}
|
||||||
|
|||||||
@@ -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={() => {}}
|
||||||
|
|||||||
Reference in New Issue
Block a user