UNPKG

@mui/material

Version:

Quickly build beautiful React apps. MUI is a simple and customizable component library to build faster, beautiful, and more accessible React applications. Follow your own design system, or start with Material Design.

466 lines (412 loc) 13.3 kB
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; import _extends from "@babel/runtime/helpers/esm/extends"; const _excluded = ["onEnter", "onExited"], _excluded2 = ["action", "anchorOrigin", "autoHideDuration", "children", "className", "ClickAwayListenerProps", "ContentProps", "disableWindowBlurListener", "message", "onBlur", "onClose", "onFocus", "onMouseEnter", "onMouseLeave", "open", "resumeHideDuration", "TransitionComponent", "transitionDuration", "TransitionProps"]; import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import { unstable_composeClasses as composeClasses } from '@mui/base'; import ClickAwayListener from '@mui/base/ClickAwayListener'; import styled from '../styles/styled'; import useTheme from '../styles/useTheme'; import useThemeProps from '../styles/useThemeProps'; import { duration } from '../styles/createTransitions'; import useEventCallback from '../utils/useEventCallback'; import capitalize from '../utils/capitalize'; import Grow from '../Grow'; import SnackbarContent from '../SnackbarContent'; import { getSnackbarUtilityClass } from './snackbarClasses'; import { jsx as _jsx } from "react/jsx-runtime"; const useUtilityClasses = ownerState => { const { classes, anchorOrigin } = ownerState; const slots = { root: ['root', `anchorOrigin${capitalize(anchorOrigin.vertical)}${capitalize(anchorOrigin.horizontal)}`] }; return composeClasses(slots, getSnackbarUtilityClass, classes); }; const SnackbarRoot = styled('div', { name: 'MuiSnackbar', slot: 'Root', overridesResolver: (props, styles) => { const { ownerState } = props; return [styles.root, styles[`anchorOrigin${capitalize(ownerState.anchorOrigin.vertical)}${capitalize(ownerState.anchorOrigin.horizontal)}`]]; } })(({ theme, ownerState }) => { const center = _extends({}, !ownerState.isRtl && { left: '50%', right: 'auto', transform: 'translateX(-50%)' }, ownerState.isRtl && { right: '50%', left: 'auto', transform: 'translateX(50%)' }); return _extends({ zIndex: theme.zIndex.snackbar, position: 'fixed', display: 'flex', left: 8, right: 8, justifyContent: 'center', alignItems: 'center' }, ownerState.anchorOrigin.vertical === 'top' ? { top: 8 } : { bottom: 8 }, ownerState.anchorOrigin.horizontal === 'left' && { justifyContent: 'flex-start' }, ownerState.anchorOrigin.horizontal === 'right' && { justifyContent: 'flex-end' }, { [theme.breakpoints.up('sm')]: _extends({}, ownerState.anchorOrigin.vertical === 'top' ? { top: 24 } : { bottom: 24 }, ownerState.anchorOrigin.horizontal === 'center' && center, ownerState.anchorOrigin.horizontal === 'left' && _extends({}, !ownerState.isRtl && { left: 24, right: 'auto' }, ownerState.isRtl && { right: 24, left: 'auto' }), ownerState.anchorOrigin.horizontal === 'right' && _extends({}, !ownerState.isRtl && { right: 24, left: 'auto' }, ownerState.isRtl && { left: 24, right: 'auto' })) }); }); const Snackbar = /*#__PURE__*/React.forwardRef(function Snackbar(inProps, ref) { const props = useThemeProps({ props: inProps, name: 'MuiSnackbar' }); const { action, anchorOrigin: { vertical, horizontal } = { vertical: 'bottom', horizontal: 'left' }, autoHideDuration = null, children, className, ClickAwayListenerProps, ContentProps, disableWindowBlurListener = false, message, onBlur, onClose, onFocus, onMouseEnter, onMouseLeave, open, resumeHideDuration, TransitionComponent = Grow, transitionDuration = { enter: duration.enteringScreen, exit: duration.leavingScreen }, TransitionProps: { onEnter, onExited } = {} } = props, TransitionProps = _objectWithoutPropertiesLoose(props.TransitionProps, _excluded), other = _objectWithoutPropertiesLoose(props, _excluded2); const theme = useTheme(); const isRtl = theme.direction === 'rtl'; const ownerState = _extends({}, props, { anchorOrigin: { vertical, horizontal }, isRtl }); const classes = useUtilityClasses(ownerState); const timerAutoHide = React.useRef(); const [exited, setExited] = React.useState(true); const handleClose = useEventCallback((...args) => { if (onClose) { onClose(...args); } }); const setAutoHideTimer = useEventCallback(autoHideDurationParam => { if (!onClose || autoHideDurationParam == null) { return; } clearTimeout(timerAutoHide.current); timerAutoHide.current = setTimeout(() => { handleClose(null, 'timeout'); }, autoHideDurationParam); }); React.useEffect(() => { if (open) { setAutoHideTimer(autoHideDuration); } return () => { clearTimeout(timerAutoHide.current); }; }, [open, autoHideDuration, setAutoHideTimer]); // Pause the timer when the user is interacting with the Snackbar // or when the user hide the window. const handlePause = () => { clearTimeout(timerAutoHide.current); }; // Restart the timer when the user is no longer interacting with the Snackbar // or when the window is shown back. const handleResume = React.useCallback(() => { if (autoHideDuration != null) { setAutoHideTimer(resumeHideDuration != null ? resumeHideDuration : autoHideDuration * 0.5); } }, [autoHideDuration, resumeHideDuration, setAutoHideTimer]); const handleFocus = event => { if (onFocus) { onFocus(event); } handlePause(); }; const handleMouseEnter = event => { if (onMouseEnter) { onMouseEnter(event); } handlePause(); }; const handleBlur = event => { if (onBlur) { onBlur(event); } handleResume(); }; const handleMouseLeave = event => { if (onMouseLeave) { onMouseLeave(event); } handleResume(); }; const handleClickAway = event => { if (onClose) { onClose(event, 'clickaway'); } }; const handleExited = node => { setExited(true); if (onExited) { onExited(node); } }; const handleEnter = (node, isAppearing) => { setExited(false); if (onEnter) { onEnter(node, isAppearing); } }; React.useEffect(() => { // TODO: window global should be refactored here if (!disableWindowBlurListener && open) { window.addEventListener('focus', handleResume); window.addEventListener('blur', handlePause); return () => { window.removeEventListener('focus', handleResume); window.removeEventListener('blur', handlePause); }; } return undefined; }, [disableWindowBlurListener, handleResume, open]); React.useEffect(() => { if (!open) { return undefined; } /** * @param {KeyboardEvent} nativeEvent */ function handleKeyDown(nativeEvent) { if (!nativeEvent.defaultPrevented) { // IE11, Edge (prior to using Bink?) use 'Esc' if (nativeEvent.key === 'Escape' || nativeEvent.key === 'Esc') { // not calling `preventDefault` since we don't know if people may ignore this event e.g. a permanently open snackbar if (onClose) { onClose(nativeEvent, 'escapeKeyDown'); } } } } document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('keydown', handleKeyDown); }; }, [exited, open, onClose]); // So we only render active snackbars. if (!open && exited) { return null; } return /*#__PURE__*/_jsx(ClickAwayListener, _extends({ onClickAway: handleClickAway }, ClickAwayListenerProps, { children: /*#__PURE__*/_jsx(SnackbarRoot, _extends({ className: clsx(classes.root, className), onBlur: handleBlur, onFocus: handleFocus, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ownerState: ownerState, ref: ref }, other, { children: /*#__PURE__*/_jsx(TransitionComponent, _extends({ appear: true, in: open, timeout: transitionDuration, direction: vertical === 'top' ? 'down' : 'up', onEnter: handleEnter, onExited: handleExited }, TransitionProps, { children: children || /*#__PURE__*/_jsx(SnackbarContent, _extends({ message: message, action: action }, ContentProps)) })) })) })); }); process.env.NODE_ENV !== "production" ? Snackbar.propTypes /* remove-proptypes */ = { // ----------------------------- Warning -------------------------------- // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the d.ts file and run "yarn proptypes" | // ---------------------------------------------------------------------- /** * The action to display. It renders after the message, at the end of the snackbar. */ action: PropTypes.node, /** * The anchor of the `Snackbar`. * On smaller screens, the component grows to occupy all the available width, * the horizontal alignment is ignored. * @default { vertical: 'bottom', horizontal: 'left' } */ anchorOrigin: PropTypes.shape({ horizontal: PropTypes.oneOf(['center', 'left', 'right']).isRequired, vertical: PropTypes.oneOf(['bottom', 'top']).isRequired }), /** * The number of milliseconds to wait before automatically calling the * `onClose` function. `onClose` should then set the state of the `open` * prop to hide the Snackbar. This behavior is disabled by default with * the `null` value. * @default null */ autoHideDuration: PropTypes.number, /** * Replace the `SnackbarContent` component. */ children: PropTypes.element, /** * Override or extend the styles applied to the component. */ classes: PropTypes.object, /** * @ignore */ className: PropTypes.string, /** * Props applied to the `ClickAwayListener` element. */ ClickAwayListenerProps: PropTypes.object, /** * Props applied to the [`SnackbarContent`](/api/snackbar-content/) element. */ ContentProps: PropTypes.object, /** * If `true`, the `autoHideDuration` timer will expire even if the window is not focused. * @default false */ disableWindowBlurListener: PropTypes.bool, /** * When displaying multiple consecutive Snackbars from a parent rendering a single * <Snackbar/>, add the key prop to ensure independent treatment of each message. * e.g. <Snackbar key={message} />, otherwise, the message may update-in-place and * features such as autoHideDuration may be canceled. */ key: () => null, /** * The message to display. */ message: PropTypes.node, /** * @ignore */ onBlur: PropTypes.func, /** * Callback fired when the component requests to be closed. * Typically `onClose` is used to set state in the parent component, * which is used to control the `Snackbar` `open` prop. * The `reason` parameter can optionally be used to control the response to `onClose`, * for example ignoring `clickaway`. * * @param {React.SyntheticEvent<any> | Event} event The event source of the callback. * @param {string} reason Can be: `"timeout"` (`autoHideDuration` expired), `"clickaway"`, or `"escapeKeyDown"`. */ onClose: PropTypes.func, /** * @ignore */ onFocus: PropTypes.func, /** * @ignore */ onMouseEnter: PropTypes.func, /** * @ignore */ onMouseLeave: PropTypes.func, /** * If `true`, the component is shown. */ open: PropTypes.bool, /** * The number of milliseconds to wait before dismissing after user interaction. * If `autoHideDuration` prop isn't specified, it does nothing. * If `autoHideDuration` prop is specified but `resumeHideDuration` isn't, * we default to `autoHideDuration / 2` ms. */ resumeHideDuration: PropTypes.number, /** * The system prop that allows defining system overrides as well as additional CSS styles. */ sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]), /** * The component used for the transition. * [Follow this guide](/components/transitions/#transitioncomponent-prop) to learn more about the requirements for this component. * @default Grow */ TransitionComponent: PropTypes.elementType, /** * The duration for the transition, in milliseconds. * You may specify a single timeout for all transitions, or individually with an object. * @default { * enter: duration.enteringScreen, * exit: duration.leavingScreen, * } */ transitionDuration: PropTypes.oneOfType([PropTypes.number, PropTypes.shape({ appear: PropTypes.number, enter: PropTypes.number, exit: PropTypes.number })]), /** * Props applied to the transition element. * By default, the element is based on this [`Transition`](http://reactcommunity.org/react-transition-group/transition/) component. * @default {} */ TransitionProps: PropTypes.object } : void 0; export default Snackbar;