mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat(client): using monaco editor in interactive editor (#64601)
Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
.monaco-editor-wrapper {
|
||||
padding-block-start: 8px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.monaco-editor .line-numbers {
|
||||
color: #858591 !important;
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import React from 'react';
|
||||
import Loadable from '@loadable/component';
|
||||
import type { EditorWillMount, monaco } from 'react-monaco-editor';
|
||||
import { useActiveCode, useSandpack } from '@codesandbox/sandpack-react';
|
||||
import './custom-monaco-editor.css';
|
||||
|
||||
const MonacoEditor = Loadable(() => import('react-monaco-editor'));
|
||||
|
||||
const CustomMonacoEditor = () => {
|
||||
const { code, updateCode } = useActiveCode();
|
||||
const { sandpack } = useSandpack();
|
||||
|
||||
const getLanguage = (filePath: string): string => {
|
||||
const extension = filePath.split('.').pop();
|
||||
|
||||
switch (extension) {
|
||||
case 'js':
|
||||
case 'jsx':
|
||||
return 'javascript';
|
||||
case 'ts':
|
||||
case 'tsx':
|
||||
return 'typescript';
|
||||
case 'html':
|
||||
return 'html';
|
||||
case 'css':
|
||||
return 'css';
|
||||
default:
|
||||
return 'plaintext';
|
||||
}
|
||||
};
|
||||
// theme
|
||||
const FCC_DARK_CUSTOM: monaco.editor.IStandaloneThemeData = {
|
||||
base: 'vs-dark',
|
||||
inherit: true,
|
||||
rules: [
|
||||
{ token: 'comment', foreground: '858591', fontStyle: 'italic' },
|
||||
{ token: 'keyword', foreground: 'dbb8ff' },
|
||||
{ token: 'tag', foreground: 'f07178' },
|
||||
{ token: 'punctuation', foreground: '99c9ff' },
|
||||
{ token: 'definition', foreground: 'ffffff' },
|
||||
{ token: 'property', foreground: '99c9ff' },
|
||||
{ token: 'static', foreground: 'f78c6c' },
|
||||
{ token: 'string', foreground: 'acd157' },
|
||||
{ token: 'number', foreground: 'f78c6c' },
|
||||
{ token: 'variable', foreground: 'ffffff' },
|
||||
{ token: 'type', foreground: 'dbb8ff' },
|
||||
{ token: 'function', foreground: 'ffffff' },
|
||||
{ token: 'identifier', foreground: 'ffffff' },
|
||||
{ token: 'regexp', foreground: 'acd157' },
|
||||
{ token: 'delimiter', foreground: 'ffffff' },
|
||||
{ token: 'attribute.name', foreground: '99c9ff' },
|
||||
{ token: 'attribute.value', foreground: 'acd157' },
|
||||
{ token: 'annotation', foreground: 'dbb8ff' },
|
||||
{ token: 'constant', foreground: 'f78c6c' },
|
||||
{ token: 'class', foreground: 'dbb8ff' },
|
||||
{ token: 'interface', foreground: 'dbb9ff' },
|
||||
{ token: 'namespace', foreground: 'dbb8ff' },
|
||||
{ token: 'enum', foreground: 'dbb8ff' },
|
||||
{ token: 'operator', foreground: 'ffffff' }
|
||||
],
|
||||
colors: {
|
||||
'editor.background': '#0a0a23', // surface1
|
||||
'editor.foreground': '#ffffff', // base (plain syntax)
|
||||
'editorCursor.foreground': '#ffffff',
|
||||
'editor.selectionBackground': '#3b3b4f',
|
||||
'editor.lineHighlightBackground': '#3b3b4f', // surface3
|
||||
'editorBracketMatch.border': '#dbb8ff'
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditor: EditorWillMount = monaco => {
|
||||
monaco.editor.defineTheme('fcc-dark', FCC_DARK_CUSTOM);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='monaco-editor-wrapper'>
|
||||
<MonacoEditor
|
||||
width='100%'
|
||||
height='100%'
|
||||
language={getLanguage(sandpack.activeFile)}
|
||||
theme='fcc-dark'
|
||||
editorWillMount={handleEditor}
|
||||
key={sandpack.activeFile}
|
||||
defaultValue={code}
|
||||
onChange={value => {
|
||||
updateCode(value || '');
|
||||
}}
|
||||
options={{
|
||||
lineNumbersMinChars: 2,
|
||||
minimap: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
automaticLayout: true
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
CustomMonacoEditor.displayname = 'CustomMonacoEditor';
|
||||
|
||||
export default CustomMonacoEditor;
|
||||
@@ -27,3 +27,39 @@
|
||||
color: var(--gray-90) !important;
|
||||
border-color: var(--gray-90) !important;
|
||||
}
|
||||
|
||||
.interactive-layout {
|
||||
display: flex;
|
||||
flex-direction: row; /* Default for desktop: side-by-side */
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
.interactive-editor-column {
|
||||
flex: 1.5 !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.interactive-preview-column {
|
||||
flex: 1 !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.sp-preview {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.interactive-layout {
|
||||
flex-direction: column;
|
||||
height: auto !important; /* Allow the height to expand as content stacks */
|
||||
}
|
||||
|
||||
.interactive-editor-column,
|
||||
.interactive-preview-column {
|
||||
width: 100%;
|
||||
/* Override desktop flex value */
|
||||
flex: 0 0 auto !important;
|
||||
/* Set a reasonable mobile height for the editor */
|
||||
height: 400px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Sandpack } from '@codesandbox/sandpack-react';
|
||||
import {
|
||||
FileTabs,
|
||||
SandpackConsole,
|
||||
SandpackLayout,
|
||||
SandpackPreview,
|
||||
SandpackProvider,
|
||||
SandpackStack
|
||||
} from '@codesandbox/sandpack-react';
|
||||
import { freeCodeCampDark } from '@codesandbox/sandpack-themes';
|
||||
import './interactive-editor.css';
|
||||
import CustomMonacoEditor from './custom-monaco-editor';
|
||||
|
||||
export interface InteractiveFile {
|
||||
ext: string;
|
||||
@@ -61,7 +69,7 @@ const InteractiveEditor = ({ files }: Props) => {
|
||||
className='interactive-editor-wrapper'
|
||||
data-playwright-test-label='sp-interactive-editor'
|
||||
>
|
||||
<Sandpack
|
||||
<SandpackProvider
|
||||
template={
|
||||
got('tsx')
|
||||
? 'react-ts'
|
||||
@@ -83,15 +91,34 @@ const InteractiveEditor = ({ files }: Props) => {
|
||||
},
|
||||
syntax: freeCodeCampDarkSyntax
|
||||
}}
|
||||
options={{
|
||||
editorHeight: 450,
|
||||
editorWidthPercentage: 60,
|
||||
showConsole: showConsole,
|
||||
showConsoleButton: showConsole,
|
||||
layout: layout,
|
||||
showLineNumbers: true
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<SandpackLayout className='interactive-layout'>
|
||||
<SandpackStack className='interactive-editor-column'>
|
||||
{files.length > 1 && <FileTabs />}
|
||||
<CustomMonacoEditor />
|
||||
</SandpackStack>
|
||||
|
||||
<SandpackStack className='interactive-preview-column'>
|
||||
{layout === 'preview' ? (
|
||||
showConsole ? (
|
||||
<>
|
||||
<SandpackPreview style={{ flex: 1.5 }} />
|
||||
<SandpackConsole
|
||||
style={{
|
||||
flex: 1,
|
||||
overflow: 'scroll'
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<SandpackPreview />
|
||||
)
|
||||
) : (
|
||||
<SandpackConsole standalone={true} />
|
||||
)}
|
||||
</SandpackStack>
|
||||
</SandpackLayout>
|
||||
</SandpackProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user