import ProblemIcon from '@vscode/codicons/src/icons/file.svg?react'; 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 SplitPane from 'react-split-pane'; import Dom from 'vj/components/react/DomComponent'; import SplitPaneFillOverlay from 'vj/components/react-splitpane/SplitPaneFillOverlayComponent'; import { Context, ctx, Service } from 'vj/context'; import ScratchpadEditor from './ScratchpadEditorContainer'; import ScratchpadPretest from './ScratchpadPretestContainer'; import ScratchpadRecords from './ScratchpadRecordsContainer'; import ScratchpadToolbar from './ScratchpadToolbarContainer'; function buildNestedPane([a, ...panes], mode: 'horizontal' | 'vertical' = 'horizontal') { const elements = [ a, ...panes.filter((p) => p.props.visible), ]; if (elements.length === 1) return a; return elements .reduce((prev, curr) => ( {prev} {curr.element} )); } const pages = { problem: { icon: () => , component: () => , }, }; 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; 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(); const forceUpdate = React.useCallback(() => updateState({}), []); React.useEffect(() => { rerenderCallback = forceUpdate; }, []); const dispatch = useDispatch(); const ui = useSelector((state) => state.ui, _.isEqual); const handleChangeSize = _.debounce((uiElement, size) => { dispatch({ type: 'SCRATCHPAD_UI_CHANGE_SIZE', payload: { uiElement, size, }, }); $('#scratchpad').trigger('vjScratchpadRelayout'); forceUpdate(); }, 500); const switchToPage = (target) => { dispatch({ type: 'SCRATCHPAD_SWITCH_TO_PAGE', payload: target, }); }; const showSidebar = Object.keys(pages).length > 1; return (
{Object.keys(pages).map((key) => { const Component = pages[key].icon; return (
switchToPage(key)}>
); })}
handleChangeSize('main', size)} allowResize={ui.activePage !== null} >
{Object.keys(pages).map((key) => { const Component = pages[key].component; return (
); })}
{buildNestedPane([ , { props: ui.pretest, onChange: (size) => handleChangeSize('pretest', size), element: , }, { props: ui.records, onChange: (size) => handleChangeSize('records', size), element: , }, ])}
); }