UNPKG

@grafana/ui

Version:
1 lines 13.6 kB
{"version":3,"file":"QueryField.mjs","sources":["../../../../src/components/QueryField/QueryField.tsx"],"sourcesContent":["import { css, cx } from '@emotion/css';\nimport classnames from 'classnames';\nimport { debounce } from 'lodash';\nimport { PureComponent } from 'react';\nimport * as React from 'react';\nimport { Value } from 'slate';\nimport Plain from 'slate-plain-serializer';\nimport { Editor, EventHook, Plugin } from 'slate-react';\n\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { selectors } from '@grafana/e2e-selectors';\n\nimport { ClearPlugin } from '../../slate-plugins/clear';\nimport { ClipboardPlugin } from '../../slate-plugins/clipboard';\nimport { IndentationPlugin } from '../../slate-plugins/indentation';\nimport { NewlinePlugin } from '../../slate-plugins/newline';\nimport { RunnerPlugin } from '../../slate-plugins/runner';\nimport { SelectionShortcutsPlugin } from '../../slate-plugins/selection_shortcuts';\nimport { SuggestionsPlugin } from '../../slate-plugins/suggestions';\nimport { withTheme2 } from '../../themes/ThemeContext';\nimport { getFocusStyles } from '../../themes/mixins';\nimport { CompletionItemGroup, SuggestionsState, TypeaheadInput, TypeaheadOutput } from '../../types/completion';\nimport { Themeable2 } from '../../types/theme';\nimport { makeValue, SCHEMA } from '../../utils/slate';\n\nexport interface QueryFieldProps extends Themeable2 {\n additionalPlugins?: Plugin[];\n cleanText?: (text: string) => string;\n disabled?: boolean;\n // We have both value and local state. This is usually an antipattern but we need to keep local state\n // for perf reasons and also have outside value in for example in Explore redux that is mutable from logs\n // creating a two way binding.\n query?: string | null;\n onRunQuery?: () => void;\n onBlur?: () => void;\n onChange?: (value: string) => void;\n onRichValueChange?: (value: Value) => void;\n onClick?: EventHook<React.MouseEvent<Element, MouseEvent>>;\n onTypeahead?: (typeahead: TypeaheadInput) => Promise<TypeaheadOutput>;\n onWillApplySuggestion?: (suggestion: string, state: SuggestionsState) => string;\n placeholder?: string;\n portalOrigin: string;\n syntax?: string;\n syntaxLoaded?: boolean;\n theme: GrafanaTheme2;\n}\n\nexport interface QueryFieldState {\n suggestions: CompletionItemGroup[];\n typeaheadContext: string | null;\n typeaheadPrefix: string;\n typeaheadText: string;\n value: Value;\n}\n\n/**\n * Renders an editor field.\n * Pass initial value as initialQuery and listen to changes in props.onValueChanged.\n * This component can only process strings. Internally it uses Slate Value.\n * Implement props.onTypeahead to use suggestions, see PromQueryField.tsx as an example.\n */\nexport class UnThemedQueryField extends PureComponent<QueryFieldProps, QueryFieldState> {\n plugins: Array<Plugin<Editor>>;\n runOnChangeDebounced: Function;\n lastExecutedValue: Value | null = null;\n mounted = false;\n editor: Editor | null = null;\n\n // By default QueryField calls onChange if onBlur is not defined, this will trigger a rerender\n // And slate will claim the focus, making it impossible to leave the field.\n static defaultProps = {\n onBlur: () => {},\n };\n\n constructor(props: QueryFieldProps) {\n super(props);\n\n this.runOnChangeDebounced = debounce(this.runOnChange, 500);\n\n const { onTypeahead, cleanText, portalOrigin, onWillApplySuggestion } = props;\n\n // Base plugins\n this.plugins = [\n // SuggestionsPlugin and RunnerPlugin need to be before NewlinePlugin\n // because they override Enter behavior\n SuggestionsPlugin({ onTypeahead, cleanText, portalOrigin, onWillApplySuggestion }),\n RunnerPlugin({ handler: this.runOnChangeAndRunQuery }),\n NewlinePlugin(),\n ClearPlugin(),\n SelectionShortcutsPlugin(),\n IndentationPlugin(),\n ClipboardPlugin(),\n ...(props.additionalPlugins || []),\n ].filter((p) => p);\n\n this.state = {\n suggestions: [],\n typeaheadContext: null,\n typeaheadPrefix: '',\n typeaheadText: '',\n value: makeValue(props.query || '', props.syntax),\n };\n }\n\n componentDidMount() {\n this.mounted = true;\n }\n\n componentWillUnmount() {\n this.mounted = false;\n }\n\n componentDidUpdate(prevProps: QueryFieldProps, prevState: QueryFieldState) {\n const { query, syntax, syntaxLoaded } = this.props;\n if (!prevProps.syntaxLoaded && syntaxLoaded && this.editor) {\n // Need a bogus edit to re-render the editor after syntax has fully loaded\n const editor = this.editor.insertText(' ').deleteBackward(1);\n this.onChange(editor.value, true);\n }\n const { value } = this.state;\n\n // Handle two way binging between local state and outside prop.\n // if query changed from the outside\n if (query !== prevProps.query) {\n // and we have a version that differs\n if (query !== Plain.serialize(value)) {\n this.setState({ value: makeValue(query || '', syntax) });\n }\n }\n }\n\n /**\n * Update local state, propagate change upstream and optionally run the query afterwards.\n */\n onChange = (value: Value, runQuery?: boolean) => {\n const documentChanged = value.document !== this.state.value.document;\n const prevValue = this.state.value;\n if (this.props.onRichValueChange) {\n this.props.onRichValueChange(value);\n }\n\n // Update local state with new value and optionally change value upstream.\n this.setState({ value }, () => {\n // The diff is needed because the actual value of editor have much more metadata (for example text selection)\n // that is not passed upstream so every change of editor value does not mean change of the query text.\n if (documentChanged) {\n const textChanged = Plain.serialize(prevValue) !== Plain.serialize(value);\n if (textChanged && runQuery) {\n this.runOnChangeAndRunQuery();\n }\n if (textChanged && !runQuery) {\n // Debounce change propagation by default for perf reasons.\n this.runOnChangeDebounced();\n }\n }\n });\n };\n\n runOnChange = () => {\n const { onChange } = this.props;\n const value = Plain.serialize(this.state.value);\n if (onChange) {\n onChange(this.cleanText(value));\n }\n };\n\n runOnRunQuery = () => {\n const { onRunQuery } = this.props;\n\n if (onRunQuery) {\n onRunQuery();\n this.lastExecutedValue = this.state.value;\n }\n };\n\n runOnChangeAndRunQuery = () => {\n // onRunQuery executes query from Redux in Explore so it needs to be updated sync in case we want to run\n // the query.\n this.runOnChange();\n this.runOnRunQuery();\n };\n\n /**\n * We need to handle blur events here mainly because of dashboard panels which expect to have query executed on blur.\n */\n handleBlur = (_: React.FocusEvent | undefined, editor: Editor, next: Function) => {\n const { onBlur } = this.props;\n\n if (onBlur) {\n onBlur();\n } else {\n // Run query by default on blur\n const previousValue = this.lastExecutedValue ? Plain.serialize(this.lastExecutedValue) : '';\n const currentValue = Plain.serialize(editor.value);\n\n if (previousValue !== currentValue) {\n this.runOnChangeAndRunQuery();\n }\n }\n return next();\n };\n\n cleanText(text: string) {\n // RegExp with invisible characters we want to remove - currently only carriage return (newlines are visible)\n const newText = text.replace(/[\\r]/g, '');\n return newText;\n }\n\n render() {\n const { disabled, theme } = this.props;\n const wrapperClassName = classnames('slate-query-field__wrapper', {\n 'slate-query-field__wrapper--disabled': disabled,\n });\n const styles = getStyles(theme);\n\n return (\n <div className={cx(wrapperClassName, styles.wrapper)}>\n <div className=\"slate-query-field\" data-testid={selectors.components.QueryField.container}>\n <Editor\n ref={(editor) => (this.editor = editor!)}\n schema={SCHEMA}\n autoCorrect={false}\n readOnly={this.props.disabled}\n onBlur={this.handleBlur}\n onClick={this.props.onClick}\n // onKeyDown={this.onKeyDown}\n onChange={(change: { value: Value }) => {\n this.onChange(change.value, false);\n }}\n placeholder={this.props.placeholder}\n plugins={this.plugins}\n spellCheck={false}\n value={this.state.value}\n />\n </div>\n </div>\n );\n }\n}\n\nexport const QueryField = withTheme2(UnThemedQueryField);\n\nconst getStyles = (theme: GrafanaTheme2) => {\n const focusStyles = getFocusStyles(theme);\n return {\n wrapper: css({\n '&:focus-within': focusStyles,\n }),\n };\n};\n"],"names":["classnames"],"mappings":"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"}