g-notification
Version:
A flexible and customizable React notification/toast component with TypeScript support
204 lines (201 loc) • 9.37 kB
JavaScript
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