DModal API를 사용하여 Discourse에서 모달 창(팝업/대화상자) 렌더링

Discourse 3.1.0.beta6에는 완전히 새로운 <DModal> 컴포넌트 기반 API가 포함되어 있습니다. DModalUI 키트의 일부이며 discourse/ui-kit/d-modal에서 가져옵니다.

:information_source: 이 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}}

:information_source: 여기서는 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에서 제안하세요.

17개의 좋아요

A post was split to a new topic: Can I show a modal from head_tag