@x-plat/design-system 0.9.0 → 0.11.0

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.
Files changed (156) hide show
  1. package/dist/components/AutoResizeTextArea/index.cjs +14 -5
  2. package/dist/components/AutoResizeTextArea/index.css +15 -0
  3. package/dist/components/AutoResizeTextArea/index.d.cts +5 -0
  4. package/dist/components/AutoResizeTextArea/index.d.ts +5 -0
  5. package/dist/components/AutoResizeTextArea/index.js +14 -5
  6. package/dist/components/Badge/index.cjs +5 -1
  7. package/dist/components/Badge/index.css +1 -0
  8. package/dist/components/Badge/index.d.cts +1 -0
  9. package/dist/components/Badge/index.d.ts +1 -0
  10. package/dist/components/Badge/index.js +5 -1
  11. package/dist/components/Box/index.d.cts +3 -0
  12. package/dist/components/Box/index.d.ts +3 -0
  13. package/dist/components/Chart/index.cjs +7 -6
  14. package/dist/components/Chart/index.d.cts +12 -1
  15. package/dist/components/Chart/index.d.ts +12 -1
  16. package/dist/components/Chart/index.js +7 -6
  17. package/dist/components/ChatInput/index.cjs +20 -7
  18. package/dist/components/ChatInput/index.css +15 -0
  19. package/dist/components/ChatInput/index.d.cts +10 -0
  20. package/dist/components/ChatInput/index.d.ts +10 -0
  21. package/dist/components/ChatInput/index.js +20 -7
  22. package/dist/components/CheckBox/index.cjs +14 -5
  23. package/dist/components/CheckBox/index.css +17 -0
  24. package/dist/components/CheckBox/index.d.cts +10 -2
  25. package/dist/components/CheckBox/index.d.ts +10 -2
  26. package/dist/components/CheckBox/index.js +14 -5
  27. package/dist/components/Chip/index.d.cts +2 -0
  28. package/dist/components/Chip/index.d.ts +2 -0
  29. package/dist/components/DatePicker/index.cjs +181 -65
  30. package/dist/components/DatePicker/index.css +19 -0
  31. package/dist/components/DatePicker/index.js +181 -65
  32. package/dist/components/Drawer/index.cjs +83 -4
  33. package/dist/components/Drawer/index.d.cts +1 -0
  34. package/dist/components/Drawer/index.d.ts +1 -0
  35. package/dist/components/Drawer/index.js +83 -4
  36. package/dist/components/Dropdown/index.cjs +35 -7
  37. package/dist/components/Dropdown/index.js +35 -7
  38. package/dist/components/Editor/index.cjs +178 -21
  39. package/dist/components/Editor/index.js +178 -21
  40. package/dist/components/FieldMessage/index.cjs +8 -3
  41. package/dist/components/FieldMessage/index.css +15 -0
  42. package/dist/components/FieldMessage/index.d.cts +11 -4
  43. package/dist/components/FieldMessage/index.d.ts +11 -4
  44. package/dist/components/FieldMessage/index.js +8 -3
  45. package/dist/components/FileUpload/index.cjs +8 -3
  46. package/dist/components/FileUpload/index.css +15 -0
  47. package/dist/components/FileUpload/index.d.cts +6 -0
  48. package/dist/components/FileUpload/index.d.ts +6 -0
  49. package/dist/components/FileUpload/index.js +8 -3
  50. package/dist/components/ImageSelector/index.cjs +8 -3
  51. package/dist/components/ImageSelector/index.css +15 -0
  52. package/dist/components/ImageSelector/index.d.cts +3 -0
  53. package/dist/components/ImageSelector/index.d.ts +3 -0
  54. package/dist/components/ImageSelector/index.js +8 -3
  55. package/dist/components/Input/index.cjs +15 -5
  56. package/dist/components/Input/index.css +15 -0
  57. package/dist/components/Input/index.d.cts +8 -0
  58. package/dist/components/Input/index.d.ts +8 -0
  59. package/dist/components/Input/index.js +15 -5
  60. package/dist/components/Modal/index.cjs +84 -5
  61. package/dist/components/Modal/index.js +84 -5
  62. package/dist/components/NotificationBadge/index.cjs +5 -1
  63. package/dist/components/NotificationBadge/index.css +1 -0
  64. package/dist/components/NotificationBadge/index.d.cts +6 -2
  65. package/dist/components/NotificationBadge/index.d.ts +6 -2
  66. package/dist/components/NotificationBadge/index.js +5 -1
  67. package/dist/components/Pagination/index.cjs +6 -2
  68. package/dist/components/Pagination/index.css +1 -0
  69. package/dist/components/Pagination/index.d.cts +13 -2
  70. package/dist/components/Pagination/index.d.ts +13 -2
  71. package/dist/components/Pagination/index.js +6 -2
  72. package/dist/components/PopOver/index.cjs +34 -6
  73. package/dist/components/PopOver/index.js +34 -6
  74. package/dist/components/Progress/index.cjs +6 -2
  75. package/dist/components/Progress/index.css +1 -0
  76. package/dist/components/Progress/index.d.cts +11 -2
  77. package/dist/components/Progress/index.d.ts +11 -2
  78. package/dist/components/Progress/index.js +6 -2
  79. package/dist/components/Radio/index.cjs +14 -5
  80. package/dist/components/Radio/index.css +17 -0
  81. package/dist/components/Radio/index.d.cts +11 -2
  82. package/dist/components/Radio/index.d.ts +11 -2
  83. package/dist/components/Radio/index.js +14 -5
  84. package/dist/components/Select/index.cjs +99 -31
  85. package/dist/components/Select/index.css +54 -0
  86. package/dist/components/Select/index.d.cts +16 -1
  87. package/dist/components/Select/index.d.ts +16 -1
  88. package/dist/components/Select/index.js +99 -31
  89. package/dist/components/Skeleton/index.d.cts +3 -0
  90. package/dist/components/Skeleton/index.d.ts +3 -0
  91. package/dist/components/Spinner/index.cjs +6 -2
  92. package/dist/components/Spinner/index.css +1 -0
  93. package/dist/components/Spinner/index.d.cts +8 -2
  94. package/dist/components/Spinner/index.d.ts +8 -2
  95. package/dist/components/Spinner/index.js +6 -2
  96. package/dist/components/Stat/index.cjs +84 -0
  97. package/dist/components/Stat/index.css +96 -0
  98. package/dist/components/Stat/index.d.cts +37 -0
  99. package/dist/components/Stat/index.d.ts +37 -0
  100. package/dist/components/Stat/index.js +57 -0
  101. package/dist/components/Steps/index.cjs +6 -2
  102. package/dist/components/Steps/index.css +11 -0
  103. package/dist/components/Steps/index.d.cts +9 -2
  104. package/dist/components/Steps/index.d.ts +9 -2
  105. package/dist/components/Steps/index.js +6 -2
  106. package/dist/components/Switch/index.cjs +20 -7
  107. package/dist/components/Switch/index.css +17 -0
  108. package/dist/components/Switch/index.d.cts +15 -2
  109. package/dist/components/Switch/index.d.ts +15 -2
  110. package/dist/components/Switch/index.js +20 -7
  111. package/dist/components/Tab/index.cjs +51 -26
  112. package/dist/components/Tab/index.css +4 -0
  113. package/dist/components/Tab/index.d.cts +12 -1
  114. package/dist/components/Tab/index.d.ts +12 -1
  115. package/dist/components/Tab/index.js +51 -26
  116. package/dist/components/Tag/index.cjs +5 -3
  117. package/dist/components/Tag/index.d.cts +12 -2
  118. package/dist/components/Tag/index.d.ts +12 -2
  119. package/dist/components/Tag/index.js +5 -3
  120. package/dist/components/TextArea/index.cjs +14 -4
  121. package/dist/components/TextArea/index.css +15 -0
  122. package/dist/components/TextArea/index.d.cts +7 -0
  123. package/dist/components/TextArea/index.d.ts +7 -0
  124. package/dist/components/TextArea/index.js +14 -4
  125. package/dist/components/TimePicker/index.cjs +448 -0
  126. package/dist/components/TimePicker/index.css +172 -0
  127. package/dist/components/TimePicker/index.d.cts +42 -0
  128. package/dist/components/TimePicker/index.d.ts +42 -0
  129. package/dist/components/TimePicker/index.js +411 -0
  130. package/dist/components/Tooltip/index.cjs +4 -2
  131. package/dist/components/Tooltip/index.d.cts +11 -1
  132. package/dist/components/Tooltip/index.d.ts +11 -1
  133. package/dist/components/Tooltip/index.js +4 -2
  134. package/dist/components/index.cjs +916 -385
  135. package/dist/components/index.css +296 -0
  136. package/dist/components/index.d.cts +3 -0
  137. package/dist/components/index.d.ts +3 -0
  138. package/dist/components/index.js +914 -385
  139. package/dist/index.cjs +940 -409
  140. package/dist/index.css +296 -0
  141. package/dist/index.d.cts +3 -0
  142. package/dist/index.d.ts +3 -0
  143. package/dist/index.js +938 -409
  144. package/dist/status-njWqQWFV.d.cts +20 -0
  145. package/dist/status-njWqQWFV.d.ts +20 -0
  146. package/guidelines/AGENT_PROMPT.md +18 -3
  147. package/guidelines/Guidelines.md +3 -0
  148. package/guidelines/components/card.md +3 -3
  149. package/guidelines/components/editor.md +86 -0
  150. package/guidelines/components/feedback.md +1 -1
  151. package/guidelines/components/form.md +19 -0
  152. package/guidelines/components/navigation.md +16 -15
  153. package/guidelines/components/overlay.md +43 -4
  154. package/guidelines/components/stat.md +31 -0
  155. package/guidelines/components/time-picker.md +49 -0
  156. package/package.json +1 -1
@@ -1,6 +1,9 @@
1
1
  // src/components/Switch/Switch.tsx
2
2
  import React from "react";
3
3
 
4
+ // src/tokens/interface/status.ts
5
+ var normalizeStatus = (value) => value === "brand" ? "primary" : value;
6
+
4
7
  // ../../node_modules/clsx/dist/clsx.mjs
5
8
  function r(e) {
6
9
  var t, f, n = "";
@@ -140,11 +143,16 @@ var FieldMessage = (props) => {
140
143
  };
141
144
  FieldMessage.displayName = "FieldMessage";
142
145
  var Field = (props) => {
143
- const { validations, children } = props;
144
- if (!validations || validations.length === 0) return /* @__PURE__ */ jsx4(Fragment, { children });
146
+ const { label, required = false, htmlFor, validations, children } = props;
147
+ const hasMessages = !!validations && validations.length > 0;
148
+ if (!label && !hasMessages) return /* @__PURE__ */ jsx4(Fragment, { children });
145
149
  return /* @__PURE__ */ jsxs3("div", { className: "lib-xplat-field", children: [
150
+ label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", htmlFor, children: [
151
+ label,
152
+ required && /* @__PURE__ */ jsx4("span", { className: "required", "aria-hidden": "true", children: "*" })
153
+ ] }),
146
154
  children,
147
- /* @__PURE__ */ jsx4(FieldMessage, { validations })
155
+ hasMessages && /* @__PURE__ */ jsx4(FieldMessage, { validations })
148
156
  ] });
149
157
  };
150
158
  Field.displayName = "Field";
@@ -157,11 +165,15 @@ var Switch = (props) => {
157
165
  const {
158
166
  value,
159
167
  size = "md",
160
- type = "brand",
168
+ type = "primary",
161
169
  disabled,
162
170
  onChange,
163
- validations
171
+ validations,
172
+ label,
173
+ required
164
174
  } = props;
175
+ const fieldId = React.useId();
176
+ const statusType = normalizeStatus(type);
165
177
  const [isAnimating, setIsAnimating] = React.useState(false);
166
178
  const timeoutRef = React.useRef(null);
167
179
  React.useEffect(() => {
@@ -182,10 +194,11 @@ var Switch = (props) => {
182
194
  const control = /* @__PURE__ */ jsx5(
183
195
  "div",
184
196
  {
197
+ id: fieldId,
185
198
  className: clsx_default(
186
199
  "lib-xplat-switch",
187
200
  size,
188
- type,
201
+ statusType,
189
202
  value ? "checked" : void 0,
190
203
  disabled && "disabled",
191
204
  isAnimating && "animating"
@@ -196,7 +209,7 @@ var Switch = (props) => {
196
209
  children: /* @__PURE__ */ jsx5("div", { className: clsx_default("knob", value ? "checked" : void 0) })
197
210
  }
198
211
  );
199
- return /* @__PURE__ */ jsx5(Field, { validations, children: control });
212
+ return /* @__PURE__ */ jsx5(Field, { validations, label, required, htmlFor: fieldId, children: control });
200
213
  };
201
214
  Switch.displayName = "Switch";
202
215
  var Switch_default = Switch;
@@ -59,9 +59,13 @@ var import_jsx_runtime = require("react/jsx-runtime");
59
59
  var TabItem = import_react.default.forwardRef((props, ref) => {
60
60
  const { isActive, title, onClick } = props;
61
61
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
62
- "div",
62
+ "button",
63
63
  {
64
64
  ref,
65
+ type: "button",
66
+ role: "tab",
67
+ "aria-selected": isActive,
68
+ tabIndex: isActive ? 0 : -1,
65
69
  className: clsx_default("tab-item", isActive ? "active" : null),
66
70
  onClick,
67
71
  children: title
@@ -74,12 +78,25 @@ var TabItem_default = TabItem;
74
78
  // src/components/Tab/Tab.tsx
75
79
  var import_jsx_runtime2 = require("react/jsx-runtime");
76
80
  var Tab = (props) => {
77
- const { activeIndex, onChange, tabs, type, size = "md" } = props;
81
+ const { activeIndex, onChange, tabs, variant, type, size = "md" } = props;
82
+ const shape = variant ?? type ?? "default";
78
83
  const [underlineStyle, setUnderlineStyle] = import_react2.default.useState({
79
84
  left: 0,
80
85
  width: 0
81
86
  });
82
87
  const itemRefs = import_react2.default.useRef([]);
88
+ const handleKeyDown = (e) => {
89
+ const last = tabs.length - 1;
90
+ let next = null;
91
+ if (e.key === "ArrowRight") next = activeIndex >= last ? 0 : activeIndex + 1;
92
+ else if (e.key === "ArrowLeft") next = activeIndex <= 0 ? last : activeIndex - 1;
93
+ else if (e.key === "Home") next = 0;
94
+ else if (e.key === "End") next = last;
95
+ if (next === null) return;
96
+ e.preventDefault();
97
+ onChange(tabs[next], next);
98
+ itemRefs.current[next]?.focus();
99
+ };
83
100
  const handleChangeActiveTab = (tabItem, tabIdx) => {
84
101
  onChange(tabItem, tabIdx);
85
102
  };
@@ -89,30 +106,38 @@ var Tab = (props) => {
89
106
  setUnderlineStyle({ left: el.offsetLeft, width: el.offsetWidth });
90
107
  }
91
108
  }, [activeIndex, tabs.length]);
92
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: clsx_default("lib-xplat-tab", `type-${type}`, size), children: [
93
- tabs.map((tab, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
94
- TabItem_default,
95
- {
96
- onClick: () => handleChangeActiveTab(tab, idx),
97
- isActive: activeIndex === idx,
98
- ref: (el) => {
99
- itemRefs.current[idx] = el;
100
- },
101
- title: tab.title
102
- },
103
- `${tab.value}_${idx}`
104
- )),
105
- type === "toggle" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
106
- "div",
107
- {
108
- className: "tab-toggle-underline",
109
- style: {
110
- left: underlineStyle.left,
111
- width: underlineStyle.width
112
- }
113
- }
114
- )
115
- ] });
109
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
110
+ "div",
111
+ {
112
+ className: clsx_default("lib-xplat-tab", `type-${shape}`, size),
113
+ role: "tablist",
114
+ onKeyDown: handleKeyDown,
115
+ children: [
116
+ tabs.map((tab, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
117
+ TabItem_default,
118
+ {
119
+ onClick: () => handleChangeActiveTab(tab, idx),
120
+ isActive: activeIndex === idx,
121
+ ref: (el) => {
122
+ itemRefs.current[idx] = el;
123
+ },
124
+ title: tab.title
125
+ },
126
+ `${tab.value}_${idx}`
127
+ )),
128
+ shape === "toggle" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
129
+ "div",
130
+ {
131
+ className: "tab-toggle-underline",
132
+ style: {
133
+ left: underlineStyle.left,
134
+ width: underlineStyle.width
135
+ }
136
+ }
137
+ )
138
+ ]
139
+ }
140
+ );
116
141
  };
117
142
  Tab.displayName = "Tab";
118
143
  var Tab_default = Tab;
@@ -42,6 +42,10 @@
42
42
  border-radius: var(--spacing-radius-md);
43
43
  }
44
44
  .lib-xplat-tab.type-default > .tab-item {
45
+ appearance: none;
46
+ font: inherit;
47
+ border: none;
48
+ background: none;
45
49
  flex: 1;
46
50
  padding: var(--spacing-space-1) var(--spacing-space-6);
47
51
  height: 100%;
@@ -5,11 +5,22 @@ interface TabItemType {
5
5
  title: string;
6
6
  }
7
7
  type TabSize = "sm" | "md" | "lg";
8
+ type TabVariant = "default" | "toggle";
8
9
  interface TabProps {
10
+ /** 활성 탭 인덱스. controlled 전용이라 onChange 와 함께 쓴다 */
9
11
  activeIndex: number;
12
+ /** 탭 목록. children 을 받지 않는다 */
10
13
  tabs: TabItemType[];
14
+ /** 첫 인자가 탭 객체, **두 번째가 인덱스**다. setActiveIndex 를 그대로 넘기면 안 된다 */
11
15
  onChange: (value: TabItemType, index: number) => void;
12
- type: "default" | "toggle";
16
+ /** 탭 모양. `default` 는 채워진 평면 탭, `toggle` 은 밑줄 인디케이터가 슬라이드 */
17
+ variant?: TabVariant;
18
+ /**
19
+ * @deprecated 모양 변형은 `variant` 로 받는다. `type` 은 상태 색 전용이다.
20
+ * 기존 사용처가 깨지지 않도록 계속 받는다 — 둘 다 주면 `variant` 가 이긴다.
21
+ */
22
+ type?: TabVariant;
23
+ /** 탭 높이와 글자 크기 */
13
24
  size?: TabSize;
14
25
  }
15
26
  declare const Tab: {
@@ -5,11 +5,22 @@ interface TabItemType {
5
5
  title: string;
6
6
  }
7
7
  type TabSize = "sm" | "md" | "lg";
8
+ type TabVariant = "default" | "toggle";
8
9
  interface TabProps {
10
+ /** 활성 탭 인덱스. controlled 전용이라 onChange 와 함께 쓴다 */
9
11
  activeIndex: number;
12
+ /** 탭 목록. children 을 받지 않는다 */
10
13
  tabs: TabItemType[];
14
+ /** 첫 인자가 탭 객체, **두 번째가 인덱스**다. setActiveIndex 를 그대로 넘기면 안 된다 */
11
15
  onChange: (value: TabItemType, index: number) => void;
12
- type: "default" | "toggle";
16
+ /** 탭 모양. `default` 는 채워진 평면 탭, `toggle` 은 밑줄 인디케이터가 슬라이드 */
17
+ variant?: TabVariant;
18
+ /**
19
+ * @deprecated 모양 변형은 `variant` 로 받는다. `type` 은 상태 색 전용이다.
20
+ * 기존 사용처가 깨지지 않도록 계속 받는다 — 둘 다 주면 `variant` 가 이긴다.
21
+ */
22
+ type?: TabVariant;
23
+ /** 탭 높이와 글자 크기 */
13
24
  size?: TabSize;
14
25
  }
15
26
  declare const Tab: {
@@ -23,9 +23,13 @@ import { jsx } from "react/jsx-runtime";
23
23
  var TabItem = React.forwardRef((props, ref) => {
24
24
  const { isActive, title, onClick } = props;
25
25
  return /* @__PURE__ */ jsx(
26
- "div",
26
+ "button",
27
27
  {
28
28
  ref,
29
+ type: "button",
30
+ role: "tab",
31
+ "aria-selected": isActive,
32
+ tabIndex: isActive ? 0 : -1,
29
33
  className: clsx_default("tab-item", isActive ? "active" : null),
30
34
  onClick,
31
35
  children: title
@@ -38,12 +42,25 @@ var TabItem_default = TabItem;
38
42
  // src/components/Tab/Tab.tsx
39
43
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
40
44
  var Tab = (props) => {
41
- const { activeIndex, onChange, tabs, type, size = "md" } = props;
45
+ const { activeIndex, onChange, tabs, variant, type, size = "md" } = props;
46
+ const shape = variant ?? type ?? "default";
42
47
  const [underlineStyle, setUnderlineStyle] = React2.useState({
43
48
  left: 0,
44
49
  width: 0
45
50
  });
46
51
  const itemRefs = React2.useRef([]);
52
+ const handleKeyDown = (e) => {
53
+ const last = tabs.length - 1;
54
+ let next = null;
55
+ if (e.key === "ArrowRight") next = activeIndex >= last ? 0 : activeIndex + 1;
56
+ else if (e.key === "ArrowLeft") next = activeIndex <= 0 ? last : activeIndex - 1;
57
+ else if (e.key === "Home") next = 0;
58
+ else if (e.key === "End") next = last;
59
+ if (next === null) return;
60
+ e.preventDefault();
61
+ onChange(tabs[next], next);
62
+ itemRefs.current[next]?.focus();
63
+ };
47
64
  const handleChangeActiveTab = (tabItem, tabIdx) => {
48
65
  onChange(tabItem, tabIdx);
49
66
  };
@@ -53,30 +70,38 @@ var Tab = (props) => {
53
70
  setUnderlineStyle({ left: el.offsetLeft, width: el.offsetWidth });
54
71
  }
55
72
  }, [activeIndex, tabs.length]);
56
- return /* @__PURE__ */ jsxs("div", { className: clsx_default("lib-xplat-tab", `type-${type}`, size), children: [
57
- tabs.map((tab, idx) => /* @__PURE__ */ jsx2(
58
- TabItem_default,
59
- {
60
- onClick: () => handleChangeActiveTab(tab, idx),
61
- isActive: activeIndex === idx,
62
- ref: (el) => {
63
- itemRefs.current[idx] = el;
64
- },
65
- title: tab.title
66
- },
67
- `${tab.value}_${idx}`
68
- )),
69
- type === "toggle" && /* @__PURE__ */ jsx2(
70
- "div",
71
- {
72
- className: "tab-toggle-underline",
73
- style: {
74
- left: underlineStyle.left,
75
- width: underlineStyle.width
76
- }
77
- }
78
- )
79
- ] });
73
+ return /* @__PURE__ */ jsxs(
74
+ "div",
75
+ {
76
+ className: clsx_default("lib-xplat-tab", `type-${shape}`, size),
77
+ role: "tablist",
78
+ onKeyDown: handleKeyDown,
79
+ children: [
80
+ tabs.map((tab, idx) => /* @__PURE__ */ jsx2(
81
+ TabItem_default,
82
+ {
83
+ onClick: () => handleChangeActiveTab(tab, idx),
84
+ isActive: activeIndex === idx,
85
+ ref: (el) => {
86
+ itemRefs.current[idx] = el;
87
+ },
88
+ title: tab.title
89
+ },
90
+ `${tab.value}_${idx}`
91
+ )),
92
+ shape === "toggle" && /* @__PURE__ */ jsx2(
93
+ "div",
94
+ {
95
+ className: "tab-toggle-underline",
96
+ style: {
97
+ left: underlineStyle.left,
98
+ width: underlineStyle.width
99
+ }
100
+ }
101
+ )
102
+ ]
103
+ }
104
+ );
80
105
  };
81
106
  Tab.displayName = "Tab";
82
107
  var Tab_default = Tab;
@@ -939,20 +939,22 @@ var import_jsx_runtime295 = require("react/jsx-runtime");
939
939
  var Tag = (props) => {
940
940
  const {
941
941
  children,
942
- type = "neutral",
942
+ variant,
943
+ type,
943
944
  onClose,
944
945
  size = "md",
945
946
  disabled = false,
946
947
  colorIndex
947
948
  } = props;
949
+ const shape = variant ?? type ?? "neutral";
948
950
  return /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)(
949
951
  "span",
950
952
  {
951
953
  className: clsx_default(
952
954
  "lib-xplat-tag",
953
- type,
955
+ shape,
954
956
  size,
955
- type === "categorical" && colorIndex !== void 0 && `cat-${colorIndex}`,
957
+ shape === "categorical" && colorIndex !== void 0 && `cat-${colorIndex}`,
956
958
  disabled && "disabled"
957
959
  ),
958
960
  children: [
@@ -1,14 +1,24 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
 
4
- type TagType = "neutral" | "categorical";
4
+ type TagVariant = "neutral" | "categorical";
5
5
  type TagSize = "sm" | "md" | "lg";
6
6
  interface TagProps {
7
7
  children: React.ReactNode;
8
- type?: TagType;
8
+ /** 태그 배색. `categorical` 은 `colorIndex` 로 계열 색을 고른다 */
9
+ variant?: TagVariant;
10
+ /**
11
+ * @deprecated 모양 변형은 `variant` 로 받는다. `type` 은 상태 색 전용이다.
12
+ * 기존 사용처가 깨지지 않도록 계속 받는다 — 둘 다 주면 `variant` 가 이긴다.
13
+ */
14
+ type?: TagVariant;
15
+ /** 주면 오른쪽에 닫기 버튼이 생긴다. 제거 가능한 태그로 쓸 때 */
9
16
  onClose?: () => void;
17
+ /** 태그 크기 */
10
18
  size?: TagSize;
19
+ /** 흐리게 표시하고 닫기 버튼을 막는다 */
11
20
  disabled?: boolean;
21
+ /** variant 가 categorical 일 때 쓸 계열 색 번호 (1~8) */
12
22
  colorIndex?: number;
13
23
  }
14
24
  declare const Tag: {
@@ -1,14 +1,24 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
 
4
- type TagType = "neutral" | "categorical";
4
+ type TagVariant = "neutral" | "categorical";
5
5
  type TagSize = "sm" | "md" | "lg";
6
6
  interface TagProps {
7
7
  children: React.ReactNode;
8
- type?: TagType;
8
+ /** 태그 배색. `categorical` 은 `colorIndex` 로 계열 색을 고른다 */
9
+ variant?: TagVariant;
10
+ /**
11
+ * @deprecated 모양 변형은 `variant` 로 받는다. `type` 은 상태 색 전용이다.
12
+ * 기존 사용처가 깨지지 않도록 계속 받는다 — 둘 다 주면 `variant` 가 이긴다.
13
+ */
14
+ type?: TagVariant;
15
+ /** 주면 오른쪽에 닫기 버튼이 생긴다. 제거 가능한 태그로 쓸 때 */
9
16
  onClose?: () => void;
17
+ /** 태그 크기 */
10
18
  size?: TagSize;
19
+ /** 흐리게 표시하고 닫기 버튼을 막는다 */
11
20
  disabled?: boolean;
21
+ /** variant 가 categorical 일 때 쓸 계열 색 번호 (1~8) */
12
22
  colorIndex?: number;
13
23
  }
14
24
  declare const Tag: {
@@ -913,20 +913,22 @@ import { jsx as jsx295, jsxs as jsxs189 } from "react/jsx-runtime";
913
913
  var Tag = (props) => {
914
914
  const {
915
915
  children,
916
- type = "neutral",
916
+ variant,
917
+ type,
917
918
  onClose,
918
919
  size = "md",
919
920
  disabled = false,
920
921
  colorIndex
921
922
  } = props;
923
+ const shape = variant ?? type ?? "neutral";
922
924
  return /* @__PURE__ */ jsxs189(
923
925
  "span",
924
926
  {
925
927
  className: clsx_default(
926
928
  "lib-xplat-tag",
927
- type,
929
+ shape,
928
930
  size,
929
- type === "categorical" && colorIndex !== void 0 && `cat-${colorIndex}`,
931
+ shape === "categorical" && colorIndex !== void 0 && `cat-${colorIndex}`,
930
932
  disabled && "disabled"
931
933
  ),
932
934
  children: [
@@ -176,11 +176,16 @@ var FieldMessage = (props) => {
176
176
  };
177
177
  FieldMessage.displayName = "FieldMessage";
178
178
  var Field = (props) => {
179
- const { validations, children } = props;
180
- if (!validations || validations.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
179
+ const { label, required = false, htmlFor, validations, children } = props;
180
+ const hasMessages = !!validations && validations.length > 0;
181
+ if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
181
182
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "lib-xplat-field", children: [
183
+ label && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
184
+ label,
185
+ required && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
186
+ ] }),
182
187
  children,
183
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FieldMessage, { validations })
188
+ hasMessages && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FieldMessage, { validations })
184
189
  ] });
185
190
  };
186
191
  Field.displayName = "Field";
@@ -194,16 +199,21 @@ var TextArea = import_react.default.forwardRef(
194
199
  className,
195
200
  size = "md",
196
201
  validations,
202
+ label,
203
+ required,
197
204
  resize = "vertical",
198
205
  rows = 3,
199
206
  disabled,
200
207
  ...textareaProps
201
208
  } = props;
209
+ const autoId = import_react.default.useId();
210
+ const fieldId = props.id ?? autoId;
202
211
  const control = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: clsx_default("lib-xplat-textarea-wrap", className), children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
203
212
  "textarea",
204
213
  {
205
214
  ...textareaProps,
206
215
  ref,
216
+ id: fieldId,
207
217
  rows,
208
218
  disabled,
209
219
  "aria-invalid": hasError(validations) || void 0,
@@ -211,7 +221,7 @@ var TextArea = import_react.default.forwardRef(
211
221
  style: { resize }
212
222
  }
213
223
  ) });
214
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { validations, children: control });
224
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
215
225
  }
216
226
  );
217
227
  TextArea.displayName = "TextArea";
@@ -4,6 +4,21 @@
4
4
  flex-direction: column;
5
5
  gap: var(--spacing-space-2);
6
6
  }
7
+ .lib-xplat-field-label {
8
+ font-family: var(--semantic-typo-label-2-m-font);
9
+ font-size: var(--semantic-typo-label-2-m-size);
10
+ line-height: var(--semantic-typo-label-2-m-lh);
11
+ font-weight: var(--semantic-typo-label-2-m-weight);
12
+ letter-spacing: var(--semantic-typo-label-2-m-ls);
13
+ color: var(--semantic-text-secondary);
14
+ display: inline-flex;
15
+ align-items: center;
16
+ gap: var(--spacing-space-1);
17
+ cursor: pointer;
18
+ }
19
+ .lib-xplat-field-label > .required {
20
+ color: var(--semantic-text-error);
21
+ }
7
22
  .lib-xplat-field-message-wrap {
8
23
  display: flex;
9
24
  gap: var(--spacing-space-2);
@@ -5,10 +5,17 @@ import { V as Validation } from '../../validation-Dqo1zqhT.cjs';
5
5
  type TextAreaSize = "sm" | "md" | "lg";
6
6
  type TextAreaResize = "none" | "vertical" | "horizontal" | "both";
7
7
  interface TextAreaProps extends Omit<TextareaAttributes, "size"> {
8
+ /** 안쪽 여백과 글자 크기 */
9
+ /** 컨트롤 위에 붙는 라벨. 클릭하면 컨트롤에 포커스가 간다 */
10
+ label?: React.ReactNode;
11
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
12
+ required?: boolean;
8
13
  size?: TextAreaSize;
9
14
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
10
15
  validations?: Validation[];
16
+ /** 사용자가 크기를 조절할 방향 */
11
17
  resize?: TextAreaResize;
18
+ /** 기본으로 보일 줄 수 */
12
19
  rows?: number;
13
20
  }
14
21
  declare const TextArea: React.ForwardRefExoticComponent<TextAreaProps & React.RefAttributes<HTMLTextAreaElement>>;
@@ -5,10 +5,17 @@ import { V as Validation } from '../../validation-Dqo1zqhT.js';
5
5
  type TextAreaSize = "sm" | "md" | "lg";
6
6
  type TextAreaResize = "none" | "vertical" | "horizontal" | "both";
7
7
  interface TextAreaProps extends Omit<TextareaAttributes, "size"> {
8
+ /** 안쪽 여백과 글자 크기 */
9
+ /** 컨트롤 위에 붙는 라벨. 클릭하면 컨트롤에 포커스가 간다 */
10
+ label?: React.ReactNode;
11
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
12
+ required?: boolean;
8
13
  size?: TextAreaSize;
9
14
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
10
15
  validations?: Validation[];
16
+ /** 사용자가 크기를 조절할 방향 */
11
17
  resize?: TextAreaResize;
18
+ /** 기본으로 보일 줄 수 */
12
19
  rows?: number;
13
20
  }
14
21
  declare const TextArea: React.ForwardRefExoticComponent<TextAreaProps & React.RefAttributes<HTMLTextAreaElement>>;
@@ -140,11 +140,16 @@ var FieldMessage = (props) => {
140
140
  };
141
141
  FieldMessage.displayName = "FieldMessage";
142
142
  var Field = (props) => {
143
- const { validations, children } = props;
144
- if (!validations || validations.length === 0) return /* @__PURE__ */ jsx4(Fragment, { children });
143
+ const { label, required = false, htmlFor, validations, children } = props;
144
+ const hasMessages = !!validations && validations.length > 0;
145
+ if (!label && !hasMessages) return /* @__PURE__ */ jsx4(Fragment, { children });
145
146
  return /* @__PURE__ */ jsxs3("div", { className: "lib-xplat-field", children: [
147
+ label && /* @__PURE__ */ jsxs3("label", { className: "lib-xplat-field-label", htmlFor, children: [
148
+ label,
149
+ required && /* @__PURE__ */ jsx4("span", { className: "required", "aria-hidden": "true", children: "*" })
150
+ ] }),
146
151
  children,
147
- /* @__PURE__ */ jsx4(FieldMessage, { validations })
152
+ hasMessages && /* @__PURE__ */ jsx4(FieldMessage, { validations })
148
153
  ] });
149
154
  };
150
155
  Field.displayName = "Field";
@@ -158,16 +163,21 @@ var TextArea = React.forwardRef(
158
163
  className,
159
164
  size = "md",
160
165
  validations,
166
+ label,
167
+ required,
161
168
  resize = "vertical",
162
169
  rows = 3,
163
170
  disabled,
164
171
  ...textareaProps
165
172
  } = props;
173
+ const autoId = React.useId();
174
+ const fieldId = props.id ?? autoId;
166
175
  const control = /* @__PURE__ */ jsx5("div", { className: clsx_default("lib-xplat-textarea-wrap", className), children: /* @__PURE__ */ jsx5(
167
176
  "textarea",
168
177
  {
169
178
  ...textareaProps,
170
179
  ref,
180
+ id: fieldId,
171
181
  rows,
172
182
  disabled,
173
183
  "aria-invalid": hasError(validations) || void 0,
@@ -175,7 +185,7 @@ var TextArea = React.forwardRef(
175
185
  style: { resize }
176
186
  }
177
187
  ) });
178
- return /* @__PURE__ */ jsx5(Field, { validations, children: control });
188
+ return /* @__PURE__ */ jsx5(Field, { validations, label, required, htmlFor: fieldId, children: control });
179
189
  }
180
190
  );
181
191
  TextArea.displayName = "TextArea";