UNPKG

pouncejs

Version:

A collection of UI components from Panther labs

229 lines (209 loc) 6.63 kB
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose"; import _extends from "@babel/runtime/helpers/extends"; import React from 'react'; import ReactDOM from 'react-dom'; import { ResizeObserver } from '@juggle/resize-observer'; import { animated, useTransition } from 'react-spring'; import useMeasure from 'react-use-measure'; import Flex from '../Flex'; import Snackbar from '../Snackbar'; import { isBrowser } from '../../utils/helpers'; import Box from '../Box'; var generateSnackbarId = function generateSnackbarId() { return Math.random().toString(36).substr(2, 5); }; var PUSH_SNACKBAR = 'PUSH_SNACKBAR'; var UPDATE_SNACKBAR = 'UPDATE_SNACKBAR'; var REMOVE_SNACKBAR = 'REMOVE_SNACKBAR'; var snackbarStateReducer = function snackbarStateReducer(snackbars, action) { switch (action.type) { case PUSH_SNACKBAR: return [].concat(snackbars, [_extends({ id: action.payload.id }, action.payload.props)]); case UPDATE_SNACKBAR: return snackbars.map(function (snackbar) { return snackbar.id === action.payload.id ? _extends({}, snackbar, action.payload.props) : snackbar; }); case REMOVE_SNACKBAR: return snackbars.filter(function (s) { return s.id !== action.payload.id; }); default: return snackbars; } }; var SnackbarContext = /*#__PURE__*/React.createContext({ pushSnackbar: function pushSnackbar() { return ''; }, updateSnackbar: function updateSnackbar() { return ''; } }); var positionStyles = { 'top-left': { top: 0, left: 6 }, 'top-middle': { top: 0, left: '50%', style: { transform: 'translate(-50%, 0)' } }, 'top-right': { top: 0, right: 6 }, 'bottom-left': { bottom: 3, left: 6 }, 'bottom-middle': { bottom: 3, left: '50%', style: { transform: 'translate(-50%, 0)' } }, 'bottom-right': { bottom: 3, right: 6 } }; /** * A component that acts both as a state-manager and provider. It provides access to methods for * managing snackbar instances */ export var SnackbarProvider = function SnackbarProvider(_ref) { var children = _ref.children; var _React$useReducer = React.useReducer(snackbarStateReducer, []), snackbars = _React$useReducer[0], dispatch = _React$useReducer[1]; var pushSnackbar = React.useCallback(function (props) { var id = generateSnackbarId(); dispatch({ type: PUSH_SNACKBAR, payload: { id: id, props: props } }); return id; }, [dispatch]); var updateSnackbar = React.useCallback(function (id, props) { dispatch({ type: UPDATE_SNACKBAR, payload: { id: id, props: props } }); return id; }, [dispatch]); var removeSnackbar = React.useCallback(function (id) { dispatch({ type: REMOVE_SNACKBAR, payload: { id: id } }); return id; }, [dispatch]); var _useMeasure = useMeasure({ debounce: 100, scroll: false, polyfill: ResizeObserver }), ref = _useMeasure[0], height = _useMeasure[1].height; // eslint-disable-next-line @typescript-eslint/no-explicit-any var transitions = useTransition(snackbars, snackbars.map(function (_ref2) { var id = _ref2.id; return id; }), { from: { opacity: 0, height: 0 }, enter: { opacity: 1, height: height }, leave: { opacity: 0, height: 0, pointerEvents: 'none' }, // Snackbars can have varying heights. As soon as the latest is measured, update the height // calculations of this same latest one. This way the last snackbar can always have the // correct height, its own (not the heights of the ones animated before) update: function update(item) { if (item.id === snackbars[snackbars.length - 1].id) { return { height: height }; } } }); var renderSnackbars = function renderSnackbars() { if (!isBrowser) { return null; } var listOfPositions = Object.keys(positionStyles); return /*#__PURE__*/ReactDOM.createPortal( /*#__PURE__*/React.createElement(React.Fragment, null, listOfPositions.map(function (position) { var _transitions$filter; return /*#__PURE__*/React.createElement(Flex, _extends({ key: position, "data-testid": position + "-snackbar-wrapper", position: "fixed", flexDirection: "column", justifyContent: "center", alignItems: "flex-end", zIndex: 9999 }, positionStyles[position]), (_transitions$filter = transitions.filter(function (snackbar) { return (// handles multiple toasts in different positions // The outer loop here loops through the six possible positions for Snackbars. // So this creates a list of all the Snackbars that are supposed to be at this position. // If the snackbar has a position value, filter it into the that position. // If there is no position listed, show it if the current position being filtered is the bottom-left (default position) snackbar.item.position ? snackbar.item.position === position : position === 'bottom-left' ); })) == null ? void 0 : _transitions$filter.map(function (_ref3) { var _ref3$item = _ref3.item, id = _ref3$item.id, snackbarPublicProps = _objectWithoutPropertiesLoose(_ref3$item, ["id"]), key = _ref3.key, styles = _ref3.props; return /*#__PURE__*/React.createElement(animated.div, { key: key, style: styles }, /*#__PURE__*/React.createElement(Box, { pt: 3, ref: ref }, /*#__PURE__*/React.createElement(Snackbar, _extends({ destroy: function destroy() { return removeSnackbar(id); } }, snackbarPublicProps)))); })); })), document.body); }; var contextPayload = React.useMemo(function () { return { pushSnackbar: pushSnackbar, updateSnackbar: updateSnackbar, removeSnackbar: removeSnackbar }; }, [pushSnackbar, updateSnackbar, removeSnackbar]); return /*#__PURE__*/React.createElement(SnackbarContext.Provider, { value: contextPayload }, renderSnackbars(), children); }; /** A shortcut for the consumer component */ export var SnackbarConsumer = SnackbarContext.Consumer; /** An alternative to the consumer component through hooks */ export var useSnackbar = function useSnackbar() { return React.useContext(SnackbarContext); };