O Discourse 3.1.0.beta6 traz uma nova API baseada em componentes <DModal>.
Isso substitui a antiga API baseada em controladores, que agora está obsoleta. Se você possui modais existentes usando as APIs antigas, consulte o guia de migração aqui.
Renderizando um Modal
Os modais são renderizados incluindo o componente <DModal> em um template Handlebars. Se você ainda não possui um template adequado, consulte Using Plugin Outlet Connectors from a Theme or Plugin.
Um modal simples seria algo assim:
<DButton
@translatedLabel="Mostrar Modal"
@action={{fn (mut this.modalIsVisible) true}}
/>
{{#if this.modalIsVisible}}
<DModal @title="Meu Modal" @closeModal={{fn (mut this.modalIsVisible) false}}>
Olá mundo, este é algum conteúdo em um modal
</DModal>
{{/if}}
O helper
muté usado aqui como uma maneira exclusiva de hbs para definir um valor. Você também poderia definirmodalIsVisibleusando qualquer outro método padrão do Ember.
Este exemplo criará um modal simples como este:
Envolver em um componente
Antes de introduzir qualquer complexidade adicional, 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="Meu Modal" @closeModal={{@closeModal}}>
Olá mundo, este é algum conteúdo em um modal
</DModal>
</template>
Atualizar este arquivo .gjs para um componente baseado em classe permitirá que você introduza lógica e estado mais complexos.
Para utilizar o novo componente, atualize o local de chamada para referenciá-lo, garantindo que você passe o argumento @closeModal.
<DButton
@translatedLabel="Mostrar Modal"
@action={{fn (mut this.modalIsVisible) true}}
/>
{{#if this.modalIsVisible}}
<MyModal @closeModal={{fn (mut this.modalIsVisible) false}} />
{{/if}}
Adicionando um rodapé
Muitos modais possuem algum tipo de chamada para ação. No Discourse, eles tendem a estar localizados na parte inferior do modal. Para tornar isso possível, o DModal possui vários „blocos nomeados“ nos quais conteúdo pode ser renderizado. Aqui está o exemplo atualizado para incluir dois botões no rodapé, um dos quais é nosso botão padrão DModalCancel.
<DModal @title="Meu Modal" @closeModal={{@closeModal}}>
<:body>
Olá mundo, este é algum conteúdo em um modal
</:body>
<:footer>
<DButton class="btn-primary" @translatedLabel="Enviar" />
<DModalCancel @close={{@closeModal}} />
</:footer>
</DModal>
Renderizando um modal de um contexto não-hbs
Idealmente, as instâncias de <DModal> devem ser renderizadas dentro 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, acione o modal passando uma referência da sua classe de 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 para seu componente automaticamente.
this.modal.show(MyModal);
// Opcionalmente, passe um parâmetro `model`. Passado como `@model` para seu componente.
// Isso pode incluir dados e também ações/callbacks para seu Modal usar.
this.modal.show(MyModal, {
model: { topic: this.topic, someAction: this.someAction },
});
// `modal.show()` retorna uma promessa, então você pode aguardar até que seja fechado
// Ela será resolvida com os dados passados para a ação `@closeModal`
const result = await this.modal.show(MyModal);
Mais personalização!
O <DModal> possui vários blocos nomeados e argumentos.
Argumentos
| Arg | Propósito |
|---|---|
@closeModal |
Obrigatório para que a UI de descarte 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 |
Esconde regiões inteiras. |
@headerClass, @bodyClass |
Classe extra nos envoltórios do cabeçalho/corpo. |
@dismissable |
Padrão verdadeiro quando @closeModal definido. Desabilita Esc / clique no fundo / X. |
@autofocus |
Padrão verdadeiro. Foca automaticamente no 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
| Bloco | Posição | Quando usar |
|---|---|---|
padrão / :body |
Área de conteúdo principal | Área padrão |
:aboveHeader |
Muito topo, antes do cabeçalho | Raramente necessário; para conteúdo que deve ficar acima da barra de título (ex.: um banner). |
:headerAboveTitle |
Dentro do cabeçalho, antes do título | Presente, mas não utilizado. Raramente necessário. |
:belowModalTitle |
Dentro de .d-modal__title, após o <h1> |
Posição excelente para metadados suplementares. |
:headerBelowTitle |
Dentro do cabeçalho, após o bloco de título | Abas, sub-navegação ou campo de busca que faz parte do cabeçalho. |
:headerPrimaryAction |
Lado direito do cabeçalho apenas em mobile | Substitui o botão de fechar X 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 cabeçalho e corpo | Conteúdo de subcabeçalho persistente (ex.: busca) que está fora do corpo rolável, para exibição fixa. |
:aboveFooter |
Entre corpo e rodapé | Suprimido quando @hideFooter está definido. Use para conteúdo vinculado ao rodapé, mas fora dele. Também raro. |
:footer |
Barra de ação inferior | Botões primários e secundários. O primeiro .btn-primary aqui é o que o Enter aciona. |
:belowFooter |
Após o rodapé | Raramente necessário; ignora @hideFooter. Útil para texto de status fora da área do rodapé com borda. |
Fontes: o guia de estilo interativo para argumentos, e a implementação do template d-modal para blocos nomeados.
CSS
Use as classes .d-modal como âncora para substituir o núcleo e evite o seletor legado .modal.
4 modificadores disponíveis:
.--largedefine largura máxima para 800px (apenas desktop).--maxdefine largura máxima para 90vw (apenas desktop).has-searchdefine altura fixa (80vh): destinado a modais com sistema de busca/filtro para evitar mudança de altura baseada no comprimento do resultado (apenas desktop).--stackeddefine botões do rodapé para empilhamento (apenas mobile)
Este documento é controlado por versão — sugira alterações no GitHub.

