"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.Dropdown = void 0;
var _react = require("react");
var _DropdownMenu = _interopRequireDefault(require("./DropdownMenu"));
var _debounce = _interopRequireDefault(require("../utilities/debounce"));
var _describeField = _interopRequireDefault(require("../utilities/describeField"));
var _classnames = _interopRequireDefault(require("classnames"));
var _cleanFieldProps = _interopRequireDefault(require("../utilities/cleanFieldProps"));
var _mergeRefs = _interopRequireDefault(require("../utilities/mergeRefs"));
var _useClickOutsideHandler = _interopRequireDefault(require("../utilities/useClickOutsideHandler"));
var _useId = _interopRequireDefault(require("../utilities/useId"));
var _useAutoFocus = _interopRequireDefault(require("../utilities/useAutoFocus"));
var _Icons = require("../Icons");
var _utils = require("./utils");
var _reactAria = require("../react-aria");
var _useLabelProps = require("../Label/useLabelProps");
var _useHint = require("../Hint/useHint");
var _useInlineError = require("../InlineError/useInlineError");
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
// from react-stately
// from react-aria

const caretIcon = /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icons.SvgIcon, {
  title: "",
  viewBox: "0 0 448 512",
  className: "ds-u-font-size--sm",
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
    d: "M212.7 148.7c6.2-6.2 16.4-6.2 22.6 0l160 160c6.2 6.2 6.2 16.4 0 22.6s-16.4 6.2-22.6 0L224 182.6 75.3 331.3c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6l160-160z"
  })
});
/**
 * For information about how and when to use this component,
 * [refer to its full documentation page](https://design.cms.gov/components/dropdown/).
 */
const Dropdown = props => {
  (0, _utils.validateProps)(props);
  const id = (0, _useId.default)('dropdown--', props.id);
  const buttonContentId = `${id}__button-content`;
  const menuId = `${id}__menu`;

  // Draw out certain props that we don't want to pass through as attributes
  const {
    autoFocus,
    children,
    className,
    fieldClassName,
    onBlur: userOnBlur,
    onChange,
    options,
    size,
    defaultValue,
    value,
    inputRef,
    inversed,
    getA11yStatusMessage,
    getA11ySelectionMessage,
    ...extraProps
  } = props;
  const optionsAndGroups = options ?? (0, _utils.parseChildren)(children);
  const renderReactStatelyItem = item => {
    const {
      label,
      value,
      ...extraAttrs
    } = item;
    return /*#__PURE__*/(0, _react.createElement)(_reactAria.Item, {
      ...extraAttrs,
      key: value
    }, label);
  };
  const reactStatelyItems = optionsAndGroups.map((item, index) => {
    if ((0, _utils.isOptGroup)(item)) {
      const {
        label,
        options,
        ...extraAttrs
      } = item;
      return /*#__PURE__*/(0, _react.createElement)(_reactAria.Section, {
        ...extraAttrs,
        key: `group-${index}`,
        title: label
      }, options.map(renderReactStatelyItem));
    } else {
      return renderReactStatelyItem(item);
    }
  });
  const isControlled = value !== undefined;
  let fallbackValue = defaultValue;
  if (!isControlled && fallbackValue === undefined) {
    fallbackValue = (0, _utils.getFirstOptionValue)(optionsAndGroups);
  }
  const [internalValueState, setInternalValueState] = (0, _react.useState)(fallbackValue);
  const selectedKey = isControlled ? value : internalValueState;
  const onSelectionChange = value => {
    triggerRef.current?.focus?.();
    if (onChange) {
      // Try to support the old API that passed an event object
      const target = {
        value,
        name: props.name
      };
      onChange({
        target,
        currentTarget: target
      });
    }
    if (!isControlled) {
      setInternalValueState(value);
    }
  };
  const state = (0, _reactAria.useSelectState)({
    ...props,
    children: reactStatelyItems,
    selectedKey,
    onSelectionChange
  });
  const {
    errorId,
    topError,
    bottomError,
    invalid
  } = (0, _useInlineError.useInlineError)({
    ...props,
    id
  });
  const {
    hintId,
    hintElement
  } = (0, _useHint.useHint)({
    ...props,
    id
  });
  const onBlur = (0, _react.useCallback)(
  // The active element is always the document body during a focus transition,
  // so in order to check if the newly focused element is one of our other date
  // inputs, we're going to have to wait a bit. We also have an issue with
  // tabbing out firing two blur events, so debounce during that time too. In
  // order for the debounce to work, we need to wrap this in a useCallback so
  // don't create a new one on each render.
  (0, _debounce.default)(event => {
    // Only call the user's onBlur handler if focus leaves the whole component
    if (!wrapperRef.current?.contains(document.activeElement)) {
      userOnBlur?.(event);
      state.setOpen(false);
    }
  }, 20), [userOnBlur, state]);
  const triggerRef = (0, _react.useRef)();
  const useSelectProps = (0, _reactAria.useSelect)({
    ...props,
    onBlur,
    isDisabled: props.disabled
  }, state, triggerRef);
  const useButtonProps = (0, _reactAria.useButton)(useSelectProps.triggerProps, triggerRef);
  const labelProps = {
    ...useSelectProps.labelProps,
    ...(0, _useLabelProps.useLabelProps)({
      ...props,
      id,
      labelClassName: (0, _classnames.default)('ds-c-label', 'ds-c-dropdown__label', props.inversed && 'ds-c-label--inverse', props.labelClassName)
    })
  };

  // Excluding `inversed` prop from `<div>` label because it's not a valid attr
  const {
    inversed: _removeInversed,
    ...divLabelProps
  } = labelProps;
  const buttonProps = {
    ...useButtonProps.buttonProps,
    ...(0, _cleanFieldProps.default)(extraProps),
    id,
    name: undefined,
    className: (0, _classnames.default)('ds-c-dropdown__button', 'ds-c-field', props.errorMessage && 'ds-c-field--error', inversed && 'ds-c-field--inverse', size && `ds-c-field--${size}`, fieldClassName),
    ref: (0, _mergeRefs.default)([triggerRef, inputRef, (0, _useAutoFocus.default)(props.autoFocus)]),
    'aria-controls': menuId,
    'aria-labelledby': `${buttonContentId} ${labelProps.id}`,
    'aria-invalid': invalid,
    'aria-describedby': (0, _describeField.default)({
      ...props,
      hintId,
      errorId
    })
    // TODO: Someday we may want to add this `combobox` role back to the button, but right
    // now desktop VoiceOver has an issue. It seems to interpret the selected value in the
    // button as user input that needs to be checked for spelling (default setting). It
    // therefore announces anything it deems misspelled as such. The `react-aria` authors
    // likely ran into the same issue, since they leave it off for `useSelect` buttons.
    // Adding the combobox role in the future can help because screen reader users are more
    // familiar with the combobox pattern.
    // Another possible issue with this role - you should be able to select an option by typing
    // a character from that option. Without this role set, VO reads whatever option is closest
    // to the character typed. With this role set, VO reads nothing.
    // role: 'combobox',
  };
  const wrapperRef = (0, _react.useRef)();
  (0, _useClickOutsideHandler.default)([wrapperRef], () => state.setOpen(false));
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
    className: (0, _classnames.default)('ds-c-dropdown', className, state.isOpen && 'ds-c-dropdown--open'),
    ref: wrapperRef,
    children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
      ...divLabelProps
    }), hintElement, topError, /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactAria.HiddenSelect, {
      isDisabled: props.disabled,
      state: state,
      triggerRef: triggerRef,
      label: props.label,
      name: props.name
    }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("button", {
      ...buttonProps,
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
        id: buttonContentId,
        className: "ds-c-dropdown__label-text",
        children: state.selectedItem ? state.selectedItem.rendered : ''
      }), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
        className: "ds-c-dropdown__caret",
        children: caretIcon
      })]
    }), state.isOpen && /*#__PURE__*/(0, _jsxRuntime.jsx)(_DropdownMenu.default, {
      ...useSelectProps.menuProps,
      componentClass: "ds-c-dropdown",
      labelId: labelProps.id,
      menuId: menuId,
      rootId: id,
      size: size,
      state: state,
      triggerRef: triggerRef
    }), bottomError]
  });
};
exports.Dropdown = Dropdown;
var _default = exports.default = Dropdown;