Merge pull request #6257 from tudi2d/6239-responsive-sidebar
Improve Sidebar UX for smaller screens
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Improve UX of the Sidebar by adding SidebarScrollWrapper component allowing the user to scroll through Plugins & Shortcuts on smaller screens. Prevent the Sidebar from opening on click on small devices
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
SidebarItem,
|
||||
SidebarDivider,
|
||||
SidebarSpace,
|
||||
SidebarScrollWrapper,
|
||||
} from '@backstage/core-components';
|
||||
|
||||
const useSidebarLogoStyles = makeStyles({
|
||||
@@ -88,10 +89,12 @@ export const Root = ({ children }: PropsWithChildren<{}>) => (
|
||||
<SidebarItem icon={CreateComponentIcon} to="create" text="Create..." />
|
||||
{/* End global nav */}
|
||||
<SidebarDivider />
|
||||
<SidebarItem icon={MapIcon} to="tech-radar" text="Tech Radar" />
|
||||
<SidebarItem icon={RuleIcon} to="lighthouse" text="Lighthouse" />
|
||||
<SidebarItem icon={MoneyIcon} to="cost-insights" text="Cost Insights" />
|
||||
<SidebarItem icon={GraphiQLIcon} to="graphiql" text="GraphiQL" />
|
||||
<SidebarScrollWrapper>
|
||||
<SidebarItem icon={MapIcon} to="tech-radar" text="Tech Radar" />
|
||||
<SidebarItem icon={RuleIcon} to="lighthouse" text="Lighthouse" />
|
||||
<SidebarItem icon={MoneyIcon} to="cost-insights" text="Cost Insights" />
|
||||
<SidebarItem icon={GraphiQLIcon} to="graphiql" text="GraphiQL" />
|
||||
</SidebarScrollWrapper>
|
||||
<SidebarDivider />
|
||||
<Shortcuts />
|
||||
<SidebarSpace />
|
||||
|
||||
@@ -25,7 +25,8 @@ const useStyles = makeStyles(() => ({
|
||||
"'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'",
|
||||
gridTemplateRows: 'auto auto 1fr',
|
||||
gridTemplateColumns: 'auto 1fr auto',
|
||||
minHeight: '100vh',
|
||||
height: '100vh',
|
||||
overflowY: 'auto',
|
||||
},
|
||||
}));
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import { makeStyles, useMediaQuery } from '@material-ui/core';
|
||||
import clsx from 'clsx';
|
||||
import React, { useRef, useState, useContext, PropsWithChildren } from 'react';
|
||||
import { sidebarConfig, SidebarContext } from './config';
|
||||
@@ -82,6 +82,9 @@ export const Sidebar = ({
|
||||
children,
|
||||
}: PropsWithChildren<Props>) => {
|
||||
const classes = useStyles();
|
||||
const isSmallScreen = useMediaQuery<BackstageTheme>(theme =>
|
||||
theme.breakpoints.down('md'),
|
||||
);
|
||||
const [state, setState] = useState(State.Closed);
|
||||
const hoverTimerRef = useRef<number>();
|
||||
const { isPinned } = useContext(SidebarPinStateContext);
|
||||
@@ -94,7 +97,7 @@ export const Sidebar = ({
|
||||
clearTimeout(hoverTimerRef.current);
|
||||
hoverTimerRef.current = undefined;
|
||||
}
|
||||
if (state !== State.Open) {
|
||||
if (state !== State.Open && !isSmallScreen) {
|
||||
hoverTimerRef.current = window.setTimeout(() => {
|
||||
hoverTimerRef.current = undefined;
|
||||
setState(State.Open);
|
||||
@@ -122,6 +125,8 @@ export const Sidebar = ({
|
||||
}
|
||||
};
|
||||
|
||||
const isOpen = (state === State.Open && !isSmallScreen) || isPinned;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes.root}
|
||||
@@ -133,13 +138,13 @@ export const Sidebar = ({
|
||||
>
|
||||
<SidebarContext.Provider
|
||||
value={{
|
||||
isOpen: state === State.Open || isPinned,
|
||||
isOpen,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={clsx(classes.drawer, {
|
||||
[classes.drawerPeek]: state === State.Peek,
|
||||
[classes.drawerOpen]: state === State.Open || isPinned,
|
||||
[classes.drawerOpen]: isOpen,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -290,3 +290,11 @@ export const SidebarDivider = styled('hr')({
|
||||
border: 'none',
|
||||
margin: '12px 0px',
|
||||
});
|
||||
|
||||
export const SidebarScrollWrapper = styled('div')({
|
||||
flex: '0 1 auto',
|
||||
overflowY: 'auto',
|
||||
// Display at least one item in the container
|
||||
// Question: Can this be a config/theme variable - if so, which? :/
|
||||
minHeight: '48px',
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@ export {
|
||||
SidebarSearchField,
|
||||
SidebarSpace,
|
||||
SidebarSpacer,
|
||||
SidebarScrollWrapper,
|
||||
} from './Items';
|
||||
export { IntroCard, SidebarIntro } from './Intro';
|
||||
export {
|
||||
|
||||
@@ -16,24 +16,19 @@
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { useObservable } from 'react-use';
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import PlayListAddIcon from '@material-ui/icons/PlaylistAdd';
|
||||
import { ShortcutItem } from './ShortcutItem';
|
||||
import { AddShortcut } from './AddShortcut';
|
||||
import { shortcutsApiRef } from './api';
|
||||
|
||||
import { Progress, SidebarItem } from '@backstage/core-components';
|
||||
import {
|
||||
Progress,
|
||||
SidebarItem,
|
||||
SidebarScrollWrapper,
|
||||
} from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
root: {
|
||||
flex: '1 1 auto',
|
||||
overflowY: 'scroll',
|
||||
},
|
||||
});
|
||||
|
||||
export const Shortcuts = () => {
|
||||
const classes = useStyles();
|
||||
const shortcutApi = useApi(shortcutsApiRef);
|
||||
const shortcuts = useObservable(
|
||||
useMemo(() => shortcutApi.shortcut$(), [shortcutApi]),
|
||||
@@ -50,7 +45,7 @@ export const Shortcuts = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<SidebarScrollWrapper>
|
||||
<SidebarItem
|
||||
icon={PlayListAddIcon}
|
||||
text="Add Shortcuts"
|
||||
@@ -72,6 +67,6 @@ export const Shortcuts = () => {
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</SidebarScrollWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user