PolymerでWeb Componentsを自作してbowerで公開する

2014/11/14に<a href="https://www.polymer-project.org/">Polymer</a>のv0.5.1がリリースされ、公開しているコードも合わせて更新したので、ついでにQiitaに記事投稿します。（以前<a href="https://atnd.org/events/52722">HTML5Minutes</a>というHTML5系のイベントで登壇した時に喋った内容を現時点でup-to-dateにしています。）

<strong><a href="https://github.com/tejitak/left-swipe-action">left-swipe-action</a></strong>というiOSのリストで左へスワイプすると後ろにActionボタンが現れるあれみたいなやつをWeb Componentsで作り公開しました。

Demoページ 
<a href="http://tejitak.github.io/left-swipe-action/demo.html">http://tejitak.github.io/left-swipe-action/demo.html</a>
(注：PolymerがサポートしていないAndroid標準のブラウザ等では見れないです)

画面イメージ１
<a href="http://www.tejitak.com/blog/wp-content/uploads/2014/08/スクリーンショット-2014-08-07-12.10.16.png"><img src="http://www.tejitak.com/blog/wp-content/uploads/2014/08/スクリーンショット-2014-08-07-12.10.16.png" alt="スクリーンショット 2014-08-07 12.10.16" width="413" height="355" class="alignnone size-full wp-image-592" /></a>

画面イメージ２
<a href="http://www.tejitak.com/blog/wp-content/uploads/2014/08/スクリーンショット-2014-08-07-12.04.19.png"><img src="http://www.tejitak.com/blog/wp-content/uploads/2014/08/スクリーンショット-2014-08-07-12.04.19.png" alt="スクリーンショット 2014-08-07 12.04.19" width="524" height="164" class="alignnone size-full wp-image-593" /></a>



本記事では、初めてWeb Componentsを作ってみるところから、<a href="http://bower.io/">bower</a>を使って、いくつか既にあるgalleryサイトへの登録までの流れをざっと紹介します。


<hr>
# なぜWeb Componentsを作って公開するのか

そもそもですが、Web Componentsを作って公開する意義はなんでしょうか。

使ってみるとわかるのですが、Web Componentsを使う側はめちゃくちゃラクです。ただ、HTMLのタグを埋め込むだけ。

これまで個人の開発者が作ってきたモジュールは、例えばjQueryのプラグインだとかAngularのディレクティブとかありますが、それらは基本的に知識のある技術者しか使えません。でも、Web Componentsの場合はただlinkタグで読み込んで、HTMLのマークアップをするだけなのでモジュールを「使う」という面での敷居が断然低くなります。

Web Componentsのギャラリー覗いて欲しいもの見つけたら配置して、マークアップのエンジニアさんやおそらくデザイナーさんでも、機能的に動く凄いものを作れてしまう時代がくるでしょう。つまり、これまでの公開されているJS/CSSモジュール等と違って、開発して公開したら使ってもらえる潜在的なユーザー層が爆発的に多くなるということです。

今後、開発者たちが作ったものをどんどん世の中に発信して良いcomponentsができてくれば自然と使う側も増え、良いエコシステムが出来上がってきそうな予感がしてます。

既にGalleryサイトもあります。
Custom Elements A Web Components Gallery for Modern Web Apps
<a href="http://customelements.io/">http://customelements.io/</a>

Web Components Catalog - Component Kitchen
<a href="http://component.kitchen/">http://component.kitchen/</a>

ゆくゆくはWeb Componentエンジニア（作る側）とマークアップによるアプリケーション作成エンジニア（使う側）と大きく職種としても分かれる可能性もありますね。そんな世の中が来る日もそう遠くない気がしますので、今Web Componentsを作って公開することは開発者が名を売るチャンスでもあるし、間違いなくこれから大きな変化が訪れるであろうWebの世界のムーブメントに乗っていくために重要なことだと思います！←大げさ？w

長い前置きはさておき、以下、ざっと初めて作って公開してみた流れを記述。

<hr>
# 目次
1. Polymerを勉強する
2. Web Componentsを実装する  
&nbsp;&nbsp;2-1. タグ名を決める  
&nbsp;&nbsp;2-2. Componentが受け取るattributeを決める  
&nbsp;&nbsp;2-3. Light DOMを想定する  
&nbsp;&nbsp;2-4. Shadow DOMを生成する  
&nbsp;&nbsp;2-5. テスト  
3.  Github.ioでデモを動かす
4.  Readmeをしっかり書く
5. Bowerに登録
6. Galleryサイトに登録されているか確認

<hr>
# 1. Polymerを勉強する


PolymerはGoogleが作ってるWeb ComponentのPolyfill(ネイティブ実装がされる前の互換実装)＆フレームワークです。Polymerの登場により急速にweb componentが普及し始めてきた感じがしています。

今回はpolymerを使用して実装しますが、実はWeb Componentsを実装するのにPolymerは必須ではありません。もっと軽量でサポートブラウザが多い<a href="https://github.com/WebReflection/document-register-element">document-register-element.js</a>というPolyfillも存在します。

document-register-element.jsは、Web Componentsを作るための最低限のpolyfillで、Polymerのような統合的にweb componentを実装するためのフレームワークのようなものでは無さそうです。

Polymerの方は、コミュニティの巨大さや活発さはやはり凄いです。そこそこチュートリアル的な記事も出回り始めてます。ただ、やはり<a href="http://www.polymer-project.org/docs/polymer/polymer.html">Polymer公式のドキュメント</a>が一番役に立つと思います。

<a href="http://stackoverflow.com/">stackoverflow</a>や<a href="https://groups.google.com/forum/?fromgroups=#!forum/polymer-dev">Google group polymer-dev</a>では色々な質問回答が活発に行われています。

今回はPolymerを使ってweb componentを作ってみました。
Polymerは大きく２つの要素に分かれます。

<strong>webcomponents.js (a.k.a v0.5.1以前のplatform.js）</strong>
Web ComponentsのPolyfill

<strong>Polymer.html</strong>
Web Componentsを作るためのフレームワーク

自分もまだPolymerが提供している機能とWeb Componentsの仕様としての機能を完璧に区別できている訳ではないですが、とりえあえず習うより慣れろの精神で作ってみています。

まずpolymerをbowerを使用してプロジェクトにもってきます。
`>bower install polymer --save`

そして、実行用のhtmlにwebcomponents.jsとpolymer.htmlを以下の様に読み込みます。

```index.html
<script src="bower_components/webcomponentsjs/webcomponents.js"></script>
<!--
Polymer v0.5.1以前の場合
<script src="bower_components/platform/platform.js"></script>
-->
<link rel="import" href="bower_components/polymer/polymer.html">
```



自作コンポーネントは、まずは依存web componentsを読み込み、以下の様にCSS + HTML + JSを一つのファイルに記述します。

```my-component.html
<!--
My Web Components Description
-->
<polymer-element name="my-component">
    <template>
        <!-- My CSS here -->
        <style>
        </style>
        <!-- My HTML here -->
        <div>
            <content></content>
        </div>
    </template>
    <!-- My JS here -->
    <script>
        Polymer({});
    </script>
</polymer-element>
```

<hr>
# 2. Web Componentsを実装する

今回の記事は、Web Componentsを作って公開するための概要説明なので、コードの詳細解説はスキップです。興味ある方は是非<a href="https://github.com/tejitak/left-swipe-action">githubのリポジトリ</a>のチェックをお願いします。

<a href="https://github.com/Polymer/PointerEvents">pointerevents</a>の使い方だとかのコードの詳細は今後の記事にでもしっかりと詳細を書きたいなぁと思っています。

### 2.1 タグ名を決める

タグ名はかなり重要です。基本は-（ハイフン）区切りでわかりやすい名前を付けていくのですが、あまりgeneral過ぎても競合するし、長過ぎても使いづらいです。ざっとgallery等見てみると、大体xxx-xxxとかxxx-xxx-xxxとか、せいぜいハイフンを２つくらい付けていますね。（早いもん勝ちなんで、今は短い名前が多いだけかもしれないですが。）
会社や組織が作る場合はgoogle-xxxの例みたいに、{compony_name}-xxxとなりそうです。

今回は左スワイプでActionを表示させるものなので、そのまま<strong>left-swipe-action</strong>にしましたw
タグ名はそのまま自作Web Componentsのファイル名になります（left-swipe-action.html）。


### 2.2 Componentが受け取るattributeを決める

Web Componentを定義する時にパラメーターを渡すには基本的にはHTMLのattributeを使用します。
今回の場合は以下の様にしました。

(Readmeから転載)

<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<th>open</th>
<td>boolean</td>
<td>false</td>
<td>If true, the content will be opened by default</td>
</tr>
<tr>
<th>nodrag</th>
<td>boolean</td>
<td>false</td>
<td>If true, drag (touch move) action is disabled and a content is automatically opened by click/touch</td>
</tr>
<tr>
<th>offset</th>
<td>number</td>
<td>60</td>
<td>Offset pixcel position for opened state</td>
</tr>
<tr>
<th>shadow</th>
<td>boolean</td>
<td>false</td>
<td>If true, box shadow is added</td>
</tr>
</tbody>
</table>


attributeを受け取る場合は、自作Web Componentファイルの先頭のpolymer elementの宣言時に明記します。
<polymer-element name="left-swipe-action” attributes="open nodrag offset shadow">

JS側で扱うための宣言は以下の様にします。

```left-swipe-action.html
    Polymer({
            /**
             * If true, the content will be opened by default
             *
             * @attribute open
             * @type boolean
             * @default false
             */
            open: false,

            /**
             * If true, drag (touch move) action is disabled and a content is automatically opened by click/touch
             *
             * @attribute nodrag
             * @type boolean
             * @default false
             */
            nodrag: false,

            /**
             * Offset pixcel position for opened state
             *
             * @attribute offset
             * @type number
             * @default 60
             */
            offset: 60,
```

なるべく上記のコメントは書きましょう。
上記の形式で書いておくと、後述のステップでギャラリーに登録した際に、ギャラリーサイトが自動的に整形してテーブル形式で表示してくれます。

### 2.3 Light DOMを想定する

Shadow DOMとはWeb Componentsの解釈後に生成するブラウザが本当のレンダリングするDOMです。一方、Light DOMはShadow DOMに対比して名付けられている使う側が定義時に記述するHTMLノードのことです。つまり、今回作成したコンポーネントであるleft-swipe-action.htmlをユーザーにimportしてもらった上で以下を記述してもらうhtmlです。

今回の例では以下の様にLight DOMを想定します。

```left-swipe-action.html
<left-swipe-action>
    <div>
        <!-- 
          Your content to be swiped here
        -->
    </div>
    <left-swipe-action-button>
      <!-- 
        Your action here
      -->
    </left-swipe-action-button>
</left-swipe-action>
```

例: スワイプした時にPaperエレメントのボタンを２つ表示

```left-swipe-action.html
<left-swipe-action shadow>
    <div style="padding: 18px;">
        <p>See how many actions are behind<br><strong>Swipe me to left</strong></p>
    </div>
    <left-swipe-action-button onclick="alert('delete')"><paper-fab icon="delete"></paper-fab></left-swipe-action-button>
    <left-swipe-action-button onclick="alert('star')"><paper-fab icon="star"></paper-fab></left-swipe-action-button>
</left-swipe-action>
```

Light DOMは上記の様にユーザーが定義するHTMLを表すもので、その内側のノードはShadowにHTMLとして渡せる引数のようなものと考えて良さそうです。今回は自作タグの中に含まれている一つ目のdivをそのままスワイプ用のコンテナに入れて、left-swipe-action-buttonで記述されたものはスワイプされると表示されるnodeと想定しています。

### 2.4 Shadow DOMを生成する

ここがWeb Componentsの実装のメインです。

受け取ったLight DOMはWeb Componentsが処理される時に、Shadow側でcontentタグを用いて任意の場所に埋め込むことができます。

今回の<content>タグの使用例

```left-swipe-action.html
        <div class="swipe-underlay" layout horizontal reverse>
            <content select="left-swipe-action-button"></content>
        </div>
        <div id="content" class="swipe-content open-{{open}}">
            <content></content>
        </div>
```

一つ目のcontent select="left-swipe-action-button"により、light DOM上のleft-swipe-action-buttonタグがこのcontentタグと置き換えられてShadow DOMが作られます。

２つ目のShadowはselectを指定していないので、渡されたlight DOMで残っているもの全てがswipeされる用のコンテンツとしてinsertされます。

上から順番にselectで指定したセレクタにマッチするものはLight DOMから取り除かれ、次のcontentタグではその取り除かれて残ったDOMに対してselectが効いていく感じです。
理解すればなんてことないですが、この挙動には少し違和感がありますねぇ。

重要な点は自作のタグと共にマークアップした内容(Light DOM)をWeb Componentsが受け取って、任意のinsertion pointを指定してShadow DOMとして表示できるということです。

#### DOMイベントhandlerの定義

今回はジェスチャーイベントに対応するためにPoymerGesturesというPolymer同梱のutilityを使用しています。

Web Componentsのライフサイクルの一つであるreadyの中でイベントhandlerのattachを行います。それぞれのイベントに対して自身のfunctionを呼び出します。

```left-swipe-action.html
            ready: function(){
                var n = this.$.content;
                if(this.nodrag){
                    PolymerGestures.addEventListener(n, "up", this.toggleOpen.bind(this));
                }else{
                    PolymerGestures.addEventListener(n, "down", this.onDown.bind(this));
                    PolymerGestures.addEventListener(n, "track", this.onMove.bind(this));
                    PolymerGestures.addEventListener(n, "up", this.onUp.bind(this));
                    PolymerGestures.addEventListener(n, "mouseleave", this.onUp.bind(this));
                }
                this.transition();
            },

            onDown: function(e){
                // それぞれの処理を書く
            },
```

#### 子コンポーネントを定義する
また、今回はアクション用のbuttonもWeb Component "<strong>left-swipe-action-button</strong>"として定義しました。
left-swipe-actionと同様にleft-swipe-action-button.htmlにcomponentの実装を書きます（少しCSSを適用するだけのwapperコンポーネントです）。

left-swipe-action側ではleft-swipe-action-buttonを使用するため、依存関係を明記しておきます。

冒頭のpolymer-elementの宣言の上にlinkタグを用いて読み込みます。

```left-swipe-action.html
<link rel="import" href="left-swipe-action-button.html">
```

よって、left-swipe-actionはleft-swipe-action-buttonを持つcompositeコンポーネントとなっています。
今回は使っていませんが、再利用性を高めるためにコンポーネントはextendsを使って継承の仕組も提供されています。

子コンポーネントとなるleft-swipe-action-buttonに関しては、ここでは少しスタイルの調整など追加します。

```left-swipe-action-button.html
<polymer-element name="left-swipe-action-button">
    <template>
        <style>
            :host {
                display: table;
                height: 100%;
                padding: 0 20px;
            }

            .action-button-wrapper {
                display: table-cell;
                vertical-align: middle;
                font-size: 1.8rem;
                font-weight: 300;
            }
        </style>
        <div class="action-button-wrapper">
            <content></content>
        </div>
    </template>
    <script>
        Polymer({});
    </script>
</polymer-element>
```

### 2.5 テスト

Polymerはまだまだ全てのブラウザで動く様な互換性はありません。現段階では、IE9やAndroidの標準ブラウザでは動きません。

Polymerのサポートブラウザ
<a href="http://www.polymer-project.org/resources/compatibility.html">http://www.polymer-project.org/resources/compatibility.html</a>

今回はtest用のフレームワークは使わず、Chrome、Firefox、iOS safari、Android Chromeで軽く動作チェックを行いました。

しっかりとテストしたい場合のガイドは公式ドキュメントに記載されています。
<a href="http://www.polymer-project.org/resources/tooling-strategy.html#building-amp-testing">http://www.polymer-project.org/resources/tooling-strategy.html#building-amp-testing</a>

<hr>
# 3. Github ioでデモを動かす

コードが出来上がって、ブラウザで動くことを確認したら、githubにpushして、github.ioに公開しましょう。

公開手順
<a href="https://pages.github.com/">https://pages.github.com/</a>

gh-pagesという名前のブランチを作成するだけです。
プロジェクトの直下にdemo.htmlというdemo用のファイルを作りましょう。はっきりとわかっていないですが、component kitchinのようなgalleryサイトはプロジェクト直下のdemo.htmlをでもファイルとして自動判別している気がします。

<hr>
# 4. Readmeをしっかり書く

あなたのWeb Componentsを使う人がまず使おうと思った時に見るのはReadmeだと思います。きちんと書かれていないと、メンテナンスもされてないと思われ使ってもらえないでしょう。

ということで、つたない英語でも最低限使い方くらいはちゃんと明記しておきます。

<hr>
# 5. Bowerに登録
今やJSのライブラリを管理するのに<a href="http://bower.io/">Bower</a>は定番ですよね。公式のPolymerのガイドでもbowerへの登録が推奨されています。
<a href="http://www.polymer-project.org/articles/distributing-components-with-bower.html">http://www.polymer-project.org/articles/distributing-components-with-bower.html</a>

まずは自作コンポーネントが依存するものはbower.jsonのdependencyに記述しましょう。

```bower.json
{
  "name": "left-swipe-action",
  "version": "0.1.0",
  "authors": [
    "tejitak"
  ],
  "description": "Left swipe action with Polymer web components",
  "keywords": [
    "Polymer",
    "web-components"
  ],
  "license": "MIT",
  "homepage": "https://github.com/tejitak/left-swipe-action",
  "dependencies": {
    "polymer": "~0.5.1"
  },
  "ignore": [
    "**/.*"
  ]
}
```

実はこのkeywordsで"<strong>Polymer</strong>"と"<strong>web-components</strong>"を指定するところが重要です！このキーワードによりgalleryのサイトがWeb Componentsの登録を自動的に検知してくれます。

bowerにregisterする前に、localでテストして確認する方法があるので、テストはします。
<a href="http://blog.edouard-lopez.com/testing-bower-dot-json-locally-before-registering-package/">http://blog.edouard-lopez.com/testing-bower-dot-json-locally-before-registering-package/</a>

テスト環境で、<strong>bower install {your-component} —save</strong>して、適当なhtmlファイル作ってPolymerを読み込み自作タグが動作したら、bowerにregisterしましょう！

タグしてpush。（タグはx.x.xの形式で）


`>git tag -a 0.1.0 -m "Tagging 0.1.0"`
`>git push --tags`


bowerに登録！

`>bower register left-swipe-action git://github.com/tejitak/left-swipe-action.git`


登録できたらbower searchでちゃんと登録されていることを確認。

`>bower search swipe-action`
Search results:
left-swipe-action git://github.com/tejitak/left-swipe-action.git


念のため、installして動作確認。

`>bower install left-swipe-action --save`

<hr>
# 6. Galleryサイトに登録されているか確認
どのくらいの頻度で更新されるのかは不明ですが、bower.jsonのキーワードに"Polymer"と"web-components"を指定したため、数時間後にはギャラリーのサイトに自動的に登録されていました。

Custom Elements A Web Components Gallery for Modern Web Apps
<a href="http://customelements.io/">http://customelements.io/</a>

Web Components Catalog - Component Kitchen 
<a href="http://component.kitchen/">http://component.kitchen/</a>


<a href="http://www.tejitak.com/blog/wp-content/uploads/2014/08/スクリーンショット-2014-08-09-13.28.48.png"><img src="http://www.tejitak.com/blog/wp-content/uploads/2014/08/スクリーンショット-2014-08-09-13.28.48.png" alt="スクリーンショット 2014-08-09 13.28.48" width="1106" height="696" class="alignnone size-full wp-image-601" /></a>

<a href="http://component.kitchen/components/left-swipe-action">http://component.kitchen/components/left-swipe-action</a>

上記の様にLive Demo付きで登録されていれば、自作コンポーネントの公開完了です！！

<hr>

以上が、Componentを作り始めてから公開するまでの道のりの紹介でした。まだ、ブラウザのサポートだとかパフォーマンスの面でプロダクション用にWeb Componentsをばりばり使うというのは難しいかもしれません。ただ、Web ComponentsはJSのライブラリというレベルではなくブラウザnativeで実装される予定のテクノロジーであるため、今後どんどん普及して行くのは間違いないと思っています。

作るのはなかなか面白かったので、引き続き趣味でWeb Components作っていこうかと思います〜（仲間募集中）。