import React from 'react' ; import classnames from 'classnames' ; import { MDCSlidingTabIndicatorFoundation } from '@material/tab-indicator/sliding-foundation' ; import { MDCFadingTabIndicatorFoundation } from '@material/tab-indicator/fading-foundation' ; import { MDCTabIndicatorAdapter } from '@material/tab-indicator/adapter' ; export interface TabIndicatorProps extends React . HTMLAttributes < HTMLSpanElement > { active ? : boolean ; className ? : string ; fade ? : boolean ; icon ? : boolean ; previousIndicatorClientRect ? : ClientRect ; } export default class TabIndicator extends React . Component < TabIndicatorProps , { } > { private tabIndicatorElement : React . RefObject < HTMLSpanElement > = React . createRef ( ) ; foundation ! : MDCFadingTabIndicatorFoundation | MDCSlidingTabIndicatorFoundation ; static defaultProps : Partial < TabIndicatorProps > = { active : false , className : '' , fade : false , icon : false , } ; componentDidMount ( ) { if ( this . props . fade ) { this . foundation = new MDCFadingTabIndicatorFoundation ( this . adapter ) ; } else { this . foundation = new MDCSlidingTabIndicatorFoundation ( this . adapter ) ; } this . foundation . init ( ) ; if ( this . props . active ) { this . foundation . activate ( ) ; } } componentWillUnmount ( ) { this . foundation . destroy ( ) ; } componentDidUpdate ( prevProps ) { if ( this . props . active !== prevProps . active ) { if ( this . props . active ) { this . foundation . activate ( this . props . previousIndicatorClientRect ) ; } else { this . foundation . deactivate ( ) ; } } } get classes ( ) { const { className , fade } = this . props ; return classnames ( 'mdc-tab-indicator' , className , { 'mdc-tab-indicator--fade' : fade , } ) ; } get contentClasses ( ) { const { icon } = this . props ; return classnames ( 'mdc-tab-indicator__content' , { 'mdc-tab-indicator__content--icon' : icon , 'mdc-tab-indicator__content--underline' : ! icon , } ) ; } get adapter ( ) : MDCTabIndicatorAdapter { return { addClass : ( className ) => { if ( ! this . tabIndicatorElement . current ) return ; this . tabIndicatorElement . current . classList . add ( className ) ; this . forceUpdate ( ) ; } , removeClass : ( className ) => { if ( ! this . tabIndicatorElement . current ) return ; this . tabIndicatorElement . current . classList . remove ( className ) ; this . forceUpdate ( ) ; } , computeContentClientRect : this . computeContentClientRect , setContentStyleProperty : ( prop , value ) => { const contentElement = this . getNativeContentElement ( ) as HTMLElement ; const typedProp = prop as keyof CSSStyleDeclaration ; if ( ! contentElement || typedProp === 'length' || typedProp === 'parentRule' ) { return ; } contentElement . style [ typedProp ] = value ; } , } ; } private getNativeContentElement = ( ) => { if ( ! this . tabIndicatorElement . current ) return ; return this . tabIndicatorElement . current . getElementsByClassName ( 'mdc-tab-indicator__content' ) [ 0 ] ; } ; computeContentClientRect = ( ) => { const contentElement = this . getNativeContentElement ( ) ; if ( ! ( contentElement && contentElement . getBoundingClientRect ) ) { const defaultDOMRect = { bottom : 0 , height : 0 , left : 0 , right : 0 , top : 0 , width : 0 , x : 0 , y : 0 , } ; return defaultDOMRect ; } return contentElement . getBoundingClientRect ( ) ; } ; render ( ) { const { active , children , className , fade , icon , previousIndicatorClientRect , ... otherProps } = this . props ; return ( < span className = { this . classes } ref = { this . tabIndicatorElement } { ... otherProps } > { this . renderContent ( ) } < /span>); } addContentClassesToChildren = ( ) => { const child = React . Children . only ( this . props . children ) as React . ReactElement < any > ; const className = classnames ( child . props . className , this . contentClasses ) ; const props = Object . assign ( { } , child . props , { className } ) ; return React . cloneElement ( child , props ) ; } ; renderContent ( ) { if ( this . props . children ) { return this . addContentClassesToChildren ( ) ; } return < span className = { this . contentClasses } />; } }