@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 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, 40px);
305
- --dropdown-option-height-medium: var(--theme-size-medium-2, 48px);
306
- --dropdown-option-height-large: var(--theme-size-medium-3, 56px);
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, var(--dropdown-option-height-medium));
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-option-font-size, var(--dropdown-text-medium-size));
2309
- font-weight: var(--dropdown-option-font-weight, var(--dropdown-text-weight));
2310
- line-height: var(--dropdown-option-line-height, var(--dropdown-text-medium-line-height));
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: inline-flex;
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: inline-flex;
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, var(--dropdown-text-medium-size));
2357
- line-height: var(--dropdown-text-line-height-selected, var(--dropdown-text-medium-line-height));
2358
- letter-spacing: var(--dropdown-text-letter-spacing-selected, var(--dropdown-text-medium-letter-spacing));
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-primitives",
3
- "version": "0.3.22",
3
+ "version": "0.3.24",
4
4
  "description": "UNIAI Design System; Primitives Components Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -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 type { CheckboxProps } from "../../../checkbox/types";
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
- if (shouldRenderCheckbox) {
57
- const checkboxClassName = clsx(
58
- "dropdown-menu-item-checkbox",
59
- checkboxProps?.className,
60
- );
45
+ // 변경: 좌측 콘텐츠가 전혀 없고 multiple도 아니면 즉시 null을 반환한다.
46
+ if (!left && !multiple) {
47
+ return null;
48
+ }
61
49
 
62
- const checkboxAdditionalProps: CheckboxProps = {
63
- ...checkboxProps,
64
- checked: isSelected,
65
- onCheckedChange: checkboxProps?.onCheckedChange ?? (() => {}),
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 size="medium" {...checkboxAdditionalProps} />
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={hasDescription ? "true" : undefined}
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
- <span className="dropdown-menu-item-body">
103
- {resolvedLabelContent}
104
- {description ? (
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
- ) : null}
109
- </span>
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-option-font-size, var(--dropdown-text-medium-size));
95
- font-weight: var(--dropdown-option-font-weight, var(--dropdown-text-weight));
96
- line-height: var(
97
- --dropdown-option-line-height,
98
- var(--dropdown-text-medium-line-height)
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
- display: inline-flex;
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
- display: inline-flex;
152
- align-items: center;
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
- --dropdown-text-size-selected,
157
- var(--dropdown-text-medium-size)
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, 40px);
59
- --dropdown-option-height-medium: var(--theme-size-medium-2, 48px);
60
- --dropdown-option-height-large: var(--theme-size-medium-3, 56px);
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={clsx("slot-text", className)}
29
- {...restProps}
45
+ className={mergedClassName}
46
+ {...nativeTitleProps}
47
+ {...normalizedRestProps}
30
48
  >
31
49
  {children}
32
50
  </SlotBase>