Home Reference Source

src/components/snippets/Alerts/Alert.jsx

import React from 'react';
import { defaultTo, isArray, map } from 'lodash';
import PropTypes from 'prop-types';
import InlineSpinner from '../Spinner/InlineSpinner';
import cx from 'classnames';
import Popover from '../Popover/Popover';

/**
 * Компонент сообщения-алерта
 * @reactProps {string} label - лейбл алерта
 * @reactProps {string} text - текст алерта
 * @reactProps {string} severity - тип алерта: 'info', 'danger', 'warning' или 'success'.
 * @reactProps {string} details - подробности, находятся под текстом, показываются (скрываются) по клику на выделенный текст
 * @reactProps {boolean} closeButton - отображать кнопку скрытия алерта или нет
 * @reactProps {boolean} onDismiss - выполняется при скрытии алерта
 * @reactProps {boolean} className - css-класс
 * @reactProps {number} style -  объект css стилей
 * @reactProps {number} icon - css-класс для иконки(иконка находится перед лейбелом )
 * @reactProps {boolean} visible - флаг видимости
 * @reactProps {string} position - настройка позиционирования
 * @reactProps {string} help - текст для Popover
 * @reactProps {boolean} animate - флаг включения анимации при появлении
 * @example
 * <Alert onDismiss={this.onDismiss}
 *             label='Сообщение'
 *             text={this.text} />
 */
class Alert extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      detailsVisible: false,
    };
    this.toggleDetails = this.toggleDetails.bind(this);
    this.renderAlert = this.renderAlert.bind(this);
    this.renderDefaultAlert = this.renderDefaultAlert.bind(this);
    this.renderLoaderAlert = this.renderLoaderAlert.bind(this);
  }

  /**
   * скрытие / показ деталей
   */
  toggleDetails() {
    this.setState({
      detailsVisible: !this.state.detailsVisible,
    });
  }

  renderAlert() {
    const { loader } = this.props;
    if (loader) {
      return this.renderLoaderAlert();
    } else {
      return this.renderDefaultAlert();
    }
  }

  renderLoaderAlert() {
    const { severity, className, style, text, animate } = this.props;

    return (
      <div
        className={cx('n2o-alert', 'n2o-alert-loader', 'alert', className, {
          [`alert-${severity}`]: severity,
          'n2o-alert--animated': animate,
        })}
        style={style}
      >
        <div className="n2o-alert-body-container">
          <InlineSpinner />
          {text || 'Загрузка...'}
        </div>
      </div>
    );
  }

  formatDetailes(details) {
    if (isArray(details)) {
      return map(details, d => (
        <React.Fragment>
          {d}
          <br />
        </React.Fragment>
      ));
    }

    return details;
  }

  renderDefaultAlert() {
    const {
      label,
      text,
      severity,
      closeButton,
      className,
      style,
      icon,
      details,
      onDismiss,
      help,
      animate,
    } = this.props;

    const { detailsVisible } = this.state;

    return (
      <div
        className={cx('n2o-alert', 'alert', className, {
          [`alert-${severity}`]: severity,
          'n2o-alert--animated': animate,
        })}
        style={style}
      >
        <div className="n2o-alert-help">{help && <Popover help={help} />}</div>
        <div className="n2o-alert-body-container">
          {label && (
            <div className="n2o-alert-header">
              {icon && <i className={icon} />}
              <h4>{label}</h4>
            </div>
          )}
          <div className={'n2o-alert-body'}>
            <div className="n2o-alert-body-text">{text}</div>
            {details && (
              <a
                className="alert-link details-label"
                onClick={this.toggleDetails}
              >
                {detailsVisible ? 'Скрыть' : 'Подробнее'}
              </a>
            )}
            {detailsVisible && (
              <div className="details">{this.formatDetailes(details)}</div>
            )}
          </div>
        </div>
        <div className="n2o-alert-close-container">
          {defaultTo(closeButton, true) && (
            <button className="close n2o-alert-close" onClick={onDismiss}>
              <span>×</span>
            </button>
          )}
        </div>
      </div>
    );
  }

  /**
   * Базовый рендер
   */
  render() {
    const { visible } = this.props;
    return visible !== false && this.renderAlert();
  }
}

Alert.defaultProps = {
  text: '',
  label: '',
  severity: 'info',
  details: '',
  closeButton: true,
  visible: true,
  onDismiss: () => {},
  position: 'relative',
  animate: false,
};

Alert.propTypes = {
  label: PropTypes.string,
  text: PropTypes.string,
  severity: PropTypes.oneOf([['info', 'danger', 'warning', 'success']]),
  details: PropTypes.string,
  closeButton: PropTypes.bool,
  onDismiss: PropTypes.func,
  className: PropTypes.string,
  style: PropTypes.object,
  icon: PropTypes.string,
  visible: PropTypes.bool,
  position: PropTypes.string,
  help: PropTypes.string,
  animate: PropTypes.bool,
};

export default Alert;