diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx
index c844df39b..6946127f6 100644
--- a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx
+++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenu.tsx
@@ -11,22 +11,23 @@ import i18n from 'i18next';
export type WindowLevelActionMenuProps = {
viewportId: string;
- element?: HTMLElement;
align?: 'start' | 'end' | 'center';
side?: 'top' | 'bottom' | 'left' | 'right';
+ onVisibilityChange?: (isVisible: boolean) => void;
};
export function WindowLevelActionMenu({
viewportId,
- element,
align,
side,
+ onVisibilityChange,
}: WindowLevelActionMenuProps): ReactElement {
return (
);
}
@@ -35,10 +36,12 @@ export function WindowLevelActionMenuContent({
viewportId,
align,
side,
+ onVisibilityChange,
}: {
viewportId: string;
align?: string;
side?: string;
+ onVisibilityChange?: (isVisible: boolean) => void;
}): ReactElement {
const { t } = useTranslation('WindowLevelActionMenu');
// Use a stable key for the menu to avoid infinite re-renders
@@ -60,6 +63,7 @@ export function WindowLevelActionMenuContent({
align={align}
side={side}
backLabel={i18n.t('WindowLevelActionMenu:Back to Display Options')}
+ onVisibilityChange={onVisibilityChange}
>
{!is3DVolume && }
diff --git a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenuWrapper.tsx b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenuWrapper.tsx
index b1ad429de..669876a5a 100644
--- a/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenuWrapper.tsx
+++ b/extensions/cornerstone/src/components/WindowLevelActionMenu/WindowLevelActionMenuWrapper.tsx
@@ -16,7 +16,6 @@ import { useViewportRendering } from '../../hooks';
export function WindowLevelActionMenuWrapper(
props: withAppTypes<{
viewportId: string;
- element?: HTMLElement;
location?: number;
isOpen?: boolean;
onOpen?: () => void;
@@ -28,7 +27,6 @@ export function WindowLevelActionMenuWrapper(
): ReactNode {
const {
viewportId,
- element,
location,
isOpen = false,
onOpen,
@@ -123,9 +121,9 @@ export function WindowLevelActionMenuWrapper(
>
diff --git a/platform/ui-next/src/components/AllInOneMenu/Menu.tsx b/platform/ui-next/src/components/AllInOneMenu/Menu.tsx
index 8fb497414..a2e75caf5 100644
--- a/platform/ui-next/src/components/AllInOneMenu/Menu.tsx
+++ b/platform/ui-next/src/components/AllInOneMenu/Menu.tsx
@@ -1,4 +1,4 @@
-import React, { createContext, ReactNode, useCallback, useEffect, useState } from 'react';
+import React, { createContext, ReactNode, useCallback, useEffect, useRef, useState } from 'react';
import DividerItem from './DividerItem';
import PanelSelector from './PanelSelector';
@@ -108,6 +108,20 @@ const Menu = (props: MenuProps) => {
]);
const [itemPanelLabels, setItemPanelLabels] = useState>([]);
+ // Store latest function references in refs so we can use them in effects
+ // without triggering re-runs when they change
+ const onVisibilityChangeRef = useRef(onVisibilityChange);
+ const preventHideMenuRef = useRef(preventHideMenu);
+
+ // Keep refs up to date
+ useEffect(() => {
+ onVisibilityChangeRef.current = onVisibilityChange;
+ }, [onVisibilityChange]);
+
+ useEffect(() => {
+ preventHideMenuRef.current = preventHideMenu;
+ }, [preventHideMenu]);
+
// If the props change for the this top level menu then we have to update the menu path
// because the props to be rendered are maintained in the state.
useEffect(() => {
@@ -118,23 +132,25 @@ const Menu = (props: MenuProps) => {
}, [activePanelIndex, props]);
const hideMenu = useCallback(() => {
- if (preventHideMenu) {
+ if (preventHideMenuRef.current) {
return;
}
setMenuPath(path => [path[0]]);
setItemPanelLabels([]);
setIsMenuVisible(false);
- onVisibilityChange?.(false);
- }, [preventHideMenu, onVisibilityChange]);
+ onVisibilityChangeRef.current?.(false);
+ }, []);
+ // Only run this effect when isVisible changes, not when functions change.
+ // Note that hideMenu is stable with no dependencies and thus will not trigger the useEffect to run.
useEffect(() => {
if (isVisible) {
setIsMenuVisible(isVisible);
- onVisibilityChange?.(isVisible);
+ onVisibilityChangeRef.current?.(isVisible);
} else {
hideMenu();
}
- }, [hideMenu, isVisible, onVisibilityChange]);
+ }, [isVisible, hideMenu]);
const showSubMenu = useCallback((subMenuProps: MenuProps) => {
setMenuPath(path => {