【備忘録】Chakra UI v3でのアイコンボタンとドロワーの実装方法

## はじめに
Chakra UIでのアイコンとモーダルの実装方法がv2以前とv3で大きく変化したようです。

参考にしていた動画教材はv2以前がベースであり、今回はv3でアイコンとモーダルを実装した時の方法を備忘録代わりに書いていこうと思います。

## 想定する実装内容
* モーダルを開閉するボタンを作成し、ハンバーガーのアイコンを設置する
┗ボタンの背景色は周囲の色と同化させる
* ボタンを押下したらドロワーが開くようにする
* ×ボタンをドロワー内に設置し、押下するとドロワーが閉じるようにする

## ①v3以降のアイコンの実装方法

### 公式ドキュメント
https://chakra-ui.com/docs/get-started/migration

>Removed @chakra-ui/icons package. Prefer to use lucide-react or react-icons instead.

v3では`@chakra-ui/icons`が使えないため`lucide-react`もしくは`react-icons`を使います。
今回は`react-icons`を使うパターンを想定します。

### 実装方法

React Iconsの公式ページにアクセスし、お目当てのアイコンを探してインストール+インポートします。今回は`FaBars`というアイコンを使います。

https://react-icons.github.io/react-icons/


```:インストール方法(React Icons公式より引用)
npm install react-icons --save
```

アイコンは単体で使うのではなくボタン化させたいので、Chakra UIの`Icon Button`で囲むことでボタン化します。
この部分はv2以前の方法と変化ありません。

https://chakra-ui.com/docs/components/icon-button

```react:Header.tsx
import { Flex, Heading, Link, Box, IconButton } from "@chakra-ui/react";
import { FC, memo } from "react";
import { FaBars } from "react-icons/fa";

export const Header: FC = memo(() => {
  return (
    <>
     <IconButton size="sm" variant="plain" aria-label="メニューボタン" display={{ base: "block", md: "none" }}>
       <FaBars color="white" />
     </IconButton>
    </>
  );
});
```

※v3ではボタンの背景色を透明にしたい場合は`variant="plain"`で実装します。公式ドキュメントの下の方に説明がありました。


## ②v3以降のドロワーの実装方法

### 公式ドキュメント

https://chakra-ui.com/docs/components/drawer#examples

### 実装方法
v3でも`Drawer`を用いてドロワーを実装する点は一致していますが、コンポーネントの内部構造が変化しています。

公式のExampleには次のコードが記載されています。
```react:公式のUsage
<Drawer.Root>
  <Drawer.Backdrop />
  <Drawer.Trigger />
  <Drawer.Positioner>
    <Drawer.Content>
      <Drawer.CloseTrigger />
      <Drawer.Header>
        <Drawer.Title />
      </Drawer.Header>
      <Drawer.Body />
      <Drawer.Footer />
    </Drawer.Content>
  </Drawer.Positioner>
</Drawer.Root>
```
この中で特に実装において重要な部分を抜き出すと以下の部分になると思います。

* `<Drawer.Root>`：**ドロワーの一番外側の部分**。この部分にサイズや出現位置のプロパティを記載する。
* ` <Drawer.Trigger />`：**ドロワーを開く際のトリガー**。入れ子にした内側に開くボタンとして扱いたいコンポーネントを記述する。
* `<Drawer.Content>`：**ドロワーの内側の部分**。いわゆるinner。
* `<Drawer.CloseTrigger />`：**ドロワーの閉じるボタンのトリガー**。入れ子にした内側に閉じるボタンとして扱いたいコンポーネントを記述する。
* `<Drawer.Header>`：**ドロワーの上部(閉じるボタン付近)にあたる要素**。titleなどを入れることが多い。
* `<Drawer.Body />`：**モーダルのヘッダーとフッターで囲まれた部分**。入れ子の内側はHTMLを書ける。
* `<Drawer.Footer />`：**ドロワーの下部**。主に「OK」「Cancel」などのボタンが入ることが多い。


以上をもとに実際に記述したコードは次のようになりました。
```react:Header.tsx
import { Flex, Heading, Link, Box, IconButton, Drawer, CloseButton } from "@chakra-ui/react";
import { FC, memo } from "react";
import { FaBars } from "react-icons/fa";

export const Header: FC = memo(() => {
  return (
    <>
      <Flex as="nav" bg="teal.500" color="gray.50" align="center" justify="space-between" padding={{ base: 3, md: 5 }}>
        <Flex align="center" as="a" mr={8} _hover={{ cursor: "pointer" }}>
          <Heading as="h1" fontSize={{ base: "md", md: "lg" }}>
            ユーザー管理アプリ
          </Heading>
        </Flex>
        <Flex align="center" fontSize="sm" flexGrow={2} display={{ base: "none", md: "flex" }}>
          <Box pr={4}>
            <Link color="gray.50">ユーザー一覧</Link>
          </Box>
          <Link color="gray.50">設定</Link>
        </Flex>
        <Drawer.Root placement="start" size="xs">
          <Drawer.Trigger asChild>
            <IconButton size="sm" variant="plain" aria-label="メニューボタン" display={{ base: "block", md: "none" }}>
              <FaBars color="white" />
            </IconButton>
          </Drawer.Trigger>
          <Drawer.Backdrop />
          <Drawer.Positioner>
            <Drawer.Content>
              <Drawer.Header>
                <Drawer.Title />
              </Drawer.Header>
              <Drawer.Body>
                <Button w="100%">TOP</Button>
                <Button w="100%">ユーザー一覧</Button>
                <Button w="100%">設定</Button>
              </Drawer.Body>
              <Drawer.Footer />
              <Drawer.CloseTrigger asChild>
                <CloseButton size="sm" />
              </Drawer.CloseTrigger>
            </Drawer.Content>
          </Drawer.Positioner>
        </Drawer.Root>
      </Flex>
    </>
  );
});
```

ポイントとしては**HTML構成的に開くボタンを設置したい部分に`Drawer`のコンポーネント群を設置する**ことでした。
今回の例で言うと親要素の`<Flex>`がヘッダー全体を囲う要素でありその中にハンバーガーボタンを配置したかったので、`<Flex>`のモーダルの内側に`Drawer`のコンポーネント群を設定しました。

また、`<Drawer.Trigger asChild> </Drawer.Trigger>`で**開くボタンにしたいコンポーネントを囲む**ことで、`onClick`等のクリックイベントを記述しなくてもモーダルの開くボタンとして機能するようになります(`closeButton`でも同様です)。

なお従来通りクリックイベントを関数化してボタンの開閉を制御することも可能のようです。詳しくは公式ドキュメントの`Controlled`のサンプルコードをご確認ください。

https://chakra-ui.com/docs/components/drawer#examples

## 最後に
v3のドロワーの実装は一見難しそうに見える反面、開閉ボタンの構造などは使いやすくなっているように見えました。
この記事が参考になれば幸いです。


## 参考記事

https://qiita.com/masa_code/items/2fb0c4a63f42f0916e61

https://qiita.com/shin-tech/items/aee37cfe4b58bcc1bba0