장바구니에 담을 물건의 수량이나 예약 인원을 정할 때는 보통 +와 - 버튼을 사용해요. 여기에 최소·최대 수량, 항목 삭제, 로딩 상태까지 더해지면 화면마다 챙겨야 할 조건이 많아져요.
Quantity Picker는 이런 조건을 일관된 방식으로 다뤄요. 최소·최대 범위를 정하고, 값을 제품에서 직접 관리하거나 Quantity Picker에 맡길 수 있어요. 값 텍스트와 버튼의 배치를 바꾸고 로딩 상태를 표시할 수도 있어요. 화면에 보이지 않는 input을 통해 선택한 값을 네이티브 폼으로 제출할 수 있고요. @seed-design/react@2.1.0, @seed-design/css@2.3.0부터 사용할 수 있어요.
import { QuantityPicker } from "seed-design/ui/quantity-picker";export default function QuantityPickerPreview() { return <QuantityPicker min={1} max={99} defaultValue={1} aria-label="상품 수량" />;}
최소 수량에서는 감소 버튼을 삭제 버튼으로 바꿀 수 있어요. 사용자는 같은 자리에서 항목을 삭제하고, 필요하다면 다시 되돌릴 수 있어요.
시간을 고르는 방식은 사용자의 언어·지역 설정과 제품의 목적에 따라 달라져요. 오전·오후를 먼저 보여줄지, 시와 분을 어떤 순서로 놓을지, 몇 분 단위로 선택하게 할지 함께 정해야 하죠.
Time Picker는 사용자의 언어·지역 설정(locale)에 맞춰 오전·오후와 시·분 칼럼의 순서와 레이블을 바꿔줘요. minuteStep으로 선택 간격을 정하거나 필요하지 않은 칼럼을 비활성화할 수 있고, 제품에서 관리하는 값이나 JavaScript Date 객체와도 연결할 수 있어요. @seed-design/react@2.2.0, @seed-design/css@2.4.0부터 사용할 수 있어요.
날짜를 고르는 경험은 달력 하나를 보여주는 것만으로 완성되지 않아요. 하나의 날짜만 고를 때도 있지만 여행이나 예약에서는 범위를 선택해야 하고, 어떤 날짜는 선택할 수 없거나 가격과 예약 상태를 함께 보여줘야 하죠.
Date Picker는 하나의 날짜와 날짜 범위, 여러 날짜를 선택하는 방식을 모두 제공해요. 한 달·두 달·한 주·연속 스크롤 레이아웃 가운데 화면과 탐색 맥락에 맞는 형태를 고를 수 있고, 최소·최대 기간이나 예약이 마감된 날짜처럼 선택을 제한하는 조건도 조합할 수 있어요. @seed-design/react@2.2.0, @seed-design/css@2.4.0부터 사용할 수 있어요.
정해진 목록에서 하나를 고르는 Select도 제품에 따라 필요한 기능이 달라져요. 여러 값을 함께 선택하거나 항목을 그룹으로 나누고, 설명과 아이콘을 더하거나, 선택한 값을 폼 검증과 연결해야 할 수도 있죠.
Select는 하나 또는 여러 값을 선택하고, 선택된 값을 제품에 필요한 형식으로 표시할 수 있어요. 항목을 그룹으로 나누거나 설명, 아이콘, 커스텀 레이블을 더할 수 있고, 긴 목록의 스크롤과 팝오버의 위치·크기, 비활성 상태도 설정할 수 있어요. @seed-design/react@2.1.0, @seed-design/css@2.3.0부터 사용할 수 있어요.
Dialog는 화면 중앙에 제목과 본문, 푸터를 배치해 사용자가 중요한 정보와 다음 행동에 집중하도록 도와줘요. 간단한 확인 메시지뿐 아니라 폼이나 스크롤되는 콘텐츠도 담을 수 있고, 콘텐츠에 맞춰 크기와 닫기 버튼, 본문 높이와 패딩, 푸터 액션의 배치를 조절할 수 있어요. @seed-design/react@2.1.0, @seed-design/css@2.3.0부터 사용할 수 있어요.
import { HStack, Text } from "@seed-design/react";import { DialogAction, DialogBody, DialogContent, DialogFooter, DialogRoot, DialogTrigger,} from "seed-design/ui/dialog";import { ActionButton } from "seed-design/ui/action-button";const DialogPreview = () => { return ( <DialogRoot> <DialogTrigger asChild> <ActionButton variant="neutralSolid">Open Dialog</ActionButton> </DialogTrigger> <DialogContent title="제목" description="설명을 작성할 수 있어요"> <DialogBody> <Text textStyle="articleBody"> 본문에는 사용자가 확인해야 할 내용이나 추가 입력 폼을 배치할 수 있습니다. </Text> </DialogBody> <DialogFooter> <HStack gap="x2" justify="flex-end"> <DialogAction variant="neutralWeak">취소</DialogAction> <DialogAction variant="neutralSolid">확인</DialogAction> </HStack> </DialogFooter> </DialogContent> </DialogRoot> );};export default DialogPreview;
열림 상태를 제품에서 직접 관리하고, Portal을 사용해 Dialog가 렌더링될 위치를 정할 수 있어요. 바깥 영역 클릭이나 ESC 입력으로 닫히지 않도록 설정해 사용자가 작성한 내용을 보호할 수 있어요. 닫으려는 이유에 따라 확인 Dialog를 한 번 더 보여줄 수도 있고요.
ResponsiveDialog를 사용하면 같은 콘텐츠를 넓은 화면에서는 Dialog로, 작은 화면에서는 Bottom Sheet로 보여줘요. 화면마다 두 가지 UI를 따로 구현하지 않아도 각 환경에 어울리는 확인 경험을 제공할 수 있어요.
import { HStack, Text, useResponsiveDialogContext } from "@seed-design/react";import { forwardRef } from "react";import { ActionButton } from "seed-design/ui/action-button";import { ResponsiveDialogAction, ResponsiveDialogBody, ResponsiveDialogContent, ResponsiveDialogFooter, ResponsiveDialogRoot, ResponsiveDialogTrigger, type ResponsiveDialogFooterProps,} from "seed-design/ui/responsive-dialog";const Footer = forwardRef<HTMLDivElement, ResponsiveDialogFooterProps>((props, ref) => { const { shouldUseBottomSheet } = useResponsiveDialogContext(); return ( <ResponsiveDialogFooter ref={ref} {...props}> <HStack gap="x2" justify="flex-end"> <ResponsiveDialogAction variant="neutralWeak" flexGrow={shouldUseBottomSheet ? 1 : undefined} > 취소 </ResponsiveDialogAction> <ResponsiveDialogAction variant="neutralSolid" flexGrow={shouldUseBottomSheet ? 1 : undefined} > 확인 </ResponsiveDialogAction> </HStack> </ResponsiveDialogFooter> );});const DialogResponsive = () => { return ( <ResponsiveDialogRoot> <ResponsiveDialogTrigger asChild> <ActionButton variant="neutralSolid">Open</ActionButton> </ResponsiveDialogTrigger> <ResponsiveDialogContent title="반응형 다이얼로그" description="화면 크기에 따라 적합한 컴포넌트로 자동 전환됩니다." > <ResponsiveDialogBody> <Text textStyle="articleBody"> md 이상에서는 화면 중앙의 Dialog로, sm 이하에서는 화면 하단에서 슬라이드되는 Bottom Sheet로 표시됩니다. </Text> </ResponsiveDialogBody> <Footer /> </ResponsiveDialogContent> </ResponsiveDialogRoot> );};export default DialogResponsive;
ScrollAutoHide는 스크롤 방향에 따라 필터, 탭, 보조 헤더 같은 고정 영역을 숨기고 드러내요. 아래로 스크롤하면 콘텐츠에 집중할 수 있도록 영역을 감추고, 위로 올리면 이동한 거리만큼 다시 보여줘요. 스크롤이 끝났을 때는 SEED의 모션 곡선(easing)을 사용해 영역을 완전히 숨기거나 드러내요.