UNPKG

@re-flex/ui

Version:
100 lines (99 loc) 4.28 kB
import { css } from "@re-flex/styles"; import TransitionGroup from "@re-flex/transition-group/TransitionGroup"; import { EventEmitter } from "@re-flex/utils"; import clsx from "clsx"; import React, { useContext, useEffect, useMemo, useState } from "react"; import { Portal } from ".."; import Alert from "../Alert"; import useIsomorphicEffect from "../hooks/useIsomorphicEffect"; import { Grow } from "../Transitions/Grow"; class SnackbarController extends EventEmitter { } export const SnackbarContext = React.createContext(null); export const useSnackbar = () => { const { onClear, onDestroy, push, onClose } = useContext(SnackbarContext); return { onClear, onDestroy, push, onClose }; }; const PlacementList = [ "top-left", "top-right", "top-center", "bottom-left", "bottom-right", "bottom-center", ]; export const SnackbarProvider = ({ defaults = { duration: 3000, maxSnack: 5, placement: "top-right" }, TransitionComponent = Grow, children, }) => { const [bars, setBars] = useState([]); useIsomorphicEffect(() => { if (document.getElementById("reflex-snackbar-area") === null) { let container = document.createElement("div"); container.id = "reflex-snackbar-area"; } }, []); const onClear = () => { setBars([]); }; const onDestroy = (id) => { setBars((barList) => [...barList.filter((i) => i.id !== id)]); }; const onClose = (id) => { setBars((barList) => { let snackIndex = barList.findIndex((s) => s.id === id); if (snackIndex === -1) return barList; barList[snackIndex].open = false; return [...barList]; }); }; const push = (message, config) => { const snackId = config?.id || Date.now().toString(24); if (!config.placement) { config.placement = defaults.placement || "top-right"; } setBars((f) => [...f, { id: snackId, open: true, message, ...config }].slice(-(defaults.maxSnack || 5))); return snackId; }; const makeSnackList = () => { const placements = PlacementList.map((place, placeKey) => { const [y, x] = place.split("-"); let placeBarList = bars.filter((i) => i.placement === place); if (placeBarList.length === 0) return null; return (React.createElement("div", { key: placeKey, "data-place-container": place, className: clsx(css({ position: "fixed", [y]: "20px", zIndex: "1500", overflowY: "auto", maxHeight: "calc(50% - 40px)", }), { [css({ [x]: "20px" })]: x !== "center", [css({ marginInline: "auto", left: 0, right: 0, width: "max-content", })]: x === "center", }) }, React.createElement(TransitionGroup, { duration: 500, appear: true, enter: true, exit: true }, placeBarList.map(({ message, id, open, ...snackProps }, snackKey) => (React.createElement(Snackbar, { ...{ message, id, open, ...snackProps }, TransitionComponent: TransitionComponent, key: id })))))); }); return placements.filter(Boolean); }; const SnackbarPlacements = useMemo(() => { return makeSnackList(); }, [bars]); return (React.createElement(SnackbarContext.Provider, { value: { onClear, onClose, onDestroy, push } }, React.createElement(Portal, { open: true }, SnackbarPlacements), children)); }; const Snackbar = ({ message, id, open, autoHideDuration = 3000, TransitionComponent = Grow, disableAutoHide, ...snackProps }) => { const { onDestroy, onClose } = useSnackbar(); useEffect(() => { if (open === true && !disableAutoHide) { setTimeout(() => { onClose(id); }, autoHideDuration); } }, [open, disableAutoHide]); return (React.createElement(TransitionComponent, { alwaysMounted: true, key: id, in: open, onExited: () => onDestroy(id) }, React.createElement(Alert, { ...snackProps }, message))); };