@uniai-fe/uds-primitives 0.3.22 → 0.3.24
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 +27 -13
- package/package.json +1 -1
- package/src/components/dropdown/markup/foundation/MenuItem.tsx +35 -36
- package/src/components/dropdown/styles/dropdown.scss +23 -25
- package/src/components/dropdown/styles/variables.scss +10 -3
- package/src/components/slot/markup/Text.tsx +20 -2
package/dist/styles.css
CHANGED
|
@@ -299,11 +299,16 @@
|
|
|
299
299
|
--dropdown-description-color: var(--color-label-neutral);
|
|
300
300
|
--dropdown-description-size: 14px;
|
|
301
301
|
--dropdown-description-line-height: 22px;
|
|
302
|
+
--dropdown-text-size-selected: var(--dropdown-text-medium-size);
|
|
303
|
+
--dropdown-text-line-height-selected: var(--dropdown-text-medium-line-height);
|
|
304
|
+
--dropdown-text-letter-spacing-selected: var(
|
|
305
|
+
--dropdown-text-medium-letter-spacing
|
|
306
|
+
);
|
|
302
307
|
--dropdown-option-height-selected: var(--dropdown-option-height-medium);
|
|
303
308
|
--dropdown-option-height-xsmall: 24px;
|
|
304
|
-
--dropdown-option-height-small: var(--theme-size-medium-1
|
|
305
|
-
--dropdown-option-height-medium: var(--theme-size-medium-2
|
|
306
|
-
--dropdown-option-height-large: var(--theme-size-medium-3
|
|
309
|
+
--dropdown-option-height-small: var(--theme-size-medium-1);
|
|
310
|
+
--dropdown-option-height-medium: var(--theme-size-medium-2);
|
|
311
|
+
--dropdown-option-height-large: var(--theme-size-medium-3);
|
|
307
312
|
--dropdown-option-padding-inline-xsmall: var(--spacing-padding-3);
|
|
308
313
|
--dropdown-option-padding-block-xsmall: 0px;
|
|
309
314
|
--dropdown-option-radius-xsmall: 4px;
|
|
@@ -2299,15 +2304,18 @@ figure.chip {
|
|
|
2299
2304
|
align-items: center;
|
|
2300
2305
|
gap: var(--dropdown-option-gap-inline);
|
|
2301
2306
|
width: 100%;
|
|
2302
|
-
min-height: var(--dropdown-option-height-selected
|
|
2307
|
+
min-height: var(--dropdown-option-height-selected);
|
|
2303
2308
|
padding: var(--dropdown-option-padding-block-selected) var(--dropdown-option-padding-inline-selected);
|
|
2304
2309
|
border-radius: var(--dropdown-option-radius-selected);
|
|
2305
2310
|
background-color: transparent;
|
|
2306
2311
|
color: var(--dropdown-option-color);
|
|
2307
2312
|
cursor: pointer;
|
|
2308
|
-
font-size: var(--dropdown-
|
|
2309
|
-
font-weight: var(--dropdown-
|
|
2310
|
-
line-height: var(--dropdown-
|
|
2313
|
+
font-size: var(--dropdown-text-size-selected);
|
|
2314
|
+
font-weight: var(--dropdown-text-weight);
|
|
2315
|
+
line-height: var(--dropdown-text-line-height-selected);
|
|
2316
|
+
}
|
|
2317
|
+
.dropdown-menu-item-trigger span {
|
|
2318
|
+
user-select: none;
|
|
2311
2319
|
}
|
|
2312
2320
|
.dropdown-menu-item-trigger[data-state=selected] {
|
|
2313
2321
|
background-color: var(--dropdown-option-bg-selected);
|
|
@@ -2335,7 +2343,7 @@ figure.chip {
|
|
|
2335
2343
|
|
|
2336
2344
|
.dropdown-menu-item-left,
|
|
2337
2345
|
.dropdown-menu-item-right {
|
|
2338
|
-
display:
|
|
2346
|
+
display: flex;
|
|
2339
2347
|
align-items: center;
|
|
2340
2348
|
color: inherit;
|
|
2341
2349
|
}
|
|
@@ -2346,22 +2354,28 @@ figure.chip {
|
|
|
2346
2354
|
gap: 0.2rem;
|
|
2347
2355
|
flex: 1 1 auto;
|
|
2348
2356
|
min-width: 0;
|
|
2357
|
+
overflow: hidden;
|
|
2349
2358
|
}
|
|
2350
2359
|
|
|
2351
2360
|
.dropdown-menu-item-label {
|
|
2352
|
-
display:
|
|
2353
|
-
align-items: center;
|
|
2361
|
+
display: block;
|
|
2354
2362
|
min-width: 0;
|
|
2363
|
+
width: 100%;
|
|
2364
|
+
max-width: 100%;
|
|
2355
2365
|
color: inherit;
|
|
2356
|
-
font-size: var(--dropdown-text-size-selected
|
|
2357
|
-
line-height: var(--dropdown-text-line-height-selected
|
|
2358
|
-
letter-spacing: var(--dropdown-text-letter-spacing-selected
|
|
2366
|
+
font-size: var(--dropdown-text-size-selected);
|
|
2367
|
+
line-height: var(--dropdown-text-line-height-selected);
|
|
2368
|
+
letter-spacing: var(--dropdown-text-letter-spacing-selected);
|
|
2359
2369
|
font-weight: var(--dropdown-text-weight);
|
|
2360
2370
|
overflow: hidden;
|
|
2361
2371
|
text-overflow: ellipsis;
|
|
2362
2372
|
white-space: nowrap;
|
|
2363
2373
|
}
|
|
2364
2374
|
|
|
2375
|
+
.dropdown-menu-item-trigger > .dropdown-menu-item-label {
|
|
2376
|
+
flex: 1 1 auto;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2365
2379
|
.dropdown-menu-item-description {
|
|
2366
2380
|
font-size: var(--dropdown-description-size);
|
|
2367
2381
|
line-height: var(--dropdown-description-line-height);
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ import { forwardRef } from "react";
|
|
|
6
6
|
|
|
7
7
|
import type { DropdownMenuItemProps } from "../../types/props";
|
|
8
8
|
import { Checkbox } from "../../../checkbox/markup/Checkbox";
|
|
9
|
-
import
|
|
9
|
+
import { Slot } from "../../../slot";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Dropdown Foundation; Menu Item 옵션 렌더링 컴포넌트
|
|
@@ -41,44 +41,36 @@ const DropdownMenuItem = forwardRef<HTMLDivElement, DropdownMenuItemProps>(
|
|
|
41
41
|
},
|
|
42
42
|
ref,
|
|
43
43
|
) => {
|
|
44
|
-
const labelContent = label ?? children;
|
|
45
|
-
// 변경: label/children이 string|number일 때만 준비된 label span으로 매핑하고, 그 외 ReactNode는 그대로 렌더링한다.
|
|
46
|
-
const resolvedLabelContent =
|
|
47
|
-
typeof labelContent === "string" || typeof labelContent === "number" ? (
|
|
48
|
-
<span className="dropdown-menu-item-label">{labelContent}</span>
|
|
49
|
-
) : (
|
|
50
|
-
labelContent
|
|
51
|
-
);
|
|
52
|
-
const hasDescription = Boolean(description);
|
|
53
|
-
const shouldRenderCheckbox = multiple && !left;
|
|
54
|
-
|
|
55
44
|
const renderLeft = () => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
45
|
+
// 변경: 좌측 콘텐츠가 전혀 없고 multiple도 아니면 즉시 null을 반환한다.
|
|
46
|
+
if (!left && !multiple) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
61
49
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
tabIndex: -1,
|
|
67
|
-
"aria-hidden": true,
|
|
68
|
-
className: checkboxClassName,
|
|
69
|
-
} as CheckboxProps;
|
|
50
|
+
// 변경: left가 있으면 우선 렌더링하고 체크박스 분기보다 앞에서 종료한다.
|
|
51
|
+
if (left) {
|
|
52
|
+
return <span className="dropdown-menu-item-left">{left}</span>;
|
|
53
|
+
}
|
|
70
54
|
|
|
55
|
+
if (multiple) {
|
|
71
56
|
return (
|
|
72
57
|
<span className="dropdown-menu-item-left" aria-hidden="true">
|
|
73
|
-
<Checkbox
|
|
58
|
+
<Checkbox
|
|
59
|
+
size="medium"
|
|
60
|
+
{...checkboxProps}
|
|
61
|
+
checked={isSelected}
|
|
62
|
+
onCheckedChange={checkboxProps?.onCheckedChange ?? (() => {})}
|
|
63
|
+
tabIndex={-1}
|
|
64
|
+
aria-hidden="true"
|
|
65
|
+
className={clsx(
|
|
66
|
+
"dropdown-menu-item-checkbox",
|
|
67
|
+
checkboxProps?.className,
|
|
68
|
+
)}
|
|
69
|
+
/>
|
|
74
70
|
</span>
|
|
75
71
|
);
|
|
76
72
|
}
|
|
77
73
|
|
|
78
|
-
if (left) {
|
|
79
|
-
return <span className="dropdown-menu-item-left">{left}</span>;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
74
|
return null;
|
|
83
75
|
};
|
|
84
76
|
|
|
@@ -90,7 +82,7 @@ const DropdownMenuItem = forwardRef<HTMLDivElement, DropdownMenuItemProps>(
|
|
|
90
82
|
className={clsx("dropdown-menu-item-trigger", className)}
|
|
91
83
|
data-state={isSelected ? "selected" : undefined}
|
|
92
84
|
data-multiple={multiple ? "true" : undefined}
|
|
93
|
-
data-has-description={
|
|
85
|
+
data-has-description={description ? "true" : undefined}
|
|
94
86
|
onSelect={event => {
|
|
95
87
|
if (multiple) {
|
|
96
88
|
event.preventDefault();
|
|
@@ -99,14 +91,21 @@ const DropdownMenuItem = forwardRef<HTMLDivElement, DropdownMenuItemProps>(
|
|
|
99
91
|
}}
|
|
100
92
|
>
|
|
101
93
|
{renderLeft()}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
94
|
+
{/* 변경: description이 있을 때만 body wrapper를 렌더링해 불필요한 중첩 span을 줄인다. */}
|
|
95
|
+
{description ? (
|
|
96
|
+
<span className="dropdown-menu-item-body">
|
|
97
|
+
<Slot.Text className="dropdown-menu-item-label">
|
|
98
|
+
{label ?? children}
|
|
99
|
+
</Slot.Text>
|
|
105
100
|
<span className="dropdown-menu-item-description">
|
|
106
101
|
{description}
|
|
107
102
|
</span>
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
</span>
|
|
104
|
+
) : (
|
|
105
|
+
<Slot.Text className="dropdown-menu-item-label">
|
|
106
|
+
{label ?? children}
|
|
107
|
+
</Slot.Text>
|
|
108
|
+
)}
|
|
110
109
|
{right ? (
|
|
111
110
|
<span className="dropdown-menu-item-right">{right}</span>
|
|
112
111
|
) : null}
|
|
@@ -81,22 +81,20 @@
|
|
|
81
81
|
align-items: center;
|
|
82
82
|
gap: var(--dropdown-option-gap-inline);
|
|
83
83
|
width: 100%;
|
|
84
|
-
min-height: var(
|
|
85
|
-
--dropdown-option-height-selected,
|
|
86
|
-
var(--dropdown-option-height-medium)
|
|
87
|
-
);
|
|
84
|
+
min-height: var(--dropdown-option-height-selected);
|
|
88
85
|
padding: var(--dropdown-option-padding-block-selected)
|
|
89
86
|
var(--dropdown-option-padding-inline-selected);
|
|
90
87
|
border-radius: var(--dropdown-option-radius-selected);
|
|
91
88
|
background-color: transparent;
|
|
92
89
|
color: var(--dropdown-option-color);
|
|
93
90
|
cursor: pointer;
|
|
94
|
-
font-size: var(--dropdown-
|
|
95
|
-
font-weight: var(--dropdown-
|
|
96
|
-
line-height: var(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
font-size: var(--dropdown-text-size-selected);
|
|
92
|
+
font-weight: var(--dropdown-text-weight);
|
|
93
|
+
line-height: var(--dropdown-text-line-height-selected);
|
|
94
|
+
|
|
95
|
+
span {
|
|
96
|
+
user-select: none;
|
|
97
|
+
}
|
|
100
98
|
|
|
101
99
|
&[data-state="selected"] {
|
|
102
100
|
background-color: var(--dropdown-option-bg-selected);
|
|
@@ -134,7 +132,8 @@
|
|
|
134
132
|
|
|
135
133
|
.dropdown-menu-item-left,
|
|
136
134
|
.dropdown-menu-item-right {
|
|
137
|
-
|
|
135
|
+
// 변경: inline-* 금지 규칙에 맞춰 정렬 컨테이너를 flex로 통일한다.
|
|
136
|
+
display: flex;
|
|
138
137
|
align-items: center;
|
|
139
138
|
color: inherit;
|
|
140
139
|
}
|
|
@@ -145,31 +144,30 @@
|
|
|
145
144
|
gap: 0.2rem;
|
|
146
145
|
flex: 1 1 auto;
|
|
147
146
|
min-width: 0;
|
|
147
|
+
overflow: hidden;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
.dropdown-menu-item-label {
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
// 변경: 말줄임표 동작을 위해 라벨 박스를 block + width 제약으로 고정한다.
|
|
152
|
+
display: block;
|
|
153
153
|
min-width: 0;
|
|
154
|
+
width: 100%;
|
|
155
|
+
max-width: 100%;
|
|
154
156
|
color: inherit;
|
|
155
|
-
font-size: var(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
);
|
|
159
|
-
line-height: var(
|
|
160
|
-
--dropdown-text-line-height-selected,
|
|
161
|
-
var(--dropdown-text-medium-line-height)
|
|
162
|
-
);
|
|
163
|
-
letter-spacing: var(
|
|
164
|
-
--dropdown-text-letter-spacing-selected,
|
|
165
|
-
var(--dropdown-text-medium-letter-spacing)
|
|
166
|
-
);
|
|
157
|
+
font-size: var(--dropdown-text-size-selected);
|
|
158
|
+
line-height: var(--dropdown-text-line-height-selected);
|
|
159
|
+
letter-spacing: var(--dropdown-text-letter-spacing-selected);
|
|
167
160
|
font-weight: var(--dropdown-text-weight);
|
|
168
161
|
overflow: hidden;
|
|
169
162
|
text-overflow: ellipsis;
|
|
170
163
|
white-space: nowrap;
|
|
171
164
|
}
|
|
172
165
|
|
|
166
|
+
// 변경: description이 없어 body wrapper를 생략하는 경로에서 label이 가용 폭을 차지하도록 한다.
|
|
167
|
+
.dropdown-menu-item-trigger > .dropdown-menu-item-label {
|
|
168
|
+
flex: 1 1 auto;
|
|
169
|
+
}
|
|
170
|
+
|
|
173
171
|
.dropdown-menu-item-description {
|
|
174
172
|
font-size: var(--dropdown-description-size);
|
|
175
173
|
line-height: var(--dropdown-description-line-height);
|
|
@@ -53,11 +53,18 @@
|
|
|
53
53
|
--dropdown-description-size: 14px;
|
|
54
54
|
--dropdown-description-line-height: 22px;
|
|
55
55
|
|
|
56
|
+
// 변경: fallback 없는 토큰 계약을 위해 기본 selected typography를 root에서 명시한다.
|
|
57
|
+
--dropdown-text-size-selected: var(--dropdown-text-medium-size);
|
|
58
|
+
--dropdown-text-line-height-selected: var(--dropdown-text-medium-line-height);
|
|
59
|
+
--dropdown-text-letter-spacing-selected: var(
|
|
60
|
+
--dropdown-text-medium-letter-spacing
|
|
61
|
+
);
|
|
62
|
+
|
|
56
63
|
--dropdown-option-height-selected: var(--dropdown-option-height-medium);
|
|
57
64
|
--dropdown-option-height-xsmall: 24px;
|
|
58
|
-
--dropdown-option-height-small: var(--theme-size-medium-1
|
|
59
|
-
--dropdown-option-height-medium: var(--theme-size-medium-2
|
|
60
|
-
--dropdown-option-height-large: var(--theme-size-medium-3
|
|
65
|
+
--dropdown-option-height-small: var(--theme-size-medium-1);
|
|
66
|
+
--dropdown-option-height-medium: var(--theme-size-medium-2);
|
|
67
|
+
--dropdown-option-height-large: var(--theme-size-medium-3);
|
|
61
68
|
--dropdown-option-padding-inline-xsmall: var(--spacing-padding-3);
|
|
62
69
|
--dropdown-option-padding-block-xsmall: 0px;
|
|
63
70
|
--dropdown-option-radius-xsmall: 4px;
|
|
@@ -21,12 +21,30 @@ export default function SlotText<C extends ElementType = "span">({
|
|
|
21
21
|
return children;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// 변경: rest spread로 유입될 수 있는 className 충돌을 제거해 slot-text 클래스가 항상 유지되도록 한다.
|
|
25
|
+
const restPropsRecord = restProps as Record<string, unknown>;
|
|
26
|
+
const restClassName =
|
|
27
|
+
typeof restPropsRecord.className === "string"
|
|
28
|
+
? restPropsRecord.className
|
|
29
|
+
: undefined;
|
|
30
|
+
const mergedClassName = clsx("slot-text", restClassName, className);
|
|
31
|
+
const normalizedRestProps = { ...restPropsRecord };
|
|
32
|
+
delete normalizedRestProps.className;
|
|
33
|
+
|
|
34
|
+
const hasCustomTitle =
|
|
35
|
+
"title" in normalizedRestProps &&
|
|
36
|
+
typeof normalizedRestProps.title !== "undefined";
|
|
37
|
+
|
|
38
|
+
// 변경: 텍스트 children은 기본 title을 자동 주입해 native tooltip 경로를 보장한다.
|
|
39
|
+
const nativeTitleProps = hasCustomTitle ? {} : { title: String(children) };
|
|
40
|
+
|
|
24
41
|
// 문자열/숫자 children만 공통 slot text 마크업으로 감싼다.
|
|
25
42
|
return (
|
|
26
43
|
<SlotBase
|
|
27
44
|
as={as as ElementType}
|
|
28
|
-
className={
|
|
29
|
-
{...
|
|
45
|
+
className={mergedClassName}
|
|
46
|
+
{...nativeTitleProps}
|
|
47
|
+
{...normalizedRestProps}
|
|
30
48
|
>
|
|
31
49
|
{children}
|
|
32
50
|
</SlotBase>
|