@uniai-fe/uds-primitives 0.3.54 → 0.3.56
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 +30 -8
- package/package.json +5 -5
- package/src/components/input/markup/foundation/Input.tsx +2 -0
- package/src/components/input/markup/foundation/TextArea.tsx +1 -0
- package/src/components/input/styles/foundation.scss +25 -1
- package/src/components/input/styles/variables.scss +1 -2
- package/src/components/select/markup/Default.tsx +1 -0
- package/src/components/select/markup/foundation/Selected.tsx +25 -4
- package/src/components/select/styles/select.scss +7 -5
- package/src/components/select/styles/variables.scss +3 -2
- package/src/components/select/types/trigger.ts +7 -0
package/dist/styles.css
CHANGED
|
@@ -428,8 +428,7 @@
|
|
|
428
428
|
--input-text-disabled-color: var(--color-label-disabled);
|
|
429
429
|
--input-placeholder-color: var(--color-label-alternative);
|
|
430
430
|
--input-placeholder-disabled-color: var(--color-label-disabled);
|
|
431
|
-
|
|
432
|
-
--input-placeholder-readonly-color: transparent;
|
|
431
|
+
--input-placeholder-readonly-color: var(--input-placeholder-color);
|
|
433
432
|
--input-font-size: var(--input-text-medium-size);
|
|
434
433
|
--input-line-height: var(--input-text-medium-line-height);
|
|
435
434
|
--input-font-weight: var(--input-text-medium-weight);
|
|
@@ -590,10 +589,11 @@
|
|
|
590
589
|
/* 변경: placeholder disabled/readonly 토큰을 분리해 상태별 제어 지점을 고정한다. */
|
|
591
590
|
--select-primary-color-placeholder-disabled: var(--color-label-disabled);
|
|
592
591
|
--select-primary-color-placeholder-readonly: var(
|
|
593
|
-
--select-primary-color-placeholder
|
|
592
|
+
--select-primary-color-placeholder
|
|
594
593
|
);
|
|
595
594
|
--select-primary-color-surface: var(--input-surface-color);
|
|
596
595
|
--select-primary-color-text: var(--color-label-alternative);
|
|
596
|
+
--select-color-text-value: var(--color-label-strong);
|
|
597
597
|
--select-primary-color-text-focused: var(--color-label-strong);
|
|
598
598
|
--select-primary-color-text-disabled: var(--color-label-disabled);
|
|
599
599
|
--select-primary-color-text-readonly: var(--color-label-strong);
|
|
@@ -617,7 +617,7 @@
|
|
|
617
617
|
--select-secondary-color-placeholder: var(--color-label-alternative);
|
|
618
618
|
--select-secondary-color-placeholder-disabled: var(--color-label-disabled);
|
|
619
619
|
--select-secondary-color-placeholder-readonly: var(
|
|
620
|
-
--select-secondary-color-placeholder
|
|
620
|
+
--select-secondary-color-placeholder
|
|
621
621
|
);
|
|
622
622
|
--select-color-surface-secondary: transparent;
|
|
623
623
|
--select-color-surface-secondary-hover: var(--color-surface-static-cool-gray);
|
|
@@ -2777,6 +2777,11 @@ figure.chip {
|
|
|
2777
2777
|
box-shadow: none;
|
|
2778
2778
|
}
|
|
2779
2779
|
|
|
2780
|
+
.input-field[data-has-value=true] .input-element:not(:disabled) {
|
|
2781
|
+
color: var(--input-text-color);
|
|
2782
|
+
caret-color: var(--input-text-color);
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2780
2785
|
.input-field-control {
|
|
2781
2786
|
display: flex;
|
|
2782
2787
|
align-items: center;
|
|
@@ -2809,9 +2814,24 @@ figure.chip {
|
|
|
2809
2814
|
--input-textarea-gap: var(--input-textarea-gap-large);
|
|
2810
2815
|
}
|
|
2811
2816
|
|
|
2817
|
+
.input[data-input-type=textarea] {
|
|
2818
|
+
min-height: var(--input-textarea-height);
|
|
2819
|
+
}
|
|
2820
|
+
.input[data-input-type=textarea] .input-box {
|
|
2821
|
+
height: 100%;
|
|
2822
|
+
}
|
|
2823
|
+
.input[data-input-type=textarea] .input-field {
|
|
2824
|
+
height: 100%;
|
|
2825
|
+
}
|
|
2826
|
+
.input[data-input-type=textarea] .input-field-control {
|
|
2827
|
+
flex: 1 1 auto;
|
|
2828
|
+
width: 100%;
|
|
2829
|
+
align-items: stretch;
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2812
2832
|
.input-textarea-element {
|
|
2813
2833
|
min-height: var(--input-textarea-min-height);
|
|
2814
|
-
height:
|
|
2834
|
+
height: 100%;
|
|
2815
2835
|
resize: none;
|
|
2816
2836
|
margin: 0;
|
|
2817
2837
|
}
|
|
@@ -4046,9 +4066,6 @@ figure.chip {
|
|
|
4046
4066
|
color: var(--select-primary-color-text-readonly);
|
|
4047
4067
|
caret-color: var(--select-primary-color-text-readonly);
|
|
4048
4068
|
}
|
|
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
4069
|
.select-button:where([data-priority=secondary]):where([data-state=disabled]) .select-input-label {
|
|
4053
4070
|
color: var(--select-secondary-color-text-disabled);
|
|
4054
4071
|
}
|
|
@@ -4056,6 +4073,11 @@ figure.chip {
|
|
|
4056
4073
|
color: var(--select-secondary-color-text-readonly);
|
|
4057
4074
|
}
|
|
4058
4075
|
|
|
4076
|
+
.select-button:where(:not([data-state=disabled])) .select-value[data-has-value=true] .select-input-label {
|
|
4077
|
+
color: var(--select-color-text-value);
|
|
4078
|
+
caret-color: var(--select-color-text-value);
|
|
4079
|
+
}
|
|
4080
|
+
|
|
4059
4081
|
.select-input-label-placeholder {
|
|
4060
4082
|
color: var(--select-primary-color-placeholder);
|
|
4061
4083
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniai-fe/uds-primitives",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.56",
|
|
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.
|
|
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.
|
|
93
|
-
"@mantine/dates": "^8.3.
|
|
94
|
-
"@mantine/hooks": "^8.3.
|
|
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
|
>
|
|
@@ -192,6 +192,7 @@ const InputTextArea = forwardRef<HTMLTextAreaElement, InputTextAreaProps>(
|
|
|
192
192
|
data-state={visualState}
|
|
193
193
|
data-priority={priority}
|
|
194
194
|
data-size={resolvedSize}
|
|
195
|
+
data-has-value={currentLength > 0 ? "true" : "false"}
|
|
195
196
|
data-readonly={isReadOnly ? "true" : undefined}
|
|
196
197
|
data-block={resolvedBlock ? "true" : undefined}
|
|
197
198
|
>
|
|
@@ -279,6 +279,11 @@
|
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
.input-field[data-has-value="true"] .input-element:not(:disabled) {
|
|
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:
|
|
344
|
+
height: 100%;
|
|
321
345
|
resize: none;
|
|
322
346
|
margin: 0;
|
|
323
347
|
}
|
|
@@ -85,8 +85,7 @@
|
|
|
85
85
|
--input-text-disabled-color: var(--color-label-disabled);
|
|
86
86
|
--input-placeholder-color: var(--color-label-alternative);
|
|
87
87
|
--input-placeholder-disabled-color: var(--color-label-disabled);
|
|
88
|
-
|
|
89
|
-
--input-placeholder-readonly-color: transparent;
|
|
88
|
+
--input-placeholder-readonly-color: var(--input-placeholder-color);
|
|
90
89
|
--input-font-size: var(--input-text-medium-size);
|
|
91
90
|
--input-line-height: var(--input-text-medium-line-height);
|
|
92
91
|
--input-font-weight: var(--input-text-medium-weight);
|
|
@@ -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 &&
|
|
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
|
|
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":
|
|
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":
|
|
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,13 @@
|
|
|
302
297
|
}
|
|
303
298
|
}
|
|
304
299
|
|
|
300
|
+
.select-button:where(:not([data-state="disabled"]))
|
|
301
|
+
.select-value[data-has-value="true"]
|
|
302
|
+
.select-input-label {
|
|
303
|
+
color: var(--select-color-text-value);
|
|
304
|
+
caret-color: var(--select-color-text-value);
|
|
305
|
+
}
|
|
306
|
+
|
|
305
307
|
.select-input-label-placeholder {
|
|
306
308
|
color: var(--select-primary-color-placeholder);
|
|
307
309
|
|
|
@@ -95,10 +95,11 @@
|
|
|
95
95
|
/* 변경: placeholder disabled/readonly 토큰을 분리해 상태별 제어 지점을 고정한다. */
|
|
96
96
|
--select-primary-color-placeholder-disabled: var(--color-label-disabled);
|
|
97
97
|
--select-primary-color-placeholder-readonly: var(
|
|
98
|
-
--select-primary-color-placeholder
|
|
98
|
+
--select-primary-color-placeholder
|
|
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);
|
|
@@ -122,7 +123,7 @@
|
|
|
122
123
|
--select-secondary-color-placeholder: var(--color-label-alternative);
|
|
123
124
|
--select-secondary-color-placeholder-disabled: var(--color-label-disabled);
|
|
124
125
|
--select-secondary-color-placeholder-readonly: var(
|
|
125
|
-
--select-secondary-color-placeholder
|
|
126
|
+
--select-secondary-color-placeholder
|
|
126
127
|
);
|
|
127
128
|
--select-color-surface-secondary: transparent;
|
|
128
129
|
--select-color-surface-secondary-hover: var(--color-surface-static-cool-gray);
|
|
@@ -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
|
*/
|