Modal
중앙에 표시되는 Modal 컴포넌트입니다. 4가지 애니메이션 variant(scale, slideDown, slideUp, bottomSheet)를 제공하며, ESC 키와 외부 클릭으로 닫기 옵션을 세밀하게 제어할 수 있습니다. Portal을 사용하여 DOM 트리의 최상단에 렌더링됩니다.
미리보기
사용법
1"use client";
2
3import { useState } from "react";
4import { Button, Modal } from "motile-ui";
5
6export default function PreviewExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <>
11 <Button onClick={() => setOpen(true)} variant="primary">
12 Open Modal
13 </Button>
14 <Modal.Root open={open} onOpenChange={setOpen}>
15 <Modal.Overlay>
16 <Modal.Content style={{ padding: "0" }}>
17 <Modal.Header>
18 <Modal.Title>알림</Modal.Title>
19 <Modal.Close />
20 </Modal.Header>
21 <Modal.Body style={{ padding: "24px" }}>
22 <p style={{ margin: 0, color: "#6b7280", lineHeight: 1.6, fontSize: "14px" }}>
23 Modal 컴포넌트입니다. ESC 키를 누르거나 외부를 클릭하여 닫을 수 있습니다.
24 </p>
25 </Modal.Body>
26 <Modal.Footer>
27 <Modal.Close asChild>
28 <Button variant="primary">확인</Button>
29 </Modal.Close>
30 </Modal.Footer>
31 </Modal.Content>
32 </Modal.Overlay>
33 </Modal.Root>
34 </>
35 );
36}API 레퍼런스
Modal.Root
Modal의 상태를 관리하는 최상위 컨텍스트 프로바이더 (제어 컴포넌트)
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
open | boolean | - | Modal 열림/닫힘 상태 |
onOpenChange | (open: boolean) => void | - | 상태가 변경될 때 실행되는 함수 |
Modal.Overlay
Modal 배경 오버레이 및 Portal
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
variant | "scale" | "slideDown" | "slideUp" | "bottomSheet" | "scale" | Modal 애니메이션 스타일 |
closeOnBackdrop | boolean | object | true | 백드롭 인터랙션으로 닫기 제어 (boolean 또는 escapeKey, clickOutside 속성을 가진 객체) |
disableScrollLock | boolean | false | 배경 스크롤 잠금 비활성화 |
width | string | - | Modal 너비 |
maxWidth | string | - | Modal 최대 너비 |
zIndex | number | 1000 | z-index 값 |
container | HTMLElement | document.body | Modal을 렌더링할 DOM 컨테이너 |
기본 <code>div</code> HTML 속성을 모두 사용할 수 있습니다.
Modal.Content
Modal 메인 콘텐츠 컨테이너
기본 <code>div</code> HTML 속성을 모두 사용할 수 있습니다.
Modal.Header
Modal 헤더 영역
기본 <code>div</code> HTML 속성을 모두 사용할 수 있습니다.
Modal.Title
Modal 제목
기본 <code>h2</code> HTML 속성을 모두 사용할 수 있습니다.
Modal.Body
Modal 본문 콘텐츠
기본 <code>div</code> HTML 속성을 모두 사용할 수 있습니다.
Modal.Footer
Modal Footer 영역
기본 <code>div</code> HTML 속성을 모두 사용할 수 있습니다.
Modal.Close
Modal을 닫는 버튼
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
asChild | boolean | false | 래퍼 없이 자식 요소만 렌더링 |
기본 <code>button</code> HTML 속성을 모두 사용할 수 있습니다.
예제
SlideDown 애니메이션
1"use client";
2
3import { useState } from "react";
4import { Button, Modal } from "motile-ui";
5
6export default function SlideDownExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <>
11 <Button onClick={() => setOpen(true)} variant="secondary" size="small">
12 SlideDown
13 </Button>
14 <Modal.Root open={open} onOpenChange={setOpen}>
15 <Modal.Overlay variant="slideDown">
16 <Modal.Content style={{ padding: "0" }}>
17 <Modal.Header>
18 <Modal.Title>SlideDown Animation</Modal.Title>
19 <Modal.Close />
20 </Modal.Header>
21 <Modal.Body>
22 <p style={{ margin: 0, color: "#6b7280", lineHeight: 1.6 }}>
23 화면 위에서 중앙으로 떨어지며 나타나는 애니메이션입니다.
24 </p>
25 </Modal.Body>
26 </Modal.Content>
27 </Modal.Overlay>
28 </Modal.Root>
29 </>
30 );
31}SlideUp 애니메이션
1"use client";
2
3import { useState } from "react";
4import { Button, Modal } from "motile-ui";
5
6export default function SlideUpExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <>
11 <Button onClick={() => setOpen(true)} variant="secondary" size="small">
12 SlideUp
13 </Button>
14 <Modal.Root open={open} onOpenChange={setOpen}>
15 <Modal.Overlay variant="slideUp">
16 <Modal.Content style={{ padding: "0" }}>
17 <Modal.Header>
18 <Modal.Title>SlideUp Animation</Modal.Title>
19 <Modal.Close />
20 </Modal.Header>
21 <Modal.Body>
22 <p style={{ margin: 0, color: "#6b7280", lineHeight: 1.6 }}>
23 화면 아래에서 중앙으로 올라오며 나타나는 애니메이션입니다.
24 </p>
25 </Modal.Body>
26 </Modal.Content>
27 </Modal.Overlay>
28 </Modal.Root>
29 </>
30 );
31}하단 Modal
1"use client";
2
3import { useState } from "react";
4import { Button, Modal } from "motile-ui";
5
6export default function BottomSheetExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <>
11 <Button onClick={() => setOpen(true)} variant="secondary" size="small">
12 하단 Modal
13 </Button>
14 <Modal.Root open={open} onOpenChange={setOpen}>
15 <Modal.Overlay variant="bottomSheet">
16 <Modal.Content style={{ padding: "24px" }}>
17 <h2 style={{ fontSize: "20px", fontWeight: 600, marginBottom: "16px" }}>
18 옵션 선택
19 </h2>
20 <p style={{ margin: "0 0 24px", color: "#6b7280", lineHeight: 1.6 }}>
21 원하는 옵션을 선택해주세요. 화면 하단에서 올라오는 bottomSheet 스타일입니다.
22 </p>
23 <Modal.Close asChild>
24 <Button variant="primary" size="large" style={{ width: "100%" }}>
25 확인
26 </Button>
27 </Modal.Close>
28 </Modal.Content>
29 </Modal.Overlay>
30 </Modal.Root>
31 </>
32 );
33}ESC 키로만 닫기
1"use client";
2
3import { useState } from "react";
4import { Button, Modal } from "motile-ui";
5
6export default function EscKeyOnlyExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <>
11 <Button onClick={() => setOpen(true)} variant="secondary" size="small">
12 ESC Only
13 </Button>
14 <Modal.Root open={open} onOpenChange={setOpen}>
15 <Modal.Overlay closeOnBackdrop={{ escapeKey: true, clickOutside: false }}>
16 <Modal.Content style={{ padding: "0" }}>
17 <Modal.Header>
18 <Modal.Title>ESC 키로만 닫기</Modal.Title>
19 <Modal.Close />
20 </Modal.Header>
21 <Modal.Body>
22 <p style={{ margin: 0, color: "#6b7280", lineHeight: 1.6 }}>
23 이 Modal은 ESC 키로만 닫을 수 있습니다. 외부 클릭은 비활성화되어 있습니다.
24 </p>
25 </Modal.Body>
26 </Modal.Content>
27 </Modal.Overlay>
28 </Modal.Root>
29 </>
30 );
31}버튼으로만 닫기
1"use client";
2
3import { useState } from "react";
4import { Button, Modal } from "motile-ui";
5
6export default function NoBackdropCloseExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <>
11 <Button onClick={() => setOpen(true)} variant="secondary" size="small">
12 No Backdrop Close
13 </Button>
14 <Modal.Root open={open} onOpenChange={setOpen}>
15 <Modal.Overlay closeOnBackdrop={false}>
16 <Modal.Content style={{ padding: "0" }}>
17 <Modal.Header>
18 <Modal.Title>버튼으로만 닫기</Modal.Title>
19 <Modal.Close />
20 </Modal.Header>
21 <Modal.Body>
22 <p style={{ margin: 0, color: "#6b7280", lineHeight: 1.6 }}>
23 이 Modal은 닫기 버튼으로만 닫을 수 있습니다.
24 ESC 키와 외부 클릭 모두 비활성화되어 있습니다.
25 </p>
26 </Modal.Body>
27 </Modal.Content>
28 </Modal.Overlay>
29 </Modal.Root>
30 </>
31 );
32}커스텀 너비
1"use client";
2
3import { useState } from "react";
4import { Button, Modal } from "motile-ui";
5
6export default function CustomWidthExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <>
11 <Button onClick={() => setOpen(true)} variant="secondary" size="small">
12 Custom Width
13 </Button>
14 <Modal.Root open={open} onOpenChange={setOpen}>
15 <Modal.Overlay width="600px">
16 <Modal.Content style={{ padding: "0" }}>
17 <Modal.Header>
18 <Modal.Title>넓은 Modal (600px)</Modal.Title>
19 <Modal.Close />
20 </Modal.Header>
21 <Modal.Body>
22 <div style={{ color: "#6b7280", lineHeight: 1.6 }}>
23 <p style={{ marginTop: 0 }}>
24 이 Modal은 사용자 정의 너비(600px)를 가지며,
25 더 많은 콘텐츠를 표시하는 데 적합합니다.
26 </p>
27 <p>
28 모바일 기기에서는 자동으로 전체 너비로 조정되어
29 작은 화면에서 최적의 사용성을 보장합니다.
30 </p>
31 <p style={{ marginBottom: 0 }}>
32 이러한 유연성은 모든 기기 크기에서
33 작동해야 하는 반응형 디자인에 완벽합니다.
34 </p>
35 </div>
36 </Modal.Body>
37 </Modal.Content>
38 </Modal.Overlay>
39 </Modal.Root>
40 </>
41 );
42}