VuetifyでQiita風UIを作る - チーム切り替えメニュー

「VuetifyでQiita風のコンポーネントを作る」、今回はヘッダーのチーム切り替えメニュー編です。
[目的やVuetifyの説明、他のコンポーネントについてはこちら](https://qiita.com/totto357/items/40a48db0d1098c8a6fee)

## チーム切り替えメニューとは

ロゴの右側にある、下三角から開けるメニューのことです。(命名は勝手にしました。)
Qiita:Teamに所属していない方にはあまり馴染みのないものかもしれませんが、
Qiita:Teamにログインしていると、このメニューから切り替えることができます。

実際のイメージはこのような感じです。

**Qiita:Teamにログインしているとき**
![別チームログイン時.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97227/e13481c7-17a1-fe4e-c8ee-82055fed3320.png)

**Qiita:Teamにログインしていないとき**
![別チーム未ログイン時.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97227/48097662-a00f-8569-d022-dc4ffccae0de.png)


## Vuetifyで実装

まずはCodepenで作ったサンプルを紹介。

<p class="codepen" data-height="418" data-theme-id="0" data-default-tab="result" data-user="totto357" data-slug-hash="mgqxWg" style="height: 418px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="Vuetify Qiita風Team切り替えメニュー">
  <span>See the Pen <a href="https://codepen.io/totto357/pen/mgqxWg/">
  Vuetify Qiita風Team切り替えメニュー</a> by totto357 (<a href="https://codepen.io/totto357">@totto357</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

SFCで表現するとこちら。

```vue
<template lang="pug">
// チーム切り替えメニュー
v-menu(offset-y nudge-bottom="16px")

  // activator 
  v-sheet.mr-2(slot="activator" color="green darken-2")
    v-icon arrow_drop_down
    
  // 別のチームにログインしている 
  v-card(v-if="isLoginToTeam" width="300px")
    v-list(dense)
      v-subheader ログイン中のチーム
      template(v-for="team, i in teams")
        v-divider.my-1(v-if="i!=0" inset)
        v-list-tile(avatar @click="" :key="team.name")
          v-list-tile-action
            v-icon(v-if="team.view") mdi-check
          v-list-tile-content
            v-list-tile-title {{ team.name }}
          v-spacer
          v-layout(align-center justify-end)
            v-avatar(size="20px")
              v-img(:src="team.user.img")
            span.ml-1 {{ team.user.name }}
      v-divider.my-1
      v-list-tile(avatar @click="")
        v-list-tile-action
          v-icon mdi-exit-to-app
        v-list-tile-content
          v-list-tile-title 別のチームにログイン...
          
  // 別のチームにログインしていない
  v-card(v-else width="250px")
    v-list(dense)
      v-list-tile(avatar @click="" )
        v-list-tile-action
          v-icon mdi-check
        v-list-tile-content
          v-list-tile-title Qiita
      v-divider.my-1
      v-subheader ログイン中のチームがありません
      v-list-tile(avatar @click="")
        v-list-tile-action
          v-icon mdi-exit-to-app
        v-list-tile-content
          v-list-tile-title Qiita:Team にログイン...
</template>

<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
  data: () => ({
    isLoginToTeam: true,
    teams: [
      { view: false, name: "HogeTeam", user: { name: "totto357", img: "https://qiita-image-store.s3.amazonaws.com/0/97227/profile-images/1516012241" }},
      { view: true, name: "Qiita", user: { name: "totto357", img: "https://qiita-image-store.s3.amazonaws.com/0/97227/profile-images/1516012241" }},
    ],
  }),
})
</script>

```

### Tips

- `v-menu`や`v-dialog`などを表示する際のトリガーとなるボタンやテキストは、`activator`というスロットを通して表現することができる
- `offset-y`、`nudge-bottom`でヘッダーの下部とメニューの上部が揃うように位置調整
- `v-sheet`は高さと幅を自由に設定できるので、わりと重宝する
    - `v-card`のベースにもなってるコンポーネント
    - 公式のサンプルはあまりないけど、自由度が高いので好きに使える
- Qiita:Teamでログインしているかどうかの切り替えは`v-if`を使って切り替えを行う
- 区切り線として利用する`v-divider`は`inset`というプロパティで幅を調整することができる
- `v-list-tile-action`の幅が広すぎると感じたら無理に使わなくてもよい
- `v-icon`は[Font Awesome](https://fontawesome.com/icons?from=io)と[Material Design Icons
](https://materialdesignicons.com/)
使えるので、[Material Icons](https://material.io/tools/icons/?style=baseline)に合うものがなければ探してみるといいかも


## `v-menu`でよく使うプロパティ

今回のようにVuetifyでメニューを表示するのに利用する`v-menu`ですが、いろんなプロパティを設定することができます。
その中でも自分がよく使うプロパティを紹介したいと思います。

https://vuetifyjs.com/en/components/menus

### close-delay

メニューが閉じるまでの時間を制御することができます。
基本的には後述する`open-on-hover`と利用することが多いかも。

例えば、ホバーで開くメニューがあって、デザイン上`activator`とメニューをちょっと離している場合、
`activator`からメニューにカーソルを移動するとホバーが外れてしまうのでメニュー閉じてしまうことがあります。
そんな時に、メニューが閉じるまでのディレイを設定することでメニューが閉じないように制御することができます。

### close-on-content-click

メニューのコンテンツ部分(デフォルトスロットで渡したコンポーネント)をクリックしたときにメニューを閉じるかどうかを制御できます。
リンクしかないメニューであれば問題ないですが、何かを入力してもらいたいようなメニューだとクリックで閉じてしまうと困ります。
そんなときに`close-on-content-click`を`false`に設定すると、クリックしてもメニューが開いたままになります。

デフォルトは`true`なので、クリックしても閉じるようになっています。

### nudget-系

`activator`に対してメニューの位置をどの程度ずらすのか設定することができます。
例えば`nudge-bottom`であれば、メニューを表示する起点から下方向にどれだけずらすのかを指定できます。
(起点はデフォルトでは`activator`の左上。後述の`offset-系`で変更できる。)

今回の「チーム切り替えメニュー」ではヘッダーの下部とメニューの上部を合わせたかったので、
`nudge-bottom`に`16px`を指定しています。

### offset-x, offset-y

メニューを表示する起点を変更することができます。
`offset-x`を指定すると、x軸の起点が`activator`の左から右に変更されます。
`offset-y`を指定すると、y軸の起点が`activator`の上から下に変更されます。
つまり、まとめると以下となります。

- なし: 左上
- offset-xのみ: 右上
- offset-yのみ: 左下
- 両方: 右下

### open-on-hover

カーソルを`activator`にホバーさせるだけで、メニューを開くかどうかを制御できます。
`close-delay`でも書きましたが、`nudge-系`を使ってるときにはカーソル移動でメニューが消えてしまうことがあるので、注意が必要です。