aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main/js
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-07-01 12:31:07 +0200
committerLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-07-01 12:31:07 +0200
commit8595c9a22830b1baacc49bb694d70389b2539eba (patch)
treecb496cd34661a0d3ec4c9b5a333e948f24e437f4 /language-web/src/main/js
parentConvert to React app (diff)
downloadrefinery-8595c9a22830b1baacc49bb694d70389b2539eba.tar.gz
refinery-8595c9a22830b1baacc49bb694d70389b2539eba.tar.zst
refinery-8595c9a22830b1baacc49bb694d70389b2539eba.zip
Add material-ui and mobx integration
Diffstat (limited to 'language-web/src/main/js')
-rw-r--r--language-web/src/main/js/App.jsx73
-rw-r--r--language-web/src/main/js/Navbar.jsx37
-rw-r--r--language-web/src/main/js/RootStore.jsx30
-rw-r--r--language-web/src/main/js/components/XtextCodeMirror.jsx32
-rw-r--r--language-web/src/main/js/editor/Editor.jsx50
-rw-r--r--language-web/src/main/js/editor/EditorButtons.jsx27
-rw-r--r--language-web/src/main/js/editor/EditorStore.jsx75
-rw-r--r--language-web/src/main/js/index.jsx21
8 files changed, 311 insertions, 34 deletions
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 @@
1import React from 'react';
2import { makeStyles } from '@material-ui/core/styles';
3import AppBar from '@material-ui/core/AppBar';
4import Box from '@material-ui/core/Box';
5import Fab from '@material-ui/core/Fab';
6import IconButton from '@material-ui/core/IconButton';
7import Toolbar from '@material-ui/core/Toolbar';
8import Typography from '@material-ui/core/Typography';
9import MenuIcon from '@material-ui/icons/Menu';
10import PlayArrowIcon from '@material-ui/icons/PlayArrow';
11
12import Editor from './editor/Editor';
13import EditorButtons from './editor/EditorButtons';
14
15const useStyles = makeStyles(theme => ({
16 menuButton: {
17 marginRight: theme.spacing(2),
18 },
19 title: {
20 flexGrow: 1,
21 },
22 fab: {
23 position: 'fixed',
24 right: theme.spacing(3),
25 bottom: theme.spacing(3),
26 zIndex: 1000,
27 },
28 extendedIcon: {
29 marginRight: theme.spacing(1),
30 }
31}));
32
33export default () => {
34 const classes = useStyles();
35
36 return (
37 <>
38 <AppBar
39 position='static'
40 >
41 <Toolbar>
42 <IconButton
43 edge='start'
44 className={classes.menuButton}
45 color='inherit'
46 aria-label='menu'
47 >
48 <MenuIcon />
49 </IconButton>
50 <Typography
51 variant='h6'
52 component='h1'
53 className={classes.title}
54 >
55 GraphSolver
56 </Typography>
57 </Toolbar>
58 </AppBar>
59 <Box>
60 <EditorButtons/>
61 </Box>
62 <Editor/>
63 <Fab
64 variant='extended'
65 color='secondary'
66 className={classes.fab}
67 >
68 <PlayArrowIcon className={classes.extendedIcon}/>
69 Generate
70 </Fab>
71 </>
72 );
73};
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 @@
1import React from 'react';
2import { Button, Navbar, Nav } from 'react-bootstrap';
3import { FaGithub, FaPlayCircle } from 'react-icons/fa';
4
5export default () => (
6 <Navbar
7 variant='secondary'
8 className='px-2'
9 >
10 <Navbar.Brand>GraphSolver</Navbar.Brand>
11 <Navbar.Toggle aria-controls='basic-navbar-nav'/>
12 <Navbar.Collapse id='basic-navbar-nav'>
13 <Nav className="me-auto">
14 <Nav.Link
15 href="https://github.com/viatra/VIATRA-Generator#readme"
16 >
17 About
18 </Nav.Link>
19 <Nav.Link
20 href="https://github.com/viatra/VIATRA-Generator/wiki"
21 >
22 Getting started
23 </Nav.Link>
24 <Nav.Link
25 href="https://github.com/viatra/VIATRA-Generator"
26 >
27 <FaGithub/> Github
28 </Nav.Link>
29 </Nav>
30 <Button
31 variant='success'
32 >
33 <FaPlayCircle/> Generate
34 </Button>
35 </Navbar.Collapse>
36 </Navbar>
37);
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 @@
1import { makeAutoObservable } from 'mobx';
2import React, { createContext, useContext } from 'react';
3
4import EditorStore from './editor/EditorStore';
5
6export default class RootStore {
7 editorStore;
8
9 constructor() {
10 this.editorStore = new EditorStore();
11 makeAutoObservable(this);
12 }
13}
14
15const StoreContext = createContext(undefined);
16
17export const RootStoreProvider = ({ children, rootStore }) => (
18 <StoreContext.Provider value={rootStore}>
19 {children}
20 </StoreContext.Provider>
21);
22
23/** @returns {RootStore} */
24export const useRootStore = () => {
25 const rootStore = useContext(StoreContext);
26 if (!rootStore) {
27 throw new Error('useRootStore must be used within RootStoreProvider');
28 }
29 return rootStore;
30};
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 @@
1import React, { useCallback, useState } from 'react';
2import { Controlled as CodeMirror } from 'react-codemirror2';
3import 'mode-problem';
4import { createServices, removeServices } from 'xtext/xtext-codemirror';
5
6export default function XtextCodeMirror({ initialValue }) {
7 const [value, setValue] = useState(initialValue);
8
9 const codeMirrorOptions = {
10 mode: 'xtext/problem',
11 indentUnit: 2,
12 };
13
14 const xtextOptions = {
15 xtextLang: 'problem',
16 enableFormattingAction: true,
17 };
18
19 const onBeforeChange = useCallback((_editor, _data, newValue) => {
20 setValue(newValue);
21 }, [setValue]);
22
23 return (
24 <CodeMirror
25 value={value}
26 options={codeMirrorOptions}
27 editorDidMount={editor => createServices(editor, xtextOptions)}
28 editorWillUnmount={removeServices}
29 onBeforeChange={onBeforeChange}
30 />
31 );
32};
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 @@
1import { observer } from 'mobx-react-lite';
2import 'mode-problem';
3import React, { useCallback } from 'react';
4import { Controlled as CodeMirror } from 'react-codemirror2';
5import { createServices, removeServices } from 'xtext/xtext-codemirror';
6
7import { useRootStore } from '../RootStore';
8
9export default observer(() => {
10 const editorStore = useRootStore().editorStore;
11
12 const codeMirrorOptions = {
13 mode: 'xtext/problem',
14 indentUnit: 2,
15 };
16
17 const xtextOptions = {
18 xtextLang: 'problem',
19 enableFormattingAction: true,
20 }
21
22 const editorDidMount = useCallback((editor) => {
23 createServices(editor, xtextOptions);
24 editorStore.updateEditor(editor);
25 }, [editorStore]);
26
27 const editorWillUnmount = useCallback((editor) => {
28 editorStore.editor = null;
29 removeServices(editor);
30 }, [editorStore]);
31
32 const onBeforeChange = useCallback((_editor, _data, value) => {
33 editorStore.updateValue(value);
34 }, [editorStore]);
35
36 const onChange = useCallback((_editor, _data, _value) => {
37 editorStore.reportChanged();
38 }, [editorStore]);
39
40 return (
41 <CodeMirror
42 value={editorStore.value}
43 options={codeMirrorOptions}
44 editorDidMount={editorDidMount}
45 editorWillUnmount={editorWillUnmount}
46 onBeforeChange={onBeforeChange}
47 onChange={onChange}
48 />
49 );
50});
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 @@
1import { observer } from 'mobx-react-lite';
2import React from 'react';
3import IconButton from '@material-ui/core/IconButton';
4import RedoIcon from '@material-ui/icons/Redo';
5import UndoIcon from '@material-ui/icons/Undo';
6
7import { useRootStore } from '../RootStore';
8
9export default observer(() => {
10 const editorStore = useRootStore().editorStore;
11 return (
12 <>
13 <IconButton
14 disabled={!editorStore.canUndo}
15 onClick={() => editorStore.undo()}
16 >
17 <UndoIcon fontSize='small'/>
18 </IconButton>
19 <IconButton
20 disabled={!editorStore.canRedo}
21 onClick={() => editorStore.redo()}
22 >
23 <RedoIcon fontSize='small'/>
24 </IconButton>
25 </>
26 );
27});
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 @@
1import CodeMirror from 'codemirror';
2import { createAtom, makeAutoObservable, observable } from 'mobx';
3
4export default class EditorStore {
5 atom;
6 /** @type {CodeMirror} */
7 editor = null;
8 /** @type {string} */
9 value = '';
10
11 constructor() {
12 this.atom = createAtom('EditorStore');
13 makeAutoObservable(this, {
14 atom: false,
15 editor: observable.ref,
16 });
17 }
18
19 /**
20 * Attaches a new CodeMirror instance.
21 *
22 * The store will node subscribe to any CodeMirror events. Instead,
23 * the editor component should subscribe to them and relay them to the store.
24 *
25 * @param {CodeMirror} newEditor The new CodeMirror instance
26 */
27 updateEditor(newEditor) {
28 this.editor = newEditor;
29 }
30
31 /**
32 * Updates the contents of the editor.
33 *
34 * @param {string} newValue The new contents of the editor
35 */
36 updateValue(newValue) {
37 this.value = newValue;
38 }
39
40 reportChanged() {
41 this.atom.reportChanged();
42 }
43
44 /**
45 * @returns {boolean} `true` if there is history to undo
46 */
47 get canUndo() {
48 this.atom.reportObserved();
49 if (!this.editor) {
50 return false;
51 }
52 const { undo: undoSize } = this.editor.historySize();
53 return undoSize > 0;
54 }
55
56 undo() {
57 this.editor.undo();
58 }
59
60 /**
61 * @returns {boolean} `true` if there is history to redo
62 */
63 get canRedo() {
64 this.atom.reportObserved();
65 if (!this.editor) {
66 return false;
67 }
68 const { redo: redoSize } = this.editor.historySize();
69 return redoSize > 0;
70 }
71
72 redo() {
73 this.editor.redo();
74 }
75}
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 @@
1import React from 'react'; 1import React from 'react';
2import { render } from 'react-dom'; 2import { render } from 'react-dom';
3import { createMuiTheme } from '@material-ui/core/styles';
4import { ThemeProvider } from '@material-ui/styles';
3 5
4import XtextCodeMirror from "./components/XtextCodeMirror"; 6import App from './App';
7import CssBaseline from '@material-ui/core/CssBaseline';
8import RootStore, { RootStoreProvider } from './RootStore';
5 9
6import '../css/index.scss'; 10import '../css/index.scss';
7 11
@@ -38,6 +42,19 @@ age(bob, bobAge).
38 42
39scope Family = 1, Person += 5..10. 43scope Family = 1, Person += 5..10.
40`; 44`;
41const app = <XtextCodeMirror initialValue={initialValue}/>; 45
46const rootStore = new RootStore();
47rootStore.editorStore.updateValue(initialValue);
48
49const theme = createMuiTheme();
50
51const app = (
52 <ThemeProvider theme={theme}>
53 <CssBaseline/>
54 <RootStoreProvider rootStore={rootStore}>
55 <App/>
56 </RootStoreProvider>
57 </ThemeProvider>
58)
42 59
43render(app, document.getElementById('app')); 60render(app, document.getElementById('app'));