Home Reference Source

src/components/snippets/Typography/Base.jsx

import React, {
  createElement,
  Fragment,
  createContext,
  Component,
} from 'react';
import { propTypes, defaultProps } from './propTypes';
import { CopyToClipboard } from 'react-copy-to-clipboard';
import { keys, pick, flowRight, values, pickBy } from 'lodash';
import { delay, wrapTags, ICON_STYLE } from './utils';
import ContentEditable from './ContentEditable';

const PropsEnd = createContext();

const EndTag = () => (
  <PropsEnd.Consumer>
    {({ text, children }) => <Fragment>{text || children}</Fragment>}
  </PropsEnd.Consumer>
);

const MainTag = ({ tag, color, ...props }) =>
  createElement(tag, {
    className: color && `text-${color} disabled`,
    ...props,
  });

/**
 * Базовый компонент для наследования остальных компонентов
 * @reactProps {boolean} code - отображать в виде кода
 * @reactProps {boolean} mark - марк
 * @reactProps {boolean} strong - отображать в виде жирным
 * @reactProps {boolean} underline - нижнее подчеркивание
 * @reactProps {boolean} small - отображать маленьким
 * @reactProps {string} text - значение
 * @reactProps {node} children - внутреннее содержимое компонента
 * @reactProps {function} onChange - callback на изменение
 * @reactProps {string} color - цвет
 * @reactProps {string} editable - редактируемое поле
 * @reactProps {string} copyable - возможность копировать
 */
class Base extends Component {
  constructor(props) {
    super(props);
    this.state = {
      copied: false,
      edit: false,
    };

    this.copyLinkClick = this.copyLinkClick.bind(this);
    this.editLinkClick = this.editLinkClick.bind(this);
    this.editableTagOnBlur = this.editableTagOnBlur.bind(this);
    this.handleContentEditable = this.handleContentEditable.bind(this);
  }

  async copyLinkClick(e) {
    e.preventDefault();
    this.setState({ copied: true });
    await delay(3000);
    this.setState({ copied: false });
  }

  editLinkClick(e) {
    e.preventDefault();
    this.setState({ edit: true });
  }

  editableTagOnBlur() {
    this.setState({ edit: false });
  }

  handleContentEditable(e) {
    const { onChange } = this.props;
    onChange(e.currentTarget.textContent);
  }

  render() {
    const {
      tag,
      text,
      children,
      color,
      copyable,
      editable,
      ...rest
    } = this.props;

    const { copied, edit } = this.state;

    const wrapperObj = pick(wrapTags, keys(pickBy(rest)));

    const wrapperFns = values(wrapperObj);
    const Wrappers = flowRight(wrapperFns)(EndTag);

    const copyIcon = !copied ? 'fa fa-files-o' : 'fa fa-check';

    const copiableFragment = (
      <span style={ICON_STYLE}>
        <CopyToClipboard text={text}>
          <a href="#" className="pl-2" onClick={this.copyLinkClick}>
            <i className={copyIcon} />
          </a>
        </CopyToClipboard>
      </span>
    );

    const editableFragment = edit ? null : (
      <span style={ICON_STYLE}>
        <a href="#" className="pl-2" onClick={this.editLinkClick}>
          <i className="fa fa-pencil" />
        </a>
      </span>
    );

    return (
      <MainTag tag={tag} onBlur={this.editableTagOnBlur} color={color}>
        <ContentEditable editable={edit} onChange={this.handleContentEditable}>
          <PropsEnd.Provider value={{ text, children }}>
            <Wrappers />
          </PropsEnd.Provider>
        </ContentEditable>
        {copyable && copiableFragment}
        {editable && editableFragment}
      </MainTag>
    );
  }
}

Base.propTypes = propTypes;

Base.defaultProps = defaultProps;

export default Base;