function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
var _excluded = ["activeClassName", "ariaLabel", "children", "className", "component", "dialog", "id", "offset", "onClose", "onOpen", "inversed", "interactiveBorder", "placement", "maxWidth", "title", "transitionDuration", "zIndex", "showCloseButton", "closeButtonLabel", "contentHeading"];
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i.return && (_r = _i.return(), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
import React from "preact/compat";
// TODO: Update react-transition-group once we update react peer dep
import CSSTransition from 'react-transition-group/CSSTransition';
import FocusTrap from 'focus-trap-react';
import { useState, useRef, useEffect, useLayoutEffect } from "preact/compat";
import classNames from 'classnames';
import { createPopper } from '@popperjs/core';
import useId from "../utilities/useId";
import { Button } from "../Button";
import { CloseIconThin } from "../Icons";
import usePrevious from "../utilities/usePrevious";

// Similarly to the Button component, we want to expand the props type definition to
// permit pass-through props for the most commonly used underlying components.
// However, unlike in Button, we have not removed the ability for applications to
// define a custom `component` prop, which means there are theoretically props
// specific to that component type which will not be available in the TooltipProps
// definition. The strategy here is to keep the types simple by including just the
// possible attributes of button and anchor, but it comes at the expense of accuracy.
// If applications have extra props for their custom components, they will need to
// tell TypeScript to ignore those props for now. We'd like to revisit Tooltip in
// the future and improve it.
/**
 * Tooltips provide additional information upon hover, focus or click.
 * For information about how and when to use this component,
 * [refer to its full documentation page](https://design.cms.gov/components/tooltip/).
 *
 * When using the `<TooltipIcon>` as the only child of `<Tooltip>`, be sure to
 * provide an `aria-label` on the `<Tooltip>` to ensure an accessible name for
 * the trigger.
 */
export var Tooltip = function Tooltip(props) {
  var popper = useRef(null);
  var contentId = useId('tooltip-trigger--', props.id);
  var triggerElement = useRef(null);
  var tooltipElement = useRef(null);
  var setTriggerElement = function setTriggerElement(elem) {
    triggerElement.current = elem;
  };
  var setTooltipElement = function setTooltipElement(elem) {
    tooltipElement.current = elem;
  };
  var _useState = useState(false),
    _useState2 = _slicedToArray(_useState, 2),
    active = _useState2[0],
    setActive = _useState2[1];
  var _useState3 = useState(false),
    _useState4 = _slicedToArray(_useState3, 2),
    isHover = _useState4[0],
    setIsHover = _useState4[1];
  var _useState5 = useState(false),
    _useState6 = _slicedToArray(_useState5, 2),
    isMobile = _useState6[0],
    setIsMobile = _useState6[1];
  var prevActiveStateVar = usePrevious(active);
  var handleEscapeKey = function handleEscapeKey(event) {
    var ESCAPE_KEY = 27;
    if (active && event.keyCode === ESCAPE_KEY) {
      setActive(false);
    }
  };
  var handleClickOutside = function handleClickOutside(event) {
    if (active && (props.dialog || isMobile)) {
      var _triggerElement$curre, _tooltipElement$curre;
      var clickedTrigger = (_triggerElement$curre = triggerElement.current) === null || _triggerElement$curre === void 0 ? void 0 : _triggerElement$curre.contains(event.target);
      var clickedTooltip = (_tooltipElement$curre = tooltipElement.current) === null || _tooltipElement$curre === void 0 ? void 0 : _tooltipElement$curre.contains(event.target);
      if (!clickedTooltip && !clickedTrigger) {
        setActive(false);
      }
    }
  };
  var handleCloseButtonClick = function handleCloseButtonClick() {
    if (active && (props.dialog || isMobile)) {
      setActive(false);
    }
  };
  var handleBlur = function handleBlur(event) {
    setTimeout(function () {
      if (!isHover && event.currentTarget !== event.target) setActive(false);
    }, 10);
  };
  var handleTouch = function handleTouch() {
    // On mobile, touch -> mouseenter -> click events can all be fired simultaneously
    // `isMobile` flag is used inside onClick and onMouseEnter handlers, so touch events can be used in isolation on mobile
    // https://stackoverflow.com/a/65055198
    setIsMobile(true);
    setActive(!active);
  };
  useEffect(function () {
    if (!triggerElement.current || !tooltipElement.current) return;
    popper.current = createPopper(triggerElement.current, tooltipElement.current, {
      placement: props.placement,
      modifiers: [{
        name: 'offset',
        options: {
          offset: props.offset
        }
      }]
    });
    return function () {
      if (popper.current) popper.current.destroy();
    };
  }, []);
  useEffect(function () {
    document.addEventListener('mousedown', handleClickOutside);
    document.addEventListener('keydown', handleEscapeKey);
    return function () {
      document.removeEventListener('mousedown', handleClickOutside);
      document.removeEventListener('keydown', handleEscapeKey);
    };
  }, [handleClickOutside, handleEscapeKey]);
  useEffect(function () {
    if (active) {
      props.onOpen && props.onOpen();
    } else {
      props.onClose && props.onClose();

      // if tooltip goes from active to inactive and is the dialog version, focus the trigger
      if (prevActiveStateVar && (props.dialog || isMobile) && props.showCloseButton) {
        if (triggerElement && triggerElement.current) {
          triggerElement.current.focus();
        }
      }
    }
  }, [active]);
  useLayoutEffect(function () {
    if (popper.current) {
      popper.current.setOptions(props);
      popper.current.forceUpdate();
    }
  });
  var renderTrigger = function renderTrigger(props) {
    var _classNames;
    var activeClassName = props.activeClassName,
      ariaLabel = props.ariaLabel,
      children = props.children,
      className = props.className,
      component = props.component,
      dialog = props.dialog,
      id = props.id,
      offset = props.offset,
      onClose = props.onClose,
      onOpen = props.onOpen,
      inversed = props.inversed,
      interactiveBorder = props.interactiveBorder,
      placement = props.placement,
      maxWidth = props.maxWidth,
      title = props.title,
      transitionDuration = props.transitionDuration,
      zIndex = props.zIndex,
      showCloseButton = props.showCloseButton,
      closeButtonLabel = props.closeButtonLabel,
      contentHeading = props.contentHeading,
      others = _objectWithoutProperties(props, _excluded);
    var TriggerComponent = component;
    var triggerClasses = classNames('ds-c-tooltip__trigger', className, (_classNames = {}, _defineProperty(_classNames, activeClassName, activeClassName && active), _defineProperty(_classNames, 'ds-c-tooltip__trigger--inverse', inversed), _classNames));
    var linkTriggerOverrides = {
      tabIndex: TriggerComponent === 'a' ? 0 : undefined
    };
    var eventHandlers = dialog ? {
      onTouchStart: function onTouchStart() {
        return handleTouch();
      },
      onClick: function onClick() {
        if (!isMobile) {
          setActive(!active);
        }
      }
    } : {
      onTouchStart: function onTouchStart() {
        return handleTouch();
      },
      onClick: function onClick() {
        if (!isMobile) {
          setActive(!active);
        }
      },
      onFocus: function onFocus() {
        return setActive(true);
      },
      onBlur: function onBlur(event) {
        return handleBlur(event);
      }
    };
    return /*#__PURE__*/React.createElement(TriggerComponent, _extends({
      type: TriggerComponent === 'button' ? 'button' : undefined,
      "aria-label": ariaLabel || undefined,
      "aria-describedby": dialog ? undefined : contentId,
      className: triggerClasses,
      ref: setTriggerElement
    }, others, linkTriggerOverrides, eventHandlers), children);
  };
  var renderContent = function renderContent(props) {
    var closeButtonLabel = props.closeButtonLabel,
      dialog = props.dialog,
      contentHeading = props.contentHeading,
      inversed = props.inversed,
      interactiveBorder = props.interactiveBorder,
      placement = props.placement,
      maxWidth = props.maxWidth,
      showCloseButton = props.showCloseButton,
      title = props.title,
      transitionDuration = props.transitionDuration,
      zIndex = props.zIndex;
    var tooltipStyle = {
      maxWidth: maxWidth,
      zIndex: zIndex
    };
    var interactiveBorderStyle = {
      left: "-".concat(interactiveBorder, "px"),
      top: "-".concat(interactiveBorder, "px"),
      border: "".concat(interactiveBorder, "px solid transparent"),
      zIndex: -999 // ensures interactive border doesnt cover tooltip content
    };

    var eventHandlers = dialog ? {} : {
      onBlur: function onBlur(event) {
        return handleBlur(event);
      }
    };
    var tooltipContent = /*#__PURE__*/React.createElement("div", _extends({
      id: contentId,
      tabIndex: dialog ? -1 : null,
      ref: setTooltipElement,
      className: classNames('ds-c-tooltip', {
        'ds-c-tooltip--inverse': inversed
      }),
      style: tooltipStyle,
      "data-placement": placement,
      "aria-hidden": !active,
      role: dialog ? 'dialog' : 'tooltip'
    }, eventHandlers), /*#__PURE__*/React.createElement("span", {
      className: "ds-c-tooltip__arrow",
      "data-popper-arrow": true
    }), /*#__PURE__*/React.createElement("div", {
      className: "ds-c-tooltip__content"
    }, contentHeading || showCloseButton ? /*#__PURE__*/React.createElement("div", {
      className: classNames('ds-c-tooltip__header', {
        'ds-c-tooltip__header--right': !contentHeading
      })
    }, contentHeading, showCloseButton && /*#__PURE__*/React.createElement(Button, {
      variation: "ghost",
      size: "small",
      className: "ds-c-tooltip__close-button",
      onClick: handleCloseButtonClick,
      "aria-label": closeButtonLabel || 'Close'
    }, /*#__PURE__*/React.createElement(CloseIconThin, null))) : null, title), !dialog && /*#__PURE__*/React.createElement("span", {
      className: "ds-c-tooltip__interactive-border",
      style: interactiveBorderStyle
    }));
    return /*#__PURE__*/React.createElement(CSSTransition, {
      in: active,
      classNames: "ds-c-tooltip",
      timeout: transitionDuration
    }, dialog ? /*#__PURE__*/React.createElement(FocusTrap, {
      active: active,
      focusTrapOptions: {
        fallbackFocus: function fallbackFocus() {
          return document.getElementById("".concat(contentId)).parentElement;
        },
        initialFocus: function initialFocus() {
          return document.getElementById("".concat(contentId));
        },
        clickOutsideDeactivates: true
      }
    }, tooltipContent) : tooltipContent);
  };
  var mainEventHandlers = props.dialog ? {} : {
    onMouseEnter: function onMouseEnter() {
      if (!isMobile) {
        setIsHover(true);
        setActive(true);
      }
    },
    onMouseLeave: function onMouseLeave() {
      if (!isMobile) {
        setIsHover(false);
        setActive(false);
      }
    }
  };
  return /*#__PURE__*/React.createElement("div", _extends({
    className: "ds-c-tooltip__container"
  }, mainEventHandlers), renderTrigger(props), renderContent(props));
};
Tooltip.defaultProps = {
  component: 'button',
  interactiveBorder: 15,
  maxWidth: '300px',
  offset: [0, 5],
  placement: 'top',
  transitionDuration: 250,
  // Equivalent to $animation-speed-1
  zIndex: 9999
};
export default Tooltip;