Skip to content

fix: Improperly aligned unfolding sub-items in context menu in data browser #2726

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: alpha
Choose a base branch
from
63 changes: 48 additions & 15 deletions src/components/ContextMenu/ContextMenu.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,40 +9,68 @@ 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
};
}

return { x: 0, y };
}
};

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,
Expand Down Expand Up @@ -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 && <span> - {item.subtext}</span>}
Expand All @@ -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]);
Expand All @@ -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();
Expand All @@ -118,8 +148,6 @@ const ContextMenu = ({ x, y, items }) => {
};
});

//#endregion

if (!visible) {
return null;
}
Expand All @@ -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 (
<MenuSection
key={`section-${position}-${level}`}
path={path}
setPath={setPath}
level={level}
items={getItemsFromLevel(level)}
items={itemsForLevel}
hide={hide}
parentItemCount={parentItemCount}
/>
);
})}
Expand Down
Loading