Chapter 25無料公開

Drawerとレスポンシブを使ってモバイルのときに隠れるメニューを作る

terrierscript
terrierscript
2021.05.09に更新

Drawerは素のまま実装しようとすると、かなり面倒なコンポーネントですが、Chakra UIが提供しているコンポーネントの一つにあります。

https://chakra-ui.com/docs/overlay/drawer

さて、ここでは一つの実例として、レスポンシブと組み合わせてモバイルの場合にだけDrawerになるメニューのサンプルを作っていきます

image

メニューを作る

基本的なメニューを作ってみましょう。例えばこんなふうになるはずです

const HoverLink = (props: LinkProps) => <Link rounded="base" _hover={{bg:"gray.200"}} p={2} {...props } />

const Navigation = () => {
  return (
    <Stack as="nav">
      <HoverLink href="/burger">Burger</HoverLink>
      <HoverLink href="/sidemenu">Sidemenu</HoverLink>
      <HoverLink href="/drink">Drink</HoverLink>
      <HoverLink href="/takeout">Takeout</HoverLink>
    </Stack>
  )
}

Drawerを作る

モバイル用のDrawerを作っていきます。概ね公式ページのサンプルと変わらないもので十分です。

const DrawerMenu = () => {
  // useDisclosureで閉じ・開きの管理
  const { isOpen, onOpen, onClose } = useDisclosure()
  const btnRef = React.useRef<HTMLButtonElement>(null)

  return (
    <>
      {/* ハンバーガーアイコン部分 */}
      <Button ref={btnRef} onClick={onOpen}>
        <HamburgerIcon />
      </Button>
      {/* Drawer部分 */}
      <Drawer
        isOpen={isOpen}
        onClose={onClose}
        placement="left"
        finalFocusRef={btnRef}
      >
        <DrawerOverlay>
          <DrawerContent>
            <DrawerCloseButton />
            <DrawerHeader>
              Menu
            </DrawerHeader>
            <DrawerBody>
              <Navigation /> {/* 先程作ったNavigatorを利用 */}
            </DrawerBody>
          </DrawerContent>
        </DrawerOverlay>
      </Drawer>
    </>
  )

useDisclosureはDrawerやMenuなど閉じ・開きをするコンポーネントで使われるものです。
btnRefとしているものはフォーカスのために利用しています。

このあたりは最初は深く考えずにDrawerコンポーネントに設定してあげましょう。

Layoutを作る

次にレスポンシブをするレイアウトのコンテナ部分を作っていきましょう。

const LayoutWithMenu : FC<{}> = ({ children }) => {
  return (
    <Stack>
      <HStack p={5}>
        <Box display={{ base: "block", md: "none" }}> {/* for mobile */}
          <DrawerMenu />{ /* Drawerを利用 */}
        </Box>
        <Heading>Humberger Shop</Heading>
      </HStack>
      <HStack alignItems="start" >
        <Box display={{ base: "none", md: "block" }} w={500} px={6}> {/* for desktop */}
          <Navigation />{ /* Navigationをそのまま利用 */}
        </Box>
        <Box>{children}</Box>
      </HStack>
    </Stack>
  )
}

display={{ base: "block", md: "none" }}の形式でレスポンシブにすることが出来ます。

あとは本体コンテンツを子として表示するように利用すれば完成です

const Page = () =>  {
  return (
    <LayoutWithMenu>
      <Box>
        Humburger Humburger Humburger Humburger Humburger Humburger Humburger
        Humburger Humburger Humburger Humburger Humburger Humburger Humburger
        Humburger Humburger Humburger Humburger
      </Box>
    </LayoutWithMenu>
  )
}

Demo