@uniai-fe/uds-primitives 0.12.11 → 0.12.13

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.11",
3
+ "version": "0.12.13",
4
4
  "description": "UNIAI Design System; Primitives Components Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -88,10 +88,10 @@
88
88
  "sass": "^1.102.0",
89
89
  "typescript": "6.0.3",
90
90
  "@uniai-fe/eslint-config": "0.4.4",
91
- "@uniai-fe/next-devkit": "0.4.1",
91
+ "@uniai-fe/uds-foundation": "0.6.0",
92
92
  "@uniai-fe/util-functions": "0.4.3",
93
93
  "@uniai-fe/tsconfig": "0.2.0",
94
- "@uniai-fe/uds-foundation": "0.6.0"
94
+ "@uniai-fe/next-devkit": "0.4.1"
95
95
  },
96
96
  "scripts": {
97
97
  "check:pre-commit": "pnpm --dir ../../.. run check:pre-commit",
@@ -21,6 +21,7 @@ import {
21
21
  } from "../../utils/date";
22
22
  import InputDateFooterTemplate from "./footer/Template";
23
23
  import InputDateTrigger from "./Trigger";
24
+ import useHiddenDateRegister from "./useHiddenDateRegister";
24
25
 
25
26
  const INPUT_DATE_TABLE_FORMAT = "YY-MM-DD";
26
27
 
@@ -180,6 +181,10 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
180
181
  [calendarValue, priority],
181
182
  );
182
183
  const serializedCalendarValue = serializeCalendarValue(calendarValue);
184
+ const hiddenInputRef = useHiddenDateRegister(
185
+ serializedCalendarValue,
186
+ register,
187
+ );
183
188
 
184
189
  /**
185
190
  * Input Date Template; trigger click 위임 함수.
@@ -294,23 +299,7 @@ const InputDateTemplate = forwardRef<HTMLDivElement, InputCalendarProps>(
294
299
  name={register?.name ?? name}
295
300
  form={form ?? hiddenInputProps?.form}
296
301
  required={required ?? hiddenInputProps?.required}
297
- ref={node => {
298
- register?.ref(node);
299
- if (node && register) {
300
- // RHF ref의 form default 적용이 끝난 뒤 Calendar 값을 DOM과 RHF storage에 동기화한다.
301
- queueMicrotask(() => {
302
- if (!node.isConnected) {
303
- return;
304
- }
305
- node.value = serializedCalendarValue;
306
- void register.onChange({
307
- target: node,
308
- currentTarget: node,
309
- type: "change",
310
- } as unknown as ChangeEvent<HTMLInputElement>);
311
- });
312
- }
313
- }}
302
+ ref={hiddenInputRef}
314
303
  />
315
304
  )}
316
305
  </>
@@ -26,6 +26,7 @@ import InputDateTodayButton from "../button/TodayButton";
26
26
  import InputDateFooterTemplateContainer from "../footer/Container";
27
27
  import InputDateFooterUtilContainer from "../footer/UtilContainer";
28
28
  import InputDateTrigger from "../Trigger";
29
+ import useHiddenDateRegister from "../useHiddenDateRegister";
29
30
 
30
31
  const INPUT_DATE_RANGE_TABLE_FORMAT = "YY-MM-DD";
31
32
 
@@ -212,6 +213,14 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
212
213
  );
213
214
  const serializedStartValue = serializeCalendarValue(calendarValue[0]);
214
215
  const serializedEndValue = serializeCalendarValue(calendarValue[1]);
216
+ const startHiddenInputRef = useHiddenDateRegister(
217
+ serializedStartValue,
218
+ startRegister,
219
+ );
220
+ const endHiddenInputRef = useHiddenDateRegister(
221
+ serializedEndValue,
222
+ endRegister,
223
+ );
215
224
 
216
225
  /**
217
226
  * Input Date Range Template; trigger click 위임 함수.
@@ -348,23 +357,7 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
348
357
  name={startRegister?.name ?? startName}
349
358
  form={form ?? startHiddenInputProps?.form}
350
359
  required={required ?? startHiddenInputProps?.required}
351
- ref={node => {
352
- startRegister?.ref(node);
353
- if (node && startRegister) {
354
- // RHF ref의 form default 적용이 끝난 뒤 range 시작값을 DOM과 RHF storage에 동기화한다.
355
- queueMicrotask(() => {
356
- if (!node.isConnected) {
357
- return;
358
- }
359
- node.value = serializedStartValue;
360
- void startRegister.onChange({
361
- target: node,
362
- currentTarget: node,
363
- type: "change",
364
- } as unknown as ChangeEvent<HTMLInputElement>);
365
- });
366
- }
367
- }}
360
+ ref={startHiddenInputRef}
368
361
  />
369
362
  )}
370
363
  {(endRegister?.name || endName) && (
@@ -377,23 +370,7 @@ const InputDateRangeTemplate = forwardRef<HTMLElement, InputCalendarRangeProps>(
377
370
  name={endRegister?.name ?? endName}
378
371
  form={form ?? endHiddenInputProps?.form}
379
372
  required={required ?? endHiddenInputProps?.required}
380
- ref={node => {
381
- endRegister?.ref(node);
382
- if (node && endRegister) {
383
- // RHF ref의 form default 적용이 끝난 뒤 range 종료값을 DOM과 RHF storage에 동기화한다.
384
- queueMicrotask(() => {
385
- if (!node.isConnected) {
386
- return;
387
- }
388
- node.value = serializedEndValue;
389
- void endRegister.onChange({
390
- target: node,
391
- currentTarget: node,
392
- type: "change",
393
- } as unknown as ChangeEvent<HTMLInputElement>);
394
- });
395
- }
396
- }}
373
+ ref={endHiddenInputRef}
397
374
  />
398
375
  )}
399
376
  </>
@@ -0,0 +1,39 @@
1
+ import type { ChangeEvent } from "react";
2
+ import { useLayoutEffect, useRef } from "react";
3
+ import type { UseFormRegisterReturn } from "react-hook-form";
4
+
5
+ /**
6
+ * Input.Date hidden input의 RHF 등록과 저장값 동기화.
7
+ * @hook
8
+ * @param {string} value Calendar의 직렬화된 날짜 값
9
+ * @param {UseFormRegisterReturn | undefined} register RHF 필드 등록 결과
10
+ * @desc ref 연결 후 값이나 필드가 바뀔 때만 RHF change를 전달해 재렌더 반복을 막는다.
11
+ * @return {(node: HTMLInputElement | null) => void} hidden input ref callback
12
+ */
13
+ export default function useHiddenDateRegister(
14
+ value: string,
15
+ register?: UseFormRegisterReturn,
16
+ ) {
17
+ const inputRef = useRef<HTMLInputElement | null>(null);
18
+ const onChange = register?.onChange;
19
+ const registerName = register?.name;
20
+
21
+ useLayoutEffect(() => {
22
+ const node = inputRef.current;
23
+ if (!node || !onChange) {
24
+ return;
25
+ }
26
+ // RHF ref가 form default를 적용한 뒤 Calendar 값을 저장한다.
27
+ node.value = value;
28
+ void onChange({
29
+ target: node,
30
+ currentTarget: node,
31
+ type: "change",
32
+ } as unknown as ChangeEvent<HTMLInputElement>);
33
+ }, [value, registerName, onChange]);
34
+
35
+ return (node: HTMLInputElement | null) => {
36
+ register?.ref(node);
37
+ inputRef.current = node;
38
+ };
39
+ }
@@ -319,11 +319,10 @@ export function SelectDefault<OptionData = unknown>({
319
319
  setOpen(false);
320
320
  };
321
321
 
322
- // 16) label input 값은 custom mode에서만 사용자 입력값/controlled inputProps.value를 사용한다.
322
+ // 16) 명시된 native value는 빈 문자열과 0도 controlled 값으로 사용하고, undefined일 때만 내부 입력값을 유지한다.
323
323
  const labelInputValue = isCustomInputActive
324
- ? typeof inputProps?.value === "string" ||
325
- typeof inputProps?.value === "number"
326
- ? String(inputProps.value) || customInputState.value
324
+ ? inputProps?.value !== undefined
325
+ ? String(inputProps.value)
327
326
  : customInputState.value
328
327
  : toSelectInputText(resolvedDisplayLabel);
329
328
 
@@ -144,9 +144,18 @@ export interface SelectCustomOptions {
144
144
 
145
145
  /**
146
146
  * Select custom input props
147
- * @property {ComponentPropsWithoutRef<"input">} [native] label input native 속성
147
+ * @property {string | number | readonly string[]} [value] 빈 문자열과 0을 포함한 controlled 표시값; undefined면 내부 입력값 유지
148
+ * @property {never} [placeholder] customOptions.placeholder를 사용하므로 전달 불가
149
+ * @desc label input native 속성에서 placeholder를 제외한 직접입력 계약
148
150
  */
149
151
  export interface SelectCustomInputProps extends ComponentPropsWithoutRef<"input"> {
152
+ /**
153
+ * 빈 문자열과 0을 포함한 controlled 표시값; undefined면 내부 입력값 유지
154
+ */
155
+ value?: string | number | readonly string[];
156
+ /**
157
+ * customOptions.placeholder를 사용하므로 전달 불가
158
+ */
150
159
  placeholder?: never;
151
160
  }
152
161