@divetocode/toast
Version:
A lightweight toast notification system for React/Next.js
97 lines (92 loc) • 3.16 kB
JavaScript
// src/Toast.tsx
var listener = null;
var showToast = (message, options) => {
listener?.(message, options);
};
var _internalSetToastListener = (fn) => {
listener = fn;
};
// src/ToastRoot.tsx
import { useEffect, useState } from "react";
import { createPortal } from "react-dom";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
var toastId = 0;
var ToastRoot = () => {
const [toasts, setToasts] = useState([]);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
_internalSetToastListener((message, options) => {
const id = toastId++;
const type = options?.type || "success";
setToasts((prev) => [...prev, { id, message, type }]);
setTimeout(() => {
setToasts((prev) => prev.filter((t) => t.id !== id));
}, 5e3);
});
}, []);
if (!mounted) return null;
return createPortal(
/* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx("style", { children: `
@keyframes slide-in {
0% {
opacity: 0;
transform: translateX(100%);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
.toast-container {
position: fixed;
top: 1.5rem; /* top-6 */
right: 1.5rem; /* right-6 */
z-index: 50;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 0.5rem; /* space-y-2 */
pointer-events: none;
}
.toast-base {
position: relative;
width: 100%;
max-width: 20rem; /* max-w-xs */
padding: 0.5rem 1rem; /* px-4 py-2 */
margin-top: 0.5rem; /* mt-2 */
border-radius: 0.375rem; /* rounded-md */
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05); /* shadow-lg */
font-size: 0.875rem; /* text-sm */
font-weight: 500; /* font-medium */
transition: all 0.5s; /* transition-all duration-500 */
transform: translateX(0); /* transform */
animation: slide-in 0.3s ease-out; /* animate-slide-in */
}
.toast-success {
background-color: #d1fae5; /* bg-green-100 */
color: #065f46; /* text-green-900 */
border: 1px solid #a7f3d0; /* border-green-300 */
}
.toast-error {
background-color: #fee2e2; /* bg-red-100 */
color: #991b1b; /* text-red-900 */
border: 1px solid #fca5a5; /* border-red-300 */
}
` }),
/* @__PURE__ */ jsx("div", { className: "toast-container", children: toasts.map((t) => {
const typeClass = t.type === "error" ? "toast-error" : "toast-success";
return /* @__PURE__ */ jsx("div", { className: `toast-base ${typeClass}`, children: t.message }, t.id);
}) })
] }),
document.body
);
};
var ToastRoot_default = ToastRoot;
export {
ToastRoot_default as ToastRoot,
showToast
};
//# sourceMappingURL=index.mjs.map