ちょい技。Lightning Web ComponentsでTooltipを表示する。

Lightning Web Componentsでツールチップを表示しようと思ったら、標準コンポーネントがありません。

ツールチップって使う側にとっては便利なんですよね。

Lightning Web Componentsでツールチップを表示する方法をご紹介します。

こういうものを作成します

行動(Event)の情報をツールチップに表示します。

ツールチップコンポーネントを作ります

まずは、ツールチップコンポーネントCalendarTooltipを作ります。

CalendarTooltip.html

<template>
    <div class="slds-popover slds-popover_tooltip slds-nubbin_top-left cal-tooltip" role="tooltip" id="tooltip">
        <div class="slds-popover__body">
            <div>
                {dateTime}
            </div>
            <div>
                {subject}
            </div>
            <div>
                {what}
            </div>
            <div>
                {description}
            </div>
        </div>
    </div>
</template>

CalendarTooltip.js

showメソッドでツールチップを表示、hideで非表示にします。

import { LightningElement, api } from 'lwc';

export default class CalendarTooltip extends LightningElement {
    /**
     * 日付時刻
     */
    dateTime;

    /**
     * 件名
     */
    subject;

    /**
     * 関連先名
     */
    what;

    /**
     * 説明
     */
    description;

    /**
     * 表示
     */
    @api show(top, left, event) {
        // ツールチップに表示する行動情報を設定する
        this.dateTime = event.startdate + ((event.isallday) ? '' : (' ' + event.starttime));
        if(event.startdate !== event.enddate) {
            this.dateTime += ' - ' + event.enddate + ((event.isallday) ? '' : (' ' + event.endtime));
        }
        this.subject = event.subject;
        this.what = event.what;
        this.description = event.description;

        // 表示位置を設定する
        const tooltip = this.template.querySelector('.cal-tooltip');
        tooltip.style.top = (top + 20).toString() + 'px';
        tooltip.style.left = left.toString() + 'px';

        // ツールチップを表示する
        tooltip.style.display = 'block';
    }

    /**
     * 非表示
     */
    @api hide() {
        // ツールチップを非表示にする
        const tooltip = this.template.querySelector('.cal-tooltip');
        tooltip.style.display = 'none';
    }
}

CalendarTooltip.css

表示位置を絶対位置にします。

.cal-tooltip {
    position: absolute;
    left: 0px;
    top: 0px;
    display: none;
}

ツールチップを表示する側を作ります

テーブル(Table)のカラム(td)に、onmouseoverとonmouseoutのイベントを設定し、onmouseoverイベントハンドラでツールチップを表示、onmouseoutイベントでツールチップを消します。

Component.html

<template>
    <div class="slds-grid">
        <div class="slds-col">
            <div class="slds-m-left_x-small">
                <span class="cal-header-label">{calendarLabel}</span>
            </div>
        </div>
        <div class="slds-col">
            <div class="slds-float_right">
                <lightning-button-group class="slds-m-right_x-small">
                    <lightning-button-icon icon-name="utility:chevronleft" onclick={handleMovePrevMonth}></lightning-button-icon>
                    <lightning-button variant="neutral" label={labels.CalendarToday} onclick={handleMoveToday}></lightning-button>
                    <lightning-button-icon icon-name="utility:chevronright" onclick={handleMoveNextMonth}></lightning-button-icon>
                </lightning-button-group>
            </div>
        </div>
    </div>
    <div class="slds-grid">
        <div class="slds-col">
            <table class="cal-table">
                <tbody>
                    <tr>
                        <template for:each={weeks} for:item="week">
                            <td class={week.class} key={week.label}>
                                <span>{week.label}</span>
                            </td>
                        </template>
                    </tr>
                </tbody>
            </table>
            <template for:each={items} for:item="week">
                <table key={week.date} class="cal-table">
                    <thead>
                        <tr>
                            <template for:each={week.days} for:item="day">
                                <th key={day.date} class={day.class}>
                                    <span>{day.day}</span>
                                </th>
                            </template>
                        </tr>
                    </thead>
                    <tbody>
                        <template for:each={week.rows} for:item="row">
                            <tr key={row.id}>
                                <template for:each={row.events} for:item="event">
                                    <template if:true={event.isdummy}>
                                        <td key={event.id} class="cal-day-event-none">
                                        </td>
                                    </template>
                                    <template if:false={event.isdummy}>
<!-- ここです -->
                                        <td key={event.id} class="cal-day-event" colspan={event.span} data-id={event.id} onmouseover={handleMouseOverToEvent} onmouseout={handleMouseOutFromEvent}>
                                            {event.labelshort}
                                        </td>
                                    </template>
                                </template>
                            </tr>
                        </template>
                    </tbody>
                </table>
            </template>
        </div>
    </div>
    <c-calendar-tooltip>
    </c-calendar-tooltip>
</template>

Component.js

onmouseoverイベントハンドラ(handleMouseOverToEvent)でツールチップの表示位置を指定しています。

layerXとlayerYを使うとツールチップらしい位置に表示できます。

import { LightningElement, api, track } from 'lwc';

export default class CalendarMonthly extends LightningElement {
    /**
     * 行動情報
     */
    _events;

    /**
     * 行動アイテムにマウスが入ったらツールチップを表示する
     */
    handleMouseOverToEvent(evt) {
        evt.stopPropagation();

        // 行動IDから行動情報を取得する
        const event = this.getEvent(evt.target.dataset.id);

        // 行動の詳細をツールチップで表示する
        this.template.querySelector('c-calendar-tooltip').show(evt.layerY, evt.layerX, event);
    }

    /**
     * 行動アイテムからマウスが離れたらツールチップを非表示にする
     */
    handleMouseOutFromEvent(evt) {
        evt.stopPropagation();

        // 行動のツールチップを非表示する
        this.template.querySelector('c-calendar-tooltip').hide();
    }

    /**
     * ID指定で行動情報を取得する
     */
    getEvent(id) {
        let event = null;
        for(let i=0 ; i<this._events.length ; i++) {
            if(this._events[i].id === id) {
                event = this._events[i];
                break;
            }
        }
        return event;
    }
}

やっぱり、ツールチップはどうしても必要です。