import React from 'react' ; import classnames from 'classnames' ; import TabScroller from '@material/react-tab-scroller' ; import Tab , { TabProps } from '@material/react-tab' ; import { MDCTabBarFoundation } from '@material/tab-bar/foundation' ; import { MDCTabBarAdapter } from '@material/tab-bar/adapter' ; export interface TabBarProps extends React . HTMLAttributes < HTMLDivElement > { indexInView ? : number ; activeIndex : number ; handleActiveIndexUpdate ? : ( index : number ) => void ; onActivated ? : ( index : number ) => void ; className ? : string ; isRtl ? : boolean ; children : React . ReactElement < TabProps > | React . ReactElement < TabProps > [ ] ; } interface TabBarState { previousActiveIndex : number ; } class TabBar extends React . Component < TabBarProps , TabBarState > { tabBarRef : React . RefObject < HTMLDivElement > = React . createRef ( ) ; tabScrollerRef : React . RefObject < TabScroller > = React . createRef ( ) ; tabList : Tab [ ] = [ ] ; foundation ! : MDCTabBarFoundation ; constructor ( props ) { super ( props ) ; this . state = { previousActiveIndex : props . activeIndex , } ; } static defaultProps : Partial < TabBarProps > = { indexInView : 0 , activeIndex : 0 , handleActiveIndexUpdate : ( ) => { } , className : '' , children : [ ] , isRtl : false , } ; componentDidMount ( ) { this . foundation = new MDCTabBarFoundation ( this . adapter ) ; this . foundation . init ( ) ; const { activeIndex , indexInView } = this . props ; if ( this . tabList [ activeIndex ] ) { const defaultDOMRect = { bottom : 0 , height : 0 , left : 0 , right : 0 , top : 0 , width : 0 , x : 0 , y : 0 , } ; this . tabList [ activeIndex ] . activate ( defaultDOMRect ) ; } this . foundation . scrollIntoView ( indexInView ! ) ; } componentDidUpdate ( prevProps ) { if ( this . props . activeIndex !== prevProps . activeIndex ) { this . setState ( { previousActiveIndex : prevProps . activeIndex } , ( ) => this . foundation . activateTab ( this . props . activeIndex ) ) ; } if ( this . props . indexInView !== prevProps . indexInView ) { this . foundation . scrollIntoView ( this . props . indexInView ! ) ; } } componentWillUnmount ( ) { this . foundation . destroy ( ) ; } get classes ( ) { return classnames ( 'mdc-tab-bar' , this . props . className ) ; } get adapter ( ) : MDCTabBarAdapter { return { scrollTo : ( scrollX ) => { this . tabScrollerRef . current && this . tabScrollerRef . current . scrollTo ( scrollX ) ; } , incrementScroll : ( scrollXIncrement ) => { this . tabScrollerRef . current && this . tabScrollerRef . current . incrementScroll ( scrollXIncrement ) ; } , getScrollPosition : ( ) => { if ( ! this . tabScrollerRef . current ) return 0 ; return this . tabScrollerRef . current . getScrollPosition ( ) ; } , getScrollContentWidth : ( ) => { if ( ! this . tabScrollerRef . current ) return 0 ; return this . tabScrollerRef . current . getScrollContentWidth ( ) ; } , getOffsetWidth : ( ) => { if ( this . tabBarRef . current === null ) return 0 ; return this . tabBarRef . current . offsetWidth ; } , isRTL : ( ) => ! ! this . props . isRtl , setActiveTab : ( index ) => { if ( ! this . props . handleActiveIndexUpdate ) return ; this . props . handleActiveIndexUpdate ( index ) ; } , activateTabAtIndex : ( index , clientRect ) => this . tabList [ index ] . activate ( clientRect ) , deactivateTabAtIndex : ( index ) => this . tabList [ index ] . deactivate ( ) , focusTabAtIndex : ( index ) => this . tabList [ index ] . focus ( ) , getTabIndicatorClientRectAtIndex : ( index ) => this . tabList [ index ] . computeIndicatorClientRect ( ) , getTabDimensionsAtIndex : ( index ) => this . tabList [ index ] . computeDimensions ( ) , getPreviousActiveTabIndex : ( ) => this . state . previousActiveIndex , getFocusedTabIndex : ( ) => { const activeElement = document . activeElement ; for ( let i = 0 ; i < this . tabList . length ; i ++ ) { if ( this . tabList [ i ] . tabRef . current === activeElement ) { return i ; } } return - 1 ; } , getIndexOfTabById : ( id ) => this . tabList . map ( ( tab ) => tab . props . id ) . indexOf ( id ) , getTabListLength : ( ) => this . tabList . length , notifyTabActivated : ( index ) => this . props . onActivated && this . props . onActivated ( index ) , } ; } pushToTabList = ( el ) => { this . tabList . push ( el ) ; } ; onKeyDown = ( e ) => { e . persist ( ) ; this . setState ( { previousActiveIndex : this . props . activeIndex } , ( ) => this . foundation . handleKeyDown ( e . nativeEvent ) ) ; if ( this . props . onKeyDown ) { this . props . onKeyDown ( e ) ; } } ; onClickTab = ( e , index , onClick ? ) => { this . setState ( { previousActiveIndex : this . props . activeIndex } , ( ) => this . adapter . setActiveTab ( index ) ) ; if ( onClick ) { onClick ( e ) ; } } ; render ( ) { const { className , indexInView , activeIndex , handleActiveIndexUpdate , onKeyDown , isRtl , children , ... otherProps } = this . props ; return ( < div dir = { isRtl ? 'rtl' : 'ltr' } className = { this . classes } role = 'tablist' onKeyDown = { this . onKeyDown } ref = { this . tabBarRef } { ... otherProps } > < TabScroller ref = { this . tabScrollerRef } > { React . Children . map ( children as React . ReactElement < TabProps > [ ] , this . renderTab ) } < /TabScroller> < /div>); } renderTab = ( tab , index ) => { const { children , onClick , ... otherProps } = tab . props ; const props = { onClick : ( e ) => this . onClickTab ( e , index , onClick ) , ref : this . pushToTabList , ... otherProps , } as TabProps ; return React . cloneElement ( tab , props , children ) ; } ; } export { Tab } ; export default TabBar ;