"use strict";

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); }

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Tabs = Tabs;

var React = _interopRequireWildcard(require("react"));

var _reactUid = require("react-uid");

var _styles = require("../styles");

var _overrides = require("../helpers/overrides");

var _focusVisible = require("../utils/focusVisible");

var _constants = require("./constants");

var _styledComponents = require("./styled-components");

var _utils = require("./utils");

var _excluded = ["childKey", "childIndex", "activeKey", "orientation", "activeTabRef", "updateHighlight", "parseKeyDown", "activateOnFocus", "uid", "disabled", "sharedStylingProps", "onChange"],
    _excluded2 = ["artwork", "overrides", "tabRef", "onClick", "title"],
    _excluded3 = ["childKey", "childIndex", "activeKey", "uid", "sharedStylingProps", "renderAll"];

function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }

function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }

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 _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 _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 = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

var KEYBOARD_ACTION = {
  next: 'next',
  previous: 'previous'
};

var getLayoutParams = function getLayoutParams(el, orientation) {
  if (!el) {
    return {
      length: 0,
      distance: 0
    };
  } // Note, we are using clientHeight/Width here, which excludes borders.
  // This means borders won't be taken into account if someone adds borders
  // through overrides. In that case you would use getBoundingClientRect
  // which includes borders, but because it returns a fractional value the
  // highlight is slightly misaligned every so often.


  if ((0, _utils.isVertical)(orientation)) {
    return {
      length: el.clientHeight,
      distance: el.offsetTop
    };
  } else {
    return {
      length: el.clientWidth,
      distance: el.offsetLeft
    };
  }
};

var scrollParentToCentreTarget = function scrollParentToCentreTarget(targetNode) {
  var _targetNode$parentNod = targetNode.parentNode.getBoundingClientRect(),
      parentX = _targetNode$parentNod.x,
      parentY = _targetNode$parentNod.y,
      parentWidth = _targetNode$parentNod.width,
      parentHeight = _targetNode$parentNod.height;

  var _targetNode$getBoundi = targetNode.getBoundingClientRect(),
      childX = _targetNode$getBoundi.x,
      childY = _targetNode$getBoundi.y,
      childWidth = _targetNode$getBoundi.width,
      childHeight = _targetNode$getBoundi.height; // get the position of the child centre, relative to parent


  var childCentre = {
    x: childX - parentX + childWidth / 2,
    y: childY - parentY + childHeight / 2
  }; // aim for the centre of the child to be the centre of the parent

  var _targetNode$parentNod2 = targetNode.parentNode,
      scrollLeft = _targetNode$parentNod2.scrollLeft,
      scrollTop = _targetNode$parentNod2.scrollTop;
  var target = {
    x: scrollLeft + childCentre.x - parentWidth / 2,
    y: scrollTop + childCentre.y - parentHeight / 2
  }; // ignore out of bounds, the browser will manage this for us

  targetNode.parentNode.scroll(target.x, target.y);
};

function Tabs(_ref) {
  var _ref$activeKey = _ref.activeKey,
      activeKey = _ref$activeKey === void 0 ? '0' : _ref$activeKey,
      _ref$disabled = _ref.disabled,
      disabled = _ref$disabled === void 0 ? false : _ref$disabled,
      children = _ref.children,
      _ref$fill = _ref.fill,
      fill = _ref$fill === void 0 ? _constants.FILL.intrinsic : _ref$fill,
      _ref$activateOnFocus = _ref.activateOnFocus,
      activateOnFocus = _ref$activateOnFocus === void 0 ? true : _ref$activateOnFocus,
      onChange = _ref.onChange,
      _ref$orientation = _ref.orientation,
      orientation = _ref$orientation === void 0 ? _constants.ORIENTATION.horizontal : _ref$orientation,
      _ref$overrides = _ref.overrides,
      overrides = _ref$overrides === void 0 ? {} : _ref$overrides,
      _ref$renderAll = _ref.renderAll,
      renderAll = _ref$renderAll === void 0 ? false : _ref$renderAll,
      _ref$uid = _ref.uid,
      customUid = _ref$uid === void 0 ? null : _ref$uid;
  // Create unique id prefix for this tabs component
  var generatedUid = (0, _reactUid.useUID)();
  var uid = customUid || generatedUid; // Unpack overrides

  var RootOverrides = overrides.Root,
      TabListOverrides = overrides.TabList,
      TabHighlightOverrides = overrides.TabHighlight,
      TabBorderOverrides = overrides.TabBorder;

  var _getOverrides = (0, _overrides.getOverrides)(RootOverrides, _styledComponents.StyledRoot),
      _getOverrides2 = _slicedToArray(_getOverrides, 2),
      Root = _getOverrides2[0],
      RootProps = _getOverrides2[1];

  var _getOverrides3 = (0, _overrides.getOverrides)(TabListOverrides, _styledComponents.StyledTabList),
      _getOverrides4 = _slicedToArray(_getOverrides3, 2),
      TabList = _getOverrides4[0],
      TabListProps = _getOverrides4[1];

  var _getOverrides5 = (0, _overrides.getOverrides)(TabHighlightOverrides, _styledComponents.StyledTabHighlight),
      _getOverrides6 = _slicedToArray(_getOverrides5, 2),
      TabHighlight = _getOverrides6[0],
      TabHighlightProps = _getOverrides6[1];

  var _getOverrides7 = (0, _overrides.getOverrides)(TabBorderOverrides, _styledComponents.StyledTabBorder),
      _getOverrides8 = _slicedToArray(_getOverrides7, 2),
      TabBorder = _getOverrides8[0],
      TabBorderProps = _getOverrides8[1]; // Count key updates
  // We disable a few things until after first mount:
  // - the highlight animation, avoiding an initial slide-in
  // - smooth scrolling active tab into view


  var _React$useState = React.useState(0),
      _React$useState2 = _slicedToArray(_React$useState, 2),
      keyUpdated = _React$useState2[0],
      setKeyUpdated = _React$useState2[1];

  React.useEffect(function () {
    setKeyUpdated(keyUpdated + 1);
  }, [activeKey]); // Positioning the highlight.

  var activeTabRef = React.useRef();

  var _React$useState3 = React.useState({
    length: 0,
    distance: 0
  }),
      _React$useState4 = _slicedToArray(_React$useState3, 2),
      highlightLayout = _React$useState4[0],
      setHighlightLayout = _React$useState4[1]; // Create a shared, memoized callback for tabs to call on resize.


  var updateHighlight = React.useCallback(function () {
    if (activeTabRef.current) {
      setHighlightLayout(getLayoutParams(activeTabRef.current, orientation));
    }
  }, [activeTabRef.current, orientation]); // Update highlight on key and orientation changes.

  React.useEffect(updateHighlight, [activeTabRef.current, orientation]); // Scroll active tab into view when the parent has scrollbar on mount and
  // on key change (smooth scroll). Note, if the active key changes while
  // the tab is not in view, the page will scroll it into view.
  // TODO: replace with custom scrolling logic.

  React.useEffect(function () {
    // Flow needs this condition pulled out.
    if (activeTabRef.current) {
      if ((0, _utils.isHorizontal)(orientation) ? // @ts-expect-error todo(flow->ts) maybe parentElement?
      activeTabRef.current.parentNode.scrollWidth > // @ts-expect-error todo(flow->ts) maybe parentElement?
      activeTabRef.current.parentNode.clientWidth : // @ts-expect-error todo(flow->ts) maybe parentElement?
      activeTabRef.current.parentNode.scrollHeight > // @ts-expect-error todo(flow->ts) maybe parentElement?
      activeTabRef.current.parentNode.clientHeight) {
        if (keyUpdated > 1) {
          activeTabRef.current.scrollIntoView({
            behavior: 'smooth',
            block: 'nearest',
            inline: 'nearest'
          });
        } else {
          scrollParentToCentreTarget(activeTabRef.current);
        }
      }
    }
  }, [activeTabRef.current]); // Collect shared styling props

  var sharedStylingProps = {
    $orientation: orientation,
    $fill: fill
  }; // Helper for parsing directional keys
  // TODO(WPT-6473): move to universal keycode aliases

  var _useStyletron = (0, _styles.useStyletron)(),
      _useStyletron2 = _slicedToArray(_useStyletron, 2),
      theme = _useStyletron2[1];

  var parseKeyDown = React.useCallback(function (event) {
    if ((0, _utils.isHorizontal)(orientation)) {
      if ((0, _utils.isRTL)(theme.direction)) {
        switch (event.keyCode) {
          case 39:
            return KEYBOARD_ACTION.previous;

          case 37:
            return KEYBOARD_ACTION.next;

          default:
            return null;
        }
      } else {
        switch (event.keyCode) {
          case 37:
            return KEYBOARD_ACTION.previous;

          case 39:
            return KEYBOARD_ACTION.next;

          default:
            return null;
        }
      }
    } else {
      switch (event.keyCode) {
        case 38:
          return KEYBOARD_ACTION.previous;

        case 40:
          return KEYBOARD_ACTION.next;

        default:
          return null;
      }
    }
  }, [orientation, theme.direction]);
  return /*#__PURE__*/React.createElement(Root, _extends({}, sharedStylingProps, RootProps), /*#__PURE__*/React.createElement(TabList, _extends({
    "data-baseweb": "tab-list",
    role: "tablist",
    "aria-orientation": orientation
  }, sharedStylingProps, TabListProps), React.Children.map(children, function (child, index) {
    if (!child) return;
    return /*#__PURE__*/React.createElement(InternalTab, _extends({
      childKey: child.key,
      childIndex: index,
      activeKey: activeKey,
      orientation: orientation,
      activeTabRef: activeTabRef,
      updateHighlight: updateHighlight,
      parseKeyDown: parseKeyDown,
      activateOnFocus: activateOnFocus,
      uid: uid,
      disabled: disabled,
      sharedStylingProps: sharedStylingProps,
      onChange: onChange
    }, child.props));
  }), /*#__PURE__*/React.createElement(TabHighlight, _extends({
    "data-baseweb": "tab-highlight",
    $length: highlightLayout.length,
    $distance: highlightLayout.distance // This avoids the tab sliding in from the side on mount
    ,
    $animate: keyUpdated > 1,
    "aria-hidden": "true",
    role: "presentation"
  }, sharedStylingProps, TabHighlightProps))), /*#__PURE__*/React.createElement(TabBorder, _extends({
    "data-baseweb": "tab-border",
    "aria-hidden": "true",
    role: "presentation"
  }, sharedStylingProps, TabBorderProps)), React.Children.map(children, function (child, index) {
    if (!child) return;
    return /*#__PURE__*/React.createElement(InternalTabPanel, _extends({
      childKey: child.key,
      childIndex: index,
      activeKey: activeKey,
      uid: uid,
      sharedStylingProps: sharedStylingProps,
      renderAll: renderAll
    }, child.props));
  }));
}

function InternalTab(_ref2) {
  var childKey = _ref2.childKey,
      childIndex = _ref2.childIndex,
      activeKey = _ref2.activeKey,
      orientation = _ref2.orientation,
      activeTabRef = _ref2.activeTabRef,
      updateHighlight = _ref2.updateHighlight,
      parseKeyDown = _ref2.parseKeyDown,
      activateOnFocus = _ref2.activateOnFocus,
      uid = _ref2.uid,
      disabled = _ref2.disabled,
      sharedStylingProps = _ref2.sharedStylingProps,
      onChange = _ref2.onChange,
      props = _objectWithoutProperties(_ref2, _excluded);

  var key = childKey || String(childIndex);
  var isActive = key == activeKey;

  var Artwork = props.artwork,
      _props$overrides = props.overrides,
      overrides = _props$overrides === void 0 ? {} : _props$overrides,
      tabRef = props.tabRef,
      _onClick = props.onClick,
      title = props.title,
      restProps = _objectWithoutProperties(props, _excluded2); // A way to share our internal activeTabRef via the "tabRef" prop.


  var ref = React.useRef();
  React.useImperativeHandle(tabRef, function () {
    return isActive ? activeTabRef.current : ref.current;
  }); // Track tab dimensions in a ref after each render
  // This is used to compare params when the resize observer fires

  var tabLayoutParams = React.useRef({
    length: 0,
    distance: 0
  });
  React.useEffect(function () {
    tabLayoutParams.current = getLayoutParams(isActive ? activeTabRef.current : ref.current, orientation);
  }); // We need to potentially update the active tab highlight when the width or
  // placement changes for a tab so we listen for resize updates in each tab.

  React.useEffect(function () {
    if (window.ResizeObserver) {
      var observer = new window.ResizeObserver(function (entries) {
        if (entries[0] && entries[0].target) {
          var tabLayoutParamsAfterResize = getLayoutParams(entries[0].target, orientation);

          if (tabLayoutParamsAfterResize.length !== tabLayoutParams.current.length || tabLayoutParamsAfterResize.distance !== tabLayoutParams.current.distance) {
            updateHighlight();
          }
        }
      });
      observer.observe(isActive ? activeTabRef.current : ref.current);
      return function () {
        observer.disconnect();
      };
    }
  }, [activeKey, orientation]);
  React.useEffect(updateHighlight, [title]); // Collect overrides

  var TabOverrides = overrides.Tab,
      ArtworkContainerOverrides = overrides.ArtworkContainer;

  var _getOverrides9 = (0, _overrides.getOverrides)(TabOverrides, _styledComponents.StyledTab),
      _getOverrides10 = _slicedToArray(_getOverrides9, 2),
      Tab = _getOverrides10[0],
      TabProps = _getOverrides10[1];

  var _getOverrides11 = (0, _overrides.getOverrides)(ArtworkContainerOverrides, _styledComponents.StyledArtworkContainer),
      _getOverrides12 = _slicedToArray(_getOverrides11, 2),
      ArtworkContainer = _getOverrides12[0],
      ArtworkContainerProps = _getOverrides12[1]; // Keyboard focus styling


  var _React$useState5 = React.useState(false),
      _React$useState6 = _slicedToArray(_React$useState5, 2),
      focusVisible = _React$useState6[0],
      setFocusVisible = _React$useState6[1];

  var handleFocus = React.useCallback(function (event) {
    if ((0, _focusVisible.isFocusVisible)(event)) {
      setFocusVisible(true);
    }
  }, []);
  var handleBlur = React.useCallback(function (event) {
    if (focusVisible !== false) {
      setFocusVisible(false);
    }
  }, [focusVisible]); // Keyboard focus management
  // @ts-expect-error todo(flow->ts): deps are required

  var handleKeyDown = React.useCallback(function (event) {
    // WAI-ARIA 1.1
    // https://www.w3.org/TR/wai-aria-practices-1.1/#tabpanel
    // We use directional keys to iterate focus through Tabs.
    // Find all tabs eligible for focus
    var availableTabs = _toConsumableArray(event.target.parentNode.childNodes).filter(function (node) {
      return !node.disabled && node.getAttribute('role') === 'tab';
    }); // Exit early if there are no other tabs available


    if (availableTabs.length === 1) return; // Find tab to focus, looping to start/end of list if necessary

    var currentTabIndex = availableTabs.indexOf(event.target);
    var action = parseKeyDown(event);

    if (action) {
      var nextTab;

      if (action === KEYBOARD_ACTION.previous) {
        if (availableTabs[currentTabIndex - 1]) {
          nextTab = availableTabs[currentTabIndex - 1];
        } else {
          nextTab = availableTabs[availableTabs.length - 1];
        }
      } else if (action === KEYBOARD_ACTION.next) {
        if (availableTabs[currentTabIndex + 1]) {
          nextTab = availableTabs[currentTabIndex + 1];
        } else {
          nextTab = availableTabs[0];
        }
      }

      if (nextTab) {
        // Focus the tab
        nextTab.focus(); // Optionally activate the tab

        if (activateOnFocus) {
          nextTab.click();
        }
      } // Prevent default page scroll when in vertical orientation


      if ((0, _utils.isVertical)(orientation)) {
        event.preventDefault();
      }
    }
  });
  return /*#__PURE__*/React.createElement(Tab, _extends({
    "data-baseweb": "tab",
    key: key,
    id: (0, _utils.getTabId)(uid, key),
    role: "tab",
    onKeyDown: handleKeyDown,
    "aria-selected": isActive,
    "aria-controls": (0, _utils.getTabPanelId)(uid, key),
    tabIndex: isActive ? '0' : '-1',
    ref: isActive ? activeTabRef : ref,
    disabled: !isActive && disabled,
    type: "button" // so it doesn't trigger a submit when used inside forms
    ,
    $focusVisible: focusVisible,
    $isActive: isActive
  }, sharedStylingProps, restProps, TabProps, {
    onClick: function onClick(event) {
      if (typeof onChange === 'function') onChange({
        activeKey: key
      });
      if (typeof _onClick === 'function') _onClick(event);
    },
    onFocus: (0, _focusVisible.forkFocus)(_objectSpread(_objectSpread({}, restProps), TabProps), handleFocus),
    onBlur: (0, _focusVisible.forkBlur)(_objectSpread(_objectSpread({}, restProps), TabProps), handleBlur)
  }), Artwork ? /*#__PURE__*/React.createElement(ArtworkContainer, _extends({
    "data-baseweb": "artwork-container"
  }, sharedStylingProps, ArtworkContainerProps), /*#__PURE__*/React.createElement(Artwork, {
    size: 20,
    color: "contentPrimary"
  })) : null, title ? title : key);
}

function InternalTabPanel(_ref3) {
  var childKey = _ref3.childKey,
      childIndex = _ref3.childIndex,
      activeKey = _ref3.activeKey,
      uid = _ref3.uid,
      sharedStylingProps = _ref3.sharedStylingProps,
      renderAll = _ref3.renderAll,
      props = _objectWithoutProperties(_ref3, _excluded3);

  var key = childKey || String(childIndex);
  var isActive = key == activeKey;
  var _props$overrides2 = props.overrides,
      overrides = _props$overrides2 === void 0 ? {} : _props$overrides2,
      children = props.children;
  var TabPanelOverrides = overrides.TabPanel;

  var _getOverrides13 = (0, _overrides.getOverrides)(TabPanelOverrides, _styledComponents.StyledTabPanel),
      _getOverrides14 = _slicedToArray(_getOverrides13, 2),
      TabPanel = _getOverrides14[0],
      TabPanelProps = _getOverrides14[1];

  return /*#__PURE__*/React.createElement(TabPanel, _extends({
    "data-baseweb": "tab-panel",
    key: key,
    role: "tabpanel",
    id: (0, _utils.getTabPanelId)(uid, key),
    "aria-labelledby": (0, _utils.getTabId)(uid, key),
    hidden: !isActive
  }, sharedStylingProps, TabPanelProps), isActive || renderAll ? children : null);
}