import React, { useRef, createContext, RefObject, useState, useContext, useEffect } from 'react'; import { Cross, AngleLeft } from 'shared/icons'; import useOnOutsideClick from 'shared/hooks/onOutsideClick'; import { createPortal } from 'react-dom'; import NOOP from 'shared/utils/noop'; import produce from 'immer'; import { Container, ContainerDiamond, Header, HeaderTitle, Content, CloseButton, PreviousButton, Wrapper, } from './Styles'; type PopupContextState = { show: (target: RefObject, content: JSX.Element, width?: string | number) => void; setTab: (newTab: number, width?: number | string) => void; getCurrentTab: () => number; hide: () => void; }; type PopupProps = { title: string | null; onClose?: () => void; tab: number; }; type PopupContainerProps = { top: number; left: number; invert: boolean; invertY: boolean; onClose: () => void; width?: string | number; }; const PopupContainer: React.FC = ({ width, top, left, onClose, children, invert, invertY }) => { const $containerRef = useRef(null); const [currentTop, setCurrentTop] = useState(top); useOnOutsideClick($containerRef, true, onClose, null); return ( {children} ); }; PopupContainer.defaultProps = { width: 316, }; const PopupContext = createContext({ show: NOOP, setTab: NOOP, getCurrentTab: () => 0, hide: NOOP, }); export const usePopup = () => { const ctx = useContext(PopupContext); return { showPopup: ctx.show, setTab: ctx.setTab, getCurrentTab: ctx.getCurrentTab, hidePopup: ctx.hide }; }; type PopupState = { isOpen: boolean; left: number; top: number; invertY: boolean; invert: boolean; currentTab: number; previousTab: number; content: JSX.Element | null; width?: string | number; }; const { Provider, Consumer } = PopupContext; const canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement); const defaultState = { isOpen: false, left: 0, top: 0, invert: false, invertY: false, currentTab: 0, previousTab: 0, content: null, }; export const PopupProvider: React.FC = ({ children }) => { const [currentState, setState] = useState(defaultState); const show = (target: RefObject, content: JSX.Element, width?: number | string) => { if (target && target.current) { const bounds = target.current.getBoundingClientRect(); let top = bounds.top + bounds.height; let invertY = false; if (window.innerHeight / 2 < top) { top = window.innerHeight - bounds.top; invertY = true; } if (bounds.left + 304 + 30 > window.innerWidth) { setState({ isOpen: true, left: bounds.left + bounds.width, top, invertY, invert: true, currentTab: 0, previousTab: 0, content, width: width ?? 316, }); } else { setState({ isOpen: true, left: bounds.left, top, invert: false, invertY, currentTab: 0, previousTab: 0, content, width: width ?? 316, }); } } }; const hide = () => { setState({ isOpen: false, left: 0, top: 0, invert: true, invertY: false, currentTab: 0, previousTab: 0, content: null, }); }; const portalTarget = canUseDOM ? document.body : null; // appease flow const setTab = (newTab: number, width?: number | string) => { const newWidth = width ?? currentState.width; setState((prevState: PopupState) => { return { ...prevState, previousTab: currentState.currentTab, currentTab: newTab, width: newWidth, }; }); }; const getCurrentTab = () => { return currentState.currentTab; }; return ( {portalTarget && currentState.isOpen && createPortal( setState(defaultState)} width={currentState.width ?? 316} > {currentState.content} , portalTarget, )} {children} ); }; type Props = { title: string | null; top: number; left: number; onClose: () => void; onPrevious?: () => void | null; noHeader?: boolean | null; width?: string | number; }; const PopupMenu: React.FC = ({ width, title, top, left, onClose, noHeader, children, onPrevious }) => { const $containerRef = useRef(null); useOnOutsideClick($containerRef, true, onClose, null); return ( {onPrevious && ( )} {noHeader ? ( onClose()}> ) : (
{title} onClose()}>
)} {children}
); }; export const Popup: React.FC = ({ title, onClose, tab, children }) => { const { getCurrentTab, setTab } = usePopup(); if (getCurrentTab() !== tab) { return null; } return ( <> {tab > 0 && ( { setTab(0); }} > )} {title && (
{title}
)} {onClose && ( onClose()}> )} {children}
); }; export default PopupMenu;