diff --git a/packages/grafana-ui/src/components/Monaco/CodeEditor.tsx b/packages/grafana-ui/src/components/Monaco/CodeEditor.tsx index 2c265b83618..8989d8d36e6 100644 --- a/packages/grafana-ui/src/components/Monaco/CodeEditor.tsx +++ b/packages/grafana-ui/src/components/Monaco/CodeEditor.tsx @@ -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 { 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 { width={width} height={height} language={language} - theme={theme.isDark ? 'grafana-dark' : 'grafana-light'} value={value} options={{ ...options, diff --git a/packages/grafana-ui/src/components/Monaco/ReactMonacoEditor.tsx b/packages/grafana-ui/src/components/Monaco/ReactMonacoEditor.tsx index 61de48cc74e..d0a00fa1172 100644 --- a/packages/grafana-ui/src/components/Monaco/ReactMonacoEditor.tsx +++ b/packages/grafana-ui/src/components/Monaco/ReactMonacoEditor.tsx @@ -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 ; + + const monacoTheme = theme.isDark ? 'grafana-dark' : 'grafana-light'; + + return ; }; diff --git a/packages/grafana-ui/src/components/Monaco/ReactMonacoEditorLazy.tsx b/packages/grafana-ui/src/components/Monaco/ReactMonacoEditorLazy.tsx index 073b0d1d643..b8db38dac1c 100644 --- a/packages/grafana-ui/src/components/Monaco/ReactMonacoEditorLazy.tsx +++ b/packages/grafana-ui/src/components/Monaco/ReactMonacoEditorLazy.tsx @@ -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') ); diff --git a/packages/grafana-ui/src/components/Monaco/theme.ts b/packages/grafana-ui/src/components/Monaco/theme.ts index d2d828c352c..fb5d911bed1 100644 --- a/packages/grafana-ui/src/components/Monaco/theme.ts +++ b/packages/grafana-ui/src/components/Monaco/theme.ts @@ -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', diff --git a/packages/grafana-ui/src/components/Monaco/types.ts b/packages/grafana-ui/src/components/Monaco/types.ts index a1d11565cf1..fc8918e8c2e 100644 --- a/packages/grafana-ui/src/components/Monaco/types.ts +++ b/packages/grafana-ui/src/components/Monaco/types.ts @@ -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; export type CodeEditorChangeHandler = (value: string) => void; export type CodeEditorSuggestionProvider = () => CodeEditorSuggestionItem[]; diff --git a/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx b/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx index c13ef9d179a..a9a6c493f63 100644 --- a/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx +++ b/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx @@ -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) => { { ensurePromQL(monaco); - ensureMonacoTheme(monaco, theme); }} onMount={(editor, monaco) => { // we setup on-blur