mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: monaco-based query-editor-field, first step (#37251)
* prometheus: add monaco-based query-field for now it is behind a feature-flag * added new trigger character * better separate grafana-specifc and prom-specific code * better styling * more styling * more styling * simpler monaco-import * better imports * simplified code * simplified type imports * refactor: group completion-provider files together * renamed type * simplify type-import Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com> * handle no-metric-name autocompletes * updated comment Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { CodeEditor, CodeEditorMonacoOptions } from '@grafana/ui';
|
||||
import { useLatest } from 'react-use';
|
||||
import { promLanguageDefinition } from 'monaco-promql';
|
||||
import { getCompletionProvider } from './monaco-completion-provider';
|
||||
import { Props } from './MonacoQueryFieldProps';
|
||||
|
||||
const options: CodeEditorMonacoOptions = {
|
||||
lineNumbers: 'off',
|
||||
minimap: { enabled: false },
|
||||
lineDecorationsWidth: 0,
|
||||
wordWrap: 'off',
|
||||
overviewRulerLanes: 0,
|
||||
overviewRulerBorder: false,
|
||||
folding: false,
|
||||
scrollBeyondLastLine: false,
|
||||
renderLineHighlight: 'none',
|
||||
fontSize: 14,
|
||||
};
|
||||
|
||||
const MonacoQueryField = (props: Props) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const { languageProvider, history, onChange, initialValue } = props;
|
||||
|
||||
const lpRef = useLatest(languageProvider);
|
||||
const historyRef = useLatest(history);
|
||||
|
||||
return (
|
||||
<div
|
||||
// NOTE: we will be setting inline-style-width/height on this element
|
||||
ref={containerRef}
|
||||
style={{
|
||||
// FIXME:
|
||||
// this is how the non-monaco query-editor is styled,
|
||||
// through the "gf-form" class
|
||||
// so to have the same effect, we do the same.
|
||||
// this should be applied somehow differently probably,
|
||||
// like a min-height on the whole row.
|
||||
marginBottom: '4px',
|
||||
}}
|
||||
>
|
||||
<CodeEditor
|
||||
onBlur={onChange}
|
||||
monacoOptions={options}
|
||||
language="promql"
|
||||
value={initialValue}
|
||||
onBeforeEditorMount={(monaco) => {
|
||||
// we construct a DataProvider object
|
||||
const getSeries = (selector: string) => lpRef.current.getSeries(selector);
|
||||
|
||||
const getHistory = () =>
|
||||
Promise.resolve(historyRef.current.map((h) => h.query.expr).filter((expr) => expr !== undefined));
|
||||
|
||||
const getAllMetricNames = () => {
|
||||
const { metricsMetadata } = lpRef.current;
|
||||
const result = metricsMetadata == null ? [] : Object.keys(metricsMetadata);
|
||||
return Promise.resolve(result);
|
||||
};
|
||||
|
||||
const dataProvider = { getSeries, getHistory, getAllMetricNames };
|
||||
|
||||
const langId = promLanguageDefinition.id;
|
||||
monaco.languages.register(promLanguageDefinition);
|
||||
promLanguageDefinition.loader().then((mod) => {
|
||||
monaco.languages.setMonarchTokensProvider(langId, mod.language);
|
||||
monaco.languages.setLanguageConfiguration(langId, mod.languageConfiguration);
|
||||
const completionProvider = getCompletionProvider(monaco, dataProvider);
|
||||
monaco.languages.registerCompletionItemProvider(langId, completionProvider);
|
||||
});
|
||||
|
||||
// FIXME: should we unregister this at end end?
|
||||
}}
|
||||
onEditorDidMount={(editor, monaco) => {
|
||||
// this code makes the editor resize itself so that the content fits
|
||||
// (it will grow taller when necessary)
|
||||
// FIXME: maybe move this functionality into CodeEditor, like:
|
||||
// <CodeEditor resizingMode="single-line"/>
|
||||
const updateElementHeight = () => {
|
||||
const containerDiv = containerRef.current;
|
||||
if (containerDiv !== null) {
|
||||
const pixelHeight = editor.getContentHeight();
|
||||
containerDiv.style.height = `${pixelHeight}px`;
|
||||
containerDiv.style.width = '100%';
|
||||
const pixelWidth = containerDiv.clientWidth;
|
||||
editor.layout({ width: pixelWidth, height: pixelHeight });
|
||||
}
|
||||
};
|
||||
|
||||
editor.onDidContentSizeChange(updateElementHeight);
|
||||
updateElementHeight();
|
||||
|
||||
// handle: shift + enter
|
||||
// FIXME: maybe move this functionality into CodeEditor?
|
||||
editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => {
|
||||
const text = editor.getValue();
|
||||
props.onChange(text);
|
||||
props.onRunQuery();
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// we will lazy-load this module using React.lazy,
|
||||
// and that only supports default-exports,
|
||||
// so we have to default-export this, even if
|
||||
// it is agains the style-guidelines.
|
||||
|
||||
export default MonacoQueryField;
|
||||
Reference in New Issue
Block a user