Skip to content

Commit

Permalink
feat: enable btns after monaco editor loaded (#10)
Browse files Browse the repository at this point in the history
* feat: enable btns after monaco loaded

* perf: reduce bundle size

* chore: move favicon to assets
  • Loading branch information
ZLY201 authored Oct 27, 2023
1 parent 45f4b64 commit c67a927
Show file tree
Hide file tree
Showing 17 changed files with 510 additions and 423 deletions.
1 change: 1 addition & 0 deletions assets/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion config/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"zh-cn": "运行示例"
},
"submit_code": {
"en": "submit",
"en": "Submit",
"zh-cn": "提交"
},
"description": {
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "typescript-tutorial-exercises",
"version": "1.0.0",
"main": "index.js",
"author": {
"name": "ZLY201",
"email": "[email protected]"
Expand All @@ -11,8 +10,8 @@
"reset": "rm -rf node_modules",
"setup": "yarn reset && yarn",
"clean": "rm -rf dist",
"dev": "rspack serve --watch --env development",
"build": "yarn clean && rspack build --env production",
"dev": "rspack serve --watch --mode=development",
"build": "yarn clean && rspack build --mode=production",
"lint": "eslint --fix --color --cache --quiet .",
"prepare": "husky install"
},
Expand All @@ -29,6 +28,7 @@
"not dead",
"not op_mini all"
],
"sideEffects": false,
"dependencies": {
"@arco-design/theme-line": "^0.0.3",
"@arco-design/web-react": "^2.53.2",
Expand Down
30 changes: 12 additions & 18 deletions rspack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@ import path from 'path';
import type { Configuration } from '@rspack/cli';
import { ArcoDesignPlugin } from '@arco-plugins/unplugin-react';

export default function createRspackConfig(env: {
production?: boolean;
}): Configuration {
const { production } = env;
export default function createRspackConfig(): Configuration {
const mode = process.env.NODE_ENV as Configuration['mode'];
return {
mode,
context: __dirname,
entry: {
main: './src/main.tsx',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash:8].bundle.js',
chunkFilename: '[name].[contenthash:8].bundle.js',
cssChunkFilename: '[name].[contenthash:8].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
builtins: {
html: [
{
template: './html/index.html',
minify: true,
template: './html/index.html',
favicon: './assets/favicon.svg',
},
],
},
devtool: production ? false : 'source-map',
devtool: mode === 'production' ? false : 'source-map',
resolve: {
alias: {
'@config': path.resolve(__dirname, './config'),
Expand Down Expand Up @@ -75,21 +75,15 @@ export default function createRspackConfig(env: {
},
optimization: {
splitChunks: {
minSize: 500 * 1024,
maxSize: 1000 * 1024,
chunks: 'all',
minSize: 100 * 1024,
maxSize: 200 * 1024,
cacheGroups: {
common0: {
common: {
chunks: 'all',
test: /[\\/]node_modules[\\/](react|react-dom|@arco-design[\\/]web-react)[\\/]/,
priority: 100,
name: 'common0',
reuseExistingChunk: true,
},
common1: {
chunks: 'all',
test: /[\\/]node_modules[\\/](react-syntax-highlighter|refractor)[\\/]/,
priority: 100,
name: 'common1',
name: 'common',
reuseExistingChunk: true,
},
vendors: {
Expand Down
2 changes: 1 addition & 1 deletion src/components/AutoResizer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function decorateWithAutoResize<T>(
height: newHeight,
};
});
}, 100);
}, 200);
window.addEventListener('resize', updateSize, { passive: true });
const interval = setInterval(updateSize, 200);
return function () {
Expand Down
6 changes: 5 additions & 1 deletion src/components/Markdown/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import {
oneDark,
oneLight,
} from 'react-syntax-highlighter/dist/esm/styles/prism';
import ts from 'react-syntax-highlighter/dist/esm/languages/prism/typescript';
import ReactMarkdown from 'react-markdown';
import { Setting } from '@src/utils/setting';
import styles from './index.module.less';

SyntaxHighlighter.registerLanguage('ts', ts);
SyntaxHighlighter.registerLanguage('typescript', ts);

const Markdown = function (props: {
content: string;
theme: Setting['theme'];
Expand Down
16 changes: 7 additions & 9 deletions src/modules/Editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,12 @@ function Editor() {
<IconUndo />
</a>
</div>
{loading && (
<Skeleton
className={styles.skeleton}
text={{ rows: 3 }}
animation={true}
/>
)}
{!loading && (
<Skeleton
className={styles.skeleton}
text={{ rows: 3 }}
animation={true}
loading={loading}
>
<div className={styles['monaco-wrapper']}>
<MonacoEditor
namespace={currentProblem.key}
Expand All @@ -95,7 +93,7 @@ function Editor() {
setting={setting}
/>
</div>
)}
</Skeleton>
</div>
);
}
Expand Down
32 changes: 20 additions & 12 deletions src/modules/Editor/monaco-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import {
} from '@src/utils/problems';
import { Setting } from '@src/utils/setting';
import emitter from '@src/utils/emit';
import { assignMonacoInstance, validateMonacoModel } from '@src/utils/monaco';
import {
assignMonacoInstance,
setMonacoEditorStatus,
validateMonacoModel,
} from '@src/utils/monaco';
import styles from './index.module.less';

export interface MonacoEditorProps {
Expand All @@ -25,23 +29,20 @@ export interface MonacoEditorProps {
setting: Setting;
}

interface Models {
[key: string]: editor.IModel;
}

const MonacoEditor = decorateWithAutoResize(
class extends React.Component<MonacoEditorProps> {
protected instance: editor.IStandaloneCodeEditor | null = null;
protected models: Models = {};
protected models: Record<string, editor.IModel | undefined> = {};
protected viewStates: { [filename: string]: editor.ICodeEditorViewState } =
{};
protected lastUpdates: { [filename: string]: string } = {};
updateTabSize = (prev: Setting['tabSize'], next: Setting['tabSize']) => {
const model = this.models[ProblemFiles.template];
model.setValue(formatCodeByUpdateTabSize(model.getValue(), prev, next));
model?.setValue(
formatCodeByUpdateTabSize(model?.getValue() || '', prev, next),
);
};
beforeMount = (monaco: Monaco) => {
assignMonacoInstance(monaco);
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
strict: true,
target: monaco.languages.typescript.ScriptTarget.ES2018,
Expand All @@ -61,6 +62,8 @@ const MonacoEditor = decorateWithAutoResize(
inherit: true,
rules: [],
});
monaco.editor.setTheme(this.props.setting.theme);
assignMonacoInstance(monaco);
for (const [filename, { content }] of Object.entries(this.props.raw)) {
this.lastUpdates[filename] = content;
const model = monaco.editor.createModel(
Expand All @@ -80,12 +83,16 @@ const MonacoEditor = decorateWithAutoResize(
};
onMount = (instance: editor.IStandaloneCodeEditor) => {
this.instance = instance;
this.instance.updateOptions(this.props.setting);
setMonacoEditorStatus(true);
emitter.emit('monacoEditorLoaded');
emitter.on('tabSizeChange', this.updateTabSize);
};
componentWillUnmount() {
setMonacoEditorStatus(false);
emitter.off('tabSizeChange', this.updateTabSize);
for (const filename of Object.keys(this.models)) {
this.models[filename].dispose();
this.models[filename]?.dispose();
}
if (this.instance) {
this.instance.dispose();
Expand All @@ -105,7 +112,7 @@ const MonacoEditor = decorateWithAutoResize(
const model = this.models[newSelectedFilename];
this.viewStates[prevProps.selectedFilename] =
this.instance.saveViewState()!;
this.instance.setModel(model);
model && this.instance.setModel(model);
this.instance.updateOptions({
readOnly: Boolean(this.props.raw[newSelectedFilename].readOnly),
});
Expand All @@ -125,12 +132,12 @@ const MonacoEditor = decorateWithAutoResize(
for (const [filename, value] of Object.entries(this.props.raw)) {
if (value.content !== this.lastUpdates[filename]) {
this.lastUpdates[filename] = value.content;
this.models[filename].setValue(value.content);
this.models[filename]?.setValue(value.content);
}
}
}
for (const filename of Object.keys(this.props.raw)) {
this.models[filename].updateOptions(this.props.setting);
this.models[filename]?.updateOptions(this.props.setting);
if (!filename.includes('node_modules')) {
validateMonacoModel(this.models[filename]);
}
Expand All @@ -151,6 +158,7 @@ const MonacoEditor = decorateWithAutoResize(
}
beforeMount={this.beforeMount}
onMount={this.onMount}
theme={this.props.setting.theme === 'light' ? 'vs' : 'vs-dark'}
options={{
...this.props.setting,
renderValidationDecorations: 'on',
Expand Down
2 changes: 1 addition & 1 deletion src/modules/Header/ProblemList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const ProblemList = function () {
}

useEffect(function () {
emitter.on('submit-code', () => setState(prev => !prev));
emitter.on('submitCode', () => setState(prev => !prev));
}, []);

return (
Expand Down
2 changes: 1 addition & 1 deletion src/modules/Question/Description.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const Description = function () {
);

useEffect(function () {
emitter.on('submit-code', () => setState(prev => !prev));
emitter.on('submitCode', () => setState(prev => !prev));
}, []);

return (
Expand Down
6 changes: 3 additions & 3 deletions src/modules/Question/Records.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const Records = function () {
item.time,
);
if (success) {
emitter.emit('delete-problem-record');
emitter.emit('deleteProblemRecord');
Message.success(
i18nJson['successfully_delete'][setting.language],
);
Expand All @@ -137,8 +137,8 @@ const Records = function () {
];

useEffect(function () {
emitter.on('submit-code', () => setState(prev => !prev));
emitter.on('delete-problem-record', () => setState(prev => !prev));
emitter.on('submitCode', () => setState(prev => !prev));
emitter.on('deleteProblemRecord', () => setState(prev => !prev));
}, []);

const records = useMemo(
Expand Down
33 changes: 30 additions & 3 deletions src/modules/Results/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import {
ProblemTestReplaceVal,
} from '@src/utils/problems';
import i18nJson from '@config/i18n.json';
import { monacoInstance, validateMonacoModel } from '@src/utils/monaco';
import {
monacoEditorLoaded,
monacoInstance,
validateMonacoModel,
} from '@src/utils/monaco';
import styles from './index.module.less';

const enum MainTab {
Expand All @@ -41,6 +45,12 @@ const Results = function () {
const [cases, setCases] = useState(noCases ? [NULL_CASE] : originCases);
const [testRaw, setTestRaw] = useState<string | undefined>(undefined);
const [casesErrors, setCasesErrors] = useState<string[][]>([]);
const [btnDisabled, setBtnDisabled] = useState(true);

const monacoEditorStatusListener = useCallback(
() => setBtnDisabled(false),
[setBtnDisabled],
);

const updateData = useCallback(
debounce(async function (problem: Problem) {
Expand All @@ -63,6 +73,17 @@ const Results = function () {
[currentProblem],
);

useEffect(function () {
if (!monacoEditorLoaded) {
emitter.on('monacoEditorLoaded', monacoEditorStatusListener);
} else {
setBtnDisabled(false);
}
return function () {
emitter.off('monacoEditorLoaded', monacoEditorStatusListener);
};
}, []);

async function run() {
if (!monacoInstance || !testRaw) return;
setLoading(true);
Expand Down Expand Up @@ -139,7 +160,7 @@ const Results = function () {
],
status,
});
emitter.emit('submit-code');
emitter.emit('submitCode');
setActiveMainTab(MainTab.result);
setLoading(false);
}
Expand Down Expand Up @@ -259,13 +280,19 @@ const Results = function () {
</CustomTabs.TabPane>
</CustomTabs>
<div className={styles.footer}>
<Button type={'primary'} className={styles.btn} onClick={run}>
<Button
type={'primary'}
onClick={run}
disabled={btnDisabled}
className={styles.btn}
>
{i18nJson['run_code'][setting.language]}
</Button>
<Button
type={'primary'}
status={'success'}
onClick={onSubmit}
disabled={btnDisabled}
className={styles.btn}
>
{i18nJson['submit_code'][setting.language]}
Expand Down
16 changes: 16 additions & 0 deletions src/types/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
declare module 'event-emitter' {
declare namespace ee {
interface Emitter<
const T extends Record<string, (...args: any[]) => void>,
const K extends keyof T = keyof T,
> {
emit: (type: K, ...args: Parameters<T[K]>) => void;
off: (type: K, listener: T[K]) => void;
on: (type: K, listener: T[K]) => void;
once: (type: K, listener: T[K]) => void;
}
}
declare function ee<T>(): ee.Emitter<T>;

export = ee;
}
Loading

0 comments on commit c67a927

Please sign in to comment.