mirror of
https://github.com/grafana/grafana.git
synced 2025-01-10 08:03:58 -06:00
Navigation: Add animation when closing nav menu (#47128)
This commit is contained in:
parent
ee92af8ebe
commit
54b1d88c44
@ -4,7 +4,7 @@ import { CollapsableSection, CustomScrollbar, Icon, IconName, useStyles2 } from
|
||||
import { FocusScope } from '@react-aria/focus';
|
||||
import { useDialog } from '@react-aria/dialog';
|
||||
import { useOverlay } from '@react-aria/overlays';
|
||||
import { css, cx, keyframes } from '@emotion/css';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { NavBarMenuItem } from './NavBarMenuItem';
|
||||
import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu';
|
||||
import { isMatchOrChildMatch } from '../utils';
|
||||
@ -29,9 +29,9 @@ export function NavBarMenu({ activeItem, navItems, onClose }: Props) {
|
||||
);
|
||||
|
||||
return (
|
||||
<FocusScope contain restoreFocus autoFocus>
|
||||
<div data-testid="navbarmenu" className={styles.container} ref={ref} {...overlayProps} {...dialogProps}>
|
||||
<nav className={styles.content}>
|
||||
<div data-testid="navbarmenu" className={styles.container}>
|
||||
<FocusScope contain restoreFocus autoFocus>
|
||||
<nav className={styles.content} ref={ref} {...overlayProps} {...dialogProps}>
|
||||
<CustomScrollbar hideHorizontalTrack>
|
||||
<ul className={styles.itemList}>
|
||||
{navItems.map((link) => (
|
||||
@ -40,53 +40,40 @@ export function NavBarMenu({ activeItem, navItems, onClose }: Props) {
|
||||
</ul>
|
||||
</CustomScrollbar>
|
||||
</nav>
|
||||
</div>
|
||||
</FocusScope>
|
||||
</FocusScope>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
NavBarMenu.displayName = 'NavBarMenu';
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
const fadeIn = keyframes`
|
||||
from {
|
||||
background-color: ${theme.colors.background.primary};
|
||||
width: ${theme.spacing(7)};
|
||||
}
|
||||
to {
|
||||
background-color: ${theme.colors.background.canvas};
|
||||
width: 300px;
|
||||
}`;
|
||||
|
||||
return {
|
||||
container: css({
|
||||
animation: `150ms ease-in 0s 1 normal forwards ${fadeIn}`,
|
||||
bottom: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
left: 0,
|
||||
whiteSpace: 'nowrap',
|
||||
paddingTop: theme.spacing(1),
|
||||
marginRight: theme.spacing(1.5),
|
||||
right: 0,
|
||||
zIndex: theme.zIndex.sidemenu,
|
||||
top: 0,
|
||||
[theme.breakpoints.up('md')]: {
|
||||
borderRight: `1px solid ${theme.colors.border.weak}`,
|
||||
right: 'unset',
|
||||
},
|
||||
}),
|
||||
content: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'auto',
|
||||
}),
|
||||
itemList: css({
|
||||
display: 'grid',
|
||||
gridAutoRows: `minmax(${theme.spacing(6)}, auto)`,
|
||||
}),
|
||||
};
|
||||
};
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
container: css({
|
||||
bottom: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
left: 0,
|
||||
whiteSpace: 'nowrap',
|
||||
paddingTop: theme.spacing(1),
|
||||
marginRight: theme.spacing(1.5),
|
||||
right: 0,
|
||||
zIndex: theme.zIndex.sidemenu,
|
||||
top: 0,
|
||||
[theme.breakpoints.up('md')]: {
|
||||
borderRight: `1px solid ${theme.colors.border.weak}`,
|
||||
right: 'unset',
|
||||
},
|
||||
}),
|
||||
content: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'auto',
|
||||
}),
|
||||
itemList: css({
|
||||
display: 'grid',
|
||||
gridAutoRows: `minmax(${theme.spacing(6)}, auto)`,
|
||||
}),
|
||||
});
|
||||
|
||||
function NavItem({
|
||||
link,
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import CSSTransition from 'react-transition-group/CSSTransition';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { GrafanaTheme2, NavModelItem, NavSection } from '@grafana/data';
|
||||
import { Icon, IconButton, IconName, useTheme2 } from '@grafana/ui';
|
||||
import { Icon, IconButton, IconName, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { config, locationService } from '@grafana/runtime';
|
||||
import { getKioskMode } from 'app/core/navigation/kiosk';
|
||||
import { KioskMode, StoreState } from 'app/types';
|
||||
@ -37,6 +38,7 @@ export const NavBarNext = React.memo(() => {
|
||||
const navBarTree = useSelector((state: StoreState) => state.navBarTree);
|
||||
const theme = useTheme2();
|
||||
const styles = getStyles(theme);
|
||||
const animStyles = useStyles2(getAnimStyles);
|
||||
const location = useLocation();
|
||||
const kiosk = getKioskMode();
|
||||
const [showSwitcherModal, setShowSwitcherModal] = useState(false);
|
||||
@ -115,16 +117,16 @@ export const NavBarNext = React.memo(() => {
|
||||
</nav>
|
||||
{showSwitcherModal && <OrgSwitcher onDismiss={toggleSwitcherModal} />}
|
||||
<div className={styles.menuWrapper}>
|
||||
{menuOpen && (
|
||||
<CSSTransition in={menuOpen} classNames={animStyles} timeout={150} unmountOnExit>
|
||||
<NavBarMenu
|
||||
activeItem={activeItem}
|
||||
navItems={[homeItem, searchItem, ...coreItems, ...pluginItems, ...configItems]}
|
||||
onClose={() => setMenuOpen(false)}
|
||||
/>
|
||||
)}
|
||||
</CSSTransition>
|
||||
<IconButton
|
||||
name={menuOpen ? 'angle-left' : 'angle-right'}
|
||||
className={cx(styles.menuToggle, { [styles.menuOpen]: menuOpen })}
|
||||
className={styles.menuToggle}
|
||||
size="xl"
|
||||
onClick={() => setMenuOpen(!menuOpen)}
|
||||
/>
|
||||
@ -233,7 +235,53 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
menuOpen: css({
|
||||
transform: 'translateX(0%)',
|
||||
}),
|
||||
});
|
||||
|
||||
const getAnimStyles = (theme: GrafanaTheme2) => {
|
||||
const transitionProps = {
|
||||
transitionProperty: 'width, background-color',
|
||||
transitionDuration: '150ms',
|
||||
transitionTimingFunction: 'ease-in-out',
|
||||
};
|
||||
|
||||
const openStyles = {
|
||||
backgroundColor: theme.colors.background.canvas,
|
||||
width: '300px',
|
||||
};
|
||||
|
||||
const closedStyles = {
|
||||
backgroundColor: theme.colors.background.primary,
|
||||
width: theme.spacing(7),
|
||||
};
|
||||
|
||||
const buttonShift = {
|
||||
'& + button': {
|
||||
transform: 'translateX(0%)',
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
enter: css({
|
||||
...closedStyles,
|
||||
...buttonShift,
|
||||
}),
|
||||
enterActive: css({
|
||||
...transitionProps,
|
||||
...openStyles,
|
||||
...buttonShift,
|
||||
}),
|
||||
enterDone: css({
|
||||
...openStyles,
|
||||
...buttonShift,
|
||||
}),
|
||||
exit: css({
|
||||
...openStyles,
|
||||
...buttonShift,
|
||||
}),
|
||||
exitActive: css({
|
||||
...transitionProps,
|
||||
...closedStyles,
|
||||
...buttonShift,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user