You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hydro/packages/ui-default/components/scratchpad/index.tsx

132 lines
4.0 KiB
TypeScript

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';
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 ScratchpadSettings from './ScratchpadSettings';
import ScratchpadToolbar from './ScratchpadToolbarContainer';
const pages = {
problem: {
icon: () => <ProblemIcon />,
component: () => <Dom childDom={$('.problem-content').get(0)} />,
},
settings: {
icon: () => <SettingsIcon />,
component: () => <ScratchpadSettings />,
},
};
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>
);
}