diff options
author | Kristóf Marussy <marussy@mit.bme.hu> | 2021-09-17 19:10:19 +0200 |
---|---|---|
committer | Kristóf Marussy <marussy@mit.bme.hu> | 2021-09-17 19:10:19 +0200 |
commit | efdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7 (patch) | |
tree | c623e8b3fba326dcbb57228f1ddbfbf55eeec494 /language-web/src/main/js | |
parent | Add ESLint config (diff) | |
download | refinery-efdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7.tar.gz refinery-efdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7.tar.zst refinery-efdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7.zip |
Frontend color theme
Diffstat (limited to 'language-web/src/main/js')
-rw-r--r-- | language-web/src/main/js/RootStore.tsx | 6 | ||||
-rw-r--r-- | language-web/src/main/js/editor/EditorStore.ts | 16 | ||||
-rw-r--r-- | language-web/src/main/js/global.d.ts | 5 | ||||
-rw-r--r-- | language-web/src/main/js/index.tsx | 32 | ||||
-rw-r--r-- | language-web/src/main/js/theme/EditorTheme.ts | 47 | ||||
-rw-r--r-- | language-web/src/main/js/theme/ThemeProvider.tsx | 15 | ||||
-rw-r--r-- | language-web/src/main/js/theme/ThemeStore.ts | 53 |
7 files changed, 145 insertions, 29 deletions
diff --git a/language-web/src/main/js/RootStore.tsx b/language-web/src/main/js/RootStore.tsx index 1c3aab2b..88b8a445 100644 --- a/language-web/src/main/js/RootStore.tsx +++ b/language-web/src/main/js/RootStore.tsx | |||
@@ -1,12 +1,16 @@ | |||
1 | import React, { createContext, useContext } from 'react'; | 1 | import React, { createContext, useContext } from 'react'; |
2 | 2 | ||
3 | import { EditorStore } from './editor/EditorStore'; | 3 | import { EditorStore } from './editor/EditorStore'; |
4 | import { ThemeStore } from './theme/ThemeStore'; | ||
4 | 5 | ||
5 | export class RootStore { | 6 | export class RootStore { |
6 | editorStore; | 7 | editorStore; |
7 | 8 | ||
9 | themeStore; | ||
10 | |||
8 | constructor() { | 11 | constructor() { |
9 | this.editorStore = new EditorStore(); | 12 | this.themeStore = new ThemeStore(); |
13 | this.editorStore = new EditorStore(this.themeStore); | ||
10 | } | 14 | } |
11 | } | 15 | } |
12 | 16 | ||
diff --git a/language-web/src/main/js/editor/EditorStore.ts b/language-web/src/main/js/editor/EditorStore.ts index 3a0c6f87..5da45ac1 100644 --- a/language-web/src/main/js/editor/EditorStore.ts +++ b/language-web/src/main/js/editor/EditorStore.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import { Editor, EditorConfiguration } from 'codemirror'; | 1 | import { Editor, EditorConfiguration } from 'codemirror'; |
2 | import 'codemirror/addon/selection/active-line'; | ||
2 | import { | 3 | import { |
3 | createAtom, | 4 | createAtom, |
4 | makeAutoObservable, | 5 | makeAutoObservable, |
@@ -12,6 +13,8 @@ import { | |||
12 | removeServices, | 13 | removeServices, |
13 | } from 'xtext/xtext-codemirror'; | 14 | } from 'xtext/xtext-codemirror'; |
14 | 15 | ||
16 | import { ThemeStore } from '../theme/ThemeStore'; | ||
17 | |||
15 | const xtextLang = 'problem'; | 18 | const xtextLang = 'problem'; |
16 | 19 | ||
17 | const xtextOptions: IXtextOptions = { | 20 | const xtextOptions: IXtextOptions = { |
@@ -22,10 +25,12 @@ const xtextOptions: IXtextOptions = { | |||
22 | const codeMirrorGlobalOptions: EditorConfiguration = { | 25 | const codeMirrorGlobalOptions: EditorConfiguration = { |
23 | mode: `xtext/${xtextLang}`, | 26 | mode: `xtext/${xtextLang}`, |
24 | indentUnit: 2, | 27 | indentUnit: 2, |
25 | theme: 'material-darker', | 28 | styleActiveLine: true, |
26 | }; | 29 | }; |
27 | 30 | ||
28 | export class EditorStore { | 31 | export class EditorStore { |
32 | themeStore; | ||
33 | |||
29 | atom; | 34 | atom; |
30 | 35 | ||
31 | editor?: Editor; | 36 | editor?: Editor; |
@@ -36,9 +41,11 @@ export class EditorStore { | |||
36 | 41 | ||
37 | showLineNumbers = false; | 42 | showLineNumbers = false; |
38 | 43 | ||
39 | constructor() { | 44 | constructor(themeStore: ThemeStore) { |
45 | this.themeStore = themeStore; | ||
40 | this.atom = createAtom('EditorStore'); | 46 | this.atom = createAtom('EditorStore'); |
41 | makeAutoObservable(this, { | 47 | makeAutoObservable(this, { |
48 | themeStore: false, | ||
42 | atom: false, | 49 | atom: false, |
43 | editor: observable.ref, | 50 | editor: observable.ref, |
44 | xtextServices: observable.ref, | 51 | xtextServices: observable.ref, |
@@ -65,8 +72,8 @@ export class EditorStore { | |||
65 | if (this.editor) { | 72 | if (this.editor) { |
66 | removeServices(this.editor); | 73 | removeServices(this.editor); |
67 | } | 74 | } |
68 | this.editor = undefined; | 75 | delete this.editor; |
69 | this.xtextServices = undefined; | 76 | delete this.xtextServices; |
70 | } | 77 | } |
71 | 78 | ||
72 | /** | 79 | /** |
@@ -89,6 +96,7 @@ export class EditorStore { | |||
89 | get codeMirrorOptions(): EditorConfiguration { | 96 | get codeMirrorOptions(): EditorConfiguration { |
90 | return { | 97 | return { |
91 | ...codeMirrorGlobalOptions, | 98 | ...codeMirrorGlobalOptions, |
99 | theme: this.themeStore.codeMirrorTheme, | ||
92 | lineNumbers: this.showLineNumbers, | 100 | lineNumbers: this.showLineNumbers, |
93 | }; | 101 | }; |
94 | } | 102 | } |
diff --git a/language-web/src/main/js/global.d.ts b/language-web/src/main/js/global.d.ts new file mode 100644 index 00000000..39bda7f3 --- /dev/null +++ b/language-web/src/main/js/global.d.ts | |||
@@ -0,0 +1,5 @@ | |||
1 | declare module '*.module.scss' { | ||
2 | const cssVariables: { [key in string]?: string }; | ||
3 | // eslint-disable-next-line import/no-default-export | ||
4 | export default cssVariables; | ||
5 | } | ||
diff --git a/language-web/src/main/js/index.tsx b/language-web/src/main/js/index.tsx index 24f0b69d..1f08feeb 100644 --- a/language-web/src/main/js/index.tsx +++ b/language-web/src/main/js/index.tsx | |||
@@ -2,11 +2,11 @@ import { CacheProvider } from '@emotion/react'; | |||
2 | import React from 'react'; | 2 | import React from 'react'; |
3 | import { render } from 'react-dom'; | 3 | import { render } from 'react-dom'; |
4 | import CssBaseline from '@material-ui/core/CssBaseline'; | 4 | import CssBaseline from '@material-ui/core/CssBaseline'; |
5 | import { ThemeProvider, createTheme } from '@material-ui/core/styles'; | ||
6 | import { getCache } from 'tss-react/cache'; | 5 | import { getCache } from 'tss-react/cache'; |
7 | 6 | ||
8 | import { App } from './App'; | 7 | import { App } from './App'; |
9 | import { RootStore, RootStoreProvider } from './RootStore'; | 8 | import { RootStore, RootStoreProvider } from './RootStore'; |
9 | import { ThemeProvider } from './theme/ThemeProvider'; | ||
10 | 10 | ||
11 | import '../css/index.scss'; | 11 | import '../css/index.scss'; |
12 | 12 | ||
@@ -49,31 +49,15 @@ scope Family = 1, Person += 5..10. | |||
49 | const rootStore = new RootStore(); | 49 | const rootStore = new RootStore(); |
50 | rootStore.editorStore.updateValue(initialValue); | 50 | rootStore.editorStore.updateValue(initialValue); |
51 | 51 | ||
52 | const theme = createTheme({ | ||
53 | palette: { | ||
54 | mode: 'dark', | ||
55 | background: { | ||
56 | default: '#212121', | ||
57 | paper: '#2f2f2f', | ||
58 | }, | ||
59 | primary: { | ||
60 | main: '#82aaff', | ||
61 | }, | ||
62 | secondary: { | ||
63 | main: '#ff5370', | ||
64 | }, | ||
65 | }, | ||
66 | }); | ||
67 | |||
68 | const app = ( | 52 | const app = ( |
69 | <CacheProvider value={getCache()}> | 53 | <RootStoreProvider rootStore={rootStore}> |
70 | <ThemeProvider theme={theme}> | 54 | <CacheProvider value={getCache()}> |
71 | <CssBaseline /> | 55 | <ThemeProvider> |
72 | <RootStoreProvider rootStore={rootStore}> | 56 | <CssBaseline /> |
73 | <App /> | 57 | <App /> |
74 | </RootStoreProvider> | 58 | </ThemeProvider> |
75 | </ThemeProvider> | 59 | </CacheProvider> |
76 | </CacheProvider> | 60 | </RootStoreProvider> |
77 | ); | 61 | ); |
78 | 62 | ||
79 | render(app, document.getElementById('app')); | 63 | render(app, document.getElementById('app')); |
diff --git a/language-web/src/main/js/theme/EditorTheme.ts b/language-web/src/main/js/theme/EditorTheme.ts new file mode 100644 index 00000000..9420dafa --- /dev/null +++ b/language-web/src/main/js/theme/EditorTheme.ts | |||
@@ -0,0 +1,47 @@ | |||
1 | import { PaletteMode } from '@material-ui/core'; | ||
2 | |||
3 | import cssVariables from '../../css/themeVariables.module.scss'; | ||
4 | |||
5 | export enum EditorTheme { | ||
6 | Light, | ||
7 | Dark, | ||
8 | } | ||
9 | |||
10 | export class EditorThemeData { | ||
11 | className: string; | ||
12 | |||
13 | paletteMode: PaletteMode; | ||
14 | |||
15 | toggleDarkMode: EditorTheme; | ||
16 | |||
17 | foreground!: string; | ||
18 | |||
19 | background!: string; | ||
20 | |||
21 | paper!: string; | ||
22 | |||
23 | primary!: string; | ||
24 | |||
25 | secondary!: string; | ||
26 | |||
27 | constructor(className: string, paletteMode: PaletteMode, toggleDarkMode: EditorTheme) { | ||
28 | this.className = className; | ||
29 | this.paletteMode = paletteMode; | ||
30 | this.toggleDarkMode = toggleDarkMode; | ||
31 | Reflect.ownKeys(this).forEach((key) => { | ||
32 | if (!Reflect.get(this, key)) { | ||
33 | const cssKey = `${this.className}--${key.toString()}`; | ||
34 | if (cssKey in cssVariables) { | ||
35 | Reflect.set(this, key, cssVariables[cssKey]); | ||
36 | } | ||
37 | } | ||
38 | }); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | export const DEFAULT_THEME = EditorTheme.Dark; | ||
43 | |||
44 | export const EDITOR_THEMES: { [key in EditorTheme]: EditorThemeData } = { | ||
45 | [EditorTheme.Light]: new EditorThemeData('light', 'light', EditorTheme.Dark), | ||
46 | [EditorTheme.Dark]: new EditorThemeData('dark', 'dark', EditorTheme.Light), | ||
47 | }; | ||
diff --git a/language-web/src/main/js/theme/ThemeProvider.tsx b/language-web/src/main/js/theme/ThemeProvider.tsx new file mode 100644 index 00000000..e7574725 --- /dev/null +++ b/language-web/src/main/js/theme/ThemeProvider.tsx | |||
@@ -0,0 +1,15 @@ | |||
1 | import { ThemeProvider as MaterialUiThemeProvider } from '@material-ui/core/styles'; | ||
2 | import { observer } from 'mobx-react-lite'; | ||
3 | import React from 'react'; | ||
4 | |||
5 | import { useRootStore } from '../RootStore'; | ||
6 | |||
7 | export const ThemeProvider: React.FC = observer(({ children }) => { | ||
8 | const { themeStore } = useRootStore(); | ||
9 | |||
10 | return ( | ||
11 | <MaterialUiThemeProvider theme={themeStore.materialUiTheme}> | ||
12 | {children} | ||
13 | </MaterialUiThemeProvider> | ||
14 | ); | ||
15 | }); | ||
diff --git a/language-web/src/main/js/theme/ThemeStore.ts b/language-web/src/main/js/theme/ThemeStore.ts new file mode 100644 index 00000000..0f283c98 --- /dev/null +++ b/language-web/src/main/js/theme/ThemeStore.ts | |||
@@ -0,0 +1,53 @@ | |||
1 | import { | ||
2 | Theme, | ||
3 | createTheme, | ||
4 | responsiveFontSizes, | ||
5 | } from '@material-ui/core/styles'; | ||
6 | import { makeAutoObservable } from 'mobx'; | ||
7 | |||
8 | import { | ||
9 | EditorTheme, | ||
10 | EditorThemeData, | ||
11 | DEFAULT_THEME, | ||
12 | EDITOR_THEMES, | ||
13 | } from './EditorTheme'; | ||
14 | |||
15 | export class ThemeStore { | ||
16 | currentTheme: EditorTheme = DEFAULT_THEME; | ||
17 | |||
18 | constructor() { | ||
19 | makeAutoObservable(this); | ||
20 | } | ||
21 | |||
22 | toggleDarkMode(): void { | ||
23 | this.currentTheme = this.currentThemeData.toggleDarkMode; | ||
24 | } | ||
25 | |||
26 | private get currentThemeData(): EditorThemeData { | ||
27 | return EDITOR_THEMES[this.currentTheme]; | ||
28 | } | ||
29 | |||
30 | get materialUiTheme(): Theme { | ||
31 | const themeData = this.currentThemeData; | ||
32 | const materialUiTheme = createTheme({ | ||
33 | palette: { | ||
34 | mode: themeData.paletteMode, | ||
35 | background: { | ||
36 | default: themeData.background, | ||
37 | paper: themeData.paper, | ||
38 | }, | ||
39 | primary: { | ||
40 | main: themeData.primary, | ||
41 | }, | ||
42 | secondary: { | ||
43 | main: themeData.secondary, | ||
44 | }, | ||
45 | }, | ||
46 | }); | ||
47 | return responsiveFontSizes(materialUiTheme); | ||
48 | } | ||
49 | |||
50 | get codeMirrorTheme(): string { | ||
51 | return `problem-${this.currentThemeData.className}`; | ||
52 | } | ||
53 | } | ||