ComponentsP3 본문

DateRangePicker

업무 기간 조회용 범위 선택기 — 임의선택(프리셋)/달력선택(듀얼 먼스) 2탭 팝업. 비제어 defaultValue + onChange(즉시)/onConfirm(선택완료 확정). 외부 클릭·Escape는 이전 확정값을 복원합니다 (DK WizDatePickerEx 사양 승격).

마지막 업데이트 2026-06-11

한눈에#

업무 조회용 기간(시작~끝) 선택기입니다 — 임의선택(프리셋)/달력선택(듀얼 먼스) 2탭 팝업으로, 연·월·분기·반기 프리셋과 듀얼 먼스 달력을 함께 제공합니다.

데스크톱 모드 — 임의선택(프리셋) / 달력선택(듀얼 먼스) 후 선택완료로 확정

아직 확정 없음

  • 임의선택·달력선택 2탭
  • 프리셋 + 듀얼 먼스
  • onChange 즉시 / onConfirm 확정
  • 비제어 + BottomSheet 모바일

임시 변경은 onChange로 즉시, 선택완료로 확정될 때 onConfirm — 외부 클릭·Escape는 직전 확정값 복원

트리거는 Input 어휘이고, 팝업 상단의 "임의선택"/"달력선택" 2탭으로 입력 방식을 전환합니다. 모든 임시 변경은 onChange로 즉시, "선택완료"로 확정될 때 onConfirm이 호출되며 트리거 표시값도 그 시점에 갱신됩니다.

사용 시점#

매출·로그 조회처럼 업무 단위 기간을 프리셋+달력으로 고르면 DateRangePicker — 단일 날짜면 DatePicker입니다.

쓴다

매출·로그 조회처럼 연·월·분기·반기 프리셋과 듀얼 먼스로 시작~끝 기간을

대신 DatePicker

하나의 날짜만(범위가 가끔 필요하면 mode=range로도 충분)

플레이그라운드#

컨트롤로 props를 조작하면 미리보기와 코드가 실시간 갱신됩니다.

import { DateRangePicker } from '@wds/ui-web';

<DateRangePicker />

변형#

입력 방식 2탭이 변형 축입니다.

임의선택 (프리셋) — 연도 · 빠른선택(오늘·이번주·이번달) · 월(6열 그리드) · 분기 · 반기 버튼 그리드입니다. 연도·월·분기는 결합 가능 유형이라 같은 유형의 두 항목을 차례로 누르면 범위가 결합됩니다(2클릭 범위 결합) — 예: 3월 → 6월을 누르면 "2026년 3월~6월". 늦은 쪽을 먼저 눌러도 정방향으로 스왑됩니다. 같은 항목을 다시 누르면 그 항목 단일 범위로 돌아가고, 빠른선택·반기는 단일 클릭으로 즉시 확정되는 단일 범위입니다. 연도 버튼을 누르면 월/분기/반기 프리셋이 그 연도 기준으로 바뀝니다. showQuickSelect/showQuarter/showHalf로 각 절을 끄고, years로 연도 버튼 목록(기본 최근 3년)을 바꿉니다.

달력선택 (듀얼 먼스) — 시작일·종료일 두 개의 Calendar를 나란히 띄웁니다. 각 달력 상단에는 시작일/종료일 제목과 현재 선택값(미선택 시 "선택 안 됨")이 표시되어 어느 달력이 무엇을 고르는지, 지금 무엇이 잡혀 있는지 한눈에 보입니다 (startLabel/endLabel로 변경 가능). 두 달력은 이미 접근 이름을 갖고 있어 스크린리더에는 동일한 구분이 전달되며, 시각 헤더는 그 구분을 눈으로도 보이게 합니다. 시작일을 종료일 이후로 바꾸면 종료일이 비워지고 종료 달력이 재마운트됩니다. 종료 달력의 최소 선택일은 선택된 시작일로 제한됩니다. 달력으로 고르면 프리셋 선택 표시(aria-pressed)는 해제됩니다.

푸터의 초기화는 임시 선택을 빈 범위로 되돌리고(프리셋 표시·달력 모두 리셋), 선택완료는 현재 임시 선택을 확정합니다.

크기#

단일 크기입니다 — 트리거 높이는 input.height(44px), 최소 폭 16rem으로 Input과 같은 폼 컨트롤 줄맞춤입니다. 팝업은 내용 폭(max-content)이되 뷰포트를 넘지 않도록 max-width: calc(100vw - space.8)로 제한되고, 좁으면 듀얼 먼스가 세로로 스택됩니다(bp.mobile 767px 기준).

상태#

비제어 컴포넌트입니다 — defaultValue로 시작하고, 임시 변경(draft)은 onChange로, 확정은 onConfirm으로 통지합니다. 열림/닫힘도 내부 소유: 트리거 클릭으로 열고, 선택완료·Escape·외부 클릭으로 닫힙니다. 단, Escape와 외부 클릭은 취소라 직전 확정값을 복원합니다(선택완료만 확정). 트리거의 Hover/Focus/Disabled는 CSS 의사클래스로 표현되고, 열림 동안 aria-expanded="true"가 유지됩니다.

모바일#

DK 모바일 재활용 표준입니다 — mobile을 켜면 팝업 대신 BottomSheet 안에 2탭 + 패널이 렌더되고, footer의 초기화/선택완료 버튼으로 마무리합니다. 시트의 닫힘 경로(배경 클릭·Escape·핸들 드래그 다운)는 취소로 동작해 직전 확정값을 복원합니다. 시트 제목은 mobileTitle(기본 placeholder)입니다. 라이브 데모는 데스크톱 뷰포트라 코드로 안내합니다.

<DateRangePicker
  mobile
  mobileTitle="조회 기간"
  years={[2024, 2025, 2026]}
  onConfirm={(value) => applyPeriod(value)}
/>

Props#

Prop타입기본값설명
defaultValue{ start: Date | null; end: Date | null }초기 범위 (비제어) — 이후 상태는 내부 소유
onChange(value: DateRangeValue) => void모든 임시 변경(프리셋 클릭·달력 클릭·초기화)에 즉시 호출
onConfirm(value: DateRangeValue) => void"선택완료"로 확정될 때 호출 — 트리거 표시값도 이 시점에 갱신
yearsReadonlyArray<number>최근 3년프리셋 연도 버튼 목록
showQuickSelectbooleantrue빠른선택(오늘·이번주·이번달) 절 노출
showQuarterbooleantrue분기 절 노출
showHalfbooleantrue반기 절 노출
minDate달력 최소 선택일 — Calendar로 전달
maxDate달력 최대 선택일 — Calendar로 전달
disabled(date: Date) => boolean비활성 날짜 판별 — Calendar로 전달
placeholderstring'기간 선택'값이 없을 때 트리거 텍스트 + 팝업 dialog의 aria-label
confirmTextstring'선택완료'확정 버튼 라벨
resetTextstring'초기화'초기화 버튼 라벨
presetTabTextstring'임의선택'프리셋 탭 라벨
calendarTabTextstring'달력선택'달력 탭 라벨
startLabelstring'시작일'왼쪽(시작) 달력 상단 제목 — 시각 헤더이자 그 달력의 접근 이름
endLabelstring'종료일'오른쪽(종료) 달력 상단 제목 — 시각 헤더이자 그 달력의 접근 이름
showCalendarHeaderbooleantrue달력 상단 제목+선택값 헤더 표시 (false면 달력만)
mobilebooleanfalsetrue면 BottomSheet 표면으로 전환 (DK 모바일 표준)
mobileTitlestring시트 제목 — 기본 placeholder
…restOmit<ButtonHTMLAttributes, 'onChange' | 'value' | 'defaultValue' | 'disabled'>className 등은 트리거 button으로 전달

DateRangeValue(onChange/onConfirm 인자):

Prop타입기본값설명
startDate | null시작일 (없으면 null)
endDate | null종료일 (없으면 null)
presetTypeDateRangePresetType프리셋으로 만든 범위면 유형 — 'today'|'thisWeek'|'thisMonth'|'month'|'quarter'|'half'|'year'
presetLabelstring프리셋 라벨 — 트리거 표시는 이 값을 우선 (예: "2026년 3월~6월")

접근성#

트리거가 aria-haspopup="dialog" + aria-expanded, 팝업이 role="dialog"(이름은 placeholder)입니다. 2탭은 aria-pressed 토글 버튼이고, 프리셋 절은 각각 role="group" + aria-label로 묶입니다.

동작
트리거 Enter / Space팝업 열기 (네이티브 button) — 다시 누르면 취소 닫기
Escape (팝업 내부 어디서든)취소 — 직전 확정값 복원 + 트리거 포커스 복원
Tab탭·프리셋 버튼·달력·푸터 버튼 순회
달력 키보드화살표(일)·Home/End(주 시작·끝)·PageUp/Down(월) — Calendar APG grid
  • 외부 클릭도 취소(직전 확정값 복원, 포커스는 클릭 대상에 양보)
  • 프리셋 선택 표시는 aria-pressed, 결합 선택이면 두 항목 모두 눌림 표시
  • 달력 월 라벨은 aria-live="polite"로 갱신 안내 (Calendar 합성)
  • 팝업 등장 모션은 opacity/transform만 + prefers-reduced-motion 존중

토큰#

component 토큰 없이 semantic(+input 어휘)을 직접 소비합니다(신설 기준 §4 미충족).

속성토큰
트리거input.height/bg/border/radius/fg · placeholder input.placeholder
트리거 포커스/열림color.focus-ring(아웃라인) · input.border-focus
팝업color.surface-raised · color.border · radius.lg · shadow.lg · z.dropdown
2탭color.surface-muted(트랙) · 활성 color.surface-raised + color.primary-text + shadow.sm
프리셋 버튼color.surface · color.border · 높이 space.8 · hover color.surface-hover → 눌림 color.surface-pressed — 선택 color.primary + color.on-primary
듀얼 먼스Calendar 토큰(color.primary·color.on-primary·color.primary-subtle)
푸터초기화 color.surface+border-strong+primary-text(눌림 color.surface-pressed) · 선택완료 button.bg/fg
모션duration.fast + 팝업 등장 ease.emphasized-decelerate / ease.standard

관련#