Usando a API DModal para renderizar janelas modais (também conhecidas como popups/diálogos) no Discourse

O Discourse 3.1.0.beta6 inclui uma nova API baseada em componentes <DModal>. O DModal faz parte do kit de UI e é importado de discourse/ui-kit/d-modal.

:information_source: Esta API substitui a antiga API baseada em controladores, que agora está obsoleta. Se você tiver modais existentes usando as antigas APIs, consulte o guia de migração aqui.

Renderizando um Modal

Modais são renderizados incluindo o componente <DModal> em um template handlebars. Se você ainda não tiver um template adequado, consulte Using Plugin Outlet Connectors from a Theme or Plugin.

Um modal simples seria algo assim:

<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: O helper mut é usado aqui como uma forma exclusiva de hbs para definir um valor. Você também poderia definir modalIsVisible usando qualquer outro método padrão do Ember.

Este exemplo criará um Modal simples como este:

Encapsulando em um componente

Antes de introduzir mais complexidade, geralmente é melhor encapsular seu novo Modal em sua própria definição de Componente. Vamos mover o conteúdo do <DModal> para dentro de um novo componente <MyModal />

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

Atualizar este arquivo .gjs para um componente baseado em classe permitirá que você introduza lógica e estado mais complexos.

Para usar o novo componente, atualize o ponto de chamada para referenciá-lo, garantindo que passe um argumento @closeModal.

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

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

Adicionando um rodapé

Muitos modais têm algum tipo de chamada para ação. No Discourse, essas ações costumam estar localizadas na parte inferior do modal. Para tornar isso possível, o DModal tem uma série de ‘blocos nomeados’ nos quais o conteúdo pode ser renderizado. Aqui está o exemplo atualizado para incluir dois botões no rodapé, um dos quais é o nosso botão padrão 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>

Renderizando um modal de um contexto não-hbs

Idealmente, as instâncias de <DModal> devem ser renderizadas a partir de um template Ember usando a técnica declarativa demonstrada acima. Se isso não for viável para o seu caso de uso, pode ser feito injetando o serviço modal e chamando modal.show().

Certifique-se de ter encapsulado seu modal em seu próprio componente, conforme descrito acima. Em seguida, dispare o modal passando uma referência da classe do seu componente para showModal:

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

// (injetar o serviço modal no local relevante)

// Adicione esta chamada sempre que quiser abrir o modal.
// Um argumento `@closeModal` será passado automaticamente para o seu componente.
this.modal.show(MyModal);

// Opcionalmente, passe um parâmetro '`model`'. Passado como `@model` para o seu componente.
// Isso pode incluir dados, bem como ações/callbacks para o seu Modal usar.
this.modal.show(MyModal, {
  model: { topic: this.topic, someAction: this.someAction },
});

// `modal.show()` retorna uma promise, então você pode esperar que ela seja fechada
// Ela será resolvida com os dados passados para a ação `@closeModal`
const result = await this.modal.show(MyModal);

Mais personalização!

<DModal> tem uma série de blocos nomeados e argumentos.

Argumentos

Arg Propósito
@closeModal Obrigatório para que a UI de dispensa apareça.
@title Renderiza <h1 id="discourse-modal-title">; conecta aria-labelledby.
@subtitle Texto pequeno abaixo do título.
@flash / @flashType Alerta inline no topo do modal (DFlashMessage).
@hideHeader, @hideFooter Oculta regiões inteiras.
@headerClass, @bodyClass Classe extra nos wrappers de cabeçalho/corpo.
@dismissable Padrão verdadeiro quando @closeModal está definido. Desativa Esc / clique no fundo / X.
@autofocus Padrão verdadeiro. Foca automaticamente o primeiro elemento focável via dTrapTab.
@submitOnEnter Padrão verdadeiro. Enter clica em .d-modal__footer .btn-primary a menos que o foco esteja em um formulário / textarea / select-kit.
@beforeClose async ({ initiatedBy }) => boolean. Retorne false para cancelar o fechamento (ex. confirmação de formulário sujo).
@hidden Pausa o tratamento de teclado; usado quando um modal aninhado está no topo.
@tagName "div" (padrão) ou "form". Use "form" para formulários para que o envio nativo funcione.

Blocos

Block Posição Quando usar
default / :body Área de conteúdo principal Área padrão
:aboveHeader Topo, antes do cabeçalho Raramente necessário; para conteúdo que deve estar acima da barra de título (ex. um banner).
:headerAboveTitle Dentro do cabeçalho, antes do título Presente, mas não usado. Raramente necessário.
:belowModalTitle Dentro de .d-modal__title, após o <h1> Excelente posição para informações meta suplementares.
:headerBelowTitle Dentro do cabeçalho, após o bloco de título Abas, sub-nav ou campo de busca que faz parte do cabeçalho.
:headerPrimaryAction Lado direito do cabeçalho apenas no mobile Substitui o botão X de fechar por uma ação primária (ex. “Salvar”). Também renderiza automaticamente um botão “Cancelar” à esquerda e adiciona .--has-primary-action ao cabeçalho.
:belowHeader Entre o cabeçalho e o corpo Conteúdo de sub-cabeçalho persistente (ex. busca) que está fora do corpo rolável, para exibição fixa.
:aboveFooter Entre o corpo e o rodapé Suprimido quando @hideFooter está definido. Use para conteúdo vinculado ao rodapé, mas fora dele. Também raro.
:footer Barra de ações inferior Botões primários + secundários. O primeiro .btn-primary aqui é o que Enter aciona.
:belowFooter Após o rodapé Raramente necessário; ignora @hideFooter. Útil para texto de status fora da área de rodapé com borda.

Fontes: o guia de estilos interativo para argumentos e a implementação do template d-modal para blocos nomeados.

CSS

Use as classes .d-modal como âncora para sobrescrever o núcleo e evite o seletor legado .modal.

4 modificadores disponíveis:

  • .--large define a largura máxima para 800px (apenas desktop)
  • .--max define a largura máxima para 90vw (apenas desktop)
  • .has-search define a altura fixa (80vh): destinado a modais com sistema de busca/filtro para evitar mudança de altura com base no comprimento do resultado (apenas desktop)
  • .--stacked define os botões do rodapé para empilhamento (apenas mobile)

Este documento é controlado por versão - sugira alterações no github.

17 Curtiram

Uma postagem foi dividida em um novo tópico: Posso exibir um modal do head_tag