diff options
author | Kristóf Marussy <kristof@marussy.com> | 2022-08-13 23:49:06 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2022-08-15 18:03:58 +0200 |
commit | c7abf6e90285857344d7e4f85553a89df971fc3c (patch) | |
tree | 1e86b700d7314a40c26612e2f6aa54de19f86108 /subprojects/frontend/src | |
parent | feat(frontend): light/dark mode switch (diff) | |
download | refinery-c7abf6e90285857344d7e4f85553a89df971fc3c.tar.gz refinery-c7abf6e90285857344d7e4f85553a89df971fc3c.tar.zst refinery-c7abf6e90285857344d7e4f85553a89df971fc3c.zip |
refactor(frondend): dark theme tweaks
Diffstat (limited to 'subprojects/frontend/src')
-rw-r--r-- | subprojects/frontend/src/App.tsx | 12 | ||||
-rw-r--r-- | subprojects/frontend/src/TopBar.tsx | 20 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorButtons.tsx | 34 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorParent.ts | 46 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/GenerateButton.tsx | 7 | ||||
-rw-r--r-- | subprojects/frontend/src/theme/ThemeProvider.tsx | 66 |
6 files changed, 104 insertions, 81 deletions
diff --git a/subprojects/frontend/src/App.tsx b/subprojects/frontend/src/App.tsx index 12cf9311..12c66eb9 100644 --- a/subprojects/frontend/src/App.tsx +++ b/subprojects/frontend/src/App.tsx | |||
@@ -1,4 +1,5 @@ | |||
1 | import Box from '@mui/material/Box'; | 1 | import Box from '@mui/material/Box'; |
2 | import Toolbar from '@mui/material/Toolbar'; | ||
2 | import React from 'react'; | 3 | import React from 'react'; |
3 | 4 | ||
4 | import TopBar from './TopBar'; | 5 | import TopBar from './TopBar'; |
@@ -10,16 +11,11 @@ export default function App(): JSX.Element { | |||
10 | return ( | 11 | return ( |
11 | <Box display="flex" flexDirection="column" sx={{ height: '100vh' }}> | 12 | <Box display="flex" flexDirection="column" sx={{ height: '100vh' }}> |
12 | <TopBar /> | 13 | <TopBar /> |
13 | <Box | 14 | <Toolbar variant="dense"> |
14 | display="flex" | ||
15 | justifyContent="space-between" | ||
16 | alignItems="center" | ||
17 | p={1} | ||
18 | > | ||
19 | <EditorButtons /> | 15 | <EditorButtons /> |
20 | <GenerateButton /> | 16 | <GenerateButton /> |
21 | </Box> | 17 | </Toolbar> |
22 | <Box flexGrow={1} flexShrink={1} sx={{ overflow: 'auto' }}> | 18 | <Box flexGrow={1} flexShrink={1} overflow="auto"> |
23 | <EditorArea /> | 19 | <EditorArea /> |
24 | </Box> | 20 | </Box> |
25 | </Box> | 21 | </Box> |
diff --git a/subprojects/frontend/src/TopBar.tsx b/subprojects/frontend/src/TopBar.tsx index 5ac74e7a..af571a1e 100644 --- a/subprojects/frontend/src/TopBar.tsx +++ b/subprojects/frontend/src/TopBar.tsx | |||
@@ -1,6 +1,4 @@ | |||
1 | import MenuIcon from '@mui/icons-material/Menu'; | ||
2 | import AppBar from '@mui/material/AppBar'; | 1 | import AppBar from '@mui/material/AppBar'; |
3 | import IconButton from '@mui/material/IconButton'; | ||
4 | import Toolbar from '@mui/material/Toolbar'; | 2 | import Toolbar from '@mui/material/Toolbar'; |
5 | import Typography from '@mui/material/Typography'; | 3 | import Typography from '@mui/material/Typography'; |
6 | import React from 'react'; | 4 | import React from 'react'; |
@@ -9,16 +7,16 @@ import ToggleDarkModeButton from './ToggleDarkModeButton'; | |||
9 | 7 | ||
10 | export default function TopBar(): JSX.Element { | 8 | export default function TopBar(): JSX.Element { |
11 | return ( | 9 | return ( |
12 | <AppBar position="static" color="primary"> | 10 | <AppBar |
11 | position="static" | ||
12 | elevation={0} | ||
13 | color="transparent" | ||
14 | sx={(theme) => ({ | ||
15 | background: theme.palette.highlight.activeLine, | ||
16 | borderBottom: `1px solid ${theme.palette.divider2}`, | ||
17 | })} | ||
18 | > | ||
13 | <Toolbar> | 19 | <Toolbar> |
14 | <IconButton | ||
15 | edge="start" | ||
16 | sx={{ mr: 2 }} | ||
17 | color="inherit" | ||
18 | aria-label="menu" | ||
19 | > | ||
20 | <MenuIcon /> | ||
21 | </IconButton> | ||
22 | <Typography variant="h6" component="h1" flexGrow={1}> | 20 | <Typography variant="h6" component="h1" flexGrow={1}> |
23 | Refinery | 21 | Refinery |
24 | </Typography> | 22 | </Typography> |
diff --git a/subprojects/frontend/src/editor/EditorButtons.tsx b/subprojects/frontend/src/editor/EditorButtons.tsx index 652ca71e..1412a314 100644 --- a/subprojects/frontend/src/editor/EditorButtons.tsx +++ b/subprojects/frontend/src/editor/EditorButtons.tsx | |||
@@ -36,24 +36,22 @@ function EditorButtons(): JSX.Element { | |||
36 | const { editorStore } = useRootStore(); | 36 | const { editorStore } = useRootStore(); |
37 | 37 | ||
38 | return ( | 38 | return ( |
39 | <Stack direction="row" spacing={1}> | 39 | <Stack direction="row" flexGrow={1}> |
40 | <Stack direction="row" alignItems="center"> | 40 | <IconButton |
41 | <IconButton | 41 | disabled={!editorStore.canUndo} |
42 | disabled={!editorStore.canUndo} | 42 | onClick={() => editorStore.undo()} |
43 | onClick={() => editorStore.undo()} | 43 | aria-label="Undo" |
44 | aria-label="Undo" | 44 | > |
45 | > | 45 | <UndoIcon fontSize="small" /> |
46 | <UndoIcon fontSize="small" /> | 46 | </IconButton> |
47 | </IconButton> | 47 | <IconButton |
48 | <IconButton | 48 | disabled={!editorStore.canRedo} |
49 | disabled={!editorStore.canRedo} | 49 | onClick={() => editorStore.redo()} |
50 | onClick={() => editorStore.redo()} | 50 | aria-label="Redo" |
51 | aria-label="Redo" | 51 | > |
52 | > | 52 | <RedoIcon fontSize="small" /> |
53 | <RedoIcon fontSize="small" /> | 53 | </IconButton> |
54 | </IconButton> | 54 | <ToggleButtonGroup size="small" sx={{ mx: 1 }}> |
55 | </Stack> | ||
56 | <ToggleButtonGroup size="small"> | ||
57 | <ToggleButton | 55 | <ToggleButton |
58 | selected={editorStore.showLineNumbers} | 56 | selected={editorStore.showLineNumbers} |
59 | onClick={() => editorStore.toggleLineNumbers()} | 57 | onClick={() => editorStore.toggleLineNumbers()} |
diff --git a/subprojects/frontend/src/editor/EditorParent.ts b/subprojects/frontend/src/editor/EditorParent.ts index 805065fc..3742b89c 100644 --- a/subprojects/frontend/src/editor/EditorParent.ts +++ b/subprojects/frontend/src/editor/EditorParent.ts | |||
@@ -1,22 +1,5 @@ | |||
1 | import { alpha, styled } from '@mui/material/styles'; | 1 | import { alpha, styled } from '@mui/material/styles'; |
2 | 2 | ||
3 | /** | ||
4 | * Returns a squiggly underline background image encoded as a CSS `url()` data URI with Base64. | ||
5 | * | ||
6 | * Based on | ||
7 | * https://github.com/codemirror/lint/blob/f524b4a53b0183bb343ac1e32b228d28030d17af/src/lint.ts#L501 | ||
8 | * | ||
9 | * @param color the color of the underline | ||
10 | * @returns the CSS `url()` | ||
11 | */ | ||
12 | function underline(color: string) { | ||
13 | const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="6" height="3"> | ||
14 | <path d="m0 3 l2 -2 l1 0 l2 2 l1 0" stroke="${color}" fill="none" stroke-width=".7"/> | ||
15 | </svg>`; | ||
16 | const svgBase64 = window.btoa(svg); | ||
17 | return `url('data:image/svg+xml;base64,${svgBase64}')`; | ||
18 | } | ||
19 | |||
20 | export default styled('div', { | 3 | export default styled('div', { |
21 | name: 'EditorParent', | 4 | name: 'EditorParent', |
22 | shouldForwardProp: (propName) => propName !== 'showLineNumbers', | 5 | shouldForwardProp: (propName) => propName !== 'showLineNumbers', |
@@ -28,7 +11,9 @@ export default styled('div', { | |||
28 | borderLeftColor: color, | 11 | borderLeftColor: color, |
29 | }; | 12 | }; |
30 | codeMirrorLintStyle[`.cm-lintRange-${severity}`] = { | 13 | codeMirrorLintStyle[`.cm-lintRange-${severity}`] = { |
31 | backgroundImage: underline(color), | 14 | backgroundImage: 'none', |
15 | textDecoration: `underline wavy ${color}`, | ||
16 | textDecorationSkipInk: 'none', | ||
32 | }; | 17 | }; |
33 | }); | 18 | }); |
34 | 19 | ||
@@ -42,10 +27,9 @@ export default styled('div', { | |||
42 | }, | 27 | }, |
43 | '.cm-scroller, .cm-tooltip-autocomplete, .cm-completionLabel, .cm-completionDetail': | 28 | '.cm-scroller, .cm-tooltip-autocomplete, .cm-completionLabel, .cm-completionDetail': |
44 | { | 29 | { |
45 | fontSize: 16, | 30 | ...theme.typography.body1, |
46 | fontFamily: '"JetBrains MonoVariable", "JetBrains Mono", monospace', | 31 | fontFamily: '"JetBrains MonoVariable", "JetBrains Mono", monospace', |
47 | fontFeatureSettings: '"liga", "calt"', | 32 | fontFeatureSettings: '"liga", "calt"', |
48 | fontWeight: 400, | ||
49 | letterSpacing: 0, | 33 | letterSpacing: 0, |
50 | textRendering: 'optimizeLegibility', | 34 | textRendering: 'optimizeLegibility', |
51 | }, | 35 | }, |
@@ -61,7 +45,7 @@ export default styled('div', { | |||
61 | color: theme.palette.secondary.main, | 45 | color: theme.palette.secondary.main, |
62 | }, | 46 | }, |
63 | '.cm-activeLine': { | 47 | '.cm-activeLine': { |
64 | background: alpha(theme.palette.text.secondary, 0.06), | 48 | background: theme.palette.highlight.activeLine, |
65 | }, | 49 | }, |
66 | '.cm-foldGutter': { | 50 | '.cm-foldGutter': { |
67 | color: alpha(theme.palette.text.primary, 0), | 51 | color: alpha(theme.palette.text.primary, 0), |
@@ -103,10 +87,10 @@ export default styled('div', { | |||
103 | }, | 87 | }, |
104 | '.cm-panel': { | 88 | '.cm-panel': { |
105 | '&, & button, & input': { | 89 | '&, & button, & input': { |
106 | fontFamily: '"Roboto","Helvetica","Arial",sans-serif', | 90 | fontFamily: theme.typography.fontFamily, |
107 | }, | 91 | }, |
108 | background: theme.palette.background.paper, | 92 | background: theme.palette.background.default, |
109 | borderTop: `1px solid ${theme.palette.divider}`, | 93 | borderTop: `1px solid ${theme.palette.divider2}`, |
110 | 'button[name="close"]': { | 94 | 'button[name="close"]': { |
111 | background: 'transparent', | 95 | background: 'transparent', |
112 | color: theme.palette.text.secondary, | 96 | color: theme.palette.text.secondary, |
@@ -114,6 +98,7 @@ export default styled('div', { | |||
114 | }, | 98 | }, |
115 | }, | 99 | }, |
116 | '.cm-panel.cm-panel-lint': { | 100 | '.cm-panel.cm-panel-lint': { |
101 | boderBottom: 'none', | ||
117 | 'button[name="close"]': { | 102 | 'button[name="close"]': { |
118 | // Close button interferes with scrollbar, so we better hide it. | 103 | // Close button interferes with scrollbar, so we better hide it. |
119 | // The panel can still be closed from the toolbar. | 104 | // The panel can still be closed from the toolbar. |
@@ -121,18 +106,13 @@ export default styled('div', { | |||
121 | }, | 106 | }, |
122 | ul: { | 107 | ul: { |
123 | li: { | 108 | li: { |
124 | borderBottom: `1px solid ${theme.palette.divider}`, | ||
125 | cursor: 'pointer', | 109 | cursor: 'pointer', |
126 | color: theme.palette.text.primary, | 110 | color: theme.palette.text.primary, |
127 | }, | 111 | }, |
128 | '[aria-selected]': { | 112 | '[aria-selected], &:focus [aria-selected]': { |
129 | background: theme.palette.selection.main, | 113 | background: theme.palette.selection.main, |
130 | color: theme.palette.selection.contrastText, | 114 | color: theme.palette.selection.contrastText, |
131 | }, | 115 | }, |
132 | '&:focus [aria-selected]': { | ||
133 | background: theme.palette.primary.main, | ||
134 | color: theme.palette.primary.contrastText, | ||
135 | }, | ||
136 | }, | 116 | }, |
137 | }, | 117 | }, |
138 | '.cm-foldPlaceholder': { | 118 | '.cm-foldPlaceholder': { |
@@ -159,7 +139,7 @@ export default styled('div', { | |||
159 | }, | 139 | }, |
160 | '.tok-comment': { | 140 | '.tok-comment': { |
161 | fontStyle: 'italic', | 141 | fontStyle: 'italic', |
162 | color: theme.palette.text.disabled, | 142 | color: theme.palette.highlight.comment, |
163 | }, | 143 | }, |
164 | '.tok-number': { | 144 | '.tok-number': { |
165 | color: theme.palette.highlight.number, | 145 | color: theme.palette.highlight.number, |
@@ -225,9 +205,9 @@ export default styled('div', { | |||
225 | fontStyle: 'normal', | 205 | fontStyle: 'normal', |
226 | }, | 206 | }, |
227 | '[aria-selected]': { | 207 | '[aria-selected]': { |
228 | background: `${theme.palette.selection.main} !important`, | 208 | background: `${theme.palette.primary.main} !important`, |
229 | '.cm-completionIcon, .cm-completionLabel, .cm-completionDetail': { | 209 | '.cm-completionIcon, .cm-completionLabel, .cm-completionDetail': { |
230 | color: theme.palette.selection.contrastText, | 210 | color: theme.palette.primary.contrastText, |
231 | }, | 211 | }, |
232 | }, | 212 | }, |
233 | }, | 213 | }, |
diff --git a/subprojects/frontend/src/editor/GenerateButton.tsx b/subprojects/frontend/src/editor/GenerateButton.tsx index fc337da9..4d5c4e44 100644 --- a/subprojects/frontend/src/editor/GenerateButton.tsx +++ b/subprojects/frontend/src/editor/GenerateButton.tsx | |||
@@ -22,11 +22,7 @@ function GenerateButton(): JSX.Element { | |||
22 | 22 | ||
23 | if (errorCount > 0) { | 23 | if (errorCount > 0) { |
24 | return ( | 24 | return ( |
25 | <Button | 25 | <Button color="error" onClick={() => editorStore.toggleLintPanel()}> |
26 | variant="outlined" | ||
27 | color="error" | ||
28 | onClick={() => editorStore.toggleLintPanel()} | ||
29 | > | ||
30 | {summary} | 26 | {summary} |
31 | </Button> | 27 | </Button> |
32 | ); | 28 | ); |
@@ -34,7 +30,6 @@ function GenerateButton(): JSX.Element { | |||
34 | 30 | ||
35 | return ( | 31 | return ( |
36 | <Button | 32 | <Button |
37 | variant="outlined" | ||
38 | color={warningCount > 0 ? 'warning' : 'primary'} | 33 | color={warningCount > 0 ? 'warning' : 'primary'} |
39 | startIcon={<PlayArrowIcon />} | 34 | startIcon={<PlayArrowIcon />} |
40 | > | 35 | > |
diff --git a/subprojects/frontend/src/theme/ThemeProvider.tsx b/subprojects/frontend/src/theme/ThemeProvider.tsx index 1b8e49f0..9a8fdd44 100644 --- a/subprojects/frontend/src/theme/ThemeProvider.tsx +++ b/subprojects/frontend/src/theme/ThemeProvider.tsx | |||
@@ -1,41 +1,76 @@ | |||
1 | import { | 1 | import { |
2 | alpha, | ||
2 | createTheme, | 3 | createTheme, |
4 | type Components, | ||
3 | responsiveFontSizes, | 5 | responsiveFontSizes, |
4 | type ThemeOptions, | 6 | type ThemeOptions, |
5 | ThemeProvider as MaterialUiThemeProvider, | 7 | ThemeProvider as MaterialUiThemeProvider, |
6 | } from '@mui/material/styles'; | 8 | } from '@mui/material/styles'; |
7 | import { observer } from 'mobx-react-lite'; | 9 | import { observer } from 'mobx-react-lite'; |
8 | import React, { type CSSProperties, type ReactNode } from 'react'; | 10 | import React, { type ReactNode } from 'react'; |
9 | 11 | ||
10 | import { useRootStore } from '../RootStore'; | 12 | import { useRootStore } from '../RootStore'; |
11 | 13 | ||
12 | import EditorTheme from './EditorTheme'; | 14 | import EditorTheme from './EditorTheme'; |
13 | 15 | ||
14 | interface HighlightStyles { | 16 | interface HighlightStyles { |
15 | number: CSSProperties['color']; | 17 | number: string; |
16 | parameter: CSSProperties['color']; | 18 | parameter: string; |
19 | comment: string; | ||
20 | activeLine: string; | ||
17 | occurences: { | 21 | occurences: { |
18 | read: CSSProperties['color']; | 22 | read: string; |
19 | write: CSSProperties['color']; | 23 | write: string; |
20 | }; | 24 | }; |
21 | } | 25 | } |
22 | 26 | ||
23 | declare module '@mui/material/styles' { | 27 | declare module '@mui/material/styles' { |
24 | interface Palette { | 28 | interface Palette { |
29 | divider2: string; | ||
25 | selection: Palette['primary']; | 30 | selection: Palette['primary']; |
26 | highlight: HighlightStyles; | 31 | highlight: HighlightStyles; |
27 | } | 32 | } |
28 | 33 | ||
29 | interface PaletteOptions { | 34 | interface PaletteOptions { |
35 | divider2: string; | ||
30 | selection: PaletteOptions['primary']; | 36 | selection: PaletteOptions['primary']; |
31 | highlight: HighlightStyles; | 37 | highlight: HighlightStyles; |
32 | } | 38 | } |
33 | } | 39 | } |
34 | 40 | ||
35 | function getMUIThemeOptions(currentTheme: EditorTheme): ThemeOptions { | 41 | function getMUIThemeOptions(currentTheme: EditorTheme): ThemeOptions { |
42 | const components: Components = { | ||
43 | MuiButton: { | ||
44 | styleOverrides: { | ||
45 | root: { borderRadius: '50em' }, | ||
46 | text: { padding: '6px 16px' }, | ||
47 | textSizeSmall: { padding: '4px 10px' }, | ||
48 | textSizeLarge: { padding: '8px 22px' }, | ||
49 | }, | ||
50 | }, | ||
51 | MuiToggleButtonGroup: { | ||
52 | styleOverrides: { | ||
53 | groupedHorizontal: { | ||
54 | borderRadius: '50em', | ||
55 | ':first-of-type': { paddingLeft: 15 }, | ||
56 | ':last-of-type': { paddingRight: 15 }, | ||
57 | '&.MuiToggleButton-sizeSmall': { | ||
58 | ':first-of-type': { paddingLeft: 9 }, | ||
59 | ':last-of-type': { paddingRight: 9 }, | ||
60 | }, | ||
61 | '&.MuiToggleButton-sizeLarge': { | ||
62 | ':first-of-type': { paddingLeft: 21 }, | ||
63 | ':last-of-type': { paddingRight: 21 }, | ||
64 | }, | ||
65 | }, | ||
66 | }, | ||
67 | }, | ||
68 | }; | ||
69 | |||
36 | switch (currentTheme) { | 70 | switch (currentTheme) { |
37 | case EditorTheme.Light: | 71 | case EditorTheme.Light: |
38 | return { | 72 | return { |
73 | components, | ||
39 | palette: { | 74 | palette: { |
40 | mode: 'light', | 75 | mode: 'light', |
41 | primary: { main: '#0097a7' }, | 76 | primary: { main: '#0097a7' }, |
@@ -43,9 +78,12 @@ function getMUIThemeOptions(currentTheme: EditorTheme): ThemeOptions { | |||
43 | main: '#c8e4fb', | 78 | main: '#c8e4fb', |
44 | contrastText: '#000', | 79 | contrastText: '#000', |
45 | }, | 80 | }, |
81 | divider2: '#d7d7d7', | ||
46 | highlight: { | 82 | highlight: { |
47 | number: '#1976d2', | 83 | number: '#1976d2', |
48 | parameter: '#6a3e3e', | 84 | parameter: '#6a3e3e', |
85 | comment: alpha('#000', 0.38), | ||
86 | activeLine: '#f5f5f5', | ||
49 | occurences: { | 87 | occurences: { |
50 | read: '#ceccf7', | 88 | read: '#ceccf7', |
51 | write: '#f0d8a8', | 89 | write: '#f0d8a8', |
@@ -55,9 +93,25 @@ function getMUIThemeOptions(currentTheme: EditorTheme): ThemeOptions { | |||
55 | }; | 93 | }; |
56 | case EditorTheme.Dark: | 94 | case EditorTheme.Dark: |
57 | return { | 95 | return { |
96 | components, | ||
58 | palette: { | 97 | palette: { |
59 | mode: 'dark', | 98 | mode: 'dark', |
60 | primary: { main: '#56b6c2' }, | 99 | primary: { main: '#56b6c2' }, |
100 | error: { main: '#e06c75' }, | ||
101 | warning: { main: '#e5c07b' }, | ||
102 | success: { main: '#57a470' }, | ||
103 | info: { main: '#52b8ff' }, | ||
104 | background: { | ||
105 | default: '#282c34', | ||
106 | paper: '#21252b', | ||
107 | }, | ||
108 | text: { | ||
109 | primary: '#ebebff', | ||
110 | secondary: '#abb2bf', | ||
111 | disabled: '#4b5263', | ||
112 | }, | ||
113 | divider: alpha('#abb2bf', 0.16), | ||
114 | divider2: '#181a1f', | ||
61 | selection: { | 115 | selection: { |
62 | main: '#3e4453', | 116 | main: '#3e4453', |
63 | contrastText: '#fff', | 117 | contrastText: '#fff', |
@@ -65,6 +119,8 @@ function getMUIThemeOptions(currentTheme: EditorTheme): ThemeOptions { | |||
65 | highlight: { | 119 | highlight: { |
66 | number: '#6188a6', | 120 | number: '#6188a6', |
67 | parameter: '#c8ae9d', | 121 | parameter: '#c8ae9d', |
122 | comment: '#6b717d', | ||
123 | activeLine: '#21252b', | ||
68 | occurences: { | 124 | occurences: { |
69 | read: 'rgba(255, 255, 255, 0.15)', | 125 | read: 'rgba(255, 255, 255, 0.15)', |
70 | write: 'rgba(255, 255, 128, 0.4)', | 126 | write: 'rgba(255, 255, 128, 0.4)', |