This is my approach to managing sidebars, which are responsive, RTL compatible, and support multiple languages using the chakra-ui
library.
The layout:
import {
Box,
Drawer,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
Grid,
GridItem,
useBreakpointValue,
} from "@chakra-ui/react";
import { ReactElement, useContext, useEffect } from "react";
import { SideBarSizeContext } from "../../contexts";
import Sidebar from "./Components/Sidebar";
import { useTheme } from "@chakra-ui/system";
export default function ProjectLayout({ children }: any): ReactElement {
const { direction } = useTheme();
const { sidebarSize, setSidebarSize } = useContext(SideBarSizeContext);
const isMobile = useBreakpointValue({ base: true, md: false });
useEffect(() => {
isMobile && setSidebarSize("hide");
}, [isMobile, setSidebarSize]);
return (
<Grid
templateColumns={{ base: "auto", md: "min-content auto" }}
css={{ transition: "2s" }}
>
{!isMobile ? (
<GridItem minH="100vh" bg="facebook.500" color="white">
{sidebarSize !== "hide" && (
<Box paddingBlockStart="1.5">
{/* <Flex h="3rem">Logo</Flex> */}
<Sidebar sidbarWidth={sidebarSize} />
</Box>
)}
</GridItem>
) : (
<Drawer
isOpen={sidebarSize !== "hide"}
onClose={() => setSidebarSize("hide")}
colorScheme="facebook"
size="xs"
placement={direction === "ltr" ? "left" : "right"}
>
<DrawerOverlay />
<DrawerContent width="2.5" dir={direction}>
<DrawerCloseButton onClick={() => setSidebarSize("hide")} />
<DrawerHeader>Drawer heading text</DrawerHeader>
<DrawerBody p="0">
<Sidebar sidbarWidth="open-wide" />
</DrawerBody>
<DrawerFooter></DrawerFooter>
</DrawerContent>
</Drawer>
)}
<GridItem>
<Box bg="gray.100" h="100%">
{children}
</Box>
</GridItem>
</Grid>
);
}
The context:
import { createContext } from "react";
interface LangCTX {
locale: string;
changeLocale: React.Dispatch<React.SetStateAction<string>>;
}
export const LanguageContext = createContext({} as LangCTX);
interface SideBarSizeCTX {
sidebarSize: "compact" | "open-wide" | "hide";
setSidebarSize: React.Dispatch<
React.SetStateAction<"compact" | "open-wide" | "hide">
>;
}
export const SideBarSizeContext = createContext({} as SideBarSizeCTX);
The sidebar:
import { Flex, List, ListIcon, ListItem, Text } from "@chakra-ui/react";
import React, { ReactElement } from "react";
import { HiOutlineCube, HiOutlineServer } from "react-icons/hi";
import { RiScales3Line, RiKeyLine } from "react-icons/ri";
import { GiFirewall } from "react-icons/gi";
import { FaNetworkWired } from "react-icons/fa";
import { BiNetworkChart } from "react-icons/bi";
import { Link as ReactRouterLink, useParams } from "react-router-dom";
import { FormattedMessage } from "react-intl";
interface Props {
sidbarWidth: "compact" | "open-wide" | "hide";
}
export default function Sidebar({ sidbarWidth }: Props): ReactElement {
const { id }: any = useParams();
return (
<Flex w="100%" justifyContent="flex-start" alignItems="center">
<List spacing={3} colorScheme="facebook">
<ListItem
as={ReactRouterLink}
to={`/project/${id}/servers`}
justifyContent="flex-start"
alignItems="center"
display="flex"
flexDirection={sidbarWidth === "open-wide" ? "row" : "column"}
gridGap={2}
title="servers"
p="2.5"
>
<ListIcon m={0} p={0} as={HiOutlineServer} w={6} h={6} />
{sidbarWidth === "open-wide" && (
<Text fontSize="xs" textAlign="center" whiteSpace="nowrap">
<FormattedMessage id="Sidebar.Servers" defaultMessage="Servers" />
</Text>
)}
</ListItem>
<ListItem
as={ReactRouterLink}
to={`/project/${id}/volumes`}
justifyContent="flex-start"
alignItems="center"
display="flex"
flexDirection={sidbarWidth === "open-wide" ? "row" : "column"}
gridGap={2}
title="volumes"
p="2.5"
>
<ListIcon m={0} p={0} as={HiOutlineCube} w={6} h={6} />
{sidbarWidth === "open-wide" && (
<Text fontSize="xs" textAlign="center" whiteSpace="nowrap">
<FormattedMessage id="Sidebar.Volumes" defaultMessage="Volumes" />
</Text>
)}
</ListItem>
<!-- More sidebar items -->
</List>
</Flex>
);
}
The language provider for App.js:
import { ChakraProvider, extendTheme } from "@chakra-ui/react";
import { ReactElement, useEffect, useState } from "react";
import { IntlProvider } from "react-intl";
import { LanguageContext } from "../contexts";
<!-- Code omitted for brevity -->
AppBar > Toggle SideBar size button: (hide, compact, open-wide):
import { Flex, IconButton, useBreakpointValue } from "@chakra-ui/react";
import { ReactElement } from "react";
import { MdExitToApp, MdMenu } from "react-icons/md";
import { useHistory } from "react-router-dom";
import LanguageSwitcher from "./LanguageSwitcher";
interface Props {
setSideBarSize: Function;
}
export default function AppBar({ setSideBarSize }: Props): ReactElement {
const history = useHistory();
const variant = useBreakpointValue({
base: () =>
setSideBarSize((prev: "compact" | "open-wide" | "hide") => {
return prev === "hide" ? "open-wide" : "hide";
}),
md: () =>
setSideBarSize((prev: "compact" | "open-wide" | "hide") => {
if (prev === "hide") return "open-wide";
if (prev === "compact") return "hide";
return "compact";
}),
});
return (
<Flex
bg="facebook.400"
color="white"
w="100%"
h="3rem"
alignItems="center"
paddingInline="0.3rem"
justifyContent="space-between"
>
<Flex alignItems="center">
<IconButton
variant="unstyled"
aria-label="Menu"
icon={<MdMenu />}
display="flex"
alignItems="center"
fontSize={24}
cursor="pointer"
onClick={variant}
/>
My Panel
</Flex>
<Flex>
<LanguageSwitcher />
<IconButton
variant="unstyled"
icon={<MdExitToApp />}
fontSize={24}
aria-label="Logout"
display="flex"
alignItems="center"
onClick={() => {
}}
/>
</Flex>
</Flex>
);
}