import { useAnimation } from "framer-motion"; import { Check, ChevronRight, Pro } from "../icons"; import React, { useCallback, useEffect, useMemo, useRef, useState, } from "react"; import { Flex, Box, Text, Button } from "rebass"; import useMobile from "../../utils/use-mobile"; import { AnimatedFlex } from "../animated"; import { getPosition } from "../../hooks/use-menu"; function useMenuFocus(items) { const [focusIndex, setFocusIndex] = useState(-1); const [isSubmenuOpen, setIsSubmenuOpen] = useState(false); const onKeyDown = useCallback( (e) => { const isSeperator = (i) => items && items[i]?.type === "seperator"; const moveDown = (i) => (i < items.length - 1 ? ++i : 0); const moveUp = (i) => (i > 0 ? --i : items.length - 1); const hasSubmenu = (i) => items && items[i]?.hasSubmenu; const openSubmenu = (index) => { if (!hasSubmenu(index)) return; setIsSubmenuOpen(true); }; const closeSubmenu = (index) => { if (!hasSubmenu(index)) return; setIsSubmenuOpen(false); }; setFocusIndex((i) => { let nextIndex = i; switch (e.key) { case "ArrowUp": if (isSubmenuOpen) break; nextIndex = moveUp(i); if (isSeperator(nextIndex)) nextIndex = moveUp(nextIndex); break; case "ArrowDown": if (isSubmenuOpen) break; nextIndex = moveDown(i); if (isSeperator(nextIndex)) nextIndex = moveDown(nextIndex); break; case "ArrowRight": openSubmenu(i); break; case "ArrowLeft": closeSubmenu(i); break; default: break; } return nextIndex; }); }, [items, isSubmenuOpen] ); useEffect(() => { window.addEventListener("keydown", onKeyDown); return () => { window.removeEventListener("keydown", onKeyDown); }; }, [onKeyDown]); return [focusIndex, setFocusIndex, isSubmenuOpen, setIsSubmenuOpen]; } function Menu({ items, data, closeMenu }) { const isMobile = useMobile(); const [focusIndex, setFocusIndex, isSubmenuOpen, setIsSubmenuOpen] = useMenuFocus(items); const Container = useMemo( () => (isMobile ? MobileMenuContainer : MenuContainer), [isMobile] ); return ( {items.map((item, index) => ( { setFocusIndex(index); setIsSubmenuOpen((state) => { return item.items?.length ? true : state ? false : state; }); }} /> ))} ); } export default React.memo(Menu); function MenuContainer({ title, children }) { return ( {title && ( {title} )} {children} ); } function MobileMenuContainer({ style, id, state, title, children }) { const animation = useAnimation(); useEffect(() => { if (state === "open") { animation.start({ y: 0 }); const menu = document.getElementById(id); menu.style.top = 0; menu.style.left = 0; } else { animation.start({ y: 500 }); } }, [state, animation, id]); return ( {title || "Properties"} {children} ); } function MenuItem({ item, data, isFocused, isSubmenuOpen, onClose, onHover }) { const { title, key, onClick, color, items, icon: Icon, iconColor, type, tooltip, isDisabled, isChecked, hasSubmenu, isPremium, } = item; const itemRef = useRef(); const subMenuRef = useRef(); useEffect(() => { if (isFocused) itemRef.current?.focus(); }, [isFocused]); useEffect(() => { if (!subMenuRef.current) return; if (!isSubmenuOpen) { subMenuRef.current.style.visibility = "hidden"; return; } const { top, left } = getPosition( subMenuRef.current, itemRef.current, "right" ); subMenuRef.current.style.visibility = "visible"; subMenuRef.current.style.top = `${top}px`; subMenuRef.current.style.left = `${left}px`; }, [isSubmenuOpen]); const onAction = useCallback( (e) => { e.stopPropagation(); if (onClose) onClose(); if (onClick) onClick(data, item); }, [onClick, onClose, item, data] ); if (type === "seperator") return ( ); return ( {hasSubmenu && ( )} ); }