diff --git a/src/components/ContextMenu/ContextMenu.react.js b/src/components/ContextMenu/ContextMenu.react.js
index fdd309c6e4..4099159be7 100644
--- a/src/components/ContextMenu/ContextMenu.react.js
+++ b/src/components/ContextMenu/ContextMenu.react.js
@@ -9,20 +9,43 @@ import PropTypes from 'lib/PropTypes';
import React, { useState, useEffect, useRef } from 'react';
import styles from 'components/ContextMenu/ContextMenu.scss';
-const getPositionToFitVisibleScreen = ref => {
+const getPositionToFitVisibleScreen = (ref, offset = 0, mainItemCount = 0, subItemCount = 0) => {
if (ref.current) {
const elBox = ref.current.getBoundingClientRect();
- const y = elBox.y + elBox.height < window.innerHeight ? 0 : 0 - elBox.y + 100;
+ let y = 0;
+
+ const footerHeight = 50;
+ const lowerLimit = window.innerHeight - footerHeight;
+ const upperLimit = 0;
+
+ if (elBox.bottom > lowerLimit) {
+ y = lowerLimit - elBox.bottom;
+ } else if (elBox.top < upperLimit) {
+ y = upperLimit - elBox.top;
+ }
+
+ const projectedTop = elBox.top + y + offset;
+ const projectedBottom = projectedTop + elBox.height;
+
+ const shouldApplyOffset = subItemCount > mainItemCount;
+ if (shouldApplyOffset && projectedTop >= upperLimit && projectedBottom <= lowerLimit) {
+ y += offset;
+ }
- // If there's a previous element show current next to it.
- // Try on right side first, then on left if there's no place.
const prevEl = ref.current.previousSibling;
if (prevEl) {
const prevElBox = prevEl.getBoundingClientRect();
+ const prevElStyle = window.getComputedStyle(prevEl);
+ const prevElTop = parseInt(prevElStyle.top, 10);
+
+ if (!shouldApplyOffset) {
+ y = prevElTop + offset;
+ }
+
const showOnRight = prevElBox.x + prevElBox.width + elBox.width < window.innerWidth;
return {
x: showOnRight ? prevElBox.width : -elBox.width,
- y,
+ y
};
}
@@ -30,19 +53,24 @@ const getPositionToFitVisibleScreen = ref => {
}
};
-const MenuSection = ({ level, items, path, setPath, hide }) => {
+const MenuSection = ({ level, items, path, setPath, hide, parentItemCount = 0 }) => {
const sectionRef = useRef(null);
const [position, setPosition] = useState();
useEffect(() => {
- const newPosition = getPositionToFitVisibleScreen(sectionRef);
+ const newPosition = getPositionToFitVisibleScreen(
+ sectionRef,
+ path[level] * 30,
+ parentItemCount,
+ items.length
+ );
newPosition && setPosition(newPosition);
}, [sectionRef]);
const style = position
? {
left: position.x,
- top: position.y + path[level] * 30,
+ top: position.y,
maxHeight: '80vh',
overflowY: 'scroll',
opacity: 1,
@@ -79,6 +107,10 @@ const MenuSection = ({ level, items, path, setPath, hide }) => {
item.callback && item.callback();
hide();
}}
+ onMouseEnter={() => {
+ const newPath = path.slice(0, level + 1);
+ setPath(newPath);
+ }}
>
{item.text}
{item.subtext && - {item.subtext}}
@@ -92,6 +124,8 @@ const MenuSection = ({ level, items, path, setPath, hide }) => {
const ContextMenu = ({ x, y, items }) => {
const [path, setPath] = useState([0]);
const [visible, setVisible] = useState(true);
+ const menuRef = useRef(null);
+
useEffect(() => {
setVisible(true);
}, [items]);
@@ -101,10 +135,6 @@ const ContextMenu = ({ x, y, items }) => {
setPath([0]);
};
- //#region Closing menu after clicking outside it
-
- const menuRef = useRef(null);
-
function handleClickOutside(event) {
if (menuRef.current && !menuRef.current.contains(event.target)) {
hide();
@@ -118,8 +148,6 @@ const ContextMenu = ({ x, y, items }) => {
};
});
- //#endregion
-
if (!visible) {
return null;
}
@@ -142,14 +170,19 @@ const ContextMenu = ({ x, y, items }) => {
}}
>
{path.map((position, level) => {
+ const itemsForLevel = getItemsFromLevel(level);
+ const parentItemCount =
+ level === 0 ? items.length : getItemsFromLevel(level - 1).length;
+
return (
);
})}