@uniai-fe/uds-primitives 0.3.53 → 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 +28 -5
- package/package.json +5 -5
- package/src/components/input/markup/foundation/Input.tsx +2 -0
- package/src/components/input/styles/foundation.scss +25 -1
- 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 +5 -5
- package/src/components/select/styles/variables.scss +2 -1
- package/src/components/select/types/trigger.ts +7 -0
- package/src/components/table/markup/Container.tsx +7 -2
- package/src/components/table/types/foundation.ts +15 -2
package/dist/styles.css
CHANGED
|
@@ -533,7 +533,7 @@
|
|
|
533
533
|
--select-table-border-default-color: var(--input-border-table-default-color);
|
|
534
534
|
--select-table-border-focus-color: var(--input-border-active-color);
|
|
535
535
|
--select-table-border-error-color: var(--input-border-error-color);
|
|
536
|
-
--select-table-border-disabled-color:
|
|
536
|
+
--select-table-border-disabled-color: transparent;
|
|
537
537
|
--select-table-border-readonly-color: transparent;
|
|
538
538
|
--select-table-surface-color: transparent;
|
|
539
539
|
--select-table-surface-disabled-color: var(--input-surface-disabled-color);
|
|
@@ -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:
|
|
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.
|
|
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.
|
|
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
|
>
|
|
@@ -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:
|
|
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 &&
|
|
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,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
|
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
--select-table-border-default-color: var(--input-border-table-default-color);
|
|
37
37
|
--select-table-border-focus-color: var(--input-border-active-color);
|
|
38
38
|
--select-table-border-error-color: var(--input-border-error-color);
|
|
39
|
-
--select-table-border-disabled-color:
|
|
39
|
+
--select-table-border-disabled-color: transparent;
|
|
40
40
|
--select-table-border-readonly-color: transparent;
|
|
41
41
|
--select-table-surface-color: transparent;
|
|
42
42
|
--select-table-surface-disabled-color: var(--input-surface-disabled-color);
|
|
@@ -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
|
*/
|
|
@@ -7,6 +7,7 @@ import TableHead from "./foundation/Head";
|
|
|
7
7
|
import TableTh from "./foundation/Th";
|
|
8
8
|
import TableRoot from "./foundation/Root";
|
|
9
9
|
import TableRow from "./foundation/Row";
|
|
10
|
+
import { Slot } from "../../slot/markup";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Table Preset; 기본 Container 조합 컴포넌트
|
|
@@ -45,6 +46,8 @@ const TableContainer = forwardRef<HTMLTableElement, TableContainerProps>(
|
|
|
45
46
|
isCustomBody = false,
|
|
46
47
|
scrollable = false,
|
|
47
48
|
scrollAxis = "x",
|
|
49
|
+
scrollAs = "div",
|
|
50
|
+
scrollProps,
|
|
48
51
|
scrollClassName,
|
|
49
52
|
footer,
|
|
50
53
|
children,
|
|
@@ -153,14 +156,16 @@ const TableContainer = forwardRef<HTMLTableElement, TableContainerProps>(
|
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
return (
|
|
156
|
-
<
|
|
159
|
+
<Slot.Base
|
|
160
|
+
as={scrollAs}
|
|
157
161
|
className={clsx("table-scroll-wrapper", scrollClassName)}
|
|
158
162
|
data-layout={tableProps?.layout ?? "line"}
|
|
159
163
|
data-role={tableProps?.role ?? "table"}
|
|
160
164
|
data-scroll-axis={scrollAxis}
|
|
165
|
+
{...scrollProps}
|
|
161
166
|
>
|
|
162
167
|
{tableNode}
|
|
163
|
-
</
|
|
168
|
+
</Slot.Base>
|
|
164
169
|
);
|
|
165
170
|
},
|
|
166
171
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
-
import type { SlotTextProps } from "../../slot";
|
|
1
|
+
import type { ComponentPropsWithoutRef, ElementType } from "react";
|
|
2
|
+
import type { SlotComponentProps, SlotTextProps } from "../../slot";
|
|
3
3
|
|
|
4
4
|
export const TABLE_CELL_ALIGN_OPTIONS = ["left", "center", "right"] as const;
|
|
5
5
|
export const TABLE_CELL_ALIGN_Y_OPTIONS = ["top", "center", "bottom"] as const;
|
|
@@ -154,6 +154,8 @@ export interface TableColumnData<
|
|
|
154
154
|
* @property {boolean} [isCustomBody] true면 body wrapper 없이 children을 직접 렌더링
|
|
155
155
|
* @property {boolean} [scrollable=false] true면 외부 스크롤 래퍼를 추가한다.
|
|
156
156
|
* @property {"x" | "y" | "both"} [scrollAxis="x"] scrollable일 때 스크롤 축
|
|
157
|
+
* @property {ElementType} [scrollAs="div"] scrollable wrapper element
|
|
158
|
+
* @property {Omit<SlotComponentProps<ElementType>, "as" | "children" | "className">} [scrollProps] scrollable wrapper native props
|
|
157
159
|
* @property {string} [scrollClassName] 스크롤 래퍼 className
|
|
158
160
|
* @property {React.ReactNode} [footer] footer 노드
|
|
159
161
|
* @property {React.ReactNode} [children] body 콘텐츠
|
|
@@ -177,6 +179,17 @@ export interface TableContainerProps<
|
|
|
177
179
|
* scrollable일 때 스크롤 축
|
|
178
180
|
*/
|
|
179
181
|
scrollAxis?: TableScrollAxis;
|
|
182
|
+
/**
|
|
183
|
+
* scrollable wrapper element
|
|
184
|
+
*/
|
|
185
|
+
scrollAs?: ElementType;
|
|
186
|
+
/**
|
|
187
|
+
* scrollable wrapper native props
|
|
188
|
+
*/
|
|
189
|
+
scrollProps?: Omit<
|
|
190
|
+
SlotComponentProps<ElementType>,
|
|
191
|
+
"as" | "children" | "className"
|
|
192
|
+
>;
|
|
180
193
|
/**
|
|
181
194
|
* 스크롤 래퍼 className
|
|
182
195
|
*/
|