|
|
|
import ProblemIcon from '@vscode/codicons/src/icons/file.svg?react';
|
|
|
|
import { Allotment } from 'allotment';
|
|
|
|
import $ from 'jquery';
|
|
|
|
import _ from 'lodash';
|
|
|
|
import type * as monaco from 'monaco-editor';
|
|
|
|
import React from 'react';
|
|
|
|
import { useDispatch, useSelector, useStore } from 'react-redux';
|
|
|
|
import Dom from 'vj/components/react/DomComponent';
|
|
|
|
import { Context, ctx, Service } from 'vj/context';
|
|
|
|
import ScratchpadEditor from './ScratchpadEditorContainer';
|
|
|
|
import ScratchpadPretest from './ScratchpadPretestContainer';
|
|
|
|
import ScratchpadRecords from './ScratchpadRecordsContainer';
|
|
|
|
import ScratchpadToolbar from './ScratchpadToolbarContainer';
|
|
|
|
|
|
|
|
const pages = {
|
|
|
|
problem: {
|
|
|
|
icon: () => <ProblemIcon />,
|
|
|
|
component: () => <Dom childDom={$('.problem-content').get(0)} />,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let rerenderCallback = null;
|
|
|
|
class ScratchpadService extends Service {
|
|
|
|
constructor(public store) {
|
|
|
|
super(ctx, 'scratchpad', true);
|
|
|
|
this.load = new Promise((resolve) => {
|
|
|
|
this.loadCallback = resolve;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
pages = pages;
|
|
|
|
load: Promise<void>;
|
|
|
|
loadCallback: () => void;
|
|
|
|
editor: monaco.editor.IStandaloneCodeEditor;
|
|
|
|
monaco: typeof import('monaco-editor');
|
|
|
|
|
|
|
|
init(editor: monaco.editor.IStandaloneCodeEditor, monaco: typeof import('monaco-editor')) {
|
|
|
|
this.editor = editor;
|
|
|
|
this.monaco = monaco;
|
|
|
|
this.loadCallback();
|
|
|
|
}
|
|
|
|
|
|
|
|
addPage(key, icon, component) {
|
|
|
|
pages[key] = {
|
|
|
|
icon,
|
|
|
|
component,
|
|
|
|
};
|
|
|
|
rerenderCallback?.();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Context.service('scratchpad', ScratchpadService);
|
|
|
|
declare module '../../context' {
|
|
|
|
interface Context {
|
|
|
|
scratchpad: ScratchpadService;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function ScratchpadContainer() {
|
|
|
|
const store = useStore();
|
|
|
|
ctx.scratchpad ||= new ScratchpadService(store);
|
|
|
|
const [, updateState] = React.useState<any>();
|
|
|
|
const forceUpdate = React.useCallback(() => updateState({}), []);
|
|
|
|
React.useEffect(() => {
|
|
|
|
rerenderCallback = forceUpdate;
|
|
|
|
}, []);
|
|
|
|
const dispatch = useDispatch();
|
|
|
|
const ui = useSelector<any, any>((state) => state.ui, _.isEqual);
|
|
|
|
|
|
|
|
const handleChangeSize = _.debounce(() => {
|
|
|
|
ctx.scratchpad.editor?.layout?.();
|
|
|
|
$('#scratchpad').trigger('vjScratchpadRelayout');
|
|
|
|
forceUpdate();
|
|
|
|
}, 500);
|
|
|
|
const switchToPage = (target) => {
|
|
|
|
dispatch({
|
|
|
|
type: 'SCRATCHPAD_SWITCH_TO_PAGE',
|
|
|
|
payload: target,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Allotment onChange={handleChangeSize}>
|
|
|
|
<Allotment.Pane visible={Object.keys(pages).length > 1} minSize={50} maxSize={50}>
|
|
|
|
<div className="scratchpad__tablist">
|
|
|
|
{Object.keys(pages).map((key) => {
|
|
|
|
const Component = pages[key].icon;
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
key={key}
|
|
|
|
className={`scratchpad__tabicon-${key}${key === ui.activePage ? ' scratchpad__tab-active' : ''}`}
|
|
|
|
onClick={() => switchToPage(key)}
|
|
|
|
>
|
|
|
|
<Component />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
</Allotment.Pane>
|
|
|
|
<Allotment.Pane visible={!!ui.activePage}>
|
|
|
|
<div className="scratchpad__problem">
|
|
|
|
{Object.keys(pages).map((key) => {
|
|
|
|
const Component = pages[key].component;
|
|
|
|
return (
|
|
|
|
<div key={key} style={{ display: key === ui.activePage ? 'block' : 'none' }}>
|
|
|
|
<Component />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
</Allotment.Pane>
|
|
|
|
<Allotment vertical onChange={handleChangeSize}>
|
|
|
|
<div key="editor" className="flex-col splitpane-fill">
|
|
|
|
<ScratchpadToolbar />
|
|
|
|
<ScratchpadEditor />
|
|
|
|
</div>
|
|
|
|
<Allotment.Pane visible={ui.pretest.visible}>
|
|
|
|
<ScratchpadPretest key="pretest" />
|
|
|
|
</Allotment.Pane>
|
|
|
|
<Allotment.Pane visible={ui.records.visible}>
|
|
|
|
<ScratchpadRecords key="records" />
|
|
|
|
</Allotment.Pane>
|
|
|
|
</Allotment>
|
|
|
|
</Allotment>
|
|
|
|
);
|
|
|
|
}
|