UNPKG

@grafana/ui

Version:
1 lines 13.8 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\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\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.\n *\n * https://developers.grafana.com/ui/latest/index.html?path=/docs/inputs-deprecated-queryfield--docs\n *\n * @deprecated\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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"}