Component

Calendar

@mycrm-ui/components의 Calendar와 DatePicker입니다. 단일 날짜, 기간, 여러 날짜 선택과 marker, disabled date, pending 상태, DatePicker popover를 제공합니다.

Calendar는 달력 본문에 집중한 headless 컴포넌트입니다. 월 이동 버튼, API 호출, 폼 연결이 필요하면 DatePicker를 사용하거나 외부 상태로 Calendar를 조립합니다.

calendar_month

Calendar 기본 사용

LLM GuideViewerMD

yearmonth로 표시 월을 정하고,selectedDateonDateSelect로 단일 날짜 선택을 제어합니다.markedDates는 날짜 하단 dot과 선택 시 반환되는 marker 메타데이터를 제공합니다.

31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11

선택된 날짜

{
  "selectedDate": "2026-09-10",
  "selectedMarkers": []
}
date_range

Range 선택

LLM GuideViewerMD

selectionMode="range"를 사용하면 첫 번째 클릭으로 시작일을 만들고, 두 번째 클릭으로 시작일/종료일을 정규화해 반환합니다.

30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

범위 선택 상태

{
  "draft": {
    "startDate": "2026-09-10",
    "endDate": null
  },
  "selectedRange": null
}
event_available

Multiple 선택

LLM GuideViewerMD

selectionMode="multiple"은 날짜를 토글 방식으로 선택합니다.maxSelectedDates로 선택 가능한 최대 개수를 제한할 수 있습니다.

30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

다중 선택 날짜

{
  "selectedDates": [
    "2026-09-10",
    "2026-09-20"
  ]
}
progress_activity

Pending / API 연동

LLM GuideViewerMD

월 변경 시 API를 호출하는 구조에서는 외부에서 pending을 켜고, 응답으로 받은 날짜를 isDateDisabled 또는 markedDates에 반영합니다. pending 중에는 dimmed overlay와 원형 spinner가 표시됩니다.

2026.09
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10

API 연동 예시

{
  "month": {
    "year": 2026,
    "month": 9
  },
  "pending": true,
  "disabledDates": []
}
edit_calendar

DatePicker

LLM GuideViewerMD

DatePicker는 input-like trigger, Calendar popover, clear 버튼, hidden input, 연도/월 선택 popover를 포함합니다. range 또는 multi shorthand로 같은 컴포넌트에서 기간/다중 선택 모드를 사용할 수 있습니다.

Single

Range

Multiple

DatePicker 선택 상태

{
  "single": "2026-09-10",
  "range": {
    "startDate": "2026-09-10",
    "endDate": "2026-09-20"
  },
  "multiple": {
    "selectedDates": [
      "2026-09-10",
      "2026-09-20"
    ]
  }
}
tune

DatePicker 고급 옵션

LLM GuideViewerMD

onMonthChange로 API 호출을 연결하고, pending,markedDates, isDateDisabled, getDayClassName을 조합해 업무 규칙을 주입합니다. theme은 내장 popover의 light/dark 테마를 바꿉니다.

Advanced

고급 옵션 선택 상태

{
  "value": "2026-09-01",
  "events": [],
  "month": {
    "year": 2026,
    "month": 9
  },
  "pending": false,
  "theme": "light"
}
data_object

Calendar API

PropType설명
year / monthnumber렌더링할 연도와 월입니다. 둘 다 필수이며 month는 1~12 값입니다.
selectionMode'single' | 'range' | 'multiple'선택 모드를 전환합니다. 기본값은 single입니다.
selectedDateDate | string | nullsingle 모드의 controlled 선택값입니다. 생략하면 Calendar 내부 상태로 선택값을 관리합니다.
selectedDates(Date | string)[]multiple 모드의 controlled 선택 목록입니다. 생략하면 내부 상태로 선택 목록을 관리합니다.
rangeStart / rangeEndDate | string | nullrange 모드의 controlled 시작일과 종료일입니다. 생략하면 내부 상태로 범위를 관리합니다.
onDateSelect(date, markers) => voidsingle/multiple 모드에서 날짜 선택 시 호출됩니다. date는 dateSelectValueType 포맷이며 해당 날짜 marker 목록을 함께 받습니다.
onRangeDraftChange(range) => voidrange 모드에서 시작일 선택 또는 범위 확정 전/후 상태가 바뀔 때 호출됩니다.
onRangeSelect(range) => voidrange 모드에서 시작일과 종료일이 모두 확정되면 호출됩니다. 시작/종료 순서는 자동 정규화됩니다.
dateSelectValueType'date' | 'yyyyMMdd' | 'yyyy-MM-dd' | 'yyyy.MM.dd'선택 콜백으로 반환할 날짜 타입/문자열 포맷입니다. Calendar 기본값은 date입니다.
selectableStartDate / selectableEndDateDate | string | null선택 가능한 날짜 범위를 제한합니다. 현재 월이 아닌 날짜는 선택 대상에서 제외됩니다.
isDateDisabled(date: Date) => boolean특정 날짜를 비활성화합니다. 현재 range 모드에서는 이 함수보다 선택 가능 기간 제한을 중심으로 동작합니다.
markedDates(Date | string | { date, color?, meta? })[]날짜 하단 dot marker를 렌더링합니다. 같은 날짜에 여러 marker를 넣을 수 있고 선택 콜백에서 markers로 반환됩니다.
maxSelectedDatesnumbermultiple 모드에서 선택 가능한 최대 개수입니다. 0 이하나 정수가 아니면 제한하지 않습니다.
pendingboolean달력 위에 dimmed overlay와 원형 spinner를 표시하고 날짜 상호작용을 막습니다. 기본값은 false입니다.
weekStartsOn0 | 10은 일요일 시작, 1은 월요일 시작입니다. 기본값은 0입니다.
weekdayLabelType'en' | 'ko'기본 요일 라벨 언어입니다. 기본값은 en입니다.
weekdayLabels / weekdayLabelsJsonreadonly string[] / { sun, mon, ... }요일 라벨을 직접 주입합니다. weekdayLabels가 있으면 배열 순서가 우선 적용됩니다.
showAdjacentMonthDaysboolean이전/다음 달 날짜 표시 여부입니다. 기본값은 true이며 adjacent 날짜는 선택할 수 없습니다.
showTodayboolean오늘 날짜 강조 표시 여부입니다. 기본값은 true입니다.
showHoverbooleanhover 스타일과 range preview hover 추적 여부입니다. 기본값은 true입니다.
hoveredDate / onHoverDateChangeDate | string | null / (date: Date | null) => voidhover 날짜를 외부에서 제어하거나 추적합니다.
getDayClassName(date, cell) => string | undefinedCalendarCell 상태를 보고 특정 날짜 cell에 className을 추가합니다.
className / classNamesstring / CalendarClassNamesroot className과 grid, weekday, day, selectedDay, rangeInsideDay 등 내부 슬롯 className입니다.
input

DatePicker API

PropType설명
selectionMode / range / multi'single' | 'range' | 'multiple' / boolean선택 모드입니다. range 또는 multi boolean shorthand를 사용할 수 있습니다. range가 multi보다 우선합니다.
value / defaultValueDate | string | nullsingle 모드의 controlled 값과 uncontrolled 초기값입니다.
onChange(date, markers) => voidsingle/multiple 모드에서 선택 날짜와 marker 목록을 받습니다. clear 시 date는 null입니다.
rangeStart / rangeEndDate | string | nullrange 모드의 controlled 시작일/종료일입니다.
defaultRangeStart / defaultRangeEndDate | string | nullrange 모드의 uncontrolled 초기 시작일/종료일입니다.
onRangeDraftChange / onRangeChange(range) => void기간 선택 중간 상태와 확정 상태를 받습니다. clear 시 둘 다 null로 호출됩니다.
selectedDates / defaultSelectedDates(Date | string)[]multiple 모드의 controlled 선택 목록과 uncontrolled 초기값입니다.
maxSelectedDatesnumbermultiple 모드에서 선택 가능한 최대 개수입니다.
onMultipleChange({ selectedDates }) => voidmultiple DatePicker의 전체 선택 목록을 받습니다. clear 시 빈 배열로 호출됩니다.
namestringform submit용 hidden input을 렌더링합니다. range는 nameStart/nameEnd, multiple은 쉼표로 연결한 값을 생성합니다.
placeholderstring선택값이 없을 때 trigger에 표시할 문구입니다. 기본값은 Select date입니다.
clearable / clearLabelboolean / string지우기 버튼 표시 여부와 라벨입니다. 기본값은 true / Clear입니다.
disabled / readOnly / requiredbooleantrigger, hidden input, 상호작용 제어에 사용합니다.
closeOnSelectboolean선택 후 popover를 닫을지 제어합니다. 기본값은 single/range true, multiple false입니다.
theme'light' | 'dark'DatePicker 내장 trigger, popover, 연도/월 선택 popover의 기본 테마입니다. 기본값은 light입니다.
dateSelectValueType'date' | 'yyyyMMdd' | 'yyyy-MM-dd' | 'yyyy.MM.dd'선택 콜백과 내부 표시값에 사용할 날짜 포맷입니다. DatePicker 기본값은 yyyy-MM-dd입니다.
className / classNamesstring / DatePickerClassNamesroot className과 field, trigger, clearButton, popover, monthPickerOption 등 DatePicker 슬롯 className입니다.
calendarClassNamesCalendarClassNames내장 Calendar 슬롯 스타일입니다.
popoverLabelstringCalendar popover dialog의 aria-label입니다. 기본값은 Choose date입니다.
previousMonthLabel / nextMonthLabelstringpopover 내부 이전/다음 달 버튼 문구입니다.
yearSelectLabel / monthSelectLabelstring연도/월 선택 popover의 섹션 라벨입니다. 없으면 weekdayLabelType에 따라 Year/Month 또는 연도/월을 사용합니다.
onMonthChange({ year, month }) => voidpopover 내부 월 변경 또는 연도/월 직접 선택 시 호출됩니다. API로 비활성 날짜나 marker를 다시 가져올 때 사용합니다.
pendingboolean내장 Calendar에 pending overlay/spinner를 표시합니다. 기본값은 false입니다.
weekStartsOn0 | 1내장 Calendar의 주 시작 요일입니다. 기본값은 0입니다.
weekdayLabelType'en' | 'ko'내장 Calendar의 기본 요일 라벨 언어입니다. 기본값은 en입니다.
weekdayLabels / weekdayLabelsJsonreadonly string[] / { sun, mon, ... }내장 Calendar의 요일 라벨을 직접 주입합니다.
showAdjacentMonthDays / showToday / showHoverboolean내장 Calendar의 adjacent 날짜, 오늘 강조, hover/range preview 표시 여부입니다.
selectableStartDate / selectableEndDateDate | string | null내장 Calendar의 선택 가능 날짜 범위를 제한합니다.
markedDates(Date | string | { date, color?, meta? })[]내장 Calendar에 marker dot을 표시하고 선택 콜백에서 markers로 반환합니다.
isDateDisabled(date: Date) => boolean내장 Calendar에서 특정 날짜를 비활성화합니다. range 모드에서는 선택 가능 기간 제한을 중심으로 동작합니다.
getDayClassName(date, cell) => string | undefined내장 Calendar의 특정 날짜 cell에 className을 추가합니다.
verified

추가 지원 기능

LLM GuideViewerMD

아래 항목은 기본 데모 화면만으로는 눈에 잘 드러나지 않지만, 현재mycrm-ui 소스에서 지원하는 입력 포맷, 접근성, 키보드, 테스트 계약, DatePicker 역할입니다.

날짜 입력 포맷

Calendar와 DatePicker는 Date 객체와 yyyyMMdd, yyyy-MM-dd, yyyy.MM.dd 문자열 입력을 파싱합니다. 선택 콜백 반환값은 dateSelectValueType으로 date, yyyyMMdd, yyyy-MM-dd, yyyy.MM.dd 중 선택할 수 있습니다.

<Calendar
  selectedDate="20260910"
  dateSelectValueType="yyyy.MM.dd"
  onDateSelect={(date) => console.log(date)}
/>

Controlled hover

hoveredDate와 onHoverDateChange를 함께 사용하면 hover 상태를 외부 상태로 제어할 수 있습니다. range 모드에서는 시작일 선택 후 hover 날짜를 기준으로 preview 범위가 표시됩니다.

<Calendar
  year={2026}
  month={9}
  selectionMode="range"
  hoveredDate={hoveredDate}
  onHoverDateChange={setHoveredDate}
/>

접근성 / 키보드

Calendar는 role="grid" 구조와 aria-selected, aria-disabled, aria-busy 속성을 사용합니다. 키보드는 방향키 이동, Home/End 행 이동, Enter/Space 선택을 지원합니다.

// 날짜 cell 키보드 동작
Arrow keys: focus 이동
Home / End: 행 시작/끝 이동
Enter / Space: 날짜 선택

Focus 격리 / 테스트

각 Calendar 인스턴스는 자체 focus 상태를 갖습니다. 날짜 cell에는 data-mycrm-ui-calendar-date가 제공되어 테스트에서 특정 날짜를 안정적으로 찾을 수 있습니다.

screen.getByRole('grid', { name: '2026-09' })
container.querySelector('[data-mycrm-ui-calendar-date="2026-09-10"]')

DatePicker의 현재 역할

DatePicker는 더 이상 Calendar를 그대로 반환하는 pass-through wrapper가 아닙니다. trigger, popover, clear 버튼, hidden input, 연도/월 선택 UI를 포함한 상위 컴포넌트입니다.

<DatePicker
  name="eventDate"
  clearable
  previousMonthLabel="이전 달"
  nextMonthLabel="다음 달"
/>

Pending UI

현재 pending UI는 skeleton이 아니라 dimmed overlay와 원형 spinner입니다. pending=true 동안 날짜 선택과 focus 대상 상호작용이 차단됩니다.

<Calendar
  year={2026}
  month={9}
  pending={isLoading}
/>
palette

CSS 커스터마이징

LLM GuideViewerMD

컴포넌트는 기본 inline style을 제공하지만, classNamescalendarClassNames로 프로젝트 CSS를 우선 적용할 수 있습니다. 외부 className이 있으면 주요 기본 스타일은 생략되도록 설계되어 있습니다.

Calendar

className은 root에, classNames는 달력 내부 슬롯에 적용합니다.

DatePicker

classNames는 trigger, clear 버튼, popover, 연도/월 선택 UI를 제어합니다.

내장 Calendar

DatePicker 안의 달력은 calendarClassNames로 별도 스타일링합니다.

<Calendar
  className="w-80 rounded-xl border p-4"
  classNames={{
    weekdaySat: 'text-blue-600',
    weekdaySun: 'text-red-600',
    selectedDay: 'bg-blue-600 text-white',
    rangeInsideDay: 'bg-blue-100 text-blue-900',
    adjacentMonthDay: 'text-slate-400 opacity-60',
    disabledDay: 'text-slate-400 opacity-45',
    dayMarker: 'ring-1 ring-white',
  }}
/>

<DatePicker
  classNames={{
    root: 'w-64',
    trigger: 'w-full rounded-lg border px-3',
    triggerValue: 'truncate whitespace-nowrap',
    clearButton: 'rounded-lg border px-2.5',
    popover: 'rounded-xl bg-white shadow-xl',
    monthPickerOptionActive: 'bg-blue-600 text-white',
  }}
  calendarClassNames={{
    selectedDay: 'bg-blue-600 text-white',
    rangeInsideDay: 'bg-blue-100',
    disabledDay: 'opacity-40 text-slate-400',
  }}
/>

적용 기준

  • className은 최외곽 root에 추가되는 단일 class입니다.
  • classNames는 컴포넌트가 노출하는 슬롯별 class입니다. 슬롯을 제공하면 해당 슬롯의 주요 기본 inline style이 생략되는 경우가 있어 프로젝트 CSS가 우선됩니다.
  • DatePicker.classNamesDatePicker.calendarClassNames는 적용 대상이 다릅니다. trigger/popup shell은 classNames, 달력 날짜 셀은 calendarClassNames를 사용합니다.

CalendarClassNames 슬롯

슬롯적용 요소관련 기능CSS 예시Tailwind 예시
기본 레이아웃
rootCalendar 최외곽 div달력 전체 크기, 테두리, 배경width: 320px; padding: 16px; border: 1px solid #e5e7ebw-80 rounded-xl border p-4
grid요일/날짜 gridgrid 간격, 폰트 크기gap: 8px; font-size: 14pxgap-2 text-sm
weekday요일 헤더 공통요일 라벨 높이, 정렬, 글꼴height: 30px; font-weight: 600h-8 font-semibold
weekdaySun / weekdaySat일/토 요일 헤더주말 색상color: #dc2626 / #2563ebtext-red-600 / text-blue-600
weekdayMon~weekdayFri월~금 요일 헤더특정 요일 색상color: #374151text-slate-700
날짜 셀
day날짜 cell wrapper셀 높이, 정렬, 상태별 wrapper 스타일min-height: 40px; display: grid; place-items: centermin-h-10 place-items-center
dayContent날짜 숫자 주변 content선택/범위 배경이 적용되는 영역width: 36px; height: 36px; border-radius: 9999pxh-9 w-9 rounded-full
dayHoverhover 가능한 날짜 내부hover 시 배경/색상background: #eff6ff; color: #2563ebbg-blue-50 text-blue-600
daySun / daySat일/토 날짜주말 날짜 색상color: #dc2626 / #2563ebtext-red-600 / text-blue-600
dayMon~dayFri월~금 날짜특정 요일 날짜 색상color: #111827text-slate-900
currentMonthDay현재 월 날짜 cell현재 월 날짜 강조color: #111827text-slate-900
adjacentMonthDay이전/다음 월 날짜 cell인접 월 날짜 흐림 처리color: #9ca3af; opacity: .6text-slate-400 opacity-60
disabledDay비활성 날짜 내부비활성 날짜 흐림 처리color: #9ca3af; opacity: .45text-slate-400 opacity-45
todayDay오늘 날짜 내부오늘 강조font-weight: 800; text-decoration: underlinefont-extrabold underline underline-offset-4
선택 / 범위
selectedDaysingle/multiple 선택 날짜 content선택 날짜 배경background: #2563eb; color: #fffbg-blue-600 text-white
rangeStartDayrange 시작 날짜 content기간 시작일background: #2563eb; color: #fffbg-blue-600 text-white
rangeEndDayrange 종료 날짜 content기간 종료일background: #2563eb; color: #fffbg-blue-600 text-white
rangeInsideDayrange 내부 날짜 content기간 내부 배경background: #dbeafe; color: #1e3a8abg-blue-100 text-blue-900
rangePreviewDayrange hover preview content종료일 선택 전 미리보기background: #eff6ff; color: #2563ebbg-blue-50 text-blue-600
rangeSingleDay시작/종료가 같은 range content하루짜리 기간background: #2563eb; color: #fffbg-blue-600 text-white
마커 / 상태
dayMarker날짜 하단 dotmarkedDates dot 스타일width: 6px; height: 6px; border-radius: 9999pxh-1.5 w-1.5 rounded-full

DatePickerClassNames 슬롯

슬롯적용 요소관련 기능CSS 예시Tailwind 예시
필드 / 트리거
rootDatePicker 최외곽 div전체 폭, 배치, form 주변 간격width: 280px; position: relativew-70 relative
fieldtrigger와 clear 버튼 wrapper버튼 간격, 정렬display: inline-flex; gap: 8pxinline-flex gap-2
triggerinput-like trigger button입력 박스 형태, 테두리, 배경height: 40px; border: 1px solid #d1d5dbh-10 rounded-lg border px-3
triggerValue선택값 텍스트긴 날짜/기간 말줄임overflow: hidden; text-overflow: ellipsis; white-space: nowraptruncate whitespace-nowrap
placeholderplaceholder 텍스트미선택 상태 색상color: #6b7280text-slate-500
icon오른쪽 calendar icon아이콘 크기/색상color: #6b7280; flex-shrink: 0shrink-0 text-slate-500
clearButton지우기 버튼clearable 버튼border: 1px solid #d1d5db; padding: 0 10pxrounded-lg border px-2.5
Calendar Popover
popoverCalendar popover dialog달력 팝오버 배경, z-index, shadowz-index: 50; border-radius: 12px; box-shadow: 0 18px 45px rgba(0,0,0,.18)z-50 rounded-xl shadow-xl
popoverHeaderpopover 상단 헤더이전/다음 버튼과 월 선택 정렬display: flex; justify-content: space-betweenflex items-center justify-between
popoverTitle현재 연도/월 텍스트월 선택 버튼 텍스트font-weight: 700font-bold
monthButton이전/다음 달 버튼월 이동 버튼height: 32px; border: 1px solid #d1d5dbh-8 rounded-lg border px-2.5
연도/월 선택 Popover
monthPickerWrap연도/월 선택 wrapper내부 popover 기준점position: relativerelative
monthPickerToggle연도/월 선택 버튼연도/월 리스트 열기 버튼min-width: 96px; border-radius: 8pxmin-w-24 rounded-lg
monthPickerPopover연도/월 리스트 popover내부 popover 패널width: 280px; max-width: calc(100vw - 64px)w-70 max-w-[calc(100vw-64px)]
monthPickerSection연도 또는 월 section옵션 griddisplay: grid; grid-template-columns: repeat(4, 1fr)grid grid-cols-4 gap-1.5
monthPickerSectionTitlesection 제목연도/월 라벨font-size: 12px; font-weight: 700text-xs font-bold
monthPickerOption연도/월 option button각 연도/월 버튼height: 32px; border-radius: 8pxh-8 rounded-lg
monthPickerOptionActive선택된 연도/월 optionactive optionbackground: #4f46e5; color: #fffbg-indigo-600 text-white