Skip to content

Commit

Permalink
feat: portal logic
Browse files Browse the repository at this point in the history
  • Loading branch information
mvriu5 committed Aug 12, 2024
1 parent 31aebe2 commit a5ac790
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 39 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "griller",
"license": "MIT",
"version": "1.0.15",
"version": "1.0.16",
"private": false,
"repository": {
"url": "https://github.com/mvriu5/griller"
Expand Down
7 changes: 0 additions & 7 deletions src/component/toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {X} from "lucide-react";
import {AnimatePresence, motion} from "framer-motion";
import clsx, {ClassValue} from "clsx";
import {twMerge} from "tailwind-merge";
import ReactDOM from "react-dom";

export const cn = (...classes: ClassValue[]) => twMerge(clsx(classes));

Expand All @@ -22,10 +21,6 @@ export const positionClasses = (position: Position) => {
return '';
}

const ToastPortal: React.FC<{ children: ReactNode }> = ({ children }) => {
return ReactDOM.createPortal(children, document.body);
}

interface ToastProps extends HTMLAttributes<HTMLDivElement> {
id: string;
title: string;
Expand Down Expand Up @@ -108,7 +103,6 @@ const Toast: React.FC<ToastProps & {
};

return (
<ToastPortal>
<AnimatePresence onExitComplete={() => removeToast(id)}>
{visible && (
<motion.div
Expand Down Expand Up @@ -177,7 +171,6 @@ const Toast: React.FC<ToastProps & {
</motion.div>
)}
</AnimatePresence>
</ToastPortal>
);
};

Expand Down
65 changes: 34 additions & 31 deletions src/component/toaster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React, {createContext, ReactNode, useCallback, useContext, useMemo, useRef, useState} from 'react';
import {Position, positionClasses, Toast, ToastProps} from './toast';
import {AnimatePresence, motion} from "framer-motion";
import { ToastPortal } from './toastportal';

interface ToastContextType {
addToast: (props: Omit<ToastProps, 'id'>) => string;
Expand Down Expand Up @@ -84,37 +85,39 @@ export const Toaster: React.FC<ToasterProps> = ({ children, layout = "stack" })
return (
<ToastContext.Provider value={{ addToast, removeToast }}>
{children}
{Object.entries(groupedToasts).map(([position, positionToasts]) => (
<motion.div
key={position}
className={`fixed z-50 flex flex-col ${positionClasses(position as Position)}`}
variants={containerVariants}
initial="initial"
whileHover="hover"
whileTap="hover"
onMouseEnter={() => setIsPaused(true)}
onMouseLeave={() => setIsPaused(false)}
>
<AnimatePresence>
{positionToasts.map((toast) => {
return(
<motion.div
key={toast.id}
layout
variants={layout === "stack" ? childStackVariants : childExpandVariants}
custom={position as Position}
>
<Toast key={toast.id}
removeToast={removeToast}
isPaused={isPaused}
{...toast}
/>
</motion.div>
)
})}
</AnimatePresence>
</motion.div>
))}
<ToastPortal>
{Object.entries(groupedToasts).map(([position, positionToasts]) => (
<motion.div
key={position}
className={`fixed z-50 flex flex-col ${positionClasses(position as Position)}`}
variants={containerVariants}
initial="initial"
whileHover="hover"
whileTap="hover"
onMouseEnter={() => setIsPaused(true)}
onMouseLeave={() => setIsPaused(false)}
>
<AnimatePresence>
{positionToasts.map((toast) => {
return(
<motion.div
key={toast.id}
layout
variants={layout === "stack" ? childStackVariants : childExpandVariants}
custom={position as Position}
>
<Toast key={toast.id}
removeToast={removeToast}
isPaused={isPaused}
{...toast}
/>
</motion.div>
)
})}
</AnimatePresence>
</motion.div>
))}
</ToastPortal>
</ToastContext.Provider>
);
};
Expand Down
30 changes: 30 additions & 0 deletions src/component/toastportal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import ReactDOM from 'react-dom';
import React, {ReactNode, useEffect, useState} from 'react';

interface ToastPortalProps {
children: ReactNode;
}

export const ToastPortal: React.FC<ToastPortalProps> = ({ children }) => {
const [portalElement, setPortalElement] = useState<HTMLElement | null>(null);

useEffect(() => {
let element = document.getElementById('toast-portal-root');
if (!element) {
element = document.createElement('div');
element.id = 'toast-portal-root';
document.body.appendChild(element);
}
setPortalElement(element);

return () => {
if (element && element.parentNode) {
element.parentNode.removeChild(element);
}
};
}, []);

if (!portalElement) return null;

return ReactDOM.createPortal(children, portalElement);
};

0 comments on commit a5ac790

Please sign in to comment.