Home Reference Source

src/components/controls/CheckboxGroup/CheckboxGroup.jsx

import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import { xorBy, some, isNull } from 'lodash';

/**
 * Компонент - группа чекбоксов, содержит {@link Checkbox} как children
 * @reactProps {array} value - выбранное значение
 * @reactProps {function} onChange - вызывается при изменении значения
 * @reactProps {boolean} disabled - флаг блокировки
 * @reactProps {boolean} visible - флаг видимости
 * @reactProps {boolean} inline - флаг вывода в ряд
 * @reactProps {object} style - стили группы
 * @reactProps {string} className - класс группы
 * @reactProps {node} children - элемент потомок копонента CheckboxGroup
 * @example
 * <CheckboxGroup value={ ['apple', 'orange'] } onChange={(val)=> console.log(val)}>
 * <Checkbox value="apple" compileLabel='Apple' />
 * <Checkbox value="orange" compileLabel='Orange'/>
 * <Checkbox value="watermelon" compileLabel='Watermelon'/>
 * </CheckboxGroup>
 **/

class CheckboxGroup extends React.Component {
  constructor(props) {
    super(props);

    this._onChange = this._onChange.bind(this);
    this._onBlur = this._onBlur.bind(this);
  }

  /**
   * Обработчик изменения чекбокса
   * @param e - событие
   * @private
   */

  _onChange(e) {
    const { onChange, value, valueFieldId } = this.props;
    const { value: newValue } = e.target;
    onChange(xorBy(value, [newValue], valueFieldId));
  }

  _onBlur(e) {
    const { onBlur, value } = this.props;
    onBlur(value);
  }

  _isIncludes(collection, object, key) {
    return some(collection, item => item[key] == object[key]);
  }

  /**
   * Рендер
   */

  render() {
    const {
      children,
      visible,
      inline,
      style,
      className,
      value,
      valueFieldId,
    } = this.props;
    const element = child => {
      return React.cloneElement(child, {
        checked:
          !isNull(value) &&
          value &&
          this._isIncludes(value, child.props.value, valueFieldId),
        disabled: this.props.disabled || child.props.disabled,
        onChange: this._onChange,
        onBlur: this._onBlur,
        onFocus: this.props.onFocus,
        inline: this.props.inline,
      });
    };

    const isCheckboxChild = child => {
      const checkboxTypes = ['Checkbox', 'CheckboxN2O', 'CheckboxButton'];
      return child.type && checkboxTypes.includes(child.type.displayName);
    };

    const isBtn =
      children &&
      React.Children.map(children, child => child.type.displayName).includes(
        'CheckboxButton'
      );

    return (
      <React.Fragment>
        {visible !== false && (
          <div
            className={cx(className, {
              [`btn-group${inline ? '' : '-vertical'}`]: isBtn,
              'btn-group-toggle': isBtn,
              'n2o-checkbox-inline': inline,
            })}
            style={style}
          >
            {children &&
              React.Children.map(children, child => {
                if (isCheckboxChild(child)) {
                  return element(child);
                }
              })}
          </div>
        )}
      </React.Fragment>
    );
  }
}

CheckboxGroup.propTypes = {
  value: PropTypes.any,
  onChange: PropTypes.func,
  disabled: PropTypes.bool,
  visible: PropTypes.bool,
  children: PropTypes.node.isRequired,
  inline: PropTypes.bool,
  style: PropTypes.object,
  className: PropTypes.string,
};

CheckboxGroup.defaultProps = {
  value: [],
  visible: true,
  onChange: () => {},
};

export default CheckboxGroup;