@uniai-fe/uds-primitives 0.2.9 → 0.2.10

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.
Files changed (59) hide show
  1. package/dist/styles.css +415 -0
  2. package/package.json +2 -1
  3. package/src/components/calendar/types/calendar.ts +5 -0
  4. package/src/components/input/markup/date/Template.tsx +36 -5
  5. package/src/components/input/markup/date/Trigger.tsx +22 -4
  6. package/src/components/input/markup/foundation/Input.tsx +19 -11
  7. package/src/components/input/markup/foundation/Utility.tsx +11 -7
  8. package/src/components/input/styles/date.scss +21 -0
  9. package/src/components/input/styles/foundation.scss +30 -0
  10. package/src/components/input/styles/variables.scss +11 -0
  11. package/src/components/input/types/date.ts +15 -0
  12. package/src/components/input/types/foundation.ts +18 -11
  13. package/src/components/input/utils/date.ts +15 -1
  14. package/src/components/select/markup/Default.tsx +6 -3
  15. package/src/components/select/markup/foundation/Base.tsx +1 -1
  16. package/src/components/select/markup/foundation/Icon.tsx +6 -1
  17. package/src/components/select/markup/multiple/Multiple.tsx +6 -3
  18. package/src/components/select/styles/select.scss +50 -0
  19. package/src/components/select/styles/variables.scss +26 -0
  20. package/src/components/select/types/base.ts +3 -2
  21. package/src/components/select/types/icon.ts +7 -6
  22. package/src/components/select/types/props.ts +1 -0
  23. package/src/components/select/types/trigger.ts +4 -0
  24. package/src/components/table/hooks/index.ts +0 -3
  25. package/src/components/table/index.tsx +5 -3
  26. package/src/components/table/markup/Container.tsx +126 -0
  27. package/src/components/table/markup/foundation/Body.tsx +24 -0
  28. package/src/components/table/markup/foundation/Cell.tsx +72 -0
  29. package/src/components/table/markup/foundation/Col.tsx +22 -0
  30. package/src/components/table/markup/foundation/Colgroup.tsx +29 -0
  31. package/src/components/table/markup/foundation/Foot.tsx +24 -0
  32. package/src/components/table/markup/foundation/Head.tsx +24 -0
  33. package/src/components/table/markup/foundation/Root.tsx +32 -0
  34. package/src/components/table/markup/foundation/Row.tsx +32 -0
  35. package/src/components/table/markup/foundation/Td.tsx +37 -0
  36. package/src/components/table/markup/foundation/Th.tsx +39 -0
  37. package/src/components/table/markup/foundation/index.tsx +30 -0
  38. package/src/components/table/markup/index.tsx +8 -2
  39. package/src/components/table/styles/foundation.scss +247 -0
  40. package/src/components/table/styles/index.scss +2 -0
  41. package/src/components/table/styles/variables.scss +29 -0
  42. package/src/components/table/types/foundation.ts +250 -0
  43. package/src/components/table/types/index.ts +1 -4
  44. package/src/components/tooltip/img/info.svg +5 -0
  45. package/src/components/tooltip/img/information.svg +9 -0
  46. package/src/components/tooltip/index.scss +1 -0
  47. package/src/components/tooltip/index.tsx +4 -0
  48. package/src/components/tooltip/markup/Message.tsx +70 -0
  49. package/src/components/tooltip/markup/Root.tsx +32 -0
  50. package/src/components/tooltip/markup/Template.tsx +46 -0
  51. package/src/components/tooltip/markup/Trigger.tsx +32 -0
  52. package/src/components/tooltip/markup/index.tsx +18 -0
  53. package/src/components/tooltip/styles/index.scss +2 -0
  54. package/src/components/tooltip/styles/tooltip.scss +47 -0
  55. package/src/components/tooltip/styles/variables.scss +14 -0
  56. package/src/components/tooltip/types/index.ts +1 -0
  57. package/src/components/tooltip/types/props.ts +118 -0
  58. package/src/index.scss +1 -0
  59. package/src/index.tsx +1 -0
@@ -9,6 +9,8 @@ import type { InputCalendarTriggerViewProps } from "../../types";
9
9
 
10
10
  /**
11
11
  * Input Date trigger field.
12
+ * priority가 table인 경우 아이콘을 left 슬롯에 배치하고,
13
+ * 그 외 priority에서는 기존처럼 right 슬롯을 유지한다.
12
14
  * @component
13
15
  * @param {InputCalendarTriggerViewProps} props
14
16
  * @param {string} [props.className] trigger className
@@ -16,6 +18,7 @@ import type { InputCalendarTriggerViewProps } from "../../types";
16
18
  * @param {string} [props.displayValue] 표시 문자열
17
19
  * @param {(event: MouseEvent<Element>) => void} [props.onClick] 클릭 핸들러
18
20
  * @param {boolean} [props.disabled] disabled 여부
21
+ * @param {"primary" | "secondary" | "tertiary" | "table"} [props.priority] trigger input priority
19
22
  * @param {string} [props.id] input id
20
23
  * @param {string} [props.name] form name
21
24
  * @param {UseFormRegisterReturn} [props.register] RHF register
@@ -31,6 +34,7 @@ const InputDateTrigger = forwardRef<
31
34
  displayValue,
32
35
  onClick,
33
36
  disabled,
37
+ priority,
34
38
  id,
35
39
  name,
36
40
  register,
@@ -55,19 +59,33 @@ const InputDateTrigger = forwardRef<
55
59
  {...restProps}
56
60
  ref={ref}
57
61
  value={displayValue}
62
+ // PopOver.Trigger(asChild)가 주입한 `type="button"`으로 placeholder가 사라지는 문제를 막기 위해
63
+ // Date trigger는 항상 readOnly text input으로 고정한다.
64
+ type="text"
58
65
  readOnly
59
66
  disabled={disabled}
60
67
  id={id}
61
68
  name={name ?? register?.name}
62
69
  register={register}
70
+ priority={priority}
63
71
  placeholder={placeholder}
64
72
  className={clsx("input-date-trigger-input", className)}
65
73
  onClick={handleInputClick}
74
+ // table priority는 icon을 왼쪽 슬롯에 배치한다.
75
+ left={
76
+ priority === "table" ? (
77
+ <figure className="input-date-trigger-icon" aria-hidden="true">
78
+ <Calendar.Icon.Calendar />
79
+ </figure>
80
+ ) : undefined
81
+ }
82
+ // 기본(priority != table)은 기존처럼 icon을 오른쪽 슬롯에 유지한다.
66
83
  right={
67
- // 토글 인터랙션은 input 단일 경로로 유지하고, 오른쪽 아이콘은 장식 요소로만 둔다.
68
- <figure className="input-date-trigger-icon" aria-hidden="true">
69
- <Calendar.Icon.Calendar />
70
- </figure>
84
+ priority === "table" ? undefined : (
85
+ <figure className="input-date-trigger-icon" aria-hidden="true">
86
+ <Calendar.Icon.Calendar />
87
+ </figure>
88
+ )
71
89
  }
72
90
  />
73
91
  );
@@ -29,15 +29,16 @@ import InputBaseUtil from "./Utility";
29
29
  *
30
30
  * @component
31
31
  * @param {InputProps} props Input 컴포넌트 공통 props
32
- * @param {"primary" | "secondary" | "tertiary"} [props.priority="primary"] 디자인 토큰 우선순위
32
+ * @param {"primary" | "secondary" | "tertiary" | "table"} [props.priority="primary"] 디자인 토큰 우선순위
33
33
  * @param {"small" | "medium" | "large"} [props.size="medium"] 높이/타이포 세트
34
- * @param {"default" | "active" | "focused" | "success" | "error" | "disabled"} [props.state="default"] 시각 상태
35
- * @param {boolean} [props.block=false] true면 width 100%
34
+ * @param {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} [props.state="default"] 시각 상태
35
+ * @param {boolean} [props.block=false] true면 width 100% (`priority="table"`은 width 미지정 시 full 기본)
36
+ * @param {FormFieldWidth} [props.width] width preset
36
37
  * @param {React.ReactNode} [props.left] 입력 왼쪽 슬롯(아이콘/텍스트)
37
38
  * @param {React.ReactNode} [props.right] 입력 오른쪽 슬롯
38
- * @param {React.ReactNode} [props.clearIcon] 입력값 초기화 아이콘. 지정하지 않으면 기본 Reset 아이콘
39
- * @param {React.ReactNode} [props.successIcon] success 상태 아이콘 override
40
- * @param {React.ReactNode} [props.errorIcon] error 상태 아이콘 override
39
+ * @param {React.ReactNode} [props.clear] 입력값 초기화 아이콘. 지정하지 않으면 기본 Reset 아이콘
40
+ * @param {React.ReactNode} [props.success] success 상태 아이콘 override
41
+ * @param {React.ReactNode} [props.error] error 상태 아이콘 override
41
42
  * @param {string} [props.inputClassName] 실제 `<input>` 요소 className
42
43
  * @param {string} [props.boxClassName] `.input-box` className
43
44
  * @param {boolean} [props.disabled] native disabled
@@ -52,6 +53,7 @@ import InputBaseUtil from "./Utility";
52
53
  * @param {string | number | readonly string[]} [props.value] 제어형 값
53
54
  * @param {string | number | readonly string[]} [props.defaultValue] 비제어 초기값
54
55
  * @param {string} [props.type="text"] native input type
56
+ * @param {boolean} [props.readOnly] native readOnly
55
57
  */
56
58
  const InputBase = forwardRef<HTMLInputElement, InputProps>(
57
59
  (
@@ -84,6 +86,10 @@ const InputBase = forwardRef<HTMLInputElement, InputProps>(
84
86
  },
85
87
  ref,
86
88
  ) => {
89
+ // table priority는 width 지정이 없으면 셀 가로폭을 기본으로 채운다.
90
+ const isTablePriority = priority === "table";
91
+ const resolvedBlock = block || (isTablePriority && width === undefined);
92
+
87
93
  const generatedId = useId();
88
94
  const registerRef = register?.ref;
89
95
  const registerOnChange = register?.onChange;
@@ -174,7 +180,7 @@ const InputBase = forwardRef<HTMLInputElement, InputProps>(
174
180
  const widthAttr =
175
181
  width !== undefined
176
182
  ? getFormFieldWidthAttr(width)
177
- : block
183
+ : resolvedBlock
178
184
  ? "full"
179
185
  : undefined;
180
186
  const widthValue =
@@ -191,13 +197,13 @@ const InputBase = forwardRef<HTMLInputElement, InputProps>(
191
197
  `input-priority-${priority}`,
192
198
  `input-size-${size}`,
193
199
  `input-state-${visualState}`,
194
- block && "input-block",
200
+ resolvedBlock && "input-block",
195
201
  className,
196
202
  )}
197
203
  data-priority={priority}
198
204
  data-size={size}
199
205
  data-state={visualState}
200
- data-block={block ? "true" : undefined}
206
+ data-block={resolvedBlock ? "true" : undefined}
201
207
  {...(simulatedState ? { "data-simulated-state": simulatedState } : {})}
202
208
  data-width={widthAttr}
203
209
  style={containerStyle}
@@ -208,7 +214,7 @@ const InputBase = forwardRef<HTMLInputElement, InputProps>(
208
214
  `input-box-priority-${priority}`,
209
215
  `input-box-size-${size}`,
210
216
  `input-box-state-${visualState}`,
211
- block && "input-box-block",
217
+ resolvedBlock && "input-box-block",
212
218
  boxClassNameProp,
213
219
  )}
214
220
  data-slot="box"
@@ -218,7 +224,7 @@ const InputBase = forwardRef<HTMLInputElement, InputProps>(
218
224
  data-state={visualState}
219
225
  data-priority={priority}
220
226
  data-size={size}
221
- data-block={block ? "true" : undefined}
227
+ data-block={resolvedBlock ? "true" : undefined}
222
228
  >
223
229
  {/* 필드 컨트롤 wrapper; 슬롯과 input 정렬 */}
224
230
  <div className="input-field-control">
@@ -242,6 +248,8 @@ const InputBase = forwardRef<HTMLInputElement, InputProps>(
242
248
  />
243
249
  </div>
244
250
  <InputBaseUtil
251
+ // table priority는 success/error 상태 아이콘 대신 border만으로 상태를 표현한다.
252
+ priority={priority}
245
253
  state={currentState}
246
254
  isDisabled={isDisabled}
247
255
  isFocused={isFocused}
@@ -9,23 +9,25 @@ import InputBaseSideSlot from "./SideSlot";
9
9
  /**
10
10
  * Input; 오른쪽 유틸리티 영역(wrapper)
11
11
  * @component
12
- * @param {InputUtilProps} props
13
- * @param {React.ReactNode} [props.right] 오른쪽 슬롯 콘텐츠
12
+ * @param {InputUtilityProps} props
13
+ * @param {React.ReactNode} [props.children] 오른쪽 슬롯 콘텐츠
14
14
  * @param {React.ReactNode} [props.clear] clear 버튼 아이콘
15
15
  * @param {React.ReactNode} [props.success] success 상태 아이콘 override
16
16
  * @param {React.ReactNode} [props.error] error 상태 아이콘 override
17
- * @param {string} props.state 현재 input 상태
17
+ * @param {"primary" | "secondary" | "tertiary" | "table"} props.priority input priority
18
+ * @param {"default" | "active" | "focused" | "success" | "error" | "disabled" | "loading"} props.state 현재 input 상태
18
19
  * @param {boolean} props.isDisabled disable 여부
19
20
  * @param {boolean} props.isFocused focus 여부
20
21
  * @param {boolean} props.hasValue 입력값 존재 여부
21
22
  * @param {boolean} [props.readOnly] readOnly 여부
22
- * @param {Function} [props.onClear] clear 버튼 클릭 핸들러
23
+ * @param {(event: React.MouseEvent<HTMLButtonElement> | React.PointerEvent<HTMLButtonElement>) => void} [props.onClear] clear 버튼 클릭 핸들러
23
24
  */
24
25
  export default function InputBaseUtil({
25
26
  children: right,
26
27
  clear,
27
28
  success,
28
29
  error,
30
+ priority,
29
31
  state,
30
32
  isDisabled,
31
33
  isFocused,
@@ -42,6 +44,8 @@ export default function InputBaseUtil({
42
44
  : state === "error"
43
45
  ? (error ?? baseStatusIcon)
44
46
  : null;
47
+ // table priority는 상태를 border로만 표현하므로 status icon을 렌더하지 않는다.
48
+ const resolvedStatusIcon = priority === "table" ? null : statusIcon;
45
49
  const clearIconNode = clear ?? InputStatusIcon.reset;
46
50
  const showClearIcon = Boolean(
47
51
  clearIconNode &&
@@ -50,7 +54,7 @@ export default function InputBaseUtil({
50
54
  (isFocused || isClearInteracting),
51
55
  );
52
56
 
53
- if (!right && !showClearIcon && !statusIcon) {
57
+ if (!right && !showClearIcon && !resolvedStatusIcon) {
54
58
  return null;
55
59
  }
56
60
 
@@ -91,13 +95,13 @@ export default function InputBaseUtil({
91
95
  {clearIconNode}
92
96
  </button>
93
97
  ) : null}
94
- {statusIcon ? (
98
+ {resolvedStatusIcon ? (
95
99
  <div
96
100
  className="input-affix input-affix-status"
97
101
  data-slot="status"
98
102
  data-state={state}
99
103
  >
100
- {statusIcon}
104
+ {resolvedStatusIcon}
101
105
  </div>
102
106
  ) : null}
103
107
  </div>
@@ -21,6 +21,27 @@
21
21
  pointer-events: none;
22
22
  }
23
23
 
24
+ .input-date-trigger-input.input-priority-table {
25
+ .input-field {
26
+ padding-inline: var(--spacing-padding-4);
27
+ }
28
+
29
+ .input-field-control {
30
+ gap: var(--spacing-gap-3);
31
+ }
32
+
33
+ .input-element {
34
+ font-size: var(--font-body-xsmall-size);
35
+ line-height: var(--font-body-xsmall-line-height);
36
+ font-weight: var(--font-body-xsmall-weight);
37
+ }
38
+
39
+ .input-date-trigger-icon {
40
+ // input date 아이콘 크기는 size/priority와 무관하게 공통 크기를 유지한다.
41
+ color: var(--color-label-alternative);
42
+ }
43
+ }
44
+
24
45
  .input-date-footer-template {
25
46
  // footer 템플릿은 input 스킨에서만 관리한다.
26
47
  display: grid;
@@ -154,6 +154,13 @@
154
154
  }
155
155
  }
156
156
 
157
+ &[data-priority="table"] {
158
+ // table priority는 default에서 border를 숨기고 상태(border-color)만 드러낸다.
159
+ border-radius: var(--input-table-radius-base);
160
+ border-color: var(--input-border-table-default-color);
161
+ background-color: transparent;
162
+ }
163
+
157
164
  &:not([data-priority="secondary"]) {
158
165
  &[data-state="active"],
159
166
  &[data-state="focused"] {
@@ -252,6 +259,24 @@
252
259
  min-height: var(--theme-size-medium-2);
253
260
  }
254
261
 
262
+ .input-field[data-priority="table"][data-size="small"] .input-element {
263
+ font-size: var(--input-table-text-small-size);
264
+ line-height: var(--input-table-text-small-line-height);
265
+ font-weight: var(--input-table-text-small-weight);
266
+ }
267
+
268
+ .input-field[data-priority="table"][data-size="medium"] .input-element {
269
+ font-size: var(--input-table-text-medium-size);
270
+ line-height: var(--input-table-text-medium-line-height);
271
+ font-weight: var(--input-table-text-medium-weight);
272
+ }
273
+
274
+ .input-field[data-priority="table"][data-size="large"] .input-element {
275
+ font-size: var(--input-table-text-large-size);
276
+ line-height: var(--input-table-text-large-line-height);
277
+ font-weight: var(--input-table-text-large-weight);
278
+ }
279
+
255
280
  .input-helper-text {
256
281
  color: var(--input-helper-color);
257
282
  font-size: var(--font-label-small-size);
@@ -362,6 +387,11 @@
362
387
  &[data-priority="secondary"] {
363
388
  background-color: transparent;
364
389
  }
390
+
391
+ &[data-priority="table"] {
392
+ // table priority는 disabled 상태에서도 배경을 투명하게 유지한다.
393
+ background-color: transparent;
394
+ }
365
395
  }
366
396
  }
367
397
 
@@ -13,6 +13,16 @@
13
13
  --input-default-gap: var(--spacing-gap-4);
14
14
  --input-default-radius-base: var(--theme-radius-large-1);
15
15
  --input-tertiary-radius-base: var(--theme-radius-large-2);
16
+ --input-table-radius-base: 0;
17
+ --input-table-text-small-size: var(--font-body-xxsmall-size);
18
+ --input-table-text-small-line-height: var(--font-body-xxsmall-line-height);
19
+ --input-table-text-small-weight: var(--font-body-xxsmall-weight);
20
+ --input-table-text-medium-size: var(--font-body-xsmall-size);
21
+ --input-table-text-medium-line-height: var(--font-body-xsmall-line-height);
22
+ --input-table-text-medium-weight: var(--font-body-xsmall-weight);
23
+ --input-table-text-large-size: var(--font-body-small-size);
24
+ --input-table-text-large-line-height: var(--font-body-small-line-height);
25
+ --input-table-text-large-weight: var(--font-body-small-weight);
16
26
 
17
27
  /* Label/helper colors */
18
28
  --input-label-color: var(--color-label-standard);
@@ -33,6 +43,7 @@
33
43
  --input-border-width-emphasis: 1.4px;
34
44
  --input-border-active-color: var(--color-blue-80);
35
45
  --input-border-success-color: var(--color-blue-80);
46
+ --input-border-table-default-color: transparent;
36
47
  /* error는 Figma 44% alpha */
37
48
  --input-border-error-color: rgba(218, 29, 11, 0.44); // --color-feedback-error
38
49
  --input-border-disabled-color: var(--color-border-standard-cool-gray);
@@ -7,6 +7,7 @@ import type {
7
7
  CalendarOnChange,
8
8
  CalendarValue,
9
9
  } from "../../calendar";
10
+ import type { InputPriority } from "./foundation";
10
11
 
11
12
  /**
12
13
  * Calendar trigger(Input) 영역에서 필요한 속성 묶음.
@@ -17,6 +18,7 @@ import type {
17
18
  * @property {(event: MouseEvent<Element>) => void} [onClick] trigger 클릭 핸들러
18
19
  * @property {boolean} [disabled] disabled 여부
19
20
  * @property {string} [placeholder] trigger 내부 placeholder
21
+ * @property {InputPriority} [priority] trigger input priority
20
22
  */
21
23
  export interface InputCalendarTriggerProps {
22
24
  /**
@@ -43,6 +45,11 @@ export interface InputCalendarTriggerProps {
43
45
  * trigger 내부 placeholder
44
46
  */
45
47
  placeholder?: string;
48
+ /**
49
+ * trigger input priority
50
+ * - table일 때 Trigger는 left icon 배치를 사용한다.
51
+ */
52
+ priority?: InputPriority;
46
53
  }
47
54
 
48
55
  /**
@@ -69,6 +76,7 @@ export interface InputCalendarTriggerViewProps extends InputCalendarTriggerProps
69
76
  * @property {string} [placeholder] 트리거 placeholder
70
77
  * @property {boolean} [disabled] disabled 여부
71
78
  * @property {(event: MouseEvent<Element>) => void} [onClick] 트리거 클릭 핸들러
79
+ * @property {InputPriority} [priority] trigger input priority
72
80
  */
73
81
  export interface InputCalendarTriggerRenderProps {
74
82
  /**
@@ -95,6 +103,11 @@ export interface InputCalendarTriggerRenderProps {
95
103
  * 트리거 클릭 핸들러
96
104
  */
97
105
  onClick?: (event: MouseEvent<Element>) => void;
106
+ /**
107
+ * trigger input priority
108
+ * - table일 때 Trigger는 left icon 배치를 사용한다.
109
+ */
110
+ priority?: InputPriority;
98
111
  }
99
112
 
100
113
  /**
@@ -108,6 +121,7 @@ export interface InputCalendarTriggerRenderProps {
108
121
  * @property {boolean} [readOnly] 읽기 전용 여부
109
122
  * @property {boolean} [disabled] disabled 여부
110
123
  * @property {CalendarDatePickerProps} [datePickerProps] Mantine DatePicker 직접 옵션
124
+ * @property {InputPriority} [priority] trigger input priority
111
125
  * @property {ReactNode} [header] 커스텀 header 콘텐츠
112
126
  * @property {ReactNode} [footer] 커스텀 footer 콘텐츠
113
127
  * @property {unknown} [timePicker] TimePicker 확장용 예약 슬롯(현재 미구현)
@@ -168,6 +182,7 @@ export interface InputCalendarProps extends InputCalendarTriggerProps {
168
182
  timePicker?: unknown;
169
183
  /**
170
184
  * calendar 열림 제어 여부
185
+ * - 지정되면 제어형(open state controlled)으로 동작한다.
171
186
  */
172
187
  calendarOpened?: boolean;
173
188
  /**
@@ -10,7 +10,7 @@ import type { FormFieldWidth } from "../../form/types/props";
10
10
  /**
11
11
  * input; priority option
12
12
  */
13
- export type InputPriority = "primary" | "secondary" | "tertiary";
13
+ export type InputPriority = "primary" | "secondary" | "tertiary" | "table";
14
14
  /**
15
15
  * input; size option
16
16
  */
@@ -34,6 +34,7 @@ export const INPUT_PRIORITIES: InputPriority[] = [
34
34
  "primary",
35
35
  "secondary",
36
36
  "tertiary",
37
+ "table",
37
38
  ];
38
39
  /**
39
40
  * size 축은 높이/타이포/spacing을 결정한다.
@@ -56,11 +57,11 @@ type NativeInputProps = ComponentPropsWithoutRef<"input">;
56
57
 
57
58
  /**
58
59
  * input; icon options
59
- * @property {React.ReactNode} [left] input 왼쪽 컨텐츠
60
- * @property {React.ReactNode} [right] input 오른쪽 컨텐츠
61
- * @property {React.ReactNode} [clear] input reset버튼 커스텀 컨텐츠
62
- * @property {React.ReactNode} [success] input 입력상태 성공시 커스텀 컨텐츠
63
- * @property {React.ReactNode} [error] input 입력상태 에러시 커스텀 컨텐츠
60
+ * @property {ReactNode} [left] input 왼쪽 컨텐츠
61
+ * @property {ReactNode} [right] input 오른쪽 컨텐츠
62
+ * @property {ReactNode} [clear] input reset버튼 커스텀 컨텐츠
63
+ * @property {ReactNode} [success] input 입력상태 성공시 커스텀 컨텐츠
64
+ * @property {ReactNode} [error] input 입력상태 에러시 커스텀 컨텐츠
64
65
  */
65
66
  export interface InputIcon {
66
67
  /**
@@ -95,12 +96,13 @@ export interface InputIcon {
95
96
  * @property {string} [inputClassName]
96
97
  * @property {string} [boxClassName]
97
98
  * @property {UseFormRegisterReturn} [register]
98
- * @property {React.ReactNode} [left] input 왼쪽 컨텐츠
99
- * @property {React.ReactNode} [right] input 오른쪽 컨텐츠
100
- * @property {React.ReactNode} [clear] input reset버튼 커스텀 컨텐츠
101
- * @property {React.ReactNode} [success] input 입력상태 성공시 커스텀 컨텐츠
102
- * @property {React.ReactNode} [error] input 입력상태 에러시 커스텀 컨텐츠
99
+ * @property {ReactNode} [left] input 왼쪽 컨텐츠
100
+ * @property {ReactNode} [right] input 오른쪽 컨텐츠
101
+ * @property {ReactNode} [clear] input reset버튼 커스텀 컨텐츠
102
+ * @property {ReactNode} [success] input 입력상태 성공시 커스텀 컨텐츠
103
+ * @property {ReactNode} [error] input 입력상태 에러시 커스텀 컨텐츠
103
104
  * @property {FormFieldWidth} [width] width preset 옵션
105
+ * @property {InputState} [data-simulated-state] Storybook 시각 상태 강제용
104
106
  */
105
107
  export interface InputProps extends Omit<NativeInputProps, "size">, InputIcon {
106
108
  /**
@@ -147,6 +149,7 @@ export interface InputProps extends Omit<NativeInputProps, "size">, InputIcon {
147
149
  * @property {ReactNode} [clear] clear 버튼 아이콘
148
150
  * @property {ReactNode} [success] success 상태 아이콘
149
151
  * @property {ReactNode} [error] error 상태 아이콘
152
+ * @property {InputPriority} priority input priority
150
153
  * @property {InputState} state 현재 상태
151
154
  * @property {boolean} isDisabled disabled 여부
152
155
  * @property {boolean} isFocused focus 여부
@@ -171,6 +174,10 @@ export interface InputUtilityProps {
171
174
  * error 상태 아이콘
172
175
  */
173
176
  error?: ReactNode;
177
+ /**
178
+ * priority 축
179
+ */
180
+ priority: InputPriority;
174
181
  /**
175
182
  * 현재 input 상태
176
183
  */
@@ -51,7 +51,21 @@ export const serializeCalendarValue = (value: CalendarValue) => value ?? "";
51
51
  * @param {CalendarValue} value 현재 값
52
52
  * @returns {string} 표시 문자열
53
53
  */
54
- export const formatTriggerValue = (value: CalendarValue) => value ?? "";
54
+ export const formatTriggerValue = (
55
+ value: CalendarValue,
56
+ format = DATE_FORMAT,
57
+ ) => {
58
+ if (!value) {
59
+ return "";
60
+ }
61
+
62
+ const parsed = dayjs(value);
63
+ if (!parsed.isValid()) {
64
+ return value;
65
+ }
66
+
67
+ return parsed.format(format);
68
+ };
55
69
 
56
70
  /**
57
71
  * columns 값을 Mantine numberOfColumns와 맞춘다.
@@ -18,7 +18,7 @@ import type { SelectDefaultComponentProps } from "../types/props";
18
18
  * @param {SelectDropdownOption[]} [props.options] dropdown option 목록
19
19
  * @param {string[]} [props.selectedOptionIds] 선택된 option id 리스트
20
20
  * @param {(option: SelectDropdownOption) => void} [props.onOptionSelect] option 선택 콜백
21
- * @param {"primary" | "secondary"} [props.priority="primary"] priority 스케일
21
+ * @param {"primary" | "secondary" | "table"} [props.priority="primary"] priority 스케일
22
22
  * @param {"small" | "medium" | "large"} [props.size="medium"] size 스케일
23
23
  * @param {"default" | "focused" | "disabled"} [props.state="default"] 시각 상태
24
24
  * @param {boolean} [props.block] block 여부
@@ -65,6 +65,9 @@ const SelectDefault = forwardRef<HTMLElement, SelectDefaultComponentProps>(
65
65
  },
66
66
  ref,
67
67
  ) => {
68
+ // 변경: table priority는 width 미지정 시 기본 full width를 사용한다.
69
+ const resolvedBlock =
70
+ block || (priority === "table" && width === undefined);
68
71
  const resolvedSelectedIds = selectedOptionIds ?? [];
69
72
 
70
73
  const resolvedDisplayLabel =
@@ -96,7 +99,7 @@ const SelectDefault = forwardRef<HTMLElement, SelectDefaultComponentProps>(
96
99
  return (
97
100
  <Container
98
101
  className={clsx("select-trigger-container", className)}
99
- block={block}
102
+ block={resolvedBlock}
100
103
  width={width}
101
104
  >
102
105
  <Dropdown.Root
@@ -112,7 +115,7 @@ const SelectDefault = forwardRef<HTMLElement, SelectDefaultComponentProps>(
112
115
  priority={priority}
113
116
  size={size}
114
117
  state={disabled ? "disabled" : state}
115
- block={block}
118
+ block={resolvedBlock}
116
119
  open={dropdownOpen}
117
120
  disabled={disabled}
118
121
  buttonType={buttonType}
@@ -11,7 +11,7 @@ import type { SelectTriggerBaseProps } from "../../types/trigger";
11
11
  * Select Foundation; Trigger Base 슬롯 렌더링 컴포넌트
12
12
  * @component
13
13
  * @param {SelectTriggerBaseProps} props trigger base props
14
- * @param {"primary" | "secondary"} [props.priority="primary"] 스타일 우선순위
14
+ * @param {"primary" | "secondary" | "table"} [props.priority="primary"] 스타일 우선순위
15
15
  * @param {"small" | "medium" | "large"} [props.size="medium"] 높이 스케일
16
16
  * @param {"default" | "focused" | "disabled"} [props.state="default"] 시각 상태
17
17
  * @param {boolean} [props.open=false] dropdown open 상태
@@ -41,10 +41,13 @@ const SelectChevronSecondaryIcon: SelectIconSizeMap = {
41
41
  * Select; Chevron 아이콘 컬렉션
42
42
  * - primary (small, medium, large)
43
43
  * - secondary (small, medium, large)
44
+ * - table (small, medium, large)
44
45
  */
45
46
  const SelectChevronIcon: SelectIconPriorityMap = {
46
47
  primary: SelectChevronPrimaryIcon,
47
48
  secondary: SelectChevronSecondaryIcon,
49
+ // 변경: table priority는 secondary chevron 자산을 재사용한다.
50
+ table: SelectChevronSecondaryIcon,
48
51
  };
49
52
 
50
53
  /**
@@ -61,10 +64,12 @@ const SelectMultipleRemoveIcon: SelectIconSizeMap = {
61
64
 
62
65
  /**
63
66
  * Select; Remove 아이콘 컬렉션
64
- * - primary (small, medium, large)
67
+ * - primary/secondary/table (small, medium, large)
65
68
  */
66
69
  const SelectRemoveIcon: SelectIconRemovePriorityMap = {
67
70
  primary: SelectMultipleRemoveIcon,
71
+ secondary: SelectMultipleRemoveIcon,
72
+ table: SelectMultipleRemoveIcon,
68
73
  };
69
74
 
70
75
  /**
@@ -22,7 +22,7 @@ import { useSelectDropdownOpenState } from "../../hooks";
22
22
  * @param {(option: SelectDropdownOption) => void} [props.onOptionSelect] option 선택 콜백
23
23
  * @param {React.ReactNode} [props.displayLabel] fallback 라벨
24
24
  * @param {React.ReactNode} [props.placeholder] placeholder 텍스트
25
- * @param {"primary" | "secondary"} [props.priority="primary"] priority scale
25
+ * @param {"primary" | "secondary" | "table"} [props.priority="primary"] priority scale
26
26
  * @param {"small" | "medium" | "large"} [props.size="medium"] size scale
27
27
  * @param {"default" | "focused" | "disabled"} [props.state="default"] 시각 상태
28
28
  * @param {boolean} [props.block] block 여부
@@ -72,6 +72,9 @@ const SelectMultipleTrigger = forwardRef<
72
72
  },
73
73
  ref,
74
74
  ) => {
75
+ // 변경: table priority는 width 미지정 시 기본 full width를 사용한다.
76
+ const resolvedBlock =
77
+ block || (priority === "table" && width === undefined);
75
78
  // hook dependency 안정화를 위해 memoized selected id 배열을 유지한다.
76
79
  const resolvedSelectedIds = useMemo(
77
80
  () => selectedOptionIds ?? [],
@@ -128,7 +131,7 @@ const SelectMultipleTrigger = forwardRef<
128
131
  return (
129
132
  <Container
130
133
  className={clsx("select-trigger-multiple", className)}
131
- block={block}
134
+ block={resolvedBlock}
132
135
  width={width}
133
136
  >
134
137
  <Dropdown.Root
@@ -144,7 +147,7 @@ const SelectMultipleTrigger = forwardRef<
144
147
  priority={priority}
145
148
  size={size}
146
149
  state={disabled ? "disabled" : state}
147
- block={block}
150
+ block={resolvedBlock}
148
151
  open={dropdownOpen}
149
152
  multiple
150
153
  disabled={disabled}