import React from 'react';
import classNames from 'classnames';
import debounce from 'lodash-es/debounce';
import { DropdownEvents } from './dropdown-events';
import { getFocusableElements } from '../hooks/use-focus-trap';
import PopperDropdownWrapper from './popper-dropdown-wrapper';
import { useTranslation } from '../hooks/use-translation';

/* USAGE EXAMPLE
  <Dropdown
    renderAnchor={({ ref, onClick }) => (
      <button ref={ref} onClick={onClick}>
        This is anchor button. If you click on it - it will trigger menu open
      </button>
    )}
  >
    {({ closeDropdown }) => (
      <div>
        This is menu body. You can assign closeDropdown action
        to some button here
        <button onClick={closeDropdown}>X</button>
      </div>
    )}
  </Dropdown>
*/
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
/** Base Dropdown component that enables custom dropdown implementations.
 * For Menu, Selectbox or Searchable Dropdown usecases please reffer to DropdownMenu or Searchable Dropdown
 * [CAPITALIZATION] Use title case for labels, no colon at the end
 * [CAPITALIZATION] Use sentence case for selection
 * @midnight-gray-supported
 */

export default function Dropdown(props) {
  const {
    t
  } = useTranslation();
  return /*#__PURE__*/_jsx(DropdownBase, {
    contentAriaLabel: t('Dropdown content'),
    ...props
  });
}
class DropdownBase extends React.Component {
  static defaultProps = {
    isDisabled: false,
    startOpen: false,
    fitWidthToRef: false,
    useCloseTransition: true,
    destroyOnScroll: false,
    onOpen: () => {},
    onClose: () => {},
    testId: '',
    forceUpdatePopperAfterOpen: false,
    hasCustomFocusHandling: false,
    shouldCloseDropdownOnEsc: true
  };

  // used to determine if Dropdown was opened using keyboard
  // some accessibility tools like NVDA will trigger onClick with event when using keyboard

  constructor(props) {
    super(props);
    this.state = {
      open: false,
      openedWithKeyboard: undefined,
      width: undefined
    };
    this.openedWithKeyboard = true;
    this.handleToggle = this.handleToggle.bind(this);
    this.closeDropdown = this.closeDropdown.bind(this);
    this.setAnchorRef = this.setAnchorRef.bind(this);
    this.fitWidth = this.fitWidth.bind(this);
    this.setWidth = this.setWidth.bind(this);
    this.scrollHandler = this.scrollHandler.bind(this);
    this.focusOnTriggerAfterClose = this.focusOnTriggerAfterClose.bind(this);
    this.onMouseOver = this.onMouseOver.bind(this);
    this.onMouseLeave = this.onMouseLeave.bind(this);
    this.onCloseAllDropdownsListener = null;
    this.debouncedResize = debounce(this.setWidth, 300);
  }
  componentDidMount() {
    if (this.props.fitWidthToRef) {
      window.addEventListener('resize', this.debouncedResize);
    }
    if (this.props.startOpen) {
      setTimeout(() => {
        this.setState({
          open: this.props.startOpen || false
        });
      }, 1000);
    }
    if (this.props.destroyOnScroll) {
      window.addEventListener('wheel', this.scrollHandler);
    }
  }
  onMouseOver() {
    this.openedWithKeyboard = false;
  }
  onMouseLeave() {
    this.openedWithKeyboard = true;
  }
  scrollHandler(event) {
    if (event && this.contentRef?.contains(event.target)) {
      return;
    }
    this.closeDropdown();
  }
  componentDidUpdate(prevProps, prevState) {
    if (this.props.isDisabled && this.state.open) {
      this.closeDropdown();
    }
    const hasJustOpened = !prevState.open && this.state.open;
    if (!this.props.hasCustomFocusHandling && hasJustOpened) {
      this.contentRef?.focus();
    }
  }
  componentWillUnmount() {
    if (this.props.fitWidthToRef) {
      window.removeEventListener('resize', this.debouncedResize);
    }
    if (this.props.destroyOnScroll) {
      window.removeEventListener('wheel', this.scrollHandler);
    }
    if (this.onCloseAllDropdownsListener) {
      this.onCloseAllDropdownsListener();
    }
    if (this.anchorRef) {
      this.anchorRef.removeEventListener('mouseover', this.onMouseOver);
      this.anchorRef.removeEventListener('mouseleave', this.onMouseLeave);
    }
    this.debouncedResize?.cancel?.();
  }
  handleToggle(event, openedWithKeyboard = false) {
    // e.detail stores number of clicks, so 0 means there was no clicks, hence - it's a keyboard initiated click
    // more info: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
    // upd. this is not always reliable as some acessibility tools like NVDA
    // will trigger onClick with event.detail===1 when using keyboard
    const inferredOpenedWithKeyboard = event?.detail === 0;
    this.setState(state => ({
      open: !state.open,
      openedWithKeyboard: openedWithKeyboard || inferredOpenedWithKeyboard || this.openedWithKeyboard
    }));
    if (!this.state.open) {
      this.props.onOpen?.(openedWithKeyboard);
      this.onCloseAllDropdownsListener = DropdownEvents.onCloseAllDropdowns(() => {
        this.closeDropdown();
      });
    } else {
      this.focusOnTriggerAfterClose();
      this.props.onClose?.();
    }
  }
  handleCustomContentKeyDown = event => {
    if (event.key === 'Tab' && this.state.open) {
      const focusableElements = getFocusableElements(this.contentRef);
      const firstFocusableElement = focusableElements[0];
      const lastFocusableElement = focusableElements.at(-1);
      const noFocusableElements = !firstFocusableElement && !lastFocusableElement;
      const isTargetLastElement = event.target === lastFocusableElement;
      const isTargetFirstElementOrContent = event.target === firstFocusableElement || event.target === this.contentRef;
      if (noFocusableElements || !event.shiftKey && isTargetLastElement || event.shiftKey && isTargetFirstElementOrContent) {
        event.preventDefault();
        this.closeDropdown();
      }
    }
  };
  closeDropdown(event) {
    if (event && this.anchorRef?.contains(event.target)) {
      return;
    }
    this.setState({
      open: false
    });
    this.focusOnTriggerAfterClose();
    this.props.onClose?.();
    if (this.onCloseAllDropdownsListener) {
      this.onCloseAllDropdownsListener();
    }
  }
  focusOnTriggerAfterClose() {
    // if dropdown was opened with keyboard and there is trigger ref - focus on trigger so that user can continue navigating through the UI with Tab key
    if (this.state.openedWithKeyboard && this.anchorRef) {
      // there is no point of returning focus to anchor if popup content not in focus anymore
      // also this were causing issues in cypress when focusOnTriggerAfterClose being called with delay and focus already moved to anoter element
      if (this.contentRef?.contains(document.activeElement)) {
        this.anchorRef?.focus();
      }
    }
  }
  setAnchorRef(ref) {
    if (ref) {
      ref.addEventListener('mouseover', this.onMouseOver);
      ref.addEventListener('mouseleave', this.onMouseLeave);
    }
    this.anchorRef = ref;
  }
  setWidth() {
    this.setState({
      width: this.anchorRef?.scrollWidth
    });
  }
  fitWidth() {
    const {
      children
    } = this.props;
    let initialWidth = this.anchorRef ? this.anchorRef?.scrollWidth : 'auto';
    if (typeof this.props.fitWidthToRef === 'string') {
      const element = document.querySelector(this.props.fitWidthToRef);
      if (element) {
        initialWidth = element.scrollWidth;
      }
    }
    const {
      width = initialWidth
    } = this.state;
    if (typeof children === 'function') {
      // eslint-disable-next-line react/display-name
      return props =>
      /*#__PURE__*/
      /* eslint-disable-next-line react/forbid-dom-props */
      _jsx("div", {
        className: "fit-width-to-ref-wrapper",
        style: {
          width
        },
        children: children(props)
      });
    }
    return /*#__PURE__*/ /* eslint-disable-next-line react/forbid-dom-props */_jsx("div", {
      className: "fit-width-to-ref-wrapper",
      style: {
        width
      },
      children: children
    });
  }
  render() {
    const {
      open,
      openedWithKeyboard
    } = this.state;
    const {
      renderAnchor,
      contentClassName,
      selectedOption,
      placement,
      children,
      fitWidthToRef,
      isDisabled,
      useCloseTransition,
      testId,
      id,
      forceUpdatePopperAfterOpen,
      shouldCloseDropdownOnEsc
    } = this.props;
    const modifiers =
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    !window.Cypress || !window.imageSnapshots ? [] : [{
      // A hack to have stable image snapshots for dropdowns
      // See https://github.com/datarobot/DataRobot/pull/80977#discussion_r630878279
      name: 'eventListeners',
      options: {
        resize: false,
        scroll: false
      }
    }];
    return /*#__PURE__*/_jsxs(_Fragment, {
      children: [renderAnchor({
        ref: this.setAnchorRef,
        onClick: this.handleToggle,
        isOpen: this.state.open,
        selectedOption
      }), /*#__PURE__*/_jsx(PopperDropdownWrapper, {
        closeDropdown: this.closeDropdown,
        anchorRef: this.anchorRef,
        open: isDisabled ? false : open,
        openedWithKeyboard: !!openedWithKeyboard,
        placement: placement,
        contentClassName: classNames(contentClassName, 'dropdown-content', {
          'fit-width-to-ref': fitWidthToRef
        }),
        useCloseTransition: useCloseTransition,
        setParentRef: r => {
          this.contentRef?.removeEventListener('keydown', this.handleCustomContentKeyDown);
          this.contentRef = r;
          if (!this.props.hasCustomFocusHandling) {
            this.contentRef?.addEventListener('keydown', this.handleCustomContentKeyDown);
            this.contentRef?.setAttribute('role', 'dialog');
            this.contentRef?.setAttribute('tabIndex', '-1'); // Make the element programmatically focusable, so we can focus on it when it opens (see componentDidUpdate)

            this.contentRef?.setAttribute('aria-label', this.props.contentAriaLabel || '');
          }
        },
        wrapperContentTestId: testId,
        wrapperContentId: id,
        modifiers: modifiers,
        forceUpdatePopperAfterOpen: forceUpdatePopperAfterOpen,
        shouldCloseOnEsc: shouldCloseDropdownOnEsc,
        children: fitWidthToRef ? this.fitWidth() : children
      })]
    });
  }
}