@uniai-fe/uds-primitives 0.12.7 → 0.12.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-primitives",
3
- "version": "0.12.7",
3
+ "version": "0.12.8",
4
4
  "description": "UNIAI Design System; Primitives Components Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -87,9 +87,9 @@
87
87
  "react-hook-form": "^7.86.0",
88
88
  "sass": "^1.102.0",
89
89
  "typescript": "6.0.3",
90
- "@uniai-fe/next-devkit": "0.4.1",
91
90
  "@uniai-fe/eslint-config": "0.4.3",
92
91
  "@uniai-fe/tsconfig": "0.2.0",
92
+ "@uniai-fe/next-devkit": "0.4.1",
93
93
  "@uniai-fe/util-functions": "0.4.3",
94
94
  "@uniai-fe/uds-foundation": "0.6.0"
95
95
  },
@@ -1,6 +1,10 @@
1
1
  "use client";
2
2
 
3
- import type { ChangeEvent, MouseEvent as ReactMouseEvent } from "react";
3
+ import type {
4
+ ChangeEvent,
5
+ FocusEvent as ReactFocusEvent,
6
+ MouseEvent as ReactMouseEvent,
7
+ } from "react";
4
8
  import { forwardRef, useCallback, useMemo, useState } from "react";
5
9
  import { useUncontrolled } from "@mantine/hooks";
6
10
  import { Calendar } from "../../../calendar";
@@ -26,30 +30,39 @@ const INPUT_DATE_TABLE_FORMAT = "YY-MM-DD";
26
30
  * placeholder는 format과 별도 props로 처리되며, format 변경으로 placeholder가 자동 치환되지는 않는다.
27
31
  * @component
28
32
  * @param {InputCalendarProps} props
29
- * @param {CalendarMode} [props.mode="date"] 날짜/시간 모드
30
- * @param {CalendarColumns} [props.columns=1] 동시 노출 달력 수
31
- * @param {CalendarValue} [props.value] 제어형 값
32
- * @param {CalendarValue} [props.defaultValue] 비제어 초기값
33
- * @param {CalendarOnChange} [props.onChange] 값 변경 이벤트
34
- * @param {CalendarOnChange} [props.onValueChange] onChange alias
35
- * @param {boolean} [props.readOnly] 읽기 전용 여부
36
- * @param {boolean} [props.disabled] 비활성화 여부
37
- * @param {CalendarDatePickerProps} [props.datePickerProps] Mantine DatePicker 옵션
38
- * @param {string} [props.name] form name/RHF name
39
- * @param {UseFormRegisterReturn} [props.register] RHF register
40
- * @param {string} [props.placeholder="YYYY-MM-DD"] placeholder
41
- * @param {"primary" | "secondary" | "tertiary" | "table"} [props.priority="primary"] trigger input priority
42
- * @param {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state="default"] trigger input state
43
- * @param {ReactNode} [props.header] 패널 header 콘텐츠
44
- * @param {ReactNode} [props.footer] 패널 footer 콘텐츠
45
- * @param {InputCalendarTexts} [props.texts] 기본 Date 문구
46
- * @param {unknown} [props.timePicker] TimePicker 확장용 예약 슬롯(현재 미구현)
47
- * @param {boolean} [props.calendarOpened] calendar 열림 제어 상태
48
- * @param {(open: boolean) => void} [props.onCalendarOpen] calendar 열림 변경 이벤트
49
- * @param {(event: MouseEvent<Element>) => void} [props.onClick] trigger 클릭 핸들러
50
- * @param {string} [props.id] trigger id
51
- * @param {ReactNode} [props.trigger] 커스텀 trigger 슬롯
52
- * @param {(props: InputCalendarTriggerRenderProps) => ReactNode} [props.renderTrigger] 커스텀 trigger 렌더 함수
33
+ * @property {"date" | "date-time" | "time"} [props.mode="date"] Calendar layout mode. 선택 body는 date로 유지된다.
34
+ * @property {1 | 2} [props.columns=1] 동시 노출 달력 수
35
+ * @property {string | null} [props.value] 제어형 날짜
36
+ * @property {string | null} [props.defaultValue] 비제어 초기 날짜 값
37
+ * @property {(value: string | null) => void} [props.onChange] 값 변경 이벤트
38
+ * @property {(value: string | null) => void} [props.onValueChange] onChange alias
39
+ * @property {boolean} [props.readOnly] 읽기 전용 여부
40
+ * @property {boolean} [props.disabled] 비활성화 여부
41
+ * @property {CalendarDatePickerProps} [props.datePickerProps] Mantine DatePicker 옵션
42
+ * @property {string} [props.name] hidden input form name
43
+ * @property {string} [props.form] hidden input form id
44
+ * @property {UseFormRegisterReturn} [props.register] hidden input RHF register
45
+ * @property {boolean} [props.required] hidden input required 여부
46
+ * @property {InputCalendarHiddenInputProps} [props.hiddenInputProps] Template 고정값을 제외한 hidden input native props
47
+ * @property {string} [props.placeholder="YYYY-MM-DD"] placeholder
48
+ * @property {"primary" | "secondary" | "tertiary" | "table"} [props.priority="primary"] trigger input priority
49
+ * @property {"small" | "medium" | "large"} [props.size="medium"] trigger input size
50
+ * @property {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state="default"] trigger input state
51
+ * @property {boolean} [props.block=false] trigger width 100% 여부
52
+ * @property {"full" | "fit" | "fill" | "auto" | number | string} [props.width] trigger width preset 또는 custom width
53
+ * @property {(event: FocusEvent<HTMLInputElement>) => void} [props.onFocus] trigger focus 핸들러
54
+ * @property {(event: FocusEvent<HTMLInputElement>) => void} [props.onBlur] trigger blur 핸들러
55
+ * @property {ReactNode} [props.header] 패널 header 콘텐츠
56
+ * @property {ReactNode} [props.footer] 패널 footer 콘텐츠
57
+ * @property {InputCalendarTexts} [props.texts] 기본 Date 문구
58
+ * @property {unknown} [props.timePicker] 미구현 compatibility prop
59
+ * @property {boolean} [props.calendarOpened] calendar 열림 제어 상태
60
+ * @property {(open: boolean) => void} [props.onCalendarOpen] calendar 열림 변경 이벤트
61
+ * @property {(event: MouseEvent<Element>) => void} [props.onClick] trigger 클릭 핸들러
62
+ * @property {string} [props.id] trigger id
63
+ * @property {ReactNode} [props.trigger] 커스텀 trigger 슬롯
64
+ * @property {(props: InputCalendarTriggerRenderProps) => ReactNode} [props.renderTrigger] 커스텀 trigger 렌더 함수
65
+ * @desc 날짜 표시 trigger, Calendar panel과 YYYY-MM-DD hidden form storage를 조합한다.
53
66
  */
54
67
  const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
55
68
  (
@@ -64,10 +77,18 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
64
77
  disabled,
65
78
  datePickerProps,
66
79
  name,
80
+ form,
67
81
  register,
82
+ required,
83
+ hiddenInputProps,
68
84
  placeholder = "YYYY-MM-DD",
69
85
  priority = "primary",
86
+ size = "medium",
70
87
  state = "default",
88
+ block = false,
89
+ width,
90
+ onFocus,
91
+ onBlur,
71
92
  header,
72
93
  footer,
73
94
  texts,
@@ -88,7 +109,7 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
88
109
  * Input Date Template; Calendar open 상태 변경 전달 함수.
89
110
  * 비제어 사용을 위해 내부 상태를 갱신하고, 제어형 사용을 위해 외부 owner에게 next open 값을 전달한다.
90
111
  * @param {boolean} nextOpen 다음 calendar open 상태
91
- * @returns {void}
112
+ * @return {void}
92
113
  */
93
114
  const handleCalendarOpenChange = useCallback(
94
115
  (nextOpen: boolean) => {
@@ -113,7 +134,7 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
113
134
  * Input Date Template; RHF hidden input 변경 이벤트 동기화 함수.
114
135
  * trigger 표시값과 form 저장값을 분리하기 위해 CalendarValue를 직렬화한 synthetic change를 register에 전달한다.
115
136
  * @param {CalendarValue} nextValue 다음 calendar 선택 값
116
- * @returns {void}
137
+ * @return {void}
117
138
  */
118
139
  const emitRegisterChange = useCallback(
119
140
  (nextValue: CalendarValue) => {
@@ -140,7 +161,7 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
140
161
  * Input Date Template; calendar value 갱신 함수.
141
162
  * 내부/제어형 value pipeline을 갱신한 뒤 RHF register 저장값을 같은 CalendarValue 기준으로 동기화한다.
142
163
  * @param {CalendarValue} nextValue 다음 calendar 선택 값
143
- * @returns {void}
164
+ * @return {void}
144
165
  */
145
166
  const updateValue = useCallback(
146
167
  (nextValue: CalendarValue) => {
@@ -164,17 +185,33 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
164
185
  * Input Date Template; trigger click 위임 함수.
165
186
  * Calendar Root의 PopOver toggle은 asChild trigger props가 담당하고, 이 함수는 소비자가 넘긴 trigger onClick만 보존한다.
166
187
  * @param {ReactMouseEvent<Element>} event trigger click event
167
- * @returns {void}
188
+ * @return {void}
168
189
  */
169
190
  const handleTriggerClick = (event: ReactMouseEvent<Element>) => {
170
191
  triggerOnClick?.(event);
171
192
  };
172
193
 
194
+ /**
195
+ * Input Date Template; trigger blur와 RHF touched 상태 동기화 함수.
196
+ * 표시 trigger는 form name을 소유하지 않으므로 직렬화된 hidden input 값을 synthetic blur event로 전달한다.
197
+ * @utility
198
+ * @param {ReactFocusEvent<HTMLInputElement>} event trigger blur event
199
+ * @desc 소비자 blur callback과 hidden input register blur를 같은 날짜 값으로 동기화한다.
200
+ * @return {void}
201
+ */
202
+ const handleTriggerBlur = (event: ReactFocusEvent<HTMLInputElement>) => {
203
+ register?.onBlur({
204
+ target: { name: register.name, value: serializedCalendarValue },
205
+ type: "blur",
206
+ });
207
+ onBlur?.(event);
208
+ };
209
+
173
210
  /**
174
211
  * Input Date Template; Calendar Core change adapter 함수.
175
212
  * Calendar.Root의 onChange 값을 Template value/RHF 동기화 pipeline으로 연결한다.
176
213
  * @param {CalendarValue} nextValue 다음 calendar 선택 값
177
- * @returns {void}
214
+ * @return {void}
178
215
  */
179
216
  const handleCalendarChange = (nextValue: CalendarValue) => {
180
217
  updateValue(nextValue);
@@ -201,7 +238,12 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
201
238
  readOnly,
202
239
  onClick: handleTriggerClick,
203
240
  priority,
241
+ size,
204
242
  state,
243
+ block,
244
+ width,
245
+ onFocus,
246
+ onBlur: handleTriggerBlur,
205
247
  };
206
248
 
207
249
  const triggerNode = trigger ?? renderTrigger?.(triggerRenderProps) ?? (
@@ -214,6 +256,11 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
214
256
  readOnly={readOnly}
215
257
  onClick={handleTriggerClick}
216
258
  priority={priority}
259
+ size={size}
260
+ block={block}
261
+ width={width}
262
+ onFocus={onFocus}
263
+ onBlur={handleTriggerBlur}
217
264
  />
218
265
  );
219
266
 
@@ -228,7 +275,7 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
228
275
  readOnly={readOnly}
229
276
  value={calendarValue}
230
277
  onChange={handleCalendarChange}
231
- // 변경: Mantine DatePicker는 valueFormat prop을 지원하지 않으므로 옵션을 그대로 전달한다.
278
+ // Mantine DatePicker는 valueFormat prop을 지원하지 않으므로 옵션을 그대로 전달한다.
232
279
  datePickerProps={datePickerProps}
233
280
  header={header}
234
281
  footer={footerContent}
@@ -239,11 +286,14 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
239
286
  </Calendar.Root>
240
287
  {(register?.name || name) && (
241
288
  <input
242
- // 변경: trigger 표시값과 저장값을 분리하기 위해 form 참여는 hidden input만 담당한다.
289
+ // trigger 표시값과 form 저장값을 분리해 hidden input만 form에 참여한다.
290
+ {...hiddenInputProps}
291
+ {...register}
243
292
  type="hidden"
244
293
  value={serializedCalendarValue}
245
294
  name={register?.name ?? name}
246
- {...register}
295
+ form={form ?? hiddenInputProps?.form}
296
+ required={required ?? hiddenInputProps?.required}
247
297
  />
248
298
  )}
249
299
  </>
@@ -19,17 +19,23 @@ import type { InputCalendarTriggerViewProps } from "../../types";
19
19
  * 그 외 priority에서는 기존처럼 right 슬롯을 유지한다.
20
20
  * @component
21
21
  * @param {InputCalendarTriggerViewProps} props
22
- * @param {string} [props.className] trigger className
23
- * @param {string} [props.placeholder] placeholder
24
- * @param {string} [props.displayValue] 표시 문자열
25
- * @param {(event: MouseEvent<Element>) => void} [props.onClick] 클릭 핸들러
26
- * @param {boolean} [props.disabled] disabled 여부
27
- * @param {boolean} [props.readOnly] readOnly 여부
28
- * @param {"primary" | "secondary" | "tertiary" | "table"} [props.priority] trigger input priority
29
- * @param {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state] trigger input state
30
- * @param {string} [props.id] input id
31
- * @param {string} [props.name] form name
32
- * @param {UseFormRegisterReturn} [props.register] RHF register
22
+ * @property {string} [props.className] trigger className
23
+ * @property {string} [props.placeholder] placeholder
24
+ * @property {string} [props.displayValue] 표시 문자열
25
+ * @property {(event: MouseEvent<Element>) => void} [props.onClick] 클릭 핸들러
26
+ * @property {boolean} [props.disabled] disabled 여부
27
+ * @property {boolean} [props.readOnly] readOnly 여부
28
+ * @property {"primary" | "secondary" | "tertiary" | "table"} [props.priority] trigger input priority
29
+ * @property {"small" | "medium" | "large"} [props.size] trigger input size
30
+ * @property {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state] trigger input state
31
+ * @property {boolean} [props.block] trigger width 100% 여부
32
+ * @property {"full" | "fit" | "fill" | "auto" | number | string} [props.width] trigger width preset 또는 custom width
33
+ * @property {(event: FocusEvent<HTMLInputElement>) => void} [props.onFocus] trigger focus 핸들러
34
+ * @property {(event: FocusEvent<HTMLInputElement>) => void} [props.onBlur] trigger blur 핸들러
35
+ * @property {string} [props.id] input id
36
+ * @property {string} [props.name] form name
37
+ * @property {UseFormRegisterReturn} [props.register] RHF register
38
+ * @desc Date 값을 직접 편집하지 않고 Calendar 선택값을 표시하는 기본 trigger다.
33
39
  */
34
40
  const InputDateTrigger = forwardRef<
35
41
  HTMLInputElement,
@@ -44,7 +50,12 @@ const InputDateTrigger = forwardRef<
44
50
  disabled,
45
51
  readOnly,
46
52
  priority,
53
+ size,
47
54
  state,
55
+ block,
56
+ width,
57
+ onFocus,
58
+ onBlur,
48
59
  id,
49
60
  name,
50
61
  register,
@@ -90,7 +101,7 @@ const InputDateTrigger = forwardRef<
90
101
  triggerInputRef.current?.click();
91
102
  };
92
103
 
93
- // 변경: Date trigger는 기본적으로 직접 타이핑을 막고, 달력 선택을 단일 입력 경로로 유지한다.
104
+ // Date trigger는 직접 타이핑을 막아 달력 선택을 단일 입력 경로로 유지한다.
94
105
  const shouldBlockTyping = !disabled && !readOnly;
95
106
 
96
107
  const handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
@@ -170,9 +181,14 @@ const InputDateTrigger = forwardRef<
170
181
  name={name ?? register?.name}
171
182
  register={register}
172
183
  priority={priority}
184
+ size={size}
173
185
  state={state}
186
+ block={block}
187
+ width={width}
174
188
  placeholder={placeholder}
175
189
  className={clsx("input-date-trigger-input", className)}
190
+ onFocus={onFocus}
191
+ onBlur={onBlur}
176
192
  onClick={handleInputClick}
177
193
  onKeyDown={handleKeyDown}
178
194
  onBeforeInput={handleBeforeInput}
@@ -1,6 +1,10 @@
1
1
  "use client";
2
2
 
3
- import type { ChangeEvent, MouseEvent as ReactMouseEvent } from "react";
3
+ import type {
4
+ ChangeEvent,
5
+ FocusEvent as ReactFocusEvent,
6
+ MouseEvent as ReactMouseEvent,
7
+ } from "react";
4
8
  import { forwardRef, useCallback, useMemo, useState } from "react";
5
9
  import { useUncontrolled } from "@mantine/hooks";
6
10
  import type { UseFormRegisterReturn } from "react-hook-form";
@@ -31,29 +35,39 @@ const INPUT_DATE_RANGE_TABLE_FORMAT = "YY-MM-DD";
31
35
  * form 저장은 start/end hidden input 2개로 분리한다.
32
36
  * @component
33
37
  * @param {InputCalendarRangeProps} props range date props
34
- * @param {CalendarRangeValue} [props.value] 제어형 range 값
35
- * @param {CalendarRangeValue} [props.defaultValue] 비제어 range 초기값
36
- * @param {CalendarRangeOnChange} [props.onChange] range 값 변경 이벤트
37
- * @param {CalendarRangeOnChange} [props.onValueChange] onChange alias
38
- * @param {boolean} [props.readOnly] 읽기 전용 여부
39
- * @param {boolean} [props.disabled] 비활성화 여부
40
- * @param {CalendarRangeDatePickerProps} [props.datePickerProps] Mantine range DatePicker 옵션
41
- * @param {string} [props.startName] 시작일 hidden input name
42
- * @param {string} [props.endName] 종료일 hidden input name
43
- * @param {UseFormRegisterReturn} [props.startRegister] 시작일 RHF register
44
- * @param {UseFormRegisterReturn} [props.endRegister] 종료일 RHF register
45
- * @param {string} [props.placeholder="YYYY-MM-DD ~ YYYY-MM-DD"] placeholder
46
- * @param {"primary" | "secondary" | "tertiary" | "table"} [props.priority="primary"] trigger input priority
47
- * @param {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state="default"] trigger input state
48
- * @param {ReactNode} [props.header] 패널 header 콘텐츠
49
- * @param {ReactNode} [props.footer] 패널 footer 콘텐츠
50
- * @param {InputCalendarTexts} [props.texts] 기본 Date 문구
51
- * @param {boolean} [props.calendarOpened] calendar 열림 제어 상태
52
- * @param {(open: boolean) => void} [props.onCalendarOpen] calendar 열림 변경 이벤트
53
- * @param {string} [props.id] trigger id
54
- * @param {ReactNode} [props.trigger] 커스텀 trigger 슬롯
55
- * @param {(props: InputCalendarTriggerRenderProps) => ReactNode} [props.renderTrigger] 커스텀 trigger 렌더 함수
56
- * @param {(event: MouseEvent<Element>) => void} [props.onClick] trigger 클릭 핸들러
38
+ * @property {[string | null, string | null]} [props.value] 제어형 range 값
39
+ * @property {[string | null, string | null]} [props.defaultValue] 비제어 range 초기값
40
+ * @property {(value: [string | null, string | null]) => void} [props.onChange] range 값 변경 이벤트
41
+ * @property {(value: [string | null, string | null]) => void} [props.onValueChange] onChange alias
42
+ * @property {boolean} [props.readOnly] 읽기 전용 여부
43
+ * @property {boolean} [props.disabled] 비활성화 여부
44
+ * @property {CalendarRangeDatePickerProps} [props.datePickerProps] Mantine range DatePicker 옵션
45
+ * @property {string} [props.startName] 시작일 hidden input name
46
+ * @property {string} [props.endName] 종료일 hidden input name
47
+ * @property {string} [props.form] start/end hidden input form id
48
+ * @property {UseFormRegisterReturn} [props.startRegister] 시작일 RHF register
49
+ * @property {UseFormRegisterReturn} [props.endRegister] 종료일 RHF register
50
+ * @property {boolean} [props.required] start/end hidden input required 여부
51
+ * @property {InputCalendarHiddenInputProps} [props.startHiddenInputProps] 시작일 hidden input native props
52
+ * @property {InputCalendarHiddenInputProps} [props.endHiddenInputProps] 종료일 hidden input native props
53
+ * @property {string} [props.placeholder="YYYY-MM-DD ~ YYYY-MM-DD"] placeholder
54
+ * @property {"primary" | "secondary" | "tertiary" | "table"} [props.priority="primary"] trigger input priority
55
+ * @property {"small" | "medium" | "large"} [props.size="medium"] trigger input size
56
+ * @property {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state="default"] trigger input state
57
+ * @property {boolean} [props.block=false] trigger width 100% 여부
58
+ * @property {"full" | "fit" | "fill" | "auto" | number | string} [props.width] trigger width preset 또는 custom width
59
+ * @property {(event: FocusEvent<HTMLInputElement>) => void} [props.onFocus] trigger focus 핸들러
60
+ * @property {(event: FocusEvent<HTMLInputElement>) => void} [props.onBlur] trigger blur 핸들러
61
+ * @property {ReactNode} [props.header] 패널 header 콘텐츠
62
+ * @property {ReactNode} [props.footer] 패널 footer 콘텐츠
63
+ * @property {InputCalendarTexts} [props.texts] 기본 Date 문구
64
+ * @property {boolean} [props.calendarOpened] calendar 열림 제어 상태
65
+ * @property {(open: boolean) => void} [props.onCalendarOpen] calendar 열림 변경 이벤트
66
+ * @property {string} [props.id] trigger id
67
+ * @property {ReactNode} [props.trigger] 커스텀 trigger 슬롯
68
+ * @property {(props: InputCalendarTriggerRenderProps) => ReactNode} [props.renderTrigger] 커스텀 trigger 렌더 함수
69
+ * @property {(event: MouseEvent<Element>) => void} [props.onClick] trigger 클릭 핸들러
70
+ * @desc 기간 표시 trigger, Calendar.Range panel과 start/end hidden form storage를 조합한다.
57
71
  * @example
58
72
  * <Input.Date.Range.Template
59
73
  * startName="start_date"
@@ -74,11 +88,20 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
74
88
  datePickerProps,
75
89
  startName,
76
90
  endName,
91
+ form,
77
92
  startRegister,
78
93
  endRegister,
94
+ required,
95
+ startHiddenInputProps,
96
+ endHiddenInputProps,
79
97
  placeholder = "YYYY-MM-DD ~ YYYY-MM-DD",
80
98
  priority = "primary",
99
+ size = "medium",
81
100
  state = "default",
101
+ block = false,
102
+ width,
103
+ onFocus,
104
+ onBlur,
82
105
  header,
83
106
  footer,
84
107
  texts,
@@ -99,7 +122,7 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
99
122
  * Input Date Range Template; Calendar open 상태 변경 전달 함수.
100
123
  * 비제어 range calendar 상태를 갱신하고, 제어형 owner가 열림 상태를 조율할 수 있게 next open 값을 전달한다.
101
124
  * @param {boolean} nextOpen 다음 range calendar open 상태
102
- * @returns {void}
125
+ * @return {void}
103
126
  */
104
127
  const handleCalendarOpenChange = useCallback(
105
128
  (nextOpen: boolean) => {
@@ -125,7 +148,7 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
125
148
  * Input Date Range Template; RHF hidden input 변경 이벤트 동기화 함수.
126
149
  * range tuple의 start/end 값을 각각 직렬화해 startRegister/endRegister에 synthetic change로 전달한다.
127
150
  * @param {CalendarRangeValue} nextValue 다음 range calendar 선택 값
128
- * @returns {void}
151
+ * @return {void}
129
152
  */
130
153
  const emitRegisterChange = useCallback(
131
154
  (nextValue: CalendarRangeValue) => {
@@ -134,7 +157,7 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
134
157
  * start 또는 end register 한쪽에 직렬화 값을 전달한다.
135
158
  * @param {UseFormRegisterReturn | undefined} register RHF register 결과
136
159
  * @param {CalendarValue} nextCalendarValue 다음 start/end 선택 값
137
- * @returns {void}
160
+ * @return {void}
138
161
  */
139
162
  const emit = (
140
163
  register: UseFormRegisterReturn | undefined,
@@ -169,7 +192,7 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
169
192
  * Input Date Range Template; range value 갱신 함수.
170
193
  * Calendar.Range tuple 값을 갱신하고 start/end hidden input 저장값을 같은 tuple 기준으로 동기화한다.
171
194
  * @param {CalendarRangeValue} nextValue 다음 range calendar 선택 값
172
- * @returns {void}
195
+ * @return {void}
173
196
  */
174
197
  const updateValue = useCallback(
175
198
  (nextValue: CalendarRangeValue) => {
@@ -194,17 +217,37 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
194
217
  * Input Date Range Template; trigger click 위임 함수.
195
218
  * Calendar.Range Root의 PopOver toggle은 asChild trigger props가 담당하고, 이 함수는 소비자가 넘긴 trigger onClick만 보존한다.
196
219
  * @param {ReactMouseEvent<Element>} event trigger click event
197
- * @returns {void}
220
+ * @return {void}
198
221
  */
199
222
  const handleTriggerClick = (event: ReactMouseEvent<Element>) => {
200
223
  triggerOnClick?.(event);
201
224
  };
202
225
 
226
+ /**
227
+ * Input Date Range Template; trigger blur와 start/end RHF touched 상태 동기화 함수.
228
+ * 표시 trigger는 저장 endpoint를 소유하지 않으므로 직렬화한 start/end 값을 각 register에 전달한다.
229
+ * @utility
230
+ * @param {ReactFocusEvent<HTMLInputElement>} event trigger blur event
231
+ * @desc 소비자 blur callback과 두 hidden input register blur를 같은 range 값으로 동기화한다.
232
+ * @return {void}
233
+ */
234
+ const handleTriggerBlur = (event: ReactFocusEvent<HTMLInputElement>) => {
235
+ startRegister?.onBlur({
236
+ target: { name: startRegister.name, value: serializedStartValue },
237
+ type: "blur",
238
+ });
239
+ endRegister?.onBlur({
240
+ target: { name: endRegister.name, value: serializedEndValue },
241
+ type: "blur",
242
+ });
243
+ onBlur?.(event);
244
+ };
245
+
203
246
  /**
204
247
  * Input Date Range Template; Calendar Range Core change adapter 함수.
205
248
  * Calendar.Range.Root의 onChange 값을 Template tuple/RHF 동기화 pipeline으로 연결한다.
206
249
  * @param {CalendarRangeValue} nextValue 다음 range calendar 선택 값
207
- * @returns {void}
250
+ * @return {void}
208
251
  */
209
252
  const handleCalendarChange = (nextValue: CalendarRangeValue) => {
210
253
  updateValue(nextValue);
@@ -232,7 +275,7 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
232
275
  >
233
276
  {texts?.clear ?? "삭제하기"}
234
277
  </InputDateClearButton>
235
- {/* 변경: range footer는 삭제/적용을 같은 row에 두고 Apply 폭을 content 기준으로 줄인다. */}
278
+ {/* range footer는 삭제/적용을 같은 row에 두고 Apply 폭을 content 기준으로 줄인다. */}
236
279
  <InputDateApplyButton
237
280
  className="input-date-range-apply-button"
238
281
  disabled={disabled}
@@ -252,7 +295,12 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
252
295
  readOnly,
253
296
  onClick: handleTriggerClick,
254
297
  priority,
298
+ size,
255
299
  state,
300
+ block,
301
+ width,
302
+ onFocus,
303
+ onBlur: handleTriggerBlur,
256
304
  };
257
305
 
258
306
  const triggerNode = trigger ?? renderTrigger?.(triggerRenderProps) ?? (
@@ -265,6 +313,11 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
265
313
  readOnly={readOnly}
266
314
  onClick={handleTriggerClick}
267
315
  priority={priority}
316
+ size={size}
317
+ block={block}
318
+ width={width}
319
+ onFocus={onFocus}
320
+ onBlur={handleTriggerBlur}
268
321
  />
269
322
  );
270
323
 
@@ -287,20 +340,26 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
287
340
  </Calendar.Range.Root>
288
341
  {(startRegister?.name || startName) && (
289
342
  <input
290
- // 변경: range 시작일 저장값은 trigger 표시와 분리해 hidden input이 담당한다.
343
+ // range 시작일 저장값은 trigger 표시와 분리해 hidden input이 담당한다.
344
+ {...startHiddenInputProps}
345
+ {...startRegister}
291
346
  type="hidden"
292
347
  value={serializedStartValue}
293
348
  name={startRegister?.name ?? startName}
294
- {...startRegister}
349
+ form={form ?? startHiddenInputProps?.form}
350
+ required={required ?? startHiddenInputProps?.required}
295
351
  />
296
352
  )}
297
353
  {(endRegister?.name || endName) && (
298
354
  <input
299
- // 변경: range 종료일 저장값은 trigger 표시와 분리해 hidden input이 담당한다.
355
+ // range 종료일 저장값은 trigger 표시와 분리해 hidden input이 담당한다.
356
+ {...endHiddenInputProps}
357
+ {...endRegister}
300
358
  type="hidden"
301
359
  value={serializedEndValue}
302
360
  name={endRegister?.name ?? endName}
303
- {...endRegister}
361
+ form={form ?? endHiddenInputProps?.form}
362
+ required={required ?? endHiddenInputProps?.required}
304
363
  />
305
364
  )}
306
365
  </>