import { clsx } from 'clsx';
import clamp from 'lodash.clamp';
import { Component, createRef } from 'react';
import '../common/theme.mjs';
import { Direction } from '../common/direction.mjs';
import '@transferwise/neptune-tokens';
import '../common/propsValues/control.mjs';
import '../common/propsValues/size.mjs';
import '../common/propsValues/typography.mjs';
import { Width } from '../common/propsValues/width.mjs';
import '../common/propsValues/type.mjs';
import '../common/propsValues/dateMode.mjs';
import '../common/propsValues/monthFormat.mjs';
import '../common/propsValues/position.mjs';
import '../common/propsValues/layouts.mjs';
import '../common/propsValues/status.mjs';
import '../common/propsValues/sentiment.mjs';
import '../common/propsValues/profileType.mjs';
import '../common/propsValues/variant.mjs';
import '../common/propsValues/scroll.mjs';
import '../common/propsValues/markdownNodeType.mjs';
import '../common/fileType.mjs';
import '@transferwise/formatting';
import '@transferwise/icons';
import 'react-intl';
import '../common/closeButton/CloseButton.messages.mjs';
import { jsx, jsxs } from 'react/jsx-runtime';
import { DirectionContext } from '../provider/direction/DirectionProvider.mjs';
import Tab from './Tab.mjs';
import TabList from './TabList.mjs';
import TabPanel from './TabPanel.mjs';
import { swipedLeftToRight, swipedRightToLeft, getSwipeDifference, swipeShouldChangeTab } from './utils.mjs';

const MIN_INDEX = 0;
class Tabs extends Component {
  static defaultProps = {
    changeTabOnSwipe: true,
    headerWidth: Width.BLOCK
  };
  containerReference = /*#__PURE__*/createRef();
  constructor(props) {
    super(props);
    this.state = {
      start: null,
      translateLineX: null,
      isSwiping: false,
      isScrolling: false,
      fullWidthTabs: props.headerWidth === Width.BLOCK
    };
  }
  container = null;
  containerWidth = 0;
  tabRefs = [];
  get MAX_INDEX() {
    return this.props.tabs.length - 1;
  }
  componentDidMount() {
    const {
      selected
    } = this.props;
    this.setTabWidth();
    this.switchTab(clamp(selected, MIN_INDEX, this.MAX_INDEX));
    this.animateLine(clamp(selected, MIN_INDEX, this.MAX_INDEX));
    document.body.addEventListener('touchmove', this.disableScroll, {
      passive: false
    });
    document.body.addEventListener('touchforcechange', this.disableScroll, {
      passive: false
    });
    window.addEventListener('resize', this.handleResize);
  }
  componentDidUpdate(previousProps) {
    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;
    if (currentHeaderWidth !== previousFullHeaderWidth || currentTabsLength !== previousTabsLength) {
      this.setTabWidth();
    }
    if (currentSelected !== previousSelected || currentDisabledTabsLength !== previousDisabledTabsLength || currentSelectedTabIsDisabled !== previousSelectedTabIsDisabled) {
      this.animateLine(clamp(currentSelected, MIN_INDEX, this.MAX_INDEX));
    }
  }
  componentWillUnmount() {
    document.body.removeEventListener('touchmove', this.disableScroll);
    document.body.removeEventListener('touchforcechange', this.disableScroll);
    window.removeEventListener('resize', this.handleResize);
  }
  handleResize = () => {
    this.setContainerWidth(this.container);
  };
  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.BLOCK || this.containerWidth < allTabsWidth)) {
      this.setState({
        fullWidthTabs: true,
        translateLineX: `${selected * 100}%`
      });
    }
    if (fullWidthTabs && headerWidth === 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 (swipedLeftToRight(start, end)) {
      nextSelected -= 1;
      if (nextSelected > MIN_INDEX && this.isTabDisabled(nextSelected)) {
        return this.getTabToSelect(nextSelected, start, end);
      }
    } else if (swipedRightToLeft(start, end)) {
      nextSelected += 1;
      if (nextSelected < this.MAX_INDEX && this.isTabDisabled(nextSelected)) {
        return this.getTabToSelect(nextSelected, start, end);
      }
    }
    nextSelected = clamp(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;
  switchTab = index => {
    const {
      onTabSelect
    } = this.props;
    onTabSelect(index);
  };
  animateLine = index => {
    this.setState(previousState => ({
      translateLineX: previousState.fullWidthTabs ? `${index * 100}%` : `${this.getDistanceToSelectedTab(index)}px`
    }));
  };
  disableScroll = event => {
    const {
      isSwiping
    } = this.state;
    if (isSwiping) {
      event.preventDefault();
    }
  };
  handleTabClick = index => () => {
    this.switchTab(index);
  };
  onKeyDown = index => event => {
    if (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
    });
  };
  handleTouchMove = event => {
    const {
      start
    } = this.state;
    if (start == null) {
      return;
    }
    const {
      selected: currentSelectedFromProps
    } = this.props;
    const end = {
      x: event.nativeEvent.changedTouches[0].clientX,
      y: event.nativeEvent.changedTouches[0].clientY,
      time: Date.now()
    };
    const difference = getSwipeDifference(start, end);
    const yAxisDifference = getSwipeDifference(start, end, 'y');
    let {
      isScrolling,
      isSwiping
    } = this.state;
    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);
    }
  };
  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 = getSwipeDifference(start, end);
    let nextSelected = selected;
    if (isSwiping) {
      if (swipeShouldChangeTab(start, end) || this.swipedOverHalfOfContainer(difference)) {
        nextSelected = this.getTabToSelect(nextSelected, start, end);
      }
      if (nextSelected !== selected) {
        this.switchTab(nextSelected);
      } else {
        this.animateLine(nextSelected);
      }
    }
    this.setState({
      isSwiping: false,
      isScrolling: false
    });
  };
  render() {
    const {
      tabs,
      changeTabOnSwipe,
      name,
      selected,
      className,
      headerWidth
    } = this.props;
    const {
      translateLineX,
      fullWidthTabs
    } = this.state;
    const selectedTab = tabs[selected];
    return /*#__PURE__*/jsx(DirectionContext.Consumer, {
      children: direction => {
        const isRTL = direction === Direction.RTL;
        return /*#__PURE__*/jsxs("div", {
          ref: this.containerReference,
          className: clsx('tabs', className, {
            'tabs--auto-width': headerWidth === Width.AUTO
          }),
          onTouchStart: changeTabOnSwipe ? this.handleTouchStart : undefined,
          onTouchEnd: changeTabOnSwipe ? this.handleTouchEnd : undefined,
          onTouchMove: changeTabOnSwipe ? this.handleTouchMove : undefined,
          children: [/*#__PURE__*/jsxs(TabList, {
            children: [tabs.map(({
              title,
              disabled
            }, index) => {
              return /*#__PURE__*/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__*/jsx("li", {
              role: "none",
              className: "tabs__line",
              style: {
                width: this.getTabLineWidth(),
                transform: isRTL ? `translateX(-${translateLineX})` : `translateX(${translateLineX})`
              }
            }) : null]
          }), /*#__PURE__*/jsx("div", {
            ref: this.setContainerRefAndWidth,
            className: "tabs__panel-container",
            children: selectedTab && !selectedTab.disabled ? /*#__PURE__*/jsx(TabPanel, {
              tabId: `${name}-tab-${selected}`,
              id: `${name}-panel-${selected}`,
              style: {
                width: '100%'
              },
              children: selectedTab.content
            }, selectedTab.title) : null
          })]
        });
      }
    });
  }
}

export { Tabs as default };
//# sourceMappingURL=Tabs.mjs.map
