Office UI FabricでパネルUIを表示する

まる1日分の周回遅れが発生しているOffice UI Fabric Advent Calendar、第11日目は[パネル](http://dev.office.com/fabric/components/panel)について解説します。

## OfficeUIのパネル

### パネルのサンプルプログラム

[Office UI Fabricでダイアログボックスを表示する](http://qiita.com/furandon_pig/items/7543e483f20be7619369)でも少し触れたように、Office UI Fabric(以降"OfficeUI")では設定画面の表示方法として、[ダイアログ](http://dev.office.com/fabric/components/dialog)の他に[パネル](http://dev.office.com/fabric/components/panel)を使うという方法もありそうです。

設定項目をパネルで設定する簡単なサンプルを作成してみました。以下のような表示になります。パネルもダイアログの表示と同じく、モーダル状態で表示されます(設定項目の値を[トグルボタン](http://dev.office.com/fabric/components/toggle)表示にしてみたところ、ディップスイッチみたいな感じになってしまいました……)。

![img600.png](https://qiita-image-store.s3.amazonaws.com/0/61137/9150cee1-5171-a8fe-b76a-bd5442486890.png)

サンプルソースコードは以下になります。

```html
<!DOCTYPE html>
<html ng-app="App">
  <head>
    <meta charset="utf-8" />
    <title>Panel Sample</title>
    <link rel="stylesheet" href="//appsforoffice.microsoft.com/fabric/1.0/fabric.min.css">
    <link rel="stylesheet" href="//appsforoffice.microsoft.com/fabric/1.0/fabric.components.min.css">
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script type="text/javascript" src="https://rawgit.com/furandon-pig/701ec8a958b4d80c34fb/raw/c3113233b4c8cad3144ee0947d79aed3e3d13c7a/officeui_panel_helper.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script>
      $(document).ready(function() {
        if ($.fn.Panel) {
            $('.ms-Panel').Panel();
        }
        // Vanilla JS Components
        if (typeof fabric !== "undefined") {
          if ('Panel' in fabric) {
            var element = document.querySelector('.ms-Panel');
            var component = new fabric['Panel'](element);
          }
        }
      });
    </script>
  </head>
  <body ng-controller="AppController">
    <div class="ms-Grid-col ms-u-sm6 ms-u-md8 ms-u-lg12">
      <button class="ms-Button ms-Button--primary js-togglePanel"> <!-- ボタンを表示する -->
        <span class="ms-Button-label">
          <i class="ms-Icon ms-Icon--gear" aria-hidden="true"></i>設定
        </span>
      </button>
    </div>
    <div class="ms-Grid-col ms-u-sm6 ms-u-md8 ms-u-lg12">
      <div class="ms-Grid-col ms-u-sm6">
        <div class="ms-Table">
          <div class="ms-Table-row">
            <span class="ms-Table-cell">設定項目</span>
            <span class="ms-Table-cell">設定値</span>
            <span class="ms-Table-cell"><br></span>
          </div>
          <div class="ms-Table-row" ng-repeat="configItem in configItems">
            <span class="ms-Table-cell">{{configItem.text}}</span>
            <span class="ms-Table-cell">
              <label style="color:{{(configItem.value==true)?'crimson':'green'}};">
                {{configItem.value}}
              </label>
            </span>
            <span class="ms-Table-cell">
              <button class="ms-Button ms-Button--command" ng-click="removeConfig($index)">
                <span class="ms-Button-label" style="color:crimson;">
                  <i class="ms-Icon ms-Icon--trash" aria-hidden="true"></i>削除
                </span>
              </button>
            </span>
          </div>
        </div> <!-- end of ms-Table -->
        <button class="ms-Button ms-Button--command" ng-click="addConfig()">
          <span class="ms-Button-label" style="color:green;">
            <i class="ms-Icon ms-Icon--plus" aria-hidden="true"></i>追加
          </span>
        </button>
      </div>
    </div>
    <div class="ms-Panel"> <!-- ここからパネル -->
      <div class="ms-Overlay ms-Overlay--dark js-togglePanel"><!-- 空のDIVにする --></div>
      <div class="ms-Panel-main">
        <div class="ms-Panel-commands">
          <div class="ms-CommandBar">
            <div class="ms-CommandBar-mainArea">
              <div class="ms-CommandBar-sideCommands">
                <div class="ms-CommandBarItem">
                  <div class="ms-CommandBarItem-linkWrapper">
                    <div class="ms-CommandBarItem-link">
                      <span class="ms-CommandBarItem-icon ms-Icon ms-Icon--star"></span>
                      <span class="ms-CommandBarItem-commandText ms-font-m ms-font-weight-regular">設定</span>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div> <!-- end of ms-Panel-commands -->
        <div class="ms-Panel-contentInner">
          <span class="ms-Panel-headerText">
            <i class="ms-Icon ms-Icon--gear" aria-hidden="true"></i>設定
          </span>
          <div ng-repeat="configItem in configItems">
            <span class="ms-Toggle ms-Toggle--textLeft" style="margin-bottom:8px;"> <!-- トグルボタン -->
              <span class="ms-Toggle-description">{{configItem.text}}</span>
              <input id="toggle{{$index}}" type="checkbox" class="ms-Toggle-input" ng-model="configItem.value">
              <label for="toggle{{$index}}" class="ms-Toggle-field">
                <span class="ms-Label ms-Label--off">OFF</span>
                <span class="ms-Label ms-Label--on">ON</span>
              </label>
            </span> <!-- end of ms-Toggle -->
          </div>
        </div> <!-- end of ms-Panel-contentInner -->
      </div> <!-- end of ms-Panel-main -->
    </div> <!-- end of ms-Panel -->
    <script>
      var app = angular.module('App', []);
      app.controller('AppController', ['$scope',
        function($scope) {
          $scope.init = function() {
            index = 0;
            $scope.configItems = [
              { text: "設定項目(" + (++index) + ")", value: false },
              { text: "設定項目(" + (++index) + ")", value: false },
              { text: "設定項目(" + (++index) + ")", value: false },
            ];
          }
          $scope.addConfig = function() {
            $scope.configItems.push({ text: "設定項目(" + (++index) + ")", value: false });
          }
          $scope.removeConfig = function(index) {
            $scope.configItems.splice(index, 1);
          }
          $scope.init();
        }
      ]);
    </script>
  </body>
</html>
```

### OfficeUIパネルを使用する際の(バッド)ノウハウ

このパネルUIですが、[ラジオボタンとドロップダウンリスト](http://qiita.com/furandon_pig/items/8b543a1df5df2ef15974)のケースと同じく、若干のバッドノウハウ(MSの[Panel](http://dev.office.com/fabric/components/panel)のサンプルを見ただけでは分からない使い方・ハマり所)があります。

#### モーダル表示は空のDIV要素にする

パネルUIはモーダル状態で表示されます。ということは、モーダル表示用のクラス(ちゃんと確認してないけど、おそらく「ms-Overlay」)でパネルの要素を挟む形になるのかな……？と考えるも、それだと上手く表示されません。

正しく(？)は以下のように、「ms-Overlay」クラスを指定した空のDIV要素を記述してからパネルの中身を記述する形になります。

```html
<div class="ms-Panel"> <!-- ここからパネル -->
  <div class="ms-Overlay ms-Overlay--dark js-togglePanel"><!-- 空のDIVにする --></div>
  ...
```

#### パネルUIの初期化

[ラジオボタンとドロップダウンリスト](http://qiita.com/furandon_pig/items/8b543a1df5df2ef15974)では、ページ読み込み時にドロップダウンリストの表示項目を初期化するコードが必要でした。

パネルでも同様に、ページ読み込み時にパネルの初期化を行う必要があります。先のサンプルコードの以下の場所が初期化を行っている箇所です。

```js
      $(document).ready(function() {
        if ($.fn.Panel) {
            $('.ms-Panel').Panel();
        }
```

Panel()の定義は外部のJavaScriptファイルで行っており、筆者のGistに用意した[officeui_panel_helper.js](https://gist.github.com/furandon-pig/701ec8a958b4d80c34fb)を読むこむ形になっています(GistからJavaScriptを読み込むにはrawgitを経由するといった別のバッドノウハウも必要になります……)。

```html
<script type="text/javascript" src="https://rawgit.com/furandon-pig/701ec8a958b4d80c34fb/raw/c3113233b4c8cad3144ee0947d79aed3e3d13c7a/officeui_panel_helper.js"></script>
```

## まとめ

まる1日分の周回遅れが発生した第11日目の記事ではパネルUIについて解説しました。ダイアログとパネルを使い分けることで、効果的なUIが作れそうです。

## 参考URL

 * [Panel](http://dev.office.com/fabric/components/panel)