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) => (
);
}
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 && (
)}
);
}