Использование DModal API для отображения модальных окон (также называемых всплывающими окнами/диалогами) в Discourse

Discourse 3.1.0.beta6 поставляется с совершенно новым компонентным API <DModal>. DModal является частью набора пользовательского интерфейса и импортируется из discourse/ui-kit/d-modal.

:information_source: Это заменяет старый API на основе контроллеров, который теперь устарел. Если у вас есть существующие модальные окна, использующие старые API, ознакомьтесь с руководством по миграции здесь.

Отрисовка модального окна

Модальные окна отрисовываются путем включения компонента <DModal> в шаблон handlebars. Если у вас еще нет подходящего шаблона, ознакомьтесь с Using Plugin Outlet Connectors from a Theme or Plugin.

Простое модальное окно будет выглядеть примерно так:

<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. Вы также можете установить modalIsVisible, используя любой другой стандартный метод Ember.

Этот пример создаст простое модальное окно, похожее на это:

Оборачивание в компонент

Прежде чем вводить дополнительную сложность, как правило, лучше обернуть новое модальное окно в собственное определение компонента. Давайте переместим содержимое <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}}

Добавление подвала (footer)

Во многих модальных окнах есть какой-то призыв к действию. В 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()` возвращает промис, поэтому вы можете дождаться его закрытия.
// Он будет разрешен с данными, переданными в действие `@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 По умолчанию true, если установлен @closeModal. Отключает Esc / клик по фону / X.
@autofocus По умолчанию true. Автоматически фокусирует первый фокусируемый элемент через dTrapTab.
@submitOnEnter По умолчанию true. Enter нажимает .d-modal__footer .btn-primary, если фокус не находится в форме / textarea / select-kit.
@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 лайков

Пост был разделён на новую тему: Могу ли я показать модальное окно из head_tag