"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// packages/block-editor/src/components/url-input/index.js
var url_input_exports = {};
__export(url_input_exports, {
  default: () => url_input_default
});
module.exports = __toCommonJS(url_input_exports);
var import_clsx = __toESM(require("clsx"));
var import_i18n = require("@wordpress/i18n");
var import_element = require("@wordpress/element");
var import_keycodes = require("@wordpress/keycodes");
var import_components = require("@wordpress/components");
var import_compose = require("@wordpress/compose");
var import_data = require("@wordpress/data");
var import_url = require("@wordpress/url");
var import_store = require("../../store/index.cjs");
var import_lock_unlock = require("../../lock-unlock.cjs");
var import_jsx_runtime = require("react/jsx-runtime");
var import_react = require("react");
var { ValidatedInputControl } = (0, import_lock_unlock.unlock)(import_components.privateApis);
function isFunction(maybeFunc) {
  return typeof maybeFunc === "function";
}
var URLInput = class extends import_element.Component {
  constructor(props) {
    super(props);
    this.onChange = this.onChange.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);
    this.selectLink = this.selectLink.bind(this);
    this.handleOnClick = this.handleOnClick.bind(this);
    this.bindSuggestionNode = this.bindSuggestionNode.bind(this);
    this.autocompleteRef = props.autocompleteRef || (0, import_element.createRef)();
    this.inputRef = props.inputRef || (0, import_element.createRef)();
    this.updateSuggestions = (0, import_compose.debounce)(
      this.updateSuggestions.bind(this),
      200
    );
    this.suggestionNodes = [];
    this.suggestionsRequest = null;
    this.state = {
      suggestions: [],
      showSuggestions: false,
      suggestionsValue: null,
      selectedSuggestion: null,
      suggestionsListboxId: "",
      suggestionOptionIdPrefix: ""
    };
  }
  componentDidUpdate(prevProps) {
    const { showSuggestions, selectedSuggestion } = this.state;
    const { value, __experimentalShowInitialSuggestions = false } = this.props;
    if (showSuggestions && selectedSuggestion !== null && this.suggestionNodes[selectedSuggestion]) {
      this.suggestionNodes[selectedSuggestion].scrollIntoView({
        behavior: "instant",
        block: "nearest",
        inline: "nearest"
      });
    }
    if (prevProps.value !== value && !this.props.disableSuggestions) {
      if (value?.length) {
        this.updateSuggestions(value);
      } else if (__experimentalShowInitialSuggestions) {
        this.updateSuggestions();
      }
    }
  }
  componentDidMount() {
    if (this.shouldShowInitialSuggestions()) {
      this.updateSuggestions();
    }
  }
  componentWillUnmount() {
    this.suggestionsRequest?.cancel?.();
    this.suggestionsRequest = null;
  }
  bindSuggestionNode(index) {
    return (ref) => {
      this.suggestionNodes[index] = ref;
    };
  }
  shouldShowInitialSuggestions() {
    const { __experimentalShowInitialSuggestions = false, value } = this.props;
    return __experimentalShowInitialSuggestions && !(value && value.length);
  }
  updateSuggestions(value = "") {
    const {
      __experimentalFetchLinkSuggestions: fetchLinkSuggestions,
      __experimentalHandleURLSuggestions: handleURLSuggestions
    } = this.props;
    if (!fetchLinkSuggestions) {
      return;
    }
    const isInitialSuggestions = !value?.length;
    value = value.trim();
    if (!isInitialSuggestions && (value.length < 2 || !handleURLSuggestions && (0, import_url.isURL)(value))) {
      this.suggestionsRequest?.cancel?.();
      this.suggestionsRequest = null;
      this.setState({
        suggestions: [],
        showSuggestions: false,
        suggestionsValue: value,
        selectedSuggestion: null,
        loading: false
      });
      return;
    }
    this.setState({
      selectedSuggestion: null,
      loading: true
    });
    const request = fetchLinkSuggestions(value, {
      isInitialSuggestions
    });
    request.then((suggestions) => {
      if (this.suggestionsRequest !== request) {
        return;
      }
      this.setState({
        suggestions,
        suggestionsValue: value,
        loading: false,
        showSuggestions: !!suggestions.length
      });
      if (!!suggestions.length) {
        this.props.debouncedSpeak(
          (0, import_i18n.sprintf)(
            /* translators: %d: number of results. */
            (0, import_i18n._n)(
              "%d result found, use up and down arrow keys to navigate.",
              "%d results found, use up and down arrow keys to navigate.",
              suggestions.length
            ),
            suggestions.length
          ),
          "assertive"
        );
      } else {
        this.props.debouncedSpeak(
          (0, import_i18n.__)("No results."),
          "assertive"
        );
      }
    }).catch(() => {
      if (this.suggestionsRequest !== request) {
        return;
      }
      this.setState({
        loading: false
      });
    }).finally(() => {
      if (this.suggestionsRequest === request) {
        this.suggestionsRequest = null;
      }
    });
    this.suggestionsRequest = request;
  }
  onChange(newValue) {
    this.props.onChange(newValue);
  }
  onFocus() {
    const { suggestions } = this.state;
    const { disableSuggestions, value } = this.props;
    if (value && !disableSuggestions && !(suggestions && suggestions.length) && this.suggestionsRequest === null) {
      this.updateSuggestions(value);
    }
  }
  onKeyDown(event) {
    this.props.onKeyDown?.(event);
    const { showSuggestions, selectedSuggestion, suggestions, loading } = this.state;
    if (!showSuggestions || !suggestions.length || loading) {
      switch (event.keyCode) {
        // When UP is pressed, if the caret is at the start of the text, move it to the 0
        // position.
        case import_keycodes.UP: {
          if (0 !== event.target.selectionStart) {
            event.preventDefault();
            event.target.setSelectionRange(0, 0);
          }
          break;
        }
        // When DOWN is pressed, if the caret is not at the end of the text, move it to the
        // last position.
        case import_keycodes.DOWN: {
          if (this.props.value.length !== event.target.selectionStart) {
            event.preventDefault();
            event.target.setSelectionRange(
              this.props.value.length,
              this.props.value.length
            );
          }
          break;
        }
        // Submitting while loading should trigger onSubmit.
        case import_keycodes.ENTER: {
          if (this.props.onSubmit) {
            event.preventDefault();
            this.props.onSubmit(null, event);
          }
          break;
        }
      }
      return;
    }
    const suggestion = this.state.suggestions[this.state.selectedSuggestion];
    switch (event.keyCode) {
      case import_keycodes.UP: {
        event.preventDefault();
        const previousIndex = !selectedSuggestion ? suggestions.length - 1 : selectedSuggestion - 1;
        this.setState({
          selectedSuggestion: previousIndex
        });
        break;
      }
      case import_keycodes.DOWN: {
        event.preventDefault();
        const nextIndex = selectedSuggestion === null || selectedSuggestion === suggestions.length - 1 ? 0 : selectedSuggestion + 1;
        this.setState({
          selectedSuggestion: nextIndex
        });
        break;
      }
      case import_keycodes.TAB: {
        if (this.state.selectedSuggestion !== null) {
          this.selectLink(suggestion);
          this.props.speak((0, import_i18n.__)("Link selected."));
        }
        break;
      }
      case import_keycodes.ENTER: {
        event.preventDefault();
        if (this.state.selectedSuggestion !== null) {
          this.selectLink(suggestion);
          if (this.props.onSubmit) {
            this.props.onSubmit(suggestion, event);
          }
        } else if (this.props.onSubmit) {
          this.props.onSubmit(null, event);
        }
        break;
      }
    }
  }
  selectLink(suggestion) {
    this.props.onChange(suggestion.url, suggestion);
    this.setState({
      selectedSuggestion: null,
      showSuggestions: false
    });
  }
  handleOnClick(suggestion) {
    this.selectLink(suggestion);
    this.inputRef.current.focus();
  }
  static getDerivedStateFromProps({
    value,
    instanceId,
    disableSuggestions,
    __experimentalShowInitialSuggestions = false
  }, { showSuggestions }) {
    let shouldShowSuggestions = showSuggestions;
    const hasValue = value && value.length;
    if (!__experimentalShowInitialSuggestions && !hasValue) {
      shouldShowSuggestions = false;
    }
    if (disableSuggestions === true) {
      shouldShowSuggestions = false;
    }
    return {
      showSuggestions: shouldShowSuggestions,
      suggestionsListboxId: `block-editor-url-input-suggestions-${instanceId}`,
      suggestionOptionIdPrefix: `block-editor-url-input-suggestion-${instanceId}`
    };
  }
  render() {
    return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
      this.renderControl(),
      this.renderSuggestions()
    ] });
  }
  renderControl() {
    const {
      label = null,
      className,
      isFullWidth,
      instanceId,
      placeholder = (0, import_i18n.__)("Paste URL or type to search"),
      __experimentalRenderControl: renderControl,
      value = "",
      hideLabelFromVision = false,
      help = null,
      disabled = false,
      customValidity,
      markWhenOptional
    } = this.props;
    const {
      loading,
      showSuggestions,
      selectedSuggestion,
      suggestionsListboxId,
      suggestionOptionIdPrefix
    } = this.state;
    const inputId = `url-input-control-${instanceId}`;
    const controlProps = {
      id: inputId,
      // Passes attribute to label for the for attribute
      label,
      className: (0, import_clsx.default)("block-editor-url-input", className, {
        "is-full-width": isFullWidth
      }),
      hideLabelFromVision
    };
    const inputProps = {
      id: inputId,
      value,
      required: true,
      type: "text",
      name: inputId,
      autoComplete: "off",
      onChange: disabled ? () => {
      } : this.onChange,
      // Disable onChange when disabled
      onFocus: disabled ? () => {
      } : this.onFocus,
      // Disable onFocus when disabled
      placeholder,
      onKeyDown: disabled ? () => {
      } : this.onKeyDown,
      // Disable onKeyDown when disabled
      role: "combobox",
      "aria-label": label ? void 0 : (0, import_i18n.__)("URL"),
      // Ensure input always has an accessible label
      "aria-expanded": showSuggestions,
      "aria-autocomplete": "list",
      "aria-owns": suggestionsListboxId,
      "aria-activedescendant": selectedSuggestion !== null ? `${suggestionOptionIdPrefix}-${selectedSuggestion}` : void 0,
      ref: this.inputRef,
      disabled,
      suffix: this.props.suffix,
      help
    };
    const validationProps = {
      customValidity,
      // Suppress the "(Required)" indicator in the label.
      // The field is still required for validation, but the indicator
      // can be hidden when markWhenOptional is set to true.
      ...markWhenOptional !== void 0 && {
        markWhenOptional
      }
    };
    if (renderControl) {
      return renderControl(controlProps, inputProps, loading);
    }
    return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_components.BaseControl, { ...controlProps, children: [
      /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
        ValidatedInputControl,
        {
          ...inputProps,
          ...validationProps,
          __next40pxDefaultSize: true
        }
      ),
      loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Spinner, {})
    ] });
  }
  renderSuggestions() {
    const {
      className,
      __experimentalRenderSuggestions: renderSuggestions
    } = this.props;
    const {
      showSuggestions,
      suggestions,
      suggestionsValue,
      selectedSuggestion,
      suggestionsListboxId,
      suggestionOptionIdPrefix,
      loading
    } = this.state;
    if (!showSuggestions || suggestions.length === 0) {
      return null;
    }
    const suggestionsListProps = {
      id: suggestionsListboxId,
      ref: this.autocompleteRef,
      role: "listbox"
    };
    const buildSuggestionItemProps = (suggestion, index) => {
      return {
        role: "option",
        tabIndex: "-1",
        id: `${suggestionOptionIdPrefix}-${index}`,
        ref: this.bindSuggestionNode(index),
        "aria-selected": index === selectedSuggestion ? true : void 0
      };
    };
    if (isFunction(renderSuggestions)) {
      return renderSuggestions({
        suggestions,
        selectedSuggestion,
        suggestionsListProps,
        buildSuggestionItemProps,
        isLoading: loading,
        handleSuggestionClick: this.handleOnClick,
        isInitialSuggestions: !suggestionsValue?.length,
        currentInputValue: suggestionsValue
      });
    }
    return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Popover, { placement: "bottom", focusOnMount: false, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
      "div",
      {
        ...suggestionsListProps,
        className: (0, import_clsx.default)("block-editor-url-input__suggestions", {
          [`${className}__suggestions`]: className
        }),
        children: suggestions.map((suggestion, index) => /* @__PURE__ */ (0, import_react.createElement)(
          import_components.Button,
          {
            __next40pxDefaultSize: true,
            ...buildSuggestionItemProps(suggestion, index),
            key: suggestion.id,
            className: (0, import_clsx.default)(
              "block-editor-url-input__suggestion",
              {
                "is-selected": index === selectedSuggestion
              }
            ),
            onClick: () => this.handleOnClick(suggestion)
          },
          suggestion.title
        ))
      }
    ) });
  }
};
var url_input_default = (0, import_compose.compose)(
  import_compose.withSafeTimeout,
  import_components.withSpokenMessages,
  import_compose.withInstanceId,
  (0, import_data.withSelect)((select, props) => {
    if (isFunction(props.__experimentalFetchLinkSuggestions)) {
      return;
    }
    const { getSettings } = select(import_store.store);
    return {
      __experimentalFetchLinkSuggestions: getSettings().__experimentalFetchLinkSuggestions
    };
  })
)(URLInput);
//# sourceMappingURL=index.cjs.map
