Home Reference Source

src/components/controls/Output/OutputText.jsx

import React, { Fragment } from 'react';
import { isEqual, isNumber, isString } from 'lodash';
import PropTypes from 'prop-types';
import cx from 'classnames';
import Text from '../../snippets/Text/Text';
import Icon from '../../snippets/Icon/Icon';

const TypesComponents = {
  icon: ({ icon }) => <Icon className="icon" name={icon} />,
  text: ({ value, format, expandable, showFullText }) => (
    <div className="text">
      <Text text={value} format={format} />
      {expandable && (
        <a href="#" onClick={showFullText} className="details-label">
          Подробнее
        </a>
      )}
    </div>
  ),
  iconAndText: ({ icon, value, format, expandable, showFullText }) => (
    <Fragment>
      {icon && <Icon className="icon" name={icon} />}
      <div className="text">
        <Text text={value} format={format} />
        {expandable && (
          <a href="#" onClick={showFullText} className="details-label">
            Подробнее
          </a>
        )}
      </div>
    </Fragment>
  ),
};

/**
 * Компонент OutPutText
 * @reactProps {boolean} disabled
 * @reactProps {string} className
 * @reactProps {object} style
 * @reactProps {string} type
 * @reactProps {string} textPlace
 * @reactProps {string} icon
 * @reactProps {string} value
 * @reactProps {string} format
 */
class OutPutText extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      value: props.value,
      formattedValue: this.formatValue(props.value),
      isOpen: false,
    };

    this.setRenderComponentRef = this.setRenderComponentRef.bind(this);
    this.showFullText = this.showFullText.bind(this);
    this.formatValue = this.formatValue.bind(this);
  }

  componentDidUpdate(prevProps) {
    if (!isEqual(prevProps.value, this.props.value)) {
      this.setState({
        value: this.props.value,
        formattedValue: this.formatValue(this.props.value),
      });
    }
  }

  setRenderComponentRef(ref) {
    this.renderComponent = ref;
  }

  showFullText(e) {
    e.preventDefault();
    this.setState({ isOpen: !this.state.isOpen });
  }

  formatValue(value) {
    const { expandable } = this.props;
    if (isNumber(expandable) && isString(value)) {
      return value.substr(0, expandable - 3) + '...';
    } else {
      return value;
    }
  }

  render() {
    const {
      textPlace,
      type,
      className,
      style,
      ellipsis,
      expandable,
      ...rest
    } = this.props;
    const { isOpen, value, formattedValue } = this.state;
    const RenderComponent = TypesComponents[type];

    return (
      <div
        className={cx('n2o-output-text', className, textPlace, {
          'n2o-output-text--ellipsis':
            (ellipsis || expandable === true) && !isOpen,
          'n2o-output-text--expandable':
            (expandable && isOpen) || isNumber(expandable),
        })}
        ref={this.setRenderComponentRef}
        style={style}
      >
        <RenderComponent
          {...rest}
          value={isOpen ? value : formattedValue}
          expandable={expandable}
          showFullText={this.showFullText}
        />
      </div>
    );
  }
}

OutPutText.propTypes = {
  disabled: PropTypes.bool,
  className: PropTypes.string,
  style: PropTypes.object,
  type: PropTypes.oneOf(Object.keys(TypesComponents)),
  textPlace: PropTypes.oneOf(['right', 'left']),
  icon: PropTypes.string,
  value: PropTypes.string,
  format: PropTypes.string,
  ellipsis: PropTypes.bool,
  expandable: PropTypes.oneOf([PropTypes.bool, PropTypes.number]),
};

OutPutText.defaultProps = {
  disabled: true,
  className: 'n2o',
  style: {},
  type: 'iconAndText',
  textPlace: 'left',
  ellipsis: false,
  expandable: false,
};

export default OutPutText;