最強のLightningページレイアウト #salesforce

(タイトルはネタですが)

テラスカイさんのブログ「Lightningページレイアウトについて考えてみる - TerraSkyBase」を読んで、以前から考えていたアイディアを試してみました。

それは、「組み込みで用意されているページレイアウトから選択する」ではなく、「どのパターンにも対応可能なレイアウトを用意すれば、迷うことがなくなるんじゃないか?」というものです。

きっかけ

Lightningページの困ることの一つは、一度作ったレイアウト(テンプレート)は、後から変更できないという制限(仕様)です。
なので、「いったん1カラムで作ってから、後で2カラムに変えたい」みたいなことができません。
設定画面(GUI)からではなく、メタデータ(XML)経由でなら変えられるんじゃないか?と思って試したりもしましたが、やっぱりNGでした。

通常の開発・運用であれば、新たに作り直せばいいのかもしれませんが、管理パッケージで配布するような場合、不用意にヘタなレイアウトで作ったLightningページをリリースしてしまうと、後からの差し替えが面倒になることが考えられます。

作ってみる

さて、Lightningページ作成時に選択するテンプレートですが、組み込みで用意されているもの以外に、カスタムで作ってそれを利用することもできます。

developer.salesforce.com

テンプレートの各リージョン(1つ1つの矩形のテンプレート領域を便宜上リージョンと呼んでいます)には、0個以上のLightningコンポーネントをスタックできるので、色んなグリッドのパターンを盛り込んだテンプレートを作ってあげれば、一つのレイアウトで色々なレイアウトのLightningページを作成できると考えました。

というわけで、作ったものがこちら。

f:id:jappy:20180718014824p:plain

(Lightningアプリケーションビルダーでの画面ですが、見やすさのために各リージョンにリッチテキストコンポーネントを配置しています)

タイトルで最強と言いつつも、以下の5タイプだけ対応しました。

  • 1カラム
  • 2カラム(等分割)
  • 3カラム(等分割)
  • 2カラム(右サイドバー形式)
  • 2カラム(左サイドバー形式)

また、順番の入れ替えもいったん未対応です。

コード

Component

カスタムのテンプレートはLightningコンポーネントバンドルとして実装します。
その際に必要なのはComponent(HTML)、Design(HTML)が必須で、後は必要に応じてSVGやStyleを作ります。今回は、Component、Design、Styleの3つを定義しました。

<aura:component implements="lightning:appHomeTemplate" description="All-in-one Template">
    <aura:attribute name="header" type="Aura.Component[]" />
    <aura:attribute name="leftOfBisection" type="Aura.Component[]" />
    <aura:attribute name="rightOfBisection" type="Aura.Component[]" />
    <aura:attribute name="leftOfTrisection" type="Aura.Component[]" />
    <aura:attribute name="centerOfTrisection" type="Aura.Component[]" />
    <aura:attribute name="rightOfTrisection" type="Aura.Component[]" />
    <aura:attribute name="leftOf2to1" type="Aura.Component[]" />
    <aura:attribute name="rightOf2to1" type="Aura.Component[]" />
    <aura:attribute name="leftOf1to2" type="Aura.Component[]" />
    <aura:attribute name="rightOf1to2" type="Aura.Component[]" />
    
    <div>
        <lightning:layout horizontalAlign="spread" multipleRows="true" pullToBoundary="medium">
            <lightning:layoutItem flexibility="grow" size="12" padding="horizontal-small">
                <div class="layout__col">{!v.header}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="6" padding="horizontal-small">
                <div class="layout__col">{!v.leftOfBisection}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="6" padding="horizontal-small">
                <div class="layout__col">{!v.rightOfBisection}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="4" padding="horizontal-small">
                <div class="layout__col">{!v.leftOfTrisection}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="4" padding="horizontal-small">
                <div class="layout__col">{!v.centerOfTrisection}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="4" padding="horizontal-small">
                <div class="layout__col">{!v.rightOfTrisection}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="8" padding="horizontal-small">
                <div class="layout__col">{!v.leftOf2to1}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="4" padding="horizontal-small">
                <div class="layout__col">{!v.rightOf2to1}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="4" padding="horizontal-small">
                <div class="layout__col">{!v.leftOf1to2}</div>
            </lightning:layoutItem>
            <lightning:layoutItem flexibility="grow" size="8" padding="horizontal-small">
                <div class="layout__col">{!v.rightOf1to2}</div>
            </lightning:layoutItem>
        </lightning:layout>
    </div>
    
</aura:component>

あんまり解説が必要なところがないのですが、レイアウトはLightningのnativeコンポーネントを使って楽にグリッドシステムを組み立てました。

Design

<design:component label="All-in-One Template">
    <flexipage:template>
        <flexipage:region name="header" defaultWidth="MEDIUM" />
        <flexipage:region name="leftOfBisection" defaultWidth="MEDIUM" />
        <flexipage:region name="rightOfBisection" defaultWidth="MEDIUM" />
        <flexipage:region name="leftOfTrisection" defaultWidth="MEDIUM" />
        <flexipage:region name="centerOfTrisection" defaultWidth="MEDIUM" />
        <flexipage:region name="rightOfTrisection" defaultWidth="MEDIUM" />
        <flexipage:region name="leftOf2to1" defaultWidth="MEDIUM" />
        <flexipage:region name="rightOf2to1" defaultWidth="MEDIUM" />
        <flexipage:region name="leftOf1to2" defaultWidth="MEDIUM" />
        <flexipage:region name="rightOf1to2" defaultWidth="MEDIUM" />
    </flexipage:template>
</design:component>

Style

CSS力が足らず少し時間がかかったのがこの部分。コンポーネントが存在しない行の場合に上下の空白の大きさを調節するため、CSS3のセレクタを使ってマージンを調整しています。

.THIS .layout__col > div:not(:empty) {
    margin-bottom: .75rem;
}

おわりに

ひとまず、目論んでいたようにカスタムテンプレートを作ることはできました。
ここで作ったテンプレートがどれくらい実用性があるのかは不明ですが、少なくとも、標準で用意されているテンプレートで対応できないケースの場合、カスタムのテンプレートを作るのは一つの手段として有力なので、こんなこともできるんだと触っておくとよいのかなと感じました。