mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Navigation: Put expand toggle at beginning of tab order (#47268)
* Put toggle at beginning of tab order * create NavBarToggle * move margin into the common component * lint fixes
This commit is contained in:
parent
322a14fe6e
commit
1c34cc8b91
@ -8,21 +8,23 @@ import { css, cx } from '@emotion/css';
|
|||||||
import { NavBarMenuItem } from './NavBarMenuItem';
|
import { NavBarMenuItem } from './NavBarMenuItem';
|
||||||
import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu';
|
import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu';
|
||||||
import { isMatchOrChildMatch } from '../utils';
|
import { isMatchOrChildMatch } from '../utils';
|
||||||
|
import { NavBarToggle } from './NavBarToggle';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
activeItem?: NavModelItem;
|
activeItem?: NavModelItem;
|
||||||
|
isOpen: boolean;
|
||||||
navItems: NavModelItem[];
|
navItems: NavModelItem[];
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NavBarMenu({ activeItem, navItems, onClose }: Props) {
|
export function NavBarMenu({ activeItem, isOpen, navItems, onClose }: Props) {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
const { dialogProps } = useDialog({}, ref);
|
const { dialogProps } = useDialog({}, ref);
|
||||||
const { overlayProps } = useOverlay(
|
const { overlayProps } = useOverlay(
|
||||||
{
|
{
|
||||||
isDismissable: true,
|
isDismissable: true,
|
||||||
isOpen: true,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
@ -32,6 +34,7 @@ export function NavBarMenu({ activeItem, navItems, onClose }: Props) {
|
|||||||
<div data-testid="navbarmenu" className={styles.container}>
|
<div data-testid="navbarmenu" className={styles.container}>
|
||||||
<FocusScope contain restoreFocus autoFocus>
|
<FocusScope contain restoreFocus autoFocus>
|
||||||
<nav className={styles.content} ref={ref} {...overlayProps} {...dialogProps}>
|
<nav className={styles.content} ref={ref} {...overlayProps} {...dialogProps}>
|
||||||
|
<NavBarToggle className={styles.menuCollapseIcon} isExpanded={isOpen} onClick={onClose} />
|
||||||
<CustomScrollbar hideHorizontalTrack>
|
<CustomScrollbar hideHorizontalTrack>
|
||||||
<ul className={styles.itemList}>
|
<ul className={styles.itemList}>
|
||||||
{navItems.map((link) => (
|
{navItems.map((link) => (
|
||||||
@ -75,6 +78,11 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridAutoRows: `minmax(${theme.spacing(6)}, auto)`,
|
gridAutoRows: `minmax(${theme.spacing(6)}, auto)`,
|
||||||
}),
|
}),
|
||||||
|
menuCollapseIcon: css({
|
||||||
|
position: 'absolute',
|
||||||
|
top: '43px',
|
||||||
|
right: '0px',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
function NavItem({
|
function NavItem({
|
||||||
|
@ -4,7 +4,7 @@ import CSSTransition from 'react-transition-group/CSSTransition';
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import { GrafanaTheme2, NavModelItem, NavSection } from '@grafana/data';
|
import { GrafanaTheme2, NavModelItem, NavSection } from '@grafana/data';
|
||||||
import { Icon, IconButton, IconName, useStyles2, useTheme2 } from '@grafana/ui';
|
import { Icon, IconName, useStyles2, useTheme2 } from '@grafana/ui';
|
||||||
import { config, locationService } from '@grafana/runtime';
|
import { config, locationService } from '@grafana/runtime';
|
||||||
import { getKioskMode } from 'app/core/navigation/kiosk';
|
import { getKioskMode } from 'app/core/navigation/kiosk';
|
||||||
import { KioskMode, StoreState } from 'app/types';
|
import { KioskMode, StoreState } from 'app/types';
|
||||||
@ -16,6 +16,7 @@ import { useSelector } from 'react-redux';
|
|||||||
import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu';
|
import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu';
|
||||||
import { FocusScope } from '@react-aria/focus';
|
import { FocusScope } from '@react-aria/focus';
|
||||||
import { NavBarContext } from '../context';
|
import { NavBarContext } from '../context';
|
||||||
|
import { NavBarToggle } from './NavBarToggle';
|
||||||
|
|
||||||
const onOpenSearch = () => {
|
const onOpenSearch = () => {
|
||||||
locationService.partial({ search: 'open' });
|
locationService.partial({ search: 'open' });
|
||||||
@ -79,6 +80,12 @@ export const NavBarNext = React.memo(() => {
|
|||||||
<Icon name="bars" size="xl" />
|
<Icon name="bars" size="xl" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<NavBarToggle
|
||||||
|
className={styles.menuExpandIcon}
|
||||||
|
isExpanded={menuOpen}
|
||||||
|
onClick={() => setMenuOpen(!menuOpen)}
|
||||||
|
/>
|
||||||
|
|
||||||
<ul className={styles.itemList}>
|
<ul className={styles.itemList}>
|
||||||
<NavBarItemWithoutMenu
|
<NavBarItemWithoutMenu
|
||||||
isActive={isMatchOrChildMatch(homeItem, activeItem)}
|
isActive={isMatchOrChildMatch(homeItem, activeItem)}
|
||||||
@ -132,16 +139,11 @@ export const NavBarNext = React.memo(() => {
|
|||||||
<CSSTransition in={menuOpen} classNames={animStyles} timeout={150} unmountOnExit>
|
<CSSTransition in={menuOpen} classNames={animStyles} timeout={150} unmountOnExit>
|
||||||
<NavBarMenu
|
<NavBarMenu
|
||||||
activeItem={activeItem}
|
activeItem={activeItem}
|
||||||
|
isOpen={menuOpen}
|
||||||
navItems={[homeItem, searchItem, ...coreItems, ...pluginItems, ...configItems]}
|
navItems={[homeItem, searchItem, ...coreItems, ...pluginItems, ...configItems]}
|
||||||
onClose={() => setMenuOpen(false)}
|
onClose={() => setMenuOpen(false)}
|
||||||
/>
|
/>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
<IconButton
|
|
||||||
name={menuOpen ? 'angle-left' : 'angle-right'}
|
|
||||||
className={styles.menuToggle}
|
|
||||||
size="xl"
|
|
||||||
onClick={() => setMenuOpen(!menuOpen)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -233,20 +235,11 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
zIndex: theme.zIndex.sidemenu,
|
zIndex: theme.zIndex.sidemenu,
|
||||||
}),
|
}),
|
||||||
menuToggle: css({
|
menuExpandIcon: css({
|
||||||
backgroundColor: theme.colors.background.secondary,
|
|
||||||
border: `1px solid ${theme.colors.border.weak}`,
|
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
marginRight: 0,
|
|
||||||
top: '43px',
|
top: '43px',
|
||||||
right: '0px',
|
right: '0px',
|
||||||
zIndex: theme.zIndex.sidemenu,
|
transform: `translateX(50%)`,
|
||||||
transform: `translateX(calc(${theme.spacing(7)} + 50%))`,
|
|
||||||
borderRadius: '50%',
|
|
||||||
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -267,34 +260,23 @@ const getAnimStyles = (theme: GrafanaTheme2) => {
|
|||||||
width: theme.spacing(7),
|
width: theme.spacing(7),
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonShift = {
|
|
||||||
'& + button': {
|
|
||||||
transform: 'translateX(0%)',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
enter: css({
|
enter: css({
|
||||||
...closedStyles,
|
...closedStyles,
|
||||||
...buttonShift,
|
|
||||||
}),
|
}),
|
||||||
enterActive: css({
|
enterActive: css({
|
||||||
...transitionProps,
|
...transitionProps,
|
||||||
...openStyles,
|
...openStyles,
|
||||||
...buttonShift,
|
|
||||||
}),
|
}),
|
||||||
enterDone: css({
|
enterDone: css({
|
||||||
...openStyles,
|
...openStyles,
|
||||||
...buttonShift,
|
|
||||||
}),
|
}),
|
||||||
exit: css({
|
exit: css({
|
||||||
...openStyles,
|
...openStyles,
|
||||||
...buttonShift,
|
|
||||||
}),
|
}),
|
||||||
exitActive: css({
|
exitActive: css({
|
||||||
...transitionProps,
|
...transitionProps,
|
||||||
...closedStyles,
|
...closedStyles,
|
||||||
...buttonShift,
|
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
41
public/app/core/components/NavBar/Next/NavBarToggle.tsx
Normal file
41
public/app/core/components/NavBar/Next/NavBarToggle.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { IconButton, useTheme2 } from '@grafana/ui';
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
className?: string;
|
||||||
|
isExpanded: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NavBarToggle = ({ className, isExpanded, onClick }: Props) => {
|
||||||
|
const theme = useTheme2();
|
||||||
|
const styles = getStyles(theme);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
name={isExpanded ? 'angle-left' : 'angle-right'}
|
||||||
|
className={classnames(className, styles.icon)}
|
||||||
|
size="xl"
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
NavBarToggle.displayName = 'NavBarToggle';
|
||||||
|
|
||||||
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
|
icon: css({
|
||||||
|
backgroundColor: theme.colors.background.secondary,
|
||||||
|
border: `1px solid ${theme.colors.border.weak}`,
|
||||||
|
borderRadius: '50%',
|
||||||
|
marginRight: 0,
|
||||||
|
zIndex: theme.zIndex.sidemenu,
|
||||||
|
|
||||||
|
[theme.breakpoints.down('md')]: {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user