Bottom Sheet
화면 하단에서 올라오는 모달 컴포넌트입니다. 추가 정보나 액션 목록을 제공하면서도 현재 컨텍스트를 유지할 때 사용됩니다.
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetPreview = () => {
return (
<BottomSheetRoot>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent title="제목" description="설명을 작성할 수 있어요">
{/* If you need to omit padding, pass px={0}. */}
<BottomSheetBody>Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetPreview;Stackflow
Stackflow와 Bottom Sheet를 함께 사용하는 방법에 대해 알아보세요.
Installation
npx @seed-design/cli@latest add ui:bottom-sheetProps
BottomSheetRoot
Prop
Type
BottomSheetTrigger
Prop
Type
BottomSheetContent
Prop
Type
BottomSheetBody
Prop
Type
BottomSheetFooter
Prop
Type
Examples
Trigger
<BottomSheetTrigger>는 asChild 패턴을 사용해 자식 요소가 BottomSheet를 열 수 있도록 합니다.
<BottomSheetTrigger>는 aria-haspopup="dialog" 속성을 설정하고, BottomSheet의 open 상태에 따라 aria-expanded 속성을 자동으로 설정합니다. 이 속성은 스크린 리더와 같은 보조 기술에 유용합니다.
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetTriggerExample = () => {
return (
<BottomSheetRoot>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent title="제목">
<BottomSheetBody minHeight="x16">Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetTriggerExample;Controlled
Trigger 외의 방식으로 BottomSheet를 열고 닫을 수 있습니다. 이 경우 open prop을 사용하여 BottomSheet의 상태를 제어합니다.
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
} from "seed-design/ui/bottom-sheet";
const BottomSheetControlled = () => {
const [open, setOpen] = useState(false);
const scheduleOpen = () => {
setTimeout(() => {
setOpen(true);
}, 1000);
};
return (
<>
<ActionButton onClick={scheduleOpen}>1초 후 열기</ActionButton>
<BottomSheetRoot open={open} onOpenChange={setOpen}>
<BottomSheetContent title="제목" description="설명을 작성할 수 있어요">
{/* If you need to omit padding, pass px={0}. */}
<BottomSheetBody minHeight="x16">Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
</>
);
};
export default BottomSheetControlled;Max Height
<BottomSheetBody>에 maxHeight prop을 전달하여 BottomSheet의 최대 높이를 설정할 수 있습니다.
import { Box, VStack } from "@seed-design/react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetMaxHeight = () => {
return (
<BottomSheetRoot>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent title="제목" description="설명을 작성할 수 있어요">
{/* If you need to omit padding, pass px={0}. */}
<BottomSheetBody>
<VStack maxHeight="300px" overflowY="auto">
<VStack justifyContent="center" alignItems="center" gap="x4" height="100%">
<Box width="100%" height="100px" bg="bg.layerBasement" />
<Box width="100%" height="100px" bg="bg.layerBasement" />
<Box width="100%" height="100px" bg="bg.layerBasement" />
<Box width="100%" height="100px" bg="bg.layerBasement" />
</VStack>
</VStack>
</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetMaxHeight;Snap Points
snapPoints prop을 사용하여 BottomSheet의 커스텀 스냅 포인트를 설정할 수 있습니다.
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const snapPoints = ["300px", "500px", 1];
const BottomSheetSnapPoints = () => {
const [snap, setSnap] = useState<number | string | null>(snapPoints[0]);
return (
<BottomSheetRoot snapPoints={snapPoints} activeSnapPoint={snap} setActiveSnapPoint={setSnap}>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent
title="제목"
description="설명을 작성할 수 있어요"
/**
* snap points를 사용할 때는 handle을 표시해야 합니다.
*/
showHandle
/**
* 높이를 100%로 설정해야 snap points가 제대로 작동합니다.
*/
style={{ height: "100%", maxHeight: "97%" }}
>
<BottomSheetBody>Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetSnapPoints;Fade From Index
<BottomSheetRoot>에 fadeFromIndex prop을 전달하여 뒷 배경이 어두워지는 시작 인덱스를 설정할 수 있습니다.
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const snapPoints = ["300px", "500px", 1];
const BottomSheetFadeFromIndex = () => {
const [snap, setSnap] = useState<number | string | null>(snapPoints[0]);
return (
<BottomSheetRoot
fadeFromIndex={0}
snapPoints={snapPoints}
activeSnapPoint={snap}
setActiveSnapPoint={setSnap}
>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent
title="제목"
description="설명을 작성할 수 있어요"
/**
* snap points를 사용할 때는 handle을 표시해야 합니다.
*/
showHandle
/**
* 높이를 100%로 설정해야 snap points가 제대로 작동합니다.
*/
style={{ height: "100%", maxHeight: "97%" }}
>
<BottomSheetBody>Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetFadeFromIndex;Show Handle
<BottomSheetContent>에 showHandle prop을 전달하여 Handle을 표시할 수 있습니다.
기본 값은 false입니다.
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetShowHandle = () => {
const [isSheetOpen, setIsSheetOpen] = useState(false);
return (
<BottomSheetRoot open={isSheetOpen} onOpenChange={setIsSheetOpen}>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent title="제목" description="설명을 작성할 수 있어요" showHandle>
{/* If you need to omit padding, pass px={0}. */}
<BottomSheetBody minHeight="x16">Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid" onClick={() => setIsSheetOpen(false)}>
닫기
</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetShowHandle;Show Close Button
<BottomSheetContent>에 showCloseButton prop을 전달하여 닫기 버튼을 표시할 수 있습니다.
기본 값은 true입니다.
showCloseButton을 false로 설정하면 닫기 버튼이 표시되지 않습니다.
이 경우 유저가 BottomSheet를 닫을 수 있는 방법을 제공해야 합니다.
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetShowCloseButton = () => {
const [isSheetOpen, setIsSheetOpen] = useState(false);
return (
<BottomSheetRoot open={isSheetOpen} onOpenChange={setIsSheetOpen}>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent
title="제목"
description="설명을 작성할 수 있어요"
showCloseButton={false}
>
{/* If you need to omit padding, pass px={0}. */}
<BottomSheetBody minHeight="x16">Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid" onClick={() => setIsSheetOpen(false)}>
닫기
</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetShowCloseButton;Dismissible
dismissible prop을 false로 설정하면 closeOnEscape, closeOnInteractOutside, draggable 기능이 비활성화됩니다.
의도적으로 BottomSheet를 닫을 수 없게 하고 싶을 때 사용합니다. 이외에는 유저가 BottomSheet를 닫을 수 있는 방법을 제공해야 합니다.
import { useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetDismissible = () => {
const [open, setOpen] = useState(false);
return (
<BottomSheetRoot open={open} onOpenChange={setOpen} dismissible={false}>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent title="제목" description="설명을 작성할 수 있어요">
{/* If you need to omit padding, pass px={0}. */}
<BottomSheetBody>Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid" onClick={() => setOpen(false)}>
닫기
</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetDismissible;With Scroll Fog
<BottomSheetBody>에 Scroll Fog를 사용하여 스크롤 힌트 효과를 적용할 수 있습니다.
ScrollFog는 항상 효과를 표시하므로, 충분한 padding을 제공해야 합니다.
권장 padding인 하단 80px, 상단 20px을 유지해야 합니다.
import { Box, ScrollFog, VStack } from "@seed-design/react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetMaxHeight = () => {
return (
<BottomSheetRoot>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent title="제목" description="설명을 작성할 수 있어요">
{/* If you need to omit padding, pass px={0}. */}
<BottomSheetBody>
<VStack maxHeight="300px" overflowY="auto">
<ScrollFog placement={["top", "bottom"]}>
<VStack
justifyContent="center"
alignItems="center"
gap="x4"
height="100%"
pb="80px"
pt="20px"
>
<Box width="100%" height="100px" bg="bg.layerBasement" />
<Box width="100%" height="100px" bg="bg.layerBasement" />
<Box width="100%" height="100px" bg="bg.layerBasement" />
<Box width="100%" height="100px" bg="bg.layerBasement" />
</VStack>
</ScrollFog>
</VStack>
</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetMaxHeight;Bottom Inset
<BottomSheetContent>에 style prop을 전달하여 아래 여백을 주기 위해 사용합니다.
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetBottomInset = () => {
return (
<BottomSheetRoot>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent
title="제목"
description="설명을 작성할 수 있어요"
// 모바일 브라우저에서 아래 여백을 주기 위해 사용
style={{ paddingBottom: "var(--seed-safe-area-bottom)" }}
>
{/* If you need to omit padding, pass px={0}. */}
<BottomSheetBody minHeight="x16">Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetBottomInset;Handle Only
<BottomSheetRoot>에 handleOnly prop을 제공하는 경우 쓸어서(swipe) Bottom Sheet를 움직일 수 있는 영역이 핸들 부분으로 제한됩니다.
이 옵션은 <BottomSheetContent showHandle={true}>와 함께 사용할 때만 작동합니다.
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetHandleOnly = () => {
return (
<BottomSheetRoot handleOnly>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent showHandle title="제목" description="설명을 작성할 수 있어요">
<BottomSheetBody>Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetHandleOnly;Skip Animation
skipAnimation prop을 사용하여 BottomSheet의 enter/exit 애니메이션을 건너뛸 수 있습니다.
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
BottomSheetTrigger,
} from "seed-design/ui/bottom-sheet";
const BottomSheetSkipAnimation = () => {
return (
<BottomSheetRoot skipAnimation>
<BottomSheetTrigger asChild>
<ActionButton>Open</ActionButton>
</BottomSheetTrigger>
<BottomSheetContent title="제목" description="설명을 작성할 수 있어요">
<BottomSheetBody>Content</BottomSheetBody>
<BottomSheetFooter>
<ActionButton variant="neutralSolid">확인</ActionButton>
</BottomSheetFooter>
</BottomSheetContent>
</BottomSheetRoot>
);
};
export default BottomSheetSkipAnimation;Last updated on