From d98fd522cf5e95802dbd47b4f16f345e417bd41d Mon Sep 17 00:00:00 2001 From: FatttSnake Date: Tue, 9 Jan 2024 14:53:55 +0800 Subject: [PATCH] Optimize CodeEditor --- .../CodeEditor/FileSelector/index.tsx | 20 ++++- .../Playground/CodeEditor/code-editor.scss | 13 +++- .../Playground/CodeEditor/index.tsx | 78 +++++++++++-------- src/components/Playground/files.ts | 57 +------------- src/components/Playground/shared.ts | 62 --------------- 5 files changed, 73 insertions(+), 157 deletions(-) diff --git a/src/components/Playground/CodeEditor/FileSelector/index.tsx b/src/components/Playground/CodeEditor/FileSelector/index.tsx index 5dc817e..b32547d 100644 --- a/src/components/Playground/CodeEditor/FileSelector/index.tsx +++ b/src/components/Playground/CodeEditor/FileSelector/index.tsx @@ -48,6 +48,10 @@ const FileSelector: React.FC = ({ } const addTab = () => { + if (hasEditing) { + return + } + setTabs([...tabs, getMaxSequenceTabName(tabs)]) setCreating(true) setTimeout(() => { @@ -74,6 +78,10 @@ const FileSelector: React.FC = ({ } const editImportMap = () => { + if (hasEditing) { + return + } + onChange?.(IMPORT_MAP_FILE_NAME) } @@ -104,17 +112,23 @@ const FileSelector: React.FC = ({ return false } + onError?.('') + return true } const handleOnRemove = (fileName: string) => { onRemoveFile?.(fileName) if (fileName === selectedFileName) { - const index = Object.keys(files).indexOf(fileName) - 1 + const keys = Object.keys(files).filter( + (item) => + ![IMPORT_MAP_FILE_NAME, ENTRY_FILE_NAME].includes(item) && !files[item].hidden + ) + const index = keys.indexOf(fileName) - 1 if (index >= 0) { - handleOnClickTab(Object.keys(files)[index]) + handleOnClickTab(keys[index]) } else { - handleOnClickTab(Object.keys(files)[1]) + handleOnClickTab(keys[1]) } } } diff --git a/src/components/Playground/CodeEditor/code-editor.scss b/src/components/Playground/CodeEditor/code-editor.scss index 9ff6eaf..d8da64c 100644 --- a/src/components/Playground/CodeEditor/code-editor.scss +++ b/src/components/Playground/CodeEditor/code-editor.scss @@ -1,5 +1,14 @@ [data-component=playground-code-editor] { - section { - height: 0 !important; + width: 100%; + height: 100%; + + .playground-code-editor-message { + position: absolute; + bottom: 0; + width: 100%; + color: white; + background-color: #FF4D4FAA; + padding: 5px 10px; + font-size: 1.6em; } } \ No newline at end of file diff --git a/src/components/Playground/CodeEditor/index.tsx b/src/components/Playground/CodeEditor/index.tsx index 8e7b635..97991a5 100644 --- a/src/components/Playground/CodeEditor/index.tsx +++ b/src/components/Playground/CodeEditor/index.tsx @@ -5,7 +5,6 @@ import { IEditorOptions, IFiles, ITheme } from '@/components/Playground/shared' import { fileNameToLanguage } from '@/components/Playground/files' import FileSelector from '@/components/Playground/CodeEditor/FileSelector' import Editor from '@/components/Playground/CodeEditor/Editor' -import FitFullscreen from '@/components/common/FitFullscreen' import FlexBox from '@/components/common/FlexBox' interface CodeEditorProps { @@ -21,6 +20,7 @@ interface CodeEditorProps { 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: React.FC = ({ @@ -35,9 +35,11 @@ const CodeEditor: React.FC = ({ onRemoveFile, onRenameFile, onChangeFileContent, + onError, ...props }) => { const [selectedFileName, setSelectedFileName] = useState(props.selectedFileName) + const [errorMsg, setErrorMsg] = useState('') const handleOnChangeSelectedFile = (fileName: string) => { if (onSelectedFileChange) { @@ -82,7 +84,15 @@ const CodeEditor: React.FC = ({ onRenameFile?.(newFileName, oldFileName, newFiles) } - const handleOnChangeFileContent = (code = '') => { + const handleOnError = (msg: string) => { + if (onError) { + onError(msg) + } else { + setErrorMsg(msg) + } + } + + const handleOnChangeFileContent = _.debounce((code = '') => { if (!files[onSelectedFileChange ? props.selectedFileName : selectedFileName]) { return } @@ -93,42 +103,42 @@ const CodeEditor: React.FC = ({ onSelectedFileChange ? props.selectedFileName : selectedFileName, clone ) - } + }, 250) return ( <> - - - + + - - - + ) || + !files[onSelectedFileChange ? props.selectedFileName : selectedFileName] + } + onChange={handleOnChangeFileContent} + /> + {errorMsg &&
{errorMsg}
} + ) } diff --git a/src/components/Playground/files.ts b/src/components/Playground/files.ts index 0f90245..c1d441c 100644 --- a/src/components/Playground/files.ts +++ b/src/components/Playground/files.ts @@ -3,7 +3,7 @@ import importMap from '@/components/Playground/template/import-map.json?raw' import AppCss from '@/components/Playground/template/src/App.css?raw' import App from '@/components/Playground/template/src/App.tsx?raw' import main from '@/components/Playground/template/src/main.tsx?raw' -import { ICustomFiles, IFiles, IImportMap } from '@/components/Playground/shared' +import { IFiles } from '@/components/Playground/shared' export const MAIN_FILE_NAME = 'App.tsx' export const IMPORT_MAP_FILE_NAME = 'import-map.json' @@ -38,61 +38,6 @@ export const base64ToStr = (base64: string) => { return '' } -const transformCustomFiles = (files: ICustomFiles) => { - const newFiles: IFiles = {} - Object.keys(files).forEach((key) => { - const tempFile = files[key] - if (typeof tempFile === 'string') { - newFiles[key] = { - name: key, - language: fileNameToLanguage(key), - value: tempFile - } - } else { - newFiles[key] = { - name: key, - language: fileNameToLanguage(key), - value: tempFile.code, - hidden: tempFile.hidden, - active: tempFile.active - } - } - }) - - 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) { - return { - ...reactTemplateFiles, - ...transformCustomFiles(files), - [IMPORT_MAP_FILE_NAME]: { - name: IMPORT_MAP_FILE_NAME, - language: 'json', - value: JSON.stringify(importMap, null, 2) - } - } - } else { - return { - ...reactTemplateFiles, - ...transformCustomFiles(files) - } - } -} - export const getFilesFromUrl = () => { let files: IFiles | undefined try { diff --git a/src/components/Playground/shared.ts b/src/components/Playground/shared.ts index 0c7354e..dfcde1e 100644 --- a/src/components/Playground/shared.ts +++ b/src/components/Playground/shared.ts @@ -1,4 +1,3 @@ -import React from 'react' import { editor } from 'monaco-editor' export type ILanguage = 'javascript' | 'typescript' | 'json' | 'css' | 'xml' @@ -7,7 +6,6 @@ export interface IFile { name: string value: string language: ILanguage - active?: boolean hidden?: boolean } @@ -17,64 +15,4 @@ export interface IFiles { export type ITheme = 'light' | 'vs-dark' -export type IImportMap = { imports: Record } - -export interface ICustomFiles { - [key: string]: - | string - | { - code: string - active?: boolean - hidden?: boolean - } -} export type IEditorOptions = editor.IStandaloneEditorConstructionOptions -export interface IEditorContainer { - showFileSelector?: boolean - fileSelectorReadOnly?: boolean - options?: IEditorOptions -} - -export interface IOutput { - showCompileOutput?: boolean -} - -export interface ISplitPane { - children?: React.ReactNode[] - defaultSizes?: number[] -} - -export type IPlayground = { - width?: string | number - height?: string | number - theme?: ITheme - importMap?: IImportMap - files?: ICustomFiles - options?: { - lineNumbers?: boolean - fontSize?: number - tabSize?: number - } - showHeader?: boolean - border?: boolean - onFilesChange?: (url: string) => void - saveOnUrl?: boolean - autorun?: boolean - // recompileDelay -} & Omit & - IOutput & - ISplitPane - -export interface IPlaygroundContext { - files: IFiles - filesHash: string - theme: ITheme - selectedFileName: string - setSelectedFileName: (fileName: string) => void - setTheme: (theme: ITheme) => void - setFiles: (files: IFiles) => void - addFile: (fileName: string) => void - removeFile: (fileName: string) => void - changeFileName: (oldFieldName: string, newFieldName: string) => void - changeTheme: (theme: ITheme) => void -}