Component
Calendar
단일 날짜와 기간을 선택하는 한국어 달력입니다.
Playground
옵션을 조절하며 화면과 사용 코드를 함께 확인하세요.
PC 1280px
Controls
하루·여러 날짜·기간 선택
컨테이너 너비에 맞게 날짜 셀 확장
동시에 표시할 개월 수
이전·다음 달 날짜 표시
매달 6주로 높이 고정
0: 일요일, 1: 월요일
선택한 날짜를 다시 눌러 해제하지 않음
Matcher 예시: 제한 없음·주말·전체 비활성
이동 가능한 첫 월 (YYYY-MM-DD)
이동 가능한 마지막 월. 시작보다 빠르면 범위를 적용하지 않음
Code
import { useState } from 'react'
import { Calendar } from '@amineslab/ui'
function parseCalendarDate(value: string): Date | undefined {
if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
return undefined;
const [year = 0, month = 0, day = 0] = value.split('-').map(Number);
const result = new Date(year, month - 1, day);
return result.getFullYear() === year &&
result.getMonth() === month - 1 &&
result.getDate() === day
? result
: undefined;
}
function calendarOptions(state: CalendarState) {
const startMonth = parseCalendarDate(state.startMonth);
const endMonth = parseCalendarDate(state.endMonth);
const validRange = !startMonth || !endMonth || startMonth <= endMonth;
return {
fullWidth: state.fullWidth,
numberOfMonths: state.numberOfMonths,
showOutsideDays: state.showOutsideDays,
fixedWeeks: state.fixedWeeks,
weekStartsOn: Number(state.weekStartsOn) as 0 | 1,
disabled: state.disabled === 'all'
? true
: state.disabled === 'weekends'
? { dayOfWeek: [0, 6] }
: undefined,
startMonth: validRange ? startMonth : undefined,
endMonth: validRange ? endMonth : undefined,
};
}
const calendarInitial = {
mode: 'single',
fullWidth: false,
numberOfMonths: 1,
showOutsideDays: true,
fixedWeeks: false,
weekStartsOn: '0',
required: false,
disabled: 'none',
startMonth: '',
endMonth: '',
};
type CalendarState = typeof calendarInitial;
function CalendarExample(options: Partial<CalendarState>) {
const state = { ...calendarInitial, ...options };
const [day, setDay] = useState<Date>();
const [days, setDays] = useState<Date[]>([]);
const [range, setRange] = useState<{
from: Date | undefined;
to?: Date;
}>();
const common = calendarOptions(state);
return (<div className="grid gap-3">
{state.mode === 'multiple' ? (<Calendar {...common} mode="multiple" required={state.required} selected={days} onSelect={(value: Date[] | undefined) => setDays(value ?? [])}/>) : state.mode === 'range' ? (<Calendar {...common} mode="range" required={state.required} selected={range} onSelect={setRange}/>) : (<Calendar {...common} mode="single" required={state.required} selected={day} onSelect={setDay}/>)}
</div>);
}
export default function ExamplePreview() {
return (<CalendarExample key={"single"} {...({"mode":"single","fullWidth":false,"numberOfMonths":1,"showOutsideDays":true,"fixedWeeks":false,"weekStartsOn":"0","required":false,"disabled":"none","startMonth":"","endMonth":""} as const)}/>)
}Props
@amineslab/ui에서 Calendar를 가져옵니다.
import { Calendar } from '@amineslab/ui'Calendar
react-day-picker v9의 DayPicker props를 전달합니다. locale은 기본 한국어이며 덮어쓸 수 있습니다.
| Name | Type | Default | Description |
|---|---|---|---|
mode | 'single' | 'multiple' | 'range' | - | 선택 방식. selected와 onSelect의 타입도 mode에 따라 달라집니다. |
selected / onSelect | DayPickerProps | - | 선택값과 변경 콜백입니다. 날짜는 로컬 Date 객체입니다. |
required | boolean | false | 선택한 날짜를 다시 클릭해 해제할 수 없게 합니다. |
fixedWeeks | boolean | false | 매달 6주를 표시해 달 이동 시 높이를 유지합니다. |
weekStartsOn | 0 | 1 | 2 | 3 | 4 | 5 | 6 | - | 주 시작 요일입니다. Playground에서는 일요일(0)과 월요일(1)을 비교합니다. |
fullWidth | boolean | false | 컨테이너를 7열로 균등하게 나눕니다. 모바일·인라인에 사용합니다. |
startMonth / endMonth | Date | - | 연·월 이동 범위입니다. 기본은 현재 연도 기준 과거 120년부터 미래 20년까지입니다. |
disabled | Matcher | Matcher[] | - | 선택 불가 일자 조건입니다. |
numberOfMonths | number | 1 | 한 번에 표시할 개월 수입니다. |
showOutsideDays | boolean | true | 이전·다음 달의 날짜를 함께 표시합니다. |
className / classNames / components | DayPickerProps | - | 달력 외곽·내부 슬롯·렌더 컴포넌트를 조정합니다. |
Examples
자주 쓰는 조합을 살펴보고, 필요한 예시의 코드를 펼쳐 확인하세요.
여러 날짜 선택
multiple은 Date[]를 사용합니다. 날짜를 클릭해 추가하거나 해제하며, required는 마지막 선택의 해제를 막습니다. 선택 결과는 달력의 선택 표시로 확인합니다.
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
import { useState } from 'react'
import { Calendar } from '@amineslab/ui'
function parseCalendarDate(value: string): Date | undefined {
if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
return undefined;
const [year = 0, month = 0, day = 0] = value.split('-').map(Number);
const result = new Date(year, month - 1, day);
return result.getFullYear() === year &&
result.getMonth() === month - 1 &&
result.getDate() === day
? result
: undefined;
}
function calendarOptions(state: CalendarState) {
const startMonth = parseCalendarDate(state.startMonth);
const endMonth = parseCalendarDate(state.endMonth);
const validRange = !startMonth || !endMonth || startMonth <= endMonth;
return {
fullWidth: state.fullWidth,
numberOfMonths: state.numberOfMonths,
showOutsideDays: state.showOutsideDays,
fixedWeeks: state.fixedWeeks,
weekStartsOn: Number(state.weekStartsOn) as 0 | 1,
disabled: state.disabled === 'all'
? true
: state.disabled === 'weekends'
? { dayOfWeek: [0, 6] }
: undefined,
startMonth: validRange ? startMonth : undefined,
endMonth: validRange ? endMonth : undefined,
};
}
const calendarInitial = {
mode: 'single',
fullWidth: false,
numberOfMonths: 1,
showOutsideDays: true,
fixedWeeks: false,
weekStartsOn: '0',
required: false,
disabled: 'none',
startMonth: '',
endMonth: '',
};
type CalendarState = typeof calendarInitial;
function CalendarExample(options: Partial<CalendarState>) {
const state = { ...calendarInitial, ...options };
const [day, setDay] = useState<Date>();
const [days, setDays] = useState<Date[]>([]);
const [range, setRange] = useState<{
from: Date | undefined;
to?: Date;
}>();
const common = calendarOptions(state);
return (<div className="grid gap-3">
{state.mode === 'multiple' ? (<Calendar {...common} mode="multiple" required={state.required} selected={days} onSelect={(value: Date[] | undefined) => setDays(value ?? [])}/>) : state.mode === 'range' ? (<Calendar {...common} mode="range" required={state.required} selected={range} onSelect={setRange}/>) : (<Calendar {...common} mode="single" required={state.required} selected={day} onSelect={setDay}/>)}
</div>);
}
export default function ExamplePreview() {
return (<CalendarExample mode="multiple"/>)
}주 단위로 이어지는 기간 표시
기간 배경은 같은 주 안에서 이어지고, 시작·종료일은 원형으로 표시합니다.
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
import { useState } from 'react'
import { Calendar } from '@amineslab/ui'
function parseCalendarDate(value: string): Date | undefined {
if (!/^\d{4}-\d{2}-\d{2}$/.test(value))
return undefined;
const [year = 0, month = 0, day = 0] = value.split('-').map(Number);
const result = new Date(year, month - 1, day);
return result.getFullYear() === year &&
result.getMonth() === month - 1 &&
result.getDate() === day
? result
: undefined;
}
function calendarOptions(state: CalendarState) {
const startMonth = parseCalendarDate(state.startMonth);
const endMonth = parseCalendarDate(state.endMonth);
const validRange = !startMonth || !endMonth || startMonth <= endMonth;
return {
fullWidth: state.fullWidth,
numberOfMonths: state.numberOfMonths,
showOutsideDays: state.showOutsideDays,
fixedWeeks: state.fixedWeeks,
weekStartsOn: Number(state.weekStartsOn) as 0 | 1,
disabled: state.disabled === 'all'
? true
: state.disabled === 'weekends'
? { dayOfWeek: [0, 6] }
: undefined,
startMonth: validRange ? startMonth : undefined,
endMonth: validRange ? endMonth : undefined,
};
}
const calendarInitial = {
mode: 'single',
fullWidth: false,
numberOfMonths: 1,
showOutsideDays: true,
fixedWeeks: false,
weekStartsOn: '0',
required: false,
disabled: 'none',
startMonth: '',
endMonth: '',
};
type CalendarState = typeof calendarInitial;
function CalendarExample(options: Partial<CalendarState>) {
const state = { ...calendarInitial, ...options };
const [day, setDay] = useState<Date>();
const [days, setDays] = useState<Date[]>([]);
const [range, setRange] = useState<{
from: Date | undefined;
to?: Date;
}>();
const common = calendarOptions(state);
return (<div className="grid gap-3">
{state.mode === 'multiple' ? (<Calendar {...common} mode="multiple" required={state.required} selected={days} onSelect={(value: Date[] | undefined) => setDays(value ?? [])}/>) : state.mode === 'range' ? (<Calendar {...common} mode="range" required={state.required} selected={range} onSelect={setRange}/>) : (<Calendar {...common} mode="single" required={state.required} selected={day} onSelect={setDay}/>)}
</div>);
}
export default function ExamplePreview() {
return (<CalendarExample mode="range" fullWidth/>)
}사용 기준
권장하는 사용
- 트리거와 반응형 표면까지 필요하면 DatePicker·DateRangePicker를 사용합니다.
- Calendar는 인라인 달력과 사용자 정의 조합에 사용합니다.
피해야 할 사용
- 겉모양만으로 사용을 결정하지 마세요.
- 의미와 키보드 동작, 모바일에서의 흐름을 함께 확인하세요.
접근성
화면에 맞는 이름을 제공하고, 키보드만으로 작업을 마칠 수 있는지 확인하세요.
방향키: 날짜 사이를 이동합니다.Enter / Space: 포커스한 날짜를 선택합니다.