@uniai-fe/uds-primitives 0.3.54 → 0.3.55

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/dist/styles.css CHANGED
@@ -594,6 +594,7 @@
594
594
  );
595
595
  --select-primary-color-surface: var(--input-surface-color);
596
596
  --select-primary-color-text: var(--color-label-alternative);
597
+ --select-color-text-value: var(--color-label-strong);
597
598
  --select-primary-color-text-focused: var(--color-label-strong);
598
599
  --select-primary-color-text-disabled: var(--color-label-disabled);
599
600
  --select-primary-color-text-readonly: var(--color-label-strong);
@@ -2777,6 +2778,11 @@ figure.chip {
2777
2778
  box-shadow: none;
2778
2779
  }
2779
2780
 
2781
+ .input-field[data-has-value=true] .input-element {
2782
+ color: var(--input-text-color);
2783
+ caret-color: var(--input-text-color);
2784
+ }
2785
+
2780
2786
  .input-field-control {
2781
2787
  display: flex;
2782
2788
  align-items: center;
@@ -2809,9 +2815,24 @@ figure.chip {
2809
2815
  --input-textarea-gap: var(--input-textarea-gap-large);
2810
2816
  }
2811
2817
 
2818
+ .input[data-input-type=textarea] {
2819
+ min-height: var(--input-textarea-height);
2820
+ }
2821
+ .input[data-input-type=textarea] .input-box {
2822
+ height: 100%;
2823
+ }
2824
+ .input[data-input-type=textarea] .input-field {
2825
+ height: 100%;
2826
+ }
2827
+ .input[data-input-type=textarea] .input-field-control {
2828
+ flex: 1 1 auto;
2829
+ width: 100%;
2830
+ align-items: stretch;
2831
+ }
2832
+
2812
2833
  .input-textarea-element {
2813
2834
  min-height: var(--input-textarea-min-height);
2814
- height: var(--input-textarea-height);
2835
+ height: 100%;
2815
2836
  resize: none;
2816
2837
  margin: 0;
2817
2838
  }
@@ -4046,9 +4067,6 @@ figure.chip {
4046
4067
  color: var(--select-primary-color-text-readonly);
4047
4068
  caret-color: var(--select-primary-color-text-readonly);
4048
4069
  }
4049
- .select-button:where([data-state=focused]) .select-input-label, .select-button:where(:focus-within) .select-input-label {
4050
- color: var(--color-label-strong);
4051
- }
4052
4070
  .select-button:where([data-priority=secondary]):where([data-state=disabled]) .select-input-label {
4053
4071
  color: var(--select-secondary-color-text-disabled);
4054
4072
  }
@@ -4056,6 +4074,11 @@ figure.chip {
4056
4074
  color: var(--select-secondary-color-text-readonly);
4057
4075
  }
4058
4076
 
4077
+ .select-value[data-has-value=true] .select-input-label {
4078
+ color: var(--select-color-text-value);
4079
+ caret-color: var(--select-color-text-value);
4080
+ }
4081
+
4059
4082
  .select-input-label-placeholder {
4060
4083
  color: var(--select-primary-color-placeholder);
4061
4084
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-primitives",
3
- "version": "0.3.54",
3
+ "version": "0.3.55",
4
4
  "description": "UNIAI Design System; Primitives Components Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -15,7 +15,7 @@
15
15
  "publishConfig": {
16
16
  "access": "public"
17
17
  },
18
- "packageManager": "pnpm@10.30.3",
18
+ "packageManager": "pnpm@10.32.0",
19
19
  "engines": {
20
20
  "node": ">=24",
21
21
  "pnpm": ">=10"
@@ -89,9 +89,9 @@
89
89
  "react-daum-postcode": "^4.0.0"
90
90
  },
91
91
  "devDependencies": {
92
- "@mantine/core": "^8.3.15",
93
- "@mantine/dates": "^8.3.15",
94
- "@mantine/hooks": "^8.3.15",
92
+ "@mantine/core": "^8.3.16",
93
+ "@mantine/dates": "^8.3.16",
94
+ "@mantine/hooks": "^8.3.16",
95
95
  "@svgr/webpack": "^8.1.0",
96
96
  "@types/node": "^24.10.2",
97
97
  "@types/react": "^19.2.14",
@@ -243,6 +243,8 @@ const InputBase = forwardRef<HTMLInputElement, InputProps>(
243
243
  data-state={visualState}
244
244
  data-priority={priority}
245
245
  data-size={resolvedSize}
246
+ // 변경: 값 존재 여부를 DOM state로 노출해 상태 색상과 분리된 텍스트 규칙을 적용한다.
247
+ data-has-value={hasValue ? "true" : "false"}
246
248
  data-readonly={isReadOnly ? "true" : undefined}
247
249
  data-block={resolvedBlock ? "true" : undefined}
248
250
  >
@@ -279,6 +279,11 @@
279
279
  }
280
280
  }
281
281
 
282
+ .input-field[data-has-value="true"] .input-element {
283
+ color: var(--input-text-color);
284
+ caret-color: var(--input-text-color);
285
+ }
286
+
282
287
  .input-field-control {
283
288
  display: flex;
284
289
  align-items: center;
@@ -315,9 +320,28 @@
315
320
  }
316
321
  }
317
322
 
323
+ // 변경: textarea 높이 토큰은 native element가 아니라 root layout이 소유하고 내부는 남은 높이를 채운다.
324
+ .input[data-input-type="textarea"] {
325
+ min-height: var(--input-textarea-height);
326
+
327
+ .input-box {
328
+ height: 100%;
329
+ }
330
+
331
+ .input-field {
332
+ height: 100%;
333
+ }
334
+
335
+ .input-field-control {
336
+ flex: 1 1 auto;
337
+ width: 100%;
338
+ align-items: stretch;
339
+ }
340
+ }
341
+
318
342
  .input-textarea-element {
319
343
  min-height: var(--input-textarea-min-height);
320
- height: var(--input-textarea-height);
344
+ height: 100%;
321
345
  resize: none;
322
346
  margin: 0;
323
347
  }
@@ -302,6 +302,7 @@ export function SelectDefault<OptionData = unknown>({
302
302
  ? labelInputValue
303
303
  : (resolvedSelectedOption?.value ?? "")
304
304
  }
305
+ valueStateSource={isCustomInputActive ? "label" : "hidden"}
305
306
  // 변경: custom mode 진입 시 label input에 focus를 연결한다.
306
307
  shouldFocusInput={isCustomInputActive}
307
308
  onLabelChange={setCustomLabelValue}
@@ -48,6 +48,7 @@ const SelectTriggerSelected = ({
48
48
  inputProps,
49
49
  valueText = "",
50
50
  valueFieldValue = "",
51
+ valueStateSource = "label",
51
52
  shouldFocusInput = false,
52
53
  onLabelChange,
53
54
  }: SelectSelectedProps) => {
@@ -60,12 +61,26 @@ const SelectTriggerSelected = ({
60
61
  const resolvedInputValueText = toInputText(inputProps?.value as ReactNode);
61
62
  const resolvedLabelText =
62
63
  valueText || resolvedInputValueText || toInputText(label);
64
+ // 변경 설명: 색상 계약은 hidden value 또는 label value 중 지정된 source를 기준으로 계산한다.
65
+ const resolvedHiddenValueText =
66
+ typeof valueFieldValue === "string" || typeof valueFieldValue === "number"
67
+ ? String(valueFieldValue)
68
+ : "";
69
+ const hasDisplayValue =
70
+ valueStateSource === "hidden"
71
+ ? resolvedHiddenValueText.length > 0
72
+ : resolvedLabelText.length > 0;
63
73
  const isLabelTextLike =
64
74
  typeof label === "string" || typeof label === "number";
75
+ const shouldUsePlaceholderStyle = !hasDisplayValue;
65
76
  // 변경 설명: readOnly에서 non-text label을 직접 렌더링하되, placeholder 상태일 때는 input placeholder 경로를 유지한다.
66
77
  // custom mode처럼 valueText가 있는 경우에는 input 렌더 경로를 유지해 값 표시를 보장한다.
67
78
  const shouldRenderReadonlyLabelNode =
68
- readOnly && !isLabelTextLike && !isPlaceholder && !resolvedLabelText;
79
+ readOnly &&
80
+ !isLabelTextLike &&
81
+ !isPlaceholder &&
82
+ !shouldUsePlaceholderStyle &&
83
+ !resolvedLabelText;
69
84
 
70
85
  // 3) custom mode 활성 시에만 label input focus를 부여한다.
71
86
  useEffect(() => {
@@ -77,13 +92,17 @@ const SelectTriggerSelected = ({
77
92
  }, [readOnly, shouldFocusInput]);
78
93
 
79
94
  return (
80
- <div className="select-value">
95
+ <div
96
+ className="select-value"
97
+ data-has-value={hasDisplayValue ? "true" : "false"}
98
+ >
81
99
  {shouldRenderReadonlyLabelNode ? (
82
100
  // 변경 설명: readonly + ReactNode label일 때는 문자열 변환 없이 label 노드를 직접 렌더링한다.
83
101
  <div
84
102
  className={clsx("select-input-label", inputProps?.className, {
85
- "select-input-label-placeholder": isPlaceholder,
103
+ "select-input-label-placeholder": shouldUsePlaceholderStyle,
86
104
  })}
105
+ data-has-value={hasDisplayValue ? "true" : "false"}
87
106
  >
88
107
  {label}
89
108
  </div>
@@ -95,8 +114,9 @@ const SelectTriggerSelected = ({
95
114
  // 내부 계약값을 마지막에 고정해 동작 우선순위를 명확히 한다.
96
115
  {...inputProps}
97
116
  className={clsx("select-input-label", inputProps?.className, {
98
- "select-input-label-placeholder": isPlaceholder,
117
+ "select-input-label-placeholder": shouldUsePlaceholderStyle,
99
118
  })}
119
+ data-has-value={hasDisplayValue ? "true" : "false"}
100
120
  placeholder={resolvedPlaceholder}
101
121
  value={resolvedLabelText}
102
122
  readOnly={readOnly}
@@ -151,6 +171,7 @@ const SelectTriggerSelected = ({
151
171
  type="hidden"
152
172
  className="select-input-value"
153
173
  value={valueFieldValue}
174
+ data-has-value={resolvedHiddenValueText.length > 0 ? "true" : "false"}
154
175
  {...register}
155
176
  />
156
177
  </div>
@@ -281,11 +281,6 @@
281
281
  color: var(--select-primary-color-text-readonly);
282
282
  caret-color: var(--select-primary-color-text-readonly);
283
283
  }
284
- // 변경: legacy label 텍스트 색상 규칙을 input 렌더 구조로 이관한다.
285
- .select-button:where([data-state="focused"]) &,
286
- .select-button:where(:focus-within) & {
287
- color: var(--color-label-strong);
288
- }
289
284
 
290
285
  .select-button:where([data-priority="secondary"]):where(
291
286
  [data-state="disabled"]
@@ -302,6 +297,11 @@
302
297
  }
303
298
  }
304
299
 
300
+ .select-value[data-has-value="true"] .select-input-label {
301
+ color: var(--select-color-text-value);
302
+ caret-color: var(--select-color-text-value);
303
+ }
304
+
305
305
  .select-input-label-placeholder {
306
306
  color: var(--select-primary-color-placeholder);
307
307
 
@@ -99,6 +99,7 @@
99
99
  );
100
100
  --select-primary-color-surface: var(--input-surface-color);
101
101
  --select-primary-color-text: var(--color-label-alternative);
102
+ --select-color-text-value: var(--color-label-strong);
102
103
  --select-primary-color-text-focused: var(--color-label-strong);
103
104
  --select-primary-color-text-disabled: var(--color-label-disabled);
104
105
  --select-primary-color-text-readonly: var(--color-label-strong);
@@ -159,6 +159,7 @@ export interface SelectTriggerMultipleProps extends SelectTriggerBaseFoundationP
159
159
  * @property {ComponentPropsWithoutRef<"input">} [inputProps] label input native 속성
160
160
  * @property {string} [valueText] label input 표시 문자열
161
161
  * @property {SelectOptionValue | ""} [valueFieldValue] hidden value input 값
162
+ * @property {"label" | "hidden"} [valueStateSource] 표시값 존재 여부를 판정할 source
162
163
  * @property {boolean} [shouldFocusInput] custom mode 진입 시 label input focus 여부
163
164
  * @property {(value: string) => void} [onLabelChange] label 입력 변경 콜백
164
165
  */
@@ -200,6 +201,12 @@ export interface SelectSelectedProps {
200
201
  * hidden value input 값
201
202
  */
202
203
  valueFieldValue?: SelectOptionValue | "";
204
+ /**
205
+ * 표시값 존재 여부를 판정할 source
206
+ * - label: select-input-label value 기준
207
+ * - hidden: select-input-value value 기준
208
+ */
209
+ valueStateSource?: "label" | "hidden";
203
210
  /**
204
211
  * custom mode 진입 시 label input focus 여부
205
212
  */