반응형 방향
desktop에서는 오른쪽 drawer, mobile에서는 bottom sheet 형태로 열립니다.
width
default는 480px, wide는 720px, full은 거의 전체 화면 폭의 drawer입니다.
motion
SheetContent와 같은 motion object 또는 false를 받아 desktop right, mobile bottom 전환을 제어합니다. duration은 number(ms) 또는 ms/s 문자열입니다.
caption
title 위 eyebrow 영역입니다. breadcrumb, 상태 badge, 과목 badge처럼 먼저 보여야 하는 메타데이터를 둡니다.
expand action
expanded와 onExpandedChange로 크게 보기 / 작게 보기 header action을 제어합니다.
footer
저장, 적용, 선택 완료 같은 하단 action을 고정합니다.

기본 drawer

오른쪽 desktop, 하단 mobile
코드 보기tsx
import { useState } from "react";
import { SheetDrawer, Badge, Button } from "@olundot/ui";

export function SheetDrawerDefault() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        빠른 상세 열기
      </Button>
      <SheetDrawer
        open={open}
        onOpenChange={setOpen}
        caption={
          <>
            <Badge tone="info">객관식</Badge>
            <Badge tone="neutral">심혈관계</Badge>
          </>
        }
        title="문항 상세"
        description="현재 선택한 문항의 검토 정보를 확인합니다."
      >
        <div className="grid gap-[var(--space-4)]">
          <div className="rounded-[var(--radius-md)] border border-[var(--border-subtle)] bg-[var(--bg-canvas)] p-[var(--space-4)]">
            <strong>흉통 감별 문항</strong>
            <Badge tone="warning">검토 필요</Badge>
            <p>정답률 62%, 이의 신청 2건이 접수되었습니다.</p>
          </div>
          <dl>
            <div><dt>최근 수정일</dt><dd>2026-06-03</dd></div>
            <div><dt>담당자</dt><dd>김교수</dd></div>
          </dl>
        </div>
      </SheetDrawer>
    </>
  );
}
저장과 취소를 drawer 하단에 고정
코드 보기tsx
import { useState } from "react";
import { SheetDrawer, Button, Checkbox } from "@olundot/ui";

export function SheetDrawerFooter() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button variant="primary" onClick={() => setOpen(true)}>
        출제 범위 선택
      </Button>
      <SheetDrawer
        open={open}
        onOpenChange={setOpen}
        title="출제 범위"
        description="이번 시험에 포함할 단원을 선택합니다."
        footer={
          <>
            <Button variant="secondary" size="sm" onClick={() => setOpen(false)}>
              취소
            </Button>
            <Button variant="primary" size="sm" onClick={() => setOpen(false)}>
              선택 완료
            </Button>
          </>
        }
      >
        <div className="grid gap-[var(--space-2)]">
          <Checkbox label="심혈관계" defaultChecked />
          <Checkbox label="호흡기계" />
          <Checkbox label="소화기계" />
        </div>
      </SheetDrawer>
    </>
  );
}

duration 비교

300ms / 400ms / 500ms
코드 보기tsx
import { useState } from "react";
import { SheetDrawer, Button, Badge } from "@olundot/ui";

const durations = [300, 400, 500] as const;

export function SheetDrawerDurationComparison() {
  const [duration, setDuration] = useState<(typeof durations)[number] | null>(null);

  return (
    <>
      <div className="flex flex-wrap gap-[var(--space-2)]">
        {durations.map((value) => (
          <Button key={value} variant="outline" onClick={() => setDuration(value)}>
            {value}ms 열기
          </Button>
        ))}
      </div>
      <SheetDrawer
        open={duration !== null}
        onOpenChange={(open) => !open && setDuration(null)}
        motion={{ duration: duration ?? 300, easing: "decel" }}
        title={`${duration ?? 300}ms drawer`}
        description="SheetDrawer도 SheetContent와 같은 시간값 duration을 사용합니다."
      >
        <div className="grid gap-[var(--space-4)]">
          <Badge tone="info">{duration ?? 300}ms</Badge>
          <p>desktop right, mobile bottom 전환에 같은 duration이 적용됩니다.</p>
        </div>
      </SheetDrawer>
    </>
  );
}

motion 없음

motion=false opt-out
코드 보기tsx
import { useState } from "react";
import { SheetDrawer, Button, Badge } from "@olundot/ui";

export function SheetDrawerWithoutMotion() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button variant="outline" onClick={() => setOpen(true)}>
        정적 drawer 열기
      </Button>
      <SheetDrawer
        open={open}
        onOpenChange={setOpen}
        motion={false}
        title="검토 메모"
        description="motion 없이 즉시 열리는 drawer입니다."
      >
        <div className="grid gap-[var(--space-4)]">
          <Badge tone="info">motion 꺼짐</Badge>
          <p>전환 효과를 끄되 desktop right, mobile bottom 배치는 그대로 유지합니다.</p>
        </div>
      </SheetDrawer>
    </>
  );
}

확장 action

expanded toggle과 full 폭
코드 보기tsx
import { useState } from "react";
import { SheetDrawer, Button } from "@olundot/ui";

export function SheetDrawerExpand() {
  const [open, setOpen] = useState(false);
  const [expanded, setExpanded] = useState(false);

  return (
    <div style={{ display: "grid", gap: "12px" }}>
      <Button variant="outline" onClick={() => setOpen(true)}>
        분석 패널 열기
      </Button>
      <SheetDrawer
        open={open}
        onOpenChange={(nextOpen) => {
          setOpen(nextOpen);
          if (!nextOpen) setExpanded(false);
        }}
        title="성취도 분석"
        description="영역별 정답률과 취약 단원을 확인합니다."
        width="wide"
        expanded={expanded}
        onExpandedChange={setExpanded}
        expandLabel="크게 보기"
        collapseLabel="작게 보기"
      >
        <div className="grid gap-[var(--space-4)]">
          {["순환기 76%", "호흡기 58%", "감염 64%"].map((item) => (
            <div key={item} className="rounded-[var(--radius-md)] border border-[var(--border-subtle)] bg-[var(--bg-canvas)] p-[var(--space-4)]">
              {item}
            </div>
          ))}
        </div>
      </SheetDrawer>
    </div>
  );
}