UNPKG

12.5 kBJavaScriptView Raw
1import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
2import _extends from "@babel/runtime/helpers/esm/extends";
3import * as React from 'react';
4import PropTypes from 'prop-types';
5import clsx from 'clsx';
6import withStyles from '../styles/withStyles';
7import { duration } from '../styles/transitions';
8import ClickAwayListener from '../ClickAwayListener';
9import useEventCallback from '../utils/useEventCallback';
10import capitalize from '../utils/capitalize';
11import createChainedFunction from '../utils/createChainedFunction';
12import deprecatedPropType from '../utils/deprecatedPropType';
13import Grow from '../Grow';
14import SnackbarContent from '../SnackbarContent';
15export const styles = theme => {
16 const top1 = {
17 top: 8
18 };
19 const bottom1 = {
20 bottom: 8
21 };
22 const right = {
23 justifyContent: 'flex-end'
24 };
25 const left = {
26 justifyContent: 'flex-start'
27 };
28 const top3 = {
29 top: 24
30 };
31 const bottom3 = {
32 bottom: 24
33 };
34 const right3 = {
35 right: 24
36 };
37 const left3 = {
38 left: 24
39 };
40 const center = {
41 left: '50%',
42 right: 'auto',
43 transform: 'translateX(-50%)'
44 };
45 return {
46 /* Styles applied to the root element. */
47 root: {
48 zIndex: theme.zIndex.snackbar,
49 position: 'fixed',
50 display: 'flex',
51 left: 8,
52 right: 8,
53 justifyContent: 'center',
54 alignItems: 'center'
55 },
56
57 /* Styles applied to the root element if `anchorOrigin={{ 'top', 'center' }}`. */
58 anchorOriginTopCenter: _extends({}, top1, {
59 [theme.breakpoints.up('sm')]: _extends({}, top3, center)
60 }),
61
62 /* Styles applied to the root element if `anchorOrigin={{ 'bottom', 'center' }}`. */
63 anchorOriginBottomCenter: _extends({}, bottom1, {
64 [theme.breakpoints.up('sm')]: _extends({}, bottom3, center)
65 }),
66
67 /* Styles applied to the root element if `anchorOrigin={{ 'top', 'right' }}`. */
68 anchorOriginTopRight: _extends({}, top1, right, {
69 [theme.breakpoints.up('sm')]: _extends({
70 left: 'auto'
71 }, top3, right3)
72 }),
73
74 /* Styles applied to the root element if `anchorOrigin={{ 'bottom', 'right' }}`. */
75 anchorOriginBottomRight: _extends({}, bottom1, right, {
76 [theme.breakpoints.up('sm')]: _extends({
77 left: 'auto'
78 }, bottom3, right3)
79 }),
80
81 /* Styles applied to the root element if `anchorOrigin={{ 'top', 'left' }}`. */
82 anchorOriginTopLeft: _extends({}, top1, left, {
83 [theme.breakpoints.up('sm')]: _extends({
84 right: 'auto'
85 }, top3, left3)
86 }),
87
88 /* Styles applied to the root element if `anchorOrigin={{ 'bottom', 'left' }}`. */
89 anchorOriginBottomLeft: _extends({}, bottom1, left, {
90 [theme.breakpoints.up('sm')]: _extends({
91 right: 'auto'
92 }, bottom3, left3)
93 })
94 };
95};
96const Snackbar = /*#__PURE__*/React.forwardRef(function Snackbar(props, ref) {
97 const {
98 action,
99 anchorOrigin: {
100 vertical,
101 horizontal
102 } = {
103 vertical: 'bottom',
104 horizontal: 'center'
105 },
106 autoHideDuration = null,
107 children,
108 classes,
109 className,
110 ClickAwayListenerProps,
111 ContentProps,
112 disableWindowBlurListener = false,
113 message,
114 onClose,
115 onEnter,
116 onEntered,
117 onEntering,
118 onExit,
119 onExited,
120 onExiting,
121 onMouseEnter,
122 onMouseLeave,
123 open,
124 resumeHideDuration,
125 TransitionComponent = Grow,
126 transitionDuration = {
127 enter: duration.enteringScreen,
128 exit: duration.leavingScreen
129 },
130 TransitionProps
131 } = props,
132 other = _objectWithoutPropertiesLoose(props, ["action", "anchorOrigin", "autoHideDuration", "children", "classes", "className", "ClickAwayListenerProps", "ContentProps", "disableWindowBlurListener", "message", "onClose", "onEnter", "onEntered", "onEntering", "onExit", "onExited", "onExiting", "onMouseEnter", "onMouseLeave", "open", "resumeHideDuration", "TransitionComponent", "transitionDuration", "TransitionProps"]);
133
134 const timerAutoHide = React.useRef();
135 const [exited, setExited] = React.useState(true);
136 const handleClose = useEventCallback((...args) => {
137 if (onClose) {
138 onClose(...args);
139 }
140 });
141 const setAutoHideTimer = useEventCallback(autoHideDurationParam => {
142 if (!onClose || autoHideDurationParam == null) {
143 return;
144 }
145
146 clearTimeout(timerAutoHide.current);
147 timerAutoHide.current = setTimeout(() => {
148 handleClose(null, 'timeout');
149 }, autoHideDurationParam);
150 });
151 React.useEffect(() => {
152 if (open) {
153 setAutoHideTimer(autoHideDuration);
154 }
155
156 return () => {
157 clearTimeout(timerAutoHide.current);
158 };
159 }, [open, autoHideDuration, setAutoHideTimer]); // Pause the timer when the user is interacting with the Snackbar
160 // or when the user hide the window.
161
162 const handlePause = () => {
163 clearTimeout(timerAutoHide.current);
164 }; // Restart the timer when the user is no longer interacting with the Snackbar
165 // or when the window is shown back.
166
167
168 const handleResume = React.useCallback(() => {
169 if (autoHideDuration != null) {
170 setAutoHideTimer(resumeHideDuration != null ? resumeHideDuration : autoHideDuration * 0.5);
171 }
172 }, [autoHideDuration, resumeHideDuration, setAutoHideTimer]);
173
174 const handleMouseEnter = event => {
175 if (onMouseEnter) {
176 onMouseEnter(event);
177 }
178
179 handlePause();
180 };
181
182 const handleMouseLeave = event => {
183 if (onMouseLeave) {
184 onMouseLeave(event);
185 }
186
187 handleResume();
188 };
189
190 const handleClickAway = event => {
191 if (onClose) {
192 onClose(event, 'clickaway');
193 }
194 };
195
196 const handleExited = () => {
197 setExited(true);
198 };
199
200 const handleEnter = () => {
201 setExited(false);
202 };
203
204 React.useEffect(() => {
205 if (!disableWindowBlurListener && open) {
206 window.addEventListener('focus', handleResume);
207 window.addEventListener('blur', handlePause);
208 return () => {
209 window.removeEventListener('focus', handleResume);
210 window.removeEventListener('blur', handlePause);
211 };
212 }
213
214 return undefined;
215 }, [disableWindowBlurListener, handleResume, open]); // So we only render active snackbars.
216
217 if (!open && exited) {
218 return null;
219 }
220
221 return /*#__PURE__*/React.createElement(ClickAwayListener, _extends({
222 onClickAway: handleClickAway
223 }, ClickAwayListenerProps), /*#__PURE__*/React.createElement("div", _extends({
224 className: clsx(classes.root, classes[`anchorOrigin${capitalize(vertical)}${capitalize(horizontal)}`], className),
225 onMouseEnter: handleMouseEnter,
226 onMouseLeave: handleMouseLeave,
227 ref: ref
228 }, other), /*#__PURE__*/React.createElement(TransitionComponent, _extends({
229 appear: true,
230 in: open,
231 onEnter: createChainedFunction(handleEnter, onEnter),
232 onEntered: onEntered,
233 onEntering: onEntering,
234 onExit: onExit,
235 onExited: createChainedFunction(handleExited, onExited),
236 onExiting: onExiting,
237 timeout: transitionDuration,
238 direction: vertical === 'top' ? 'down' : 'up'
239 }, TransitionProps), children || /*#__PURE__*/React.createElement(SnackbarContent, _extends({
240 message: message,
241 action: action
242 }, ContentProps)))));
243});
244process.env.NODE_ENV !== "production" ? Snackbar.propTypes = {
245 // ----------------------------- Warning --------------------------------
246 // | These PropTypes are generated from the TypeScript type definitions |
247 // | To update them edit the d.ts file and run "yarn proptypes" |
248 // ----------------------------------------------------------------------
249
250 /**
251 * The action to display. It renders after the message, at the end of the snackbar.
252 */
253 action: PropTypes.node,
254
255 /**
256 * The anchor of the `Snackbar`.
257 */
258 anchorOrigin: PropTypes.shape({
259 horizontal: PropTypes.oneOf(['center', 'left', 'right']).isRequired,
260 vertical: PropTypes.oneOf(['bottom', 'top']).isRequired
261 }),
262
263 /**
264 * The number of milliseconds to wait before automatically calling the
265 * `onClose` function. `onClose` should then set the state of the `open`
266 * prop to hide the Snackbar. This behavior is disabled by default with
267 * the `null` value.
268 */
269 autoHideDuration: PropTypes.number,
270
271 /**
272 * Replace the `SnackbarContent` component.
273 */
274 children: PropTypes.element,
275
276 /**
277 * Override or extend the styles applied to the component.
278 * See [CSS API](#css) below for more details.
279 */
280 classes: PropTypes.object,
281
282 /**
283 * @ignore
284 */
285 className: PropTypes.string,
286
287 /**
288 * Props applied to the `ClickAwayListener` element.
289 */
290 ClickAwayListenerProps: PropTypes.object,
291
292 /**
293 * Props applied to the [`SnackbarContent`](/api/snackbar-content/) element.
294 */
295 ContentProps: PropTypes.object,
296
297 /**
298 * If `true`, the `autoHideDuration` timer will expire even if the window is not focused.
299 */
300 disableWindowBlurListener: PropTypes.bool,
301
302 /**
303 * When displaying multiple consecutive Snackbars from a parent rendering a single
304 * <Snackbar/>, add the key prop to ensure independent treatment of each message.
305 * e.g. <Snackbar key={message} />, otherwise, the message may update-in-place and
306 * features such as autoHideDuration may be canceled.
307 */
308 key: PropTypes.any,
309
310 /**
311 * The message to display.
312 */
313 message: PropTypes.node,
314
315 /**
316 * Callback fired when the component requests to be closed.
317 * Typically `onClose` is used to set state in the parent component,
318 * which is used to control the `Snackbar` `open` prop.
319 * The `reason` parameter can optionally be used to control the response to `onClose`,
320 * for example ignoring `clickaway`.
321 *
322 * @param {object} event The event source of the callback.
323 * @param {string} reason Can be: `"timeout"` (`autoHideDuration` expired), `"clickaway"`.
324 */
325 onClose: PropTypes.func,
326
327 /**
328 * Callback fired before the transition is entering.
329 * @deprecated Use the `TransitionProps` prop instead.
330 */
331 onEnter: deprecatedPropType(PropTypes.func, 'Use the `TransitionProps` prop instead.'),
332
333 /**
334 * Callback fired when the transition has entered.
335 * @deprecated Use the `TransitionProps` prop instead.
336 */
337 onEntered: deprecatedPropType(PropTypes.func, 'Use the `TransitionProps` prop instead.'),
338
339 /**
340 * Callback fired when the transition is entering.
341 * @deprecated Use the `TransitionProps` prop instead.
342 */
343 onEntering: deprecatedPropType(PropTypes.func, 'Use the `TransitionProps` prop instead.'),
344
345 /**
346 * Callback fired before the transition is exiting.
347 * @deprecated Use the `TransitionProps` prop instead.
348 */
349 onExit: deprecatedPropType(PropTypes.func, 'Use the `TransitionProps` prop instead.'),
350
351 /**
352 * Callback fired when the transition has exited.
353 * @deprecated Use the `TransitionProps` prop instead.
354 */
355 onExited: deprecatedPropType(PropTypes.func, 'Use the `TransitionProps` prop instead.'),
356
357 /**
358 * Callback fired when the transition is exiting.
359 * @deprecated Use the `TransitionProps` prop instead.
360 */
361 onExiting: deprecatedPropType(PropTypes.func, 'Use the `TransitionProps` prop instead.'),
362
363 /**
364 * @ignore
365 */
366 onMouseEnter: PropTypes.func,
367
368 /**
369 * @ignore
370 */
371 onMouseLeave: PropTypes.func,
372
373 /**
374 * If `true`, `Snackbar` is open.
375 */
376 open: PropTypes.bool,
377
378 /**
379 * The number of milliseconds to wait before dismissing after user interaction.
380 * If `autoHideDuration` prop isn't specified, it does nothing.
381 * If `autoHideDuration` prop is specified but `resumeHideDuration` isn't,
382 * we default to `autoHideDuration / 2` ms.
383 */
384 resumeHideDuration: PropTypes.number,
385
386 /**
387 * The component used for the transition.
388 * [Follow this guide](/components/transitions/#transitioncomponent-prop) to learn more about the requirements for this component.
389 */
390 TransitionComponent: PropTypes.elementType,
391
392 /**
393 * The duration for the transition, in milliseconds.
394 * You may specify a single timeout for all transitions, or individually with an object.
395 */
396 transitionDuration: PropTypes.oneOfType([PropTypes.number, PropTypes.shape({
397 appear: PropTypes.number,
398 enter: PropTypes.number,
399 exit: PropTypes.number
400 })]),
401
402 /**
403 * Props applied to the [`Transition`](http://reactcommunity.org/react-transition-group/transition#Transition-props) element.
404 */
405 TransitionProps: PropTypes.object
406} : void 0;
407export default withStyles(styles, {
408 flip: false,
409 name: 'MuiSnackbar'
410})(Snackbar);
\No newline at end of file