import { jsx } from 'react/jsx-runtime';
import { cx, css } from '@emotion/css';
import classNames from 'classnames';
import { debounce } from 'lodash';
import { PureComponent } from 'react';
import Plain from 'slate-plain-serializer';
import { Editor } from 'slate-react';
import { selectors } from '@grafana/e2e-selectors';
import { ClearPlugin } from '../../slate-plugins/clear.mjs';
import { ClipboardPlugin } from '../../slate-plugins/clipboard.mjs';
import { IndentationPlugin } from '../../slate-plugins/indentation.mjs';
import { NewlinePlugin } from '../../slate-plugins/newline.mjs';
import { RunnerPlugin } from '../../slate-plugins/runner.mjs';
import { SelectionShortcutsPlugin } from '../../slate-plugins/selection_shortcuts.mjs';
import { SuggestionsPlugin } from '../../slate-plugins/suggestions.mjs';
import { withTheme2 } from '../../themes/ThemeContext.mjs';
import { getFocusStyles } from '../../themes/mixins.mjs';
import { makeValue, SCHEMA } from '../../utils/slate.mjs';

"use strict";
class UnThemedQueryField extends PureComponent {
  constructor(props) {
    super(props);
    this.lastExecutedValue = null;
    this.mounted = false;
    this.editor = null;
    /**
     * Update local state, propagate change upstream and optionally run the query afterwards.
     */
    this.onChange = (value, runQuery) => {
      const documentChanged = value.document !== this.state.value.document;
      const prevValue = this.state.value;
      if (this.props.onRichValueChange) {
        this.props.onRichValueChange(value);
      }
      this.setState({ value }, () => {
        if (documentChanged) {
          const textChanged = Plain.serialize(prevValue) !== Plain.serialize(value);
          if (textChanged && runQuery) {
            this.runOnChangeAndRunQuery();
          }
          if (textChanged && !runQuery) {
            this.runOnChangeDebounced();
          }
        }
      });
    };
    this.runOnChange = () => {
      const { onChange } = this.props;
      const value = Plain.serialize(this.state.value);
      if (onChange) {
        onChange(this.cleanText(value));
      }
    };
    this.runOnRunQuery = () => {
      const { onRunQuery } = this.props;
      if (onRunQuery) {
        onRunQuery();
        this.lastExecutedValue = this.state.value;
      }
    };
    this.runOnChangeAndRunQuery = () => {
      this.runOnChange();
      this.runOnRunQuery();
    };
    /**
     * We need to handle blur events here mainly because of dashboard panels which expect to have query executed on blur.
     */
    this.handleBlur = (_, editor, next) => {
      const { onBlur } = this.props;
      if (onBlur) {
        onBlur();
      } else {
        const previousValue = this.lastExecutedValue ? Plain.serialize(this.lastExecutedValue) : "";
        const currentValue = Plain.serialize(editor.value);
        if (previousValue !== currentValue) {
          this.runOnChangeAndRunQuery();
        }
      }
      return next();
    };
    this.runOnChangeDebounced = debounce(this.runOnChange, 500);
    const { onTypeahead, cleanText, portalOrigin, onWillApplySuggestion } = props;
    this.plugins = [
      // SuggestionsPlugin and RunnerPlugin need to be before NewlinePlugin
      // because they override Enter behavior
      SuggestionsPlugin({ onTypeahead, cleanText, portalOrigin, onWillApplySuggestion }),
      RunnerPlugin({ handler: this.runOnChangeAndRunQuery }),
      NewlinePlugin(),
      ClearPlugin(),
      SelectionShortcutsPlugin(),
      IndentationPlugin(),
      ClipboardPlugin(),
      ...props.additionalPlugins || []
    ].filter((p) => p);
    this.state = {
      suggestions: [],
      typeaheadContext: null,
      typeaheadPrefix: "",
      typeaheadText: "",
      value: makeValue(props.query || "", props.syntax)
    };
  }
  componentDidMount() {
    this.mounted = true;
  }
  componentWillUnmount() {
    this.mounted = false;
  }
  componentDidUpdate(prevProps, prevState) {
    const { query, syntax, syntaxLoaded } = this.props;
    if (!prevProps.syntaxLoaded && syntaxLoaded && this.editor) {
      const editor = this.editor.insertText(" ").deleteBackward(1);
      this.onChange(editor.value, true);
    }
    const { value } = this.state;
    if (query !== prevProps.query) {
      if (query !== Plain.serialize(value)) {
        this.setState({ value: makeValue(query || "", syntax) });
      }
    }
  }
  cleanText(text) {
    const newText = text.replace(/[\r]/g, "");
    return newText;
  }
  render() {
    const { disabled, theme } = this.props;
    const wrapperClassName = classNames("slate-query-field__wrapper", {
      "slate-query-field__wrapper--disabled": disabled
    });
    const styles = getStyles(theme);
    return /* @__PURE__ */ jsx("div", { className: cx(wrapperClassName, styles.wrapper), children: /* @__PURE__ */ jsx("div", { className: "slate-query-field", "data-testid": selectors.components.QueryField.container, children: /* @__PURE__ */ jsx(
      Editor,
      {
        ref: (editor) => this.editor = editor,
        schema: SCHEMA,
        autoCorrect: false,
        readOnly: this.props.disabled,
        onBlur: this.handleBlur,
        onClick: this.props.onClick,
        onChange: (change) => {
          this.onChange(change.value, false);
        },
        placeholder: this.props.placeholder,
        plugins: this.plugins,
        spellCheck: false,
        value: this.state.value
      }
    ) }) });
  }
}
// By default QueryField calls onChange if onBlur is not defined, this will trigger a rerender
// And slate will claim the focus, making it impossible to leave the field.
UnThemedQueryField.defaultProps = {
  onBlur: () => {
  }
};
const QueryField = withTheme2(UnThemedQueryField);
const getStyles = (theme) => {
  const focusStyles = getFocusStyles(theme);
  return {
    wrapper: css({
      "&:focus-within": focusStyles
    })
  };
};

export { QueryField, UnThemedQueryField };
//# sourceMappingURL=QueryField.mjs.map
