wix-style-react
Version:
245 lines (225 loc) • 7.59 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import throttle from 'lodash/throttle';
import { generateDataAttr } from '../../utils/generateDataAttr';
import deprecationLog from '../../utils/deprecationLog';
import HeaderLayout from './HeaderLayout';
import FooterLayout from './FooterLayout';
import styles from './MessageBoxFunctionalLayout.scss';
class MessageBoxFunctionalLayout extends React.PureComponent {
constructor(props) {
super(props);
deprecationLog(
`Using "<MessageBoxFunctionalLayout/>" is deprecated. Instead, we advise you to use the newer "<MessageModalLayout/>" component. Please refer to it's documentation.`,
);
this.state = {
hasScroll: false,
scrolledToBottom: false,
};
this.messageBoxRef = null;
}
componentWillUnmount() {
if (this.state.hasScroll) {
this._handleMessageBoxScroll.cancel();
this.messageBoxRef.removeEventListener(
'scroll',
this._handleMessageBoxScroll,
);
}
}
_initializeMessageBoxRef = el => {
if (el && el.scrollHeight > el.clientHeight) {
this.setState({ hasScroll: true });
this.messageBoxRef = el;
this.messageBoxRef.addEventListener(
'scroll',
this._handleMessageBoxScroll,
);
}
};
_handleMessageBoxScroll = throttle(() => {
const scrolledToBottom =
this.messageBoxRef.scrollTop + this.messageBoxRef.clientHeight ===
this.messageBoxRef.scrollHeight;
if (scrolledToBottom !== this.state.scrolledToBottom) {
this.setState({ scrolledToBottom });
}
}, 16);
_renderContent = () => {
const {
children,
hideFooter,
noBodyPadding,
maxHeight,
fullscreen,
withEmptyState,
} = this.props;
const { hasScroll, scrolledToBottom } = this.state;
const messageBoxBodyClassNames = classNames(styles.body, {
[styles.scrollable]: typeof maxHeight !== 'undefined',
[styles.noPadding]: noBodyPadding,
[styles.fullscreenBody]: fullscreen,
[styles.noFooter]: hideFooter,
[styles.footerBorder]: hasScroll && !scrolledToBottom,
[styles.withEmptyState]: withEmptyState,
});
const messageBoxBodyStyle = {
maxHeight,
};
return (
<div
data-hook="message-box-body"
className={messageBoxBodyClassNames}
style={messageBoxBodyStyle}
ref={this._initializeMessageBoxRef}
>
{children}
</div>
);
};
render() {
const { theme } = this.props;
const {
dataHook,
title,
onCancel,
onOk,
onClose,
confirmText,
confirmPrefixIcon,
confirmSuffixIcon,
cancelText,
cancelPrefixIcon,
cancelSuffixIcon,
buttonsHeight,
hideFooter,
footerBottomChildren,
closeButton,
disableConfirmation,
disableCancel,
width,
margin,
noBodyPadding,
fullscreen,
withEmptyState,
sideActions,
image,
} = this.props;
const contentClassName = classNames(styles.content, {
[styles.fullscreenContent]: fullscreen,
});
const imageClassName = classNames(styles.image, {
[styles.withFooterAction]: sideActions,
[styles.noPadding]: noBodyPadding,
});
return (
<div
data-hook={dataHook}
className={contentClassName}
style={{ width, margin }}
{...generateDataAttr(this.props, ['noBodyPadding', 'theme'])}
>
<HeaderLayout
title={title}
onCancel={onClose ? onClose : onCancel}
theme={theme}
closeButton={closeButton}
/>
{image && !withEmptyState ? (
<div className={styles.messageWithImage}>
<div className={imageClassName} children={image} />
{this._renderContent()}
</div>
) : (
this._renderContent()
)}
{!hideFooter ? (
<FooterLayout
bottomChildren={footerBottomChildren}
enableCancel={!disableCancel}
enableOk={!disableConfirmation}
buttonsHeight={buttonsHeight}
confirmText={confirmText}
confirmPrefixIcon={confirmPrefixIcon}
confirmSuffixIcon={confirmSuffixIcon}
cancelText={cancelText}
cancelPrefixIcon={cancelPrefixIcon}
cancelSuffixIcon={cancelSuffixIcon}
onCancel={onCancel}
onOk={onOk}
theme={theme}
sideActions={sideActions}
/>
) : null}
</div>
);
}
}
MessageBoxFunctionalLayout.propTypes = {
/** applied as data-hook HTML attribute that can be used to create driver in testing */
dataHook: PropTypes.string,
/** Hides the footer that contains the action buttons */
hideFooter: PropTypes.bool,
/** Defines the main action button text */
confirmText: PropTypes.node,
/** Add a prefix icon for the main action button */
confirmPrefixIcon: PropTypes.element,
/** Add a suffix icon for the main action button */
confirmSuffixIcon: PropTypes.element,
/** Defines the secondary action button text */
cancelText: PropTypes.node,
/** Add a prefix icon for the secondary action button */
cancelPrefixIcon: PropTypes.element,
/** Add a suffix icon for the secondary action button */
cancelSuffixIcon: PropTypes.element,
/** modal theme color */
theme: PropTypes.oneOf(['red', 'blue', 'purple']),
/** Called when the main action (confirm) is clicked */
onOk: PropTypes.func,
/** Called when the secondary action (cancel) is clicked */
onCancel: PropTypes.func,
/** Called when the close button is clicked */
onClose: PropTypes.func,
/** Specify exact width */
width: PropTypes.string,
/** Specify exact margin. Use to fine tune position inside other elements.
* When used inside `<Modal>`, beware that `<Modal>` is a flex container, therefore specific flex item CSS rules apply for this margin. */
margin: PropTypes.string,
/** Defines the modals's header title */
title: PropTypes.node,
/** The content to be displayed. can be text or some node */
children: PropTypes.any,
/** Max height. When supplied - will allow internal scroll to the component */
maxHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Defines the buttons size */
buttonsHeight: PropTypes.oneOf(['tiny', 'small', 'medium', 'large']),
/** Show/hide the close button */
closeButton: PropTypes.bool,
/** Defines the secondary action button */
disableCancel: PropTypes.bool,
/** Defines the main action button */
disableConfirmation: PropTypes.bool,
/** Removes the content padding. Used in custom modal that defines it's own padding*/
noBodyPadding: PropTypes.bool,
/** A render slot to display a foot note */
footerBottomChildren: PropTypes.node,
/** Stretches the component to a full screen mode (with some padding) */
fullscreen: PropTypes.bool,
/** Changes the internal padding to be used with `<EmptyState/>` component */
withEmptyState: PropTypes.bool,
/** Used to display some side component in the footer, for example `<Checkbox/>` */
sideActions: PropTypes.node,
/** Used to display an illustration on the left side */
image: PropTypes.node,
};
MessageBoxFunctionalLayout.defaultProps = {
theme: 'blue',
buttonsHeight: 'small',
disableCancel: false,
disableConfirmation: false,
noBodyPadding: false,
fullscreen: false,
withEmptyState: false,
};
export default MessageBoxFunctionalLayout;