mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
SingleTopNav: Initial changes (#93694)
initial changes for single top nav
This commit is contained in:
parent
9a67cd614d
commit
8e5459791b
@ -224,6 +224,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
flexGrow: 1,
|
||||
}),
|
||||
content: css({
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
}),
|
||||
contentWithIcon: css({
|
||||
|
@ -3,7 +3,7 @@ import classNames from 'classnames';
|
||||
import { PropsWithChildren, useEffect } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { locationSearchToObject, locationService } from '@grafana/runtime';
|
||||
import { config, locationSearchToObject, locationService } from '@grafana/runtime';
|
||||
import { useStyles2, LinkButton, useTheme2 } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange';
|
||||
@ -17,6 +17,7 @@ import { DOCKED_LOCAL_STORAGE_KEY, DOCKED_MENU_OPEN_LOCAL_STORAGE_KEY } from './
|
||||
import { MegaMenu, MENU_WIDTH } from './MegaMenu/MegaMenu';
|
||||
import { NavToolbar } from './NavToolbar/NavToolbar';
|
||||
import { ReturnToPrevious } from './ReturnToPrevious/ReturnToPrevious';
|
||||
import { SingleTopBar } from './TopBar/SingleTopBar';
|
||||
import { TopSearchBar } from './TopBar/TopSearchBar';
|
||||
import { TOP_BAR_LEVEL_HEIGHT } from './types';
|
||||
|
||||
@ -34,6 +35,7 @@ export function AppChrome({ children }: Props) {
|
||||
const menuDockedAndOpen = !state.chromeless && state.megaMenuDocked && state.megaMenuOpen;
|
||||
const scopesDashboardsState = useScopesDashboardsState();
|
||||
const isScopesDashboardsOpen = Boolean(scopesDashboardsState?.isEnabled && scopesDashboardsState?.isPanelOpened);
|
||||
const isSingleTopNav = config.featureToggles.singleTopNav;
|
||||
|
||||
useMediaQueryChange({
|
||||
breakpoint: dockedMenuBreakpoint,
|
||||
@ -92,16 +94,27 @@ export function AppChrome({ children }: Props) {
|
||||
Skip to main content
|
||||
</LinkButton>
|
||||
<header className={cx(styles.topNav)}>
|
||||
{!searchBarHidden && <TopSearchBar />}
|
||||
<NavToolbar
|
||||
searchBarHidden={searchBarHidden}
|
||||
sectionNav={state.sectionNav.node}
|
||||
pageNav={state.pageNav}
|
||||
actions={state.actions}
|
||||
onToggleSearchBar={chrome.onToggleSearchBar}
|
||||
onToggleMegaMenu={handleMegaMenu}
|
||||
onToggleKioskMode={chrome.onToggleKioskMode}
|
||||
/>
|
||||
{isSingleTopNav ? (
|
||||
<SingleTopBar
|
||||
sectionNav={state.sectionNav.node}
|
||||
pageNav={state.pageNav}
|
||||
onToggleMegaMenu={handleMegaMenu}
|
||||
onToggleKioskMode={chrome.onToggleKioskMode}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{!searchBarHidden && <TopSearchBar />}
|
||||
<NavToolbar
|
||||
searchBarHidden={searchBarHidden}
|
||||
sectionNav={state.sectionNav.node}
|
||||
pageNav={state.pageNav}
|
||||
actions={state.actions}
|
||||
onToggleSearchBar={chrome.onToggleSearchBar}
|
||||
onToggleMegaMenu={handleMegaMenu}
|
||||
onToggleKioskMode={chrome.onToggleKioskMode}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</header>
|
||||
</>
|
||||
)}
|
||||
@ -140,11 +153,12 @@ export function AppChrome({ children }: Props) {
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, searchBarHidden: boolean) => {
|
||||
const isSingleTopNav = config.featureToggles.singleTopNav;
|
||||
return {
|
||||
content: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
paddingTop: TOP_BAR_LEVEL_HEIGHT * 2,
|
||||
paddingTop: isSingleTopNav ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2,
|
||||
flexGrow: 1,
|
||||
height: 'auto',
|
||||
}),
|
||||
@ -167,13 +181,13 @@ const getStyles = (theme: GrafanaTheme2, searchBarHidden: boolean) => {
|
||||
},
|
||||
{
|
||||
position: 'fixed',
|
||||
height: `calc(100% - ${searchBarHidden ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2}px)`,
|
||||
height: `calc(100% - ${searchBarHidden || isSingleTopNav ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2}px)`,
|
||||
zIndex: 2,
|
||||
}
|
||||
),
|
||||
scopesDashboardsContainer: css({
|
||||
position: 'fixed',
|
||||
height: `calc(100% - ${searchBarHidden ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2}px)`,
|
||||
height: `calc(100% - ${searchBarHidden || isSingleTopNav ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2}px)`,
|
||||
zIndex: 1,
|
||||
}),
|
||||
scopesDashboardsContainerDocked: css({
|
||||
|
@ -6,6 +6,7 @@ import { useRef } from 'react';
|
||||
import CSSTransition from 'react-transition-group/CSSTransition';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { KioskMode } from 'app/types';
|
||||
@ -76,7 +77,8 @@ export function AppChromeMenu({}: Props) {
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2, searchBarHidden?: boolean) => {
|
||||
const topPosition = searchBarHidden ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2;
|
||||
const topPosition =
|
||||
searchBarHidden || config.featureToggles.singleTopNav ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2;
|
||||
|
||||
return {
|
||||
backdrop: css({
|
||||
|
@ -213,7 +213,7 @@ export class AppChromeService {
|
||||
private getNextKioskMode() {
|
||||
const { kioskMode, searchBarHidden } = this.state.getValue();
|
||||
|
||||
if (searchBarHidden || kioskMode === KioskMode.TV) {
|
||||
if (searchBarHidden || kioskMode === KioskMode.TV || config.featureToggles.singleTopNav) {
|
||||
appEvents.emit(AppEvents.alertInfo, [t('navigation.kiosk.tv-alert', 'Press ESC to exit kiosk mode')]);
|
||||
return KioskMode.Full;
|
||||
}
|
||||
|
131
public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx
Normal file
131
public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx
Normal file
@ -0,0 +1,131 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { Dropdown, Icon, ToolbarButton, useStyles2 } from '@grafana/ui';
|
||||
import { config } from 'app/core/config';
|
||||
import { contextSrv } from 'app/core/core';
|
||||
import { HOME_NAV_ID } from 'app/core/reducers/navModel';
|
||||
import { ScopesSelector } from 'app/features/scopes';
|
||||
import { useSelector } from 'app/types';
|
||||
|
||||
import { Branding } from '../../Branding/Branding';
|
||||
import { Breadcrumbs } from '../../Breadcrumbs/Breadcrumbs';
|
||||
import { buildBreadcrumbs } from '../../Breadcrumbs/utils';
|
||||
import { enrichHelpItem } from '../MegaMenu/utils';
|
||||
import { NewsContainer } from '../News/NewsContainer';
|
||||
import { OrganizationSwitcher } from '../OrganizationSwitcher/OrganizationSwitcher';
|
||||
import { QuickAdd } from '../QuickAdd/QuickAdd';
|
||||
import { TOP_BAR_LEVEL_HEIGHT } from '../types';
|
||||
|
||||
import { SignInLink } from './SignInLink';
|
||||
import { TopNavBarMenu } from './TopNavBarMenu';
|
||||
import { TopSearchBarCommandPaletteTrigger } from './TopSearchBarCommandPaletteTrigger';
|
||||
import { TopSearchBarSection } from './TopSearchBarSection';
|
||||
|
||||
interface Props {
|
||||
sectionNav: NavModelItem;
|
||||
pageNav?: NavModelItem;
|
||||
onToggleMegaMenu(): void;
|
||||
onToggleKioskMode(): void;
|
||||
}
|
||||
|
||||
export const SingleTopBar = memo(function SingleTopBar({
|
||||
onToggleMegaMenu,
|
||||
onToggleKioskMode,
|
||||
pageNav,
|
||||
sectionNav,
|
||||
}: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const navIndex = useSelector((state) => state.navIndex);
|
||||
|
||||
const helpNode = cloneDeep(navIndex['help']);
|
||||
const enrichedHelpNode = helpNode ? enrichHelpItem(helpNode) : undefined;
|
||||
const profileNode = navIndex['profile'];
|
||||
const homeNav = useSelector((state) => state.navIndex)[HOME_NAV_ID];
|
||||
const breadcrumbs = buildBreadcrumbs(sectionNav, pageNav, homeNav);
|
||||
|
||||
return (
|
||||
<div className={styles.layout}>
|
||||
<TopSearchBarSection>
|
||||
<ToolbarButton narrow onClick={onToggleMegaMenu}>
|
||||
<Branding.MenuLogo className={styles.img} />
|
||||
</ToolbarButton>
|
||||
<Breadcrumbs breadcrumbs={breadcrumbs} className={styles.breadcrumbsWrapper} />
|
||||
<OrganizationSwitcher />
|
||||
<ScopesSelector />
|
||||
</TopSearchBarSection>
|
||||
|
||||
<TopSearchBarSection align="right">
|
||||
<TopSearchBarCommandPaletteTrigger />
|
||||
<QuickAdd />
|
||||
{enrichedHelpNode && (
|
||||
<Dropdown overlay={() => <TopNavBarMenu node={enrichedHelpNode} />} placement="bottom-end">
|
||||
<ToolbarButton iconOnly icon="question-circle" aria-label="Help" />
|
||||
</Dropdown>
|
||||
)}
|
||||
{config.newsFeedEnabled && <NewsContainer />}
|
||||
{!contextSrv.user.isSignedIn && <SignInLink />}
|
||||
{profileNode && (
|
||||
<Dropdown overlay={() => <TopNavBarMenu node={profileNode} />} placement="bottom-end">
|
||||
<ToolbarButton
|
||||
className={styles.profileButton}
|
||||
imgSrc={contextSrv.user.gravatarUrl}
|
||||
imgAlt="User avatar"
|
||||
aria-label="Profile"
|
||||
/>
|
||||
</Dropdown>
|
||||
)}
|
||||
<ToolbarButton className={styles.kioskToggle} onClick={onToggleKioskMode} narrow aria-label="Enable kiosk mode">
|
||||
<Icon name="angle-up" size="xl" />
|
||||
</ToolbarButton>
|
||||
</TopSearchBarSection>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
layout: css({
|
||||
height: TOP_BAR_LEVEL_HEIGHT,
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
alignItems: 'center',
|
||||
padding: theme.spacing(0, 1),
|
||||
borderBottom: `1px solid ${theme.colors.border.weak}`,
|
||||
justifyContent: 'space-between',
|
||||
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
gridTemplateColumns: '2fr minmax(240px, 1fr)', // TODO probably change these values
|
||||
display: 'grid',
|
||||
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
}),
|
||||
breadcrumbsWrapper: css({
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
minWidth: '50%',
|
||||
},
|
||||
}),
|
||||
img: css({
|
||||
alignSelf: 'center',
|
||||
height: theme.spacing(3),
|
||||
width: theme.spacing(3),
|
||||
}),
|
||||
profileButton: css({
|
||||
padding: theme.spacing(0, 0.25),
|
||||
img: {
|
||||
borderRadius: theme.shape.radius.circle,
|
||||
height: '24px',
|
||||
marginRight: 0,
|
||||
width: '24px',
|
||||
},
|
||||
}),
|
||||
kioskToggle: css({
|
||||
[theme.breakpoints.down('lg')]: {
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
import { createContext, useCallback, useContext } from 'react';
|
||||
|
||||
import { GrafanaConfig } from '@grafana/data';
|
||||
import { LocationService, locationService, BackendSrv } from '@grafana/runtime';
|
||||
import { LocationService, locationService, BackendSrv, config } from '@grafana/runtime';
|
||||
|
||||
import { AppChromeService } from '../components/AppChrome/AppChromeService';
|
||||
import { NewFrontendAssetsChecker } from '../services/NewFrontendAssetsChecker';
|
||||
@ -50,7 +50,7 @@ export function useChromeHeaderHeight() {
|
||||
|
||||
if (kioskMode || chromeless) {
|
||||
return 0;
|
||||
} else if (searchBarHidden) {
|
||||
} else if (searchBarHidden || config.featureToggles.singleTopNav) {
|
||||
return SINGLE_HEADER_BAR_HEIGHT;
|
||||
} else {
|
||||
return SINGLE_HEADER_BAR_HEIGHT * 2;
|
||||
|
Loading…
Reference in New Issue
Block a user