grafana/public/app/core/components/NavBar/NavBarItem.tsx
Ashley Harrison 4350f4931d
NavBar: Styling tweaks to tidy up appearance (#39871)
* NavBar: Styling tweaks to tidy up appearance

* NavBar: Add external link icon to external links

* NavBar: Dim the external link icon

* bump drone

* NavBar: Rename variable to better describe what it's doing
2021-10-01 15:53:56 +01:00

160 lines
3.7 KiB
TypeScript

import React, { ReactNode } from 'react';
import { css, cx } from '@emotion/css';
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
import { Link, useTheme2 } from '@grafana/ui';
import NavBarDropdown from './NavBarDropdown';
export interface Props {
isActive?: boolean;
children: ReactNode;
label: string;
menuItems?: NavModelItem[];
menuSubTitle?: string;
onClick?: () => void;
reverseMenuDirection?: boolean;
target?: HTMLAnchorElement['target'];
url?: string;
}
const NavBarItem = ({
isActive = false,
children,
label,
menuItems = [],
menuSubTitle,
onClick,
reverseMenuDirection = false,
target,
url,
}: Props) => {
const theme = useTheme2();
const styles = getStyles(theme, isActive);
let element = (
<button className={styles.element} onClick={onClick} aria-label={label}>
<span className={styles.icon}>{children}</span>
</button>
);
if (url) {
element =
!target && url.startsWith('/') ? (
<Link
className={styles.element}
href={url}
target={target}
aria-label={label}
onClick={onClick}
aria-haspopup="true"
>
<span className={styles.icon}>{children}</span>
</Link>
) : (
<a href={url} target={target} className={styles.element} onClick={onClick} aria-label={label}>
<span className={styles.icon}>{children}</span>
</a>
);
}
return (
<div className={cx(styles.container, 'dropdown', { dropup: reverseMenuDirection })}>
{element}
<NavBarDropdown
headerTarget={target}
headerText={label}
headerUrl={url}
items={menuItems}
onHeaderClick={onClick}
reverseDirection={reverseMenuDirection}
subtitleText={menuSubTitle}
/>
</div>
);
};
export default NavBarItem;
const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive']) => ({
container: css`
position: relative;
@keyframes dropdown-anim {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
${theme.breakpoints.up('md')} {
color: ${isActive ? theme.colors.text.primary : theme.colors.text.secondary};
&:hover {
background-color: ${theme.colors.action.hover};
color: ${theme.colors.text.primary};
.dropdown-menu {
animation: dropdown-anim 150ms ease-in-out 100ms forwards;
display: flex;
// important to overlap it otherwise it can be hidden
// again by the mouse getting outside the hover space
left: ${theme.components.sidemenu.width - 1}px;
margin: 0;
opacity: 0;
top: 0;
z-index: ${theme.zIndex.sidemenu};
}
&.dropup .dropdown-menu {
top: auto;
}
}
}
`,
element: css`
background-color: transparent;
border: none;
color: inherit;
display: block;
line-height: ${theme.components.sidemenu.width}px;
padding: 0;
text-align: center;
width: ${theme.components.sidemenu.width}px;
&::before {
display: ${isActive ? 'block' : 'none'};
content: ' ';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
border-radius: 2px;
background-image: ${theme.colors.gradients.brandVertical};
}
&:focus-visible {
background-color: ${theme.colors.action.hover};
box-shadow: none;
color: ${theme.colors.text.primary};
outline: 2px solid ${theme.colors.primary.main};
outline-offset: -2px;
transition: none;
}
.sidemenu-open--xs & {
display: none;
}
`,
icon: css`
height: 100%;
width: 100%;
img {
border-radius: 50%;
height: 28px;
width: 28px;
}
`,
});