Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Modal from '../Modal';
import Button from '../../../controls/Button/Button';
import ButtonLabel from '../../../controls/Button/ButtonLabel';
import LoadingButton from '../../../controls/Button/LoadingButton';
import Icon from '../../../visuals/Icon/Icon';
import Close from '../../../visuals/Icon/svg/ic_close.svg';

const buttonShape = {
  onClick: PropTypes.func,
  label: PropTypes.string,
  href: PropTypes.string,
  disabled: PropTypes.bool,
  loading: PropTypes.bool,
  autoFocus: PropTypes.bool,
};

class StandardModal extends Component {
  static propTypes = {
    /** Modal contents displayed over underlay, no styling/wrapper provided */
    content: PropTypes.any.isRequired,
    /** A11y-enabled modal title */
    title: PropTypes.string.isRequired,
    /** Image to be used as part of the Modal Header, when this image is added, the close icon is not shown */
    Image: PropTypes.elementType,
    /** Control whether to show the modal: this allows internal entering/exiting logic for transitions */
    show: PropTypes.bool.isRequired,
    /** If provided, it is invoked for escape key or underlay click interactions. If omitted, the modal dialog does not respond to these interactions: you have to provide a way of closing it from the modal dialog content (an interaction that will toggle the `show` prop) . */
    onExit: PropTypes.func,
    /** If providedm it is invoked as soon as the modal is an entered/opened state */
    onEnter: PropTypes.func,
    /** The primary button of the modal */
    primaryAction: PropTypes.shape(buttonShape),
    /** The secondary button of the modal */
    secondaryAction: PropTypes.shape(buttonShape),
    /** The tertiary button of the modal. Will take precedence if both this and the hint are provided   */
    tertiaryAction: PropTypes.shape(buttonShape),
    /** A hint that will only be rendered if no tertiary button is provided    */
    hint: PropTypes.string,
    /** Allows the StandardModal to grow in width beyond the 640px limit */
    wide: PropTypes.bool,
  };
  static defaultProps = {
    Image: undefined,
    onExit: undefined,
    onEnter: undefined,
    primaryAction: undefined,
    secondaryAction: undefined,
    tertiaryAction: undefined,
    hint: undefined,
    wide: false,
  };

  constructor(props) {
    super(props);

    this.state = {
      verticalAlign: window.innerWidth < 640 ? 'bottom' : 'middle',
    };
  }

  componentDidMount() {
    window.addEventListener('resize', this.getScreenInnerWidth);
  }

  componentWillUnmount() {
    window.addEventListener('resize', this.getScreenInnerWidth);
  }

  onKeyPress = event => {
    const { onExit } = this.props;
    const enterPressed = event.key === 'Enter';
    if (enterPressed) onExit();
  };

  getScreenInnerWidth = () => {
    const isSmallScreen = window.innerWidth < 640;
    this.setState({
      verticalAlign: isSmallScreen ? 'bottom' : 'middle',
      verticalBottomDialogOffset: isSmallScreen ? '5vw' : null,
    });
  };

  renderFooter = () => {
    const { primaryAction, secondaryAction, tertiaryAction, hint } = this.props;

    const shouldRenderFooter = primaryAction || secondaryAction || tertiaryAction || hint;

    if (shouldRenderFooter) {
      return (
        <div className="StandardModal-footer">
          {tertiaryAction && (
            <Button
              autoFocus={!!tertiaryAction.autoFocus}
              onClick={tertiaryAction.onClick}
              href={tertiaryAction.href}
              variant="linkBlue"
              disabled={tertiaryAction.disabled}
              inputButtonClass={tertiaryAction.loading ? 'loading' : null}
            >
              <ButtonLabel>{tertiaryAction.label}</ButtonLabel>
              <LoadingButton />
            </Button>
          )}
          {!tertiaryAction && hint && <div className="StandardModal-hint tiny--soft">{hint}</div>}
          <div className="StandardModal-buttonWrapper">
            {secondaryAction && (
              <Button
                autoFocus={!!secondaryAction.autoFocus}
                onClick={secondaryAction.onClick}
                href={secondaryAction.href}
                variant="whiteWithBorder"
                disabled={secondaryAction.disabled}
                inputButtonClass={secondaryAction.loading ? 'loading' : null}
              >
                <ButtonLabel>{secondaryAction.label}</ButtonLabel>
                <LoadingButton />
              </Button>
            )}
            {primaryAction && (
              <Button
                className="StandardModal-primaryButton"
                autoFocus={!!primaryAction.autoFocus}
                onClick={primaryAction.onClick}
                href={primaryAction.href}
                variant="blue"
                disabled={primaryAction.disabled}
                inputButtonClass={primaryAction.loading ? 'loading' : null}
              >
                <ButtonLabel>{primaryAction.label}</ButtonLabel>
                <LoadingButton />
              </Button>
            )}
          </div>
        </div>
      );
    }
    return null;
  };

  render() {
    const { content, title, Image, onExit, wide } = this.props;
    const { verticalAlign, verticalBottomDialogOffset } = this.state;

    const classNames = classnames('StandardModal-container', {
      'StandardModal-container--wide': wide,
    });

    return (
      <Modal
        {...this.props}
        variant="ink"
        verticalAlign={verticalAlign}
        verticalBottomDialogOffset={verticalBottomDialogOffset}
      >
        <div className={classNames}>
          {Image && <Image className="StandardModal-image" />}
          <div className="StandardModal-header">
            <h2 className="StandardModal-title">{title}</h2>
            {!Image && (
              <Icon
                className="StandardModal-close"
                icon={Close}
                tabIndex="0"
                onKeyPress={this.onKeyPress}
                onClick={onExit}
              />
            )}
          </div>
          <div className="StandardModal-content body">{content}</div>
          {this.renderFooter()}
        </div>
      </Modal>
    );
  }
}

export default StandardModal;