@re-flex/ui
Version:
Re-Flex ui library
100 lines (99 loc) • 4.28 kB
JavaScript
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)));
};