import { useNotificationService, useNotifications } from 'onekijs-framework'; import React, { useRef } from 'react'; import ReactDOM from 'react-dom'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; import { addClassname } from '../../../utils/style'; import { NotificationsProps } from '../typings'; import DefaultNotificationComponent from './NotificationComponent'; const NotificationsComponent: React.FC = ({ className, topics, NotificationComponent = DefaultNotificationComponent, max, animate = 500, position = 'bottom-right', showTimer, }) => { const classNames = addClassname('o-notifications', className); const translateX = position === 'top-right' || position === 'bottom-right' ? '250px' : '-250px'; const notificationService = useNotificationService(); let notifications = useNotifications(topics); if (max) { notifications = notifications.slice(0, max); } const bodyOverflowXRef = useRef('unset'); const bodyOverflowYRef = useRef('unset'); const transitionRefs = useRef(new Map>()); const getTransitionRef = (id: string) => { let ref = transitionRefs.current.get(id); if (!ref) { ref = React.createRef(); transitionRefs.current.set(id, ref); } return ref; }; const onExiting = (node: HTMLElement) => { bodyOverflowXRef.current = document.body.style.overflowX || document.body.style.overflow || 'unset'; bodyOverflowYRef.current = document.body.style.overflowY || document.body.style.overflow || 'unset'; node.style.transform = 'translateX(0px)'; node.style.opacity = '1'; node.style.transition = `transform ${animate}ms, opacity ${animate}ms, height ${animate / 2}ms ease-out ${ animate / 2 }ms`; node.style.height = `${node.getBoundingClientRect().height}px`; if (document.body.scrollWidth <= document.body.clientWidth) { document.body.style.overflowX = 'hidden'; } setTimeout(() => { node.style.transform = `translateX(${translateX})`; node.style.opacity = '0'; node.style.overflowY = 'hidden'; node.style.height = '0'; }, 0); }; const onExited = () => { document.body.style.overflowX = bodyOverflowXRef.current; document.body.style.overflowY = bodyOverflowYRef.current; }; const onEntering = (node: HTMLElement) => { bodyOverflowXRef.current = document.body.style.overflowX || document.body.style.overflow || 'unset'; bodyOverflowYRef.current = document.body.style.overflowY || document.body.style.overflow || 'unset'; if (document.body.scrollWidth <= document.body.clientWidth) { document.body.style.overflowX = 'hidden'; } if (document.body.scrollHeight <= document.body.clientHeight) { document.body.style.overflowY = 'hidden'; } node.style.opacity = '0'; node.style.transition = `height ${animate}ms, transform ${animate / 2}ms`; const currentHeight = node.getBoundingClientRect().height; if (position === 'bottom-right' || position === 'bottom-left') { node.style.height = '0'; } else { node.style.transform = `translateX(${translateX})`; } setTimeout(() => { if (position === 'bottom-right' || position === 'bottom-left') { node.style.transform = 'translateY(0px)'; node.style.height = `${currentHeight}px`; } else { node.style.transform = 'translateX(0px)'; } node.style.opacity = '1'; }, 0); }; const onEntered = () => { document.body.style.overflowX = bodyOverflowXRef.current; document.body.style.overflowY = bodyOverflowYRef.current; }; const element = (
{notifications.length > 1 && (
notificationService.clearAll()}> Close all
)} {notifications.map((notification, index) => { const transitionRef = getTransitionRef(`o-notification-${notification.id}`); return ( transitionRef.current && onEntering(transitionRef.current)} onExiting={() => transitionRef.current && onExiting(transitionRef.current)} onEntered={onEntered} onExited={onExited} >
); })}
); return <>{ReactDOM.createPortal(element, document.body)}; }; export default NotificationsComponent;