import { Component, createRef } from 'react'; import { CSSTransition } from 'react-transition-group'; import Button from '../button'; import Body from '../body'; import { Theme, type ThemeDark, type ThemeLight } from '../common'; import { DirectionContext } from '../provider/direction'; import withNextPortal from '../withNextPortal/withNextPortal'; const CSS_TRANSITION_DURATION = 400; export interface SnackbarProps { action?: { label: string; onClick?: React.MouseEventHandler; }; text: React.ReactNode; /** @deprecated */ theme?: ThemeLight | ThemeDark; timeout: number; timestamp: number; } interface SnackbarState extends Pick { visible: boolean; } export class Snackbar extends Component { bodyRef = createRef(); timeout = 0; transitionTimeout = 0; constructor(props: SnackbarProps) { super(props); this.state = { visible: false, text: '', }; } componentWillUnmount() { window.clearTimeout(this.timeout); window.clearTimeout(this.transitionTimeout); } shouldComponentUpdate(nextProps: SnackbarProps, nextState: SnackbarState) { if (!nextProps.text) { return false; } if ( nextProps.timestamp === this.props.timestamp && nextState.visible === this.state.visible && nextState.text === this.state.text ) { return false; } return true; } setLeaveTimeout = () => { const { timeout } = this.props; this.timeout = window.setTimeout(() => { this.setState({ visible: false }); }, timeout); }; componentDidUpdate(previousProps: SnackbarProps) { const { action, text, timestamp } = this.props; if (!previousProps.text) { this.setState({ visible: true, action, text }, () => { this.setLeaveTimeout(); }); } else if (previousProps.timestamp !== timestamp) { window.clearTimeout(this.timeout); if (this.state.visible) { this.setState({ visible: false }, () => { this.transitionTimeout = window.setTimeout(() => { this.setState({ visible: true, action, text }); this.setLeaveTimeout(); }, CSS_TRANSITION_DURATION); }); } else { this.setState({ visible: true, action, text }); this.setLeaveTimeout(); } } } render() { const { action, text, visible } = this.state; const { timeout } = this.props; return (
{text} {action ? ( ) : null}
); } } Snackbar.contextType = DirectionContext; export default withNextPortal(Snackbar);