Files
oxygen-ui/src/components/Playground/CodeEditor/index.tsx

176 lines
5.8 KiB
TypeScript

import { Monaco } from '@monaco-editor/react'
import { editor } from 'monaco-editor'
import _ from 'lodash'
import useStyles from '@/components/Playground/CodeEditor/index.style'
import FlexBox from '@/components/common/FlexBox'
import { IEditorOptions, IFiles, ITsconfig } from '@/components/Playground/shared'
import {
fileNameToLanguage,
getFileNameList,
IMPORT_MAP_FILE_NAME,
TS_CONFIG_FILE_NAME
} from '@/components/Playground/files'
import FileSelector from '@/components/Playground/CodeEditor/FileSelector'
import Editor, { ExtraLib } from '@/components/Playground/CodeEditor/Editor'
interface CodeEditorProps {
isDarkMode?: boolean
showFileSelector?: boolean
tsconfig?: ITsconfig
files: IFiles
readonly?: boolean
readonlyFiles?: string[]
notRemovable?: string[]
selectedFileName?: string
options?: IEditorOptions
extraLibs?: ExtraLib[]
onEditorDidMount?: (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => void
onSelectedFileChange?: (fileName: string) => void
onAddFile?: (fileName: string, files: IFiles) => void
onRemoveFile?: (fileName: string, files: IFiles) => void
onRenameFile?: (newFileName: string, oldFileName: string, files: IFiles) => void
onChangeFileContent?: (content: string, fileName: string, files: IFiles) => void
onError?: (msg: string) => void
}
const CodeEditor = ({
isDarkMode,
showFileSelector = true,
tsconfig,
files,
readonly,
readonlyFiles,
notRemovable,
options,
onSelectedFileChange,
onAddFile,
onRemoveFile,
onRenameFile,
onChangeFileContent,
onError,
extraLibs,
onEditorDidMount,
...props
}: CodeEditorProps) => {
const { styles } = useStyles()
const filteredFilesName = getFileNameList(files).filter(
(item) => ![IMPORT_MAP_FILE_NAME, TS_CONFIG_FILE_NAME].includes(item) && !files[item].hidden
)
const propsSelectedFileName =
props.selectedFileName || (filteredFilesName.length ? filteredFilesName[0] : '')
const [selectedFileName, setSelectedFileName] = useState(propsSelectedFileName)
const [errorMsg, setErrorMsg] = useState('')
const handleOnChangeSelectedFile = (fileName: string) => {
if (onSelectedFileChange) {
onSelectedFileChange(fileName)
} else {
setSelectedFileName(fileName)
}
}
const handleOnRemoveFile = (fileName: string) => {
const clone = _.cloneDeep(files)
delete clone[fileName]
onRemoveFile?.(fileName, clone)
}
const handleOnAddFile = (fileName: string) => {
const clone = _.cloneDeep(files)
clone[fileName] = {
name: fileName,
language: fileNameToLanguage(fileName),
value: ''
}
onAddFile?.(fileName, clone)
handleOnChangeSelectedFile(fileName)
}
const handleOnUpdateFileName = (newFileName: string, oldFileName: string) => {
if (!files[oldFileName] || !newFileName) {
return
}
const { [oldFileName]: value, ...rest } = files
const newFile: IFiles = {
[newFileName]: {
...value,
language: fileNameToLanguage(newFileName),
name: newFileName
}
}
const newFiles: IFiles = { ...rest, ...newFile }
onRenameFile?.(newFileName, oldFileName, newFiles)
}
const handleOnError = (msg: string) => {
if (onError) {
onError(msg)
} else {
setErrorMsg(msg)
}
}
const handleOnChangeFileContent = (code: string = '') => {
if (!files[onSelectedFileChange ? propsSelectedFileName : selectedFileName]) {
return
}
const clone = _.cloneDeep(files)
clone[onSelectedFileChange ? propsSelectedFileName : selectedFileName].value = code ?? ''
onChangeFileContent?.(
code,
onSelectedFileChange ? propsSelectedFileName : selectedFileName,
clone
)
}
return (
<>
<FlexBox className={styles.root}>
{showFileSelector && (
<FileSelector
files={files}
readonly={readonly}
notRemovableFiles={notRemovable}
selectedFileName={
onSelectedFileChange ? propsSelectedFileName : selectedFileName
}
onChange={handleOnChangeSelectedFile}
onRemoveFile={handleOnRemoveFile}
onUpdateFileName={handleOnUpdateFileName}
onAddFile={handleOnAddFile}
onError={handleOnError}
/>
)}
<Editor
isDarkMode={isDarkMode}
tsconfig={tsconfig}
selectedFileName={
onSelectedFileChange ? propsSelectedFileName : selectedFileName
}
files={files}
options={options}
readonly={
readonly ||
readonlyFiles?.includes(
onSelectedFileChange ? propsSelectedFileName : selectedFileName
) ||
!files[onSelectedFileChange ? propsSelectedFileName : selectedFileName]
}
onChange={handleOnChangeFileContent}
onJumpFile={handleOnChangeSelectedFile}
extraLibs={extraLibs}
onEditorDidMount={onEditorDidMount}
/>
{errorMsg && <div className={styles.errorMessage}>{errorMsg}</div>}
</FlexBox>
</>
)
}
CodeEditor.Editor = Editor
CodeEditor.FileSelector = FileSelector
export default CodeEditor