{"version":3,"file":"QueryField.cjs","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  ['aria-labelledby']?: string;\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, ['aria-labelledby']: ariaLabelledby } = 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) => {\n              this.editor = editor;\n            }}\n            aria-labelledby={ariaLabelledby}\n            schema={SCHEMA}\n            autoCorrect={false}\n            readOnly={this.props.disabled}\n            onBlur={this.handleBlur}\n            onClick={this.props.onClick}\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":["PureComponent","Plain","debounce","SuggestionsPlugin","RunnerPlugin","NewlinePlugin","ClearPlugin","SelectionShortcutsPlugin","IndentationPlugin","ClipboardPlugin","makeValue","classnames","cx","jsx","selectors","Editor","SCHEMA","withTheme2","getFocusStyles","css"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDO,MAAM,2BAA2BA,mBAAA,CAAgD;AAAA,EAatF,YAAY,KAAA,EAAwB;AAClC,IAAA,KAAA,CAAM,KAAK,CAAA;AAXb,IAAA,IAAA,CAAA,iBAAA,GAAkC,IAAA;AAClC,IAAA,IAAA,CAAA,OAAA,GAAU,KAAA;AACV,IAAA,IAAA,CAAA,MAAA,GAAwB,IAAA;AAoExB;AAAA;AAAA;AAAA,IAAA,IAAA,CAAA,QAAA,GAAW,CAAC,OAAc,QAAA,KAAuB;AAC/C,MAAA,MAAM,eAAA,GAAkB,KAAA,CAAM,QAAA,KAAa,IAAA,CAAK,MAAM,KAAA,CAAM,QAAA;AAC5D,MAAA,MAAM,SAAA,GAAY,KAAK,KAAA,CAAM,KAAA;AAC7B,MAAA,IAAI,IAAA,CAAK,MAAM,iBAAA,EAAmB;AAChC,QAAA,IAAA,CAAK,KAAA,CAAM,kBAAkB,KAAK,CAAA;AAAA,MACpC;AAGA,MAAA,IAAA,CAAK,QAAA,CAAS,EAAE,KAAA,EAAM,EAAG,MAAM;AAG7B,QAAA,IAAI,eAAA,EAAiB;AACnB,UAAA,MAAM,cAAcC,sBAAA,CAAM,SAAA,CAAU,SAAS,CAAA,KAAMA,sBAAA,CAAM,UAAU,KAAK,CAAA;AACxE,UAAA,IAAI,eAAe,QAAA,EAAU;AAC3B,YAAA,IAAA,CAAK,sBAAA,EAAuB;AAAA,UAC9B;AACA,UAAA,IAAI,WAAA,IAAe,CAAC,QAAA,EAAU;AAE5B,YAAA,IAAA,CAAK,oBAAA,EAAqB;AAAA,UAC5B;AAAA,QACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA;AAEA,IAAA,IAAA,CAAA,WAAA,GAAc,MAAM;AAClB,MAAA,MAAM,EAAE,QAAA,EAAS,GAAI,IAAA,CAAK,KAAA;AAC1B,MAAA,MAAM,KAAA,GAAQA,sBAAA,CAAM,SAAA,CAAU,IAAA,CAAK,MAAM,KAAK,CAAA;AAC9C,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,QAAA,CAAS,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,MAChC;AAAA,IACF,CAAA;AAEA,IAAA,IAAA,CAAA,aAAA,GAAgB,MAAM;AACpB,MAAA,MAAM,EAAE,UAAA,EAAW,GAAI,IAAA,CAAK,KAAA;AAE5B,MAAA,IAAI,UAAA,EAAY;AACd,QAAA,UAAA,EAAW;AACX,QAAA,IAAA,CAAK,iBAAA,GAAoB,KAAK,KAAA,CAAM,KAAA;AAAA,MACtC;AAAA,IACF,CAAA;AAEA,IAAA,IAAA,CAAA,sBAAA,GAAyB,MAAM;AAG7B,MAAA,IAAA,CAAK,WAAA,EAAY;AACjB,MAAA,IAAA,CAAK,aAAA,EAAc;AAAA,IACrB,CAAA;AAKA;AAAA;AAAA;AAAA,IAAA,IAAA,CAAA,UAAA,GAAa,CAAC,CAAA,EAAiC,MAAA,EAAgB,IAAA,KAAmB;AAChF,MAAA,MAAM,EAAE,MAAA,EAAO,GAAI,IAAA,CAAK,KAAA;AAExB,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,MAAA,EAAO;AAAA,MACT,CAAA,MAAO;AAEL,QAAA,MAAM,gBAAgB,IAAA,CAAK,iBAAA,GAAoBA,uBAAM,SAAA,CAAU,IAAA,CAAK,iBAAiB,CAAA,GAAI,EAAA;AACzF,QAAA,MAAM,YAAA,GAAeA,sBAAA,CAAM,SAAA,CAAU,MAAA,CAAO,KAAK,CAAA;AAEjD,QAAA,IAAI,kBAAkB,YAAA,EAAc;AAClC,UAAA,IAAA,CAAK,sBAAA,EAAuB;AAAA,QAC9B;AAAA,MACF;AACA,MAAA,OAAO,IAAA,EAAK;AAAA,IACd,CAAA;AA3HE,IAAA,IAAA,CAAK,oBAAA,GAAuBC,eAAA,CAAS,IAAA,CAAK,WAAA,EAAa,GAAG,CAAA;AAE1D,IAAA,MAAM,EAAE,WAAA,EAAa,SAAA,EAAW,YAAA,EAAc,uBAAsB,GAAI,KAAA;AAGxE,IAAA,IAAA,CAAK,OAAA,GAAU;AAAA;AAAA;AAAA,MAGbC,8BAAkB,EAAE,WAAA,EAAa,SAAA,EAAW,YAAA,EAAc,uBAAuB,CAAA;AAAA,MACjFC,mBAAA,CAAa,EAAE,OAAA,EAAS,IAAA,CAAK,wBAAwB,CAAA;AAAA,MACrDC,qBAAA,EAAc;AAAA,MACdC,iBAAA,EAAY;AAAA,MACZC,4CAAA,EAAyB;AAAA,MACzBC,6BAAA,EAAkB;AAAA,MAClBC,yBAAA,EAAgB;AAAA,MAChB,GAAI,KAAA,CAAM,iBAAA,IAAqB;AAAC,KAClC,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAC,CAAA;AAEjB,IAAA,IAAA,CAAK,KAAA,GAAQ;AAAA,MACX,aAAa,EAAC;AAAA,MACd,gBAAA,EAAkB,IAAA;AAAA,MAClB,eAAA,EAAiB,EAAA;AAAA,MACjB,aAAA,EAAe,EAAA;AAAA,MACf,OAAOC,eAAA,CAAU,KAAA,CAAM,KAAA,IAAS,EAAA,EAAI,MAAM,MAAM;AAAA,KAClD;AAAA,EACF;AAAA,EAEA,iBAAA,GAAoB;AAClB,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACjB;AAAA,EAEA,oBAAA,GAAuB;AACrB,IAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AAAA,EACjB;AAAA,EAEA,kBAAA,CAAmB,WAA4B,SAAA,EAA4B;AACzE,IAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,YAAA,KAAiB,IAAA,CAAK,KAAA;AAC7C,IAAA,IAAI,CAAC,SAAA,CAAU,YAAA,IAAgB,YAAA,IAAgB,KAAK,MAAA,EAAQ;AAE1D,MAAA,MAAM,SAAS,IAAA,CAAK,MAAA,CAAO,WAAW,GAAG,CAAA,CAAE,eAAe,CAAC,CAAA;AAC3D,MAAA,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,KAAA,EAAO,IAAI,CAAA;AAAA,IAClC;AACA,IAAA,MAAM,EAAE,KAAA,EAAM,GAAI,IAAA,CAAK,KAAA;AAIvB,IAAA,IAAI,KAAA,KAAU,UAAU,KAAA,EAAO;AAE7B,MAAA,IAAI,KAAA,KAAUT,sBAAA,CAAM,SAAA,CAAU,KAAK,CAAA,EAAG;AACpC,QAAA,IAAA,CAAK,QAAA,CAAS,EAAE,KAAA,EAAOS,eAAA,CAAU,SAAS,EAAA,EAAI,MAAM,GAAG,CAAA;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAAA,EAyEA,UAAU,IAAA,EAAc;AAEtB,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,EAAE,CAAA;AACxC,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEA,MAAA,GAAS;AACP,IAAA,MAAM,EAAE,UAAU,KAAA,EAAO,CAAC,iBAAiB,GAAG,cAAA,KAAmB,IAAA,CAAK,KAAA;AACtE,IAAA,MAAM,gBAAA,GAAmBC,4BAAW,4BAAA,EAA8B;AAAA,MAChE,sCAAA,EAAwC;AAAA,KACzC,CAAA;AACD,IAAA,MAAM,MAAA,GAAS,UAAU,KAAK,CAAA;AAE9B,IAAA,sCACG,KAAA,EAAA,EAAI,SAAA,EAAWC,MAAA,CAAG,gBAAA,EAAkB,OAAO,OAAO,CAAA,EACjD,QAAA,kBAAAC,cAAA,CAAC,KAAA,EAAA,EAAI,WAAU,mBAAA,EAAoB,aAAA,EAAaC,sBAAA,CAAU,UAAA,CAAW,WAAW,SAAA,EAC9E,QAAA,kBAAAD,cAAA;AAAA,MAACE,iBAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,CAAC,MAAA,KAAW;AACf,UAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,QAChB,CAAA;AAAA,QACA,iBAAA,EAAiB,cAAA;AAAA,QACjB,MAAA,EAAQC,YAAA;AAAA,QACR,WAAA,EAAa,KAAA;AAAA,QACb,QAAA,EAAU,KAAK,KAAA,CAAM,QAAA;AAAA,QACrB,QAAQ,IAAA,CAAK,UAAA;AAAA,QACb,OAAA,EAAS,KAAK,KAAA,CAAM,OAAA;AAAA,QACpB,QAAA,EAAU,CAAC,MAAA,KAA6B;AACtC,UAAA,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,KAAA,EAAO,KAAK,CAAA;AAAA,QACnC,CAAA;AAAA,QACA,WAAA,EAAa,KAAK,KAAA,CAAM,WAAA;AAAA,QACxB,SAAS,IAAA,CAAK,OAAA;AAAA,QACd,UAAA,EAAY,KAAA;AAAA,QACZ,KAAA,EAAO,KAAK,KAAA,CAAM;AAAA;AAAA,OAEtB,CAAA,EACF,CAAA;AAAA,EAEJ;AACF;AAAA;AAAA;AAnLa,kBAAA,CASJ,YAAA,GAAe;AAAA,EACpB,QAAQ,MAAM;AAAA,EAAC;AACjB,CAAA;AAoLK,MAAM,UAAA,GAAaC,wBAAW,kBAAkB;AAEvD,MAAM,SAAA,GAAY,CAAC,KAAA,KAAyB;AAC1C,EAAA,MAAM,WAAA,GAAcC,sBAAe,KAAK,CAAA;AACxC,EAAA,OAAO;AAAA,IACL,SAASC,OAAA,CAAI;AAAA,MACX,gBAAA,EAAkB;AAAA,KACnB;AAAA,GACH;AACF,CAAA;;;;;"}