【MUI v5】メモしておきたいMenuの使い方
開発環境
詳しい開発環境はこちら
はじめに
新人エンジニアのこはるです。
仕事では主にJavaを使っているSES社員です。
個人開発をするときに調べたMUIコンポーネントの使い方をメモする為に書きました。
なので、使い方の全ては網羅していません。ご了承ください。
Menuコンポーネント編です。
↓公式ドキュメント
勉強しながらコードを書いていっている途中なので、もし間違いとかがありましたら優しく指摘してください。
他のコンポーネントについてはこちら
使い方早見
簡単なコードの使い方早見です。
今回はMenuコンポーネントのメモなので、他コンポーネントの記述は最小限です。
const Menu: FC = () => {
// メニューの開閉を管理
const [open, setOpen] = useState<boolean>(false);
// メニューを配置するHTML要素を格納する
const anchorEl = useRef<HTMLButtonElement>(null);
// メニュー開閉ハンドル
const handleClick = () => {
setOpen(!open);
};
// メニューを閉めるハンドル
const handleClose = () => {
setOpen(false);
};
return (
<>
<Button
// ボタンのHTML要素
ref={anchorEl}
// ボタンのタイプ
variant="contained"
// クリックアクション
onClick={handleClick}
// Style
sx={{ m: 5 }}
>
ボタン
</Button>
<Menu
// ここでボタンの位置にメニューを紐づける
// この紐づけのお陰でメニューがボタンの隣に出現する
// これが無いと画面の変なところでメニューが出現することになる
anchorEl={anchorEl.current}
// メニューの出現を管理
open={open}
// Falseだと、メニューを開いた時にメニューアイテムがフォーカスの対象になる
disableAutoFocusItem={false}
// Trueだとメニューが開いた時に一番上のメニューアイテムのオートフォーカスされる
autoFocus={false}
// 主にメニューを閉めたいときに発生するイベント
onClose={handleClose}
// Trueにすると、メニューが閉じている状態でもメニューのノードが存在するようになる
keepMounted
// メニューの開閉のアニメーション速度を設定できる
transitionDuration={"auto"}
// CSS in JS を記述できる(HTMLのstyle属性の役割を果たす)
sx={{}}
// 紐づけたHTML要素のどこを標準位置にしてメニューを配置するか設定できる
anchorOrigin={{
vertical: "bottom",
horizontal: "right"
}}
// メニューの起点を設定できる。アニメーションもこの起点から生えるように出現する
transformOrigin={{
vertical: "top",
horizontal: "right"
}}
// Menuコンポーネント内部で使用されているMenuListコンポーネントのPropsを変更できる
MenuListProps={{}}
// Menuコンポーネント内部で使用されているPaperコンポーネントのPropsを変更できる
PaperProps={{
// PaperProps.elevationはメニューのシャドーを調整できる(超重要!)
elevation: 3
}}
>
<MenuItem onClick={handleClose}>メニュー1</MenuItem>
<MenuItem onClick={handleClose}>メニュー2</MenuItem>
<MenuItem onClick={handleClose}>メニュー3</MenuItem>
</Menu>
</>
);
};
CodeSandboxのサンプル
CodeSandboxにサンプルを作成しました。
挙動はこちらで確認してください。
解説
公式の使用例とは少し違う形です。
open
とsetOpen
で開閉を制御しています。
こっちの方が自分としては分かりやすく、融通が利くと思っています。
ただこれは好みもあると思いますので、自身のやりやすいコードか、現場に合ったコードで書いていけばいいでしょう。
また、ひとつMenu
の注意点として、
見落としやすいですがMenu
はMenu APIで記載されているPropsの他にPopover APIとModal APIのPropsも使うことができます。
↓ここからPropsの解説に入ります。
anchorEl
ここに紐づけ用のHTML要素を指定すると、メニューがそのHTML要素を目印にポップするようになります。
これが無いと、メニューは画面の変なところからポップします。
早見では、MUIのButtonコンポーネントのref
属性からuseRef
でHTML要素を取得して、それをanchorEl
に指定しています。
これにより、メニューはButton
の隣にポップするようになります。
open
メニューの出現を管理します。
True
で開く。
False
で閉じる。
分かりやすいですね。
早見では、useState
のopne
変数で制御しています。
disableAutoFocusItem
False
だと、メニューを開いた時にメニューアイテムがフォーカスの対象になります。
デフォルトはFalse
です。
CodeSandboxのサンプルブラウザーをクリックした後に、Tabでボタンをフォーカス
↓
Enterでメニューを開いてください
メニュー1が灰色にオートフォーカスされましたでしょうか?
Trueに設定すると、このオートフォーカスが起こらなくなります。
メニューが開いた状態でもう一度Tabを押下するとメニュー1にフォーカスされます。あくまでMenu
の子要素(この場合はMenuItem
)に対するオートフォーカスが無くなるだけで、Menu
にはフォーカスされます
特別な理由が無い限り、ここはデフォルトのFalse
で良いでしょう。
autoFocus
True
だとメニューが開いた時に一番上のメニューアイテムにオートフォーカスされます。
デフォルトはTrue
です。
これは前述のdisableAutoFocusItem
に似ていて非常に分かりにくいのですが、
disableAutoFocusItem
はTrue
に設定すると
「Menu
の子要素にはフォーカスされないが、Menu
自体にはフォーカスが残る」
のに対し、
autoFocus
をFalse
に設定すると
「Menu
自体にもフォーカスされない」
という挙動が起こります。
autoFocus
をFalse
にすると「アクセシビリティに重大な影響を与える」とMUIの公式ドキュメントにも記載がありますので、こちらも特別な理由が無い限り、デフォルトのTrue
にしてください。
onClose
主にメニューを閉めたいときに発生するイベントハンドラーです。
メニューのポップ中に
- Escapeキーの押下
- Tabキーの押下
- メニュー以外の場所をクリック
のいづれかが発生したときにonClose
のイベントが発火します。
メニューを閉じる関数を指定するのが一般的でしょう。
keepMounted
Trueにすると、メニューが閉じている状態でもMenu
のノードが存在するようになります。
デフォルトはFalse
です。
Google ChromeのDevToolsを使ったら分かるのですが、Menu
は閉じているときDOMにノードが存在しません。
keepMounted
をTrue
にすることで、Menu
が閉じている状態でも「見えないだけでノードは存在している」というふうにできます。
これはSEOの対策や、メニューのポップ速度を最大化したいときに使用できます。
transitionDuration
メニューの開閉のアニメーション速度を設定できます。
デフォルトはauto
です。
速度の単位はms(ミリ秒)で、Number
型で設定できます。
他にも、Object
型{ appear?: number, enter?: number, exit?: number }
でそれぞれのメンバーを個別に設定することで、出現時、エンター時、終了時それぞれの速度を細かく設定できます。
sx
CSS in JS を記述できます。
役割はHTMLのstyle
属性とほぼ同じです。
但し、型はObject
型です。
詳しくは公式ドキュメントを参照
anchorOrigin
紐づけたHTML要素のどこを標準位置にしてメニューを配置するか設定できます。
データ型は、
{
horizontal: 'center' | 'left' | 'right' | number,
vertical: 'bottom' | 'center' | 'top' | number
}
です。
早見では右下{vertical: "bottom",horizontal: "right"}
に設定しているため、メニューはボタンの右下からポップします。
transformOrigin
メニューの起点を設定できます。
アニメーションもこの起点から生えるように出現します。
データ型は、
{
horizontal: 'center' | 'left' | 'right' | number,
vertical: 'bottom' | 'center' | 'top' | number
}
です。
早見では右上{vertical: "top",horizontal: "right"}
に設定しているため、メニューは右上を起点にポップします。
MenuListProps
Menu
内部で使用されているMenuList
のPropsを変更できます。
MenuList
のPropsについては公式ドキュメントを参考にしてください。
PaperProps
Menu
内部で使用されているPaper
の属性を変更できます。
PaperProps.elevation
を利用すると、メニューのシャドーを調整できます!
ここ重要です!
PaperProps={{
elevation: 3
}}
Paper
のPropsについては公式ドキュメントを参考にしてください。
カスタマイズしたメニュー
こちらでは、カスタマイズしたメニューを載せていきます。
普通のMenu
とは違う機能を使いたいときに利用します。
身も蓋もありませんが、
ぶっちゃけ、私はMenu
よりもこっちを基本的に使う事になると思っています。
何故かというと、デフォルトのMenu
はユーザビリティで欠点を抱えていると私は考えているからです。
CodeSandboxのサンプルを確認していただきたいのですが、左側のボタンのメニューを開いた状態で、右側のボタンを押そうとすると直ぐに押せないですよね。
一度クリックでメニューを閉じないと、他の操作ができません。
これはマウス操作に慣れている人だと、恐らく2、3回はクリックを空振りすると思います。
(スマホでも、タップしても中々操作ができず数回タップして「おっ、やっと開いた」となるはずです)
こうした戸惑いやちょっとしたイラだちは、ユーザー体験(UX)的にあまりよろしくないでしょう。
これが、デフォルトのMenu
の欠点です。
(これ、実はMenu
だけでどうにかなるんでしょうか? できるなら恥ずかしいのですが……(/ω\))
カスタマイズのCodeSandboxのサンプル
この欠点を無くしたカスタマイズがこちらです。
メニューがポップしている状態でも、別の場所をクリックできるようになっています。
Menu
は元々、Popper
やGrow
、Paper
などのMUIの別コンポーネントの組み合わせなので、それを自分でつくった感じになります。
コンポーネントの分割などちゃんと出来ていないので、このままだと使いずらいですが……
いずれもっと使いやすいように改良します。
まとめ
今回はMenu
の使い方をメモしました。
他のコンポーネントについてはまた次回。
結構まとめるのに時間が掛かったので、ちょっとずつ追加していこうと思います。
それではまた(´・ω・`)ノシ
Discussion