🔖

Yamada UIを使ってどうしてもMantineのAppShellを作りたかっただけ

2024/11/14に公開

こんにちは!普段フロントエンド開発をしている人です🙌

最近はUI作成時によくMantineを使用しているのですが、Yamada UIもずうううっと気になっていたので触ってみたところ…
「え、AppShellない…🥺」ってなりました。

AppShellってなんだよって方はこちら→Mantineのドキュメント

もうちょこっと書けばアプリケーションの枠組みができる状態から開発するのに慣れていたため、1から書くのはめんどくさい…ので!
自分でAppShellっぽいもの作っておけばあとはパラメータ足すだけでいいやんって感じでゆるーく作りました。


まずはみんな大好きYamada UIのご紹介

日本初のReact UIコンポーネントライブラリです。概要は以下の開発者の山田さんが説明してくださっている記事があるのでそちらに飛んでみてください!
https://zenn.dev/hirotomoyamada/articles/15b6f46d12841b

ドキュメントはこちら↓
https://yamada-ui.com/ja/getting-started

英語さっぱりの私でも読める日本語ドキュメントでとっても助かってます!

Discordにしれっと参加しているのですが、結構頻繁に活動されている様子。(というか山田さんずっとmeeting roomにいる気がする)
私は眺めているだけなので、今回AppShell作って欲しいっていう文句言うだけの人になってます…許して

今回作成したもの

文句を言うだけの懺悔をしながら実際に作ってみたコードを紹介します。

AppShell.tsx
import { AlignJustifyIcon } from "@yamada-ui/lucide";
import { Box, Collapse, Flex, IconButton } from "@yamada-ui/react";
import React, { useState } from "react";

export type AppshellProps = {
  header?: React.ReactNode;
  navBar?: React.ReactNode;
  asideBar?: React.ReactNode;
  headerHeight?: number | string;
  navBarWidth?: number | string;
  asideBarWidth?: number | string;
} & React.PropsWithChildren;

export const AppShell = (props: AppshellProps) => {
  const {
    header,
    navBar,
    asideBar,
    children,
    headerHeight,
    navBarWidth,
    asideBarWidth,
  } = props;
  const [opened, setOpened] = useState<boolean>(true);
  return (
    <Flex style={{ flexDirection: "column", width: "100%", height: "100%" }}>
      {header && (
        <Flex
          style={{
            width: "100%",
            height: headerHeight ?? "40px",
            borderBottom: "1px solid lightgray",
          }}
        >
          {(navBar || asideBar) && (
            <IconButton
              icon={<AlignJustifyIcon />}
              variant={"link"}
              onClick={() => {
                setOpened(!opened);
              }}
              style={{ width: "36px", height: "36px", margin: "auto 0" }}
            />
          )}
          <Box style={{ width: "calc(100% - 36px)", height: "100%" }}>
            {header}
          </Box>
        </Flex>
      )}
      <Flex
        style={{
          width: "100%",
          height: `calc(100% - ${headerHeight ?? "40px"})`,
        }}
      >
        {navBar && (
          <Collapse isOpen={opened} w={opened ? navBarWidth ?? "200px" : 0}>
            <Box
              style={{
                width: "100%",
                borderRight: "1px solid lightgray",
                height: "100%",
              }}
            >
              {navBar}
            </Box>
          </Collapse>
        )}
        <Box style={{ flex: 1 }}>{children}</Box>
        {asideBar && (
          <Collapse isOpen={opened} w={opened ? asideBarWidth ?? "200px" : 0}>
            <Box
              style={{
                width: "100%",
                borderLeft: "1px solid lightgray",
              }}
            >
              {asideBar}
            </Box>
          </Collapse>
        )}
      </Flex>
    </Flex>
  );
};

実際に使ってみたらこんな感じ

App.tsx
import { Button, Flex, IconButton, Text, useColorMode } from "@yamada-ui/react";
import { AppShell } from "../components";
import { MoonIcon, SunIcon } from "@yamada-ui/lucide";

export const ContainerApp = () => {
  const { colorMode, changeColorMode } = useColorMode();
  return (
    <AppShell
      header={
        <Flex w={"100%"} p={"0 0.5rem"} h={"100%"} justify={"space-between"}>
          <Text m={"auto 0"}>これはタイトルです</Text>
          <IconButton
            icon={colorMode === "dark" ? <MoonIcon /> : <SunIcon />}
            onClick={() =>
              changeColorMode(colorMode === "light" ? "dark" : "light")
            }
            variant={"outline"}
            w={"36px"}
            h={"36px"}
            m={"auto 0"}
          />
        </Flex>
      }
      navBar={
        <Flex w={"100%"} h={"100%"} direction={"column"}>
          {Array.from({ length: 10 }).map((_, i) => (
            <Button key={`navButton-${i}`} variant={"ghost"}>
              navButton-{i + 1}
            </Button>
          ))}
        </Flex>
      }
    >
      <Flex bg={"#79B6EB"} w={"100%"} h={"100%"}>
        ここはmainComponentです
      </Flex>
    </AppShell>
  );
};

どちらでもできるよ的な感じを伝えたかったため、styleの書き方はあえて統一していません。

普段からこんな汚いコード書いているのかよって思わないでください…もっと綺麗に書いているはずです多分…

出来上がった実際の画面がこちら

まあ枠組みはできたのではって感じですね。

今回使用したYamada UIのコンポーネントについて

個人的にいいなと思ったのは
https://yamada-ui.com/ja/components/transitions/collapse
https://yamada-ui.com/ja/hooks/use-color-mode

の二つ。
Collapseは今回サイドバーの開閉で使用していて、これからも色々なもので使えそう🧐
useColorModeは私がMantineを使用していて欲しいなあと思っていたものだったので今回紹介したくて無理やり載せました笑
(MantineにもuseColorSchemaというものがありますが、現在のColorのモードを取得することしかできません)


まとめ

ほぼ自分用と言ってもいいようなAppShellの作成を行いました。
使用するときに、必要なパラメータがあれば追加していく感じで使おうと思います。
こんなパラメータもあった方がいいのではないかっていうご意見ありましたら、ぜひお聞かせください🙌

山田さんAppShell的なコンポーネント作ってください…

Discussion