mirror of https://github.com/grafana/grafana.git
				
				
				
			SingleTopNav: Flip collapse arrows for better alignment (#94081)
flip megamenu arrows for better alignment
This commit is contained in:
		
							parent
							
								
									ddbbf8df4b
								
							
						
					
					
						commit
						cf61ab3da2
					
				| 
						 | 
					@ -23,7 +23,7 @@ export function MegaMenuHeader({ handleMegaMenu, handleDockedMenu, onClose }: Pr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className={styles.header}>
 | 
					    <div className={styles.header}>
 | 
				
			||||||
      <Stack alignItems="center" minWidth={0}>
 | 
					      <Stack alignItems="center" minWidth={0} gap={0.25}>
 | 
				
			||||||
        <ToolbarButton narrow onClick={handleMegaMenu}>
 | 
					        <ToolbarButton narrow onClick={handleMegaMenu}>
 | 
				
			||||||
          <Branding.MenuLogo className={styles.img} />
 | 
					          <Branding.MenuLogo className={styles.img} />
 | 
				
			||||||
        </ToolbarButton>
 | 
					        </ToolbarButton>
 | 
				
			||||||
| 
						 | 
					@ -69,7 +69,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
 | 
				
			||||||
    display: 'flex',
 | 
					    display: 'flex',
 | 
				
			||||||
    gap: theme.spacing(1),
 | 
					    gap: theme.spacing(1),
 | 
				
			||||||
    justifyContent: 'space-between',
 | 
					    justifyContent: 'space-between',
 | 
				
			||||||
    padding: theme.spacing(0, 1, 0, 0.5),
 | 
					    padding: theme.spacing(0, 1, 0, 0.75),
 | 
				
			||||||
    height: TOP_BAR_LEVEL_HEIGHT,
 | 
					    height: TOP_BAR_LEVEL_HEIGHT,
 | 
				
			||||||
    minHeight: TOP_BAR_LEVEL_HEIGHT,
 | 
					    minHeight: TOP_BAR_LEVEL_HEIGHT,
 | 
				
			||||||
  }),
 | 
					  }),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import { useLocation } from 'react-router-dom-v5-compat';
 | 
				
			||||||
import { useLocalStorage } from 'react-use';
 | 
					import { useLocalStorage } from 'react-use';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { GrafanaTheme2, NavModelItem, toIconName } from '@grafana/data';
 | 
					import { GrafanaTheme2, NavModelItem, toIconName } from '@grafana/data';
 | 
				
			||||||
 | 
					import { config } from '@grafana/runtime';
 | 
				
			||||||
import { useStyles2, Text, IconButton, Icon, Stack } from '@grafana/ui';
 | 
					import { useStyles2, Text, IconButton, Icon, Stack } from '@grafana/ui';
 | 
				
			||||||
import { useGrafana } from 'app/core/context/GrafanaContext';
 | 
					import { useGrafana } from 'app/core/context/GrafanaContext';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +40,7 @@ export function MegaMenuItem({ link, activeItem, level = 0, onClick, onPin, isPi
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
  const showExpandButton = level < MAX_DEPTH && Boolean(linkHasChildren(link) || link.emptyMessage);
 | 
					  const showExpandButton = level < MAX_DEPTH && Boolean(linkHasChildren(link) || link.emptyMessage);
 | 
				
			||||||
  const item = useRef<HTMLLIElement>(null);
 | 
					  const item = useRef<HTMLLIElement>(null);
 | 
				
			||||||
 | 
					  const isSingleTopNav = config.featureToggles.singleTopNav;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const styles = useStyles2(getStyles);
 | 
					  const styles = useStyles2(getStyles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,6 +76,29 @@ export function MegaMenuItem({ link, activeItem, level = 0, onClick, onPin, isPi
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function getIconName(isExpanded: boolean) {
 | 
				
			||||||
 | 
					    if (isSingleTopNav) {
 | 
				
			||||||
 | 
					      return isExpanded ? 'angle-up' : 'angle-down';
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return isExpanded ? 'angle-down' : 'angle-right';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const collapseIcon = (
 | 
				
			||||||
 | 
					    <div className={styles.collapseButtonWrapper}>
 | 
				
			||||||
 | 
					      {showExpandButton && (
 | 
				
			||||||
 | 
					        <IconButton
 | 
				
			||||||
 | 
					          aria-label={`${sectionExpanded ? 'Collapse' : 'Expand'} section ${link.text}`}
 | 
				
			||||||
 | 
					          className={styles.collapseButton}
 | 
				
			||||||
 | 
					          onClick={() => setSectionExpanded(!sectionExpanded)}
 | 
				
			||||||
 | 
					          name={getIconName(Boolean(sectionExpanded))}
 | 
				
			||||||
 | 
					          size="md"
 | 
				
			||||||
 | 
					          variant="secondary"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <li ref={item} className={styles.listItem}>
 | 
					    <li ref={item} className={styles.listItem}>
 | 
				
			||||||
      <div
 | 
					      <div
 | 
				
			||||||
| 
						 | 
					@ -83,18 +108,7 @@ export function MegaMenuItem({ link, activeItem, level = 0, onClick, onPin, isPi
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        {level !== 0 && <Indent level={level === MAX_DEPTH ? level - 1 : level} spacing={3} />}
 | 
					        {level !== 0 && <Indent level={level === MAX_DEPTH ? level - 1 : level} spacing={3} />}
 | 
				
			||||||
        {level === MAX_DEPTH && <div className={styles.itemConnector} />}
 | 
					        {level === MAX_DEPTH && <div className={styles.itemConnector} />}
 | 
				
			||||||
        <div className={styles.collapseButtonWrapper}>
 | 
					        {!isSingleTopNav && collapseIcon}
 | 
				
			||||||
          {showExpandButton && (
 | 
					 | 
				
			||||||
            <IconButton
 | 
					 | 
				
			||||||
              aria-label={`${sectionExpanded ? 'Collapse' : 'Expand'} section ${link.text}`}
 | 
					 | 
				
			||||||
              className={styles.collapseButton}
 | 
					 | 
				
			||||||
              onClick={() => setSectionExpanded(!sectionExpanded)}
 | 
					 | 
				
			||||||
              name={sectionExpanded ? 'angle-down' : 'angle-right'}
 | 
					 | 
				
			||||||
              size="md"
 | 
					 | 
				
			||||||
              variant="secondary"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
          )}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div className={styles.collapsibleSectionWrapper}>
 | 
					        <div className={styles.collapsibleSectionWrapper}>
 | 
				
			||||||
          <MegaMenuItemText
 | 
					          <MegaMenuItemText
 | 
				
			||||||
            isActive={isActive}
 | 
					            isActive={isActive}
 | 
				
			||||||
| 
						 | 
					@ -118,6 +132,7 @@ export function MegaMenuItem({ link, activeItem, level = 0, onClick, onPin, isPi
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </MegaMenuItemText>
 | 
					          </MegaMenuItemText>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					        {isSingleTopNav && collapseIcon}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      {showExpandButton && sectionExpanded && (
 | 
					      {showExpandButton && sectionExpanded && (
 | 
				
			||||||
        <ul className={styles.children}>
 | 
					        <ul className={styles.children}>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,9 @@
 | 
				
			||||||
 | 
					import { css } from '@emotion/css';
 | 
				
			||||||
import { useEffect, useState } from 'react';
 | 
					import { useEffect, useState } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { SelectableValue } from '@grafana/data';
 | 
					import { GrafanaTheme2, SelectableValue } from '@grafana/data';
 | 
				
			||||||
import { config, locationService } from '@grafana/runtime';
 | 
					import { config, locationService } from '@grafana/runtime';
 | 
				
			||||||
import { Text, useTheme2 } from '@grafana/ui';
 | 
					import { Text, useStyles2, useTheme2 } from '@grafana/ui';
 | 
				
			||||||
import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange';
 | 
					import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange';
 | 
				
			||||||
import { contextSrv } from 'app/core/services/context_srv';
 | 
					import { contextSrv } from 'app/core/services/context_srv';
 | 
				
			||||||
import { getUserOrganizations, setUserOrganization } from 'app/features/org/state/actions';
 | 
					import { getUserOrganizations, setUserOrganization } from 'app/features/org/state/actions';
 | 
				
			||||||
| 
						 | 
					@ -17,6 +18,7 @@ export function OrganizationSwitcher() {
 | 
				
			||||||
  const theme = useTheme2();
 | 
					  const theme = useTheme2();
 | 
				
			||||||
  const dispatch = useDispatch();
 | 
					  const dispatch = useDispatch();
 | 
				
			||||||
  const orgs = useSelector((state) => state.organization.userOrgs);
 | 
					  const orgs = useSelector((state) => state.organization.userOrgs);
 | 
				
			||||||
 | 
					  const styles = useStyles2(getStyles);
 | 
				
			||||||
  const onSelectChange = (option: SelectableValue<UserOrg>) => {
 | 
					  const onSelectChange = (option: SelectableValue<UserOrg>) => {
 | 
				
			||||||
    if (option.value) {
 | 
					    if (option.value) {
 | 
				
			||||||
      setUserOrganization(option.value.orgId);
 | 
					      setUserOrganization(option.value.orgId);
 | 
				
			||||||
| 
						 | 
					@ -46,7 +48,11 @@ export function OrganizationSwitcher() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (orgs?.length <= 1) {
 | 
					  if (orgs?.length <= 1) {
 | 
				
			||||||
    if (config.featureToggles.singleTopNav) {
 | 
					    if (config.featureToggles.singleTopNav) {
 | 
				
			||||||
      return <Text truncate>{Branding.AppTitle}</Text>;
 | 
					      return (
 | 
				
			||||||
 | 
					        <span className={styles.brandTitle}>
 | 
				
			||||||
 | 
					          <Text truncate>{Branding.AppTitle}</Text>
 | 
				
			||||||
 | 
					        </span>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -56,3 +62,9 @@ export function OrganizationSwitcher() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return <Switcher orgs={orgs} onSelectChange={onSelectChange} />;
 | 
					  return <Switcher orgs={orgs} onSelectChange={onSelectChange} />;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getStyles = (theme: GrafanaTheme2) => ({
 | 
				
			||||||
 | 
					  brandTitle: css({
 | 
				
			||||||
 | 
					    paddingLeft: theme.spacing(1),
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -96,7 +96,7 @@ const getStyles = (theme: GrafanaTheme2, menuDockedAndOpen: boolean) => ({
 | 
				
			||||||
    gap: theme.spacing(1),
 | 
					    gap: theme.spacing(1),
 | 
				
			||||||
    alignItems: 'center',
 | 
					    alignItems: 'center',
 | 
				
			||||||
    padding: theme.spacing(0, 1),
 | 
					    padding: theme.spacing(0, 1),
 | 
				
			||||||
    paddingLeft: menuDockedAndOpen ? theme.spacing(3.5) : theme.spacing(0.5),
 | 
					    paddingLeft: menuDockedAndOpen ? theme.spacing(3.5) : theme.spacing(0.75),
 | 
				
			||||||
    borderBottom: `1px solid ${theme.colors.border.weak}`,
 | 
					    borderBottom: `1px solid ${theme.colors.border.weak}`,
 | 
				
			||||||
    justifyContent: 'space-between',
 | 
					    justifyContent: 'space-between',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue