Sheet
화면 왼쪽 또는 오른쪽에서 슬라이드되는 사이드 패널 컴포넌트입니다. Compound Component 구조(Root, Trigger, Portal, Overlay, Content, Header, Title, Body, Close)로 동작하며, 설정, 필터, 네비게이션 등의 용도로 사용됩니다. 모바일 최적화를 위해 768px 이하에서는 maxWidth가 무시되고 화면 전체 너비로 표시되며, 히스토리 기반 뒤로가기 제스처를 지원합니다.
미리보기
사용법
1"use client";
2
3import { useState } from "react";
4import { Button, Sheet } from "motile-ui";
5
6export default function PreviewExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <div style={{ display: "flex", justifyContent: "center" }}>
11 <Sheet.Root open={open} onOpenChange={setOpen}>
12 <Sheet.Trigger asChild>
13 <Button variant="primary">Sheet 열기</Button>
14 </Sheet.Trigger>
15 <Sheet.Portal>
16 <Sheet.Overlay />
17 <Sheet.Content>
18 <Sheet.Header>
19 <Sheet.Title>Sheet 제목</Sheet.Title>
20 <Sheet.Close />
21 </Sheet.Header>
22 <Sheet.Body>
23 <p>Sheet 컴포넌트의 내용이 여기에 표시됩니다.</p>
24 <p>화면 오른쪽에서 슬라이드되는 사이드 패널입니다.</p>
25 </Sheet.Body>
26 </Sheet.Content>
27 </Sheet.Portal>
28 </Sheet.Root>
29 </div>
30 );
31}API 레퍼런스
Sheet.Root
Sheet의 상태와 설정을 관리하는 최상위 컴포넌트
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
position | "left" | "right" | "right" | Sheet가 나타나는 위치 (left: 왼쪽, right: 오른쪽) |
closeOnBackdrop | boolean | { escapeKey?: boolean; clickOutside?: boolean } | true | 백드롭 클릭 또는 ESC 키로 닫기 제어 - boolean 또는 객체로 세밀한 제어 가능 |
maxWidth | string | "600px" | Sheet 최대 너비 (데스크톱 전용, 모바일은 전체 너비) |
zIndex | number | 1000 | Sheet의 z-index 값 |
open | boolean | - | Sheet 열림 상태 (제어 모드) |
defaultOpen | boolean | false | Sheet 초기 열림 상태 (비제어 모드) |
onOpenChange | (open: boolean) => void | - | Sheet 열림 상태 변경 시 호출되는 콜백 |
Sheet.Trigger
Sheet를 여는 트리거 요소
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
children | React.ReactElement | - | 트리거로 사용할 React 요소 |
asChild | boolean | false | 래퍼 없이 자식 요소만 렌더링 |
Sheet.Portal
Sheet를 특정 DOM 위치에 렌더링하는 Portal 컴포넌트
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
children | React.ReactNode | - | Portal에 렌더링할 내용 |
container | HTMLElement | document.body | Portal이 렌더링될 DOM 요소 |
Sheet.Overlay
Sheet 뒤에 표시되는 오버레이 배경
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
className | string | - | 추가 CSS 클래스명 |
style | React.CSSProperties | - | 인라인 스타일 |
Sheet.Content
Sheet의 메인 콘텐츠를 담는 컴포넌트
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
children | React.ReactNode | - | Sheet 콘텐츠 |
className | string | - | 추가 CSS 클래스명 |
style | React.CSSProperties | - | 인라인 스타일 |
Sheet.Header
Sheet 헤더 영역 (Title과 Close 버튼 포함)
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
children | React.ReactNode | - | Sheet 헤더 내용 |
className | string | - | 추가 CSS 클래스명 |
Sheet.Title
Sheet 제목을 표시하는 컴포넌트
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
children | React.ReactNode | - | Sheet 제목 텍스트 |
className | string | - | 추가 CSS 클래스명 |
Sheet.Body
Sheet 본문 내용을 담는 스크롤 가능한 영역
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
children | React.ReactNode | - | Sheet 본문 내용 |
className | string | - | 추가 CSS 클래스명 |
Sheet.Close
Sheet를 닫는 버튼
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
children | React.ReactNode | X icon | 닫기 버튼 내용 (기본: X 아이콘) |
asChild | boolean | false | 래퍼 없이 자식 요소만 렌더링 |
예제
왼쪽 위치
1"use client";
2
3import { useState } from "react";
4import { Button, Sheet } from "motile-ui";
5
6export default function LeftExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <div style={{ display: "flex", justifyContent: "center" }}>
11 <Sheet.Root open={open} onOpenChange={setOpen} position="left">
12 <Sheet.Trigger asChild>
13 <Button variant="secondary">왼쪽 Sheet 열기</Button>
14 </Sheet.Trigger>
15 <Sheet.Portal>
16 <Sheet.Overlay />
17 <Sheet.Content>
18 <Sheet.Header>
19 <Sheet.Title>왼쪽 Sheet</Sheet.Title>
20 <Sheet.Close />
21 </Sheet.Header>
22 <Sheet.Body>
23 <p>화면 왼쪽에서 슬라이드되는 Sheet입니다.</p>
24 <p>position="left" prop을 사용했습니다.</p>
25 </Sheet.Body>
26 </Sheet.Content>
27 </Sheet.Portal>
28 </Sheet.Root>
29 </div>
30 );
31}커스텀 닫기 버튼
1"use client";
2
3import { useState } from "react";
4import { Button, Sheet } from "motile-ui";
5
6export default function CustomCloseExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <div style={{ display: "flex", justifyContent: "center" }}>
11 <Sheet.Root open={open} onOpenChange={setOpen}>
12 <Sheet.Trigger asChild>
13 <Button variant="primary">커스텀 닫기 버튼</Button>
14 </Sheet.Trigger>
15 <Sheet.Portal>
16 <Sheet.Overlay />
17 <Sheet.Content>
18 <Sheet.Header>
19 <Sheet.Title>커스텀 닫기 버튼</Sheet.Title>
20 <Sheet.Close asChild>
21 <button
22 style={{
23 padding: "8px 16px",
24 backgroundColor: "#ef4444",
25 color: "white",
26 border: "none",
27 borderRadius: "6px",
28 cursor: "pointer",
29 fontSize: "14px",
30 fontWeight: "500",
31 }}
32 >
33 닫기
34 </button>
35 </Sheet.Close>
36 </Sheet.Header>
37 <Sheet.Body>
38 <p>asChild prop을 사용하여 커스텀 닫기 버튼을 만들 수 있습니다.</p>
39 <p>이 예제에서는 빨간색 버튼을 사용했습니다.</p>
40 </Sheet.Body>
41 </Sheet.Content>
42 </Sheet.Portal>
43 </Sheet.Root>
44 </div>
45 );
46}닫기 옵션 제어
1"use client";
2
3import { useState } from "react";
4import { Button, Sheet } from "motile-ui";
5
6export default function CloseOptionsExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <div style={{ display: "flex", justifyContent: "center" }}>
11 <Sheet.Root
12 open={open}
13 onOpenChange={setOpen}
14 closeOnBackdrop={{ escapeKey: true, clickOutside: false }}
15 >
16 <Sheet.Trigger asChild>
17 <Button variant="secondary">닫기 옵션</Button>
18 </Sheet.Trigger>
19 <Sheet.Portal>
20 <Sheet.Overlay />
21 <Sheet.Content>
22 <Sheet.Header>
23 <Sheet.Title>닫기 옵션 예제</Sheet.Title>
24 <Sheet.Close />
25 </Sheet.Header>
26 <Sheet.Body>
27 <p>ESC 키로만 닫히고 외부 클릭으로는 닫히지 않습니다.</p>
28 <p>closeOnBackdrop 옵션을 세밀하게 제어할 수 있습니다.</p>
29 <ul>
30 <li>ESC 키: 활성화</li>
31 <li>외부 클릭: 비활성화</li>
32 </ul>
33 </Sheet.Body>
34 </Sheet.Content>
35 </Sheet.Portal>
36 </Sheet.Root>
37 </div>
38 );
39}최대 너비 설정
1"use client";
2
3import { useState } from "react";
4import { Button, Sheet } from "motile-ui";
5
6export default function MaxWidthExample() {
7 const [open, setOpen] = useState(false);
8
9 return (
10 <div style={{ display: "flex", justifyContent: "center" }}>
11 <Sheet.Root open={open} onOpenChange={setOpen} maxWidth="400px">
12 <Sheet.Trigger asChild>
13 <Button variant="ghost">좁은 Sheet</Button>
14 </Sheet.Trigger>
15 <Sheet.Portal>
16 <Sheet.Overlay />
17 <Sheet.Content>
18 <Sheet.Header>
19 <Sheet.Title>최대 너비 제한</Sheet.Title>
20 <Sheet.Close />
21 </Sheet.Header>
22 <Sheet.Body>
23 <p>maxWidth="400px"로 설정된 Sheet입니다.</p>
24 <p>데스크톱에서는 최대 400px 너비로 제한됩니다.</p>
25 <p>모바일에서는 전체 너비를 차지합니다.</p>
26 </Sheet.Body>
27 </Sheet.Content>
28 </Sheet.Portal>
29 </Sheet.Root>
30 </div>
31 );
32}