UNPKG

react-ui-icheck

Version:

iCheck customizable checkboxes and radios built with React.

298 lines (257 loc) 6.72 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; const iCheck = 'iCheck'; const iCheckHelper = `${iCheck}-helper`; class Input extends Component { constructor(props) { super(props); this.handleBlur = this.handleBlur.bind(this); this.handleChange = this.handleChange.bind(this); this.handleFocus = this.handleFocus.bind(this); this.handleHelperClick = this.handleHelperClick.bind(this); const { checked, hovered, indeterminate } = props; this.state = { active: false, focused: false, prevPropChecked: props.checked, checked, hovered, indeterminate, }; } static getDerivedStateFromProps(props, state) { if (state.prevPropChecked !== props.checked) { return { checked: props.checked, prevPropChecked: props.checked, }; } if (props.hovered !== state.hovered) { return { hovered: props.hovered, }; } return null; } handleBlur(event) { const { onBlur } = this.props; this.setState({ focused: false }); if (onBlur) onBlur(event); } handleChange(event) { const { onChange } = this.props; const { checked } = event.target; this.setState((state) => ({ checked: !state.checked })); if (onChange) onChange(event, checked); } handleFocus(event) { const { onFocus } = this.props; this.setState({ focused: true }); if (onFocus) onFocus(event); } handleHelperClick(event) { const { disabled, inputType, insert, label, onChange } = this.props; const { checked } = this.state; if ((!label && !insert) || disabled) return; if (inputType.toString() === 'checkbox') this.setState((state) => ({ checked: !state.checked })); event.preventDefault(); event.stopPropagation(); if (onChange) { // make sure <ins /> element is not target event.target = this.checkbox; // eslint-disable-line no-param-reassign onChange(event, checked); } } render() { const { activeClassName, aria, checkboxClassName, checkedClassName, className, disabled, disabledClassName, focusClassName, hoverClassName, id, increaseArea, indeterminateClassName, inheritClassName, inheritID, inputType, insert, isMobile, name, radioClassName, value, hovered, indeterminate, ...other } = this.props; const { active, checked, focused, hovered: stateHovered, indeterminate: stateIndeterminate, } = this.state; const wrapProps = { className: classNames({ [checkboxClassName]: inputType.toString() === 'checkbox', [radioClassName]: inputType.toString() === 'radio', [indeterminateClassName]: stateIndeterminate, [checkedClassName]: checked, [disabledClassName]: disabled, [hoverClassName]: stateHovered, [focusClassName]: focused, [activeClassName]: active, }), }; if (inheritClassName) wrapProps.className = classNames(wrapProps.className, className); if (inheritID && id) wrapProps.id = `${iCheck}-${id}`; if (aria) { wrapProps.role = inputType; // Set ARIA "labelledby" wrapProps['aria-labelledby'] = wrapProps.id; wrapProps['aria-checked'] = checked; } // Clickable area let area = `${increaseArea}`.replace('%', '') || 0; if (area < -50) area = -50; // Layer styles const offset = `${-area}%`; const size = `${100 + area * 2}%`; const layer = { position: 'absolute', top: offset, left: offset, display: 'block', width: size, height: size, margin: 0, padding: 0, background: '#fff', border: 0, opacity: 0, cursor: disabled ? 'default' : 'pointer', }; // Layer addition const helper = ( <ins className={iCheckHelper} role="presentation" style={layer} onClick={this.handleHelperClick} /> ); // Choose how to hide input let hide; if (isMobile) { hide = { position: 'absolute', visibility: 'hidden', }; } else if (area) { hide = layer; } else { hide = { opacity: 0, position: 'absolute', }; } const inputElement = ( <input {...other} checked={checked} disabled={disabled} name={name} ref={(c) => { this.checkbox = c; }} style={hide} type={inputType} value={value} onBlur={this.handleBlur} onChange={this.handleChange} onFocus={this.handleFocus} /> ); let insertElement = null; if (insert) { insertElement = React.isValidElement(insert) ? ( insert ) : ( // eslint-disable-next-line react/no-danger <div dangerouslySetInnerHTML={{ __html: insert }} /> ); } return ( <div {...wrapProps}> {inputElement} {insertElement} {helper} </div> ); } } Input.propTypes = { inputType: PropTypes.string.isRequired, activeClassName: PropTypes.string, aria: PropTypes.bool, checkboxClassName: PropTypes.string, checked: PropTypes.bool, checkedClassName: PropTypes.string, className: PropTypes.string, disabled: PropTypes.bool, disabledClassName: PropTypes.string, focusClassName: PropTypes.string, hoverClassName: PropTypes.string, hovered: PropTypes.bool, id: PropTypes.string, increaseArea: PropTypes.string, indeterminate: PropTypes.bool, indeterminateClassName: PropTypes.string, inheritClassName: PropTypes.bool, inheritID: PropTypes.bool, insert: PropTypes.node, isMobile: PropTypes.bool, label: PropTypes.string, name: PropTypes.string, radioClassName: PropTypes.string, value: PropTypes.string, onBlur: PropTypes.func, onChange: PropTypes.func, onFocus: PropTypes.func, }; Input.defaultProps = { activeClassName: null, aria: false, checkboxClassName: 'icheckbox', checked: false, checkedClassName: 'checked', className: null, disabled: false, disabledClassName: 'disabled', focusClassName: 'focus', hoverClassName: 'hover', hovered: false, id: null, increaseArea: '', indeterminate: false, indeterminateClassName: 'indeterminate', inheritClassName: false, inheritID: false, insert: null, isMobile: false, label: null, name: null, radioClassName: 'iradio', value: '', onBlur: null, onChange: null, onFocus: null, }; export default Input;