react-cm-ui
Version:
React UI for Healthy Church
287 lines (257 loc) • 8.39 kB
JSX
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;