"use strict";

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

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

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

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

var _index = require("../styles/index.js");

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

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

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

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

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

function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (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 _extends() { _extends = Object.assign || 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 ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (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 = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { 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) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }

function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }

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

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }

function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_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 debounce = function debounce(fn, wait) {
  var timeout;
  return function () {
    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    clearTimeout(timeout);
    timeout = setTimeout(function () {
      fn.apply(void 0, args);
    }, wait);
  };
};

var getHighlightLayoutParams = function getHighlightLayoutParams(el, orientation) {
  // 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
    };
  }
};

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;
  // Create unique id prefix for this tabs component
  var uid = (0, _reactUid.useUID)(); // 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]; // Update highlight on key and orientation changes.


  React.useEffect(function () {
    if (activeTabRef.current) {
      setHighlightLayout(getHighlightLayoutParams(activeTabRef.current, orientation));
    }
  }, [activeKey, orientation]); // Create a shared, memoized, debounced callback for tabs to call on resize.

  var updateHighlight = React.useCallback(debounce(function () {
    if (activeTabRef.current) {
      setHighlightLayout(getHighlightLayoutParams(activeTabRef.current, orientation));
    }
  }, 100), [activeKey, 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) ? activeTabRef.current.parentNode.scrollWidth > activeTabRef.current.parentNode.clientWidth : activeTabRef.current.parentNode.scrollHeight > activeTabRef.current.parentNode.clientHeight) {
        if (keyUpdated > 1) {
          activeTabRef.current.scrollIntoView({
            behavior: 'smooth',
            block: 'nearest',
            inline: 'nearest'
          });
        } else {
          activeTabRef.current.scrollIntoView({
            block: 'center',
            inline: 'center'
          });
        }
      }
    }
  }, [activeKey]); // 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, _index.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 React.createElement(Root, _extends({}, sharedStylingProps, RootProps), 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;
    var key = child.key || index;
    var isActive = key == activeKey;

    var _child$props = child.props,
        Artwork = _child$props.artwork,
        _child$props$override = _child$props.overrides,
        overrides = _child$props$override === void 0 ? {} : _child$props$override,
        tabRef = _child$props.tabRef,
        _onClick = _child$props.onClick,
        restProps = _objectWithoutProperties(_child$props, ["artwork", "overrides", "tabRef", "onClick"]); // 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;
    });
    React.useEffect(function () {
      if (window.ResizeObserver) {
        // We need to update the active tab highlight when the width or
        // placement changes so we listen for resize updates in each tab.
        var observer = new window.ResizeObserver(updateHighlight);
        observer.observe(isActive ? activeTabRef.current : ref.current);
        return function () {
          observer.disconnect();
        };
      }
    }, [activeKey, orientation]); // 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

    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 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
    }, 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({}, restProps, {}, TabProps), handleFocus),
      onBlur: (0, _focusVisible.forkBlur)(_objectSpread({}, restProps, {}, TabProps), handleBlur)
    }), Artwork ? React.createElement(ArtworkContainer, _extends({
      "data-baseweb": "artwork-container"
    }, sharedStylingProps, ArtworkContainerProps), React.createElement(Artwork, {
      size: 20,
      color: "contentPrimary"
    })) : null, child.props.title ? child.props.title : key);
  }), 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))), 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;
    var key = child.key || index;
    var isActive = key == activeKey;
    var _child$props2 = child.props,
        _child$props2$overrid = _child$props2.overrides,
        overrides = _child$props2$overrid === void 0 ? {} : _child$props2$overrid,
        children = _child$props2.children;
    var TabPanelOverrides = overrides.TabPanel;

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


    var _React$useState7 = React.useState(false),
        _React$useState8 = _slicedToArray(_React$useState7, 2),
        focusVisible = _React$useState8[0],
        setFocusVisible = _React$useState8[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]);
    return 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),
      tabIndex: isActive ? '0' : null,
      "aria-expanded": isActive,
      hidden: !isActive,
      $focusVisible: focusVisible
    }, sharedStylingProps, TabPanelProps, {
      onFocus: (0, _focusVisible.forkFocus)(TabPanelProps, handleFocus),
      onBlur: (0, _focusVisible.forkBlur)(TabPanelProps, handleBlur)
    }), isActive || renderAll ? children : null);
  }));
}