'use strict';

var web = require('@react-spring/web');
var clsx = require('clsx');
var clamp = require('lodash.clamp');
var React = require('react');
require('../common/theme.js');
var direction = require('../common/direction.js');
require('../common/propsValues/control.js');
require('../common/propsValues/breakpoint.js');
require('../common/propsValues/size.js');
require('../common/propsValues/typography.js');
var width = require('../common/propsValues/width.js');
require('../common/propsValues/type.js');
require('../common/propsValues/dateMode.js');
require('../common/propsValues/monthFormat.js');
require('../common/propsValues/position.js');
require('../common/propsValues/layouts.js');
require('../common/propsValues/status.js');
require('../common/propsValues/sentiment.js');
require('../common/propsValues/profileType.js');
require('../common/propsValues/variant.js');
require('../common/propsValues/scroll.js');
require('../common/propsValues/markdownNodeType.js');
require('../common/fileType.js');
require('@transferwise/icons');
require('react-intl');
require('../common/closeButton/CloseButton.messages.js');
var jsxRuntime = require('react/jsx-runtime');
var DirectionProvider = require('../provider/direction/DirectionProvider.js');
var Tab = require('./Tab.js');
var TabList = require('./TabList.js');
var TabPanel = require('./TabPanel.js');
var utils = require('./utils.js');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var clamp__default = /*#__PURE__*/_interopDefault(clamp);

const MIN_INDEX = 0;
const SpacerWidth = {
  default: 0,
  xs: 8,
  sm: 16,
  md: 24,
  lg: 32
};
class Tabs extends React.Component {
  static defaultProps = {
    changeTabOnSwipe: true,
    transitionSpacing: 'default',
    headerWidth: width.Width.BLOCK
  };
  containerReference = /*#__PURE__*/React.createRef();
  constructor(props) {
    super(props);
    this.state = {
      start: null,
      translateX: 0,
      translateFrom: 0,
      translateTo: 0,
      translateLineX: null,
      isAnimating: false,
      isSwiping: false,
      isScrolling: false,
      lastSwipeVelocity: 0,
      fullWidthTabs: props.headerWidth === width.Width.BLOCK,
      currentSwipe: [],
      selectedTabIndex: props.selected
    };
  }
  container = null;
  containerWidth = 0;
  tabRefs = [];
  get filteredTabsLength() {
    return this.props.tabs.filter(tab => !tab.disabled).length;
  }
  get MAX_INDEX() {
    return this.props.tabs.length - 1;
  }
  componentDidMount() {
    const {
      selected
    } = this.props;
    this.setTabWidth();
    this.switchTab(clamp__default.default(selected, MIN_INDEX, this.MAX_INDEX));
    this.animateToTab(clamp__default.default(selected, MIN_INDEX, this.MAX_INDEX), true);
    document.body.addEventListener('touchmove', this.disableScroll, {
      passive: false
    });
    document.body.addEventListener('touchforcechange', this.disableScroll, {
      passive: false
    });
    window.addEventListener('resize', this.handleResize);
  }
  componentDidUpdate(previousProps, previousState) {
    const currentSelected = this.props.selected;
    const previousSelected = previousProps.selected;
    const currentSelectedTab = this.props.tabs[currentSelected];
    const currentSelectedTabIsDisabled = currentSelectedTab?.disabled;
    const previousSelectedTab = previousProps.tabs[previousSelected];
    const previousSelectedTabIsDisabled = previousSelectedTab?.disabled;
    const currentTabsLength = this.props.tabs.length;
    const previousTabsLength = previousProps.tabs.length;
    const currentDisabledTabsLength = this.props.tabs.filter(tab => !tab.disabled).length;
    const previousDisabledTabsLength = previousProps.tabs.filter(tab => !tab.disabled).length;
    const currentHeaderWidth = this.props.headerWidth;
    const previousFullHeaderWidth = previousProps.headerWidth;
    const {
      animatePanelsOnClick
    } = this.props;
    const instantOnClick = !animatePanelsOnClick && !previousState.isSwiping;
    if (currentHeaderWidth !== previousFullHeaderWidth || currentTabsLength !== previousTabsLength) {
      this.setTabWidth();
    }
    if (currentSelected !== previousSelected || currentDisabledTabsLength !== previousDisabledTabsLength || currentSelectedTabIsDisabled !== previousSelectedTabIsDisabled) {
      this.animateToTab(clamp__default.default(currentSelected, MIN_INDEX, this.MAX_INDEX), currentSelected === previousSelected || instantOnClick);
    }
  }
  componentWillUnmount() {
    document.body.removeEventListener('touchmove', this.disableScroll);
    document.body.removeEventListener('touchforcechange', this.disableScroll);
    window.removeEventListener('resize', this.handleResize);
  }
  handleResize = () => {
    this.setContainerWidth(this.container);
    this.setState(({
      selectedTabIndex
    }) => ({
      translateTo: -(this.containerWidth * selectedTabIndex)
    }));
  };
  setContainerRefAndWidth = node => {
    this.container = node;
    this.setContainerWidth(node);
  };
  setContainerWidth = node => {
    if (!node) {
      return;
    }
    const {
      width
    } = node.getBoundingClientRect();
    this.containerWidth = width;
  };
  isTabDisabled = index => {
    const {
      tabs
    } = this.props;
    return tabs[index]?.disabled ?? false;
  };
  getAllTabsWidth = () => {
    return this.tabRefs.map(reference => {
      return reference ? reference.getBoundingClientRect().width : 0;
    }).reduce((a, b) => a + b, 0);
  };
  getDistanceToSelectedTab = selectedTabIndex => {
    return this.tabRefs.filter((_, idx) => idx < selectedTabIndex).map(reference => reference ? reference.getBoundingClientRect().width : 0).reduce((a, b) => a + b, 0);
  };
  setTabWidth = () => {
    const {
      fullWidthTabs
    } = this.state;
    const {
      headerWidth,
      selected
    } = this.props;
    const allTabsWidth = this.getAllTabsWidth();
    if (!fullWidthTabs && (headerWidth === width.Width.BLOCK || this.containerWidth < allTabsWidth)) {
      this.setState({
        fullWidthTabs: true,
        translateLineX: `${selected * 100}%`
      });
    }
    if (fullWidthTabs && headerWidth === width.Width.AUTO && this.containerWidth >= allTabsWidth) {
      this.setState({
        fullWidthTabs: false,
        translateLineX: `${this.getDistanceToSelectedTab(selected)}px`
      });
    }
  };
  getTabLineWidth = () => {
    const {
      fullWidthTabs
    } = this.state;
    const {
      selected,
      tabs
    } = this.props;
    if (fullWidthTabs) {
      return `${1 / tabs.length * 100}%`;
    }
    const reference = this.tabRefs[selected] || this.tabRefs[this.tabRefs.length - 1];
    const width = reference ? reference.getBoundingClientRect().width : 0;
    return `${width}px`;
  };
  /*
   * Gets the next tab that should be selected based on the swipe direction
   * and the current selected tab (is called recursively to account for disabled tabs).
   */
  getTabToSelect = (selected, start, end) => {
    let nextSelected = selected;
    if (utils.swipedLeftToRight(start, end)) {
      nextSelected -= 1;
      if (nextSelected > MIN_INDEX && this.isTabDisabled(nextSelected)) {
        return this.getTabToSelect(nextSelected, start, end);
      }
    } else if (utils.swipedRightToLeft(start, end)) {
      nextSelected += 1;
      if (nextSelected < this.MAX_INDEX && this.isTabDisabled(nextSelected)) {
        return this.getTabToSelect(nextSelected, start, end);
      }
    }
    nextSelected = clamp__default.default(nextSelected, Math.max(selected - 1, MIN_INDEX), Math.min(selected + 1, this.MAX_INDEX));
    if (this.isTabDisabled(nextSelected)) {
      return selected;
    }
    return nextSelected;
  };
  swipedOverHalfOfContainer = difference => difference / this.containerWidth >= 0.5;
  calculateApplicableDragDifference = ({
    currentSelected,
    nextSelected,
    start,
    end
  }) => {
    const difference = utils.getSwipeDifference(start, end);
    const elasticDrag = utils.getElasticDragDifference(difference);
    if (utils.swipedLeftToRight(start, end)) {
      if (currentSelected > MIN_INDEX && currentSelected !== nextSelected) {
        return Math.min(difference, this.containerWidth);
      }
      return elasticDrag;
    }
    if (utils.swipedRightToLeft(start, end)) {
      if (currentSelected < this.MAX_INDEX && currentSelected !== nextSelected) {
        return -Math.min(difference, this.containerWidth);
      }
      return -elasticDrag;
    }
    return false;
  };
  switchTab = index => {
    const {
      onTabSelect
    } = this.props;
    onTabSelect(index);
  };
  getTabIndexWithoutDisabledTabs(index) {
    return index - this.props.tabs.slice(0, index).filter(tab => tab.disabled).length;
  }
  animateToTab = (index, instant) => {
    this.animateLine(index);
    this.animatePanel(this.getTabIndexWithoutDisabledTabs(index), instant);
  };
  animateLine = index => {
    this.setState(previousState => ({
      translateLineX: previousState.fullWidthTabs ? `${index * 100}%` : `${this.getDistanceToSelectedTab(index)}px`
    }));
  };
  // Pass `instant` to set the `translateX` to the new panel with no transition
  animatePanel = (index, instant = false) => {
    const {
      translateTo: currentTranslateTo
    } = this.state;
    const translateFrom = currentTranslateTo;
    const translateTo = -(this.containerWidth * index);
    this.setState({
      selectedTabIndex: index,
      isAnimating: !instant && translateFrom !== translateTo,
      translateFrom,
      translateTo
    });
  };
  disableScroll = event => {
    const {
      isSwiping
    } = this.state;
    if (isSwiping) {
      event.preventDefault();
    }
  };
  handleTabClick = index => () => {
    this.switchTab(index);
  };
  onKeyDown = index => event => {
    if (event && event.key === 'Enter') {
      this.switchTab(index);
    }
  };
  handleTouchStart = event => {
    const start = {
      x: event.nativeEvent.touches[0].clientX,
      y: event.nativeEvent.touches[0].clientY,
      time: Date.now()
    };
    this.setState({
      start,
      currentSwipe: [start]
    });
    event.persist();
  };
  handleTouchMove = event => {
    const {
      start
    } = this.state;
    if (start == null) {
      return;
    }
    const {
      selected: currentSelectedFromProps
    } = this.props;
    const selected = this.getTabIndexWithoutDisabledTabs(currentSelectedFromProps);
    const end = {
      x: event.nativeEvent.changedTouches[0].clientX,
      y: event.nativeEvent.changedTouches[0].clientY,
      time: Date.now()
    };
    const difference = utils.getSwipeDifference(start, end);
    const yAxisDifference = utils.getSwipeDifference(start, end, 'y');
    let {
      isScrolling,
      isSwiping
    } = this.state;
    event.persist();
    if (!isScrolling && !isSwiping) {
      if (difference > yAxisDifference) {
        isSwiping = true;
      } else {
        isScrolling = true;
      }
    }
    this.setState({
      isScrolling,
      isSwiping
    });
    if (isSwiping) {
      const nextSelected = this.getTabToSelect(currentSelectedFromProps, start, end);
      this.animateLine(this.swipedOverHalfOfContainer(difference) ? nextSelected : currentSelectedFromProps);
      const dragDifference = this.calculateApplicableDragDifference({
        currentSelected: currentSelectedFromProps,
        nextSelected,
        start,
        end
      });
      const translateX = dragDifference ? -(this.containerWidth * selected) + dragDifference : false;
      this.setState(state => ({
        currentSwipe: [...state.currentSwipe, end],
        translateFrom: translateX || state.translateFrom,
        translateTo: translateX || state.translateTo
      }));
    }
  };
  handleTouchEnd = event => {
    const {
      start,
      isSwiping
    } = this.state;
    if (start == null) {
      return;
    }
    const {
      selected
    } = this.props;
    const end = {
      x: event.nativeEvent.changedTouches[0].clientX,
      y: event.nativeEvent.changedTouches[0].clientY,
      time: Date.now()
    };
    const difference = utils.getSwipeDifference(start, end);
    const velocity = utils.getVelocity([...this.state.currentSwipe, end]);
    this.setState({
      currentSwipe: []
    });
    let nextSelected = selected;
    event.persist();
    if (isSwiping) {
      if (utils.swipeShouldChangeTab(start, end) || this.swipedOverHalfOfContainer(difference)) {
        nextSelected = this.getTabToSelect(nextSelected, start, end);
      }
      if (nextSelected !== selected) {
        this.switchTab(nextSelected);
        this.setState({
          lastSwipeVelocity: velocity
        });
      } else {
        this.animateToTab(nextSelected);
      }
    }
    this.setState({
      isSwiping: false,
      isScrolling: false
    });
  };
  render() {
    const {
      tabs,
      changeTabOnSwipe,
      name,
      selected,
      className,
      transitionSpacing,
      headerWidth
    } = this.props;
    const {
      isSwiping,
      translateLineX,
      isAnimating,
      translateFrom,
      translateTo,
      lastSwipeVelocity,
      fullWidthTabs
    } = this.state;
    const spacer = SpacerWidth[transitionSpacing];
    const tabsLength = this.filteredTabsLength;
    const distanceSwiped = Math.abs(-translateFrom - this.containerWidth * selected);
    const remainingContainerToTravel = isSwiping ? 1 - distanceSwiped / this.containerWidth : 1 - Math.abs(distanceSwiped / this.containerWidth - 1);
    const restrictedVelocity = (Number.isNaN(remainingContainerToTravel) ? 0 : remainingContainerToTravel) * Math.min(10 * Math.E, lastSwipeVelocity * 10 * Math.E);
    const hidePanelOverflow = isAnimating || isSwiping;
    const sliderWidth = tabsLength * this.containerWidth + spacer * 2;
    // Uses `props.panelTransitionSpacing` to add a spacer in-between the `TabPanel` you're transitioning to/from
    const Spacer = ({
      id
    }) => spacer > 0 && /*#__PURE__*/jsxRuntime.jsx("div", {
      style: {
        width: spacer,
        display: hidePanelOverflow ? 'block' : 'none'
      }
    }, id);
    return /*#__PURE__*/jsxRuntime.jsx(DirectionProvider.DirectionContext.Consumer, {
      children: direction$1 => {
        const isRTL = direction$1 === direction.Direction.RTL;
        return /*#__PURE__*/jsxRuntime.jsxs("div", {
          ref: this.containerReference,
          className: clsx.clsx('tabs', className, {
            'tabs--auto-width': headerWidth === width.Width.AUTO
          }),
          onTouchStart: changeTabOnSwipe ? this.handleTouchStart : undefined,
          onTouchEnd: changeTabOnSwipe ? this.handleTouchEnd : undefined,
          onTouchMove: changeTabOnSwipe ? this.handleTouchMove : undefined,
          children: [/*#__PURE__*/jsxRuntime.jsxs(TabList, {
            children: [tabs.map(({
              title,
              disabled
            }, index) => {
              return /*#__PURE__*/jsxRuntime.jsx(Tab, {
                ref: node => {
                  this.tabRefs[index] = node;
                },
                id: `${name}-tab-${index}`,
                panelId: `${name}-panel-${index}`,
                selected: selected === index,
                disabled: disabled,
                focusTab: () => {
                  if (this.containerReference.current?.contains(document.activeElement)) {
                    this.tabRefs[index]?.focus();
                  }
                },
                onClick: disabled ? undefined : this.handleTabClick(index),
                onKeyDown: this.onKeyDown(index),
                ...(fullWidthTabs ? {
                  style: {
                    width: `${1 / tabs.length * 100}%`
                  }
                } : {}),
                children: title
              }, title);
            }), translateLineX ? /*#__PURE__*/jsxRuntime.jsx("li", {
              role: "none",
              className: "tabs__line",
              style: {
                width: this.getTabLineWidth(),
                transform: isRTL ? `translateX(-${translateLineX})` : `translateX(${translateLineX})`
              }
            }) : null]
          }), /*#__PURE__*/jsxRuntime.jsx("div", {
            ref: this.setContainerRefAndWidth,
            className: "tabs__panel-container",
            style: {
              overflow: hidePanelOverflow ? 'hidden' : 'visible'
            },
            children: /*#__PURE__*/jsxRuntime.jsx(web.Spring, {
              from: {
                transform: `translateX(${translateFrom - spacer}px)`
              },
              to: {
                transform: `translateX(${translateTo - spacer}px)`
              },
              config: {
                precision: isSwiping ? 1 : 0.01,
                velocity: !isSwiping ? restrictedVelocity : 0,
                clamp: true
              },
              onRest: () => {
                if (isAnimating) {
                  this.setState({
                    isAnimating: false,
                    lastSwipeVelocity: 0
                  });
                }
              },
              children: props => /*#__PURE__*/jsxRuntime.jsx(web.animated.div, {
                className: "tabs__slider",
                style: {
                  width: hidePanelOverflow ? `${sliderWidth}px` : '100%',
                  transform: hidePanelOverflow ? props.transform : 'translateX(0px)'
                },
                children: tabs.map(({
                  content,
                  disabled
                }, index) => !disabled ? /*#__PURE__*/jsxRuntime.jsxs(React.Fragment, {
                  children: [index === selected && /*#__PURE__*/jsxRuntime.jsx(Spacer, {
                    id: "left-spacer"
                  }), /*#__PURE__*/jsxRuntime.jsx(TabPanel, {
                    tabId: `${name}-tab-${index}`,
                    id: `${name}-panel-${index}`,
                    style: {
                      width: hidePanelOverflow ? `${this.containerWidth}px` : '100%',
                      display: hidePanelOverflow || index === selected ? 'block' : 'none'
                    },
                    children: content
                  }, tabs[index].title), index === selected && /*#__PURE__*/jsxRuntime.jsx(Spacer, {
                    id: "right-spacer"
                  })]
                }, `${tabs[index].title}-fragment`) : null)
              })
            })
          })]
        });
      }
    });
  }
}

module.exports = Tabs;
//# sourceMappingURL=Tabs.js.map
