grafana-ui: simplify and centralize monaco-theme-handling (#40643)

* grafana-ui: simplify and centralize monaco-theme-handling

* simplify code

* monaco: better theme-setup code

* eslint fix

* fix useEffect dependency
This commit is contained in:
Gábor Farkas 2021-10-25 14:55:39 +02:00 committed by GitHub
parent 57750639cd
commit b5ae62d6ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 35 deletions

View File

@ -10,7 +10,6 @@ import { Themeable2 } from '../../types';
import { CodeEditorProps, Monaco, MonacoEditor as MonacoEditorType, MonacoOptions } from './types';
import { registerSuggestions } from './suggestions';
import defineThemes from './theme';
type Props = CodeEditorProps & Themeable2;
@ -85,8 +84,7 @@ class UnthemedCodeEditor extends React.PureComponent<Props> {
handleBeforeMount = (monaco: Monaco) => {
this.monaco = monaco;
const { language, theme, getSuggestions, onBeforeEditorMount } = this.props;
defineThemes(monaco, theme);
const { language, getSuggestions, onBeforeEditorMount } = this.props;
if (getSuggestions) {
this.completionCancel = registerSuggestions(monaco, language, getSuggestions);
@ -148,7 +146,6 @@ class UnthemedCodeEditor extends React.PureComponent<Props> {
width={width}
height={height}
language={language}
theme={theme.isDark ? 'grafana-dark' : 'grafana-light'}
value={value}
options={{
...options,

View File

@ -1,5 +1,8 @@
import React from 'react';
import MonacoEditor, { loader as monacoEditorLoader, EditorProps as MonacoEditorProps } from '@monaco-editor/react';
import React, { useEffect } from 'react';
import MonacoEditor, { loader as monacoEditorLoader, useMonaco } from '@monaco-editor/react';
import defineThemes from './theme';
import { useTheme2 } from '../../themes';
import type { ReactMonacoEditorProps } from './types';
let initalized = false;
function initMonaco() {
@ -13,9 +16,30 @@ function initMonaco() {
},
});
initalized = true;
monacoEditorLoader.init().then((monaco) => {
// this call makes sure the themes exist.
// they will not have the correct colors,
// but we need them to exist since the beginning,
// because if we start a monaco instance with
// a theme that does not exist, it will not work well.
defineThemes(monaco);
});
}
export const ReactMonacoEditor = (props: MonacoEditorProps) => {
export const ReactMonacoEditor = (props: ReactMonacoEditorProps) => {
const theme = useTheme2();
const monaco = useMonaco();
useEffect(() => {
// monaco can be null at the beginning, because it is loaded in asynchronously
if (monaco !== null) {
defineThemes(monaco, theme);
}
}, [monaco, theme]);
initMonaco();
return <MonacoEditor {...props} />;
const monacoTheme = theme.isDark ? 'grafana-dark' : 'grafana-light';
return <MonacoEditor theme={monacoTheme} {...props} />;
};

View File

@ -2,13 +2,13 @@ import React from 'react';
import { useAsyncDependency } from '../../utils/useAsyncDependency';
import { ErrorWithStack, LoadingPlaceholder } from '..';
// we only use import type so it will not be included in the bundle
import type { EditorProps } from '@monaco-editor/react';
import type { ReactMonacoEditorProps } from './types';
/**
* @internal
* Experimental export
**/
export const ReactMonacoEditorLazy = (props: EditorProps) => {
export const ReactMonacoEditorLazy = (props: ReactMonacoEditorProps) => {
const { loading, error, dependency } = useAsyncDependency(
import(/* webpackChunkName: "react-monaco-editor" */ './ReactMonacoEditor')
);

View File

@ -1,12 +1,22 @@
import { GrafanaTheme2 } from '@grafana/data';
import { Monaco } from './types';
import { Monaco, monacoTypes } from './types';
export default function defineThemes(monaco: Monaco, theme: GrafanaTheme2) {
function getColors(theme?: GrafanaTheme2): monacoTypes.editor.IColors {
if (theme === undefined) {
return {};
} else {
return {
'editor.background': theme.components.input.background,
'minimap.background': theme.colors.background.secondary,
};
}
}
// we support calling this without a theme, it will make sure the themes
// are registered in monaco, even if the colors are not perfect.
export default function defineThemes(monaco: Monaco, theme?: GrafanaTheme2) {
// color tokens are defined here https://github.com/microsoft/vscode/blob/main/src/vs/platform/theme/common/colorRegistry.ts#L174
const colors = {
'editor.background': theme.components.input.background,
'minimap.background': theme.colors.background.secondary,
};
const colors = getColors(theme);
monaco.editor.defineTheme('grafana-dark', {
base: 'vs-dark',

View File

@ -1,5 +1,14 @@
// We use `import type` to guarantee it'll be erased from the JS and it doesnt accidently bundle monaco
import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api';
import type { EditorProps } from '@monaco-editor/react';
// we do not allow customizing the theme.
// (theme is complicated in Monaco, right now there is
// a limitation where all monaco editors must have
// the same theme, see
// https://github.com/microsoft/monaco-editor/issues/338#issuecomment-274837186
// )
export type ReactMonacoEditorProps = Omit<EditorProps, 'theme'>;
export type CodeEditorChangeHandler = (value: string) => void;
export type CodeEditorSuggestionProvider = () => CodeEditorSuggestionItem[];

View File

@ -51,23 +51,6 @@ function ensurePromQL(monaco: Monaco) {
}
}
const THEME_NAME = 'grafana-prometheus-query-field';
let MONACO_THEME_SETUP_STARTED = false;
function ensureMonacoTheme(monaco: Monaco, theme: GrafanaTheme2) {
if (MONACO_THEME_SETUP_STARTED === false) {
MONACO_THEME_SETUP_STARTED = true;
monaco.editor.defineTheme(THEME_NAME, {
base: theme.isDark ? 'vs-dark' : 'vs',
inherit: true,
colors: {
'editor.background': theme.components.input.background,
},
rules: [],
});
}
}
const getStyles = (theme: GrafanaTheme2) => {
return {
container: css`
@ -106,11 +89,9 @@ const MonacoQueryField = (props: Props) => {
<ReactMonacoEditor
options={options}
language="promql"
theme={THEME_NAME}
value={initialValue}
beforeMount={(monaco) => {
ensurePromQL(monaco);
ensureMonacoTheme(monaco, theme);
}}
onMount={(editor, monaco) => {
// we setup on-blur