Skip to content

Commit 9b4c1c1

Browse files
authored
feat(webui): Improve the UI of the SQL editor. (#1244)
- Add disabled state and theme to `SqlEditor`. - Focus `SqlEditor` on page load and after query termination. - Remove autocomplete.
1 parent a456dc9 commit 9b4c1c1

File tree

6 files changed

+181
-130
lines changed

6 files changed

+181
-130
lines changed

components/webui/client/package-lock.json

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/webui/client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"chart.js": "^4.4.9",
2727
"chartjs-adapter-dayjs-4": "^1.0.4",
2828
"chartjs-plugin-zoom": "^2.2.0",
29+
"color": "^5.0.0",
2930
"dayjs": "^1.11.13",
3031
"monaco-editor": "^0.52.2",
3132
"react": "^19.0.0",
Lines changed: 83 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,118 @@
11
import {
22
useCallback,
33
useEffect,
4-
useState,
4+
useImperativeHandle,
5+
useRef,
56
} from "react";
67

78
import {
89
Editor,
910
EditorProps,
1011
useMonaco,
1112
} from "@monaco-editor/react";
12-
import {language as sqlLanguage} from "monaco-editor/esm/vs/basic-languages/sql/sql.js";
13+
import {theme} from "antd";
14+
import color from "color";
1315
import * as monaco from "monaco-editor/esm/vs/editor/editor.api.js";
1416

1517
import "./monaco-loader";
1618

1719

18-
const MAX_VISIBLE_LINES: number = 5;
20+
type SqlEditorRef = {
21+
focus: () => void;
22+
};
23+
24+
type SqlEditorProps = Omit<EditorProps, "language"> & React.RefAttributes<SqlEditorRef> & {
25+
disabled: boolean;
1926

20-
type SqlEditorProps = Omit<EditorProps, "language">;
27+
/** Callback when the editor is mounted and ref is ready to use. */
28+
onEditorReady?: () => void;
29+
};
2130

2231
/**
23-
* Monaco editor with highlighting and autocomplete for SQL syntax.
32+
* Monaco editor with highlighting for SQL syntax.
2433
*
2534
* @param props
2635
* @return
2736
*/
2837
const SqlEditor = (props: SqlEditorProps) => {
38+
const {ref, disabled, onEditorReady, ...editorProps} = props;
39+
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>(null);
2940
const monacoEditor = useMonaco();
30-
41+
const {token} = theme.useToken();
42+
43+
useImperativeHandle(ref, () => ({
44+
focus: () => {
45+
editorRef.current?.focus();
46+
},
47+
}), []);
48+
49+
const handleEditorDidMount = useCallback((
50+
editor: monaco.editor.IStandaloneCodeEditor,
51+
) => {
52+
editorRef.current = editor;
53+
onEditorReady?.();
54+
}, [onEditorReady]);
55+
56+
// Define disabled theme for monaco editor
3157
useEffect(() => {
3258
if (null === monacoEditor) {
33-
return () => {
34-
};
59+
return;
3560
}
36-
37-
// Adds autocomplete suggestions for SQL keywords on editor load
38-
const provider = monacoEditor.languages.registerCompletionItemProvider("sql", {
39-
provideCompletionItems: (model, position) => {
40-
const word = model.getWordUntilPosition(position);
41-
const range = {
42-
startLineNumber: position.lineNumber,
43-
endLineNumber: position.lineNumber,
44-
startColumn: word.startColumn,
45-
endColumn: word.endColumn,
46-
};
47-
const suggestions = sqlLanguage.keywords.map((keyword: string) => ({
48-
detail: "Presto SQL (CLP)",
49-
insertText: `${keyword} `,
50-
kind: monacoEditor.languages.CompletionItemKind.Keyword,
51-
label: keyword,
52-
range: range,
53-
}));
54-
55-
// When SQL keyword suggestions appear (e.g., after "SELECT a"), hitting Enter
56-
// accepts the first suggestion. To prevent accidental auto-completion
57-
// in multi-line queries and to allow users to dismiss suggestions more easily,
58-
// we make the current input the first suggestion.
59-
// Users can then use arrow keys to select a keyword if needed.
60-
const typedWord = model.getValueInRange(range);
61-
if (0 < typedWord.length) {
62-
suggestions.push({
63-
detail: "Current",
64-
insertText: `${typedWord}\n`,
65-
kind: monaco.languages.CompletionItemKind.Text,
66-
label: typedWord,
67-
range: range,
68-
});
69-
}
70-
71-
return {
72-
suggestions: suggestions,
73-
incomplete: true,
74-
};
61+
monacoEditor.editor.defineTheme("disabled-theme", {
62+
base: "vs",
63+
inherit: true,
64+
rules: [],
65+
colors: {
66+
"editor.background": color(token.colorBgContainerDisabled).hexa(),
67+
"editor.foreground": color(token.colorTextDisabled).hexa(),
68+
69+
// transparent
70+
"focusBorder": "#00000000",
7571
},
76-
triggerCharacters: [
77-
" ",
78-
"\n",
79-
],
80-
});
81-
82-
return () => {
83-
provider.dispose();
84-
};
85-
}, [monacoEditor]);
86-
87-
const [isContentMultiline, setIsContentMultiline] = useState<boolean>(false);
88-
89-
const handleMonacoMount = useCallback((editor: monaco.editor.IStandaloneCodeEditor) => {
90-
editor.onDidContentSizeChange((ev) => {
91-
if (false === ev.contentHeightChanged) {
92-
return;
93-
}
94-
if (null === monacoEditor) {
95-
throw new Error("Unexpected null Monaco instance");
96-
}
97-
const domNode = editor.getDomNode();
98-
if (null === domNode) {
99-
throw new Error("Unexpected null editor DOM node");
100-
}
101-
const model = editor.getModel();
102-
if (null === model) {
103-
throw new Error("Unexpected null editor model");
104-
}
105-
const lineHeight = editor.getOption(monacoEditor.editor.EditorOption.lineHeight);
106-
const contentHeight = editor.getContentHeight();
107-
const approxWrappedLines = Math.round(contentHeight / lineHeight);
108-
setIsContentMultiline(1 < approxWrappedLines);
109-
if (MAX_VISIBLE_LINES >= approxWrappedLines) {
110-
domNode.style.height = `${contentHeight}px`;
111-
} else {
112-
domNode.style.height = `${lineHeight * MAX_VISIBLE_LINES}px`;
113-
}
11472
});
115-
}, [monacoEditor]);
73+
}, [
74+
monacoEditor,
75+
token,
76+
]);
11677

11778
return (
118-
<Editor
119-
language={"sql"}
120-
121-
// Use white background while loading (default is grey) so transition to editor with
122-
// white background is less jarring.
123-
loading={<div style={{backgroundColor: "white", height: "100%", width: "100%"}}/>}
124-
options={{
125-
automaticLayout: true,
126-
folding: isContentMultiline,
127-
fontSize: 20,
128-
lineHeight: 30,
129-
lineNumbers: isContentMultiline ?
130-
"on" :
131-
"off",
132-
lineNumbersMinChars: 2,
133-
minimap: {enabled: false},
134-
overviewRulerBorder: false,
135-
placeholder: "Enter your SQL query",
136-
renderLineHighlightOnlyWhenFocus: true,
137-
scrollBeyondLastLine: false,
138-
wordWrap: "on",
139-
}}
140-
onMount={handleMonacoMount}
141-
{...props}/>
79+
<div
80+
style={
81+
disabled ?
82+
{pointerEvents: "none"} :
83+
{}
84+
}
85+
>
86+
<Editor
87+
language={"sql"}
88+
loading={
89+
<div
90+
style={{
91+
backgroundColor: "white",
92+
height: "100%",
93+
width: "100%",
94+
}}/>
95+
}
96+
options={{
97+
automaticLayout: true,
98+
folding: false,
99+
fontSize: 16,
100+
lineNumbers: "off",
101+
minimap: {enabled: false},
102+
overviewRulerBorder: false,
103+
placeholder: "Enter your SQL query",
104+
renderLineHighlightOnlyWhenFocus: true,
105+
scrollBeyondLastLine: false,
106+
wordWrap: "on",
107+
}}
108+
theme={disabled ?
109+
"disabled-theme" :
110+
"light"}
111+
onMount={handleEditorDidMount}
112+
{...editorProps}/>
113+
</div>
142114
);
143115
};
144116

145117
export default SqlEditor;
118+
export type {SqlEditorRef};

components/webui/client/src/components/SqlEditor/monaco-loader.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@
22
import {loader} from "@monaco-editor/react";
33
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
44
import EditorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
5-
65
import "monaco-editor/esm/vs/basic-languages/sql/sql.contribution.js";
7-
import "monaco-editor/esm/vs/editor/contrib/clipboard/browser/clipboard.js";
8-
import "monaco-editor/esm/vs/editor/contrib/contextmenu/browser/contextmenu.js";
9-
import "monaco-editor/esm/vs/editor/contrib/find/browser/findController.js";
10-
import "monaco-editor/esm/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.js";
11-
import "monaco-editor/esm/vs/editor/contrib/suggest/browser/suggestController.js";
12-
import "monaco-editor/esm/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.js";
136

147

158
/* eslint-enable import/default, @stylistic/max-len */

components/webui/client/src/components/SqlEditor/monaco-sql.d.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)