ui: add editor setting (#622)

pull/630/head
undefined 1 year ago committed by GitHub
parent 3615d13ce2
commit 2dabe3194e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,11 +8,16 @@ interface ScratchpadOptions {
value?: string;
language?: string;
handleUpdateCode?: (str: string, event: monaco.editor.IModelContentChangedEvent) => void;
settings?: any;
pendingCommand?: string;
commandDone?: () => void;
}
export default connect((state: any) => ({
value: state.editor.code,
language: window.LANGS[state.editor.lang]?.monaco,
settings: state.ui.settings.config,
pendingCommand: state.ui.pendingCommand,
}), (dispatch) => ({
handleUpdateCode: (code: string) => {
dispatch({
@ -20,6 +25,12 @@ export default connect((state: any) => ({
payload: code,
});
},
commandDone: () => {
dispatch({
type: 'SCRATCHPAD_TRIGGER_EDITOR_COMMAND',
payload: { command: '' },
});
},
}))(class MonacoEditor extends React.PureComponent<ScratchpadOptions> {
disposable: monaco.IDisposable[] = [];
__prevent_trigger_change_event = false;
@ -36,8 +47,8 @@ export default connect((state: any) => ({
if (this.containerElement) {
const config: monaco.editor.IStandaloneEditorConstructionOptions = {
theme: 'vs-light',
...customOptions,
fontFamily: UserContext.codeFontFamily,
...customOptions,
lineNumbers: 'on',
glyphMargin: true,
lightbulb: { enabled: true },
@ -88,6 +99,14 @@ export default connect((state: any) => ({
this.model = monaco.editor.getModel(uri) || monaco.editor.createModel(val, language, uri);
editor.setModel(this.model);
}
if (editor && this.props.settings) {
editor.updateOptions(this.props.settings);
}
if (this.props.pendingCommand) {
editor.focus();
editor.getAction(this.props.pendingCommand)?.run();
this.props.commandDone();
}
}
componentWillUnmount() {

@ -0,0 +1,48 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { i18n } from 'vj/utils';
import { RootState } from './reducers';
export default function ScratchpadSettings() {
const config = useSelector((state: RootState) => state.ui.settings.config);
const parsed = React.useMemo(() => {
const settings = { fontSize: 14, tabSize: 4 };
try {
Object.assign(settings, config);
} catch (e) { }
return settings;
}, [config]);
const dispatch = useDispatch();
function dispatcher(setting: string, numeric = false) {
return (ev) => {
const val = ev?.target?.value || ev;
dispatch({ type: 'SCRATCHPAD_SETTING_UPDATE', payload: { setting, value: numeric ? +val : val } });
};
}
function openThemeSelect() {
dispatch({ type: 'SCRATCHPAD_TRIGGER_EDITOR_COMMAND', payload: { command: 'hydro.changeEditorTheme' } });
}
// TODO update style
return <div>
<div className="row"><div className="medium-6 columns">
<label>
{i18n('Font size')}:
<div className="textbox-container">
<input type="number" step="1" value={parsed.fontSize} onChange={dispatcher('fontSize', true)} className="textbox" />
</div>
</label>
</div></div>
<div className="row"><div className="medium-6 columns">
<label>
{i18n('Tab size')}:
<div className="textbox-container">
<input type="number" step="1" value={parsed.tabSize} onChange={dispatcher('tabSize', true)} className="textbox" />
</div>
</label>
</div></div>
<div className="row"><div className="columns">
<p>{i18n('Theme')}: </p>
<button className="rounded primary button" onClick={openThemeSelect}>{i18n('Open theme select')}</button>
</div></div>
</div>;
}

@ -1,4 +1,5 @@
import ProblemIcon from '@vscode/codicons/src/icons/file.svg?react';
import SettingsIcon from '@vscode/codicons/src/icons/settings-gear.svg?react';
import { Allotment } from 'allotment';
import $ from 'jquery';
import _ from 'lodash';
@ -10,6 +11,7 @@ import { Context, ctx, Service } from 'vj/context';
import ScratchpadEditor from './ScratchpadEditorContainer';
import ScratchpadPretest from './ScratchpadPretestContainer';
import ScratchpadRecords from './ScratchpadRecordsContainer';
import ScratchpadSettings from './ScratchpadSettings';
import ScratchpadToolbar from './ScratchpadToolbarContainer';
const pages = {
@ -17,6 +19,10 @@ const pages = {
icon: () => <ProblemIcon />,
component: () => <Dom childDom={$('.problem-content').get(0)} />,
},
settings: {
icon: () => <SettingsIcon />,
component: () => <ScratchpadSettings />,
},
};
let rerenderCallback = null;

@ -11,11 +11,16 @@ export default function reducer(state = {
visible: UiContext.canViewRecord && localStorage.getItem('scratchpad/records') === 'true',
isLoading: false,
},
settings: {
visible: false,
config: JSON.parse(localStorage.getItem('editor.config') || '{}'),
},
isPosting: false,
pretestWaitSec: 0,
submitWaitSec: 0,
lastTick: 0,
activePage: 'problem',
pendingCommand: '',
}, action: any = {}) {
switch (action.type) {
case 'SCRATCHPAD_UI_SET_VISIBILITY': {
@ -40,6 +45,27 @@ export default function reducer(state = {
},
};
}
case 'SCRATCHPAD_SETTING_UPDATE': {
const { setting, value } = action.payload;
const config = {
...state.settings.config,
[setting]: value,
};
localStorage.setItem('editor.config', JSON.stringify(config));
return {
...state,
settings: {
...state.settings,
config,
},
};
}
case 'SCRATCHPAD_TRIGGER_EDITOR_COMMAND': {
return {
...state,
pendingCommand: action.payload.command,
};
}
case 'SCRATCHPAD_POST_PRETEST_PENDING':
case 'SCRATCHPAD_POST_SUBMIT_PENDING': {
return {

Loading…
Cancel
Save