{
* @public
*/
export const Sidebar = (props: SidebarProps) => {
- const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props;
+ const sidebarConfig: SidebarConfig = makeSidebarConfig(
+ props.sidebarOptions ?? {},
+ );
+ const submenuConfig: SubmenuConfig = makeSidebarSubmenuConfig(
+ props.submenuOptions ?? {},
+ );
+ const { children, disableExpandOnHover, openDelayMs, closeDelayMs } = props;
const { isMobile } = useContext(SidebarPinStateContext);
return isMobile ? (
{children}
) : (
-
- {children}
-
+
+
+ {children}
+
+
);
};
function A11ySkipSidebar() {
+ const { sidebarConfig } = useContext(SidebarConfigContext);
const { focusContent, contentRef } = useContent();
- const classes = useStyles();
+ const classes = useStyles({ sidebarConfig });
if (!contentRef?.current) {
return null;
diff --git a/packages/core-components/src/layout/Sidebar/Intro.tsx b/packages/core-components/src/layout/Sidebar/Intro.tsx
index 1fcc54188f..d1d8f4b2a2 100644
--- a/packages/core-components/src/layout/Sidebar/Intro.tsx
+++ b/packages/core-components/src/layout/Sidebar/Intro.tsx
@@ -23,7 +23,8 @@ import CloseIcon from '@material-ui/icons/Close';
import React, { useContext, useState } from 'react';
import { useLocalStorageValue } from '@react-hookz/web';
import {
- sidebarConfig,
+ SidebarConfigContext,
+ SidebarConfig,
SidebarContext,
SIDEBAR_INTRO_LOCAL_STORAGE,
} from './config';
@@ -37,18 +38,18 @@ export type SidebarIntroClassKey =
| 'introDismissText'
| 'introDismissIcon';
-const useStyles = makeStyles
(
+const useStyles = makeStyles(
theme => ({
- introCard: {
+ introCard: props => ({
color: '#b5b5b5',
// XXX (@koroeskohr): should I be using a Mui theme variable?
fontSize: 12,
- width: sidebarConfig.drawerWidthOpen,
+ width: props.sidebarConfig.drawerWidthOpen,
marginTop: 18,
marginBottom: 12,
- paddingLeft: sidebarConfig.iconPadding,
- paddingRight: sidebarConfig.iconPadding,
- },
+ paddingLeft: props.sidebarConfig.iconPadding,
+ paddingRight: props.sidebarConfig.iconPadding,
+ }),
introDismiss: {
display: 'flex',
justifyContent: 'flex-end',
@@ -96,7 +97,8 @@ type IntroCardProps = {
*/
export function IntroCard(props: IntroCardProps) {
- const classes = useStyles();
+ const { sidebarConfig } = useContext(SidebarConfigContext);
+ const classes = useStyles({ sidebarConfig });
const { text, onClose } = props;
const handleClose = () => onClose();
diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx
index 7eb86772f9..b3beed9147 100644
--- a/packages/core-components/src/layout/Sidebar/Items.tsx
+++ b/packages/core-components/src/layout/Sidebar/Items.tsx
@@ -47,9 +47,10 @@ import {
useResolvedPath,
} from 'react-router-dom';
import {
- sidebarConfig,
SidebarContext,
+ SidebarConfigContext,
SidebarItemWithSubmenuContext,
+ SidebarConfig,
} from './config';
import {
SidebarSubmenuItemProps,
@@ -82,126 +83,120 @@ export type SidebarItemClassKey =
| 'arrows'
| 'selected';
-const useStyles = makeStyles(
- theme => {
- const {
- selectedIndicatorWidth,
- drawerWidthClosed,
- drawerWidthOpen,
- iconContainerWidth,
- } = sidebarConfig;
- return {
- root: {
- color: theme.palette.navigation.color,
- display: 'flex',
- flexFlow: 'row nowrap',
- alignItems: 'center',
- height: 48,
- cursor: 'pointer',
+const useStyles = makeStyles(
+ theme => ({
+ root: {
+ color: theme.palette.navigation.color,
+ display: 'flex',
+ flexFlow: 'row nowrap',
+ alignItems: 'center',
+ height: 48,
+ cursor: 'pointer',
+ },
+ buttonItem: {
+ background: 'none',
+ border: 'none',
+ width: '100%',
+ margin: 0,
+ padding: 0,
+ textAlign: 'inherit',
+ font: 'inherit',
+ },
+ closed: props => ({
+ width: props.sidebarConfig.drawerWidthClosed,
+ justifyContent: 'center',
+ }),
+ open: props => ({
+ [theme.breakpoints.up('sm')]: {
+ width: props.sidebarConfig.drawerWidthOpen,
},
- buttonItem: {
- background: 'none',
- border: 'none',
- width: '100%',
- margin: 0,
- padding: 0,
- textAlign: 'inherit',
- font: 'inherit',
- },
- closed: {
- width: drawerWidthClosed,
- justifyContent: 'center',
- },
- open: {
- [theme.breakpoints.up('sm')]: {
- width: drawerWidthOpen,
- },
- },
- highlightable: {
- '&:hover': {
- background:
- theme.palette.navigation.navItem?.hoverBackground ?? '#404040',
- },
- },
- highlighted: {
+ }),
+ highlightable: {
+ '&:hover': {
background:
theme.palette.navigation.navItem?.hoverBackground ?? '#404040',
},
- label: {
- // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs
- fontWeight: 'bold',
- whiteSpace: 'nowrap',
- lineHeight: 'auto',
- flex: '3 1 auto',
- width: '110px',
- overflow: 'hidden',
- 'text-overflow': 'ellipsis',
+ },
+ highlighted: {
+ background:
+ theme.palette.navigation.navItem?.hoverBackground ?? '#404040',
+ },
+ label: {
+ // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs
+ fontWeight: 'bold',
+ whiteSpace: 'nowrap',
+ lineHeight: 'auto',
+ flex: '3 1 auto',
+ width: '110px',
+ overflow: 'hidden',
+ 'text-overflow': 'ellipsis',
+ },
+ iconContainer: props => ({
+ boxSizing: 'border-box',
+ height: '100%',
+ width: props.sidebarConfig.iconContainerWidth,
+ marginRight: -theme.spacing(2),
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ }),
+ searchRoot: {
+ marginBottom: 12,
+ },
+ searchField: {
+ color: '#b5b5b5',
+ fontWeight: 'bold',
+ fontSize: theme.typography.fontSize,
+ },
+ searchFieldHTMLInput: {
+ padding: theme.spacing(2, 0, 2),
+ },
+ searchContainer: props => ({
+ width:
+ props.sidebarConfig.drawerWidthOpen -
+ props.sidebarConfig.iconContainerWidth,
+ }),
+ secondaryAction: {
+ width: theme.spacing(6),
+ textAlign: 'center',
+ marginRight: theme.spacing(1),
+ },
+ closedItemIcon: {
+ width: '100%',
+ justifyContent: 'center',
+ },
+ submenuArrow: {
+ display: 'flex',
+ },
+ expandButton: {
+ background: 'none',
+ border: 'none',
+ color: theme.palette.navigation.color,
+ width: '100%',
+ cursor: 'pointer',
+ position: 'relative',
+ height: 48,
+ },
+ arrows: {
+ position: 'absolute',
+ right: 10,
+ },
+ selected: props => ({
+ '&$root': {
+ borderLeft: `solid ${props.sidebarConfig.selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`,
+ color: theme.palette.navigation.selectedColor,
},
- iconContainer: {
- boxSizing: 'border-box',
- height: '100%',
- width: iconContainerWidth,
- marginRight: -theme.spacing(2),
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
+ '&$closed': {
+ width: props.sidebarConfig.drawerWidthClosed,
},
- searchRoot: {
- marginBottom: 12,
+ '& $closedItemIcon': {
+ paddingRight: props.sidebarConfig.selectedIndicatorWidth,
},
- searchField: {
- color: '#b5b5b5',
- fontWeight: 'bold',
- fontSize: theme.typography.fontSize,
+ '& $iconContainer': {
+ marginLeft: -props.sidebarConfig.selectedIndicatorWidth,
},
- searchFieldHTMLInput: {
- padding: theme.spacing(2, 0, 2),
- },
- searchContainer: {
- width: drawerWidthOpen - iconContainerWidth,
- },
- secondaryAction: {
- width: theme.spacing(6),
- textAlign: 'center',
- marginRight: theme.spacing(1),
- },
- closedItemIcon: {
- width: '100%',
- justifyContent: 'center',
- },
- submenuArrow: {
- display: 'flex',
- },
- expandButton: {
- background: 'none',
- border: 'none',
- color: theme.palette.navigation.color,
- width: '100%',
- cursor: 'pointer',
- position: 'relative',
- height: 48,
- },
- arrows: {
- position: 'absolute',
- right: 10,
- },
- selected: {
- '&$root': {
- borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`,
- color: theme.palette.navigation.selectedColor,
- },
- '&$closed': {
- width: drawerWidthClosed,
- },
- '& $closedItemIcon': {
- paddingRight: selectedIndicatorWidth,
- },
- '& $iconContainer': {
- marginLeft: -selectedIndicatorWidth,
- },
- },
- };
- },
+ }),
+ }),
{ name: 'BackstageSidebarItem' },
);
@@ -356,7 +351,8 @@ const SidebarItemBase = forwardRef((props, ref) => {
className,
...navLinkProps
} = props;
- const classes = useStyles();
+ const { sidebarConfig } = useContext(SidebarConfigContext);
+ const classes = useStyles({ sidebarConfig });
// XXX (@koroeskohr): unsure this is optimal. But I just really didn't want to have the item component
// depend on the current location, and at least have it being optionally forced to selected.
// Still waiting on a Q answered to fine tune the implementation
@@ -429,7 +425,8 @@ const SidebarItemWithSubmenu = ({
}: SidebarItemBaseProps & {
children: React.ReactElement;
}) => {
- const classes = useStyles();
+ const { sidebarConfig } = useContext(SidebarConfigContext);
+ const classes = useStyles({ sidebarConfig });
const [isHoveredOn, setIsHoveredOn] = useState(false);
const location = useLocation();
const isActive = useLocationMatch(children, location);
@@ -518,8 +515,9 @@ type SidebarSearchFieldProps = {
};
export function SidebarSearchField(props: SidebarSearchFieldProps) {
+ const { sidebarConfig } = useContext(SidebarConfigContext);
const [input, setInput] = useState('');
- const classes = useStyles();
+ const classes = useStyles({ sidebarConfig });
const Icon = props.icon ? props.icon : SearchIcon;
const search = () => {
@@ -647,7 +645,8 @@ export const SidebarScrollWrapper = styled('div')(({ theme }) => {
* @public
*/
export const SidebarExpandButton = () => {
- const classes = useStyles();
+ const { sidebarConfig } = useContext(SidebarConfigContext);
+ const classes = useStyles({ sidebarConfig });
const { isOpen, setOpen } = useContext(SidebarContext);
const isSmallScreen = useMediaQuery(
theme => theme.breakpoints.down('md'),
diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx
index 6734d4b53a..aefc58bc45 100644
--- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx
+++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx
@@ -25,11 +25,10 @@ import Typography from '@material-ui/core/Typography';
import CloseIcon from '@material-ui/icons/Close';
import MenuIcon from '@material-ui/icons/Menu';
import { orderBy } from 'lodash';
-import React, { createContext, useEffect, useState } from 'react';
+import React, { createContext, useEffect, useState, useContext } from 'react';
import { useLocation } from 'react-router';
-import { SidebarContext } from '.';
-import { sidebarConfig } from './config';
import { SidebarGroup } from './SidebarGroup';
+import { SidebarConfigContext, SidebarContext, SidebarConfig } from './config';
/**
* Type of `MobileSidebarContext`
@@ -60,44 +59,46 @@ type OverlayMenuProps = {
children?: React.ReactNode;
};
-const useStyles = makeStyles(theme => ({
- root: {
- position: 'fixed',
- backgroundColor: theme.palette.navigation.background,
- color: theme.palette.navigation.color,
- bottom: 0,
- left: 0,
- right: 0,
- zIndex: theme.zIndex.snackbar,
- // SidebarDivider color
- borderTop: '1px solid #383838',
- },
+const useStyles = makeStyles(
+ theme => ({
+ root: {
+ position: 'fixed',
+ backgroundColor: theme.palette.navigation.background,
+ color: theme.palette.navigation.color,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ zIndex: theme.zIndex.snackbar,
+ // SidebarDivider color
+ borderTop: '1px solid #383838',
+ },
- overlay: {
- background: theme.palette.navigation.background,
- width: '100%',
- bottom: `${sidebarConfig.mobileSidebarHeight}px`,
- height: `calc(100% - ${sidebarConfig.mobileSidebarHeight}px)`,
- flex: '0 1 auto',
- overflow: 'auto',
- },
+ overlay: props => ({
+ background: theme.palette.navigation.background,
+ width: '100%',
+ bottom: `${props.sidebarConfig.mobileSidebarHeight}px`,
+ height: `calc(100% - ${props.sidebarConfig.mobileSidebarHeight}px)`,
+ flex: '0 1 auto',
+ overflow: 'auto',
+ }),
- overlayHeader: {
- display: 'flex',
- color: theme.palette.bursts.fontColor,
- alignItems: 'center',
- justifyContent: 'space-between',
- padding: theme.spacing(2, 3),
- },
+ overlayHeader: {
+ display: 'flex',
+ color: theme.palette.bursts.fontColor,
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ padding: theme.spacing(2, 3),
+ },
- overlayHeaderClose: {
- color: theme.palette.bursts.fontColor,
- },
+ overlayHeaderClose: {
+ color: theme.palette.bursts.fontColor,
+ },
- marginMobileSidebar: {
- marginBottom: `${sidebarConfig.mobileSidebarHeight}px`,
- },
-}));
+ marginMobileSidebar: props => ({
+ marginBottom: `${props.sidebarConfig.mobileSidebarHeight}px`,
+ }),
+ }),
+);
const sortSidebarGroupsForPriority = (children: React.ReactElement[]) =>
orderBy(
@@ -114,7 +115,8 @@ const OverlayMenu = ({
open,
onClose,
}: OverlayMenuProps) => {
- const classes = useStyles();
+ const { sidebarConfig } = useContext(SidebarConfigContext);
+ const classes = useStyles({ sidebarConfig });
return (
({
* @public
*/
export const MobileSidebar = (props: MobileSidebarProps) => {
+ const { sidebarConfig } = useContext(SidebarConfigContext);
const { children } = props;
- const classes = useStyles();
+ const classes = useStyles({ sidebarConfig });
const location = useLocation();
const [selectedMenuItemIndex, setSelectedMenuItemIndex] =
useState(-1);
diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx
index 464d0a893e..9ce4a9f997 100644
--- a/packages/core-components/src/layout/Sidebar/Page.tsx
+++ b/packages/core-components/src/layout/Sidebar/Page.tsx
@@ -25,29 +25,32 @@ import React, {
useRef,
useState,
} from 'react';
-import { sidebarConfig } from './config';
+import { SidebarConfigContext, SidebarConfig } from './config';
import { BackstageTheme } from '@backstage/theme';
import { LocalStorage } from './localStorage';
import useMediaQuery from '@material-ui/core/useMediaQuery';
export type SidebarPageClassKey = 'root';
-const useStyles = makeStyles(
+const useStyles = makeStyles<
+ BackstageTheme,
+ { sidebarConfig: SidebarConfig; isPinned: boolean }
+>(
theme => ({
- root: {
+ root: props => ({
width: '100%',
transition: 'padding-left 0.1s ease-out',
isolation: 'isolate',
[theme.breakpoints.up('sm')]: {
- paddingLeft: ({ isPinned }) =>
- isPinned
- ? sidebarConfig.drawerWidthOpen
- : sidebarConfig.drawerWidthClosed,
+ paddingLeft: () =>
+ props.isPinned
+ ? props.sidebarConfig.drawerWidthOpen
+ : props.sidebarConfig.drawerWidthClosed,
},
[theme.breakpoints.down('xs')]: {
- paddingBottom: sidebarConfig.mobileSidebarHeight,
+ paddingBottom: props.sidebarConfig.mobileSidebarHeight,
},
- },
+ }),
content: {
zIndex: 0,
isolation: 'isolate',
@@ -107,6 +110,7 @@ export function SidebarPage(props: SidebarPageProps) {
const [isPinned, setIsPinned] = useState(() =>
LocalStorage.getSidebarPinState(),
);
+ const { sidebarConfig } = useContext(SidebarConfigContext);
const contentRef = useRef(null);
@@ -130,7 +134,7 @@ export function SidebarPage(props: SidebarPageProps) {
const toggleSidebarPinState = () => setIsPinned(!isPinned);
- const classes = useStyles({ isPinned });
+ const classes = useStyles({ isPinned, sidebarConfig });
return (
(theme => ({
- root: {
- flexGrow: 0,
- margin: theme.spacing(0, 2),
- color: theme.palette.navigation.color,
- },
+const useStyles = makeStyles(
+ theme => ({
+ root: {
+ flexGrow: 0,
+ margin: theme.spacing(0, 2),
+ color: theme.palette.navigation.color,
+ },
- selected: {
- color: `${theme.palette.navigation.selectedColor}!important`,
- borderTop: `solid ${sidebarConfig.selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`,
- marginTop: '-1px',
- },
+ selected: props => ({
+ color: `${theme.palette.navigation.selectedColor}!important`,
+ borderTop: `solid ${props.sidebarConfig.selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`,
+ marginTop: '-1px',
+ }),
- label: {
- display: 'none',
- },
-}));
+ label: {
+ display: 'none',
+ },
+ }),
+);
/**
* Returns a MUI `BottomNavigationAction`, which is aware of the current location & the selected item in the `BottomNavigation`,
@@ -75,7 +77,8 @@ const useStyles = makeStyles(theme => ({
*/
const MobileSidebarGroup = (props: SidebarGroupProps) => {
const { to, label, icon, value } = props;
- const classes = useStyles();
+ const { sidebarConfig } = useContext(SidebarConfigContext);
+ const classes = useStyles({ sidebarConfig });
const location = useLocation();
const { selectedMenuItemIndex, setSelectedMenuItemIndex } =
useContext(MobileSidebarContext);
diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx
index 5a511b2094..d1b79263eb 100644
--- a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx
+++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx
@@ -19,72 +19,74 @@ import classnames from 'classnames';
import React, { ReactNode, useContext, useEffect, useState } from 'react';
import {
SidebarItemWithSubmenuContext,
- sidebarConfig,
SidebarContext,
- submenuConfig,
+ SidebarConfigContext,
+ SubmenuConfig,
} from './config';
import { BackstageTheme } from '@backstage/theme';
-const useStyles = (props: { left: number }) =>
- makeStyles(
- theme => ({
- root: {
- zIndex: 1000,
- position: 'relative',
- overflow: 'visible',
- width: theme.spacing(7) + 1,
+const useStyles = makeStyles<
+ BackstageTheme,
+ { submenuConfig: SubmenuConfig; left: number }
+>(
+ theme => ({
+ root: {
+ zIndex: 1000,
+ position: 'relative',
+ overflow: 'visible',
+ width: theme.spacing(7) + 1,
+ },
+ drawer: props => ({
+ display: 'flex',
+ flexFlow: 'column nowrap',
+ alignItems: 'flex-start',
+ position: 'fixed',
+ [theme.breakpoints.up('sm')]: {
+ marginLeft: props.left,
+ transition: theme.transitions.create('margin-left', {
+ easing: theme.transitions.easing.sharp,
+ duration: theme.transitions.duration.shortest,
+ }),
},
- drawer: {
- display: 'flex',
- flexFlow: 'column nowrap',
- alignItems: 'flex-start',
- position: 'fixed',
- [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,
- background: theme.palette.navigation.submenu?.background ?? '#404040',
- overflowX: 'hidden',
- msOverflowStyle: 'none',
- scrollbarWidth: 'none',
- cursor: 'default',
- width: submenuConfig.drawerWidthClosed,
- transitionDelay: `${submenuConfig.defaultOpenDelayMs}ms`,
- '& > *': {
- flexShrink: 0,
- },
- '&::-webkit-scrollbar': {
- display: 'none',
- },
+ top: 0,
+ bottom: 0,
+ padding: 0,
+ background: theme.palette.navigation.submenu?.background ?? '#404040',
+ overflowX: 'hidden',
+ msOverflowStyle: 'none',
+ scrollbarWidth: 'none',
+ cursor: 'default',
+ width: props.submenuConfig.drawerWidthClosed,
+ transitionDelay: `${props.submenuConfig.defaultOpenDelayMs}ms`,
+ '& > *': {
+ flexShrink: 0,
},
- drawerOpen: {
- width: submenuConfig.drawerWidthOpen,
- [theme.breakpoints.down('xs')]: {
- width: '100%',
- position: 'relative',
- paddingLeft: theme.spacing(3),
- left: 0,
- top: 0,
- },
- },
- title: {
- fontSize: 24,
- fontWeight: 500,
- color: '#FFF',
- padding: 20,
- [theme.breakpoints.down('xs')]: {
- display: 'none',
- },
+ '&::-webkit-scrollbar': {
+ display: 'none',
},
}),
- { name: 'BackstageSidebarSubmenu' },
- );
+ drawerOpen: props => ({
+ width: props.submenuConfig.drawerWidthOpen,
+ [theme.breakpoints.down('xs')]: {
+ width: '100%',
+ position: 'relative',
+ paddingLeft: theme.spacing(3),
+ left: 0,
+ top: 0,
+ },
+ }),
+ title: {
+ fontSize: 24,
+ fontWeight: 500,
+ color: '#FFF',
+ padding: 20,
+ [theme.breakpoints.down('xs')]: {
+ display: 'none',
+ },
+ },
+ }),
+ { name: 'BackstageSidebarSubmenu' },
+);
/**
* Holds a title for text Header of a sidebar submenu and children
@@ -104,10 +106,11 @@ export type SidebarSubmenuProps = {
*/
export const SidebarSubmenu = (props: SidebarSubmenuProps) => {
const { isOpen } = useContext(SidebarContext);
+ const { sidebarConfig, submenuConfig } = useContext(SidebarConfigContext);
const left = isOpen
? sidebarConfig.drawerWidthOpen
: sidebarConfig.drawerWidthClosed;
- const classes = useStyles({ left })();
+ const classes = useStyles({ left, submenuConfig });
const { isHoveredOn } = useContext(SidebarItemWithSubmenuContext);
const [isSubmenuOpen, setIsSubmenuOpen] = useState(false);
diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts
index b72e6f4601..08d469d5a3 100644
--- a/packages/core-components/src/layout/Sidebar/config.ts
+++ b/packages/core-components/src/layout/Sidebar/config.ts
@@ -20,6 +20,35 @@ const drawerWidthClosed = 72;
const iconPadding = 24;
const userBadgePadding = 18;
+/** @public **/
+export type SidebarOptions = {
+ drawerWidthClosed?: number;
+ drawerWidthOpen?: number;
+};
+
+/** @public **/
+export type SubmenuOptions = {
+ drawerWidthClosed?: number;
+ drawerWidthOpen?: number;
+};
+
+/** @internal **/
+export type SidebarConfig = {
+ drawerWidthClosed: number;
+ drawerWidthOpen: number;
+ defaultOpenDelayMs: number;
+ defaultCloseDelayMs: number;
+ defaultFadeDuration: number;
+ logoHeight: number;
+ iconContainerWidth: number;
+ iconSize: number;
+ iconPadding: number;
+ selectedIndicatorWidth: number;
+ userBadgePadding: number;
+ userBadgeDiameter: number;
+ mobileSidebarHeight: number;
+};
+
export const sidebarConfig = {
drawerWidthClosed,
drawerWidthOpen: 224,
@@ -38,12 +67,37 @@ export const sidebarConfig = {
mobileSidebarHeight: 56,
};
+export const makeSidebarConfig = (
+ customSidebarConfig: Partial,
+) => ({
+ ...sidebarConfig,
+ ...customSidebarConfig,
+ iconContainerWidth: sidebarConfig.drawerWidthClosed,
+ iconSize: sidebarConfig.drawerWidthClosed - sidebarConfig.iconPadding * 2,
+ userBadgeDiameter:
+ sidebarConfig.drawerWidthClosed - sidebarConfig.userBadgePadding * 2,
+});
+
+/** @internal **/
+export type SubmenuConfig = {
+ drawerWidthClosed: number;
+ drawerWidthOpen: number;
+ defaultOpenDelayMs: number;
+};
+
export const submenuConfig = {
drawerWidthClosed: 0,
drawerWidthOpen: 202,
defaultOpenDelayMs: sidebarConfig.defaultOpenDelayMs + 200,
};
+export const makeSidebarSubmenuConfig = (
+ customSubmenuConfig: Partial,
+) => ({
+ ...submenuConfig,
+ ...customSubmenuConfig,
+});
+
export const SIDEBAR_INTRO_LOCAL_STORAGE =
'@backstage/core/sidebar-intro-dismissed';
@@ -63,6 +117,16 @@ export const SidebarContext = createContext({
setOpen: () => {},
});
+export type SidebarConfigContextType = {
+ sidebarConfig: SidebarConfig;
+ submenuConfig: SubmenuConfig;
+};
+
+export const SidebarConfigContext = createContext({
+ sidebarConfig,
+ submenuConfig,
+});
+
export type SidebarItemWithSubmenuContextType = {
isHoveredOn: boolean;
setIsHoveredOn: Dispatch>;
diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts
index 96fedaa75a..ecc573fbda 100644
--- a/packages/core-components/src/layout/Sidebar/index.ts
+++ b/packages/core-components/src/layout/Sidebar/index.ts
@@ -59,4 +59,8 @@ export {
SidebarContext,
sidebarConfig,
} from './config';
-export type { SidebarContextType } from './config';
+export type {
+ SidebarContextType,
+ SidebarOptions,
+ SubmenuOptions,
+} from './config';