🌒

IonicのIonItemを参考にしてslot属性の使い方を理解する

2020/12/21に公開

Ionic Frameworkは各コンポーネントによって属性は違うのですが、アイテムコンポーネントには大体 slot 属性があります。これがどういったものであるかから、実装までをみていきましょう。

Web Componentsとしてのslot

Ionicのコンポーネントは、 colormode など、独自の属性も多くありますが、 slot 属性はWeb ComponentsのShadow DOMに定められている slot 要素にDOMを挿入するための標準仕様です。

https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/slot

例えば、Ionic Frameworkの IonItem の場合、テンプレートは以下のようになっています(slotまわりだけを抽出しており、他は割愛しています)

<ion-item>
  <slot name="start"></slot>
  <div class="item-inner">
    <div class="input-wrapper">
      <slot></slot>
    </div>
    <slot name="end"></slot>
  </div>
</ion-item>

ion-item 以下がShadow DOMとなっています。そして、このテンプレートは以下のように利用します。

<ion-item>
  <ion-icon name="close" slot="start"></ion-icon>
  <ion-label>
    <h2>H2 Title Text</h2>
    <p>Button on right</p>
  </ion-label>
  <ion-button fill="outline" slot="end">View</ion-button>
</ion-item>

すると、上記のHTMLは slot[name] が一致する部分に挿入されます。つまり、

  • ion-thumbnail[slot=start]ion-item > slot[name=start]
  • ion-button[slot=end]ion-item > .item-inner > slot[name=end]
  • slotが指定されていない ion-label は、 ion-item > .item-inner > .input-wrapper > slot

の中に挿入されます。Ionic FrameworkはWeb標準に基づいてつくられておりますので、ここらへんはWeb Componentsの仕様そのままですね。

slotのスタイリング

ではスタイリングはどうなってるかを簡単にみてみましょう。まず、 slot はCSSで指定する時、 ::slotted() で指定することができます。ですので、上記のスタイリングの一部はこのようになっています。

<ion-item>
  <slot name="start"></slot>
  <div class="item-inner">
    <div class="input-wrapper">
      <slot></slot>
    </div>
    <slot name="end"></slot>
  </div>
</ion-item>
::slotted(ion-icon) {
  font-size: 1.6em;
}

::slotted(ion-button) {
  --margin-top: 0;
  --margin-bottom: 0;
  --margin-start: 0;
  --margin-end: 0;

  z-index: 1;
}

::slotted(ion-label) {
  flex: 1;
}

slot 指定されてる ion-iconfont-size: 1.6em に指定されています。 ion-button はデフォルトのスタイリングを打ち消す形で margin が0に設定されており、また、 ion-label は親にあたる .input-wrapper があることから flex:1 に指定されています。複数の ion-label があった場合の時のためですよね。

このデザインで肝になるのは、 .item-inner.input-wrapper です(一部割愛しています)

.item-inner {
  display: flex;

  // This is required to work with an inset highlight
  position: relative;

  flex: 1;
  flex-direction: inherit;
  align-items: inherit;
  align-self: stretch;
}

.input-wrapper {
  display: flex;

  flex: 1;
  flex-direction: inherit;

  align-items: inherit;
  align-self: stretch;

  text-overflow: ellipsis;

  overflow: inherit;
  box-sizing: border-box;
}

[slot=start] がついた属性は基本的に横幅の上限があるアイテム( ion-iconion-button など)が来ますので、 slot が空のパーツは残った空白を埋める必要があります。そこで、 .item-inneralign-self: stretch が指定されています。 .item-inner 以下の input-wrapper も同様です。

これにより、「 [slot=start] がまず場所をとり、.item-inner 内で次に [slot=end] が場所をとって、残りが無名のslotに割り振られるという構造になっています。

まとめ

フレームワーク設計の際に、アイテムの親子関係やユースケースがよく練られてることが伺える一例だと思います。ソースコードを読んでるとこういう設計の意図が伺えますので、ぜひより詳しくなりたい方はコードを読むようにしてください。

https://github.com/ionic-team/ionic-framework/blob/master/core/src/components/item/item.scss

それではまた。

Discussion