import React, { Component, ReactElement, ReactNode } from 'react' import styled from 'styled-components' import { flexFlow } from '@monorail/helpers/exports' import { HorizontalNavigationItemController } from '@monorail/metaComponents/horizontalNavigation/HorizontalNavigationItemController' import { isNil } from '@monorail/sharedHelpers/typeGuards' import { TabProps } from '@monorail/visualComponents/tabs/Tab' // TODO(unsafe-any): Fix unsafe anys // tslint:disable no-unsafe-any const TabBarActions = styled.div` ${flexFlow('row')}; align-items: center; margin-left: auto; margin-right: 0; ` export type TabBarIndicatorProps = { left: number width: number duration: number } type TabBarProps = { getActiveTabIndex?: (activeTabIndex: number) => void actions?: ReactNode tabBarIndicator: (props: TabBarIndicatorProps) => ReactNode activeTabIndex?: number } type TabBarState = { activeTabIndex: number indicatorLeft: number indicatorTransitionDuration: number indicatorWidth: number } export class HorizontalNavigationController extends Component< TabBarProps, TabBarState > { state: TabBarState = { activeTabIndex: this.props.activeTabIndex || 0, indicatorLeft: 0, indicatorTransitionDuration: 0, indicatorWidth: 0, } componentDidUpdate(prevProps: TabBarProps, prevState: TabBarState) { // Check if the indicator needs to move, if it does set the distance of the move as the transition duration. if (prevState.indicatorLeft !== this.state.indicatorLeft) { this.setState(() => ({ indicatorTransitionDuration: Math.abs( prevState.indicatorLeft - this.state.indicatorLeft, ), })) } // Check if the activeTabIndex needs to change if (prevProps.activeTabIndex !== this.props.activeTabIndex) { this.setState(() => ({ activeTabIndex: this.props.activeTabIndex || 0, })) } } setIndicator = (width: number, left: number) => this.setState(() => ({ indicatorWidth: width, indicatorLeft: left, })) updateActiveTab = (index: number) => { const { getActiveTabIndex } = this.props this.setState(() => ({ activeTabIndex: index })) getActiveTabIndex && getActiveTabIndex(index) } renderTabs() { const { children } = this.props const { activeTabIndex } = this.state /** * If we're controlling the activeTabIndex with a prop, * then we set updateIsActive to undefined to prevent * automatic updates on Tab click. */ return React.Children.map( children as ReactElement, (child: ReactElement, index: number) => !isNil(child) && React.isValidElement(child) && ( React.cloneElement(child, props)} /> ), ) } render() { const { actions, tabBarIndicator } = this.props const { indicatorLeft, indicatorWidth, indicatorTransitionDuration, } = this.state return ( <> {this.renderTabs()} {!isNil(actions) && ( {actions} )} {tabBarIndicator({ width: indicatorWidth, left: indicatorLeft, duration: indicatorTransitionDuration, })} ) } } // tslint:enable