Discourse で DModal API を使ってモーダルウィンドウ(ポップアップ/ダイアログ)をレンダリングする方法

Discourse 3.1.0.beta6 には、新しい <DModal> コンポーネントベースの API が搭載されています。

:information_source: これは、現在非推奨となっている古いコントローラーベースの API を置き換えるものです。古い API を使用している既存のモーダルがある場合は、こちら here の移行ガイドをご覧ください。

モーダルのレンダリング

モーダルは、Handlebars テンプレートに <DModal> コンポーネントを含めることでレンダリングされます。適切なテンプレートをお持ちでない場合は、Using Plugin Outlet Connectors from a Theme or Plugin をご覧ください。

シンプルなモーダルは、以下のようなものになります。

<DButton
  @translatedLabel="モーダルを表示"
  @action={{fn (mut this.modalIsVisible) true}}
/>

{{#if this.modalIsVisible}}
  <DModal @title="私のモーダル" @closeModal={{fn (mut this.modalIsVisible) false}}>
    こんにちは世界、これはモーダル内のコンテンツです
  </DModal>
{{/if}}

:information_source: mut ヘルパー は、ここでは値を設定するための hbs 固有の方法として使用されています。他の標準的な Ember メソッドを使用して modalIsVisible を設定することも可能です。

この例では、以下のようなシンプルなモーダルが作成されます。

コンポーネントへのラップ

より複雑な処理を導入する前に、新しいモーダルを独自のコンポーネント定義にラップするのが一般的に最善です。<DModal> 関連のコードを新しい <MyModal /> コンポーネント内に移動してみましょう。

// components/my-modal.gjs
<template>
  <DModal @title="私のモーダル" @closeModal={{@closeModal}}>
    こんにちは世界、これはモーダル内のコンテンツです
  </DModal>
</template>

この .gjs ファイルをクラスベースのコンポーネントにアップグレードすることで、より複雑なロジックや状態を導入できるようになります。

新しいコンポーネントを利用するには、呼び出し元を更新して参照し、@closeModal 引数を渡すようにしてください。

<DButton
  @translatedLabel="モーダルを表示"
  @action={{fn (mut this.modalIsVisible) true}}
/>

{{#if this.modalIsVisible}}
  <MyModal @closeModal={{fn (mut this.modalIsVisible) false}} />
{{/if}}

フッターの追加

多くのモーダルには、何らかの呼び出しアクション(Call-to-Action)が含まれています。Discourse では、これらは通常モーダルの下部に配置されます。これを実現するために、DModal にはコンテンツをレンダリングできる「名前付きブロック(named blocks)」がいくつか用意されています。以下は、フッターに 2 つのボタン(そのうち 1 つは標準的な DModalCancel ボタン)を含めるように更新された例です。

<DModal @title="私のモーダル" @closeModal={{@closeModal}}>
  <:body>
    こんにちは世界、これはモーダル内のコンテンツです
  </:body>
  <:footer>
    <DButton class="btn-primary" @translatedLabel="送信" />
    <DModalCancel @close={{@closeModal}} />
  </:footer>
</DModal>

hbs 以外のコンテキストからのモーダルレンダリング

理想的には、<DModal> インスタンスは、上記の宣言的な手法を使用して Ember テンプレート内からレンダリングされるべきです。しかし、ユースケースによってはそれが不可能な場合もあります。そのような場合は、modal サービスを注入し、modal.show() を呼び出すことで実現できます。

まず、上記で説明した通り、モーダルを独自のコンポーネントにラップしていることを確認してください。その後、コンポーネントクラスの参照を showModal に渡すことでモーダルをトリガーします。

import MyModal from "discourse/components/my-modal";

// (関連する場所に modal サービスを注入する)

// モーダルを開きたいときにこの呼び出しを追加します。
// `@closeModal` 引数は自動的にコンポーネントに渡されます。
this.modal.show(MyModal);

// オプションで、`model` パラメータを渡すことができます。これは `@model` としてコンポーネントに渡されます。
// これにはデータや、モーダルで使用するためのアクション/コールバックを含めることができます。
this.modal.show(MyModal, {
  model: { topic: this.topic, someAction: this.someAction },
});

// `modal.show()` は Promise を返すため、閉じるまで待機できます。
// `@closeModal` アクションに渡されたデータで解決されます。
const result = await this.modal.show(MyModal);

さらにカスタマイズ可能に!

<DModal> には、いくつかの名前付きブロックと引数が用意されています。

引数

引数 目的
@closeModal 閉じる UI を表示するために必須。
@title <h1 id="discourse-modal-title"> をレンダリングし、aria-labelledby を接続します。
@subtitle タイトル下の小さなテキスト。
@flash / @flashType モーダル上部のインラインアラート(DFlashMessage)。
@hideHeader, @hideFooter 領域全体を非表示にします。
@headerClass, @bodyClass ヘッダー/ボディラッパーへの追加クラス。
@dismissable @closeModal が設定されている場合、デフォルトは true。Esc キー、背景クリック、X ボタンを無効化します。
@autofocus デフォルトは true。dTrapTab を介して最初のフォーカス可能な要素に自動フォーカスを設定します。
@submitOnEnter デフォルトは true。フォーカスがフォーム/テキストエリア/セレクトキット内でない限り、Enter キーで .d-modal__footer .btn-primary がクリックされます。
@beforeClose async ({ initiatedBy }) => boolean。close をキャンセルするには false を返します(例:未保存フォームの確認)。
@hidden キーボード処理を一時停止します。ネストされたモーダルが上にある場合に使用されます。
@tagName "div"(デフォルト)または "form"。ネイティブの送信が機能するようにフォームには "form" を使用してください。

ブロック

ブロック 位置 使用タイミング
default / :body メインコンテンツエリア デフォルトエリア
:aboveHeader ヘッダーの直前、最上部 稀に必要。タイトルバーの上に配置する必要があるコンテンツ(例:バナー)用。
:headerAboveTitle ヘッダー内、タイトル前 存在するが未使用。稀に必要。
:belowModalTitle .d-modal__title 内、<h1> の後 補足的なメタ情報を入れるのに最適な位置。
:headerBelowTitle ヘッダー内、タイトルブロック後 タブ、サブナビゲーション、またはヘッダーの一部である検索入力。
:headerPrimaryAction モバイルのみのヘッダー右側 X 閉じるボタンをプライマリアクション(例:「保存」)に置き換えます。また、左側に「キャンセル」ボタンを自動レンダリングし、ヘッダーに .--has-primary-action を追加します。
:belowHeader ヘッダーとボディの間 スクロール可能なボディの外側にある永続的なサブヘッダーコンテンツ(例:検索バー)。スクロールしても表示され続ける(sticky display)ように設定されます。
:aboveFooter ボディとフッターの間 @hideFooter が設定されている場合は抑制されます。フッターに関連するがフッター外にあるコンテンツに使用します。これも稀です。
:footer 下部アクションバー プライマリおよびセカンダリボタン。ここにある最初の .btn-primary が Enter キーでトリガーされます。
:belowFooter フッター後 稀に必要。@hideFooter を無視します。枠線付きのフッターエリア外のステータステキストなどに有用。

出典:引数については インタラクティブなスタイルガイド、名前付きブロックについては d-modal テンプレートの実装 を参照してください。

CSS

コアを上書きするアンカーとして .d-modal クラスを使用し、レガシーな .modal セレクターは避けてください。

利用可能な 4 つの修飾子:

  • .--large最大幅 を 800px に設定します(デスクトップのみ)
  • .--max最大幅 を 90vw に設定します(デスクトップのみ)
  • .has-search固定高さ(80vh)を設定します。結果の長さによる高さ変化を避けるために、検索/フィルタシステムを備えたモーダル用に設計されています(デスクトップのみ)
  • .--stacked はフッターのボタンをスタック表示に設定します(モバイルのみ)

このドキュメントはバージョン管理されています。変更を提案する場合は GitHub まで。

「いいね!」 17

投稿が新しいトピックに分割されました: head_tagからモーダルを表示できますか