使用 DModal API 在 Discourse 中渲染模态窗口(即弹窗/对话框)

Discourse 3.1.0.beta6 引入了全新的基于组件的 <DModal> API。DModalUI 套件 的一部分,从 discourse/ui-kit/d-modal 导入。

:information_source: 此 API 取代了旧的基于控制器的 API,后者现已弃用。如果您有使用旧 API 的现有模态框,请查看此处的迁移指南。

渲染模态框

通过在 Handlebars 模板中包含 <DModal> 组件来渲染模态框。如果您还没有合适的模板,请查看 https://meta.discourse.org/t/using-plugin-outlet-connectors-from-a-theme-or-plugin/32727。

一个简单的模态框可能如下所示:

<DButton
  @translatedLabel="Show Modal"
  @action={{fn (mut this.modalIsVisible) true}}
/>

{{#if this.modalIsVisible}}
  <DModal @title="My Modal" @closeModal={{fn (mut this.modalIsVisible) false}}>
    Hello world, this is some content in a modal
  </DModal>
{{/if}}

:information_source: 这里使用了 mut 辅助函数 作为仅在 hbs 中设置值的方法。您也可以使用任何其他标准的 Ember 方法来设置 modalIsVisible

此示例将创建一个简单的模态框,如下所示:

封装为组件

在引入更多复杂性之前,通常最好将您的新模态框封装在单独的组件定义中。让我们将 <DModal> 的内容移动到一个新的 <MyModal /> 组件中。

// components/my-modal.gjs
<template>
  <DModal @title="My Modal" @closeModal={{@closeModal}}>
    Hello world, this is some content in a modal
  </DModal>
</template>

将此 .gjs 文件升级为基于类的组件,将允许您引入更复杂的逻辑和状态。

要使用新组件,请更新调用位置以引用该组件,并确保传入 @closeModal 参数。

<DButton
  @translatedLabel="Show Modal"
  @action={{fn (mut this.modalIsVisible) true}}
/>

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

添加页脚

许多模态框都有某种行动号召(Call-to-Action)。在 Discourse 中,这些通常位于模态框的底部。为了实现这一点,DModal 提供了多个“命名块”(named blocks),可以在其中渲染内容。以下是更新后的示例,包含页脚中的两个按钮,其中一个是我们的标准 DModalCancel 按钮:

<DModal @title="My Modal" @closeModal={{@closeModal}}>
  <:body>
    Hello world, this is some content in a modal
  </:body>
  <:footer>
    <DButton class="btn-primary" @translatedLabel="Submit" />
    <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。除非焦点在表单 / 文本区域 / select-kit 中,否则 Enter 键将点击 .d-modal__footer .btn-primary
@beforeClose async ({ initiatedBy }) => boolean。返回 false 以取消关闭(例如,脏表单确认)。
@hidden 暂停键盘处理;用于嵌套模态框位于顶部时。
@tagName "div"(默认)或 "form"。对于表单,请使用 "form" 以使原生提交功能正常工作。

位置 使用时机
default / :body 主要内容区域 默认区域
:aboveHeader 最顶部,头部之前 很少需要;用于必须位于标题栏上方的内容(例如横幅)。
:headerAboveTitle 头部内部,标题之前 存在但未使用。很少需要。
:belowModalTitle .d-modal__title 内部,<h1> 之后 非常适合放置补充元信息。
:headerBelowTitle 头部内部,标题块之后 属于头部的一部分的选项卡、子导航或搜索输入框。
:headerPrimaryAction 仅移动端头部右侧 用主要操作(例如“保存”)替换 X 关闭按钮。还会自动在左侧渲染“取消”按钮,并在头部添加 .--has-primary-action
:belowHeader 头部和主体之间 位于可滚动主体之外的持久性子头部内容(例如搜索栏),因此可以固定显示。
: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 个赞

帖子已拆分为新主题:Can I show a modal from head_tag