fix: correct submenu positioning

This commit is contained in:
thecodrr
2022-02-04 23:21:29 +05:00
parent 3015a60764
commit ae4cc13804
2 changed files with 45 additions and 15 deletions

View File

@@ -10,6 +10,7 @@ import React, {
import { Flex, Box, Text, Button } from "rebass"; import { Flex, Box, Text, Button } from "rebass";
import useMobile from "../../utils/use-mobile"; import useMobile from "../../utils/use-mobile";
import { AnimatedFlex } from "../animated"; import { AnimatedFlex } from "../animated";
import { getPosition } from "../../hooks/use-menu";
function useMenuFocus(items) { function useMenuFocus(items) {
const [focusIndex, setFocusIndex] = useState(-1); const [focusIndex, setFocusIndex] = useState(-1);
@@ -121,8 +122,7 @@ function MenuContainer({ title, children }) {
borderRadius: "default", borderRadius: "default",
boxShadow: "0px 0px 10px 0px #00000022", boxShadow: "0px 0px 10px 0px #00000022",
border: "1px solid var(--border)", border: "1px solid var(--border)",
minWidth: 200, width: 220,
maxWidth: 500,
}} }}
> >
{title && ( {title && (
@@ -217,16 +217,35 @@ function MenuItem({ item, data, isFocused, isSubmenuOpen, onClose, onHover }) {
isPremium, isPremium,
} = item; } = item;
const itemRef = useRef(); const itemRef = useRef();
const subMenuRef = useRef();
useEffect(() => { useEffect(() => {
if (isFocused) itemRef.current?.focus(); if (isFocused) itemRef.current?.focus();
}, [isFocused]); }, [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( const onAction = useCallback(
(e) => { (e) => {
e.stopPropagation(); e.stopPropagation();
if (onClose) onClose(); if (onClose) onClose();
onClick(data, item); if (onClick) onClick(data, item);
}, },
[onClick, onClose, item, data] [onClick, onClose, item, data]
); );
@@ -249,7 +268,7 @@ function MenuItem({ item, data, isFocused, isSubmenuOpen, onClose, onHover }) {
as="li" as="li"
flexDirection={"column"} flexDirection={"column"}
flex={1} flex={1}
sx={{ position: "relative" }} // sx={{ position: "relative" }}
onMouseOver={onHover} onMouseOver={onHover}
> >
<Button <Button
@@ -290,12 +309,14 @@ function MenuItem({ item, data, isFocused, isSubmenuOpen, onClose, onHover }) {
{hasSubmenu && <ChevronRight size={14} />} {hasSubmenu && <ChevronRight size={14} />}
</Flex> </Flex>
</Button> </Button>
{hasSubmenu && isSubmenuOpen && ( {hasSubmenu && (
<Flex <Flex
ref={subMenuRef}
style={{ visibility: "hidden" }}
sx={{ sx={{
position: "absolute", position: "absolute",
top: 0, // top: 0,
left: itemRef.current?.offsetWidth, // left: itemRef.current?.offsetWidth,
}} }}
> >
<Menu <Menu

View File

@@ -46,28 +46,32 @@ export function useMenu() {
* @returns * @returns
*/ */
export function getPosition(element, relativeTo = "mouse", location) { export function getPosition(element, relativeTo = "mouse", location) {
const { x, y, width, height } = const { x, y, width, height, actualX, actualY } =
relativeTo === "mouse" ? mousePosition : getElementPosition(relativeTo); relativeTo === "mouse" ? mousePosition : getElementPosition(relativeTo);
const elementWidth = element.offsetWidth; const elementWidth = element.offsetWidth;
const elementHeight = element.offsetHeight; const elementHeight = element.offsetHeight;
const windowWidth = window.document.body.offsetWidth; const windowWidth = window.innerHeight;
const windowHeight = window.document.body.offsetHeight; const windowHeight = window.innerHeight - 50;
let position = { top: undefined, left: undefined }; let position = { top: undefined, left: undefined };
if (windowWidth - x < elementWidth) { if (windowWidth - actualX < elementWidth) {
const xDiff = actualX - x;
position.left = windowWidth - elementWidth; position.left = windowWidth - elementWidth;
position.left -= xDiff;
} else { } else {
position.left = x; position.left = x;
if (location === "right") position.top += width; if (location === "right") position.left += width;
else if (location === "left") position.top -= width; else if (location === "left") position.left -= width;
} }
if (windowHeight - y < elementHeight) { if (windowHeight - actualY < elementHeight) {
const yDiff = actualY - y;
position.top = windowHeight - elementHeight; position.top = windowHeight - elementHeight;
position.top -= yDiff;
} else { } else {
position.top = y; position.top = y;
if (location === "below") position.top += height; if (location === "below") position.top += height;
@@ -98,6 +102,8 @@ function getMousePosition(e) {
return { return {
x: posx, x: posx,
y: posy, y: posy,
actualY: posy,
actualX: posx,
}; };
} }
@@ -106,11 +112,14 @@ function getMousePosition(e) {
* @param {HTMLElement} element * @param {HTMLElement} element
*/ */
function getElementPosition(element) { function getElementPosition(element) {
const rect = element.getBoundingClientRect();
return { return {
x: element.offsetLeft, x: element.offsetLeft,
y: element.offsetTop, y: element.offsetTop,
width: element.offsetWidth, width: element.offsetWidth,
height: element.offsetHeight, height: element.offsetHeight,
actualY: rect.y,
actualX: rect.x,
}; };
} }
@@ -167,7 +176,7 @@ function mapMenuItems(items, data) {
color, color,
iconColor, iconColor,
items, items: hasSubmenu ? mapMenuItems(items, data) : [],
}; };
prev.push(menuItem); prev.push(menuItem);