Improve mobile submenu experience
Signed-off-by: Philipp Hugenroth <philipph@spotify.com>
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
import { IconComponent, useElementFilter } from '@backstage/core-plugin-api';
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
import { makeStyles, styled, Theme } from '@material-ui/core/styles';
|
||||
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
||||
import Badge from '@material-ui/core/Badge';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
@@ -46,6 +47,8 @@ import {
|
||||
SidebarItemWithSubmenuContext,
|
||||
} from './config';
|
||||
import { SidebarSubmenu } from './SidebarSubmenu';
|
||||
import ArrowDropUp from '@material-ui/icons/ArrowDropUp';
|
||||
import ArrowDropDown from '@material-ui/icons/ArrowDropDown';
|
||||
|
||||
export type SidebarItemClassKey =
|
||||
| 'root'
|
||||
@@ -151,6 +154,9 @@ const useStyles = makeStyles<BackstageTheme>(
|
||||
submenuArrow: {
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
right: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
selected: {
|
||||
'&$root': {
|
||||
@@ -274,6 +280,9 @@ const SidebarItemWithSubmenu = ({
|
||||
const [isHoveredOn, setIsHoveredOn] = useState(false);
|
||||
const { pathname: locationPathname } = useLocation();
|
||||
const isActive = isSidebarItemWithSubmenuActive(children, locationPathname);
|
||||
const isSmallScreen = useMediaQuery<BackstageTheme>((theme: BackstageTheme) =>
|
||||
theme.breakpoints.down('sm'),
|
||||
);
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setIsHoveredOn(true);
|
||||
@@ -308,6 +317,21 @@ const SidebarItemWithSubmenu = ({
|
||||
</>
|
||||
);
|
||||
|
||||
const arrowIcon = () => {
|
||||
if (isSmallScreen) {
|
||||
return isHoveredOn ? (
|
||||
<ArrowDropUp fontSize="small" className={classes.submenuArrow} />
|
||||
) : (
|
||||
<ArrowDropDown fontSize="small" className={classes.submenuArrow} />
|
||||
);
|
||||
}
|
||||
return (
|
||||
!isHoveredOn && (
|
||||
<ArrowRightIcon fontSize="small" className={classes.submenuArrow} />
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SidebarItemWithSubmenuContext.Provider
|
||||
value={{
|
||||
@@ -315,7 +339,11 @@ const SidebarItemWithSubmenu = ({
|
||||
setIsHoveredOn,
|
||||
}}
|
||||
>
|
||||
<div onMouseLeave={handleMouseLeave} onMouseEnter={handleMouseEnter}>
|
||||
<div
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onTouchStart={isHoveredOn ? handleMouseLeave : handleMouseEnter}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
>
|
||||
<div
|
||||
data-testid="item-with-submenu"
|
||||
className={clsx(
|
||||
@@ -327,9 +355,7 @@ const SidebarItemWithSubmenu = ({
|
||||
)}
|
||||
>
|
||||
{isOpen ? openContent : itemIcon}
|
||||
{!isHoveredOn && (
|
||||
<ArrowRightIcon fontSize="small" className={classes.submenuArrow} />
|
||||
)}
|
||||
{arrowIcon()}
|
||||
</div>
|
||||
{isHoveredOn && children}
|
||||
</div>
|
||||
|
||||
@@ -38,7 +38,13 @@ const useStyles = (props: { left: number }) =>
|
||||
flexFlow: 'column nowrap',
|
||||
alignItems: 'flex-start',
|
||||
position: 'fixed',
|
||||
left: props.left,
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
marginLeft: props.left,
|
||||
transition: theme.transitions.create('margin-left', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.shortest,
|
||||
}),
|
||||
},
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
padding: 0,
|
||||
@@ -58,7 +64,7 @@ const useStyles = (props: { left: number }) =>
|
||||
},
|
||||
drawerOpen: {
|
||||
width: submenuConfig.drawerWidthOpen,
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
[theme.breakpoints.down('xs')]: {
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
paddingLeft: theme.spacing(3),
|
||||
@@ -99,7 +105,7 @@ export const SidebarSubmenu = ({
|
||||
const left = isOpen
|
||||
? sidebarConfig.drawerWidthOpen
|
||||
: sidebarConfig.drawerWidthClosed;
|
||||
const props = { left: left };
|
||||
const props = { left };
|
||||
const classes = useStyles(props)();
|
||||
|
||||
const { isHoveredOn } = useContext(SidebarItemWithSubmenuContext);
|
||||
|
||||
@@ -29,6 +29,7 @@ import { BackstageTheme } from '@backstage/theme';
|
||||
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
|
||||
import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp';
|
||||
import { SidebarItemWithSubmenuContext } from './config';
|
||||
import { SidebarContext } from '../..';
|
||||
|
||||
const useStyles = makeStyles<BackstageTheme>(theme => ({
|
||||
item: {
|
||||
@@ -123,8 +124,13 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
|
||||
const { pathname: locationPathname } = useLocation();
|
||||
const { pathname: toPathname } = useResolvedPath(to);
|
||||
const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext);
|
||||
const { setOpen } = useContext(SidebarContext);
|
||||
|
||||
const closeSubmenu = () => {
|
||||
setIsHoveredOn(false);
|
||||
if (setOpen) {
|
||||
setOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
let isActive = locationPathname === toPathname;
|
||||
@@ -142,6 +148,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
|
||||
<div className={classes.itemContainer}>
|
||||
<button
|
||||
onClick={handleClickDropdown}
|
||||
onTouchStart={e => e.stopPropagation()}
|
||||
className={clsx(
|
||||
classes.item,
|
||||
isActive ? classes.selected : undefined,
|
||||
@@ -166,6 +173,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
|
||||
underline="none"
|
||||
className={classes.dropdownItem}
|
||||
onClick={closeSubmenu}
|
||||
onTouchStart={e => e.stopPropagation()}
|
||||
key={key}
|
||||
>
|
||||
<Typography className={classes.textContent}>
|
||||
@@ -187,6 +195,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
|
||||
underline="none"
|
||||
className={clsx(classes.item, isActive ? classes.selected : undefined)}
|
||||
onClick={closeSubmenu}
|
||||
onTouchStart={e => e.stopPropagation()}
|
||||
>
|
||||
<Icon fontSize="small" />
|
||||
<Typography variant="subtitle1" className={classes.label}>
|
||||
|
||||
Reference in New Issue
Block a user