diff options
Diffstat (limited to 'subprojects')
-rw-r--r-- | subprojects/frontend/src/App.tsx | 19 | ||||
-rw-r--r-- | subprojects/frontend/src/Loading.tsx | 4 | ||||
-rw-r--r-- | subprojects/frontend/src/RootStore.tsx | 21 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorArea.tsx | 22 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorButtons.tsx | 52 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorPane.tsx | 38 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/EditorTheme.ts | 8 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/GenerateButton.tsx | 17 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/SearchPanelPortal.tsx | 25 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/SearchToolbar.tsx | 46 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/createEditorState.ts | 9 | ||||
-rw-r--r-- | subprojects/frontend/src/editor/editorClassNames.ts | 10 | ||||
-rw-r--r-- | subprojects/frontend/src/index.tsx | 9 |
13 files changed, 179 insertions, 101 deletions
diff --git a/subprojects/frontend/src/App.tsx b/subprojects/frontend/src/App.tsx index 12c66eb9..3a25f43a 100644 --- a/subprojects/frontend/src/App.tsx +++ b/subprojects/frontend/src/App.tsx | |||
@@ -1,23 +1,14 @@ | |||
1 | import Box from '@mui/material/Box'; | 1 | import Stack from '@mui/material/Stack'; |
2 | import Toolbar from '@mui/material/Toolbar'; | ||
3 | import React from 'react'; | 2 | import React from 'react'; |
4 | 3 | ||
5 | import TopBar from './TopBar'; | 4 | import TopBar from './TopBar'; |
6 | import EditorArea from './editor/EditorArea'; | 5 | import EditorPane from './editor/EditorPane'; |
7 | import EditorButtons from './editor/EditorButtons'; | ||
8 | import GenerateButton from './editor/GenerateButton'; | ||
9 | 6 | ||
10 | export default function App(): JSX.Element { | 7 | export default function App(): JSX.Element { |
11 | return ( | 8 | return ( |
12 | <Box display="flex" flexDirection="column" sx={{ height: '100vh' }}> | 9 | <Stack direction="column" height="100vh" overflow="auto"> |
13 | <TopBar /> | 10 | <TopBar /> |
14 | <Toolbar variant="dense"> | 11 | <EditorPane /> |
15 | <EditorButtons /> | 12 | </Stack> |
16 | <GenerateButton /> | ||
17 | </Toolbar> | ||
18 | <Box flexGrow={1} flexShrink={1} overflow="auto"> | ||
19 | <EditorArea /> | ||
20 | </Box> | ||
21 | </Box> | ||
22 | ); | 13 | ); |
23 | } | 14 | } |
diff --git a/subprojects/frontend/src/Loading.tsx b/subprojects/frontend/src/Loading.tsx index a699adca..72020a43 100644 --- a/subprojects/frontend/src/Loading.tsx +++ b/subprojects/frontend/src/Loading.tsx | |||
@@ -3,8 +3,8 @@ import { styled } from '@mui/material/styles'; | |||
3 | import React from 'react'; | 3 | import React from 'react'; |
4 | 4 | ||
5 | const LoadingRoot = styled('div')({ | 5 | const LoadingRoot = styled('div')({ |
6 | width: '100vw', | 6 | width: '100%', |
7 | height: '100vh', | 7 | height: '100%', |
8 | display: 'flex', | 8 | display: 'flex', |
9 | alignItems: 'center', | 9 | alignItems: 'center', |
10 | justifyContent: 'center', | 10 | justifyContent: 'center', |
diff --git a/subprojects/frontend/src/RootStore.tsx b/subprojects/frontend/src/RootStore.tsx index 4a267b0e..e08dd750 100644 --- a/subprojects/frontend/src/RootStore.tsx +++ b/subprojects/frontend/src/RootStore.tsx | |||
@@ -1,16 +1,31 @@ | |||
1 | import { getLogger } from 'loglevel'; | ||
2 | import { makeObservable, observable, runInAction } from 'mobx'; | ||
1 | import React, { createContext, useContext } from 'react'; | 3 | import React, { createContext, useContext } from 'react'; |
2 | 4 | ||
3 | import EditorStore from './editor/EditorStore'; | 5 | import type EditorStore from './editor/EditorStore'; |
4 | import ThemeStore from './theme/ThemeStore'; | 6 | import ThemeStore from './theme/ThemeStore'; |
5 | 7 | ||
8 | const log = getLogger('RootStore'); | ||
9 | |||
6 | export default class RootStore { | 10 | export default class RootStore { |
7 | readonly editorStore: EditorStore; | 11 | editorStore: EditorStore | undefined; |
8 | 12 | ||
9 | readonly themeStore: ThemeStore; | 13 | readonly themeStore: ThemeStore; |
10 | 14 | ||
11 | constructor(initialValue: string) { | 15 | constructor(initialValue: string) { |
12 | this.editorStore = new EditorStore(initialValue); | ||
13 | this.themeStore = new ThemeStore(); | 16 | this.themeStore = new ThemeStore(); |
17 | makeObservable(this, { | ||
18 | editorStore: observable, | ||
19 | }); | ||
20 | import('./editor/EditorStore') | ||
21 | .then(({ default: EditorStore }) => { | ||
22 | runInAction(() => { | ||
23 | this.editorStore = new EditorStore(initialValue); | ||
24 | }); | ||
25 | }) | ||
26 | .catch((error) => { | ||
27 | log.error('Failed to load EditorStore', error); | ||
28 | }); | ||
14 | } | 29 | } |
15 | } | 30 | } |
16 | 31 | ||
diff --git a/subprojects/frontend/src/editor/EditorArea.tsx b/subprojects/frontend/src/editor/EditorArea.tsx index 915ec657..1c9b031b 100644 --- a/subprojects/frontend/src/editor/EditorArea.tsx +++ b/subprojects/frontend/src/editor/EditorArea.tsx | |||
@@ -1,17 +1,15 @@ | |||
1 | import Portal from '@mui/material/Portal'; | ||
2 | import { useTheme } from '@mui/material/styles'; | 1 | import { useTheme } from '@mui/material/styles'; |
3 | import { observer } from 'mobx-react-lite'; | 2 | import { observer } from 'mobx-react-lite'; |
4 | import React, { useCallback, useEffect } from 'react'; | 3 | import React, { useCallback, useEffect } from 'react'; |
5 | 4 | ||
6 | import { useRootStore } from '../RootStore'; | 5 | import type EditorStore from './EditorStore'; |
7 | |||
8 | import EditorTheme from './EditorTheme'; | 6 | import EditorTheme from './EditorTheme'; |
9 | import SearchToolbar from './SearchToolbar'; | ||
10 | 7 | ||
11 | function EditorArea(): JSX.Element { | 8 | function EditorArea({ |
12 | const { editorStore } = useRootStore(); | 9 | editorStore, |
13 | const { searchPanel: searchPanelStore } = editorStore; | 10 | }: { |
14 | const { element: searchPanelContainer } = searchPanelStore; | 11 | editorStore: EditorStore; |
12 | }): JSX.Element { | ||
15 | const { | 13 | const { |
16 | palette: { mode: paletteMode }, | 14 | palette: { mode: paletteMode }, |
17 | } = useTheme(); | 15 | } = useTheme(); |
@@ -32,13 +30,7 @@ function EditorArea(): JSX.Element { | |||
32 | <EditorTheme | 30 | <EditorTheme |
33 | showLineNumbers={editorStore.showLineNumbers} | 31 | showLineNumbers={editorStore.showLineNumbers} |
34 | ref={editorParentRef} | 32 | ref={editorParentRef} |
35 | > | 33 | /> |
36 | {searchPanelContainer !== undefined && ( | ||
37 | <Portal container={searchPanelContainer}> | ||
38 | <SearchToolbar store={searchPanelStore} /> | ||
39 | </Portal> | ||
40 | )} | ||
41 | </EditorTheme> | ||
42 | ); | 34 | ); |
43 | } | 35 | } |
44 | 36 | ||
diff --git a/subprojects/frontend/src/editor/EditorButtons.tsx b/subprojects/frontend/src/editor/EditorButtons.tsx index 8db9dfb7..cbe7c424 100644 --- a/subprojects/frontend/src/editor/EditorButtons.tsx +++ b/subprojects/frontend/src/editor/EditorButtons.tsx | |||
@@ -15,7 +15,7 @@ import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; | |||
15 | import { observer } from 'mobx-react-lite'; | 15 | import { observer } from 'mobx-react-lite'; |
16 | import React from 'react'; | 16 | import React from 'react'; |
17 | 17 | ||
18 | import { useRootStore } from '../RootStore'; | 18 | import type EditorStore from './EditorStore'; |
19 | 19 | ||
20 | // Exhastive switch as proven by TypeScript. | 20 | // Exhastive switch as proven by TypeScript. |
21 | // eslint-disable-next-line consistent-return | 21 | // eslint-disable-next-line consistent-return |
@@ -32,22 +32,24 @@ function getLintIcon(severity: Diagnostic['severity'] | undefined) { | |||
32 | } | 32 | } |
33 | } | 33 | } |
34 | 34 | ||
35 | function EditorButtons(): JSX.Element { | 35 | function EditorButtons({ |
36 | const { editorStore } = useRootStore(); | 36 | editorStore, |
37 | 37 | }: { | |
38 | editorStore: EditorStore | undefined; | ||
39 | }): JSX.Element { | ||
38 | return ( | 40 | return ( |
39 | <Stack direction="row" flexGrow={1}> | 41 | <Stack direction="row" flexGrow={1}> |
40 | <IconButton | 42 | <IconButton |
41 | disabled={!editorStore.canUndo} | 43 | disabled={editorStore === undefined || !editorStore.canUndo} |
42 | onClick={() => editorStore.undo()} | 44 | onClick={() => editorStore?.undo()} |
43 | aria-label="Undo" | 45 | aria-label="Undo" |
44 | color="inherit" | 46 | color="inherit" |
45 | > | 47 | > |
46 | <UndoIcon fontSize="small" /> | 48 | <UndoIcon fontSize="small" /> |
47 | </IconButton> | 49 | </IconButton> |
48 | <IconButton | 50 | <IconButton |
49 | disabled={!editorStore.canRedo} | 51 | disabled={editorStore === undefined || !editorStore.canRedo} |
50 | onClick={() => editorStore.redo()} | 52 | onClick={() => editorStore?.redo()} |
51 | aria-label="Redo" | 53 | aria-label="Redo" |
52 | color="inherit" | 54 | color="inherit" |
53 | > | 55 | > |
@@ -55,38 +57,44 @@ function EditorButtons(): JSX.Element { | |||
55 | </IconButton> | 57 | </IconButton> |
56 | <ToggleButtonGroup size="small" className="rounded" sx={{ mx: 1 }}> | 58 | <ToggleButtonGroup size="small" className="rounded" sx={{ mx: 1 }}> |
57 | <ToggleButton | 59 | <ToggleButton |
58 | selected={editorStore.showLineNumbers} | 60 | selected={editorStore?.showLineNumbers ?? false} |
59 | onClick={() => editorStore.toggleLineNumbers()} | 61 | disabled={editorStore === undefined} |
62 | onClick={() => editorStore?.toggleLineNumbers()} | ||
60 | aria-label="Show line numbers" | 63 | aria-label="Show line numbers" |
61 | value="show-line-numbers" | 64 | value="show-line-numbers" |
62 | > | 65 | > |
63 | <FormatListNumberedIcon fontSize="small" /> | 66 | <FormatListNumberedIcon fontSize="small" /> |
64 | </ToggleButton> | 67 | </ToggleButton> |
65 | <ToggleButton | 68 | <ToggleButton |
66 | selected={editorStore.searchPanel.state} | 69 | selected={editorStore?.searchPanel?.state ?? false} |
67 | onClick={() => editorStore.searchPanel.toggle()} | 70 | disabled={editorStore === undefined} |
71 | onClick={() => editorStore?.searchPanel?.toggle()} | ||
68 | aria-label="Show find/replace" | 72 | aria-label="Show find/replace" |
69 | {...(editorStore.searchPanel.state && { | 73 | {...(editorStore !== undefined && |
70 | 'aria-controls': editorStore.searchPanel.id, | 74 | editorStore.searchPanel.state && { |
71 | })} | 75 | 'aria-controls': editorStore.searchPanel.id, |
76 | })} | ||
72 | value="show-search-panel" | 77 | value="show-search-panel" |
73 | > | 78 | > |
74 | <SearchIcon fontSize="small" /> | 79 | <SearchIcon fontSize="small" /> |
75 | </ToggleButton> | 80 | </ToggleButton> |
76 | <ToggleButton | 81 | <ToggleButton |
77 | selected={editorStore.lintPanel.state} | 82 | selected={editorStore?.lintPanel?.state ?? false} |
78 | onClick={() => editorStore.lintPanel.toggle()} | 83 | disabled={editorStore === undefined} |
84 | onClick={() => editorStore?.lintPanel.toggle()} | ||
79 | aria-label="Show diagnostics panel" | 85 | aria-label="Show diagnostics panel" |
80 | {...(editorStore.lintPanel.state && { | 86 | {...(editorStore !== undefined && |
81 | 'aria-controls': editorStore.lintPanel.id, | 87 | editorStore.lintPanel.state && { |
82 | })} | 88 | 'aria-controls': editorStore.lintPanel.id, |
89 | })} | ||
83 | value="show-lint-panel" | 90 | value="show-lint-panel" |
84 | > | 91 | > |
85 | {getLintIcon(editorStore.highestDiagnosticLevel)} | 92 | {getLintIcon(editorStore?.highestDiagnosticLevel)} |
86 | </ToggleButton> | 93 | </ToggleButton> |
87 | </ToggleButtonGroup> | 94 | </ToggleButtonGroup> |
88 | <IconButton | 95 | <IconButton |
89 | onClick={() => editorStore.formatText()} | 96 | disabled={editorStore === undefined} |
97 | onClick={() => editorStore?.formatText()} | ||
90 | aria-label="Automatic format" | 98 | aria-label="Automatic format" |
91 | color="inherit" | 99 | color="inherit" |
92 | > | 100 | > |
diff --git a/subprojects/frontend/src/editor/EditorPane.tsx b/subprojects/frontend/src/editor/EditorPane.tsx new file mode 100644 index 00000000..935a84e8 --- /dev/null +++ b/subprojects/frontend/src/editor/EditorPane.tsx | |||
@@ -0,0 +1,38 @@ | |||
1 | import Box from '@mui/material/Box'; | ||
2 | import Stack from '@mui/material/Stack'; | ||
3 | import Toolbar from '@mui/material/Toolbar'; | ||
4 | import { observer } from 'mobx-react-lite'; | ||
5 | import React from 'react'; | ||
6 | |||
7 | import Loading from '../Loading'; | ||
8 | import { useRootStore } from '../RootStore'; | ||
9 | |||
10 | import EditorArea from './EditorArea'; | ||
11 | import EditorButtons from './EditorButtons'; | ||
12 | import GenerateButton from './GenerateButton'; | ||
13 | import SearchPanelPortal from './SearchPanelPortal'; | ||
14 | |||
15 | function EditorPane(): JSX.Element { | ||
16 | const { editorStore } = useRootStore(); | ||
17 | |||
18 | return ( | ||
19 | <Stack direction="column" flexGrow={1} flexShrink={1} overflow="auto"> | ||
20 | <Toolbar variant="dense"> | ||
21 | <EditorButtons editorStore={editorStore} /> | ||
22 | <GenerateButton editorStore={editorStore} /> | ||
23 | </Toolbar> | ||
24 | <Box flexGrow={1} flexShrink={1} overflow="auto"> | ||
25 | {editorStore === undefined ? ( | ||
26 | <Loading /> | ||
27 | ) : ( | ||
28 | <> | ||
29 | <SearchPanelPortal editorStore={editorStore} /> | ||
30 | <EditorArea editorStore={editorStore} /> | ||
31 | </> | ||
32 | )} | ||
33 | </Box> | ||
34 | </Stack> | ||
35 | ); | ||
36 | } | ||
37 | |||
38 | export default observer(EditorPane); | ||
diff --git a/subprojects/frontend/src/editor/EditorTheme.ts b/subprojects/frontend/src/editor/EditorTheme.ts index d8680070..ca07d602 100644 --- a/subprojects/frontend/src/editor/EditorTheme.ts +++ b/subprojects/frontend/src/editor/EditorTheme.ts | |||
@@ -4,8 +4,6 @@ import infoSVG from '@material-icons/svg/svg/info/baseline.svg?raw'; | |||
4 | import warningSVG from '@material-icons/svg/svg/warning/baseline.svg?raw'; | 4 | import warningSVG from '@material-icons/svg/svg/warning/baseline.svg?raw'; |
5 | import { alpha, styled, type CSSObject } from '@mui/material/styles'; | 5 | import { alpha, styled, type CSSObject } from '@mui/material/styles'; |
6 | 6 | ||
7 | import editorClassNames from './editorClassNames'; | ||
8 | |||
9 | function svgURL(svg: string): string { | 7 | function svgURL(svg: string): string { |
10 | return `url('data:image/svg+xml;utf8,${svg}')`; | 8 | return `url('data:image/svg+xml;utf8,${svg}')`; |
11 | } | 9 | } |
@@ -315,7 +313,7 @@ export default styled('div', { | |||
315 | '.cm-gutters:hover .cm-foldGutter': { | 313 | '.cm-gutters:hover .cm-foldGutter': { |
316 | opacity: 1, | 314 | opacity: 1, |
317 | }, | 315 | }, |
318 | [`.${editorClassNames.foldMarker}`]: { | 316 | '.problem-editor-foldMarker': { |
319 | display: 'block', | 317 | display: 'block', |
320 | margin: '4px 0', | 318 | margin: '4px 0', |
321 | padding: 0, | 319 | padding: 0, |
@@ -330,10 +328,10 @@ export default styled('div', { | |||
330 | margin: '2px 0', | 328 | margin: '2px 0', |
331 | }, | 329 | }, |
332 | }, | 330 | }, |
333 | [`.${editorClassNames.foldMarkerClosed}`]: { | 331 | '.problem-editor-foldMarker-closed': { |
334 | transform: 'rotate(-90deg)', | 332 | transform: 'rotate(-90deg)', |
335 | }, | 333 | }, |
336 | [`.${editorClassNames.foldPlaceholder}`]: { | 334 | '.problem-editor-foldPlaceholder': { |
337 | ...editorFontStyle, | 335 | ...editorFontStyle, |
338 | padding: 0, | 336 | padding: 0, |
339 | fontFamily: 'inherit', | 337 | fontFamily: 'inherit', |
diff --git a/subprojects/frontend/src/editor/GenerateButton.tsx b/subprojects/frontend/src/editor/GenerateButton.tsx index 4b998af6..a28f6b4b 100644 --- a/subprojects/frontend/src/editor/GenerateButton.tsx +++ b/subprojects/frontend/src/editor/GenerateButton.tsx | |||
@@ -3,12 +3,23 @@ import Button from '@mui/material/Button'; | |||
3 | import { observer } from 'mobx-react-lite'; | 3 | import { observer } from 'mobx-react-lite'; |
4 | import React from 'react'; | 4 | import React from 'react'; |
5 | 5 | ||
6 | import { useRootStore } from '../RootStore'; | 6 | import type EditorStore from './EditorStore'; |
7 | 7 | ||
8 | const GENERATE_LABEL = 'Generate'; | 8 | const GENERATE_LABEL = 'Generate'; |
9 | 9 | ||
10 | function GenerateButton(): JSX.Element { | 10 | function GenerateButton({ |
11 | const { editorStore } = useRootStore(); | 11 | editorStore, |
12 | }: { | ||
13 | editorStore: EditorStore | undefined; | ||
14 | }): JSX.Element { | ||
15 | if (editorStore === undefined) { | ||
16 | return ( | ||
17 | <Button color="inherit" className="rounded" disabled> | ||
18 | Loading… | ||
19 | </Button> | ||
20 | ); | ||
21 | } | ||
22 | |||
12 | const { errorCount, warningCount } = editorStore; | 23 | const { errorCount, warningCount } = editorStore; |
13 | 24 | ||
14 | const diagnostics: string[] = []; | 25 | const diagnostics: string[] = []; |
diff --git a/subprojects/frontend/src/editor/SearchPanelPortal.tsx b/subprojects/frontend/src/editor/SearchPanelPortal.tsx new file mode 100644 index 00000000..e8301489 --- /dev/null +++ b/subprojects/frontend/src/editor/SearchPanelPortal.tsx | |||
@@ -0,0 +1,25 @@ | |||
1 | import Portal from '@mui/material/Portal'; | ||
2 | import { observer } from 'mobx-react-lite'; | ||
3 | import React from 'react'; | ||
4 | |||
5 | import type EditorStore from './EditorStore'; | ||
6 | import SearchToolbar from './SearchToolbar'; | ||
7 | |||
8 | function SearchPanelPortal({ | ||
9 | editorStore: { searchPanel: searchPanelStore }, | ||
10 | }: { | ||
11 | editorStore: EditorStore; | ||
12 | }): JSX.Element | null { | ||
13 | const { element: searchPanelContainer } = searchPanelStore; | ||
14 | |||
15 | if (searchPanelContainer === undefined) { | ||
16 | return null; | ||
17 | } | ||
18 | return ( | ||
19 | <Portal container={searchPanelContainer}> | ||
20 | <SearchToolbar searchPanelStore={searchPanelStore} /> | ||
21 | </Portal> | ||
22 | ); | ||
23 | } | ||
24 | |||
25 | export default observer(SearchPanelPortal); | ||
diff --git a/subprojects/frontend/src/editor/SearchToolbar.tsx b/subprojects/frontend/src/editor/SearchToolbar.tsx index 45f1336d..7e6ff4f7 100644 --- a/subprojects/frontend/src/editor/SearchToolbar.tsx +++ b/subprojects/frontend/src/editor/SearchToolbar.tsx | |||
@@ -20,12 +20,16 @@ import type SearchPanelStore from './SearchPanelStore'; | |||
20 | const SPLIT_MEDIA_QUERY = '@media (max-width: 1200px)'; | 20 | const SPLIT_MEDIA_QUERY = '@media (max-width: 1200px)'; |
21 | const ABBREVIATE_MEDIA_QUERY = '@media (max-width: 720px)'; | 21 | const ABBREVIATE_MEDIA_QUERY = '@media (max-width: 720px)'; |
22 | 22 | ||
23 | function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | 23 | function SearchToolbar({ |
24 | searchPanelStore, | ||
25 | }: { | ||
26 | searchPanelStore: SearchPanelStore; | ||
27 | }): JSX.Element { | ||
24 | const { | 28 | const { |
25 | id: panelId, | 29 | id: panelId, |
26 | query: { search, valid, caseSensitive, literal, regexp, replace }, | 30 | query: { search, valid, caseSensitive, literal, regexp, replace }, |
27 | invalidRegexp, | 31 | invalidRegexp, |
28 | } = store; | 32 | } = searchPanelStore; |
29 | const split = useMediaQuery(SPLIT_MEDIA_QUERY); | 33 | const split = useMediaQuery(SPLIT_MEDIA_QUERY); |
30 | const abbreviate = useMediaQuery(ABBREVIATE_MEDIA_QUERY); | 34 | const abbreviate = useMediaQuery(ABBREVIATE_MEDIA_QUERY); |
31 | const [showRepalceState, setShowReplaceState] = useState(false); | 35 | const [showRepalceState, setShowReplaceState] = useState(false); |
@@ -37,8 +41,8 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
37 | 41 | ||
38 | const searchFieldRef = useCallback( | 42 | const searchFieldRef = useCallback( |
39 | (element: HTMLInputElement | null) => | 43 | (element: HTMLInputElement | null) => |
40 | store.setSearchField(element ?? undefined), | 44 | searchPanelStore.setSearchField(element ?? undefined), |
41 | [store], | 45 | [searchPanelStore], |
42 | ); | 46 | ); |
43 | 47 | ||
44 | return ( | 48 | return ( |
@@ -68,15 +72,15 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
68 | value={search} | 72 | value={search} |
69 | error={invalidRegexp} | 73 | error={invalidRegexp} |
70 | onChange={(event) => | 74 | onChange={(event) => |
71 | store.updateQuery({ search: event.target.value }) | 75 | searchPanelStore.updateQuery({ search: event.target.value }) |
72 | } | 76 | } |
73 | onKeyDown={(event) => { | 77 | onKeyDown={(event) => { |
74 | if (event.key === 'Enter') { | 78 | if (event.key === 'Enter') { |
75 | event.preventDefault(); | 79 | event.preventDefault(); |
76 | if (event.shiftKey) { | 80 | if (event.shiftKey) { |
77 | store.findPrevious(); | 81 | searchPanelStore.findPrevious(); |
78 | } else { | 82 | } else { |
79 | store.findNext(); | 83 | searchPanelStore.findNext(); |
80 | } | 84 | } |
81 | } | 85 | } |
82 | }} | 86 | }} |
@@ -108,7 +112,7 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
108 | <IconButton | 112 | <IconButton |
109 | aria-label="Previous" | 113 | aria-label="Previous" |
110 | disabled={!valid} | 114 | disabled={!valid} |
111 | onClick={() => store.findPrevious()} | 115 | onClick={() => searchPanelStore.findPrevious()} |
112 | color="inherit" | 116 | color="inherit" |
113 | > | 117 | > |
114 | <KeyboardArrowUpIcon fontSize="small" /> | 118 | <KeyboardArrowUpIcon fontSize="small" /> |
@@ -116,7 +120,7 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
116 | <IconButton | 120 | <IconButton |
117 | aria-label="Next" | 121 | aria-label="Next" |
118 | disabled={!valid} | 122 | disabled={!valid} |
119 | onClick={() => store.findNext()} | 123 | onClick={() => searchPanelStore.findNext()} |
120 | color="inherit" | 124 | color="inherit" |
121 | > | 125 | > |
122 | <KeyboardArrowDownIcon fontSize="small" /> | 126 | <KeyboardArrowDownIcon fontSize="small" /> |
@@ -133,7 +137,9 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
133 | <Checkbox | 137 | <Checkbox |
134 | checked={caseSensitive} | 138 | checked={caseSensitive} |
135 | onChange={(event) => | 139 | onChange={(event) => |
136 | store.updateQuery({ caseSensitive: event.target.checked }) | 140 | searchPanelStore.updateQuery({ |
141 | caseSensitive: event.target.checked, | ||
142 | }) | ||
137 | } | 143 | } |
138 | size="small" | 144 | size="small" |
139 | /> | 145 | /> |
@@ -146,7 +152,9 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
146 | <Checkbox | 152 | <Checkbox |
147 | checked={literal} | 153 | checked={literal} |
148 | onChange={(event) => | 154 | onChange={(event) => |
149 | store.updateQuery({ literal: event.target.checked }) | 155 | searchPanelStore.updateQuery({ |
156 | literal: event.target.checked, | ||
157 | }) | ||
150 | } | 158 | } |
151 | size="small" | 159 | size="small" |
152 | /> | 160 | /> |
@@ -159,7 +167,9 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
159 | <Checkbox | 167 | <Checkbox |
160 | checked={regexp} | 168 | checked={regexp} |
161 | onChange={(event) => | 169 | onChange={(event) => |
162 | store.updateQuery({ regexp: event.target.checked }) | 170 | searchPanelStore.updateQuery({ |
171 | regexp: event.target.checked, | ||
172 | }) | ||
163 | } | 173 | } |
164 | size="small" | 174 | size="small" |
165 | /> | 175 | /> |
@@ -172,7 +182,7 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
172 | selected={showReplace} | 182 | selected={showReplace} |
173 | onClick={() => { | 183 | onClick={() => { |
174 | if (showReplace) { | 184 | if (showReplace) { |
175 | store.updateQuery({ replace: '' }); | 185 | searchPanelStore.updateQuery({ replace: '' }); |
176 | setShowReplaceState(false); | 186 | setShowReplaceState(false); |
177 | } else { | 187 | } else { |
178 | setShowReplaceState(true); | 188 | setShowReplaceState(true); |
@@ -201,12 +211,12 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
201 | aria-label="Replace with" | 211 | aria-label="Replace with" |
202 | value={replace} | 212 | value={replace} |
203 | onChange={(event) => | 213 | onChange={(event) => |
204 | store.updateQuery({ replace: event.target.value }) | 214 | searchPanelStore.updateQuery({ replace: event.target.value }) |
205 | } | 215 | } |
206 | onKeyDown={(event) => { | 216 | onKeyDown={(event) => { |
207 | if (event.key === 'Enter') { | 217 | if (event.key === 'Enter') { |
208 | event.preventDefault(); | 218 | event.preventDefault(); |
209 | store.replaceNext(); | 219 | searchPanelStore.replaceNext(); |
210 | } | 220 | } |
211 | }} | 221 | }} |
212 | variant="standard" | 222 | variant="standard" |
@@ -221,7 +231,7 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
221 | > | 231 | > |
222 | <Button | 232 | <Button |
223 | disabled={!valid} | 233 | disabled={!valid} |
224 | onClick={() => store.replaceNext()} | 234 | onClick={() => searchPanelStore.replaceNext()} |
225 | color="inherit" | 235 | color="inherit" |
226 | startIcon={<FindReplaceIcon fontSize="inherit" />} | 236 | startIcon={<FindReplaceIcon fontSize="inherit" />} |
227 | > | 237 | > |
@@ -229,7 +239,7 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
229 | </Button> | 239 | </Button> |
230 | <Button | 240 | <Button |
231 | disabled={!valid} | 241 | disabled={!valid} |
232 | onClick={() => store.replaceAll()} | 242 | onClick={() => searchPanelStore.replaceAll()} |
233 | color="inherit" | 243 | color="inherit" |
234 | startIcon={<FindReplaceIcon fontSize="inherit" />} | 244 | startIcon={<FindReplaceIcon fontSize="inherit" />} |
235 | > | 245 | > |
@@ -241,7 +251,7 @@ function SearchToolbar({ store }: { store: SearchPanelStore }): JSX.Element { | |||
241 | <Stack direction="row" alignSelf="stretch" alignItems="start" mt="1px"> | 251 | <Stack direction="row" alignSelf="stretch" alignItems="start" mt="1px"> |
242 | <IconButton | 252 | <IconButton |
243 | aria-label="Close find/replace" | 253 | aria-label="Close find/replace" |
244 | onClick={() => store.close()} | 254 | onClick={() => searchPanelStore.close()} |
245 | color="inherit" | 255 | color="inherit" |
246 | > | 256 | > |
247 | <CloseIcon fontSize="small" /> | 257 | <CloseIcon fontSize="small" /> |
diff --git a/subprojects/frontend/src/editor/createEditorState.ts b/subprojects/frontend/src/editor/createEditorState.ts index 7b0f3c07..c61f4a22 100644 --- a/subprojects/frontend/src/editor/createEditorState.ts +++ b/subprojects/frontend/src/editor/createEditorState.ts | |||
@@ -36,7 +36,6 @@ import problemLanguageSupport from '../language/problemLanguageSupport'; | |||
36 | 36 | ||
37 | import type EditorStore from './EditorStore'; | 37 | import type EditorStore from './EditorStore'; |
38 | import SearchPanel from './SearchPanel'; | 38 | import SearchPanel from './SearchPanel'; |
39 | import editorClassNames from './editorClassNames'; | ||
40 | import findOccurrences from './findOccurrences'; | 39 | import findOccurrences from './findOccurrences'; |
41 | import semanticHighlighting from './semanticHighlighting'; | 40 | import semanticHighlighting from './semanticHighlighting'; |
42 | 41 | ||
@@ -75,7 +74,7 @@ export default function createEditorState( | |||
75 | codeFolding({ | 74 | codeFolding({ |
76 | placeholderDOM(_view, onClick) { | 75 | placeholderDOM(_view, onClick) { |
77 | const button = document.createElement('button'); | 76 | const button = document.createElement('button'); |
78 | button.className = editorClassNames.foldPlaceholder; | 77 | button.className = 'problem-editor-foldPlaceholder'; |
79 | button.ariaLabel = 'Unfold lines'; | 78 | button.ariaLabel = 'Unfold lines'; |
80 | const span = document.createElement('span'); | 79 | const span = document.createElement('span'); |
81 | span.innerText = '...'; | 80 | span.innerText = '...'; |
@@ -88,10 +87,8 @@ export default function createEditorState( | |||
88 | markerDOM(open) { | 87 | markerDOM(open) { |
89 | const div = document.createElement('div'); | 88 | const div = document.createElement('div'); |
90 | div.className = [ | 89 | div.className = [ |
91 | editorClassNames.foldMarker, | 90 | 'problem-editor-foldMarker', |
92 | open | 91 | `problem-editor-foldMarker-${open ? 'open' : 'closed'}`, |
93 | ? editorClassNames.foldMarkerOpen | ||
94 | : editorClassNames.foldMarkerClosed, | ||
95 | ].join(' '); | 92 | ].join(' '); |
96 | return div; | 93 | return div; |
97 | }, | 94 | }, |
diff --git a/subprojects/frontend/src/editor/editorClassNames.ts b/subprojects/frontend/src/editor/editorClassNames.ts deleted file mode 100644 index 5584e8c2..00000000 --- a/subprojects/frontend/src/editor/editorClassNames.ts +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | const PREFIX = 'problem-editor-'; | ||
2 | |||
3 | const editorClassNames = { | ||
4 | foldPlaceholder: `${PREFIX}fold-placeholder`, | ||
5 | foldMarker: `${PREFIX}fold-marker`, | ||
6 | foldMarkerClosed: `${PREFIX}fold-marker-closed`, | ||
7 | foldMarkerOpen: `${PREFIX}fold-marker-open`, | ||
8 | }; | ||
9 | |||
10 | export default editorClassNames; | ||
diff --git a/subprojects/frontend/src/index.tsx b/subprojects/frontend/src/index.tsx index 460f6f77..3f06b332 100644 --- a/subprojects/frontend/src/index.tsx +++ b/subprojects/frontend/src/index.tsx | |||
@@ -1,3 +1,4 @@ | |||
1 | import Box from '@mui/material/Box'; | ||
1 | import CssBaseline from '@mui/material/CssBaseline'; | 2 | import CssBaseline from '@mui/material/CssBaseline'; |
2 | import { SnackbarProvider } from 'notistack'; | 3 | import { SnackbarProvider } from 'notistack'; |
3 | import React, { Suspense, lazy } from 'react'; | 4 | import React, { Suspense, lazy } from 'react'; |
@@ -71,9 +72,11 @@ const app = ( | |||
71 | <WindowControlsOverlayColor /> | 72 | <WindowControlsOverlayColor /> |
72 | <SnackbarProvider> | 73 | <SnackbarProvider> |
73 | <RegisterServiceWorker /> | 74 | <RegisterServiceWorker /> |
74 | <Suspense fallback={<Loading />}> | 75 | <Box height="100vh" overflow="auto"> |
75 | <App /> | 76 | <Suspense fallback={<Loading />}> |
76 | </Suspense> | 77 | <App /> |
78 | </Suspense> | ||
79 | </Box> | ||
77 | </SnackbarProvider> | 80 | </SnackbarProvider> |
78 | </ThemeProvider> | 81 | </ThemeProvider> |
79 | </RootStoreProvider> | 82 | </RootStoreProvider> |