Discourse 3.1.0.beta6에는 완전히 새로운 <DModal> 컴포넌트 기반 API가 포함되어 있습니다. DModal은 UI 키트의 일부이며 discourse/ui-kit/d-modal에서 가져옵니다.
이 API는 이제 비추천(deprecated)된 기존 컨트롤러 기반 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}}
여기서는 hbs 전용으로 값을 설정하는 방법으로
mut헬퍼가 사용됩니다.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}}
푸터 추가
많은 모달에는某种 형태의 호출-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가 아닌 컨텍스트에서 모달 렌더링
이상적으로는 위에서 시연된 선언적 기술을 사용하여 Ember 템플릿 내에서 <DModal> 인스턴스를 렌더링해야 합니다. 이것이 사용 사례에 적합하지 않다면, modal 서비스를 주입(inject)하고 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` 액션에 전달된 데이터로 해결(resolve)됩니다.
const result = await this.modal.show(MyModal);
더 높은 커스터마이징!
<DModal>에는 여러 개의 명명된 블록과 인자가 있습니다.
인자(Arguments)
| Arg | 목적 |
|---|---|
@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". 네이티브 제출(submit)이 작동하도록 폼에는 "form"을 사용합니다. |
블록(Blocks)
| Block | 위치 | 사용 시점 |
|---|---|---|
default / :body |
주요 콘텐츠 영역 | 기본 영역 |
:aboveHeader |
헤더 이전, 가장 상단 | 드물게 필요하지만; 제목 바 위에 있어야 하는 콘텐츠(예: 배너)에 사용됩니다. |
:headerAboveTitle |
헤더 내부, 제목 이전 | 존재하지만 미사용입니다. 드물게 필요합니다. |
:belowModalTitle |
.d-modal__title 내부, <h1> 이후 |
보조 메타 정보를 위한 훌륭한 위치입니다. |
:headerBelowTitle |
헤더 내부, 제목 블록 이후 | 헤더의 일부인 탭, 서브 내비게이션 또는 검색 입력입니다. |
:headerPrimaryAction |
모바일 전용 헤더의 오른쪽 | X 닫기 버튼을 기본 액션(예: “저장”)으로 대체합니다. 또한 왼쪽에 “취소” 버튼을 자동으로 렌더링하고 헤더에 .--has-primary-action을 추가합니다. |
:belowHeader |
헤더와 바디 사이 | 스크롤 가능한 바디 외부에 있어 고정(sticky) 표시되는 영구적인 서브 헤더 콘텐츠(예: 검색바). |
:aboveFooter |
바디와 푸터 사이 | @hideFooter가 설정되면 억제됩니다. 푸터와 연결되었지만 푸터 외부에 있는 콘텐츠에 사용합니다. 또한 드뭅니다. |
:footer |
하단 액션 바 | 기본 + 보조 버튼. 여기의 첫 번째 .btn-primary는 Enter가 트리거하는 대상입니다. |
:belowFooter |
푸터 이후 | 드물게 필요합니다; @hideFooter를 무시합니다. 경계된 푸터 영역 외부의 상태 텍스트에 유용합니다. |
출처: 인자에 대한 인터랙티브 스타일 가이드 및 명명된 블록에 대한 d-modal 템플릿 구현.
CSS
코어 오버라이드를 위해 .d-modal 클래스를 앵커로 사용하고, 레거시 .modal 선택자를 피하세요.
사용 가능한 4가지 수정자(modifiers):
- .
--large는 최대 너비를 800px로 설정합니다 (데스크톱 전용) - .
--max는 최대 너비를 90vw로 설정합니다 (데스크톱 전용) - .
has-search는 고정 높이(80vh)를 설정합니다: 결과 길이에 기반한 높이 변경을 피하기 위해 검색/필터 시스템이 있는 모달에 사용됩니다 (데스크톱 전용) .--stacked는 푸터 버튼을 스택킹(stack)하도록 설정합니다 (모바일 전용)
이 문서는 버전 관리됩니다 - 변경 사항을 github에서 제안하세요.

