fix(context-menu): Move the reposition logic from the context menu to the dialog component. (#5060)

This commit is contained in:
Joe Boccanfuso 2025-05-23 22:12:08 -04:00 committed by GitHub
parent fa9bbe3afc
commit cb814e6a60
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 509 additions and 496 deletions

948
bun.lock

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../components/Dialog/Dialog';
import { cn } from '../lib/utils';
@ -34,6 +34,37 @@ const ManagedDialog: React.FC<ManagedDialogProps> = ({
unstyled,
containerClassName,
}) => {
// When a default position is provided, the assumption is that the position
// is respected unless the position chosen results in the dialog being
// clipped off-screen (i.e. part of the dialog is rendered outside the browser
// window). When the dialog is clipped it will be repositioned about
// the default position such that it is no longer clipped. To avoid a flash
// during the reposition, we initially hide the dialog.
const [contentVisibility, setContentVisibility] = useState(
defaultPosition ? 'invisible' : 'visible'
);
// The callback to reposition an explicitly positioned dialog. Note that
// if the dialog is larger than the window (in either dimension), the
// dialog will still be clipped in some manner.
const contentRef = useCallback(
contentNode => {
if (!contentNode) {
return;
}
const boundingClientRect = contentNode.getBoundingClientRect();
if (boundingClientRect.bottom > window.innerHeight) {
defaultPosition.y = defaultPosition.y - boundingClientRect.height;
}
if (boundingClientRect.right > window.innerWidth) {
defaultPosition.x = defaultPosition.x - boundingClientRect.width;
}
setContentVisibility('visible');
},
[defaultPosition]
);
return (
<Dialog
open={isOpen}
@ -49,7 +80,8 @@ const ManagedDialog: React.FC<ManagedDialogProps> = ({
showOverlay={showOverlay}
>
<DialogContent
className={cn(unstyled ? 'p-0' : '', containerClassName)}
ref={contentRef}
className={cn(unstyled ? 'p-0' : '', containerClassName, contentVisibility)}
unstyled={unstyled}
style={{
...(defaultPosition
@ -60,6 +92,7 @@ const ManagedDialog: React.FC<ManagedDialogProps> = ({
transform: 'translate(0, 0)',
margin: 0,
animation: 'none',
transition: 'none',
}
: {}),
}}

View File

@ -1,33 +1,15 @@
import React, { useEffect, useRef } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import Typography from '../Typography';
import { Icons } from '@ohif/ui-next';
const ContextMenu = ({ items, ...props }) => {
const contextMenuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!contextMenuRef?.current) {
return;
}
const contextMenu = contextMenuRef.current;
const boundingClientRect = contextMenu.getBoundingClientRect();
if (boundingClientRect.bottom > window.innerHeight) {
props.defaultPosition.y = props.defaultPosition.y - boundingClientRect.height;
}
if (boundingClientRect.right > window.innerWidth) {
props.defaultPosition.x = props.defaultPosition.x - boundingClientRect.width;
}
}, [props.defaultPosition]);
if (!items) {
return null;
}
return (
<div
ref={contextMenuRef}
data-cy="context-menu"
className="bg-secondary-dark relative z-50 block w-48 rounded"
onContextMenu={e => e.preventDefault()}