import React from 'react' ; import classnames from 'classnames' ; import { MDCCheckboxFoundation } from '@material/checkbox/foundation' ; import { MDCCheckboxAdapter } from '@material/checkbox/adapter' ; import { cssClasses } from '@material/checkbox/constants' ; import { withRipple , InjectedProps , RippledComponentProps , } from '@material/react-ripple' ; import NativeControl from './NativeControl' ; export interface CheckboxProps extends InjectedProps < HTMLDivElement , HTMLInputElement > { checked ? : boolean ; className ? : string ; disabled ? : boolean ; indeterminate ? : boolean ; name ? : string ; nativeControlId ? : string ; onChange ? : ( evt : React . ChangeEvent < HTMLInputElement > ) => void ; initRipple : ( surface : HTMLDivElement , activator ? : HTMLInputElement ) => void ; children ? : React . ReactNode ; unbounded : boolean ; } interface CheckboxState { checked ? : boolean ; indeterminate ? : boolean ; classList : Set < string > ; 'aria-checked' : string ; disabled : boolean ; } export class Checkbox extends React . Component < CheckboxProps , CheckboxState > { inputElement : React . RefObject < HTMLInputElement > = React . createRef ( ) ; foundation ! : MDCCheckboxFoundation ; constructor ( props ) { super ( props ) ; this . state = { checked : props . checked , indeterminate : props . indeterminate , classList : new Set ( ) , 'aria-checked' : 'false' , disabled : props . disabled ! , } ; } static defaultProps : Partial < CheckboxProps > = { checked : false , className : '' , disabled : false , indeterminate : false , onChange : ( ) => { } , unbounded : true , } ; componentDidMount ( ) { this . foundation = new MDCCheckboxFoundation ( this . adapter ) ; this . foundation . init ( ) ; this . foundation . setDisabled ( this . props . disabled ! ) ; if ( this . inputElement . current ) { this . inputElement . current . indeterminate = this . state . indeterminate ! ; } } componentDidUpdate ( prevProps ) { const { checked , indeterminate , disabled } = this . props ; if ( checked !== prevProps . checked || indeterminate !== prevProps . indeterminate ) { this . handleChange ( checked ! , indeterminate ! ) ; } if ( disabled !== prevProps . disabled ) { this . foundation . setDisabled ( disabled ! ) ; } } componentWillUnmount ( ) { if ( this . foundation ) { this . foundation . destroy ( ) ; } } init = ( el ) => { if ( ! this . inputElement . current ) return ; this . props . initRipple ( el , this . inputElement . current ) ; } ; handleChange = ( checked , indeterminate ) => { this . setState ( { checked , indeterminate } , ( ) => { this . foundation . handleChange ( ) ; if ( this . inputElement . current ) { this . inputElement . current . indeterminate = indeterminate ; } } ) ; } ; get classes ( ) : string { const { classList } = this . state ; const { className } = this . props ; return classnames ( 'mdc-checkbox' , Array . from ( classList ) , this . state . disabled ? cssClasses . DISABLED : null , className ) ; } updateState = ( key , value ) => { this . setState ( ( prevState ) => ( { ... prevState , [ key ] : value , } ) ) ; } ; removeState = ( key ) => this . updateState ( key , false ) ; get adapter ( ) : MDCCheckboxAdapter { return { addClass : ( className ) => { const { classList } = this . state ; classList . add ( className ) ; this . setState ( { classList } ) ; } , removeClass : ( className ) => { const { classList } = this . state ; classList . delete ( className ) ; this . setState ( { classList } ) ; } , hasNativeControl : ( ) => true , isAttachedToDOM : ( ) => true , isChecked : ( ) => this . state . checked ! , isIndeterminate : ( ) => this . state . indeterminate ! , setNativeControlAttr : this . updateState , setNativeControlDisabled : ( disabled ) => { this . updateState ( 'disabled' , disabled ) ; } , removeNativeControlAttr : this . removeState , forceLayout : ( ) => null , } ; } onChange = ( evt ) => { const { onChange } = this . props ; const { checked , indeterminate } = evt . target ; this . handleChange ( checked , indeterminate ) ; onChange ! ( evt ) ; } ; render ( ) { const { className , checked , indeterminate , initRipple , onChange , unbounded , disabled , nativeControlId , name , ... otherProps } = this . props ; return ( < div className = { this . classes } onAnimationEnd = { ( ) => this . foundation . handleAnimationEnd ( ) } ref = { this . init } { ... otherProps } > < NativeControl id = { nativeControlId } checked = { this . state . checked } disabled = { this . state . disabled } aria - checked = { ( this . state [ 'aria-checked' ] || this . state . checked ! . toString ( ) ) as ( 'true' | 'false' ) } name = { name } onChange = { this . onChange } rippleActivatorRef = { this . inputElement } / > < div className = 'mdc-checkbox__background' > < svg className = 'mdc-checkbox__checkmark' viewBox = '0 0 24 24' focusable = 'false' > < path className = 'mdc-checkbox__checkmark-path' fill = 'none' d = 'M1.73,12.91 8.1,19.28 22.79,4.59' / > < /svg> < div className = 'mdc-checkbox__mixedmark' / > < /div> < /div>); } } export default withRipple < CheckboxProps , HTMLDivElement , HTMLInputElement > ( Checkbox ) ;