Finish editor in Playground Code Editor
This commit is contained in:
83
src/components/ReactPlayground/CodeEditor/Editor/ata.ts
Normal file
83
src/components/ReactPlayground/CodeEditor/Editor/ata.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { ATABootstrapConfig, setupTypeAcquisition } from '@typescript/ata'
|
||||
|
||||
type DelegateListener = Required<{
|
||||
[k in keyof ATABootstrapConfig['delegate']]: Set<NonNullable<ATABootstrapConfig['delegate'][k]>>
|
||||
}>
|
||||
|
||||
const createDelegate = (): DelegateListener => {
|
||||
return {
|
||||
receivedFile: new Set(),
|
||||
progress: new Set(),
|
||||
errorMessage: new Set(),
|
||||
finished: new Set(),
|
||||
started: new Set()
|
||||
}
|
||||
}
|
||||
|
||||
const delegateListener = createDelegate()
|
||||
|
||||
type InferSet<T> = T extends Set<infer U> ? U : never
|
||||
|
||||
export const createATA = async () => {
|
||||
// @ts-ignore
|
||||
const ts = await import('https://esm.sh/typescript@5.3.3')
|
||||
const ata = setupTypeAcquisition({
|
||||
projectName: 'monaco-ts',
|
||||
typescript: ts,
|
||||
logger: console,
|
||||
fetcher: (input, init) => {
|
||||
let result: any
|
||||
try {
|
||||
result = fetch(input, init)
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error)
|
||||
}
|
||||
return result
|
||||
},
|
||||
delegate: {
|
||||
receivedFile: (code, path) => {
|
||||
delegateListener.receivedFile.forEach((fn) => fn(code, path))
|
||||
},
|
||||
progress: (downloaded, estimatedTotal) => {
|
||||
delegateListener.progress.forEach((fn) => fn(downloaded, estimatedTotal))
|
||||
},
|
||||
started: () => {
|
||||
delegateListener.started.forEach((fn) => fn())
|
||||
},
|
||||
finished: (files) => {
|
||||
delegateListener.finished.forEach((fn) => fn(files))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const acquireType = (code: string) => ata(code)
|
||||
|
||||
const addListener = <T extends keyof DelegateListener>(
|
||||
event: T,
|
||||
handler: InferSet<DelegateListener[T]>
|
||||
) => {
|
||||
// @ts-ignore
|
||||
delegateListener[event].add(handler)
|
||||
}
|
||||
|
||||
const removeListener = <T extends keyof DelegateListener>(
|
||||
event: T,
|
||||
handler: InferSet<DelegateListener[T]>
|
||||
) => {
|
||||
// @ts-ignore
|
||||
delegateListener[event].delete(handler)
|
||||
}
|
||||
|
||||
const dispose = () => {
|
||||
for (const key in delegateListener) {
|
||||
delegateListener[key as keyof DelegateListener].clear()
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
acquireType,
|
||||
addListener,
|
||||
removeListener,
|
||||
dispose
|
||||
}
|
||||
}
|
||||
43
src/components/ReactPlayground/CodeEditor/Editor/editor.scss
Normal file
43
src/components/ReactPlayground/CodeEditor/Editor/editor.scss
Normal file
@@ -0,0 +1,43 @@
|
||||
.monaco-editor-light {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: var(--border);
|
||||
|
||||
.jsx-tag-angle-bracket {
|
||||
color: #800000;
|
||||
}
|
||||
|
||||
.jsx-text {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.jsx-tag-name {
|
||||
color: #800000;
|
||||
}
|
||||
|
||||
.jsx-tag-attribute-key {
|
||||
color: #f00;
|
||||
}
|
||||
}
|
||||
|
||||
.monaco-editor-vs-dark {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: var(--border);
|
||||
|
||||
.jsx-tag-angle-bracket {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.jsx-text {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.jsx-tag-name {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.jsx-tag-attribute-key {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
}
|
||||
72
src/components/ReactPlayground/CodeEditor/Editor/hooks.ts
Normal file
72
src/components/ReactPlayground/CodeEditor/Editor/hooks.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { editor, IPosition, Selection } from 'monaco-editor'
|
||||
import ScrollType = editor.ScrollType
|
||||
import { Monaco } from '@monaco-editor/react'
|
||||
import { getWorker, MonacoJsxSyntaxHighlight } from 'monaco-jsx-syntax-highlight'
|
||||
import { createATA } from '@/components/ReactPlayground/CodeEditor/Editor/ata.ts'
|
||||
|
||||
export const useEditor = () => {
|
||||
const doOpenEditor = (
|
||||
editor: editor.IStandaloneCodeEditor,
|
||||
input: { options: { selection: Selection } }
|
||||
) => {
|
||||
const selection = input.options ? input.options.selection : null
|
||||
if (selection) {
|
||||
if (
|
||||
typeof selection.endLineNumber === 'number' &&
|
||||
typeof selection.endColumn === 'number'
|
||||
) {
|
||||
editor.setSelection(selection)
|
||||
editor.revealRangeInCenter(selection, ScrollType.Immediate)
|
||||
} else {
|
||||
const position: IPosition = {
|
||||
lineNumber: selection.startLineNumber,
|
||||
column: selection.startColumn
|
||||
}
|
||||
editor.setPosition(position)
|
||||
editor.revealPositionInCenter(position, ScrollType.Immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const loadJsxSyntaxHighlight = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
|
||||
const monacoJsxSyntaxHighlight = new MonacoJsxSyntaxHighlight(getWorker(), monaco)
|
||||
const { highlighter, dispose } = monacoJsxSyntaxHighlight.highlighterBuilder({ editor })
|
||||
|
||||
editor.onDidChangeModelContent(() => {
|
||||
highlighter()
|
||||
})
|
||||
highlighter()
|
||||
|
||||
return { highlighter, dispose }
|
||||
}
|
||||
|
||||
const autoLoadExtraLib = async (
|
||||
editor: editor.IStandaloneCodeEditor,
|
||||
monaco: Monaco,
|
||||
defaultValue: string,
|
||||
onWatch: any
|
||||
) => {
|
||||
const typeHelper = await createATA()
|
||||
|
||||
onWatch(typeHelper)
|
||||
|
||||
editor.onDidChangeModelContent(() => {
|
||||
typeHelper.acquireType(editor.getValue())
|
||||
})
|
||||
|
||||
const addLibraryToRuntime = (code: string, path: string) => {
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(code, `file://${path}`)
|
||||
}
|
||||
|
||||
typeHelper.addListener('receivedFile', addLibraryToRuntime)
|
||||
typeHelper.acquireType(defaultValue)
|
||||
|
||||
return typeHelper
|
||||
}
|
||||
|
||||
return {
|
||||
doOpenEditor,
|
||||
loadJsxSyntaxHighlight,
|
||||
autoLoadExtraLib
|
||||
}
|
||||
}
|
||||
90
src/components/ReactPlayground/CodeEditor/Editor/index.tsx
Normal file
90
src/components/ReactPlayground/CodeEditor/Editor/index.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import React from 'react'
|
||||
import { editor, Selection } from 'monaco-editor'
|
||||
import MonacoEditor, { Monaco } from '@monaco-editor/react'
|
||||
import '@/components/ReactPlayground/CodeEditor/Editor/editor.scss'
|
||||
import { IEditorOptions, IFile, IFiles, ITheme } from '@/components/ReactPlayground/shared'
|
||||
import { MonacoEditorConfig } from '@/components/ReactPlayground/CodeEditor/Editor/monacoConfig'
|
||||
import { fileNameToLanguage } from '@/components/ReactPlayground/utils'
|
||||
import { useEditor } from '@/components/ReactPlayground/CodeEditor/Editor/hooks'
|
||||
|
||||
interface EditorProps {
|
||||
file: IFile
|
||||
onChange?: (code: string | undefined) => void
|
||||
options?: IEditorOptions
|
||||
theme?: ITheme
|
||||
files?: IFiles
|
||||
onJumpFile?: (fileName: string) => void
|
||||
}
|
||||
|
||||
const Editor: React.FC<EditorProps> = ({ file, files, theme, onChange, options, onJumpFile }) => {
|
||||
const editorRef = useRef<editor.IStandaloneCodeEditor>()
|
||||
const { doOpenEditor, loadJsxSyntaxHighlight } = useEditor()
|
||||
const jsxSyntaxHighlightRef = useRef<{
|
||||
highlighter: (code?: string | undefined) => void
|
||||
dispose: () => void
|
||||
}>({
|
||||
highlighter: () => undefined,
|
||||
dispose: () => undefined
|
||||
})
|
||||
|
||||
const handleOnEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
|
||||
editorRef.current = editor
|
||||
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
|
||||
void editor.getAction('editor.action.formatDocument')?.run()
|
||||
})
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
jsx: monaco.languages.typescript.JsxEmit.Preserve,
|
||||
esModuleInterop: true
|
||||
})
|
||||
|
||||
files &&
|
||||
Object.entries(files).forEach(([key]) => {
|
||||
if (!monaco.editor.getModel(monaco.Uri.parse(`file:///${key}`))) {
|
||||
monaco.editor.createModel(
|
||||
files[key].value,
|
||||
fileNameToLanguage(key),
|
||||
monaco.Uri.parse(`file:///${key}`)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
editor['_codeEditorService'].doOpenEditor = function (
|
||||
editor: editor.IStandaloneCodeEditor,
|
||||
input: { options: { selection: Selection }; resource: { path: string } }
|
||||
) {
|
||||
const path = input.resource.path
|
||||
if (!path.startsWith('/node_modules/')) {
|
||||
onJumpFile?.(path.replace('/', ''))
|
||||
doOpenEditor(editor, input)
|
||||
}
|
||||
}
|
||||
|
||||
jsxSyntaxHighlightRef.current = loadJsxSyntaxHighlight(editor, monaco)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
editorRef.current?.focus()
|
||||
jsxSyntaxHighlightRef?.current?.highlighter?.()
|
||||
}, [file.name])
|
||||
|
||||
return (
|
||||
<>
|
||||
<MonacoEditor
|
||||
theme={theme}
|
||||
path={file.name}
|
||||
className={`monaco-editor-${theme ?? 'light'}`}
|
||||
language={file.language}
|
||||
value={file.value}
|
||||
onChange={onChange}
|
||||
onMount={handleOnEditorDidMount}
|
||||
options={{ ...MonacoEditorConfig, ...options, theme: undefined }}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Editor
|
||||
@@ -0,0 +1,34 @@
|
||||
import { editor } from 'monaco-editor'
|
||||
|
||||
export const MonacoEditorConfig: editor.IStandaloneEditorConstructionOptions = {
|
||||
automaticLayout: true,
|
||||
cursorBlinking: 'smooth',
|
||||
fontLigatures: true,
|
||||
formatOnPaste: true,
|
||||
formatOnType: true,
|
||||
fontSize: 14,
|
||||
showDeprecated: true,
|
||||
showUnused: true,
|
||||
showFoldingControls: 'mouseover',
|
||||
scrollBeyondLastLine: false,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
inlineSuggest: {
|
||||
enabled: false,
|
||||
},
|
||||
fixedOverflowWidgets: true,
|
||||
smoothScrolling: true,
|
||||
smartSelect: {
|
||||
selectSubwords: true,
|
||||
selectLeadingAndTrailingWhitespace: true,
|
||||
},
|
||||
tabSize: 2,
|
||||
overviewRulerBorder: false, // 不要滚动条的边框
|
||||
// 滚动条设置
|
||||
scrollbar: {
|
||||
verticalScrollbarSize: 6, // 竖滚动条
|
||||
horizontalScrollbarSize: 6, // 横滚动条
|
||||
},
|
||||
// lineNumbers: 'off', // 隐藏控制行号
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
/*
|
||||
import React from 'react'
|
||||
import { IPlayground } from '@/components/ReactPlayground/shared.ts'
|
||||
import { PlaygroundContext } from '@/components/ReactPlayground/Provider.tsx'
|
||||
@@ -6,7 +7,7 @@ import {
|
||||
getCustomActiveFile,
|
||||
getMergedCustomFiles,
|
||||
getPlaygroundTheme
|
||||
} from '@/components/ReactPlayground/Utils.ts'
|
||||
} from '@/components/ReactPlayground/utils.ts'
|
||||
|
||||
const defaultCodeSandboxOptions = {
|
||||
theme: 'dark',
|
||||
@@ -67,7 +68,8 @@ const Playground: React.FC<IPlayground> = (props) => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
return files[ENTRY_FILE_NAME] ? <></> : undefined
|
||||
return files[ENTRY_FILE_NAME] ? <div></div> : undefined
|
||||
}
|
||||
|
||||
export default Playground
|
||||
*/
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react'
|
||||
import { IFiles, IPlaygroundContext, ITheme } from '@/components/ReactPlayground/shared.ts'
|
||||
import { MAIN_FILE_NAME } from '@/components/files.ts'
|
||||
import { MAIN_FILE_NAME } from '@/components/ReactPlayground/files.ts'
|
||||
import {
|
||||
fileNameToLanguage,
|
||||
setPlaygroundTheme,
|
||||
strToBase64
|
||||
} from '@/components/ReactPlayground/Utils.ts'
|
||||
} from '@/components/ReactPlayground/utils.ts'
|
||||
|
||||
const initialContext: Partial<IPlaygroundContext> = {
|
||||
selectedFileName: MAIN_FILE_NAME
|
||||
|
||||
68
src/components/ReactPlayground/files.ts
Normal file
68
src/components/ReactPlayground/files.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import importMap from '@/components/ReactPlayground/template/import-map.json?raw'
|
||||
import AppCss from '@/components/ReactPlayground/template/src/App.css?raw'
|
||||
import App from '@/components/ReactPlayground/template/src/App.tsx?raw'
|
||||
import main from '@/components/ReactPlayground/template/src/main.tsx?raw'
|
||||
import { IFiles } from '@/components/ReactPlayground/shared'
|
||||
import { base64ToStr } from '@/components/ReactPlayground/utils.ts'
|
||||
|
||||
export const MAIN_FILE_NAME = 'App.tsx'
|
||||
export const IMPORT_MAP_FILE_NAME = 'import-map.json'
|
||||
export const ENTRY_FILE_NAME = 'main.tsx'
|
||||
|
||||
const fileNameToLanguage = (name: string) => {
|
||||
const suffix = name.split('.').pop() || ''
|
||||
if (['js', 'jsx'].includes(suffix)) return 'javascript'
|
||||
if (['ts', 'tsx'].includes(suffix)) return 'typescript'
|
||||
if (['json'].includes(suffix)) return 'json'
|
||||
if (['css'].includes(suffix)) return 'css'
|
||||
return 'javascript'
|
||||
}
|
||||
|
||||
const getFilesFromUrl = () => {
|
||||
let files: IFiles | undefined
|
||||
try {
|
||||
if (typeof window !== 'undefined') {
|
||||
const hash = window.location.hash
|
||||
if (hash) files = JSON.parse(base64ToStr(hash?.split('#')[1]))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
export const initFiles: IFiles = getFilesFromUrl() || {
|
||||
[ENTRY_FILE_NAME]: {
|
||||
name: ENTRY_FILE_NAME,
|
||||
language: fileNameToLanguage(ENTRY_FILE_NAME),
|
||||
value: main
|
||||
},
|
||||
[MAIN_FILE_NAME]: {
|
||||
name: MAIN_FILE_NAME,
|
||||
language: fileNameToLanguage(MAIN_FILE_NAME),
|
||||
value: App
|
||||
},
|
||||
'App.css': {
|
||||
name: 'App.css',
|
||||
language: 'css',
|
||||
value: AppCss
|
||||
},
|
||||
[IMPORT_MAP_FILE_NAME]: {
|
||||
name: IMPORT_MAP_FILE_NAME,
|
||||
language: fileNameToLanguage(IMPORT_MAP_FILE_NAME),
|
||||
value: importMap
|
||||
}
|
||||
}
|
||||
|
||||
export const reactTemplateFiles = {
|
||||
[ENTRY_FILE_NAME]: {
|
||||
name: ENTRY_FILE_NAME,
|
||||
language: fileNameToLanguage(ENTRY_FILE_NAME),
|
||||
value: main
|
||||
},
|
||||
[IMPORT_MAP_FILE_NAME]: {
|
||||
name: IMPORT_MAP_FILE_NAME,
|
||||
language: fileNameToLanguage(IMPORT_MAP_FILE_NAME),
|
||||
value: importMap
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { editor } from 'monaco-editor'
|
||||
export interface IFile {
|
||||
name: string
|
||||
value: string
|
||||
language: string
|
||||
language: 'javascript' | 'typescript' | 'json' | 'css'
|
||||
active?: boolean
|
||||
hidden?: boolean
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export interface IFiles {
|
||||
[key: string]: IFile
|
||||
}
|
||||
|
||||
export type ITheme = 'light' | 'dark'
|
||||
export type ITheme = 'light' | 'vs-dark'
|
||||
|
||||
export type IImportMap = { imports: Record<string, string> }
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface ICustomFiles {
|
||||
hidden?: boolean
|
||||
}
|
||||
}
|
||||
export type IEditorOptions = editor.IStandaloneEditorConstructionOptions & any
|
||||
export type IEditorOptions = editor.IStandaloneEditorConstructionOptions
|
||||
export interface IEditorContainer {
|
||||
showFileSelector?: boolean
|
||||
fileSelectorReadOnly?: boolean
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ICustomFiles, IFiles, IImportMap, ITheme } from '@/components/ReactPlayground/shared.ts'
|
||||
import { strFromU8, strToU8, unzlibSync, zlibSync } from 'fflate'
|
||||
import { IMPORT_MAP_FILE_NAME, reactTemplateFiles } from '@/components/files.ts'
|
||||
import { ICustomFiles, IFiles, IImportMap, ITheme } from '@/components/ReactPlayground/shared'
|
||||
import { IMPORT_MAP_FILE_NAME, reactTemplateFiles } from '@/components/ReactPlayground/files'
|
||||
|
||||
export const strToBase64 = (str: string) => {
|
||||
const buffer = strToU8(str)
|
||||
@@ -22,19 +22,10 @@ export const base64ToStr = (base64: string) => {
|
||||
return ''
|
||||
}
|
||||
|
||||
export const fileNameToLanguage = (name: string) => {
|
||||
const suffix = name.split('.').pop() || ''
|
||||
if (['js', 'jsx'].includes(suffix)) return 'javascript'
|
||||
if (['ts', 'tsx'].includes(suffix)) return 'typescript'
|
||||
if (['json'].includes(suffix)) return 'json'
|
||||
if (['css'].includes(suffix)) return 'css'
|
||||
return 'javascript'
|
||||
}
|
||||
|
||||
const STORAGE_DARK_THEME = 'react-playground-prefer-dark'
|
||||
|
||||
export const setPlaygroundTheme = (theme: ITheme) => {
|
||||
localStorage.setItem(STORAGE_DARK_THEME, String(theme === 'dark'))
|
||||
localStorage.setItem(STORAGE_DARK_THEME, String(theme === 'vs-dark'))
|
||||
document
|
||||
.querySelectorAll('div[data-id="react-playground"]')
|
||||
?.forEach((item) => item.setAttribute('class', theme))
|
||||
@@ -42,7 +33,7 @@ export const setPlaygroundTheme = (theme: ITheme) => {
|
||||
|
||||
export const getPlaygroundTheme = () => {
|
||||
const isDarkTheme = JSON.parse(localStorage.getItem(STORAGE_DARK_THEME) || 'false')
|
||||
return isDarkTheme ? 'dark' : 'light'
|
||||
return isDarkTheme ? 'vs-dark' : 'light'
|
||||
}
|
||||
|
||||
const transformCustomFiles = (files: ICustomFiles) => {
|
||||
@@ -69,6 +60,17 @@ const transformCustomFiles = (files: ICustomFiles) => {
|
||||
return newFiles
|
||||
}
|
||||
|
||||
export const getCustomActiveFile = (files?: ICustomFiles) => {
|
||||
if (!files) return null
|
||||
return Object.keys(files).find((key) => {
|
||||
const tempFile = files[key]
|
||||
if (typeof tempFile !== 'string' && tempFile.active) {
|
||||
return key
|
||||
}
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
export const getMergedCustomFiles = (files?: ICustomFiles, importMap?: IImportMap) => {
|
||||
if (!files) return null
|
||||
if (importMap) {
|
||||
@@ -89,16 +91,6 @@ export const getMergedCustomFiles = (files?: ICustomFiles, importMap?: IImportMa
|
||||
}
|
||||
}
|
||||
|
||||
export const getCustomActiveFile = (files?: ICustomFiles) => {
|
||||
if (!files) return null
|
||||
return Object.keys(files).find((key) => {
|
||||
const tempFile = files[key]
|
||||
if (typeof tempFile !== 'string' && tempFile.active) {
|
||||
return key
|
||||
}
|
||||
return null
|
||||
})
|
||||
}
|
||||
export const getFilesFromUrl = () => {
|
||||
let files: IFiles | undefined
|
||||
try {
|
||||
@@ -111,3 +103,12 @@ export const getFilesFromUrl = () => {
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
export const fileNameToLanguage = (name: string) => {
|
||||
const suffix = name.split('.').pop() || ''
|
||||
if (['js', 'jsx'].includes(suffix)) return 'javascript'
|
||||
if (['ts', 'tsx'].includes(suffix)) return 'typescript'
|
||||
if (['json'].includes(suffix)) return 'json'
|
||||
if (['css'].includes(suffix)) return 'css'
|
||||
return 'javascript'
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import importMap from './template/import-map.json?raw'
|
||||
import AppCss from './template/src/App.css?raw'
|
||||
import App from './template/src/App.tsx?raw'
|
||||
import main from './template/src/main.tsx?raw'
|
||||
import { IFiles } from '@/components/ReactPlayground/shared.ts'
|
||||
import { fileNameToLanguage } from '@/components/ReactPlayground/Utils.ts'
|
||||
|
||||
export const MAIN_FILE_NAME = 'App.tsx'
|
||||
export const IMPORT_MAP_FILE_NAME = 'import-map.json'
|
||||
export const ENTRY_FILE_NAME = 'main.tsx'
|
||||
|
||||
export const initFiles: IFiles = getFilesFromUrl() || {
|
||||
[ENTRY_FILE_NAME]: {
|
||||
name: ENTRY_FILE_NAME,
|
||||
language: fileNameToLanguage(ENTRY_FILE_NAME),
|
||||
value: main
|
||||
},
|
||||
[MAIN_FILE_NAME]: {
|
||||
name: MAIN_FILE_NAME,
|
||||
language: fileNameToLanguage(MAIN_FILE_NAME),
|
||||
value: App
|
||||
},
|
||||
'App.css': {
|
||||
name: 'App.css',
|
||||
language: 'css',
|
||||
value: AppCss
|
||||
},
|
||||
[IMPORT_MAP_FILE_NAME]: {
|
||||
name: IMPORT_MAP_FILE_NAME,
|
||||
language: fileNameToLanguage(IMPORT_MAP_FILE_NAME),
|
||||
value: importMap
|
||||
}
|
||||
}
|
||||
|
||||
export const reactTemplateFiles = {
|
||||
[ENTRY_FILE_NAME]: {
|
||||
name: ENTRY_FILE_NAME,
|
||||
language: fileNameToLanguage(ENTRY_FILE_NAME),
|
||||
value: main
|
||||
},
|
||||
[IMPORT_MAP_FILE_NAME]: {
|
||||
name: IMPORT_MAP_FILE_NAME,
|
||||
language: fileNameToLanguage(IMPORT_MAP_FILE_NAME),
|
||||
value: importMap
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,19 @@
|
||||
import React from 'react'
|
||||
import Editor from '@/components/ReactPlayground/CodeEditor/Editor'
|
||||
|
||||
const OnlineEditor: React.FC = () => {
|
||||
return <></>
|
||||
return (
|
||||
<>
|
||||
<Editor
|
||||
theme={'light'}
|
||||
file={{
|
||||
name: 'App.tsx',
|
||||
language: 'typescript',
|
||||
value: 'const a = () => {}'
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default OnlineEditor
|
||||
|
||||
Reference in New Issue
Block a user