angular
angular-material

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

DropdownButton とは?

Qiita のこれですね!

スクリーンショット 2017-12-12 22.04.53.png

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

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

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

Untitled2.gif

どうやって作ろうか

とりあえずググってみると、DropdownMenu を渇望している 公式の issue を発見。

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

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

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

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

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

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

実装してみる

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 等を矯正しつつ、お好みのスタイルを当てます
%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

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

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

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

  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 }
  ];

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

  <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