From efdb6c6620e9ea7f5861efbef7d3462bf3c7bcd7 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Fri, 17 Sep 2021 19:10:19 +0200 Subject: Frontend color theme --- language-web/src/main/js/RootStore.tsx | 6 ++- language-web/src/main/js/editor/EditorStore.ts | 16 +++++-- language-web/src/main/js/global.d.ts | 5 +++ language-web/src/main/js/index.tsx | 32 ++++---------- language-web/src/main/js/theme/EditorTheme.ts | 47 +++++++++++++++++++++ language-web/src/main/js/theme/ThemeProvider.tsx | 15 +++++++ language-web/src/main/js/theme/ThemeStore.ts | 53 ++++++++++++++++++++++++ 7 files changed, 145 insertions(+), 29 deletions(-) create mode 100644 language-web/src/main/js/global.d.ts create mode 100644 language-web/src/main/js/theme/EditorTheme.ts create mode 100644 language-web/src/main/js/theme/ThemeProvider.tsx create mode 100644 language-web/src/main/js/theme/ThemeStore.ts (limited to 'language-web/src/main/js') 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 @@ import React, { createContext, useContext } from 'react'; import { EditorStore } from './editor/EditorStore'; +import { ThemeStore } from './theme/ThemeStore'; export class RootStore { editorStore; + themeStore; + constructor() { - this.editorStore = new EditorStore(); + this.themeStore = new ThemeStore(); + this.editorStore = new EditorStore(this.themeStore); } } 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 @@ import { Editor, EditorConfiguration } from 'codemirror'; +import 'codemirror/addon/selection/active-line'; import { createAtom, makeAutoObservable, @@ -12,6 +13,8 @@ import { removeServices, } from 'xtext/xtext-codemirror'; +import { ThemeStore } from '../theme/ThemeStore'; + const xtextLang = 'problem'; const xtextOptions: IXtextOptions = { @@ -22,10 +25,12 @@ const xtextOptions: IXtextOptions = { const codeMirrorGlobalOptions: EditorConfiguration = { mode: `xtext/${xtextLang}`, indentUnit: 2, - theme: 'material-darker', + styleActiveLine: true, }; export class EditorStore { + themeStore; + atom; editor?: Editor; @@ -36,9 +41,11 @@ export class EditorStore { showLineNumbers = false; - constructor() { + constructor(themeStore: ThemeStore) { + this.themeStore = themeStore; this.atom = createAtom('EditorStore'); makeAutoObservable(this, { + themeStore: false, atom: false, editor: observable.ref, xtextServices: observable.ref, @@ -65,8 +72,8 @@ export class EditorStore { if (this.editor) { removeServices(this.editor); } - this.editor = undefined; - this.xtextServices = undefined; + delete this.editor; + delete this.xtextServices; } /** @@ -89,6 +96,7 @@ export class EditorStore { get codeMirrorOptions(): EditorConfiguration { return { ...codeMirrorGlobalOptions, + theme: this.themeStore.codeMirrorTheme, lineNumbers: this.showLineNumbers, }; } 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 @@ +declare module '*.module.scss' { + const cssVariables: { [key in string]?: string }; + // eslint-disable-next-line import/no-default-export + export default cssVariables; +} 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'; import React from 'react'; import { render } from 'react-dom'; import CssBaseline from '@material-ui/core/CssBaseline'; -import { ThemeProvider, createTheme } from '@material-ui/core/styles'; import { getCache } from 'tss-react/cache'; import { App } from './App'; import { RootStore, RootStoreProvider } from './RootStore'; +import { ThemeProvider } from './theme/ThemeProvider'; import '../css/index.scss'; @@ -49,31 +49,15 @@ scope Family = 1, Person += 5..10. const rootStore = new RootStore(); rootStore.editorStore.updateValue(initialValue); -const theme = createTheme({ - palette: { - mode: 'dark', - background: { - default: '#212121', - paper: '#2f2f2f', - }, - primary: { - main: '#82aaff', - }, - secondary: { - main: '#ff5370', - }, - }, -}); - const app = ( - - - - + + + + - - - + + + ); 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 @@ +import { PaletteMode } from '@material-ui/core'; + +import cssVariables from '../../css/themeVariables.module.scss'; + +export enum EditorTheme { + Light, + Dark, +} + +export class EditorThemeData { + className: string; + + paletteMode: PaletteMode; + + toggleDarkMode: EditorTheme; + + foreground!: string; + + background!: string; + + paper!: string; + + primary!: string; + + secondary!: string; + + constructor(className: string, paletteMode: PaletteMode, toggleDarkMode: EditorTheme) { + this.className = className; + this.paletteMode = paletteMode; + this.toggleDarkMode = toggleDarkMode; + Reflect.ownKeys(this).forEach((key) => { + if (!Reflect.get(this, key)) { + const cssKey = `${this.className}--${key.toString()}`; + if (cssKey in cssVariables) { + Reflect.set(this, key, cssVariables[cssKey]); + } + } + }); + } +} + +export const DEFAULT_THEME = EditorTheme.Dark; + +export const EDITOR_THEMES: { [key in EditorTheme]: EditorThemeData } = { + [EditorTheme.Light]: new EditorThemeData('light', 'light', EditorTheme.Dark), + [EditorTheme.Dark]: new EditorThemeData('dark', 'dark', EditorTheme.Light), +}; 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 @@ +import { ThemeProvider as MaterialUiThemeProvider } from '@material-ui/core/styles'; +import { observer } from 'mobx-react-lite'; +import React from 'react'; + +import { useRootStore } from '../RootStore'; + +export const ThemeProvider: React.FC = observer(({ children }) => { + const { themeStore } = useRootStore(); + + return ( + + {children} + + ); +}); 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 @@ +import { + Theme, + createTheme, + responsiveFontSizes, +} from '@material-ui/core/styles'; +import { makeAutoObservable } from 'mobx'; + +import { + EditorTheme, + EditorThemeData, + DEFAULT_THEME, + EDITOR_THEMES, +} from './EditorTheme'; + +export class ThemeStore { + currentTheme: EditorTheme = DEFAULT_THEME; + + constructor() { + makeAutoObservable(this); + } + + toggleDarkMode(): void { + this.currentTheme = this.currentThemeData.toggleDarkMode; + } + + private get currentThemeData(): EditorThemeData { + return EDITOR_THEMES[this.currentTheme]; + } + + get materialUiTheme(): Theme { + const themeData = this.currentThemeData; + const materialUiTheme = createTheme({ + palette: { + mode: themeData.paletteMode, + background: { + default: themeData.background, + paper: themeData.paper, + }, + primary: { + main: themeData.primary, + }, + secondary: { + main: themeData.secondary, + }, + }, + }); + return responsiveFontSizes(materialUiTheme); + } + + get codeMirrorTheme(): string { + return `problem-${this.currentThemeData.className}`; + } +} -- cgit v1.2.3-54-g00ecf