DModal-API zum Rendern von Modal-Fenstern (auch Popups/Dialoge genannt) in Discourse

Discourse 3.1.0.beta6 wird mit einer brandneuen, komponentenbasierten <DModal>-API ausgeliefert. DModal ist Teil des UI-Kit und wird aus discourse/ui-kit/d-modal importiert.

:information_source: Dies ersetzt die alte, auf Controllern basierende API, die nun als veraltet gilt. Wenn du bestehende Modals mit den alten APIs verwendest, findest du den Migrationsleitfaden hier.

Rendern eines Modals

Modals werden gerendert, indem die <DModal>-Komponente in einer Handlebars-Vorlage enthalten wird. Wenn du noch keine geeignete Vorlage hast, schaue dir Using Plugin Outlet Connectors from a Theme or Plugin an.

Ein einfaches Modal könnte etwa so aussehen:

<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: Der mut-Helper wird hier als hbs-spezifische Methode verwendet, um einen Wert zu setzen. Du könntest modalIsVisible auch mit jeder anderen Standard-Ember-Methode setzen.

Dieses Beispiel erstellt ein einfaches Modal wie dieses:

Einbetten in eine Komponente

Bevor weitere Komplexität eingeführt wird, ist es in der Regel am besten, das neue Modal in seiner eigenen Komponentendefinition zu kapseln. Verschieben wir den <DModal>-Teil in eine neue <MyModal />-Komponente.

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

Das Aufrüsten dieser .gjs-Datei zu einer klassenbasierten Komponente ermöglicht es dir, komplexere Logik und Zustände einzuführen.

Um die neue Komponente zu verwenden, aktualisiere die Aufrufstelle, damit sie darauf verweist, und achte darauf, ein @closeModal-Argument zu übergeben.

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

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

Hinzufügen einer Fußzeile

Viele Modals haben eine Art Handlungsaufforderung (Call-to-Action). In Discourse befinden sich diese tendenziell am unteren Rand des Modals. Um dies zu ermöglichen, verfügt DModal über eine Reihe von „benannten Blöcken“ (named blocks), in die Inhalte gerendert werden können. Hier ist das Beispiel aktualisiert, um zwei Buttons in der Fußzeile zu enthalten, von denen einer unser Standard-DModalCancel-Button ist.

<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>

Rendern eines Modals aus einem Nicht-hbs-Kontext

Idealerweise sollten <DModal>-Instanzen innerhalb einer Ember-Vorlage gerendert werden, indem die oben demonstrierte deklarative Technik verwendet wird. Wenn das für deinen Anwendungsfall nicht machbar ist, kann dies durch Injizieren des modal-Services und Aufrufen von modal.show() erledigt werden.

Stelle sicher, dass du dein Modal wie oben beschrieben in seiner eigenen Komponente gekapselt hast. Dann löst du das Modal aus, indem du eine Referenz auf deine Komponentenklasse an showModal übergibst:

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

// (injiziere den modal-Service an der relevanten Stelle)

// Füge diesen Aufruf hinzu, wann immer du das Modal öffnen möchtest.
// Ein `@closeModal`-Argument wird deiner Komponente automatisch übergeben.
this.modal.show(MyModal);

// Optional: Übergebe einen '`model`'-Parameter. Wird als `@model` an deine Komponente übergeben.
// Dies kann Daten und auch Aktionen/Callbacks enthalten, die dein Modal verwenden soll.
this.modal.show(MyModal, {
  model: { topic: this.topic, someAction: this.someAction },
});

// `modal.show()` gibt ein Promise zurück, daher kannst du darauf warten, dass es geschlossen wird.
// Es wird mit den Daten aufgelöst, die an die `@closeModal`-Aktion übergeben wurden.
const result = await this.modal.show(MyModal);

Mehr Anpassungsfähigkeit!

<DModal> verfügt über eine Reihe von benannten Blöcken und Argumenten.

Argumente

Arg Zweck
@closeModal Erforderlich, damit die UI zum Schließen überhaupt angezeigt wird.
@title Rendert <h1 id="discourse-modal-title">; verdrahtet aria-labelledby.
@subtitle Kleiner Text unter dem Titel.
@flash / @flashType Inline-Warnung am oberen Rand des Modals (DFlashMessage).
@hideHeader, @hideFooter Versteckt ganze Bereiche.
@headerClass, @bodyClass Zusätzliche Klasse für Header-/Body-Wrapper.
@dismissable Standardmäßig true, wenn @closeModal gesetzt ist. Deaktiviert Esc / Klick auf Hintergrund / X.
@autofocus Standardmäßig true. Fokussiert automatisch das erste fokussierbare Element über dTrapTab.
@submitOnEnter Standardmäßig true. Enter klickt .d-modal__footer .btn-primary, es sei denn, der Fokus liegt in einem Formular / Textbereich / Select-Kit.
@beforeClose async ({ initiatedBy }) => boolean. Gib false zurück, um das Schließen abzubrechen (z. B. Bestätigung bei schmutzigem Formular).
@hidden Pausiert die Tastatursteuerung; wird verwendet, wenn ein verschachteltes Modal darüber liegt.
@tagName "div" (Standard) oder "form". Verwende "form" für Formulare, damit das native Absenden funktioniert.

Blöcke

Block Position Wann zu verwenden
default / :body Hauptinhaltsbereich Standardbereich
:aboveHeader Ganz oben, vor dem Header Selten benötigt; für Inhalte, die über der Titelleiste sitzen müssen (z. B. ein Banner).
:headerAboveTitle Im Header, vor dem Titel Vorhanden, aber ungenutzt. Selten benötigt.
:belowModalTitle Innerhalb von .d-modal__title, nach dem <h1> Hervorragende Position für ergänzende Metainformationen.
:headerBelowTitle Im Header, nach dem Titelblock Tabs, Sub-Navigation oder Suchfeld, die Teil des Headers sind.
:headerPrimaryAction Rechte Seite des Headers nur auf Mobilgeräten Ersetzt den X-Schließen-Button durch eine primäre Aktion (z. B. „Speichern“). Rendert auch automatisch einen „Abbrechen“-Button links und fügt .--has-primary-action zum Header hinzu.
:belowHeader Zwischen Header und Body Persistenter Sub-Header-Inhalt (z. B. Suche), der außerhalb des scrollbaren Body liegt, für eine feste Anzeige.
:aboveFooter Zwischen Body und Footer Unterdrückt, wenn @hideFooter gesetzt ist. Verwende für Inhalte, die mit der Fußzeile verbunden sind, aber außerhalb liegen. Ebenfalls selten.
:footer Untere Aktionsleiste Primäre + sekundäre Buttons. Der erste .btn-primary hier ist der, der durch Enter ausgelöst wird.
:belowFooter Nach der Fußzeile Selten benötigt; ignoriert @hideFooter. Nützlich für Statuszeilen außerhalb des gerahmten Fußzeilenbereichs.

Quellen: der interaktive Styleguide für Argumente und die d-modal-Vorlagendurchführung für benannte Blöcke.

CSS

Verwende die .d-modal-Klassen als Anker, um das Core-Design zu überschreiben, und vermeide den Legacy-.modal-Selektor.

4 Modifikatoren verfügbar:

  • .--large setzt die maximale Breite auf 800px (nur Desktop)
  • .--max setzt die maximale Breite auf 90vw (nur Desktop)
  • .has-search setzt die feste Höhe (80vh): für Modals mit Such-/Filtersystem gedacht, um Höhenänderungen basierend auf der Ergebnislänge zu vermeiden (nur Desktop)
  • .--stacked setzt die Footer-Buttons auf gestapelte Anordnung (nur Mobilgeräte)

Dieses Dokument ist versioniert - schlage Änderungen auf GitHub vor.

17 „Gefällt mir“

Ein Beitrag wurde in ein neues Thema aufgeteilt: Kann ich ein Modal aus head_tag anzeigen