Components

BottomSheet

사용 예시

공유

항목을 BottomSheetClose로 감싸면 고를 때 BottomSheet가 닫혀요. 동작 줄이기를 켠 기기에서는 아래에서 올라오지 않고 제자리에서 나타나요.

tsx
<BottomSheet>  <BottomSheetTrigger render={<Button color="neutral" emphasis="outline" />}>    <Share2 />    공유  </BottomSheetTrigger>  <BottomSheetContent>    <BottomSheetHeader closable>      <BottomSheetTitle>공유</BottomSheetTitle>      <BottomSheetDescription>보낼 곳을 고르세요.</BottomSheetDescription>    </BottomSheetHeader>    <div className="flex flex-col gap-tight">      {targets.map(target => (        <BottomSheetClose key={target.label} render={<Button color="neutral" emphasis="ghost" className="justify-start" />}>          <target.Icon />          {target.label}        </BottomSheetClose>      ))}    </div>  </BottomSheetContent></BottomSheet>

고르면 바로 닫기

정렬처럼 하나를 고르면 끝나는 선택은 고른 뒤 호출하는 쪽에서 닫아요.

tsx
<BottomSheet open={open} onOpenChange={setOpen}>  <BottomSheetTrigger render={<Button color="neutral" emphasis="outline" />}>    <ArrowUpDown />    {sortLabel[sort]}  </BottomSheetTrigger>  <BottomSheetContent>    <BottomSheetHeader closable>      <BottomSheetTitle>정렬</BottomSheetTitle>    </BottomSheetHeader>    <RadioGroup value={sort} onValueChange={v => { setSort(v); setOpen(false) }} aria-label="정렬">      <RadioGroupItem value="recent"><RadioGroupItemLabel>최근 등록순</RadioGroupItemLabel></RadioGroupItem>      <RadioGroupItem value="price"><RadioGroupItemLabel>낮은 가격순</RadioGroupItemLabel></RadioGroupItem>      <RadioGroupItem value="popular"><RadioGroupItemLabel>인기순</RadioGroupItemLabel></RadioGroupItem>    </RadioGroup>  </BottomSheetContent></BottomSheet>

푸터 버튼

BottomSheetFooter의 layout을 fill로 두면 버튼이 같은 폭으로 나뉘어 손가락으로 누르기 쉬워요.

tsx
<BottomSheet>  <BottomSheetTrigger render={<Button color="brand" emphasis="solid" />}>장바구니에 담기</BottomSheetTrigger>  <BottomSheetContent>    <BottomSheetHeader>      <BottomSheetTitle>장바구니에 담았어요</BottomSheetTitle>      <BottomSheetDescription>러닝화 265mm 1개</BottomSheetDescription>    </BottomSheetHeader>    <BottomSheetFooter layout="fill">      <BottomSheetClose render={<Button color="neutral" emphasis="outline" />}>계속 쇼핑</BottomSheetClose>      <BottomSheetClose render={<Button color="brand" emphasis="solid" />}>장바구니 보기</BottomSheetClose>    </BottomSheetFooter>  </BottomSheetContent></BottomSheet>

긴 내용

내용이 길면 화면 높이의 11/12까지 올라와요. 본문을 BottomSheetBody에 담으면 제목과 푸터 버튼은 그대로 있고 본문만 스크롤돼요. 위쪽에 빈 곳이 남아서 뒤 화면을 눌러 닫을 수 있어요.

tsx
<BottomSheet>  <BottomSheetTrigger render={<Button color="neutral" emphasis="outline" />}>약관 보기</BottomSheetTrigger>  <BottomSheetContent>    <BottomSheetHeader closable>      <BottomSheetTitle>이용 약관</BottomSheetTitle>    </BottomSheetHeader>    <BottomSheetBody className="flex flex-col gap-tight">      {terms.map(line => <p key={line} className="text-prose">{line}</p>)}    </BottomSheetBody>    <BottomSheetFooter layout="fill">      <BottomSheetClose render={<Button color="brand" emphasis="solid" />}>동의</BottomSheetClose>    </BottomSheetFooter>  </BottomSheetContent></BottomSheet>

BottomSheet 위의 BottomSheet

BottomSheet 안에서 다른 BottomSheet를 열면 뒤의 BottomSheet가 조금 작아지고 overlay로 어두워져요. 안쪽 BottomSheet는 바깥 BottomSheetContent 안에 둬요. 안쪽이 더 낮으면 뒤 BottomSheet의 위쪽이 보여요.

tsx
<BottomSheet>  <BottomSheetTrigger render={<Button color="neutral" emphasis="outline" />}>    <Share2 />    공유  </BottomSheetTrigger>  <BottomSheetContent>    <BottomSheetHeader closable>      <BottomSheetTitle>공유</BottomSheetTitle>    </BottomSheetHeader>    <div className="flex flex-col gap-tight">      {targets.map(target => (        <BottomSheetClose key={target.label} render={<Button color="neutral" emphasis="ghost" className="justify-start" />}>          <target.Icon />          {target.label}        </BottomSheetClose>      ))}    </div>    <BottomSheet>      <BottomSheetTrigger render={<Button color="neutral" emphasis="ghost" className="justify-start" />}>        <Ellipsis />        더 보기      </BottomSheetTrigger>      <BottomSheetContent>        <BottomSheetHeader closable>          <BottomSheetTitle>더 보기</BottomSheetTitle>        </BottomSheetHeader>        <div className="flex flex-col gap-tight">          {moreTargets.map(target => (            <BottomSheetClose key={target} render={<Button color="neutral" emphasis="ghost" className="justify-start" />}>              {target}            </BottomSheetClose>          ))}        </div>      </BottomSheetContent>    </BottomSheet>  </BottomSheetContent></BottomSheet>

BottomSheet 안에서 묻기

되돌릴 수 없는 동작은 BottomSheet 안에서도 AlertDialog로 물어요. AlertDialog는 화면 가운데에 열리고 뒤의 BottomSheet를 어둡게 해요.

tsx
<BottomSheet>  <BottomSheetTrigger render={<Button color="neutral" emphasis="outline" />}>    <Ellipsis />    사진 메뉴  </BottomSheetTrigger>  <BottomSheetContent>    <BottomSheetHeader closable>      <BottomSheetTitle>사진</BottomSheetTitle>    </BottomSheetHeader>    <AlertDialog>      <AlertDialogTrigger render={<Button color="danger" emphasis="ghost" className="justify-start" />}>        <Trash2 />        사진 지우기      </AlertDialogTrigger>      <AlertDialogContent>        <AlertDialogHeader>          <AlertDialogTitle>사진을 지울까요?</AlertDialogTitle>          <AlertDialogDescription>지운 사진은 되돌릴 수 없어요.</AlertDialogDescription>        </AlertDialogHeader>        <AlertDialogFooter>          <AlertDialogCancel>그대로 두기</AlertDialogCancel>          <AlertDialogAction color="danger">지우기</AlertDialogAction>        </AlertDialogFooter>      </AlertDialogContent>    </AlertDialog>  </BottomSheetContent></BottomSheet>

명세

이름
이름값기본
BottomSheet
openboolean-
호출하는 쪽에서 열림 상태를 관리해요.
defaultOpenbooleanfalse
처음에 열어 둘지 정해요.
onOpenChange(open: boolean, eventDetails) => void-
열리거나 닫히면 호출돼요. 아래로 스와이프해서 닫으면 eventDetails.reason이 swipe예요.
renderReactElement-
기본 요소 대신 그릴 요소예요. BottomSheetTrigger와 BottomSheetClose에 Button을 넘길 때 써요.
classNamestring-
모든 하위 컴포넌트에 쓸 수 있어요. 640px 이상에서 BottomSheetContent의 너비를 바꾸려면 sm:max-w-160처럼 sm:을 붙여요.
BottomSheetHeader
leadReactNode-
제목 앞에 보여요.
trailReactNode-
헤더 오른쪽 끝에 보여요.
closablebooleanfalse
헤더 오른쪽 끝에 닫기 버튼을 보여요. trail이 있으면 trail 다음에 와요.
closeLabelstring닫기
닫기 버튼의 접근성 이름이에요.
BottomSheetBody
childrenReactNode-
본문이에요. 길면 헤더와 BottomSheetFooter는 그대로 두고 이 안에서만 스크롤돼요.
BottomSheetFooter
layoutend, between, fill, grow-last, { narrow, wide }end
버튼 배치예요. end는 오른쪽에 모으고, between은 첫 버튼만 왼쪽 끝에 두고, fill은 모든 버튼을 같은 폭으로, grow-last는 마지막 버튼만 남은 폭만큼 늘려요. 640px 아래와 위를 다르게 하려면 narrow와 wide를 넘겨요.