mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Navigation: Keeps nav item hover when hovering over the item menu (#47281)
This commit is contained in:
@@ -12,6 +12,7 @@ import { NavBarItemMenu } from './NavBarItemMenu';
|
|||||||
import { getNavModelItemKey } from '../utils';
|
import { getNavModelItemKey } from '../utils';
|
||||||
import { useLingui } from '@lingui/react';
|
import { useLingui } from '@lingui/react';
|
||||||
import menuItemTranslations from '../navBarItem-translations';
|
import menuItemTranslations from '../navBarItem-translations';
|
||||||
|
import { useNavBarContext } from '../context';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
@@ -33,6 +34,7 @@ const NavBarItem = ({
|
|||||||
const { i18n } = useLingui();
|
const { i18n } = useLingui();
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const menuItems = link.children ?? [];
|
const menuItems = link.children ?? [];
|
||||||
|
const { menuIdOpen } = useNavBarContext();
|
||||||
|
|
||||||
// Spreading `menuItems` here as otherwise we'd be mutating props
|
// Spreading `menuItems` here as otherwise we'd be mutating props
|
||||||
const menuItemsSorted = reverseMenuDirection ? [...menuItems].reverse() : menuItems;
|
const menuItemsSorted = reverseMenuDirection ? [...menuItems].reverse() : menuItems;
|
||||||
@@ -81,7 +83,7 @@ const NavBarItem = ({
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<li className={cx(styles.container, className)}>
|
<li className={cx(styles.container, { [styles.containerHover]: section.id === menuIdOpen }, className)}>
|
||||||
<NavBarItemMenuTrigger
|
<NavBarItemMenuTrigger
|
||||||
item={section}
|
item={section}
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
@@ -126,6 +128,10 @@ export default NavBarItem;
|
|||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2, adjustHeightForBorder: boolean, isActive?: boolean) => ({
|
const getStyles = (theme: GrafanaTheme2, adjustHeightForBorder: boolean, isActive?: boolean) => ({
|
||||||
...getNavBarItemWithoutMenuStyles(theme, isActive),
|
...getNavBarItemWithoutMenuStyles(theme, isActive),
|
||||||
|
containerHover: css({
|
||||||
|
backgroundColor: theme.colors.action.hover,
|
||||||
|
color: theme.colors.text.primary,
|
||||||
|
}),
|
||||||
primaryText: css({
|
primaryText: css({
|
||||||
color: theme.colors.text.primary,
|
color: theme.colors.text.primary,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -47,9 +47,10 @@ export function NavBarItemMenuTrigger(props: NavBarItemMenuTriggerProps): ReactE
|
|||||||
onHoverChange: (isHovering) => {
|
onHoverChange: (isHovering) => {
|
||||||
if (isHovering) {
|
if (isHovering) {
|
||||||
state.open();
|
state.open();
|
||||||
setMenuIdOpen(ref.current?.id || null);
|
setMenuIdOpen(item.id);
|
||||||
} else {
|
} else {
|
||||||
state.close();
|
state.close();
|
||||||
|
setMenuIdOpen(undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -57,13 +58,13 @@ export function NavBarItemMenuTrigger(props: NavBarItemMenuTriggerProps): ReactE
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// close the menu when changing submenus
|
// close the menu when changing submenus
|
||||||
// or when the state of the overlay changes (i.e hovering outside)
|
// or when the state of the overlay changes (i.e hovering outside)
|
||||||
if (menuIdOpen !== ref.current?.id || !state.isOpen) {
|
if (menuIdOpen !== item.id || !state.isOpen) {
|
||||||
state.close();
|
state.close();
|
||||||
setMenuHasFocus(false);
|
setMenuHasFocus(false);
|
||||||
} else {
|
} else {
|
||||||
state.open();
|
state.open();
|
||||||
}
|
}
|
||||||
}, [menuIdOpen, state]);
|
}, [menuIdOpen, state, item.id]);
|
||||||
|
|
||||||
const { keyboardProps } = useKeyboard({
|
const { keyboardProps } = useKeyboard({
|
||||||
onKeyDown: (e) => {
|
onKeyDown: (e) => {
|
||||||
@@ -71,11 +72,12 @@ export function NavBarItemMenuTrigger(props: NavBarItemMenuTriggerProps): ReactE
|
|||||||
case 'ArrowRight':
|
case 'ArrowRight':
|
||||||
if (!state.isOpen) {
|
if (!state.isOpen) {
|
||||||
state.open();
|
state.open();
|
||||||
|
setMenuIdOpen(item.id);
|
||||||
}
|
}
|
||||||
setMenuHasFocus(true);
|
setMenuHasFocus(true);
|
||||||
break;
|
break;
|
||||||
case 'Tab':
|
case 'Tab':
|
||||||
setMenuIdOpen(null);
|
setMenuIdOpen(undefined);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -142,7 +144,10 @@ export function NavBarItemMenuTrigger(props: NavBarItemMenuTriggerProps): ReactE
|
|||||||
const { dialogProps } = useDialog({}, overlayRef);
|
const { dialogProps } = useDialog({}, overlayRef);
|
||||||
const { overlayProps } = useOverlay(
|
const { overlayProps } = useOverlay(
|
||||||
{
|
{
|
||||||
onClose: () => state.close(),
|
onClose: () => {
|
||||||
|
state.close();
|
||||||
|
setMenuIdOpen(undefined);
|
||||||
|
},
|
||||||
isOpen: state.isOpen,
|
isOpen: state.isOpen,
|
||||||
isDismissable: true,
|
isDismissable: true,
|
||||||
},
|
},
|
||||||
@@ -160,7 +165,7 @@ export function NavBarItemMenuTrigger(props: NavBarItemMenuTriggerProps): ReactE
|
|||||||
onFocusWithin: (e) => {
|
onFocusWithin: (e) => {
|
||||||
if (e.target.id === ref.current?.id) {
|
if (e.target.id === ref.current?.id) {
|
||||||
// If focussing on the trigger itself, set the menu id that is open
|
// If focussing on the trigger itself, set the menu id that is open
|
||||||
setMenuIdOpen(ref.current?.id);
|
setMenuIdOpen(item.id);
|
||||||
state.open();
|
state.open();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -168,7 +173,7 @@ export function NavBarItemMenuTrigger(props: NavBarItemMenuTriggerProps): ReactE
|
|||||||
if (e.target?.getAttribute('role') === 'menuitem' && !overlayRef.current?.contains(e.relatedTarget)) {
|
if (e.target?.getAttribute('role') === 'menuitem' && !overlayRef.current?.contains(e.relatedTarget)) {
|
||||||
// If it is blurring from a menuitem to an element outside the current overlay
|
// If it is blurring from a menuitem to an element outside the current overlay
|
||||||
// close the menu that is open
|
// close the menu that is open
|
||||||
setMenuIdOpen(null);
|
setMenuIdOpen(undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export const NavBarNext = React.memo(() => {
|
|||||||
const activeItem = isSearchActive(location) ? searchItem : getActiveItem(navTree, location.pathname);
|
const activeItem = isSearchActive(location) ? searchItem : getActiveItem(navTree, location.pathname);
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
const [menuAnimationInProgress, setMenuAnimationInProgress] = useState(false);
|
const [menuAnimationInProgress, setMenuAnimationInProgress] = useState(false);
|
||||||
const [menuIdOpen, setMenuIdOpen] = useState<string | null>(null);
|
const [menuIdOpen, setMenuIdOpen] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
if (kiosk !== KioskMode.Off) {
|
if (kiosk !== KioskMode.Off) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ export function useNavBarItemMenuContext(): NavBarItemMenuContextProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface NavBarContextProps {
|
export interface NavBarContextProps {
|
||||||
menuIdOpen: string | null;
|
menuIdOpen: string | undefined;
|
||||||
setMenuIdOpen: (id: string | null) => void;
|
setMenuIdOpen: (id: string | undefined) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NavBarContext = createContext<NavBarContextProps>({
|
export const NavBarContext = createContext<NavBarContextProps>({
|
||||||
menuIdOpen: null,
|
menuIdOpen: undefined,
|
||||||
setMenuIdOpen: () => undefined,
|
setMenuIdOpen: () => undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user