UNPKG

wix-style-react

Version:
221 lines • 8.9 kB
import React, { Children } from 'react'; import PropTypes from 'prop-types'; import CloseButton from '../CloseButton'; import TextLabel from './TextLabel'; import ActionButton from './ActionButton'; import { st, classes } from './Notification.st.css'; import { StatusComplete, StatusWarning, StatusAlert, } from '@wix/wix-ui-icons-common'; import { dataHooks } from './constants'; import { timingMap } from '../Transition/constants'; export const LOCAL_NOTIFICATION = 'local'; export const GLOBAL_NOTIFICATION = 'global'; export const STICKY_NOTIFICATION = 'sticky'; export const DEFAULT_AUTO_HIDE_TIMEOUT = 6000; export const DEFAULT_TIMEOUT = DEFAULT_AUTO_HIDE_TIMEOUT; const animationsTimeouts = { enter: 300, exit: 300, }; const themeIcon = { error: React.createElement(StatusAlert, { className: classes.iconStyling }), success: React.createElement(StatusComplete, { className: classes.iconStyling }), warning: React.createElement(StatusWarning, { className: classes.iconStyling }), }; function mapChildren(children) { const childrenArray = Children.toArray(children); return childrenArray.reduce((childrenAsMap, child) => { switch (child.type.displayName) { case 'Notification.TextLabel': childrenAsMap.label = child; break; case 'Notification.ActionButton': childrenAsMap.ctaButton = child; break; case 'Notification.CloseButton': childrenAsMap.closeButton = React.cloneElement(child, { size: 'small', }); break; default: break; } return childrenAsMap; }, {}); } class Notification extends React.PureComponent { constructor() { super(...arguments); this.ref = React.createRef(); this.childRef = React.createRef(); this.state = { hideByCloseClick: false, hideByTimer: false, height: 0, showChildren: false, }; this._hideNotificationOnCloseClick = () => { this.setState({ hideByCloseClick: true }); setTimeout(() => this.props.onClose && this.props.onClose('hide-by-close-click'), animationsTimeouts.exit + 100); }; this._hideNotificationOnTimeout = () => { this.setState({ hideByTimer: true }); setTimeout(() => this.props.onClose && this.props.onClose('hide-by-timer'), animationsTimeouts.exit + 100); }; } UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.show) { this._setChildrenVisibility(nextProps); this._bypassCloseFlags(); this._clearCloseTimeout(); this._startCloseTimer(nextProps); } } componentDidMount() { this._setChildrenVisibility(this.props); this._startCloseTimer(this.props); } componentWillUnmount() { this._clearCloseTimeout(); this._clearTransitionTimeout(); } componentDidUpdate(prevProps, prevState) { const { showChildren, height, hideByCloseClick, hideByTimer } = this.state; if (prevProps.show !== this.props.show || prevState.showChildren !== this.state.showChildren) { const show = this._shouldShowNotification(this.props.show); if (show) { if (showChildren && this.ref.current && height === 0) { this.setState({ height: this._setHeightToChild(this.ref.current) }); } } else { if (showChildren && height !== 0) { this.setState({ height: 0 }); } } } if (prevProps.hideByCloseClick !== hideByCloseClick || prevProps.hideByTimer !== hideByTimer) { this._setChildrenVisibility(this.props); } } _setChildrenVisibility({ show }) { if (this._shouldShowNotification(show)) { this.setState({ showChildren: true }); } else { this.setState({ height: 0 }); this.transitionTimeout = setTimeout(() => { this.transitionTimeout && this.setState({ showChildren: false }); }, timingMap['medium01']); } } _clearTransitionTimeout() { if (this.transitionTimeout) { clearTimeout(this.transitionTimeout); this.transitionTimeout = null; } } _startCloseTimer({ autoHideTimeout }) { if (autoHideTimeout) { this.closeTimeout = setTimeout(() => this._hideNotificationOnTimeout(), autoHideTimeout || DEFAULT_AUTO_HIDE_TIMEOUT); } } _clearCloseTimeout() { if (this.closeTimeout) { clearTimeout(this.closeTimeout); this.closeTimeout = null; } } _bypassCloseFlags() { this.setState({ hideByCloseClick: false, hideByTimer: false, }); } _shouldShowNotification(show) { return show && !this.state.hideByCloseClick && !this.state.hideByTimer; } _setHeightToChild(node) { const height = this.childRef && this.childRef.current ? this.childRef.current.offsetHeight : 0; if (node && node.firstChild) { node.firstChild.style.height = `${height}px`; } return height; } _getChildren() { if (!this.state.showChildren) { return null; } const { theme, children } = this.props; const childrenComponents = mapChildren(children); return (React.createElement("div", { className: classes.wrapper, style: { overflow: 'hidden', transition: `height ${timingMap['medium01']}ms`, height: this.state.height, } }, React.createElement("div", { "data-hook": dataHooks.notificationContent, className: classes.notification, role: "alert", "aria-labelledby": "notification-label", "aria-live": "polite", ref: this.childRef }, themeIcon[theme] && React.createElement("div", null, themeIcon[theme]), React.createElement("div", { className: classes.labelWrapper }, childrenComponents.label, childrenComponents.ctaButton), childrenComponents.closeButton && (React.createElement("div", { "data-hook": dataHooks.notificationCloseButton, className: classes.closeButton, onClick: this._hideNotificationOnCloseClick, children: childrenComponents.closeButton }))))); } render() { const { dataHook, theme, type, zIndex } = this.props; return (React.createElement("div", { className: st(classes.root, { theme, type }), style: { zIndex }, "data-hook": dataHook, "data-theme": theme, "data-type": type }, React.createElement("div", { className: classes.animator, style: { overflow: 'hidden', transition: `height ${timingMap['medium01']}ms`, height: this.state.height, } }, React.createElement("div", { ref: this.ref, className: classes.animatorContent }, this._getChildren())))); } } const Close = props => React.createElement(CloseButton, { skin: "lightFilled", ...props }); Close.displayName = 'Notification.CloseButton'; Notification.displayName = 'Notification'; Notification.propTypes = { /** when set to `true`, notification is shown */ show: PropTypes.bool, /** Notification theme */ theme: PropTypes.oneOf([ 'standard', 'error', 'success', 'warning', 'premium', ]), /** Sets how <Notification/> should be displayed: * - `type="global"` will take up space and push the content down. * - `type="local"` will not take up space and will be displayed on top of content * - `type="sticky"` will not take up space and will be displayed at the top of whole page and on top of content (position: fixed;) * */ type: PropTypes.oneOf([ GLOBAL_NOTIFICATION, LOCAL_NOTIFICATION, STICKY_NOTIFICATION, ]), /** When provided, then the Notification will be hidden after the specified timeout. */ autoHideTimeout: PropTypes.number, /** Notification z-index */ zIndex: PropTypes.number, onClose: PropTypes.func, /** Can be either: * - `<Notification.TextLabel/>` (required) * - `<Notification.CloseButton/>` * - `<Notification.ActionButton/>` */ children: PropTypes.node, }; Notification.defaultProps = { theme: 'standard', type: GLOBAL_NOTIFICATION, onClose: null, }; Notification.CloseButton = Close; Notification.TextLabel = TextLabel; Notification.ActionButton = ActionButton; export default Notification; //# sourceMappingURL=Notification.js.map