angular-material2 を利用してリッチな DropdownButton を作る

# DropdownButton とは？

Qiita のこれですね！

<img width="165" alt="スクリーンショット 2017-12-12 22.04.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/41793/b5ef2764-9086-38e4-b314-2997110d43ff.png">


Qiita と同じように `限定公開` ←→ `一般公開` ...etc 的なUIが作りたくて 「どんなのが良いかなぁ」 と考えた結果、 DropdownButton なら綺麗にまとまりそう！ って思ったので自作することにしました。

今回のライブ Demo はこちら → [https://stackblitz.com/edit/angular-miln1y](https://stackblitz.com/edit/angular-miln1y)

チラ見用 gif も置いておきます

![Untitled2.gif](https://qiita-image-store.s3.amazonaws.com/0/41793/a4be59ea-8fe9-bba2-5f26-ff3a944693b9.gif)

# どうやって作ろうか

とりあえずググってみると、DropdownMenu を渇望している [公式の issue](https://github.com/angular/material2/issues/7765) を発見。

そうそう これが欲しかったんだよ！

![](https://camo.githubusercontent.com/ebf6822bd6b213c221d0f7d93c97fa755799beaf/68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f6d6174657269616c2d64657369676e2f7075626c6973682f6d6174657269616c5f765f31322f6173736574732f30427a6870355a347748626133526b6c5561475652626c39744d44672f636f6d706f6e656e74732d627574746f6e732d64726f70646f776e322e706e67)

しかし...、今のところ実装予定ではなさそうですね・・・

しゃーなぃ　自分で作るかぁ・・ ん？

- mat-menu
- mat-button-toggle-group

とかで実装したというコメントを発見

…なるほど、確かに組み合わせればそれっぽいのできそう！
ありがとう コメントの人！

# 実装してみる

## html

- だいたいこんな感じの構造かなぁ とイメージを膨らませつつ書く

```html
<mat-menu #menu="matMenu">
    <button mat-menu-item *ngFor="let item of selectItems" (click)="onSelect(item)">
        <mat-icon *ngIf="item.icon">{{ item.icon }}</mat-icon>
        <span>{{ item.text }}</span>
    </button>
</mat-menu>

<mat-button-toggle-group class="dropdown-button" [class.dropdown-button--disabled]="isDisabled()">
    <mat-button-toggle mat-ripple [disabled]="disabled" class="dropdown-button__main" [matRippleDisabled]="isDisabled()" (click)="onClick()">
        {{ selectedItem.text }}
    </mat-button-toggle>

    <mat-button-toggle mat-ripple class="dropdown-button__trigger" [matMenuTriggerFor]="menu">
        <mat-icon>arrow_drop_down</mat-icon>
    </mat-button-toggle>
</mat-button-toggle-group>
```

- mat-menu と mat-button-toggle を基本として構成
- ripple エフェクトも angular-material から お借りしました
- `mat-ripple` をつけるだけで、簡単にリッチ感漂う UI を実装できます 凄いです(*ﾟOﾟ)

## css

- `::ng-deep` で padding 等を矯正しつつ、お好みのスタイルを当てます

```scss
%default-theme {
  color: #fff;
  background-color: #00897b;

  transition: all 0.3s ease-out;

  cursor: pointer;
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

%disabled-theme {
  color: rgba(0, 0, 0, 0.5);
  background-color: rgba(0, 0, 0, 0.12);
}

.dropdown-button {
    position: relative;
    transform: translateY(-1px);
    transition: all 0.3s ease-out;

    &--disabled {
        box-shadow: none;
        transform: translateY(1px);

        .dropdown-button {
            &__main {
              @extend %disabled-theme;
            }

            &__trigger {
                @extend %disabled-theme;
            }
        }
    }

    &__main {
        @extend  %default-theme;

        & ::ng-deep .mat-button-toggle-label-content {
            font-size: 12px;
            font-weight: bold;
        }
    }

    &__trigger {
        @extend  %default-theme;

        & ::ng-deep .mat-button-toggle-label-content {
            padding: 0;
        }
    }
}

```
- disable 時の `translateY(1px)` がこだわりポイント （・∀・）
- 影が無くなるということは、物体はその分下に落ちる
- ↑ をさり気なく行うことで ワンランク上のエフェクトになる気がしています
- ホントは素直に 2px 移動させたいのですが、上下中央寄せされた際にずれが目立ってしまうので 1px と -1px にしました

## ts

```typescript
export interface DropdownButtonItem {
    type: string;
    text: string;

    icon?: string;
    isSelect?: boolean;
}
```

- 自分が欲しい機能を考慮して 注入する選択要素の interface を定義
- icon
    - メニュー側で表示する際に mat-icon を付ける
- isSelect
    - 最初に選択されている要素（必要なら）

これをこんな感じで定義して

```typescript
  selectedItems: DropdownButtonItem[] = [
    { icon: 'create', type: DropdownButtonItemTypes.Html, text: 'HTML' },
    { icon: 'save', type: DropdownButtonItemTypes.Css, text: 'CSS' },
    { icon: 'code', type: DropdownButtonItemTypes.Javascript, text: 'JS', isSelect: true }
  ];
```

作成した カスタムコンポーネント に渡します

```html
  <app-dropdown-button [disabled]="disabled" [selectItems]="selectedItems" (confirm)="onConfirm($event)"></app-dropdown-button>
```

- dropdown-button.component 自体はイベントの受け渡しをしているだけなので省略

# 完成！

angular-material2 の

- mat-menu
- mat-ripple
- mat-button-toggle
- mat-button-toggle-group

を組み合わせることで **簡単に** 思い通りの DropdownButton を作成することができました (=ﾟ▽ﾟ)／

もし angular を使っていて DropdownButton が必要な方がいらっしゃいましたら チューニングしてお使いくださいm(__)m