aboutsummaryrefslogtreecommitdiffstats
path: root/language-web/src/main
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-09-17 19:10:19 +0200
committerLibravatar Kristóf Marussy <marussy@mit.bme.hu>2021-09-17 19:10:19 +0200
commitefdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7 (patch)
treec623e8b3fba326dcbb57228f1ddbfbf55eeec494 /language-web/src/main
parentAdd ESLint config (diff)
downloadrefinery-efdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7.tar.gz
refinery-efdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7.tar.zst
refinery-efdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7.zip
Frontend color theme
Diffstat (limited to 'language-web/src/main')
-rw-r--r--language-web/src/main/css/index.scss105
-rw-r--r--language-web/src/main/css/themeVariables.module.scss9
-rw-r--r--language-web/src/main/css/themes.scss36
-rw-r--r--language-web/src/main/js/RootStore.tsx6
-rw-r--r--language-web/src/main/js/editor/EditorStore.ts16
-rw-r--r--language-web/src/main/js/global.d.ts5
-rw-r--r--language-web/src/main/js/index.tsx32
-rw-r--r--language-web/src/main/js/theme/EditorTheme.ts47
-rw-r--r--language-web/src/main/js/theme/ThemeProvider.tsx15
-rw-r--r--language-web/src/main/js/theme/ThemeStore.ts53
10 files changed, 269 insertions, 55 deletions
diff --git a/language-web/src/main/css/index.scss b/language-web/src/main/css/index.scss
index c92588b8..21a9c05b 100644
--- a/language-web/src/main/css/index.scss
+++ b/language-web/src/main/css/index.scss
@@ -1,3 +1,4 @@
1@use 'sass:map';
1@use '@fontsource/roboto/scss/mixins' as Roboto; 2@use '@fontsource/roboto/scss/mixins' as Roboto;
2@use '@fontsource/jetbrains-mono/scss/mixins' as JetbrainsMono; 3@use '@fontsource/jetbrains-mono/scss/mixins' as JetbrainsMono;
3 4
@@ -5,14 +6,16 @@
5@import 'codemirror/addon/hint/show-hint'; 6@import 'codemirror/addon/hint/show-hint';
6@import 'codemirror/theme/material-darker'; 7@import 'codemirror/theme/material-darker';
7 8
8$robotoWeights: 300, 400, 500, 700; 9@import './themes';
9@each $weight in $robotoWeights { 10
11$fontWeights: 300, 400, 500, 700;
12@each $weight in $fontWeights {
10 @include Roboto.fontFace($fontName: 'Roboto', $weight: $weight); 13 @include Roboto.fontFace($fontName: 'Roboto', $weight: $weight);
11 @include Roboto.fontFace($fontName: 'Roboto', $weight: $weight, $style: italic); 14 @include Roboto.fontFace($fontName: 'Roboto', $weight: $weight, $style: italic);
12} 15}
13 16
14$jetbrainsMonoWeights: 400, 700; 17$monoFontWeights: 400, 700;
15@each $weight in $jetbrainsMonoWeights { 18@each $weight in $monoFontWeights {
16 @include JetbrainsMono.fontFace($fontName: 'JetBrains Mono', $weight: $weight); 19 @include JetbrainsMono.fontFace($fontName: 'JetBrains Mono', $weight: $weight);
17 @include JetbrainsMono.fontFace($fontName: 'JetBrains Mono', $weight: $weight, $style: italic); 20 @include JetbrainsMono.fontFace($fontName: 'JetBrains Mono', $weight: $weight, $style: italic);
18} 21}
@@ -35,8 +38,78 @@ body {
35 text-rendering: optimizeLegibility; 38 text-rendering: optimizeLegibility;
36} 39}
37 40
41@each $themeName, $theme in $themes {
42 .cm-s-problem-#{$themeName} {
43 &.CodeMirror {
44 background: map.get($theme, 'background');
45 color: map.get($theme, 'foreground');
46 }
47
48 .CodeMirror-gutters {
49 background: map.get($theme, 'background');
50 border: none;
51 }
52
53 .CodeMirror-cursor {
54 border-left: 1px solid map.get($theme, 'cursor');
55 }
56
57 div.CodeMirror-selected,
58 &.CodeMirror-focused div.CodeMirror-selected,
59 .CodeMirror-line::selection,
60 .CodeMirror-line > span::selection,
61 .CodeMirror-line > span > span::selection {
62 background: map.get($theme, 'selection');
63 }
64
65 .CodeMirror-guttermarker,
66 .CodeMirror-guttermarker-subtle,
67 .CodeMirror-linenumber {
68 color: map.get($theme, 'lineNumber');
69 }
70
71 .CodeMirror-activeline-background {
72 background: map.get($theme, 'currentLine');
73 }
74
75 .CodeMirror-activeline-gutter {
76 background: map.get($theme, 'currentLine');
77
78 .CodeMirror-guttermarker,
79 .CodeMirror-guttermarker-subtle,
80 .CodeMirror-linenumber {
81 color: map.get($theme, 'foreground');
82 }
83 }
84
85 .cm-keyword {
86 color: map.get($theme, 'keyword');
87 }
88
89 .cm-number {
90 color: map.get($theme, 'number');
91 }
92
93 .cm-lparen, .cm-rparen {
94 color: map.get($theme, 'delimiter');
95 }
96
97 .problem-predicate, .problem-class, .problem-reference, .problem-enum {
98 color: map.get($theme, 'predicate');
99 }
100
101 .problem-unique-node {
102 color: map.get($theme, 'uniqueNode');
103 }
104
105 .problem-variable {
106 color: map.get($theme, 'variable');
107 }
108 }
109}
110
38.CodeMirror-hints { 111.CodeMirror-hints {
39 background: #333333; 112 background: #333;
40 border: 0; 113 border: 0;
41 border-radius: 4px; 114 border-radius: 4px;
42 box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 115 box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2),
@@ -107,38 +180,18 @@ li.CodeMirror-hint-active {
107 background: rgba(128, 203, 196, 0.2); 180 background: rgba(128, 203, 196, 0.2);
108} 181}
109 182
183
110.xtext-marker_write { 184.xtext-marker_write {
111 background: rgba(255, 229, 100, 0.2); 185 background: rgba(255, 229, 100, 0.2);
112} 186}
113 187
114.problem-class, .problem-enum {
115 @extend .cm-type;
116}
117
118.problem-abstract { 188.problem-abstract {
119 font-style: italic; 189 font-style: italic;
120} 190}
121 191
122.problem-reference {
123 @extend .cm-def;
124}
125
126.problem-containment { 192.problem-containment {
127 font-weight: 700; 193 font-weight: 700;
128} 194}
129
130.problem-unique-node {
131 @extend .cm-atom;
132}
133
134.problem-new-node { 195.problem-new-node {
135 font-style: italic; 196 font-style: italic;
136} 197}
137
138.problem-variable {
139 @extend .cm-variable;
140}
141
142.problem-singleton-variable {
143 opacity: 0.6;
144}
diff --git a/language-web/src/main/css/themeVariables.module.scss b/language-web/src/main/css/themeVariables.module.scss
new file mode 100644
index 00000000..85af4219
--- /dev/null
+++ b/language-web/src/main/css/themeVariables.module.scss
@@ -0,0 +1,9 @@
1@import './themes';
2
3:export {
4 @each $themeName, $theme in $themes {
5 @each $variable, $value in $theme {
6 #{$themeName}--#{$variable}: $value,
7 }
8 }
9}
diff --git a/language-web/src/main/css/themes.scss b/language-web/src/main/css/themes.scss
new file mode 100644
index 00000000..0118290d
--- /dev/null
+++ b/language-web/src/main/css/themes.scss
@@ -0,0 +1,36 @@
1$themes: (
2 'dark': (
3 'foreground': #abb2bf,
4 'background': #282c34,
5 'paper': #21252b,
6 'primary': #56b6c2,
7 'secondary': #ff5370,
8 'keyword': #56b6c2,
9 'predicate': #d6e9ff,
10 'variable': #c8ae9d,
11 'uniqueNode': #d6e9ff,
12 'number': #6e88a6,
13 'delimiter': #6f7682,
14 'cursor': #56b6c2,
15 'selection': #3e4452,
16 'currentLine': #2c323c,
17 'lineNumber': #5c6340,
18 ),
19 'light': (
20 'foreground': #abb2bf,
21 'background': #282c34,
22 'paper': #21252b,
23 'primary': #56b6c2,
24 'secondary': #ff5370,
25 'keyword': #56b6c2,
26 'predicate': #d6e9ff,
27 'variable': #c8ae9d,
28 'uniqueNode': #d6e9ff,
29 'number': #6e88a6,
30 'delimiter': #6f7682,
31 'cursor': #f3efe7,
32 'selection': #3e4452,
33 'currentLine': #2c323c,
34 'lineNumber': #5c6340,
35 ),
36);
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 @@
1import React, { createContext, useContext } from 'react'; 1import React, { createContext, useContext } from 'react';
2 2
3import { EditorStore } from './editor/EditorStore'; 3import { EditorStore } from './editor/EditorStore';
4import { ThemeStore } from './theme/ThemeStore';
4 5
5export class RootStore { 6export 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 @@
1import { Editor, EditorConfiguration } from 'codemirror'; 1import { Editor, EditorConfiguration } from 'codemirror';
2import 'codemirror/addon/selection/active-line';
2import { 3import {
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
16import { ThemeStore } from '../theme/ThemeStore';
17
15const xtextLang = 'problem'; 18const xtextLang = 'problem';
16 19
17const xtextOptions: IXtextOptions = { 20const xtextOptions: IXtextOptions = {
@@ -22,10 +25,12 @@ const xtextOptions: IXtextOptions = {
22const codeMirrorGlobalOptions: EditorConfiguration = { 25const 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
28export class EditorStore { 31export 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 @@
1declare 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';
2import React from 'react'; 2import React from 'react';
3import { render } from 'react-dom'; 3import { render } from 'react-dom';
4import CssBaseline from '@material-ui/core/CssBaseline'; 4import CssBaseline from '@material-ui/core/CssBaseline';
5import { ThemeProvider, createTheme } from '@material-ui/core/styles';
6import { getCache } from 'tss-react/cache'; 5import { getCache } from 'tss-react/cache';
7 6
8import { App } from './App'; 7import { App } from './App';
9import { RootStore, RootStoreProvider } from './RootStore'; 8import { RootStore, RootStoreProvider } from './RootStore';
9import { ThemeProvider } from './theme/ThemeProvider';
10 10
11import '../css/index.scss'; 11import '../css/index.scss';
12 12
@@ -49,31 +49,15 @@ scope Family = 1, Person += 5..10.
49const rootStore = new RootStore(); 49const rootStore = new RootStore();
50rootStore.editorStore.updateValue(initialValue); 50rootStore.editorStore.updateValue(initialValue);
51 51
52const 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
68const app = ( 52const 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
79render(app, document.getElementById('app')); 63render(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 @@
1import { PaletteMode } from '@material-ui/core';
2
3import cssVariables from '../../css/themeVariables.module.scss';
4
5export enum EditorTheme {
6 Light,
7 Dark,
8}
9
10export 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
42export const DEFAULT_THEME = EditorTheme.Dark;
43
44export 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 @@
1import { ThemeProvider as MaterialUiThemeProvider } from '@material-ui/core/styles';
2import { observer } from 'mobx-react-lite';
3import React from 'react';
4
5import { useRootStore } from '../RootStore';
6
7export 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 @@
1import {
2 Theme,
3 createTheme,
4 responsiveFontSizes,
5} from '@material-ui/core/styles';
6import { makeAutoObservable } from 'mobx';
7
8import {
9 EditorTheme,
10 EditorThemeData,
11 DEFAULT_THEME,
12 EDITOR_THEMES,
13} from './EditorTheme';
14
15export 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}