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