Calendar
한 달을 페이지 안에 그리고, 지금 담고 있는 날들을 켜 둡니다. 네 개의 picker가 여는 것과 같은 그리드에서 팝업만 걷어낸 것으로, 날짜가 항상 보여야 하는 화면을 위한 것입니다.
import { Calendar } from 'neba';
<Calendar value={day} onValueChange={setDay} />;Props
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| size공통 | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | 칸의 높이와 타입 스케일. md에서 32px로 Button·TextField와 같은 사다리입니다 |
| color공통 | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | 의미론적 색 역할. 임의 색상값은 받지 않습니다 |
| elevation공통 | 0 | 1 | 2 | 3 | 0 | 그림자 깊이. 페이지 안에 앉은 달력은 떠 있지 않으므로 기본값은 0입니다 |
| mode | 'single' | 'multiple' | 'range' | 'single' | 값이 무엇인지를 정합니다. 하루, 날들의 배열, { start, end } 구간 |
| value | Date | null | Date[] | CalendarRange | — | mode에 따라 달라지는 값. onValueChange와 함께 제어 컴포넌트로 씁니다 |
| defaultValue | Date | null | Date[] | CalendarRange | — | 초기 값 |
| onValueChange | (value) => void | — | 선택이 바뀔 때 |
| month | Date | — | 화면의 달. onMonthChange와 함께 제어합니다 |
| defaultMonth | Date | — | 처음 열리는 달. 기본값은 값이 있는 달, 없으면 이번 달 |
| onMonthChange | (month: Date) => void | — | 화면의 달이 바뀔 때 |
| granularity | 'day' | 'month' | 'year' | 'day' | 클릭이 무엇을 고르는지, 하루, 한 달, 한 해. DatePicker와 같습니다 |
| renderDay | (date: Date) => ReactNode | — | 날짜 칸이 숫자 아래에 그리는 것, 점, 개수, 막대. 하루치 일정을 담을 자리가 아닙니다 |
| bordered | boolean | true | picker 팝업이 그리는 시트를 그립니다. 끄면 맨 그리드 |
| minDate | Date | null | — | 고를 수 있는 가장 이른 날. granularity 단위로 비교합니다 |
| maxDate | Date | null | — | minDate의 반대쪽 끝 |
| shouldDisableDate | (date: Date) => boolean | — | 범위 안이지만 고를 수 없는 칸, 주말, 공휴일, 이미 예약된 방. 셀은 그리드에 남은 채 비활성이 되고, 인자로는 그 칸이 만들 값이 들어옵니다 |
| weekStartsOn공통 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | — | 주가 시작하는 요일. 일요일이 0이며, 기본값은 로케일을 따릅니다 |
| locale | string | — | 월·요일 이름과 헤더의 연/월 순서를 정하는 BCP 47 태그 |
| showOutsideDays | boolean | true | 옆 달에 속한 앞뒤 날들을 그립니다 |
| labels | Partial<PickerLabels> | — | 스크린 리더가 읽는 문자열. 기본값은 locale의 표현입니다 |
<div>의 native 속성은 root로 전달됩니다. 공통 축은 prop 규약에 설명돼 있습니다.
이것이 아닌 것
스케줄러가 아닙니다. 칸의 크기는 컨트롤 사다리의 높이이고(md에서 32px), 그래서 renderDay는 숫자 아래에 점 하나, 개수 하나, 막대 하나를 놓을 자리이지 하루치 일정을 담을 자리가 아닙니다. 그것을 그리는 컴포넌트는 다른 그리드가 필요하고, 이것을 그렇게 부르는 것은 크기가 지킬 수 없는 약속입니다.
고르기 · 거르기 · 표시하기에 쓰세요. 날짜가 필드 뒤에 있어야 한다면 DatePicker입니다.
예시
mode
mode가 값이 무엇인지를 정합니다.
mode | value |
|---|---|
single(기본값) | Date | null |
multiple | Date[] |
range | { start: Date | null, end: Date | null } |
multiple에서는 이미 담긴 날을 다시 누르면 빠집니다. 포인터로 되돌릴 수 있는 유일한 방법입니다.
range에서는 첫 클릭이 시작을, 두 번째가 끝을 정합니다. 시작보다 앞을 누르면 기존 구간을 뒤집는 대신 새 구간을 시작합니다. 뒤집는 동작이야말로 독자가 잘못 눌렀다고 믿게 만드는 것이기 때문입니다. 구간이 완성된 뒤의 클릭은 또 새 구간을 시작합니다.
renderDay
반환한 것이 날짜 칸 안, 숫자 아래에 그려집니다. 칸이 position: relative이므로 absolute로 배치한 마크가 원하는 자리에 놓입니다.
events prop이 아니라 hook인 이유는, 하루에 무엇이 있는지를 아는 것은 호출하는 쪽뿐이고, 여기서 데이터 모양을 받는 순간 그 모양에 대한 의견을 갖게 되기 때문입니다.
granularity
DatePicker가 제공하는 것과 같은 세 단위입니다. month나 year에서는 그 뷰에서 열리고 거기서의 클릭이 답이며, 값은 고른 단위의 첫날이 됩니다.
minDate · maxDate · shouldDisableDate
DatePicker와 똑같이 granularity 단위로 읽습니다. 막힌 칸은 그리드에 남아 disabled 속성 대신 aria-disabled로 표시되므로 방향키 경로에서 빠지지 않습니다.
bordered와 elevation
bordered는 picker 팝업이 그리는 시트를 그립니다. 이미 테두리가 있는 Card 안에 넣을 때는 끄고 맨 그리드만 쓰세요. elevation의 기본값은 0입니다. 페이지 안에 앉은 달력은 떠 있지 않습니다.
키보드
| 키 | 동작 |
|---|---|
← → ↑ ↓ | 하루 또는 한 주 이동, 가장자리에서 달 넘김 |
Home / End | 주의 처음 또는 끝 |
PageUp / PageDown | 한 달씩: Shift와 함께면 1년씩 |
그리드 전체가 tab 정지 하나이므로 Tab은 42개 칸을 지나지 않고 그리드를 빠져나갑니다.
접근성
- 그리드는
role="grid", 각 칸은role="gridcell"버튼입니다. 칸의 이름은 숫자가 아니라 완전한 날짜입니다. - 담긴 날은
aria-selected, 오늘은aria-current="date"와 숫자 아래의 점으로 표시됩니다. renderDay가 그리는 것은aria-hidden을 붙이지 않는 한 칸의 accessible name에 포함됩니다. 라벨이 이미 말한 것을 되풀이하는 점은 숨기고, 무언가를 더하는 개수는 숨기지 마세요.