🎊

YouTubeに実装されている並び替えボタンを作る方法【Material-UI】

2020/12/04に公開

ReactのUIフレームワークを使う際、Material-UIを使う人が多いと思います。その際、YouTubeで使われているソーターを作りたいという人もいるでしょう。クリックしたら選択肢の吹き出しが表示されるものです。

なので今回はMaterial-UIを使ったソーターの作り方について解説します。

並び順ボタンのソースと完成イメージ

まず今回作成するソーターは以下のようなものになります。まずは全体のソースを見ていきましょう。

import React from 'react';
import Button from '@material-ui/core/Button';
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Grow from '@material-ui/core/Grow';
import Paper from '@material-ui/core/Paper';
import Popper from '@material-ui/core/Popper';
import MenuItem from '@material-ui/core/MenuItem';
import MenuList from '@material-ui/core/MenuList';
import SortIcon from '@material-ui/icons/Sort';

export default function sorter() {
  const [open, setOpen] = React.useState(false);  //吹き出しの表示判定
  const anchorRef = React.useRef(null);           //吹き出し表示位置
  
  //吹き出しを閉じるときの処理
  const handleClose = () => {
    setOpen(false);
  };
  
  //吹き出しに表示する項目
  const menuItems =
    [
      {
        attributes: {onClick : handleClose},
        item : "登録順"
      },
      {
        attributes: {onClick : handleClose},
        item : "名前順"
      },
      {
        attributes: {onClick : handleClose},
        item : "番号順"
      }
    ]

  //並び順ボタンをクリックしたときの処理
  const clickButton = () => {
    setOpen(!open);
  };

  return (
          <>
            {/* 並び順ボタン */}
            <Button
              ref={anchorRef}
              onClick={clickButton}
            >
              <SortIcon />並び順
            </Button>

            {/* 吹き出し */}
            <Popper 
              open={open}
              anchorEl={anchorRef.current} 
              transition>
              {({ TransitionProps}) => (
                // 表示時のアニメーションを設定
                <Grow
                  {...TransitionProps}
                  style={{transformOrigin:'top'}}
                >
                  {/*吹き出し表示したときの枠を作るため  */}
                  <Paper>
                    {/* クリックイベントが他の要素で発生したかを判定 */}
                    <ClickAwayListener onClickAway={handleClose}>
                      {/* 表示するメニュー */}
                      <MenuList>
                        {menuItems.map((item) => 
                          <MenuItem {...item.attributes}>{item.item}</MenuItem>
                        )}
                      </MenuList>
                    </ClickAwayListener>
                  </Paper>
                </Grow>
              )}
            </Popper>
          </>
  );
}

並び順のボタンを作成

まず初めに並び順のボタンを作成しています。

<Button
  ref={anchorRef}
  onClick={clickButton}
>
  <SortIcon />並び順
</Button>

ButtonタグのrefにuseRefの変数を値を渡すことでDOMにアクセスできるようにしています。この値を使って、後に吹き出しが表示される場所を指定しています。

onClickイベントハンドラーで表示判定用のステータスをtrueにして、吹き出しを表示にしています。これにより、クリックしたときに吹き出しが表示されるのです。

Buttonタグの中身はインポートしたソーターアイコンと「並び順」の文字を表示させています。

吹き出しの作成

次に吹き出し部分を作成していきます。以下のコンポーネントが必要です。

  • Popper:吹き出しの表示
  • Grow:吹き出し表示時のアニメーションを設定
  • Paper:吹き出し表示時の枠を表示
  • ClickAwayListener:吹き出し以外の部分がクリックされたときの処理を設定
  • MenuList:吹き出しに表示するメニューリスト
  • MenuItem:メニューの1項目分のデータ

順番に説明していきますね。

1.popperタグ

<Popper 
   open={open}
   anchorEl={anchorRef.current} 
   transition>

まずはpopperタグで吹き出しを設定しています

  • open:表示非表示の設定
  • anchorEl:吹き出しを表示する位置
  • transition:吹き出し表示時のアニメーション

anchorElで設定しているのは並び順のボタン位置です。先ほどuseRefで設定したボタン属性をセットしています。

2.Growタグ

{({ TransitionProps}) => (
   // 表示時のアニメーションを設定
   <Grow
     {...TransitionProps}
     style={{transformOrigin:'top'}}
   >

次にGrowタグです。Popperタグの子要素の関数の引数として渡されたTransitionPropsをそのまま渡しています。これにより、吹き出しが表示されたときにアニメーションが効くようになります。

そして、style属性のtransformOriginにtopを設定することで上から展開するようなアニメーションを可能にしているのです。

3.Paperタグ

3つ目がPaperタグです。これを設定することで吹き出しの枠を作っています。もし、paperを設定しなければ次のように背景と同化して扱いづらいため使用しているのです。

4.ClickAwayListenerタグ

4つ目がClickAwayListenerタグ。これにより子要素以外の部分にクリックイベントが発生したときに吹き出しを閉じるように設定しています。

5.MenuListタグ・MenuItemタグ

<MenuList>
  {menuItems.map((item) => 
    <MenuItem {...item.attributes}>{item.item}</MenuItem>
  )}
</MenuList>

MenuListタグで表示するリストの一覧を束ねて、MenuItemでそれぞれの項目を表示しています。それぞれのコンテンツをクリックした際はopenステータスをfalseに設定して、吹き出しをクローズしています。

まとめ

  • popperタグとgrowタグを組み合わせることで吹き出しを表示するときのアニメーションを可能にする

  • ClickAwayListenerタグで対象要素以外がクリックされたときに吹き出しを閉じる処理を行える

  • 吹き出しを作る際にpaperタグを使うことで背景と同化せず、見やすくなる

Discussion