Lightning コンポーネント開発メモ: ポップオーバーを使ってみた

今回は `lightning:overlayLibrary` でポップアップを表示してみました。
単純にポップオーバーを表示するだけであれば特に迷わないのですが、ポップオーバー自体へのマウスホバーで少し迷いました。(これくらい機能として持っていてくれてもいいじゃないか！)

## 動作イメージ
- 特定の要素にマウスホバーした時に、ポップオーバーを表示
- 表示されたポップオーバーにもマウスホバー出来る
![ezgif-1-f94666cc896f.gif](https://qiita-image-store.s3.amazonaws.com/0/26378/9711102e-601d-76f7-946d-6bdf0e171b07.gif)

## サンプルコード

```html:SimplePopoverExample.cmp
<aura:component implements="flexipage:availableForAllPageTypes" access="global" >
    <!-- Public Attributes -->
    <aura:attribute access="public" type="Aura.component" name="popover" />
    <aura:attribute access="public" type="Integer"        name="closeTimer" />
    
    <!-- Overlay Library -->
    <lightning:overlayLibrary aura:id="overlayLib"/>
    
    <!-- User Interface -->
    <div>
        <lightning:card iconName="utility:quick_text" title="Simple Popover Example">
            <lightning:layout>
                    <lightning:layoutItem padding="around-medium" size="12">
                        <span class="popoverReference">
                            <a href="javascript:void(0);" onmouseenter="{!c.onMouseEntered}" onmouseleave="{!c.onMouseLeft}">
                                ホバーするとポップオーバーが出るよ！
                            </a>
                        </span>
                    </lightning:layoutItem>
            </lightning:layout>
        </lightning:card>
    </div>
</aura:component>
```


```js:SimplePopoverExampleController.js
({
    onMouseEntered: function(c, e, h) {
        
        // 既に開いているポップオーバーがあれば、まず閉じる。
        const popover = c.get("v.popover");
       if (popover) popover.close();
        
        
        // ポップオーバーのボディを作成する。
        const parser = new DOMParser();
        const popoverBody = parser.parseFromString("<div style=\"padding:16px;\">Hello World!</div>", "text/html").querySelector("body").firstChild;
        
        // ポップオーバーにマウスホバーした時にポップオーバーを閉じたくないのでクローズ待ちタイマーを止める。
        // ポップオーバーからマウスが離れたら、ポップオーバーを閉じる。
        popoverBody.addEventListener("mouseenter", $A.getCallback(function(){
            clearTimeout(c.get("v.closeTimer"));
        }));
        popoverBody.addEventListener("mouseleave", $A.getCallback(function(){
            const popover = c.get("v.popover");
            if (popover) popover.close();
        }));
            
        
        // ポップオーバーを表示する
        const overlayLib = c.find("overlayLib");
        overlayLib.showCustomPopover({
            body: popoverBody, // ポップオーバーのボディ
            referenceSelector: ".popoverReference", // ポップオーバーを表示する親要素のセレクタ
         }).then(function (popover) {
            c.set("v.popover", popover);
        })
        .catch(function(reason) {
            console.error(reason);
        }); 
    },
    onMouseLeft: function(c, e, h) {
        
        // 親要素からマウスが離れた時は、ポップオーバーにホバーしていなければ閉じる。
        // (ホバーした場合はクローズ待ちタイマーを止める)
        c.set("v.closeTimer", setTimeout($A.getCallback(function(){
            const popover = c.get("v.popover");
            if (popover) popover.close();
        }), 100));
    },
})
```

```css:SimplePopoverExample.css
.THIS .popoverReference {
   padding-right: 1rem;
}
```

## 気になった点

#####表示されたポップオーバーの矢印が、ホバーした親要素に重なる（!マークが隠れてる）。
![Screen_Shot_2018-10-30_at_19_23_48.png](https://qiita-image-store.s3.amazonaws.com/0/26378/d000fc43-fe6e-216f-d929-0dfd3cd4a988.png)

- マウスホバーの要素の上位にポップオーバーの参照要素を作成し、パディングで調整する。 `padding-right: 1rem;`

#####ポップオーバーのボディを動的に作成した場合、たまに矢印の位置がずれる
![Screen_Shot_2018-10-30_at_19_26_46.png](https://qiita-image-store.s3.amazonaws.com/0/26378/801f1417-e4c0-1d35-bb17-4ee987f36b44.png)

- 何回か表示/非表示を繰り返すと揃うようになるので、多分ポッポオーバーのボディの高さ確定とポップオーバーの位置算出のタイミングの問題。
- 高さを早く確定して上げる必要があるので、すべてのデータが揃ってからポップオーバーの描画を開始するのが良さそう。