import React from 'react' ; import classnames from 'classnames' ; import { MDCTabScrollerFoundation } from '@material/tab-scroller/foundation' ; import { MDCTabScrollerAdapter } from '@material/tab-scroller/adapter' ; import { computeHorizontalScrollbarHeight } from '@material/tab-scroller/util' ; import { matches } from '@material/dom/ponyfill' ; const convertDashToCamelCase = ( propName ) => propName . replace ( /-(\w)/g , ( _ , v ) => v . toUpperCase ( ) ) ; export interface TabScrollerProps extends React . HTMLAttributes < HTMLDivElement > { alignStart ? : boolean ; alignEnd ? : boolean ; alignCenter ? : boolean ; className ? : string ; } interface TabScrollerState { classList : Set < string > ; areaClassList : Set < string > ; scrollAreaStyleProperty : React . CSSProperties ; scrollContentStyleProperty : React . CSSProperties ; } type ScrollerElementNames = 'scrollAreaStyleProperty' | 'scrollContentStyleProperty' ; export default class TabScroller extends React . Component < TabScrollerProps , TabScrollerState > { areaElement : React . RefObject < HTMLDivElement > = React . createRef ( ) ; contentElement : React . RefObject < HTMLDivElement > = React . createRef ( ) ; foundation ! : MDCTabScrollerFoundation ; state : TabScrollerState = { classList : new Set ( ) , areaClassList : new Set ( ) , scrollAreaStyleProperty : { } , scrollContentStyleProperty : { } , } ; static defaultProps : Partial < TabScrollerProps > = { alignStart : false , alignEnd : false , alignCenter : false , className : '' , children : null , onWheel : ( ) => { } , onTouchStart : ( ) => { } , onPointerDown : ( ) => { } , onMouseDown : ( ) => { } , onKeyDown : ( ) => { } , onTransitionEnd : ( ) => { } , } ; componentDidMount ( ) { this . foundation = new MDCTabScrollerFoundation ( this . adapter ) ; this . foundation . init ( ) ; } componentWillUnmount ( ) { this . foundation . destroy ( ) ; } get classes ( ) { const { alignStart , alignEnd , alignCenter , className } = this . props ; const { classList } = this . state ; return classnames ( 'mdc-tab-scroller' , Array . from ( classList ) , className , { 'mdc-tab-scroller--align-start' : alignStart , 'mdc-tab-scroller--align-end' : alignEnd , 'mdc-tab-scroller--align-center' : alignCenter , } ) ; } setStyleToElement = ( prop , value , elementStyleProperty ) => { const styleName = convertDashToCamelCase ( prop ) ; const updateElementStyleProperty = Object . assign ( { } , this . state [ elementStyleProperty ] , { [ styleName ] : value } ) ; this . setState ( ( prevState ) => { return Object . assign ( prevState , { [ elementStyleProperty ] : updateElementStyleProperty , } ) ; } ) ; } ; get adapter ( ) : MDCTabScrollerAdapter { return { eventTargetMatchesSelector : ( evtTarget , selector ) => { if ( selector ) { return matches ( evtTarget , selector ) ; } return false ; } , addClass : ( className ) => { const classList = new Set ( this . state . classList ) ; classList . add ( className ) ; this . setState ( { classList } ) ; } , removeClass : ( className ) => { const classList = new Set ( this . state . classList ) ; classList . delete ( className ) ; this . setState ( { classList } ) ; } , addScrollAreaClass : ( className ) => { const areaClassList = new Set ( this . state . areaClassList ) ; areaClassList . add ( className ) ; this . setState ( { areaClassList } ) ; } , setScrollAreaStyleProperty : ( prop , value ) => this . setStyleToElement ( prop , value , 'scrollAreaStyleProperty' ) , setScrollContentStyleProperty : ( prop , value ) => this . setStyleToElement ( prop , value , 'scrollContentStyleProperty' ) , getScrollContentStyleValue : ( propName ) => this . contentElement . current ? window . getComputedStyle ( this . contentElement . current ) . getPropertyValue ( propName ) : '' , setScrollAreaScrollLeft : ( scrollX ) => { if ( ! this . areaElement . current ) return ; this . areaElement . current . scrollLeft = scrollX ; } , getScrollAreaScrollLeft : ( ) => this . areaElement . current ? this . areaElement . current . scrollLeft : 0 , getScrollContentOffsetWidth : this . getScrollContentWidth , getScrollAreaOffsetWidth : ( ) => this . areaElement . current ? this . areaElement . current . offsetWidth : 0 , computeScrollAreaClientRect : ( ) => { return this . getBoundingClientRectOf ( this . contentElement . current ) ; } , computeScrollContentClientRect : ( ) => { return this . getBoundingClientRectOf ( this . contentElement . current ) ; } , computeHorizontalScrollbarHeight : ( ) => computeHorizontalScrollbarHeight ( document ) , } ; } getBoundingClientRectOf = ( element ) => { if ( ! element ) { const defaultDOMRect = { bottom : 0 , height : 0 , left : 0 , right : 0 , top : 0 , width : 0 , x : 0 , y : 0 , } ; return defaultDOMRect ; } return element . getBoundingClientRect ( ) ; } ; getScrollPosition = ( ) => { return this . foundation . getScrollPosition ( ) ; } ; getScrollContentWidth = ( ) => { return this . contentElement . current ? this . contentElement . current . offsetWidth : 0 ; } ; incrementScroll = ( scrollXIncrement ) => { this . foundation . incrementScroll ( scrollXIncrement ) ; } ; scrollTo = ( scrollX ) => { this . foundation . scrollTo ( scrollX ) ; } ; handleWheel_ = ( evt ) => { this . props . onWheel && this . props . onWheel ( evt ) ; this . foundation . handleInteraction ( ) ; } ; handleTouchStart_ = ( evt ) => { this . props . onTouchStart && this . props . onTouchStart ( evt ) ; this . foundation . handleInteraction ( ) ; } ; handlePointerDown_ = ( evt ) => { this . props . onPointerDown && this . props . onPointerDown ( evt ) ; this . foundation . handleInteraction ( ) ; } ; handleMouseDown_ = ( evt ) => { this . props . onMouseDown && this . props . onMouseDown ( evt ) ; this . foundation . handleInteraction ( ) ; } ; handleKeyDown_ = ( evt ) => { this . props . onKeyDown && this . props . onKeyDown ( evt ) ; this . foundation . handleInteraction ( ) ; } ; handleTransitionEnd_ = ( evt ) => { this . props . onTransitionEnd && this . props . onTransitionEnd ( evt ) ; this . foundation . handleTransitionEnd ( evt . nativeEvent ) ; } ; render ( ) { const { areaClassList , scrollAreaStyleProperty , scrollContentStyleProperty , } = this . state ; const { children , alignStart , alignEnd , alignCenter , className , onWheel , onTouchStart , onPointerDown , onMouseDown , onKeyDown , onTransitionEnd , ... otherProps } = this . props ; const areaClasses = classnames ( 'mdc-tab-scroller__scroll-area' , Array . from ( areaClassList ) ) ; return ( < div className = { this . classes } onWheel = { this . handleWheel_ } onTouchStart = { this . handleTouchStart_ } onPointerDown = { this . handlePointerDown_ } onMouseDown = { this . handleMouseDown_ } onKeyDown = { this . handleKeyDown_ } onTransitionEnd = { this . handleTransitionEnd_ } { ... otherProps } > < div className = { areaClasses } style = { scrollAreaStyleProperty } ref = { this . areaElement } > < div className = 'mdc-tab-scroller__scroll-content' style = { scrollContentStyleProperty } ref = { this . contentElement } > { children } < /div> < /div> < /div>); } }