From 8595c9a22830b1baacc49bb694d70389b2539eba Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 1 Jul 2021 12:31:07 +0200 Subject: Add material-ui and mobx integration --- language-web/src/main/css/index.scss | 64 ------------------ language-web/src/main/js/App.jsx | 73 +++++++++++++++++++++ language-web/src/main/js/Navbar.jsx | 37 +++++++++++ language-web/src/main/js/RootStore.jsx | 30 +++++++++ .../src/main/js/components/XtextCodeMirror.jsx | 32 --------- language-web/src/main/js/editor/Editor.jsx | 50 +++++++++++++++ language-web/src/main/js/editor/EditorButtons.jsx | 27 ++++++++ language-web/src/main/js/editor/EditorStore.jsx | 75 ++++++++++++++++++++++ language-web/src/main/js/index.jsx | 21 +++++- 9 files changed, 311 insertions(+), 98 deletions(-) create mode 100644 language-web/src/main/js/App.jsx create mode 100644 language-web/src/main/js/Navbar.jsx create mode 100644 language-web/src/main/js/RootStore.jsx delete mode 100644 language-web/src/main/js/components/XtextCodeMirror.jsx create mode 100644 language-web/src/main/js/editor/Editor.jsx create mode 100644 language-web/src/main/js/editor/EditorButtons.jsx create mode 100644 language-web/src/main/js/editor/EditorStore.jsx (limited to 'language-web/src') diff --git a/language-web/src/main/css/index.scss b/language-web/src/main/css/index.scss index e788bbd1..62109a2f 100644 --- a/language-web/src/main/css/index.scss +++ b/language-web/src/main/css/index.scss @@ -3,70 +3,6 @@ @import 'xtext/xtext-codemirror'; -body { - width: 100%; - height: 100%; - overflow: hidden; - font: 16px Helvetica,sans-serif; -} - -a { - color: #22a; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -.container { - display: block; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: 24px 24px 36px 24px; -} - -.header { - display: block; - position: absolute; - top: 0; - left: 0; - right: 0; - padding: 16px; - background: #dbdbdb; - border-radius: 16px; - color: #383838; -} - -.header h1 { - font-size: 36px; - margin: 0; -} - -.content { - display: block; - position: absolute; - top: 108px; - bottom: 0; - left: 0; - width: 100%; - box-shadow: 0 0 8px rgba(80, 10, 98, .25), 0 10px 24px rgba(80, 10, 98, .35); - border-radius: 16px; -} - -.xtext-editor { - display: block; - position: absolute; - top: 16px; - bottom: 16px; - left: 16px; - right: 16px; - border: 1px solid #999; -} - .problem-class { color: #005032; } diff --git a/language-web/src/main/js/App.jsx b/language-web/src/main/js/App.jsx new file mode 100644 index 00000000..61ded943 --- /dev/null +++ b/language-web/src/main/js/App.jsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import AppBar from '@material-ui/core/AppBar'; +import Box from '@material-ui/core/Box'; +import Fab from '@material-ui/core/Fab'; +import IconButton from '@material-ui/core/IconButton'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import MenuIcon from '@material-ui/icons/Menu'; +import PlayArrowIcon from '@material-ui/icons/PlayArrow'; + +import Editor from './editor/Editor'; +import EditorButtons from './editor/EditorButtons'; + +const useStyles = makeStyles(theme => ({ + menuButton: { + marginRight: theme.spacing(2), + }, + title: { + flexGrow: 1, + }, + fab: { + position: 'fixed', + right: theme.spacing(3), + bottom: theme.spacing(3), + zIndex: 1000, + }, + extendedIcon: { + marginRight: theme.spacing(1), + } +})); + +export default () => { + const classes = useStyles(); + + return ( + <> + + + + + + + GraphSolver + + + + + + + + + + Generate + + + ); +}; diff --git a/language-web/src/main/js/Navbar.jsx b/language-web/src/main/js/Navbar.jsx new file mode 100644 index 00000000..cf1bc54f --- /dev/null +++ b/language-web/src/main/js/Navbar.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { Button, Navbar, Nav } from 'react-bootstrap'; +import { FaGithub, FaPlayCircle } from 'react-icons/fa'; + +export default () => ( + + GraphSolver + + + + + + +); diff --git a/language-web/src/main/js/RootStore.jsx b/language-web/src/main/js/RootStore.jsx new file mode 100644 index 00000000..1ee2823d --- /dev/null +++ b/language-web/src/main/js/RootStore.jsx @@ -0,0 +1,30 @@ +import { makeAutoObservable } from 'mobx'; +import React, { createContext, useContext } from 'react'; + +import EditorStore from './editor/EditorStore'; + +export default class RootStore { + editorStore; + + constructor() { + this.editorStore = new EditorStore(); + makeAutoObservable(this); + } +} + +const StoreContext = createContext(undefined); + +export const RootStoreProvider = ({ children, rootStore }) => ( + + {children} + +); + +/** @returns {RootStore} */ +export const useRootStore = () => { + const rootStore = useContext(StoreContext); + if (!rootStore) { + throw new Error('useRootStore must be used within RootStoreProvider'); + } + return rootStore; +}; diff --git a/language-web/src/main/js/components/XtextCodeMirror.jsx b/language-web/src/main/js/components/XtextCodeMirror.jsx deleted file mode 100644 index 75a20daa..00000000 --- a/language-web/src/main/js/components/XtextCodeMirror.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { useCallback, useState } from 'react'; -import { Controlled as CodeMirror } from 'react-codemirror2'; -import 'mode-problem'; -import { createServices, removeServices } from 'xtext/xtext-codemirror'; - -export default function XtextCodeMirror({ initialValue }) { - const [value, setValue] = useState(initialValue); - - const codeMirrorOptions = { - mode: 'xtext/problem', - indentUnit: 2, - }; - - const xtextOptions = { - xtextLang: 'problem', - enableFormattingAction: true, - }; - - const onBeforeChange = useCallback((_editor, _data, newValue) => { - setValue(newValue); - }, [setValue]); - - return ( - createServices(editor, xtextOptions)} - editorWillUnmount={removeServices} - onBeforeChange={onBeforeChange} - /> - ); -}; diff --git a/language-web/src/main/js/editor/Editor.jsx b/language-web/src/main/js/editor/Editor.jsx new file mode 100644 index 00000000..c4b2e183 --- /dev/null +++ b/language-web/src/main/js/editor/Editor.jsx @@ -0,0 +1,50 @@ +import { observer } from 'mobx-react-lite'; +import 'mode-problem'; +import React, { useCallback } from 'react'; +import { Controlled as CodeMirror } from 'react-codemirror2'; +import { createServices, removeServices } from 'xtext/xtext-codemirror'; + +import { useRootStore } from '../RootStore'; + +export default observer(() => { + const editorStore = useRootStore().editorStore; + + const codeMirrorOptions = { + mode: 'xtext/problem', + indentUnit: 2, + }; + + const xtextOptions = { + xtextLang: 'problem', + enableFormattingAction: true, + } + + const editorDidMount = useCallback((editor) => { + createServices(editor, xtextOptions); + editorStore.updateEditor(editor); + }, [editorStore]); + + const editorWillUnmount = useCallback((editor) => { + editorStore.editor = null; + removeServices(editor); + }, [editorStore]); + + const onBeforeChange = useCallback((_editor, _data, value) => { + editorStore.updateValue(value); + }, [editorStore]); + + const onChange = useCallback((_editor, _data, _value) => { + editorStore.reportChanged(); + }, [editorStore]); + + return ( + + ); +}); diff --git a/language-web/src/main/js/editor/EditorButtons.jsx b/language-web/src/main/js/editor/EditorButtons.jsx new file mode 100644 index 00000000..b9f0d076 --- /dev/null +++ b/language-web/src/main/js/editor/EditorButtons.jsx @@ -0,0 +1,27 @@ +import { observer } from 'mobx-react-lite'; +import React from 'react'; +import IconButton from '@material-ui/core/IconButton'; +import RedoIcon from '@material-ui/icons/Redo'; +import UndoIcon from '@material-ui/icons/Undo'; + +import { useRootStore } from '../RootStore'; + +export default observer(() => { + const editorStore = useRootStore().editorStore; + return ( + <> + editorStore.undo()} + > + + + editorStore.redo()} + > + + + + ); +}); diff --git a/language-web/src/main/js/editor/EditorStore.jsx b/language-web/src/main/js/editor/EditorStore.jsx new file mode 100644 index 00000000..9c286c28 --- /dev/null +++ b/language-web/src/main/js/editor/EditorStore.jsx @@ -0,0 +1,75 @@ +import CodeMirror from 'codemirror'; +import { createAtom, makeAutoObservable, observable } from 'mobx'; + +export default class EditorStore { + atom; + /** @type {CodeMirror} */ + editor = null; + /** @type {string} */ + value = ''; + + constructor() { + this.atom = createAtom('EditorStore'); + makeAutoObservable(this, { + atom: false, + editor: observable.ref, + }); + } + + /** + * Attaches a new CodeMirror instance. + * + * The store will node subscribe to any CodeMirror events. Instead, + * the editor component should subscribe to them and relay them to the store. + * + * @param {CodeMirror} newEditor The new CodeMirror instance + */ + updateEditor(newEditor) { + this.editor = newEditor; + } + + /** + * Updates the contents of the editor. + * + * @param {string} newValue The new contents of the editor + */ + updateValue(newValue) { + this.value = newValue; + } + + reportChanged() { + this.atom.reportChanged(); + } + + /** + * @returns {boolean} `true` if there is history to undo + */ + get canUndo() { + this.atom.reportObserved(); + if (!this.editor) { + return false; + } + const { undo: undoSize } = this.editor.historySize(); + return undoSize > 0; + } + + undo() { + this.editor.undo(); + } + + /** + * @returns {boolean} `true` if there is history to redo + */ + get canRedo() { + this.atom.reportObserved(); + if (!this.editor) { + return false; + } + const { redo: redoSize } = this.editor.historySize(); + return redoSize > 0; + } + + redo() { + this.editor.redo(); + } +} diff --git a/language-web/src/main/js/index.jsx b/language-web/src/main/js/index.jsx index 48c0baeb..17e6aef2 100644 --- a/language-web/src/main/js/index.jsx +++ b/language-web/src/main/js/index.jsx @@ -1,7 +1,11 @@ import React from 'react'; import { render } from 'react-dom'; +import { createMuiTheme } from '@material-ui/core/styles'; +import { ThemeProvider } from '@material-ui/styles'; -import XtextCodeMirror from "./components/XtextCodeMirror"; +import App from './App'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import RootStore, { RootStoreProvider } from './RootStore'; import '../css/index.scss'; @@ -38,6 +42,19 @@ age(bob, bobAge). scope Family = 1, Person += 5..10. `; -const app = ; + +const rootStore = new RootStore(); +rootStore.editorStore.updateValue(initialValue); + +const theme = createMuiTheme(); + +const app = ( + + + + + + +) render(app, document.getElementById('app')); -- cgit v1.2.3-70-g09d2