mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Navigation: Remove plus button behind feature toggle * Navigation: Add home button behind feature toggle * Navigation: Move settings/admin to bottom section behind feature toggle * Navigation: Refactor grafana logo to be a NavBarItem * Navigation: Create new PluginSection and styling changes to support new sections * Navigation: Hack to use mobile menu as a mega menu for now * Navigation: Only render plugin section if there are items * Navigation: mobile menu is always 100% width if toggle is off * Navigation: Reset width back to 48 and fix broken css property * Navigation: Create generic NavBarSection component to reduce repetition * Navigation: Don't show sublinks for core items * Navigation: Comments from UX review * Navigation: Remove mobile menu hack * Navigation: Unit tests for enrichConfigItems and other minor review comments * Navigation: Move section logic to backend * Navigation: Refactor alerting links out into a separate function * Navigation: More tests for isLinkActive * Linting... * Navigation: Create new NavBar component for when feature toggle is enabled
126 lines
3.6 KiB
TypeScript
126 lines
3.6 KiB
TypeScript
import React from 'react';
|
|
import { css } from '@emotion/css';
|
|
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
|
import { IconName, Link, useTheme2 } from '@grafana/ui';
|
|
import DropdownChild from './DropdownChild';
|
|
|
|
interface Props {
|
|
headerTarget?: HTMLAnchorElement['target'];
|
|
headerText: string;
|
|
headerUrl?: string;
|
|
items?: NavModelItem[];
|
|
onHeaderClick?: () => void;
|
|
reverseDirection?: boolean;
|
|
subtitleText?: string;
|
|
}
|
|
|
|
const NavBarDropdown = ({
|
|
headerTarget,
|
|
headerText,
|
|
headerUrl,
|
|
items = [],
|
|
onHeaderClick,
|
|
reverseDirection = false,
|
|
subtitleText,
|
|
}: Props) => {
|
|
const filteredItems = items.filter((item) => !item.hideFromMenu);
|
|
const theme = useTheme2();
|
|
const styles = getStyles(theme, reverseDirection, filteredItems);
|
|
|
|
let header = (
|
|
<button onClick={onHeaderClick} className={styles.header}>
|
|
{headerText}
|
|
</button>
|
|
);
|
|
if (headerUrl) {
|
|
header =
|
|
!headerTarget && headerUrl.startsWith('/') ? (
|
|
<Link href={headerUrl} onClick={onHeaderClick} className={styles.header}>
|
|
{headerText}
|
|
</Link>
|
|
) : (
|
|
<a href={headerUrl} target={headerTarget} onClick={onHeaderClick} className={styles.header}>
|
|
{headerText}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ul className={`${styles.menu} dropdown-menu dropdown-menu--sidemenu`} role="menu">
|
|
<li>{header}</li>
|
|
{filteredItems.map((child, index) => (
|
|
<DropdownChild
|
|
key={`${child.url}-${index}`}
|
|
isDivider={child.divider}
|
|
icon={child.icon as IconName}
|
|
onClick={child.onClick}
|
|
target={child.target}
|
|
text={child.text}
|
|
url={child.url}
|
|
/>
|
|
))}
|
|
{subtitleText && <li className={styles.subtitle}>{subtitleText}</li>}
|
|
</ul>
|
|
);
|
|
};
|
|
|
|
export default NavBarDropdown;
|
|
|
|
const getStyles = (
|
|
theme: GrafanaTheme2,
|
|
reverseDirection: Props['reverseDirection'],
|
|
filteredItems: Props['items']
|
|
) => {
|
|
const adjustHeightForBorder = filteredItems!.length === 0;
|
|
|
|
return {
|
|
header: css`
|
|
align-items: center;
|
|
background-color: ${theme.colors.background.secondary};
|
|
border: none;
|
|
color: ${theme.colors.text.primary};
|
|
height: ${theme.components.sidemenu.width - (adjustHeightForBorder ? 2 : 1)}px;
|
|
font-size: ${theme.typography.h4.fontSize};
|
|
font-weight: ${theme.typography.h4.fontWeight};
|
|
padding: ${theme.spacing(1)} ${theme.spacing(1)} ${theme.spacing(1)} ${theme.spacing(2)} !important;
|
|
white-space: nowrap;
|
|
width: 100%;
|
|
|
|
&:hover {
|
|
background-color: ${theme.colors.action.hover};
|
|
}
|
|
|
|
.sidemenu-open--xs & {
|
|
display: flex;
|
|
font-size: ${theme.typography.body.fontSize};
|
|
font-weight: ${theme.typography.body.fontWeight};
|
|
padding-left: ${theme.spacing(1)} !important;
|
|
}
|
|
`,
|
|
menu: css`
|
|
border: 1px solid ${theme.components.panel.borderColor};
|
|
flex-direction: ${reverseDirection ? 'column-reverse' : 'column'};
|
|
|
|
.sidemenu-open--xs & {
|
|
display: flex;
|
|
flex-direction: column;
|
|
float: none;
|
|
position: unset;
|
|
width: 100%;
|
|
}
|
|
`,
|
|
subtitle: css`
|
|
border-${reverseDirection ? 'bottom' : 'top'}: 1px solid ${theme.colors.border.weak};
|
|
color: ${theme.colors.text.secondary};
|
|
font-size: ${theme.typography.bodySmall.fontSize};
|
|
font-weight: ${theme.typography.bodySmall.fontWeight};
|
|
padding: ${theme.spacing(1)} ${theme.spacing(2)} ${theme.spacing(1)};
|
|
white-space: nowrap;
|
|
|
|
.sidemenu-open--xs & {
|
|
border-${reverseDirection ? 'bottom' : 'top'}: none;
|
|
}
|
|
`,
|
|
};
|
|
};
|