Updates
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import React, { DetailedHTMLProps, HTMLAttributes } from "react";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import Card from "./Card";
|
||||
import { X } from "lucide-react";
|
||||
import ReactDOM from "react-dom";
|
||||
import Span from "../layout/Span";
|
||||
|
||||
export const ToastStyles = ["normal", "success", "error"] as const;
|
||||
export const ToastColors = ToastStyles;
|
||||
|
||||
export type TWUIToastProps = DetailedHTMLProps<
|
||||
HTMLAttributes<HTMLDivElement>,
|
||||
HTMLDivElement
|
||||
> & {
|
||||
open?: boolean;
|
||||
setOpen?: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
closeDispatch?: (open?: boolean) => void;
|
||||
closeDelay?: number;
|
||||
color?: (typeof ToastStyles)[number];
|
||||
};
|
||||
|
||||
let interval: any;
|
||||
let timeout: any;
|
||||
|
||||
/**
|
||||
* # Toast Component
|
||||
* @className twui-toast-root
|
||||
* @className twui-toast
|
||||
* @className twui-toast-success
|
||||
* @className twui-toast-error
|
||||
*/
|
||||
export default function Toast({
|
||||
open,
|
||||
setOpen,
|
||||
closeDelay = 4000,
|
||||
color,
|
||||
closeDispatch,
|
||||
...props
|
||||
}: TWUIToastProps) {
|
||||
const [ready, setReady] = React.useState(false);
|
||||
const IDName = "twui-toast-root";
|
||||
|
||||
React.useEffect(() => {
|
||||
const toastRoot = document.getElementById(IDName);
|
||||
|
||||
if (toastRoot) {
|
||||
setReady(true);
|
||||
} else {
|
||||
const newToastRootEl = document.createElement("div");
|
||||
newToastRootEl.id = IDName;
|
||||
document.body.appendChild(newToastRootEl);
|
||||
setReady(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!ready || !open) return;
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
setOpen?.(false);
|
||||
closeDispatch?.(open);
|
||||
}, closeDelay);
|
||||
|
||||
return function () {
|
||||
setOpen?.(false);
|
||||
closeDispatch?.(open);
|
||||
};
|
||||
}, [ready, open]);
|
||||
|
||||
if (!ready) return null;
|
||||
if (!open) return null;
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
<Card
|
||||
{...props}
|
||||
className={twMerge(
|
||||
"fixed bottom-4 right-4 z-[250] border-none",
|
||||
"pl-6 pr-8 py-4 bg-primary dark:bg-primary-dark",
|
||||
color == "success"
|
||||
? "bg-success-dark dark:bg-success-dark twui-toast-success"
|
||||
: color == "error"
|
||||
? "bg-error dark:bg-error-dark twui-toast-error"
|
||||
: "",
|
||||
props.className,
|
||||
"twui-toast",
|
||||
)}
|
||||
onMouseEnter={() => {
|
||||
window.clearTimeout(timeout);
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
timeout = setTimeout(() => {
|
||||
setOpen?.(false);
|
||||
closeDispatch?.(open);
|
||||
}, closeDelay);
|
||||
}}
|
||||
>
|
||||
<Span
|
||||
className={twMerge(
|
||||
"absolute top-2 right-2 z-[100] cursor-pointer",
|
||||
"text-white",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setOpen?.(false);
|
||||
closeDispatch?.(open);
|
||||
}}
|
||||
>
|
||||
<X size={15} />
|
||||
</Span>
|
||||
<Span className={twMerge("text-white! font-semibold")}>
|
||||
{props.children}
|
||||
</Span>
|
||||
</Card>,
|
||||
document.getElementById(IDName) as HTMLElement,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user