import React from 'react' ; import classnames from 'classnames' ; import { MDCFloatingLabelFoundation } from '@material/floating-label/foundation' ; import { MDCFloatingLabelAdapter } from '@material/floating-label/adapter' ; import { cssClasses } from '@material/floating-label/constants' ; export interface FloatingLabelProps extends React . LabelHTMLAttributes < HTMLLabelElement > { className ? : string ; handleWidthChange ? : ( width : number ) => void ; float ? : boolean ; } interface FloatingLabelState { classList : Set < string > ; } export default class FloatingLabel extends React . Component < FloatingLabelProps , FloatingLabelState > { foundation ! : MDCFloatingLabelFoundation ; labelElement : React . RefObject < HTMLLabelElement > = React . createRef ( ) ; static defaultProps : Partial < FloatingLabelProps > = { className : '' , float : false , } ; state : FloatingLabelState = { classList : new Set ( ) , } ; componentDidMount ( ) { this . initializeFoundation ( ) ; this . handleWidthChange ( ) ; if ( this . props . float ) { this . foundation . float ( true ) ; } } componentWillUnmount ( ) { this . foundation . destroy ( ) ; } componentDidUpdate ( prevProps ) { if ( this . props . children !== prevProps . children ) { this . handleWidthChange ( ) ; } if ( this . props . float !== prevProps . float ) { this . foundation . float ( this . props . float ! ) ; } } initializeFoundation = ( ) => { this . foundation = new MDCFloatingLabelFoundation ( this . adapter ) ; this . foundation . init ( ) ; } ; get classes ( ) { const { classList } = this . state ; const { className } = this . props ; return classnames ( cssClasses . ROOT , Array . from ( classList ) , className ) ; } get adapter ( ) : MDCFloatingLabelAdapter { return { addClass : ( className ) => this . setState ( { classList : this . state . classList . add ( className ) } ) , removeClass : this . removeClassFromClassList , registerInteractionHandler : ( ) => undefined , deregisterInteractionHandler : ( ) => undefined , getWidth : ( ) => 0 , } ; } shake = ( ) => { this . foundation . shake ( true ) ; } ; removeClassFromClassList = ( className ) => { const { classList } = this . state ; classList . delete ( className ) ; this . setState ( { classList } ) ; } ; handleWidthChange = ( ) => { if ( this . props . handleWidthChange && this . labelElement . current ) { this . props . handleWidthChange ( this . labelElement . current . offsetWidth ) ; } } ; onShakeEnd = ( ) => { this . removeClassFromClassList ( cssClasses . LABEL_SHAKE ) ; } ; render ( ) { const { className , children , handleWidthChange , float , ... otherProps } = this . props ; return ( < label className = { this . classes } ref = { this . labelElement } onAnimationEnd = { this . onShakeEnd } { ... otherProps } > { children } < /label>); } }