@idbrnd/design-system 4.2.2 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @idbrnd/design-system
2
2
 
3
- ![Version](https://img.shields.io/static/v1?label=version&message=4.2.2&color=blue&style=flat-square) <!-- x-release-please-version -->
3
+ ![Version](https://img.shields.io/static/v1?label=version&message=4.3.0&color=blue&style=flat-square) <!-- x-release-please-version -->
4
4
  ![React](https://img.shields.io/badge/React-18.3.1-61DAFB?style=flat-square&logo=react&logoColor=white)
5
5
  ![Vite](https://img.shields.io/badge/Vite-7.3.0-646CFF?style=flat-square&logo=vite&logoColor=white)
6
6
  ![TypeScript](https://img.shields.io/badge/TypeScript-5.9.3-3178C6?style=flat-square&logo=typescript&logoColor=white)
@@ -1179,6 +1179,26 @@ function RangeExample() {
1179
1179
  <DatePicker variant="period" hasTime timeVariant="hh-mm" value={range} onChange={setRange} />
1180
1180
  ```
1181
1181
 
1182
+ **범위 유효성 검증 (minDurationMinutes / maxRangeDays)**
1183
+
1184
+ `period`/`range`에서 시작~종료 사이 최소 간격(분) 또는 최대 기간(일)을 제한할 수 있습니다.
1185
+ 위반 시 적용(Apply) 버튼이 비활성화되며, `hasTime` 여부와 무관하게 종료가 시작보다 빠른 경우도
1186
+ 함께 걸러집니다. 각 위반 종류별 토스트 메시지는 opt-in이라 미지정 시 토스트 없이 적용만 막힙니다.
1187
+
1188
+ ```tsx
1189
+ <DatePicker
1190
+ variant="range"
1191
+ hasTime
1192
+ value={range}
1193
+ onChange={setRange}
1194
+ minDurationMinutes={30} // 시작~종료 최소 30분. hasTime일 때만 검증됨
1195
+ maxRangeDays={7} // 시작~종료 최대 7일. hasTime과 무관하게 검증됨
1196
+ timeOrderToastText="종료 시간은 시작 시간 이후여야 해요"
1197
+ minDurationToastText="최소 30분 이상 선택해 주세요"
1198
+ maxRangeToastText="최대 7일까지 선택할 수 있어요"
1199
+ />
1200
+ ```
1201
+
1182
1202
  **특정 날짜 비활성화**
1183
1203
 
1184
1204
  ```tsx
@@ -1253,6 +1273,11 @@ function isDateDisabled(date: Date) {
1253
1273
  | `monthYearActionArea`| `boolean` | `true` | 헤더의 연/월 라벨 클릭 시 빠른 선택 드롭다운 노출 여부 |
1254
1274
  | `startLabel` | `string` | `"시작"` | `hasTime`일 때 시작 시간 행의 라벨 |
1255
1275
  | `endLabel` | `string` | `"종료"` | `hasTime && (period\|range)`일 때 종료 시간 행의 라벨 |
1276
+ | `minDurationMinutes` | `number` | — | `period`/`range`에서 시작~종료 최소 간격(분). `hasTime`일 때만 검증됨. 미지정 시 검증 안 함 |
1277
+ | `maxRangeDays` | `number` | — | `period`/`range`에서 시작~종료 최대 기간(일). `hasTime`과 무관하게 검증됨. 미지정 시 검증 안 함 |
1278
+ | `timeOrderToastText` | `string` | — | 종료가 시작보다 빠를 때 노출되는 토스트 메시지. 미지정 시 토스트 없이 적용만 막힘 |
1279
+ | `minDurationToastText` | `string` | — | `minDurationMinutes` 위반 시 노출되는 토스트 메시지. 미지정 시 토스트 없이 적용만 막힘 |
1280
+ | `maxRangeToastText` | `string` | — | `maxRangeDays` 위반 시 노출되는 토스트 메시지. 미지정 시 토스트 없이 적용만 막힘 |
1256
1281
  | `weekdayLabels` | `readonly string[]` | `["월",…,"일"]` | 요일 라벨. 월요일부터 순서대로 7개 전달 |
1257
1282
  | `amLabel` / `pmLabel`| `string` | `"오전"` / `"오후"` | 시간 선택 휠의 오전/오후 라벨 |
1258
1283
  | `formatMonthYear` | `(year: number, month: number) => string` | — | "2026년 8월" 헤더 포맷 override. `month`는 1~12로 전달 |
@@ -1281,6 +1306,8 @@ function isDateDisabled(date: Date) {
1281
1306
  - 팝오버는 `document.body`에 포탈로 렌더링되며, 뷰포트 하단 공간이 부족하면 트리거 위쪽으로 자동 전환(flip)됩니다.
1282
1307
  - `actionSubContent`/`actionMainContent`를 함수로 전달하면 인자로 받는 `onClick`이 기본 동작(오늘로 이동 / 적용)을 그대로 실행하므로, 라벨이나 마크업만 바꾼 커스텀 요소에도 기존 동작을 재사용할 수 있습니다. `ReactNode`를 직접 전달하면 완전히 다른 요소로 교체되며 동작은 직접 구현해야 합니다.
1283
1308
  - `hasTime`일 때 시간 필드는 [TimePicker](#timepicker)를 그대로 임베드한 것으로, `actionArea`/`actionSub`/`actionMainType`이 동일하게 전달됩니다. 예를 들어 `actionArea={false}`면 날짜뿐 아니라 시간도 휠에서 값을 고르는 즉시 커밋됩니다.
1309
+ - `period`/`range`에서 당일(시작일=종료일) 선택은 `hasTime`이 `true`일 때만 허용됩니다. `hasTime`이 `false`면 같은 날짜를 다시 클릭해도 새 선택으로 재시작될 뿐 당일 range는 만들어지지 않습니다.
1310
+ - `minDurationMinutes`/`maxRangeDays`를 위반한 상태에서는 적용(Apply) 버튼이 비활성화되어 `onChange`가 호출되지 않습니다. `actionArea={false}`(즉시 커밋)인 경우에도 위반 시 커밋이 취소되고 토스트만 노출됩니다.
1284
1311
 
1285
1312
  ---
1286
1313
 
@@ -68,6 +68,24 @@ interface DatePickerBaseProps {
68
68
  startLabel?: string;
69
69
  /** hasTime && (period|range)일 때 종료 시간 행의 라벨. @defaultValue `'종료'` */
70
70
  endLabel?: string;
71
+ /**
72
+ * period|range일 때 시작~종료 사이 최소 간격(분). hasTime일 때만 의미가 있다.
73
+ * 미지정 시 최소 간격을 검증하지 않는다.
74
+ */
75
+ minDurationMinutes?: number;
76
+ /**
77
+ * period|range일 때 시작~종료 사이 최대 일수. 미지정 시 최대 기간을 검증하지 않는다.
78
+ */
79
+ maxRangeDays?: number;
80
+ /**
81
+ * period|range && hasTime일 때, 종료 시각이 시작 시각보다 빠르면 노출되는 토스트 메시지.
82
+ * 미지정 시 토스트 없이 적용(Apply)만 막는다.
83
+ */
84
+ timeOrderToastText?: string;
85
+ /** minDurationMinutes 위반 시 노출되는 토스트 메시지. 미지정 시 토스트 없이 적용만 막는다. */
86
+ minDurationToastText?: string;
87
+ /** maxRangeDays 위반 시 노출되는 토스트 메시지. 미지정 시 토스트 없이 적용만 막는다. */
88
+ maxRangeToastText?: string;
71
89
  /**
72
90
  * 캘린더 헤더/월·년 선택 라벨("2026년 8월") 포맷을 재정의한다. i18n 등에 사용.
73
91
  * month는 1~12 값으로 전달된다. 미지정 시 `${year}년 ${month}월` 형식을 사용한다.
@@ -9,6 +9,8 @@ export declare function isSameDay(a: Date | null, b: Date | null): boolean;
9
9
  export declare function isSameMonth(a: Date, b: Date): boolean;
10
10
  export declare function isBefore(a: Date, b: Date): boolean;
11
11
  export declare function isAfter(a: Date, b: Date): boolean;
12
+ /** 두 날짜의 일(day) 단위 차이. 시/분/초는 무시하고 달력 날짜만 비교한다 */
13
+ export declare function diffInDays(a: Date, b: Date): number;
12
14
  export declare function isWithinRange(date: Date, start: Date | null, end: Date | null): boolean;
13
15
  export declare function addMonths(year: number, month: number, delta: number): {
14
16
  year: number;