# Time Picker
URL: /react/components/time-picker
Source: https://github.com/daangn/seed-design/blob/dev/docs/content/react/components/time-picker.mdx
휠을 스크롤하여 오전·오후, 시, 분을 선택하는 12시간제 시간 선택 컴포넌트입니다.
사용 가능 버전: @seed-design/react@2.2.0, @seed-design/css@2.4.0
## Preview
```tsx
import { Box, TimePicker } from "@seed-design/react";
export default function TimePickerPreview() {
return (
);
}
```
## Installation
- npm: npm install @seed-design/react @seed-design/css
- pnpm: pnpm add @seed-design/react @seed-design/css
- yarn: yarn add @seed-design/react @seed-design/css
- bun: bun add @seed-design/react @seed-design/css
## Props
## Examples
### Basic
`TimePicker`는 휠 형태의 시간 선택 UI만 제공합니다. Trigger, Bottom Sheet, 확인·취소 버튼과 폼 직렬화는 사용하는 화면에서 구성합니다.
```tsx
import { TimePicker } from "@seed-design/react";
```
### Controlled
`value`와 `onValueChange`를 사용하여 선택한 시간을 제어할 수 있습니다. `onValueChange`는 스크롤이 선택 항목에 정착하여 값이 확정되었을 때 호출됩니다.
현재는 12시간제 UI만 지원합니다. 다만 `value`, `defaultValue`, `onValueChange`에서 사용하는 `TimePickerValue`는 오전·오후를 별도 필드로 나누지 않고 24시간 형식으로 표현합니다.
값을 지정하지 않으면 `00:00`에서 시작합니다. 선택된 시간이 없을 때 현재 시각에서 시작하려면 현재 시각의 `hour`와 `minute`을 `defaultValue`로 전달하세요.
`hour`와 `minute`은 각각 유효한 범위의 정수여야 합니다. 유효하지 않은 `value`, `defaultValue`, `minuteStep`을 전달하면 `RangeError`가 발생합니다.
```tsx
"use client";
import { Box, Text, TimePicker, VStack, type TimePickerValue } from "@seed-design/react";
import * as React from "react";
export default function TimePickerControlled() {
const [value, setValue] = React.useState({ hour: 10, minute: 30 });
return (
{String(value.hour).padStart(2, "0")}:{String(value.minute).padStart(2, "0")}
);
}
```
### Use Case
Time Picker가 화면에서 차지하는 비중과 플랫폼에 따라 레이아웃을 정합니다.
- 시간 선택이 화면의 주된 작업이면 **Inline**을 사용합니다.
- 모바일 폼의 시간 입력 필드에는 **Bottom Sheet**를 우선 검토합니다.
Bottom Sheet는 surface를 여는 trigger가 필요합니다. `FieldButton`처럼 현재 값을 표시하는 입력형 버튼과 합성할 수 있습니다. Inline의 확정은 화면의 액션 영역이, Bottom Sheet의 확정은 surface 안의 확인 버튼이 담당합니다.
#### Inline
글쓰기나 설정 플로우에서 시간 선택이 하나의 단계라면 화면 흐름 안에 Time Picker를 직접 배치합니다.
```tsx
"use client";
import { Box, HStack, Text, TimePicker, VStack, type TimePickerValue } from "@seed-design/react";
import { ActionButton } from "seed-design/ui/action-button";
import * as React from "react";
function formatTime({ hour, minute }: TimePickerValue) {
const period = hour < 12 ? "오전" : "오후";
const displayHour = hour % 12 || 12;
return `${period} ${displayHour}:${String(minute).padStart(2, "0")}`;
}
export default function TimePickerInline() {
const [value, setValue] = React.useState({ hour: 10, minute: 30 });
const [savedValue, setSavedValue] = React.useState(value);
return (
영업 시작 시간
현재 설정: {formatTime(savedValue)}
setSavedValue(value)}>
완료
);
}
```
#### Bottom Sheet
모바일 폼에서는 입력 필드를 누르면 화면 하단에서 Time Picker가 나타나도록 구성합니다. 사용자가 완료하기 전까지는 임시 값을 관리하고, 확인 버튼을 누를 때 입력 필드의 값으로 반영합니다.
```tsx
"use client";
import { Portal, TimePicker, type TimePickerValue } from "@seed-design/react";
import { ActionButton } from "seed-design/ui/action-button";
import {
BottomSheetBody,
BottomSheetContent,
BottomSheetFooter,
BottomSheetRoot,
} from "seed-design/ui/bottom-sheet";
import { FieldButton, FieldButtonValue } from "seed-design/ui/field-button";
import * as React from "react";
function formatTime({ hour, minute }: TimePickerValue) {
const period = hour < 12 ? "오전" : "오후";
const displayHour = hour % 12 || 12;
return `${period} ${displayHour}:${String(minute).padStart(2, "0")}`;
}
export default function TimePickerBottomSheet() {
const [open, setOpen] = React.useState(false);
const [value, setValue] = React.useState({ hour: 10, minute: 30 });
const [draft, setDraft] = React.useState(value);
const handleOpenChange = (nextOpen: boolean) => {
if (nextOpen) setDraft(value);
setOpen(nextOpen);
};
return (
handleOpenChange(true),
}}
>
{formatTime(value)}
{
setValue(draft);
setOpen(false);
}}
>
완료
);
}
```
### Date 객체와 함께 사용하기
`TimePicker`는 날짜나 시간대를 해석하지 않습니다. 애플리케이션에서 `Date`를 상태로 관리한다면 `hour`와 `minute`을 `TimePickerValue`로 변환하고, 변경된 시간을 기존 날짜에 반영하세요.
```tsx
"use client";
import { Box, Text, TimePicker, VStack, type TimePickerValue } from "@seed-design/react";
import * as React from "react";
export default function TimePickerDateValue() {
const [date, setDate] = React.useState(() => new Date(2026, 6, 28, 13, 30));
const value: TimePickerValue = {
hour: date.getHours(),
minute: date.getMinutes(),
};
const handleValueChange = ({ hour, minute }: TimePickerValue) => {
setDate((currentDate) => {
const nextDate = new Date(currentDate);
nextDate.setHours(hour, minute, 0, 0);
return nextDate;
});
};
return (
{date.toLocaleString("ko-KR")}
);
}
```
### Minute Step
`minuteStep`은 `1`, `5`, `10`, `15`, `30` 중 하나를 사용할 수 있으며 기본값은 `5`입니다.
선택된 분이 간격에 맞지 않으면 가장 가까운 값으로 반올림합니다. 예를 들어 9시 13분은 `minuteStep={5}`에서 9시 15분으로 표시됩니다.
반올림은 시각 전체를 기준으로 계산하므로 시가 함께 바뀔 수 있습니다. 예를 들어 23시 59분은 `minuteStep={5}`에서 0시 0분이 됩니다. `value`를 제어하는 경우 정규화 자체로 `onValueChange`가 호출되지는 않습니다.
분 컬럼은 순환하지만 경계를 넘어도 시는 유지됩니다. 예를 들어 10시 55분에서 다음 분 항목인 00을 선택하면 10시 00분이 됩니다.
```tsx
import { Box, TimePicker } from "@seed-design/react";
export default function TimePickerMinuteStep() {
return (
);
}
```
### Disabled
`disabled`를 사용하면 모든 컬럼을 조작하거나 포커스할 수 없습니다.
```tsx
import { Box, TimePicker } from "@seed-design/react";
export default function TimePickerDisabled() {
return (
);
}
```
### Locale
`locale`에 따라 오전·오후와 숫자 표기, 컬럼 순서가 달라집니다.
전체와 각 컬럼의 기본 접근성 이름도 `locale`에 따라 한국어, 영어 또는 일본어로 제공됩니다. 기본값 외의 언어가 필요하거나 맥락을 더 구체적으로 설명하려면 `aria-label`, `periodAriaLabel`, `hourAriaLabel`, `minuteAriaLabel`을 직접 지정합니다.
```tsx
import { Box, TimePicker } from "@seed-design/react";
export default function TimePickerLocalization() {
return (
);
}
```
### Accessibility
Time Picker 전체는 `group`, 오전·오후·시·분 컬럼은 각각 `spinbutton`으로 제공됩니다. 키보드에서는 `ArrowUp`과 `ArrowDown`으로 이전·다음 항목을, `Home`과 `End`로 처음·마지막 항목을 선택할 수 있습니다.
화면에 Time Picker의 이름을 나타내는 요소가 있다면 `aria-labelledby`로 연결할 수 있습니다. `aria-labelledby`를 제공하지 않으면 전체 컴포넌트에는 기본값인 `"시간 선택"`이 사용됩니다.