aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/frontend/src/editor/EditorArea.tsx
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-08-16 21:14:50 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-08-16 21:14:50 +0200
commit19cd11118cde7160cd447c81bc965007c0437479 (patch)
tree5fea613e7a46d69380995368a68cc72f186078a4 /subprojects/frontend/src/editor/EditorArea.tsx
parentchore(deps): bump frontend dependencies (diff)
downloadrefinery-19cd11118cde7160cd447c81bc965007c0437479.tar.gz
refinery-19cd11118cde7160cd447c81bc965007c0437479.tar.zst
refinery-19cd11118cde7160cd447c81bc965007c0437479.zip
refactor(frondend): improve editor store and theme
Also bumps frontend dependencies.
Diffstat (limited to 'subprojects/frontend/src/editor/EditorArea.tsx')
-rw-r--r--subprojects/frontend/src/editor/EditorArea.tsx138
1 files changed, 15 insertions, 123 deletions
diff --git a/subprojects/frontend/src/editor/EditorArea.tsx b/subprojects/frontend/src/editor/EditorArea.tsx
index d4305610..e5712461 100644
--- a/subprojects/frontend/src/editor/EditorArea.tsx
+++ b/subprojects/frontend/src/editor/EditorArea.tsx
@@ -1,139 +1,31 @@
1import { closeLintPanel, openLintPanel } from '@codemirror/lint'; 1import { useTheme } from '@mui/material/styles';
2import { closeSearchPanel, openSearchPanel } from '@codemirror/search';
3import { type Command, EditorView } from '@codemirror/view';
4import { observer } from 'mobx-react-lite'; 2import { observer } from 'mobx-react-lite';
5import React, { useCallback, useEffect, useRef, useState } from 'react'; 3import React, { useCallback, useEffect } from 'react';
6 4
7import { useRootStore } from '../RootStore'; 5import { useRootStore } from '../RootStore';
8import getLogger from '../utils/getLogger';
9 6
10import EditorParent from './EditorParent'; 7import EditorTheme from './EditorTheme';
11
12const log = getLogger('editor.EditorArea');
13
14function usePanel(
15 panelId: string,
16 stateToSet: boolean,
17 editorView: EditorView | null,
18 openCommand: Command,
19 closeCommand: Command,
20 closeCallback: () => void,
21) {
22 const [cachedViewState, setCachedViewState] = useState<boolean>(false);
23 useEffect(() => {
24 if (editorView === null || cachedViewState === stateToSet) {
25 return;
26 }
27 if (stateToSet) {
28 openCommand(editorView);
29 const buttonQuery = `.cm-${panelId}.cm-panel button[name="close"]`;
30 const closeButton = editorView.dom.querySelector(buttonQuery);
31 if (closeButton) {
32 log.debug('Addig close button callback to', panelId, 'panel');
33 // We must remove the event listener added by CodeMirror from the button
34 // that dispatches a transaction without going through `EditorStorre`.
35 // Cloning a DOM node removes event listeners,
36 // see https://stackoverflow.com/a/9251864
37 const closeButtonWithoutListeners = closeButton.cloneNode(true);
38 closeButtonWithoutListeners.addEventListener('click', (event) => {
39 closeCallback();
40 event.preventDefault();
41 });
42 closeButton.replaceWith(closeButtonWithoutListeners);
43 } else {
44 log.error('Opened', panelId, 'panel has no close button');
45 }
46 } else {
47 closeCommand(editorView);
48 }
49 setCachedViewState(stateToSet);
50 }, [
51 stateToSet,
52 editorView,
53 cachedViewState,
54 panelId,
55 openCommand,
56 closeCommand,
57 closeCallback,
58 ]);
59 return setCachedViewState;
60}
61
62function fixCodeMirrorAccessibility(editorView: EditorView) {
63 // Reported by Lighthouse 8.3.0.
64 const { contentDOM } = editorView;
65 contentDOM.removeAttribute('aria-expanded');
66 contentDOM.setAttribute('aria-label', 'Code editor');
67}
68 8
69function EditorArea(): JSX.Element { 9function EditorArea(): JSX.Element {
70 const { editorStore } = useRootStore(); 10 const { editorStore } = useRootStore();
71 const editorParentRef = useRef<HTMLDivElement | null>(null); 11 const {
72 const [editorViewState, setEditorViewState] = useState<EditorView | null>( 12 palette: { mode: paletteMode },
73 null, 13 } = useTheme();
74 );
75 14
76 const setSearchPanelOpen = usePanel( 15 useEffect(
77 'search', 16 () => editorStore.setDarkMode(paletteMode === 'dark'),
78 editorStore.showSearchPanel, 17 [editorStore, paletteMode],
79 editorViewState,
80 openSearchPanel,
81 closeSearchPanel,
82 useCallback(() => editorStore.setSearchPanelOpen(false), [editorStore]),
83 ); 18 );
84 19
85 const setLintPanelOpen = usePanel( 20 const editorParentRef = useCallback(
86 'panel-lint', 21 (editorParent: HTMLDivElement | null) => {
87 editorStore.showLintPanel, 22 editorStore.setEditorParent(editorParent);
88 editorViewState, 23 },
89 openLintPanel, 24 [editorStore],
90 closeLintPanel,
91 useCallback(() => editorStore.setLintPanelOpen(false), [editorStore]),
92 ); 25 );
93 26
94 useEffect(() => {
95 if (editorParentRef.current === null) {
96 return () => {
97 // Nothing to clean up.
98 };
99 }
100
101 const editorView = new EditorView({
102 state: editorStore.state,
103 parent: editorParentRef.current,
104 dispatch: (transaction) => {
105 editorStore.onTransaction(transaction);
106 editorView.update([transaction]);
107 if (editorView.state !== editorStore.state) {
108 log.error(
109 'Failed to synchronize editor state - store state:',
110 editorStore.state,
111 'view state:',
112 editorView.state,
113 );
114 }
115 },
116 });
117 fixCodeMirrorAccessibility(editorView);
118 setEditorViewState(editorView);
119 setSearchPanelOpen(false);
120 setLintPanelOpen(false);
121 // `dispatch` is bound to the view instance,
122 // so it does not have to be called as a method.
123 // eslint-disable-next-line @typescript-eslint/unbound-method
124 editorStore.updateDispatcher(editorView.dispatch);
125 log.info('Editor created');
126
127 return () => {
128 editorStore.updateDispatcher(null);
129 editorView.destroy();
130 log.info('Editor destroyed');
131 };
132 }, [editorStore, setSearchPanelOpen, setLintPanelOpen]);
133
134 return ( 27 return (
135 <EditorParent 28 <EditorTheme
136 className="dark"
137 showLineNumbers={editorStore.showLineNumbers} 29 showLineNumbers={editorStore.showLineNumbers}
138 ref={editorParentRef} 30 ref={editorParentRef}
139 /> 31 />