OSS版のAuraのタブコンポーネントをLightningコンポーネント化してみた

co-meetingの遠藤です。
Salesforce App Cloud Advent Calendar 2015の24日目の記事になります。

Lightningコンポーネントを勉強し始めた頃に、オープンソース版のAuraのコンポーネントをLightningコンポーネントとして動かしたソースがあったのでそれを整理して公開してみました。

動かしてみたのは[ui:tabset](http://documentation.auraframework.org/auradocs#reference?descriptor=ui:tabset&defType=component)になります。

ソースはGithubに公開して、パッケージとしてもまとめてみました

- Github: [hrendoh/aura-ui-tabset](https://github.com/hrendoh/aura-ui-tabset)
- パッケージインストールURL: 	[https://login.salesforce.com/packaging/installPackage.apexp?p0=04t28000000Pv8y](	https://login.salesforce.com/packaging/installPackage.apexp?p0=04t28000000Pv8y)

Lightningコンポーネントと3rdパーティフレームワークの組み合わせはパッケージではしばらく難しい状況であったり、一方では、業務アプリについてもそろそろLightningコンポーネントが使われはじめそう？な状況なので、Lightningコンポーネントでシステムの画面を作ることも増えてくのではないかと思っています。
しかし、実際Lightningだけで画面を作っていくにはUI部品が少ないので車輪の再開発が増えてしまう状況は避けられなさそうです。

そこで、オープンソース版のAuraコンポーネントをLightning コンポーネントとして利用できるようにすることで、不足しているコンポーネントを補うことができるかもなあと思ったりしました。

以下、AuraのコンポーネントをLightningコンポーネント化する手順もまとめてみましたので、今回対応したtabset以外を使いたくなった場合などに参考にしていただければと思います。

## オープンソース版 Aura のコンポーネント

Lightningコンポーネントは標準のUIコンポーネントがまだまだ足りない状況ですが、Lightning Design SystemがリリースされCSSのコンポーネントは揃ったところで、いずれはそれに対応する標準コンポーネントがリリースされるとのうわさです。

Winter16の時点でのUI標準部品は[Reference](https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/ref_intro.htm)に載っている39コンポーネントのみになります(名前空間uiのコンポーネント)が、UI部品としてまとまったコンポーネントはui:menuくらいという印象です。

一方、オープンソースの方の[Auraフレームワーク](http://documentation.auraframework.org/auradocs)を見てみると、以前から多くのコンポーネントがすでに用意されています。

例えば、Lightning Design Systemで用意されている、以下のようなコンポーネントが実装されています。

- Modal [ui:modal](http://documentation.auraframework.org/auradocs#reference?descriptor=ui:modal&defType=component)
- Popovers [ui:popoup](http://documentation.auraframework.org/auradocs#reference?descriptor=ui:popup&defType=component)
- Tabs: [ui:tabset](http://documentation.auraframework.org/auradocs#reference?descriptor=ui:tabset&defType=component)
- Trees: [ui:tree](http://documentation.auraframework.org/auradocs#reference?descriptor=ui:tree&defType=component)

その他、Design Systemにはないですが、SF1の商談ページのカルーセル [ui:carousel](http://documentation.auraframework.org/auradocs#reference?descriptor=ui:carousel&defType=component) などもあったりします。

以下、その一つ、ui:tabsetとそれに関連するコンポーネントをLightningコンポーネントとして使えるようにした際の手順になります。

## Aura UIコンポーネントのLightning Component化手順

### 1. 必要なコンポーネントの選択

ui:tabsetは以下のコンポーネントとイベントの組み合わせで実装されています。
かなり細かくコンポーネントがわけられているので、この作業が一番大変です。

コンポーネント

- ui:tabset
- ui:tab
- ui:tabBar
- ui:tabItem
- ui:tabOverflowMenuitem
- ui:tabOverflowMenuList
- ui:resizeObserver

インタフェース
- ui:visible.int

イベント

- ui:tabsetEvent
- ui:onTabHover
- ui:updateSize
- ui:click
- ui:dbclick
- ui:mousedown
- ui:mousemove
- ui:mouseout
- ui:mouseover
- ui:mouse
- ui:baseMouseEvent
- ui:baseDOMEvent
- ui:collapse

ここで、気をつける点は、すでに提供されているuiコンポーネントはそちらを使うようにします。
tabsetの場合はui:tabOverflowMenuitemがui:menuをコンポーネントを利用しているのでui:menuは含める必要はありません(ui:collapseイベントはなぜか使えなかったので追加しています)。

しかし、タブを実装するためにこれだけのコンポーネントとイベントのバンドルが増えるのもかなりいやな感じではあります。

効果がありそうなのは、tabsetはui:visibleインタフェースをimplementsしていますが、DOMマウスイベントをグローバルに提供するユースケースはすぐには思い浮かばないし、このインタフェースのために冗長なDOMイベントが増えているので、impementsを外して関連するイベントをごっそり削除するのが良いかもしれません。

### 2. Lightning Componentではサポートされていない属性を削除する

次にLightning Componentでは使えないタグが含まれているので、それを削除します。

tabsetでは、以下の属性を削除する必要がありました。

- support
- dynamicallyFlavorable
- provider

以下の場合 support="GA" を削除しています
```
<aura:component access="GLOBAL" support="GA" extensible="true" implements="c:visible" extends="c:popupTarget"
                dynamicallyFlavorable="true" description="A menu component that contains menu items.">
```

### 3. 名前空間 ui を c に変換する

名前空間uiはデフォルトのcに変更しておきます

例えば、以下のような箇所を修正します

```tabset/tabset.cmp
-        <ui:tabBar aura:id="tabBar" tabs="{!v.tabItems}"
+        <c:tabBar aura:id="tabBar" tabs="{!v.tabItems}"
```

```tabset/tabsetHelper.js;322行目
    getTabComponents: function (body) {
-        var type = "ui:tab";
+        var type = "c:tab";
```

```tabset/tabsetHelper.js;444行目
    CONSTANTS: {
-        TAB_DEF: "markup://ui:tab",
+        TAB_DEF: "markup://c:tab",
-        TAB_ITEM_DEF: "markup://c:tabItem"
+        TAB_ITEM_DEF: "markup://ui:tabItem"
    }
```

### 4. CSSのdiv.THIS、.THIS--defaultを.THISに修正する

tabset/tabsetFlavors.cssは、以下のようなLightning Componentでは使用できないCSSセレクタが指定されているので、.THISに修正します

```
- .THIS--default
+ .THIS
```

また、aura:flavorableという仕組みはLightningでは対応していないので、このCSSはtabset.cssに移動しました。

実際には、2,3,4の手順をしながら必要なコンポーネントを抽出しています。

## tabsetコンポーネントの使い方

使い方については、[uiExamples:tabsetExample](http://documentation.auraframework.org/auradocs#reference?descriptor=uiExamples:tabsetExample&defType=component)に一通り例があります。

その中の一つですが、以下のように使います。

```
<h1> Default active tab</h1>
<c:tabset class="tabset">
    <c:tab title="Tab 1">
        <p>Tab 1</p>
    <ui:inputText value="" label="Name" placeholder="input text" />
                    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed metus nibh, sodales a, porta at, vulputate eget, dui. Pellentesque ut nisl. Maecenas tortor turpis, interdum non, sodales non, iaculis ac, lacus. Vestibulum auctor, tortor quis iaculis malesuada, libero lectus bibendum purus, sit amet tincidunt quam turpis vel lacus. In pellentesque nisl non sem. Suspendisse nunc sem, pretium eget, cursus a, fringilla vel, urna.

        Aliquam commodo ullamcorper erat. Nullam vel justo in neque porttitor laoreet. Aenean lacus dui, consequat eu, adipiscing eget, nonummy non, nisi. Morbi nunc est, dignissim non, ornare sed, luctus eu, massa. Vivamus eget quam. Vivamus tincidunt diam nec urna. Curabitur velit.

    </c:tab>
    <c:tab title="Tab 2" >
        <p>Tab 2</p>
                    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed metus nibh, sodales a, porta at, vulputate eget, dui. Pellentesque ut nisl. Maecenas tortor turpis, interdum non, sodales non, iaculis ac, lacus. Vestibulum auctor, tortor quis iaculis malesuada, libero lectus bibendum purus, sit amet tincidunt quam turpis vel lacus. In pellentesque nisl non sem. Suspendisse nunc sem, pretium eget, cursus a, fringilla vel, urna.

        Aliquam commodo ullamcorper erat. Nullam vel justo in neque porttitor laoreet. Aenean lacus dui, consequat eu, adipiscing eget, nonummy non, nisi. Morbi nunc est, dignissim non, ornare sed, luctus eu, massa. Vivamus eget quam. Vivamus tincidunt diam nec urna. Curabitur velit.

    </c:tab>
    <c:tab title="Tab 3" active="{!true}" >
        <p>Tab 3</p>
                    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed metus nibh, sodales a, porta at, vulputate eget, dui. Pellentesque ut nisl. Maecenas tortor turpis, interdum non, sodales non, iaculis ac, lacus. Vestibulum auctor, tortor quis iaculis malesuada, libero lectus bibendum purus, sit amet tincidunt quam turpis vel lacus. In pellentesque nisl non sem. Suspendisse nunc sem, pretium eget, cursus a, fringilla vel, urna.

        Aliquam commodo ullamcorper erat. Nullam vel justo in neque porttitor laoreet. Aenean lacus dui, consequat eu, adipiscing eget, nonummy non, nisi. Morbi nunc est, dignissim non, ornare sed, luctus eu, massa. Vivamus eget quam. Vivamus tincidunt diam nec urna. Curabitur velit.

    </c:tab>
    <c:tab title="Tab 4" >
        <p>Tab 4</p>
                    Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed metus nibh, sodales a, porta at, vulputate eget, dui. Pellentesque ut nisl. Maecenas tortor turpis, interdum non, sodales non, iaculis ac, lacus. Vestibulum auctor, tortor quis iaculis malesuada, libero lectus bibendum purus, sit amet tincidunt quam turpis vel lacus. In pellentesque nisl non sem. Suspendisse nunc sem, pretium eget, cursus a, fringilla vel, urna.

        Aliquam commodo ullamcorper erat. Nullam vel justo in neque porttitor laoreet. Aenean lacus dui, consequat eu, adipiscing eget, nonummy non, nisi. Morbi nunc est, dignissim non, ornare sed, luctus eu, massa. Vivamus eget quam. Vivamus tincidunt diam nec urna. Curabitur velit.

    </c:tab>
</c:tabset>
```

SF1で表示すると以下のように表示されます

<img width="414" alt="IMG_0042.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/28827/94cb6a62-8b26-2d13-4219-f94f951d3166.png">

Auraのui:tabsetとすでにリリースされているui:menuの組み合わせでちゃんと動いているので、ui:tabsetが、正式にリリースされたら名前空間をcからuiに戻せば、アプリケーションはそのまま動作するのではないかと思うので一時的な対応としても良さそうです。

## Lightning Design Systemの適用

AuraフレームワークのコンポーネントのCSSは、綺麗なものではないのでLightning Design Systemの[Tabsコンポーネント](https://www.lightningdesignsystem.com/components/tabs)を適用してみました。

修正点は、Githubの[sldsブランチ](https://github.com/hrendoh/aura-ui-tabset/tree/slds)を参照ください

tabsetコンポーネントは、CSSのセレクターを渡せるように実装されていますが、SLDSのタグ構造になっていないため、コンポーネントの構造をSLDSに合わせて変更しています。

SLDS版を表示するとこんな感じです

<img width="414" alt="IMG_0043.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/28827/6d602d79-577b-68e2-239d-888be00b375d.png">

SLDSのTabsは、モバイルで表示するとイマイチですね。
こういった形のタブってモバイルであまり使わないのでしょうがないか。

ちなみに、moreの機能がSLDS対応ブランチではまだ未実装なので、Lightningコンポーネントもくもく会のときにでも気が向いたら追加修正してみます。

## まとめ

このように、オープンソース版のAuraに含まれるコンポーネントをLightning化することは、ロジックの修正無しにできるようです。
ピュアLightning コンポーネントで画面を作らなければならない場合、対応したコンポーネントが正式リリースされたら、アプリの変更は名前空間を戻すだけでいけるとしたら、標準部品が正式リリースされるまでの暫定対応としてはなかなか良い選択肢な気がします。

改めて整理してみて幾つか気がついた点としては

- 現行リリース済みのものも含めてuiコンポーネントとLightning Design Systemは、対応しているわけではないので、SLDSに対応したコンポーネントを出すにはまだ本体側の整理がかなり必要そう
- Lightning コンポーネントは依存関係の解決するしくみがなく、かつ継承があるのでコンポーネントが分割され数が多くなると依存関係を把握するのが大変。これは複数人での開発ではかなり支障があり
- Auraのコンポーネントを見るとReactJSなみにコンポーネントを分けることを想定していることがわかるが、バンドルが増えすぎるので、パッケージする仕組みがないとAppExchangeアプリではなかなか厳しい

特に業務アプリについては、はやく標準部品だけで作れるようになってほしいものですねえ。