exports
Sheet · SheetTrigger · SheetClose · SheetContent · SheetHeader · SheetFooter · SheetTitle · SheetDescription
side
right, left, top, bottom 위치를 SheetContent에서 선택합니다.
mobileSide
좁은 화면에서 다른 방향으로 전환해야 할 때 사용합니다.
motion
type과 easing은 preset, duration은 number(ms) 또는 ms/s 문자열로 지정하며 motion={false}로 끌 수 있습니다.
showCloseButton
기본 닫기 버튼 표시 여부입니다. 명시적 footer 닫기를 둘 때 false로 둡니다.

오른쪽 패널

Sheet primitives와 명시적 닫기
코드 보기tsx
import {
  Sheet, SheetTrigger, SheetContent, SheetHeader,
  SheetTitle, SheetDescription, SheetFooter, SheetClose,
  Badge, Button,
} from "@olundot/ui";

export function SheetRight() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button variant="outline">상세 패널 열기</Button>
      </SheetTrigger>
      <SheetContent side="right" showCloseButton={false}>
        <SheetHeader>
          <SheetTitle>학생 상세</SheetTitle>
          <SheetDescription>응시 상태와 최근 활동을 확인합니다.</SheetDescription>
        </SheetHeader>
        <div className="grid gap-[var(--space-4)] p-[var(--space-6)]">
          <div className="rounded-[var(--radius-md)] border border-[var(--border-subtle)] bg-[var(--bg-canvas)] p-[var(--space-4)]">
            <strong>김지수</strong>
            <p>의학과 4학년 · A반</p>
            <Badge tone="success">응시 가능</Badge>
          </div>
          <dl>
            <div><dt>최근 제출</dt><dd>내과학 모의고사 2회</dd></div>
            <div><dt>평균 점수</dt><dd>88점</dd></div>
          </dl>
        </div>
        <SheetFooter>
          <SheetClose asChild>
            <Button variant="secondary">닫기</Button>
          </SheetClose>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

하단 패널

mobileSide 없이 bottom 배치
코드 보기tsx
import {
  Sheet, SheetTrigger, SheetContent, SheetHeader,
  SheetTitle, SheetDescription, SheetFooter, SheetClose,
  Button, Checkbox, Input,
} from "@olundot/ui";

export function SheetBottom() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button variant="outline">필터 열기</Button>
      </SheetTrigger>
      <SheetContent side="bottom" showCloseButton={false}>
        <SheetHeader>
          <SheetTitle>필터</SheetTitle>
          <SheetDescription>목록에 적용할 조건을 선택합니다.</SheetDescription>
        </SheetHeader>
        <div className="grid gap-[var(--space-4)] p-[var(--space-6)]">
          <Input surface="canvas" label="검색어" name="query" placeholder="학생명 또는 문항 코드" />
          <Checkbox label="미채점 항목만 보기" defaultChecked />
          <Checkbox label="오류 보고 항목 포함" />
        </div>
        <SheetFooter>
          <SheetClose asChild>
            <Button variant="primary">적용</Button>
          </SheetClose>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

duration 비교

300ms / 400ms / 500ms
코드 보기tsx
import { useState } from "react";
import {
  Sheet, SheetContent, SheetHeader, SheetTitle,
  SheetDescription, SheetFooter, SheetClose, Button,
} from "@olundot/ui";

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

export function SheetDurationComparison() {
  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>
      <Sheet open={duration !== null} onOpenChange={(open) => !open && setDuration(null)}>
        <SheetContent side="right" motion={{ duration: duration ?? 300, easing: "decel" }} showCloseButton={false}>
          <SheetHeader>
            <SheetTitle>{duration ?? 300}ms motion</SheetTitle>
            <SheetDescription>duration prop은 number(ms), "400ms", "0.4s" 형식을 받습니다.</SheetDescription>
          </SheetHeader>
          <div className="grid gap-[var(--space-2)] p-[var(--space-6)] text-body-sm text-text-secondary">
            <p>패널과 overlay가 같은 시간값으로 열리고 닫힙니다.</p>
          </div>
          <SheetFooter>
            <SheetClose asChild>
              <Button variant="secondary">닫기</Button>
            </SheetClose>
          </SheetFooter>
        </SheetContent>
      </Sheet>
    </>
  );
}

motion 없음

motion=false opt-out
코드 보기tsx
import {
  Sheet, SheetTrigger, SheetContent, SheetHeader,
  SheetTitle, SheetDescription, SheetFooter, SheetClose,
  Button,
} from "@olundot/ui";

export function SheetWithoutMotion() {
  return (
    <Sheet>
      <SheetTrigger asChild>
        <Button variant="outline">정적 패널 열기</Button>
      </SheetTrigger>
      <SheetContent side="right" motion={false} showCloseButton={false}>
        <SheetHeader>
          <SheetTitle>검토 메모</SheetTitle>
          <SheetDescription>motion을 끄고 즉시 열리는 보조 패널입니다.</SheetDescription>
        </SheetHeader>
        <div className="grid gap-[var(--space-2)] p-[var(--space-6)]">
          <p>민감한 화면 녹화나 저사양 환경에서는 motion을 끌 수 있습니다.</p>
        </div>
        <SheetFooter>
          <SheetClose asChild>
            <Button variant="secondary">닫기</Button>
          </SheetClose>
        </SheetFooter>
      </SheetContent>
    </Sheet>
  );
}

제어 상태

open과 onOpenChange
코드 보기tsx
import { useState } from "react";
import {
  Sheet, SheetContent, SheetHeader, SheetTitle,
  SheetDescription, SheetFooter, SheetClose, Button,
} from "@olundot/ui";

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

  return (
    <>
      <Button variant="primary" onClick={() => setOpen(true)}>
        문항 목록 열기
      </Button>
      <Sheet open={open} onOpenChange={setOpen}>
        <SheetContent side="left" showCloseButton={false}>
          <SheetHeader>
            <SheetTitle>문항 목록</SheetTitle>
            <SheetDescription>검토할 문항으로 빠르게 이동합니다.</SheetDescription>
          </SheetHeader>
          <div className="grid gap-[var(--space-2)] p-[var(--space-6)]">
            {["1. 흉통 감별", "2. 심전도 판독", "3. 응급 처치"].map((item) => (
              <button type="button" key={item}>{item}</button>
            ))}
          </div>
          <SheetFooter>
            <SheetClose asChild>
              <Button variant="secondary">닫기</Button>
            </SheetClose>
          </SheetFooter>
        </SheetContent>
      </Sheet>
    </>
  );
}