mirror of
https://github.com/grafana/grafana.git
synced 2026-08-08 20:28:14 -05:00
Page: Refactor out section nav state logic and move it to SectionNav component (#57036)
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
// Libraries
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
|
||||
import { CustomScrollbar, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
import { CustomScrollbar, useStyles2 } from '@grafana/ui';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
|
||||
import { Footer } from '../Footer/Footer';
|
||||
@@ -16,7 +15,6 @@ import { PageContents } from './PageContents';
|
||||
import { PageHeader } from './PageHeader';
|
||||
import { PageTabs } from './PageTabs';
|
||||
import { SectionNav } from './SectionNav';
|
||||
import { SectionNavToggle } from './SectionNavToggle';
|
||||
|
||||
export const Page: PageType = ({
|
||||
navId,
|
||||
@@ -31,29 +29,14 @@ export const Page: PageType = ({
|
||||
scrollRef,
|
||||
...otherProps
|
||||
}) => {
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
const navModel = usePageNav(navId, oldNavProp);
|
||||
const { chrome } = useGrafana();
|
||||
|
||||
const isSmallScreen = window.matchMedia(`(max-width: ${theme.breakpoints.values.lg}px)`).matches;
|
||||
const [navExpandedPreference, setNavExpandedPreference] = useLocalStorage<boolean>(
|
||||
'grafana.sectionNav.expanded',
|
||||
!isSmallScreen
|
||||
);
|
||||
const [isNavExpanded, setNavExpanded] = useState(!isSmallScreen && navExpandedPreference);
|
||||
|
||||
usePageTitle(navModel, pageNav);
|
||||
|
||||
const pageHeaderNav = pageNav ?? navModel?.node;
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia(`(max-width: ${theme.breakpoints.values.lg}px)`);
|
||||
const onMediaQueryChange = (e: MediaQueryListEvent) => setNavExpanded(e.matches ? false : navExpandedPreference);
|
||||
mediaQuery.addEventListener('change', onMediaQueryChange);
|
||||
return () => mediaQuery.removeEventListener('change', onMediaQueryChange);
|
||||
}, [navExpandedPreference, theme.breakpoints.values.lg]);
|
||||
|
||||
useEffect(() => {
|
||||
if (navModel) {
|
||||
chrome.update({
|
||||
@@ -63,25 +46,11 @@ export const Page: PageType = ({
|
||||
}
|
||||
}, [navModel, pageNav, chrome]);
|
||||
|
||||
const onToggleSectionNav = () => {
|
||||
setNavExpandedPreference(!isNavExpanded);
|
||||
setNavExpanded(!isNavExpanded);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cx(styles.wrapper, className)} {...otherProps}>
|
||||
{layout === PageLayoutType.Standard && (
|
||||
<div className={styles.panes}>
|
||||
{navModel && (
|
||||
<>
|
||||
<SectionNav model={navModel} isExpanded={Boolean(isNavExpanded)} />
|
||||
<SectionNavToggle
|
||||
className={styles.collapseIcon}
|
||||
isExpanded={Boolean(isNavExpanded)}
|
||||
onClick={onToggleSectionNav}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{navModel && <SectionNav model={navModel} />}
|
||||
<div className={styles.pageContainer}>
|
||||
<CustomScrollbar autoHeightMin={'100%'} scrollTop={scrollTop} scrollRefCallback={scrollRef}>
|
||||
<div className={styles.pageInner}>
|
||||
@@ -125,18 +94,6 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
: '0 0.6px 1.5px -1px rgb(0 0 0 / 8%),0 2px 4px rgb(0 0 0 / 6%),0 5px 10px -1px rgb(0 0 0 / 5%)';
|
||||
|
||||
return {
|
||||
collapseIcon: css({
|
||||
border: `1px solid ${theme.colors.border.weak}`,
|
||||
transform: 'translateX(50%)',
|
||||
top: theme.spacing(8),
|
||||
right: theme.spacing(-1),
|
||||
|
||||
[theme.breakpoints.down('md')]: {
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, 50%) rotate(90deg)',
|
||||
top: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
wrapper: css({
|
||||
label: 'page-wrapper',
|
||||
height: '100%',
|
||||
|
||||
@@ -1,38 +1,68 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
|
||||
import { NavModel, GrafanaTheme2 } from '@grafana/data';
|
||||
import { useStyles2, CustomScrollbar } from '@grafana/ui';
|
||||
import { useStyles2, CustomScrollbar, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { SectionNavItem } from './SectionNavItem';
|
||||
import { SectionNavToggle } from './SectionNavToggle';
|
||||
|
||||
export interface Props {
|
||||
model: NavModel;
|
||||
isExpanded: boolean;
|
||||
}
|
||||
|
||||
export function SectionNav({ model, isExpanded }: Props) {
|
||||
export function SectionNav({ model }: Props) {
|
||||
const styles = useStyles2(getStyles);
|
||||
const { isExpanded, onToggleSectionNav } = useSectionNavState();
|
||||
|
||||
if (!Boolean(model.main?.children?.length)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={cx(styles.nav, {
|
||||
[styles.navExpanded]: isExpanded,
|
||||
})}
|
||||
>
|
||||
<CustomScrollbar showScrollIndicators>
|
||||
<div className={styles.items} role="tablist">
|
||||
<SectionNavItem item={model.main} isSectionRoot />
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
</nav>
|
||||
<>
|
||||
<nav
|
||||
className={cx(styles.nav, {
|
||||
[styles.navExpanded]: isExpanded,
|
||||
})}
|
||||
>
|
||||
<CustomScrollbar showScrollIndicators>
|
||||
<div className={styles.items} role="tablist">
|
||||
<SectionNavItem item={model.main} isSectionRoot />
|
||||
</div>
|
||||
</CustomScrollbar>
|
||||
</nav>
|
||||
<SectionNavToggle className={styles.collapseIcon} isExpanded={Boolean(isExpanded)} onClick={onToggleSectionNav} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function useSectionNavState() {
|
||||
const theme = useTheme2();
|
||||
|
||||
const isSmallScreen = window.matchMedia(`(max-width: ${theme.breakpoints.values.lg}px)`).matches;
|
||||
const [navExpandedPreference, setNavExpandedPreference] = useLocalStorage<boolean>(
|
||||
'grafana.sectionNav.expanded',
|
||||
!isSmallScreen
|
||||
);
|
||||
const [isExpanded, setIsExpanded] = useState(!isSmallScreen && navExpandedPreference);
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia(`(max-width: ${theme.breakpoints.values.lg}px)`);
|
||||
const onMediaQueryChange = (e: MediaQueryListEvent) => setIsExpanded(e.matches ? false : navExpandedPreference);
|
||||
mediaQuery.addEventListener('change', onMediaQueryChange);
|
||||
return () => mediaQuery.removeEventListener('change', onMediaQueryChange);
|
||||
}, [navExpandedPreference, theme.breakpoints.values.lg]);
|
||||
|
||||
const onToggleSectionNav = () => {
|
||||
setNavExpandedPreference(!isExpanded);
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
|
||||
return { isExpanded, onToggleSectionNav };
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => {
|
||||
return {
|
||||
nav: css({
|
||||
@@ -62,5 +92,17 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
padding: theme.spacing(4.5, 1, 2, 2),
|
||||
minWidth: '250px',
|
||||
}),
|
||||
collapseIcon: css({
|
||||
border: `1px solid ${theme.colors.border.weak}`,
|
||||
transform: 'translateX(50%)',
|
||||
top: theme.spacing(8),
|
||||
right: theme.spacing(-1),
|
||||
|
||||
[theme.breakpoints.down('md')]: {
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, 50%) rotate(90deg)',
|
||||
top: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user