Sheet

Sheet는 화면의 가장자리에서 슬라이드되어 나타나는 오버레이 컴포넌트입니다. 사이드바, 메뉴, 폼, 상세 정보 등을 표시할 때 사용합니다.
'use client';

import { Button, Sheet } from '@vapor-ui/core';
import { CloseOutlineIcon } from '@vapor-ui/icons';

export default function DefaultSheet() {
    return (
        <Sheet.Root>
            <Sheet.Trigger render={<Button variant="outline" />}>Open Sheet</Sheet.Trigger>
            <Sheet.Content>
                <div className="absolute top-4 right-4">
                    <Sheet.Close aria-label="Close sheet" className="flex">
                        <CloseOutlineIcon />
                    </Sheet.Close>
                </div>
                <Sheet.Header>
                    <Sheet.Title>알림</Sheet.Title>
                </Sheet.Header>
                <Sheet.Body>
                    <Sheet.Description>
                        Sheet는 화면 가장자리에서 슬라이드되어 나타나는 오버레이 컴포넌트입니다.
                        추가 정보나 작업을 위한 공간을 제공합니다.
                    </Sheet.Description>
                </Sheet.Body>
                <Sheet.Footer>
                    <Sheet.Close render={<Button variant="ghost" />}>닫기</Sheet.Close>
                    <Button color="primary">확인</Button>
                </Sheet.Footer>
            </Sheet.Content>
        </Sheet.Root>
    );
}

Property


Side

Sheet가 나타날 위치를 설정할 수 있습니다. 기본값은 'right'입니다.

'use client';

import { Button, Sheet } from '@vapor-ui/core';
import { CloseOutlineIcon } from '@vapor-ui/icons';

export default function SheetSide() {
    return (
        <div className="flex flex-wrap gap-4">
            {/* Right Side (Default) */}
            <Sheet.Root>
                <Sheet.Trigger render={<Button variant="outline" />}>Right</Sheet.Trigger>
                <Sheet.Content positionerProps={{ side: 'right' }}>
                    <div className="absolute top-4 right-4">
                        <Sheet.Close aria-label="Close sheet" className="flex">
                            <CloseOutlineIcon />
                        </Sheet.Close>
                    </div>
                    <Sheet.Header>
                        <Sheet.Title>우측 Sheet</Sheet.Title>
                    </Sheet.Header>
                    <Sheet.Body>
                        <Sheet.Description>
                            화면 우측에서 슬라이드됩니다. (기본값)
                        </Sheet.Description>
                    </Sheet.Body>
                </Sheet.Content>
            </Sheet.Root>

            {/* Left Side */}
            <Sheet.Root>
                <Sheet.Trigger render={<Button variant="outline" />}>Left</Sheet.Trigger>
                <Sheet.Content positionerProps={{ side: 'left' }}>
                    <div className="absolute top-4 right-4">
                        <Sheet.Close aria-label="Close sheet" className="flex">
                            <CloseOutlineIcon />
                        </Sheet.Close>
                    </div>
                    <Sheet.Header>
                        <Sheet.Title>좌측 Sheet</Sheet.Title>
                    </Sheet.Header>
                    <Sheet.Body>
                        <Sheet.Description>화면 좌측에서 슬라이드됩니다.</Sheet.Description>
                    </Sheet.Body>
                </Sheet.Content>
            </Sheet.Root>

            {/* Top Side */}
            <Sheet.Root>
                <Sheet.Trigger render={<Button variant="outline" />}>Top</Sheet.Trigger>
                <Sheet.Content positionerProps={{ side: 'top' }}>
                    <div className="absolute top-4 right-4">
                        <Sheet.Close aria-label="Close sheet" className="flex">
                            <CloseOutlineIcon />
                        </Sheet.Close>
                    </div>
                    <Sheet.Header>
                        <Sheet.Title>상단 Sheet</Sheet.Title>
                    </Sheet.Header>
                    <Sheet.Body>
                        <Sheet.Description>화면 상단에서 슬라이드됩니다.</Sheet.Description>
                    </Sheet.Body>
                </Sheet.Content>
            </Sheet.Root>

            {/* Bottom Side */}
            <Sheet.Root>
                <Sheet.Trigger render={<Button variant="outline" />}>Bottom</Sheet.Trigger>
                <Sheet.Content positionerProps={{ side: 'bottom' }}>
                    <div className="absolute top-4 right-4">
                        <Sheet.Close aria-label="Close sheet" className="flex">
                            <CloseOutlineIcon />
                        </Sheet.Close>
                    </div>
                    <Sheet.Header>
                        <Sheet.Title>하단 Sheet</Sheet.Title>
                    </Sheet.Header>
                    <Sheet.Body>
                        <Sheet.Description>화면 하단에서 슬라이드됩니다.</Sheet.Description>
                    </Sheet.Body>
                </Sheet.Content>
            </Sheet.Root>
        </div>
    );
}

Controlled State

Sheet의 열림/닫힘 상태를 외부에서 제어할 수 있습니다.

'use client';

import { useState } from 'react';

import { Button, Sheet } from '@vapor-ui/core';
import { CloseOutlineIcon } from '@vapor-ui/icons';

export default function SheetControlled() {
    const [isOpen, setIsOpen] = useState(false);

    return (
        <div className="space-y-4">
            <div className="flex gap-2">
                <Button onClick={() => setIsOpen(true)} color="primary">
                    Sheet 열기
                </Button>
                <Button onClick={() => setIsOpen(false)} color="danger" variant="outline">
                    Sheet 닫기
                </Button>
            </div>

            <p className="text-sm text-gray-600">
                현재 상태: <strong>{isOpen ? '열림' : '닫힘'}</strong>
            </p>

            <Sheet.Root open={isOpen} onOpenChange={setIsOpen}>
                <Sheet.Content>
                    <div className="absolute top-4 right-4">
                        <Sheet.Close aria-label="Close sheet" className="flex">
                            <CloseOutlineIcon />
                        </Sheet.Close>
                    </div>
                    <Sheet.Header>
                        <Sheet.Title>제어된 Sheet</Sheet.Title>
                    </Sheet.Header>
                    <Sheet.Body>
                        <Sheet.Description>
                            이 Sheet는 외부 버튼으로 상태가 제어됩니다. 프로그래밍 방식으로
                            열림/닫힘을 관리할 수 있습니다.
                        </Sheet.Description>
                    </Sheet.Body>
                    <Sheet.Footer>
                        <Sheet.Close render={<Button variant="ghost" />}>닫기</Sheet.Close>
                    </Sheet.Footer>
                </Sheet.Content>
            </Sheet.Root>
        </div>
    );
}

Keep Mounted

Sheet가 닫혀도 DOM에서 제거되지 않고 유지되도록 설정할 수 있습니다.

'use client';

import { Button, Sheet, TextInput } from '@vapor-ui/core';
import { CloseOutlineIcon } from '@vapor-ui/icons';

export default function SheetKeepMounted() {
    return (
        <div className="flex gap-4">
            {/* Normal Sheet */}
            <Sheet.Root>
                <Sheet.Trigger render={<Button variant="outline" />}>일반 Sheet</Sheet.Trigger>
                <Sheet.Content>
                    <div className="absolute top-4 right-4">
                        <Sheet.Close aria-label="Close sheet" className="flex">
                            <CloseOutlineIcon />
                        </Sheet.Close>
                    </div>
                    <Sheet.Header>
                        <Sheet.Title>일반 Sheet</Sheet.Title>
                    </Sheet.Header>
                    <Sheet.Body>
                        <Sheet.Description>
                            이 Sheet는 닫힐 때 DOM에서 제거됩니다. 다시 열 때마다 내용이 새로
                            생성됩니다.
                        </Sheet.Description>
                        <div className="mt-4">
                            <TextInput placeholder="입력해보세요..." />
                        </div>
                    </Sheet.Body>
                </Sheet.Content>
            </Sheet.Root>

            {/* Keep Mounted Sheet */}
            <Sheet.Root>
                <Sheet.Trigger render={<Button variant="outline" color="success" />}>
                    유지 Sheet
                </Sheet.Trigger>
                <Sheet.Content portalProps={{ keepMounted: true }}>
                    <div className="absolute top-4 right-4">
                        <Sheet.Close aria-label="Close sheet" className="flex">
                            <CloseOutlineIcon />
                        </Sheet.Close>
                    </div>
                    <Sheet.Header>
                        <Sheet.Title>유지되는 Sheet</Sheet.Title>
                    </Sheet.Header>
                    <Sheet.Body>
                        <Sheet.Description>
                            이 Sheet는 닫혀도 DOM에 유지됩니다. 입력한 내용이 보존되는 것을
                            확인해보세요.
                        </Sheet.Description>
                        <div className="mt-4">
                            <TextInput placeholder="상태 보존 테스트..." />
                        </div>
                    </Sheet.Body>
                </Sheet.Content>
            </Sheet.Root>
        </div>
    );
}

Examples


Basic Usage

Sheet는 다양한 용도로 활용할 수 있는 기본적인 오버레이 컴포넌트입니다.

'use client';

import { Button, Sheet } from '@vapor-ui/core';
import { CloseOutlineIcon } from '@vapor-ui/icons';

export default function SheetSimple() {
    return (
        <Sheet.Root>
            <Sheet.Trigger render={<Button variant="outline" />}>간단한 Sheet</Sheet.Trigger>
            <Sheet.Content>
                <div className="absolute top-4 right-4">
                    <Sheet.Close aria-label="Close sheet" className="flex">
                        <CloseOutlineIcon />
                    </Sheet.Close>
                </div>
                <Sheet.Header>
                    <Sheet.Title>간단한 Sheet</Sheet.Title>
                </Sheet.Header>
                <Sheet.Body>
                    <Sheet.Description>
                        이것은 Sheet 컴포넌트의 가장 기본적인 사용 예시입니다. 최소한의 구성으로
                        Sheet를 만들 수 있습니다.
                    </Sheet.Description>
                </Sheet.Body>
            </Sheet.Content>
        </Sheet.Root>
    );
}

Props Table


Sheet.Root

Sheet의 루트 컨테이너로, 전체 Sheet 컴포넌트의 상태와 동작을 관리합니다.

PropDefaultType
open?
null
boolean
defaultOpen?
false
boolean
onOpenChange?
null
(open: boolean) => void
children?
null
React.ReactNode

Sheet.Trigger

Sheet를 여는 트리거 요소입니다.

PropDefaultType
render?
button
React.ReactElement
className?
null
string
children?
null
React.ReactNode

Sheet.Content

Sheet의 실제 콘텐츠를 담는 컨테이너입니다. Portal, Overlay, Positioner를 조합하여 구성됩니다.

PropDefaultType
portalProps?
{}
SheetPortalProps
overlayProps?
{}
SheetOverlayProps
positionerProps?
{}
SheetPositionerProps
className?
null
string
children?
null
React.ReactNode

Sheet.Portal

Sheet를 DOM의 다른 위치에 렌더링하는 포털 컴포넌트입니다.

PropDefaultType
keepMounted?
false
boolean
container?
document.body
HTMLElement() => HTMLElement

Sheet.Overlay

Sheet 뒤의 배경 오버레이를 담당합니다.

PropDefaultType
render?
div
React.ReactElement
className?
null
string

Sheet.Popup

Sheet의 실제 팝업 콘텐츠 영역입니다.

PropDefaultType
render?
div
React.ReactElement
className?
null
string
children?
null
React.ReactNode

Sheet.Positioner

Sheet의 위치를 설정하는 컴포넌트입니다.

PropDefaultType
side?
"right"
"top""right""bottom""left"
render?
div
React.ReactElement
className?
null
string

Sheet.Header

Sheet의 헤더 영역을 담당합니다.

PropDefaultType
render?
div
React.ReactElement
className?
null
string
children?
null
React.ReactNode

Sheet.Body

Sheet의 본문 영역을 담당합니다.

PropDefaultType
render?
div
React.ReactElement
className?
null
string
children?
null
React.ReactNode

Sheet.Footer

Sheet의 푸터 영역을 담당합니다.

PropDefaultType
render?
div
React.ReactElement
className?
null
string
children?
null
React.ReactNode

Sheet.Title

Sheet의 제목을 표시하는 컴포넌트입니다.

PropDefaultType
render?
h2
React.ReactElement
className?
null
string
children?
null
React.ReactNode

Sheet.Description

Sheet의 설명을 표시하는 컴포넌트입니다.

PropDefaultType
render?
p
React.ReactElement
className?
null
string
children?
null
React.ReactNode

Sheet.Close

Sheet를 닫는 버튼 컴포넌트입니다.

PropDefaultType
render?
button
React.ReactElement
className?
null
string
children?
null
React.ReactNode