UNPKG

react-cm-ui

Version:
287 lines (257 loc) 8.39 kB
import { isFunction, } from 'lodash'; import React from 'react'; import ClassNames from 'classnames'; import PropTypes from 'prop-types'; import Icon from '../../dataDisplay/icon'; const propTypes = { align: PropTypes.oneOf(['left', 'right']), checked: PropTypes.bool, className: PropTypes.string, /** * Deprecated prop. Please use `disabled` instead. */ disable: PropTypes.bool, /** * A Checkbox can be disabled. */ disabled: PropTypes.bool, fluid: PropTypes.bool, /** * Forwarded Ref */ forwardedRef: PropTypes.oneOfType([ // Either a function PropTypes.func, // Or the instance of a DOM native element (see the note about SSR) PropTypes.shape({ current: PropTypes.instanceOf(Element) }), ]), id: PropTypes.string, inverse: PropTypes.bool, label: PropTypes.oneOfType([ PropTypes.shape({}), PropTypes.string, ]), labelClassName: PropTypes.string, labelClick: PropTypes.bool, labelStyle: PropTypes.shape({}), labelWeight: PropTypes.oneOf(['bold', 'normal', 'semibold']), name: PropTypes.string, onChange: PropTypes.func, size: PropTypes.oneOf(['small', 'large']), style: PropTypes.shape({}), tabIndex: PropTypes.number, toggle: PropTypes.bool, value: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), }; const defaultProps = { align: null, checked: false, className: null, disable: false, disabled: false, fluid: false, forwardedRef: undefined, id: null, inverse: null, label: null, labelClassName: null, labelClick: null, labelStyle: null, labelWeight: null, name: null, onChange: null, size: null, style: null, tabIndex: -1, toggle: null, value: null, }; class Checkbox extends React.Component { constructor(props) { super(props); this.state = { isChecked: props.checked || false }; this.onClick = this.onClick.bind(this); this.onKeyDown = this.onKeyDown.bind(this); this.onLabelClick = this.onLabelClick.bind(this); this.onLabelKeyDown = this.onLabelKeyDown.bind(this); this.checkbox = props.forwardedRef ?? React.createRef(); } componentDidMount() { const { checked } = this.props; if (checked) { // isChecked is already set by the props.checked in the constructor. // eslint-disable-next-line no-underscore-dangle this.checkbox.current.checked = checked; } } componentDidUpdate(prevProps) { const { checked, onChange, } = this.props; if (isFunction(onChange) && prevProps.checked !== checked) { this.setState({ isChecked: checked, }, () => { // eslint-disable-next-line no-underscore-dangle this.checkbox.current.checked = checked; }); } } onClick(event) { const { disable, disabled, id, onChange, } = this.props; const { isChecked } = this.state; const isDisabled = disabled || disable; if (!isDisabled) { if (isFunction(onChange)) { onChange(id, !isChecked, event); } else { this.setState({ isChecked: !isChecked, }, () => { this.checkbox.current.checked = !isChecked; }); } } else { event.stopPropagation(); } } onKeyDown() { /** * NOTE: Need to use a prop function here someday. */ } onLabelClick(event) { const { labelClick } = this.props; if (isFunction(labelClick) && labelClick === false) { event.stopPropagation(); } } onLabelKeyDown() { /** * NOTE: Need to use a prop function here someday */ } render() { const { align, className, disable, disabled, fluid, id, inverse, label, labelClassName, labelClick, labelStyle, labelWeight, name, size, style, tabIndex, toggle, value, } = this.props; const { isChecked } = this.state; const isDisabled = disable || disabled; const newValue = value || ''; const containerClasses = ClassNames('ui', 'checkbox', className, { 'checkbox-align-left': align === 'left', 'checkbox-align-right': align === 'right', 'checkbox-disabled': isDisabled, 'checkbox-full-width': fluid, 'checkbox-inverse': inverse, 'checkbox-is-checked': isChecked, 'checkbox-size-small': size === 'small', 'checkbox-toggle': toggle, }); const labelClasses = ClassNames('checkbox-label', { 'label-not-clickable': isFunction(labelClick) && labelClick === false, }); const labelTextClasses = ClassNames('checkbox-label-text', labelClassName, { 'checkbox-label-text-weight-bold': labelWeight === 'bold', 'checkbox-label-text-weight-normal': !labelWeight || labelWeight === 'normal', 'checkbox-label-text-weight-semibold': labelWeight === 'semibold', }); const checkSize = size === 'small' ? 8 : 10; const inputId = id ? `${id}_hidden_input` : null; return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions <div className={containerClasses} id={id} onClick={this.onClick} onKeyDown={this.onKeyDown} style={style} tabIndex={tabIndex} > <input checked={isChecked} className="input" disabled={isDisabled} id={inputId} name={name} readOnly // eslint-disable-next-line no-underscore-dangle ref={this.checkbox} type="checkbox" value={newValue} /> {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} <label className={labelClasses} > {label && ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions <span className={labelTextClasses} onClick={this.onLabelClick} onKeyDown={this.onLabelKeyDown} style={labelStyle} > {label} </span> )} {toggle ? ( <div className="checkbox-toggle-text"> <span className="checkbox-toggle-text-on">On</span> <span className="checkbox-toggle-text-off">Off</span> </div> ) : null} <Icon color={isDisabled ? 'static' : 'primary'} compact inverse size={checkSize} type="check" /> </label> </div> ); } } Checkbox.propTypes = propTypes; Checkbox.defaultProps = defaultProps; const CheckboxWrapper = React.forwardRef((props, ref) => (( // eslint-disable-next-line react/jsx-props-no-spreading <Checkbox {...props} forwardedRef={ref} /> ))); const wrapperPropTypes = { ...propTypes }; delete wrapperPropTypes.forwardedRef; const wrapperDefaultProps = { ...defaultProps }; delete wrapperDefaultProps.forwardedRef; CheckboxWrapper.displayName = 'Checkbox'; CheckboxWrapper.propTypes = wrapperPropTypes; CheckboxWrapper.defaultProps = wrapperDefaultProps; export default CheckboxWrapper;