"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.TOOLTIP_PLACEMENT_TYPES = exports.TOOLTIP_DELAY_TYPES = void 0;
var _react = _interopRequireWildcard(require("react"));
var _classnames = _interopRequireDefault(require("classnames"));
var _reactPopper = require("../react-popper");
var _useFocusTrap = require("../hooks/use-focus-trap");
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
// TODO decide what to do with these constants later, for now just declaring them here
// import {
//   TOOLTIP_DELAY_TYPES,
//   TOOLTIP_DELAY_VALUES,
// } from 'js/constants/timer-delays';
const TOOLTIP_DELAY_TYPES = exports.TOOLTIP_DELAY_TYPES = {
  NONE: 'none',
  FAST: 'fast',
  MEDIUM: 'medium',
  SLOW: 'slow'
};
const TOOLTIP_DELAY_VALUES = {
  [TOOLTIP_DELAY_TYPES.NONE]: 0,
  [TOOLTIP_DELAY_TYPES.FAST]: 200,
  [TOOLTIP_DELAY_TYPES.MEDIUM]: 500,
  [TOOLTIP_DELAY_TYPES.SLOW]: 700
};
const TOOLTIP_PLACEMENT_TYPES = exports.TOOLTIP_PLACEMENT_TYPES = {
  BOTTOM: 'bottom',
  BOTTOM_END: 'bottom-end',
  BOTTOM_START: 'bottom-start',
  LEFT: 'left',
  LEFT_END: 'left-end',
  LEFT_START: 'left-start',
  RIGHT: 'right',
  RIGHT_END: 'right-end',
  RIGHT_START: 'right-start',
  TOP: 'top',
  TOP_END: 'top-end',
  TOP_START: 'top-start'
};
let idCounter = 0;
function generateUniqueId() {
  return `tooltip-${++idCounter}`;
}

/** Tooltip display informative text when users hover over or focus on element
 * [CAPITALIZATION] Use sentence case. If it is a complete sentence, use a period. If it is not, do not use a period. However, if it is followed by an action, like "Open documentation", make it a complete sentence and use a period.
 * @alpine-light-supported
 * @midnight-gray-supported
 */
class Tooltip extends _react.Component {
  static defaultProps = {
    isDisabled: false,
    delayType: TOOLTIP_DELAY_TYPES.FAST,
    interactiveCloseOnClick: false,
    placement: TOOLTIP_PLACEMENT_TYPES.RIGHT,
    testId: 'tooltip',
    white: false
  };
  constructor(props) {
    super(props);
    this.childRef = /*#__PURE__*/_react.default.createRef();
    this.popper = /*#__PURE__*/_react.default.createRef();
    this.state = {
      shouldOpen: false,
      openedWithKeyboard: false,
      interactiveElements: []
    };
    this.timer = null;
    this.delay = TOOLTIP_DELAY_VALUES[props.delayType || TOOLTIP_DELAY_TYPES.FAST];
    this.tooltipId = this.props.tooltipId || generateUniqueId();
  }
  componentWillUnmount() {
    if (this.timer) {
      clearTimeout(this.timer);
    }
  }

  // MOUSE INTERACTIONS
  handleMouseOver = event => {
    const {
      children
    } = this.props;
    if (children.props.onMouseOver) {
      children.props.onMouseOver(event);
    }
    this.toggleTooltip(true);
    document.addEventListener('keydown', this.handleKeydownTabEscOnMouseOver);
    this.setState({
      openedWithKeyboard: false
    });
  };
  handleMouseLeave = event => {
    const {
      children
    } = this.props;
    const fastDelay = TOOLTIP_DELAY_VALUES[TOOLTIP_DELAY_TYPES.FAST];
    if (children.props.onMouseOut) {
      children.props.onMouseOut(event);
    }
    if (this.timer) {
      clearTimeout(this.timer);
    }
    document.removeEventListener('keydown', this.handleKeydownTabEscOnMouseOver);
    setTimeout(() => {
      if (this.cursorInPopout || !this.popper.current) {
        return;
      }
      this.toggleTooltip(false);
    }, fastDelay);
  };
  handlePopoutMouseEnter = () => {
    const noneDelay = TOOLTIP_DELAY_VALUES[TOOLTIP_DELAY_TYPES.NONE];
    this.cursorInPopout = true;
    setTimeout(() => {
      if (!this.popper.current) {
        return;
      }
      this.setState({
        openedWithKeyboard: false
      });
      this.popper.current.focus();
      this.popper.current.addEventListener('mouseleave', this.handlePopoutMouseLeave);
    }, noneDelay);
  };
  handlePopoutMouseLeave = () => {
    this.cursorInPopout = false;
    this.toggleTooltip(false);
    if (!this.popper.current) {
      return;
    }
    this.popper.current.removeEventListener('mouseleave', this.handlePopoutMouseLeave);
  };
  handlePopoutContentClick = event => {
    event.stopPropagation();
    if (this.props.interactiveCloseOnClick) {
      this.toggleTooltip(false);
    }
  };
  handleClick = event => {
    const {
      children
    } = this.props;
    this.toggleTooltip(false);
    return children.props.onClick && children.props.onClick(event);
  };

  // KEYBOARD INTERACTIONS
  handleOnKeydown = event => {
    const {
      key,
      shiftKey
    } = event;
    const {
      children
    } = this.props;
    if (children.props.onKeyDown) {
      children.props.onKeyDown(event);
    }
    if (key === 'Tab' && this.state.interactiveElements.length > 0 && this.popper.current) {
      if (shiftKey) {
        event.preventDefault();
        this.toggleTooltip(false);
        return;
      }
      this.popper.current.focus();
      this.popper.current.addEventListener('keydown', e => this.handlePopperKeydown(e));
    }
    if (key === 'Escape') {
      this.toggleTooltip(false);
    }
  };
  handleKeydownTabEscOnMouseOver = event => {
    const {
      key
    } = event;
    if (key === 'Tab' && this.childRef.current && this.state.interactiveElements.length === 0) {
      this.toggleTooltip(false);
    }
    if (key === 'Tab' && this.popper.current && this.state.interactiveElements.length > 0) {
      this.popper.current.focus();
      this.popper.current.addEventListener('keydown', e => this.handlePopperKeydown(e));
    }
    if (key === 'Escape') {
      this.toggleTooltip(false);
    }
  };
  handlePopoutOnKeydown = event => {
    event.stopPropagation();
    if (event.key === 'Escape') {
      this.toggleTooltip(false);
    }
  };
  handleKeyboardFocus = event => {
    const {
      children
    } = this.props;
    this.setState({
      openedWithKeyboard: true
    });
    if (children.props.onFocus) {
      children.props.onFocus(event);
    }
    this.toggleTooltip(true);
  };
  handleOnKeyboardBlur = event => {
    const {
      children
    } = this.props;
    if (children.props.onBlur) {
      children.props.onBlur(event);
    }
    if (this.timer) {
      clearTimeout(this.timer);
    }
    if (this.state.interactiveElements.length === 0) {
      this.toggleTooltip(false);
    }
  };
  handleLeaveAndCloseTooltip = () => {
    if (!this.popper.current) {
      return;
    }
    this.childRef.current?.focus();
    this.popper.current.removeEventListener('keydown', this.handlePopperKeydown);
    this.toggleTooltip(false);
  };
  handlePopperKeydown = event => {
    if (event.key === 'Escape') {
      this.handleLeaveAndCloseTooltip();
    }
    if (event.key === 'Tab' && this.popper.current) {
      const {
        interactiveElements
      } = this.state;
      const firstFocusableElement = interactiveElements[0];
      const lastFocusableElement = interactiveElements.at(-1);
      const noFocusableElements = !firstFocusableElement && !lastFocusableElement;
      const isTargetLastElement = event.target === lastFocusableElement;
      const isTargetFirstElementOrContent = event.target === firstFocusableElement || event.target === this.popper.current;
      if (noFocusableElements || !event.shiftKey && isTargetLastElement || event.shiftKey && isTargetFirstElementOrContent) {
        event.preventDefault();
        this.handleLeaveAndCloseTooltip();
      }
    }
  };

  // STATE MANAGEMENT
  shouldTooltipBeRendered() {
    const {
      content,
      docsLink,
      isDisabled
    } = this.props;
    return (!!content || !!docsLink) && !isDisabled;
  }
  toggleTooltip(shouldOpen) {
    if (this.timer) {
      clearTimeout(this.timer);
    }

    // open with delay
    const delay = shouldOpen ? this.delay : TOOLTIP_DELAY_VALUES[TOOLTIP_DELAY_TYPES.NONE];
    this.timer = setTimeout(() => {
      this.timer = null;
      this.setState({
        shouldOpen
      }, () => {
        if (this.state.shouldOpen && this.popper.current) {
          const interactiveElements = (0, _useFocusTrap.getFocusableElements)(this.popper.current);
          if (interactiveElements.length > 0) {
            this.setState({
              interactiveElements
            });
          }
        }
      });
    }, delay);
  }
  render() {
    const {
      children,
      content,
      className,
      placement,
      testId,
      docsLink,
      white,
      popperModifiers
    } = this.props;
    const {
      shouldOpen,
      interactiveElements
    } = this.state;
    const isInteractive = interactiveElements.length > 0;
    return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
      children: [/*#__PURE__*/_react.default.cloneElement(children, {
        ...(shouldOpen && content && {
          'aria-describedby': this.tooltipId
        }),
        ref: element => {
          // eslint-disable-next-line @typescript-eslint/ban-ts-comment
          // @ts-ignore
          this.childRef.current = element;
          // this.props?.childRef?.(element);

          // if children of the tooltip had separate ref used in JSX - preserve it instead of overwriting
          // won't work if children is an array ;(
          /* eslint-disable @typescript-eslint/ban-ts-comment */
          // @ts-ignore
          if (children.ref) {
            // this just to cover case when consumer passed used ref={(element) => } instead of passing ref. (Which is valid although weird, yeah)
            // @ts-ignore
            if (typeof children.ref === 'function') {
              // @ts-ignore
              children.ref(element);
            } else {
              // @ts-ignore
              children.ref.current = element;
            }
          }
        },
        onMouseOver: this.handleMouseOver,
        onMouseLeave: this.handleMouseLeave,
        onClick: this.handleClick,
        onFocus: this.handleKeyboardFocus,
        onBlur: this.handleOnKeyboardBlur,
        onKeyDown: this.handleOnKeydown
      }), this.shouldTooltipBeRendered() && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactPopper.Popper, {
        anchorEl: this.childRef.current,
        open: shouldOpen,
        placement: placement,
        rootEl: this.popper,
        strategy: _reactPopper.POPPER_STRATEGY_OPTIONS.FIXED,
        popperModifiers: popperModifiers || [_reactPopper.OFFSET_MODIFIER, _reactPopper.FLIP_MODIFIER],
        "aria-hidden": false,
        children: () =>
        /*#__PURE__*/
        // eslint-disable-next-line jsx-a11y/no-static-element-interactions
        (0, _jsxRuntime.jsxs)("div", {
          className: (0, _classnames.default)('popout popout-transition react-tooltip', {
            white
          }, className),
          onClick: this.handlePopoutContentClick,
          onKeyDown: this.handlePopoutOnKeydown,
          onMouseEnter: this.handlePopoutMouseEnter,
          ref: this.popper,
          "test-id": testId,
          id: this.tooltipId,
          tabIndex: -1,
          children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
            className: "tooltip-content",
            tabIndex: isInteractive ? 0 : undefined,
            role: isInteractive ? 'dialog' : 'tooltip',
            children: [content, docsLink && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
              className: "popout-docs-link",
              children: docsLink
            })]
          }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
            "data-popper-arrow": ""
          })]
        })
      })]
    });
  }
}
var _default = exports.default = Tooltip;