UNPKG

g-notification

Version:

A flexible and customizable React notification/toast component with TypeScript support

204 lines (201 loc) 9.37 kB
import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; import React, { createContext, useState, useRef, useContext } from 'react'; const GNotificationContext = createContext(undefined); let toastId = 0; function getToastStyle(toast) { let baseStyle; if (toast.type === "custom" && toast.style) { baseStyle = toast.style; } else { switch (toast.type) { case "success": baseStyle = { background: "#d1fae5", color: "#065f46", }; break; case "danger": baseStyle = { background: "#fee2e2", color: "#991b1b", }; break; case "warning": baseStyle = { background: "#fef9c3", color: "#92400e", }; break; case "info": baseStyle = { background: "#dbeafe", color: "#1e40af", }; break; default: baseStyle = { background: "#333", color: "#fff", }; } } // Animasyon ekle if (toast.cssAnimation) { baseStyle = { ...baseStyle, animationName: toast.cssAnimation.animationName, animationDuration: toast.cssAnimation.animationDuration || "0.5s", animationFillMode: "both", }; } return baseStyle; } const GNotificationProvider = ({ children, position = "top-right", maxVisibleNotification = 4, zIndex = 9999, progressBar = false }) => { const [toasts, setToasts] = useState([]); const [queue, setQueue] = useState([]); const [progressStates, setProgressStates] = useState({}); const intervals = useRef({}); const g_toast = (toast) => { const id = toastId++; const newToast = { ...toast, id }; setToasts((prev) => { if (prev.length < maxVisibleNotification) { if (!toast.persistent) { setProgressStates((ps) => ({ ...ps, [id]: 100 })); const duration = toast.duration ?? 3000; const start = Date.now(); intervals.current[id] = setInterval(() => { setProgressStates((ps) => { const elapsed = Date.now() - start; const percent = Math.max(0, 100 - (elapsed / duration) * 100); return { ...ps, [id]: percent }; }); }, 50); setTimeout(() => { clearInterval(intervals.current[id]); setProgressStates((ps) => { const copy = { ...ps }; delete copy[id]; return copy; }); handleClose(id); }, duration); } return [...prev, newToast]; } else { setQueue((q) => [...q, newToast]); return prev; } }); }; const handleClose = (id) => { clearInterval(intervals.current[id]); setProgressStates((ps) => { const copy = { ...ps }; delete copy[id]; return copy; }); setToasts((prev) => { const filtered = prev.filter((t) => t.id !== id); if (queue.length > 0 && filtered.length < maxVisibleNotification) { const next = queue[0]; setQueue((q) => q.slice(1)); if (!next.persistent) { setProgressStates((ps) => ({ ...ps, [next.id]: 100 })); const duration = next.duration ?? 3000; const start = Date.now(); intervals.current[next.id] = setInterval(() => { setProgressStates((ps) => { const elapsed = Date.now() - start; const percent = Math.max(0, 100 - (elapsed / duration) * 100); return { ...ps, [next.id]: percent }; }); }, 50); setTimeout(() => { clearInterval(intervals.current[next.id]); setProgressStates((ps) => { const copy = { ...ps }; delete copy[next.id]; return copy; }); handleClose(next.id); }, duration); } return [...filtered, next]; } return filtered; }); }; // onPress için basılı tutma algılama const handleMouseDown = (toast) => { if (toast.onPress) { toast._pressTimeout = setTimeout(() => { toast.onPress && toast.onPress(); }, 400); // 400ms basılı tutunca tetiklenir } }; const handleMouseUp = (toast) => { if (toast._pressTimeout) { clearTimeout(toast._pressTimeout); toast._pressTimeout = undefined; } }; // Pozisyona göre container style const getContainerStyle = () => { const base = { position: "fixed", zIndex, pointerEvents: "none", }; switch (position) { case "top-left": return { ...base, top: 20, left: 20, alignItems: "flex-start" }; case "top-right": return { ...base, top: 20, right: 20, alignItems: "flex-end" }; case "bottom-left": return { ...base, bottom: 20, left: 20, alignItems: "flex-start" }; case "bottom-right": return { ...base, bottom: 20, right: 20, alignItems: "flex-end" }; default: return { ...base, top: 20, right: 20, alignItems: "flex-end" }; } }; return (jsxs(GNotificationContext.Provider, { value: { g_toast }, children: [children, jsx("div", { style: getContainerStyle(), children: toasts.map((toast) => (jsxs(React.Fragment, { children: [toast.cssAnimation && (jsx("style", { children: `@keyframes ${toast.cssAnimation.animationName} {${toast.cssAnimation.keyframes}}` })), jsx("div", { style: { ...getToastStyle(toast), padding: "16px", borderRadius: "8px", marginBottom: "10px", minWidth: "250px", boxShadow: "0 2px 8px rgba(0,0,0,0.15)", position: "relative", cursor: toast.onClick || toast.onPress ? "pointer" : undefined, pointerEvents: "auto", }, onClick: () => { toast.onClick && toast.onClick(); if (toast.link) { window.open(toast.link, "_blank"); } }, onMouseDown: () => handleMouseDown(toast), onMouseUp: () => handleMouseUp(toast), onMouseLeave: () => handleMouseUp(toast), children: toast.customRender ? (toast.customRender({ close: () => handleClose(toast.id) })) : (jsxs(Fragment, { children: [toast.closeButton && (jsx("button", { onClick: (e) => { e.stopPropagation(); handleClose(toast.id); }, style: { position: "absolute", top: 8, right: 8, background: "transparent", border: "none", color: "inherit", fontSize: 18, cursor: "pointer", }, "aria-label": "Kapat", children: toast.closeButtonContent || "×" })), toast.icon && jsx("span", { style: { marginRight: 8 }, children: toast.icon }), toast.title && jsx("strong", { style: { display: "block" }, children: toast.title }), jsx("span", { children: toast.content }), progressBar && !toast.persistent && (jsx("div", { style: { position: "absolute", left: 0, bottom: 0, height: 4, width: "100%", background: "rgba(255,255,255,0.2)" }, children: jsx("div", { style: { height: "100%", width: `${progressStates[toast.id] ?? 100}%`, background: "#fff", opacity: 0.7, transition: "width 0.1s linear" } }) }))] })) })] }, toast.id))) })] })); }; const useGNotification = () => { const ctx = useContext(GNotificationContext); if (!ctx) throw new Error("useGNotification must be used within a GNotificationProvider"); return ctx; }; export { GNotificationProvider, useGNotification }; //# sourceMappingURL=index.esm.js.map