@x-plat/design-system 0.10.0 → 0.12.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 (115) 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 +4 -0
  4. package/dist/components/AutoResizeTextArea/index.d.ts +4 -0
  5. package/dist/components/AutoResizeTextArea/index.js +14 -5
  6. package/dist/components/Badge/index.d.cts +1 -1
  7. package/dist/components/Badge/index.d.ts +1 -1
  8. package/dist/components/ChatInput/index.cjs +20 -7
  9. package/dist/components/ChatInput/index.css +15 -0
  10. package/dist/components/ChatInput/index.d.cts +4 -0
  11. package/dist/components/ChatInput/index.d.ts +4 -0
  12. package/dist/components/ChatInput/index.js +20 -7
  13. package/dist/components/CheckBox/index.cjs +8 -3
  14. package/dist/components/CheckBox/index.css +15 -0
  15. package/dist/components/CheckBox/index.d.cts +1 -1
  16. package/dist/components/CheckBox/index.d.ts +1 -1
  17. package/dist/components/CheckBox/index.js +8 -3
  18. package/dist/components/DatePicker/index.cjs +179 -64
  19. package/dist/components/DatePicker/index.css +19 -0
  20. package/dist/components/DatePicker/index.js +179 -64
  21. package/dist/components/Drawer/index.cjs +83 -4
  22. package/dist/components/Drawer/index.js +83 -4
  23. package/dist/components/Dropdown/index.cjs +57 -16
  24. package/dist/components/Dropdown/index.css +3 -0
  25. package/dist/components/Dropdown/index.js +57 -16
  26. package/dist/components/Editor/index.cjs +779 -50
  27. package/dist/components/Editor/index.css +100 -0
  28. package/dist/components/Editor/index.d.cts +1 -1
  29. package/dist/components/Editor/index.d.ts +1 -1
  30. package/dist/components/Editor/index.js +779 -50
  31. package/dist/components/FieldMessage/index.cjs +8 -3
  32. package/dist/components/FieldMessage/index.css +15 -0
  33. package/dist/components/FieldMessage/index.d.cts +11 -4
  34. package/dist/components/FieldMessage/index.d.ts +11 -4
  35. package/dist/components/FieldMessage/index.js +8 -3
  36. package/dist/components/FileUpload/index.cjs +8 -3
  37. package/dist/components/FileUpload/index.css +15 -0
  38. package/dist/components/FileUpload/index.js +8 -3
  39. package/dist/components/ImageSelector/index.cjs +8 -3
  40. package/dist/components/ImageSelector/index.css +15 -0
  41. package/dist/components/ImageSelector/index.js +8 -3
  42. package/dist/components/Input/index.cjs +15 -5
  43. package/dist/components/Input/index.css +15 -0
  44. package/dist/components/Input/index.d.cts +4 -0
  45. package/dist/components/Input/index.d.ts +4 -0
  46. package/dist/components/Input/index.js +15 -5
  47. package/dist/components/Modal/index.cjs +84 -5
  48. package/dist/components/Modal/index.js +84 -5
  49. package/dist/components/NotificationBadge/index.d.cts +1 -1
  50. package/dist/components/NotificationBadge/index.d.ts +1 -1
  51. package/dist/components/Pagination/index.d.cts +1 -1
  52. package/dist/components/Pagination/index.d.ts +1 -1
  53. package/dist/components/PopOver/index.cjs +55 -13
  54. package/dist/components/PopOver/index.css +3 -0
  55. package/dist/components/PopOver/index.js +55 -13
  56. package/dist/components/Progress/index.d.cts +1 -1
  57. package/dist/components/Progress/index.d.ts +1 -1
  58. package/dist/components/Radio/index.cjs +8 -3
  59. package/dist/components/Radio/index.css +15 -0
  60. package/dist/components/Radio/index.d.cts +1 -1
  61. package/dist/components/Radio/index.d.ts +1 -1
  62. package/dist/components/Radio/index.js +8 -3
  63. package/dist/components/Select/index.cjs +120 -39
  64. package/dist/components/Select/index.css +57 -0
  65. package/dist/components/Select/index.d.cts +11 -0
  66. package/dist/components/Select/index.d.ts +11 -0
  67. package/dist/components/Select/index.js +120 -39
  68. package/dist/components/Spinner/index.d.cts +1 -1
  69. package/dist/components/Spinner/index.d.ts +1 -1
  70. package/dist/components/Stat/index.cjs +84 -0
  71. package/dist/components/Stat/index.css +96 -0
  72. package/dist/components/Stat/index.d.cts +37 -0
  73. package/dist/components/Stat/index.d.ts +37 -0
  74. package/dist/components/Stat/index.js +57 -0
  75. package/dist/components/Steps/index.d.cts +1 -1
  76. package/dist/components/Steps/index.d.ts +1 -1
  77. package/dist/components/Switch/index.cjs +14 -5
  78. package/dist/components/Switch/index.css +15 -0
  79. package/dist/components/Switch/index.d.cts +6 -1
  80. package/dist/components/Switch/index.d.ts +6 -1
  81. package/dist/components/Switch/index.js +14 -5
  82. package/dist/components/Tab/index.cjs +49 -25
  83. package/dist/components/Tab/index.css +4 -0
  84. package/dist/components/Tab/index.js +49 -25
  85. package/dist/components/TextArea/index.cjs +14 -4
  86. package/dist/components/TextArea/index.css +15 -0
  87. package/dist/components/TextArea/index.d.cts +4 -0
  88. package/dist/components/TextArea/index.d.ts +4 -0
  89. package/dist/components/TextArea/index.js +14 -4
  90. package/dist/components/TimePicker/index.cjs +461 -0
  91. package/dist/components/TimePicker/index.css +175 -0
  92. package/dist/components/TimePicker/index.d.cts +42 -0
  93. package/dist/components/TimePicker/index.d.ts +42 -0
  94. package/dist/components/TimePicker/index.js +424 -0
  95. package/dist/components/index.cjs +1500 -398
  96. package/dist/components/index.css +385 -0
  97. package/dist/components/index.d.cts +3 -1
  98. package/dist/components/index.d.ts +3 -1
  99. package/dist/components/index.js +1498 -398
  100. package/dist/index.cjs +1524 -422
  101. package/dist/index.css +385 -0
  102. package/dist/index.d.cts +3 -1
  103. package/dist/index.d.ts +3 -1
  104. package/dist/index.js +1522 -422
  105. package/dist/status-njWqQWFV.d.cts +20 -0
  106. package/dist/status-njWqQWFV.d.ts +20 -0
  107. package/guidelines/AGENT_PROMPT.md +3 -3
  108. package/guidelines/Guidelines.md +3 -0
  109. package/guidelines/components/editor.md +141 -0
  110. package/guidelines/components/form.md +19 -0
  111. package/guidelines/components/stat.md +31 -0
  112. package/guidelines/components/time-picker.md +49 -0
  113. package/package.json +1 -1
  114. package/dist/status-g_KP3qT6.d.cts +0 -8
  115. package/dist/status-g_KP3qT6.d.ts +0 -8
@@ -6,6 +6,21 @@
6
6
  flex-direction: column;
7
7
  gap: var(--spacing-space-2);
8
8
  }
9
+ .lib-xplat-field-label {
10
+ font-family: var(--semantic-typo-label-2-m-font);
11
+ font-size: var(--semantic-typo-label-2-m-size);
12
+ line-height: var(--semantic-typo-label-2-m-lh);
13
+ font-weight: var(--semantic-typo-label-2-m-weight);
14
+ letter-spacing: var(--semantic-typo-label-2-m-ls);
15
+ color: var(--semantic-text-secondary);
16
+ display: inline-flex;
17
+ align-items: center;
18
+ gap: var(--spacing-space-1);
19
+ cursor: pointer;
20
+ }
21
+ .lib-xplat-field-label > .required {
22
+ color: var(--semantic-text-error);
23
+ }
9
24
  .lib-xplat-field-message-wrap {
10
25
  display: flex;
11
26
  gap: var(--spacing-space-2);
@@ -144,6 +159,45 @@
144
159
  .lib-xplat-select .select-trigger .select-trigger-icon svg {
145
160
  font-size: var(--semantic-typo-label-1-m-size);
146
161
  }
162
+ .lib-xplat-select-content .select-search {
163
+ position: sticky;
164
+ top: 0;
165
+ z-index: 1;
166
+ padding: var(--spacing-space-2);
167
+ background-color: var(--semantic-surface-neutral-primary);
168
+ border-bottom: 1px solid var(--semantic-border-secondary);
169
+ }
170
+ .lib-xplat-select-content .select-search > input {
171
+ width: 100%;
172
+ border: 1px solid var(--semantic-border-primary);
173
+ border-radius: var(--spacing-radius-sm);
174
+ padding: var(--spacing-space-1) var(--spacing-space-2);
175
+ outline: none;
176
+ color: var(--semantic-text-primary);
177
+ background-color: var(--semantic-surface-neutral-primary);
178
+ font-family: var(--semantic-typo-label-2-m-font);
179
+ font-size: var(--semantic-typo-label-2-m-size);
180
+ line-height: var(--semantic-typo-label-2-m-lh);
181
+ font-weight: var(--semantic-typo-label-2-m-weight);
182
+ letter-spacing: var(--semantic-typo-label-2-m-ls);
183
+ transition: border-color 0.28s cubic-bezier(0.25, 0.46, 0.45, 0.94), box-shadow 0.28s cubic-bezier(0.25, 0.46, 0.45, 0.94);
184
+ }
185
+ .lib-xplat-select-content .select-search > input:focus {
186
+ border-color: var(--semantic-interaction-focus-ring);
187
+ box-shadow: 0 0 0 2px var(--semantic-interaction-overlay-10);
188
+ outline: 2px solid transparent;
189
+ outline-offset: 2px;
190
+ }
191
+ .lib-xplat-select-content .select-empty {
192
+ padding: var(--spacing-space-3);
193
+ text-align: center;
194
+ color: var(--semantic-text-tertiary);
195
+ font-family: var(--semantic-typo-label-2-m-font);
196
+ font-size: var(--semantic-typo-label-2-m-size);
197
+ line-height: var(--semantic-typo-label-2-m-lh);
198
+ font-weight: var(--semantic-typo-label-2-m-weight);
199
+ letter-spacing: var(--semantic-typo-label-2-m-ls);
200
+ }
147
201
  .lib-xplat-select-content {
148
202
  position: fixed;
149
203
  z-index: 1000;
@@ -219,3 +273,6 @@
219
273
  cursor: not-allowed;
220
274
  color: var(--semantic-text-disabled);
221
275
  }
276
+ .lib-xplat-select-content.repositioning {
277
+ transition: none;
278
+ }
@@ -38,6 +38,17 @@ interface SelectProps {
38
38
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
39
39
  validations?: Validation[];
40
40
  /** 메시지 없이 테두리만 붉게 한다. validations 와 함께 쓰면 둘 중 하나만 참이어도 적용된다 */
41
+ /** 컨트롤 위에 붙는 라벨. 클릭하면 컨트롤에 포커스가 간다 */
42
+ label?: React.ReactNode;
43
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
44
+ required?: boolean;
45
+ /**
46
+ * 목록 위에 검색칸을 띄운다. 항목이 많아 스크롤로 찾기 어려울 때 켠다.
47
+ * 항목의 children 텍스트로 걸러낸다
48
+ */
49
+ searchable?: boolean;
50
+ /** 검색칸 안내 문구 */
51
+ searchPlaceholder?: string;
41
52
  error?: boolean;
42
53
  /** 사이즈 */
43
54
  size?: SelectSize;
@@ -38,6 +38,17 @@ interface SelectProps {
38
38
  /** 컨트롤 아래에 표시할 검증 메시지. DS 폼 컴포넌트 공통 */
39
39
  validations?: Validation[];
40
40
  /** 메시지 없이 테두리만 붉게 한다. validations 와 함께 쓰면 둘 중 하나만 참이어도 적용된다 */
41
+ /** 컨트롤 위에 붙는 라벨. 클릭하면 컨트롤에 포커스가 간다 */
42
+ label?: React.ReactNode;
43
+ /** 라벨 뒤에 필수 표시(*)를 붙인다 */
44
+ required?: boolean;
45
+ /**
46
+ * 목록 위에 검색칸을 띄운다. 항목이 많아 스크롤로 찾기 어려울 때 켠다.
47
+ * 항목의 children 텍스트로 걸러낸다
48
+ */
49
+ searchable?: boolean;
50
+ /** 검색칸 안내 문구 */
51
+ searchPlaceholder?: string;
41
52
  error?: boolean;
42
53
  /** 사이즈 */
43
54
  size?: SelectSize;
@@ -1,5 +1,5 @@
1
1
  // src/components/Select/Select.tsx
2
- import React5 from "react";
2
+ import React6 from "react";
3
3
 
4
4
  // src/tokens/hooks/Portal.tsx
5
5
  import ReactDOM from "react-dom";
@@ -15,8 +15,10 @@ import React from "react";
15
15
  var useAutoPosition = (triggerRef, popRef, enabled = true) => {
16
16
  const [position, setPosition] = React.useState({
17
17
  position: {},
18
- direction: "bottom"
18
+ direction: "bottom",
19
+ repositioning: false
19
20
  });
21
+ const placedRef = React.useRef(false);
20
22
  const calculatePosition = React.useCallback(() => {
21
23
  if (!triggerRef.current || !popRef.current) return;
22
24
  const triggerRect = triggerRef.current.getBoundingClientRect();
@@ -41,11 +43,16 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
41
43
  if (left < 8) left = 8;
42
44
  setPosition({
43
45
  position: { top, left },
44
- direction
46
+ direction,
47
+ repositioning: placedRef.current
45
48
  });
49
+ placedRef.current = true;
46
50
  }, [triggerRef, popRef]);
47
51
  React.useLayoutEffect(() => {
48
- if (!enabled) return;
52
+ if (!enabled) {
53
+ placedRef.current = false;
54
+ return;
55
+ }
49
56
  calculatePosition();
50
57
  const raf = requestAnimationFrame(calculatePosition);
51
58
  return () => cancelAnimationFrame(raf);
@@ -60,11 +67,17 @@ var useAutoPosition = (triggerRef, popRef, enabled = true) => {
60
67
  }, [calculatePosition, enabled, popRef]);
61
68
  React.useEffect(() => {
62
69
  if (!enabled) return;
63
- window.addEventListener("resize", calculatePosition);
64
- window.addEventListener("scroll", calculatePosition, true);
70
+ let raf = 0;
71
+ const onScrollOrResize = () => {
72
+ cancelAnimationFrame(raf);
73
+ raf = requestAnimationFrame(calculatePosition);
74
+ };
75
+ window.addEventListener("resize", onScrollOrResize, { passive: true });
76
+ window.addEventListener("scroll", onScrollOrResize, { passive: true, capture: true });
65
77
  return () => {
66
- window.removeEventListener("resize", calculatePosition);
67
- window.removeEventListener("scroll", calculatePosition, true);
78
+ cancelAnimationFrame(raf);
79
+ window.removeEventListener("resize", onScrollOrResize);
80
+ window.removeEventListener("scroll", onScrollOrResize, true);
68
81
  };
69
82
  }, [calculatePosition, enabled]);
70
83
  return position;
@@ -96,6 +109,30 @@ var useClickOutside = (refs, handler, enabled = true) => {
96
109
  };
97
110
  var useClickOutside_default = useClickOutside;
98
111
 
112
+ // src/tokens/hooks/useOverlayKeyboard.ts
113
+ import React3 from "react";
114
+ var FOCUSABLE = [
115
+ "a[href]",
116
+ "button:not([disabled])",
117
+ "input:not([disabled]):not([type='hidden'])",
118
+ "select:not([disabled])",
119
+ "textarea:not([disabled])",
120
+ "[tabindex]:not([tabindex='-1'])"
121
+ ].join(",");
122
+ var useEscapeKey = (enabled, onEscape) => {
123
+ React3.useEffect(() => {
124
+ if (!enabled) return;
125
+ const onKey = (e) => {
126
+ if (e.key !== "Escape") return;
127
+ if (e.defaultPrevented) return;
128
+ e.preventDefault();
129
+ onEscape();
130
+ };
131
+ document.addEventListener("keydown", onKey);
132
+ return () => document.removeEventListener("keydown", onKey);
133
+ }, [enabled, onEscape]);
134
+ };
135
+
99
136
  // src/tokens/svg/action/CopyIcon.tsx
100
137
  import { jsx, jsxs } from "react/jsx-runtime";
101
138
 
@@ -1126,27 +1163,32 @@ var FieldMessage = (props) => {
1126
1163
  };
1127
1164
  FieldMessage.displayName = "FieldMessage";
1128
1165
  var Field = (props) => {
1129
- const { validations, children } = props;
1130
- if (!validations || validations.length === 0) return /* @__PURE__ */ jsx295(Fragment, { children });
1166
+ const { label, required = false, htmlFor, validations, children } = props;
1167
+ const hasMessages = !!validations && validations.length > 0;
1168
+ if (!label && !hasMessages) return /* @__PURE__ */ jsx295(Fragment, { children });
1131
1169
  return /* @__PURE__ */ jsxs189("div", { className: "lib-xplat-field", children: [
1170
+ label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", htmlFor, children: [
1171
+ label,
1172
+ required && /* @__PURE__ */ jsx295("span", { className: "required", "aria-hidden": "true", children: "*" })
1173
+ ] }),
1132
1174
  children,
1133
- /* @__PURE__ */ jsx295(FieldMessage, { validations })
1175
+ hasMessages && /* @__PURE__ */ jsx295(FieldMessage, { validations })
1134
1176
  ] });
1135
1177
  };
1136
1178
  Field.displayName = "Field";
1137
1179
  var hasError = (validations) => !!validations?.some((v) => v.status === "error");
1138
1180
 
1139
1181
  // src/components/Select/context.ts
1140
- import React3 from "react";
1141
- var SelectContext = React3.createContext(null);
1182
+ import React4 from "react";
1183
+ var SelectContext = React4.createContext(null);
1142
1184
  var context_default = SelectContext;
1143
1185
 
1144
1186
  // src/components/Select/SelectItem.tsx
1145
- import React4 from "react";
1187
+ import React5 from "react";
1146
1188
  import { jsx as jsx296 } from "react/jsx-runtime";
1147
1189
  var SelectItem = (props) => {
1148
1190
  const { children, value, onClick, disabled = false } = props;
1149
- const ctx = React4.useContext(context_default);
1191
+ const ctx = React5.useContext(context_default);
1150
1192
  const handleClick = (e) => {
1151
1193
  e.preventDefault();
1152
1194
  e.stopPropagation();
@@ -1186,32 +1228,37 @@ var SelectRoot = (props) => {
1186
1228
  onChange,
1187
1229
  children,
1188
1230
  disabled = false,
1231
+ searchable = false,
1232
+ searchPlaceholder = "\uAC80\uC0C9",
1189
1233
  error = false,
1190
1234
  validations,
1235
+ label,
1236
+ required,
1191
1237
  size = "md"
1192
1238
  } = props;
1193
- const itemChildren = React5.Children.toArray(children).filter(
1194
- (child) => React5.isValidElement(child) && child.type === SelectItem_default
1239
+ const fieldId = React6.useId();
1240
+ const itemChildren = React6.Children.toArray(children).filter(
1241
+ (child) => React6.isValidElement(child) && child.type === SelectItem_default
1195
1242
  );
1196
1243
  const isControlled = valueProp !== void 0;
1197
- const [isOpen, setOpen] = React5.useState(false);
1198
- const [uncontrolledLabel, setUncontrolledLabel] = React5.useState(null);
1199
- const [uncontrolledValue, setUncontrolledValue] = React5.useState(void 0);
1200
- const controlledLabel = React5.useMemo(() => {
1244
+ const [isOpen, setOpen] = React6.useState(false);
1245
+ const [uncontrolledLabel, setUncontrolledLabel] = React6.useState(null);
1246
+ const [uncontrolledValue, setUncontrolledValue] = React6.useState(void 0);
1247
+ const controlledLabel = React6.useMemo(() => {
1201
1248
  if (!isControlled) return null;
1202
1249
  const match = itemChildren.find((child) => child.props.value === valueProp);
1203
1250
  return match ? match.props.children : null;
1204
1251
  }, [isControlled, valueProp, itemChildren]);
1205
1252
  const selectedLabel = isControlled ? controlledLabel : uncontrolledLabel;
1206
1253
  const selectedValue = isControlled ? valueProp : uncontrolledValue;
1207
- const triggerRef = React5.useRef(null);
1208
- const contentRef = React5.useRef(null);
1209
- const [mounted, setMounted] = React5.useState(false);
1210
- const [visible, setVisible] = React5.useState(false);
1211
- React5.useEffect(() => {
1254
+ const triggerRef = React6.useRef(null);
1255
+ const contentRef = React6.useRef(null);
1256
+ const [mounted, setMounted] = React6.useState(false);
1257
+ const [visible, setVisible] = React6.useState(false);
1258
+ React6.useEffect(() => {
1212
1259
  if (disabled && isOpen) setOpen(false);
1213
1260
  }, [disabled, isOpen]);
1214
- React5.useEffect(() => {
1261
+ React6.useEffect(() => {
1215
1262
  if (isOpen) {
1216
1263
  setMounted(true);
1217
1264
  const t2 = setTimeout(() => setVisible(true), 1);
@@ -1221,22 +1268,42 @@ var SelectRoot = (props) => {
1221
1268
  const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS);
1222
1269
  return () => clearTimeout(t);
1223
1270
  }, [isOpen]);
1224
- const open = React5.useCallback(() => setOpen(true), []);
1225
- const close = React5.useCallback(() => setOpen(false), []);
1226
- const toggle = React5.useCallback(() => setOpen((prev) => !prev), []);
1271
+ const [query, setQuery] = React6.useState("");
1272
+ const textOf = (node) => {
1273
+ if (node === null || node === void 0 || typeof node === "boolean") return "";
1274
+ if (typeof node === "string" || typeof node === "number") return String(node);
1275
+ if (Array.isArray(node)) return node.map(textOf).join("");
1276
+ if (React6.isValidElement(node)) return textOf(node.props.children);
1277
+ return "";
1278
+ };
1279
+ const visibleItems = React6.useMemo(() => {
1280
+ if (!searchable || !query.trim()) return itemChildren;
1281
+ const q = query.trim().toLowerCase();
1282
+ return itemChildren.filter((c) => textOf(c.props.children).toLowerCase().includes(q));
1283
+ }, [searchable, query, itemChildren]);
1284
+ const open = React6.useCallback(() => setOpen(true), []);
1285
+ const close = React6.useCallback(() => {
1286
+ setOpen(false);
1287
+ setQuery("");
1288
+ }, []);
1289
+ const toggle = React6.useCallback(() => setOpen((prev) => !prev), []);
1227
1290
  useClickOutside_default([contentRef, triggerRef], close, isOpen);
1291
+ useEscapeKey(isOpen, () => {
1292
+ close();
1293
+ triggerRef.current?.focus({ preventScroll: true });
1294
+ });
1228
1295
  const position = useAutoPosition_default(triggerRef, contentRef, mounted);
1229
- const setSelected = React5.useCallback(
1230
- (label, itemValue) => {
1296
+ const setSelected = React6.useCallback(
1297
+ (label2, itemValue) => {
1231
1298
  if (!isControlled) {
1232
- setUncontrolledLabel(label);
1299
+ setUncontrolledLabel(label2);
1233
1300
  setUncontrolledValue(itemValue);
1234
1301
  }
1235
- onChange?.(itemValue, label);
1302
+ onChange?.(itemValue, label2);
1236
1303
  },
1237
1304
  [isControlled, onChange]
1238
1305
  );
1239
- const ctxValue = React5.useMemo(
1306
+ const ctxValue = React6.useMemo(
1240
1307
  () => ({
1241
1308
  isOpen,
1242
1309
  mounted,
@@ -1272,6 +1339,7 @@ var SelectRoot = (props) => {
1272
1339
  /* @__PURE__ */ jsxs190(
1273
1340
  "div",
1274
1341
  {
1342
+ id: fieldId,
1275
1343
  ref: triggerRef,
1276
1344
  className: clsx_default(
1277
1345
  "select-trigger",
@@ -1314,20 +1382,33 @@ var SelectRoot = (props) => {
1314
1382
  ]
1315
1383
  }
1316
1384
  ),
1317
- mounted && /* @__PURE__ */ jsx297(Portal_default, { children: /* @__PURE__ */ jsx297(
1385
+ mounted && /* @__PURE__ */ jsx297(Portal_default, { children: /* @__PURE__ */ jsxs190(
1318
1386
  "div",
1319
1387
  {
1320
- className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass),
1388
+ className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass, position.repositioning && "repositioning"),
1321
1389
  ref: contentRef,
1322
1390
  style: { ...position.position, width: triggerRef.current?.offsetWidth },
1323
1391
  role: "listbox",
1324
- children: /* @__PURE__ */ jsx297(context_default.Provider, { value: ctxValue, children: itemChildren })
1392
+ children: [
1393
+ searchable && /* @__PURE__ */ jsx297("div", { className: "select-search", children: /* @__PURE__ */ jsx297(
1394
+ "input",
1395
+ {
1396
+ type: "text",
1397
+ value: query,
1398
+ placeholder: searchPlaceholder,
1399
+ onChange: (e) => setQuery(e.target.value),
1400
+ autoFocus: true,
1401
+ "aria-label": searchPlaceholder
1402
+ }
1403
+ ) }),
1404
+ /* @__PURE__ */ jsx297(context_default.Provider, { value: ctxValue, children: visibleItems.length > 0 ? visibleItems : /* @__PURE__ */ jsx297("div", { className: "select-empty", children: "\uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4" }) })
1405
+ ]
1325
1406
  }
1326
1407
  ) })
1327
1408
  ]
1328
1409
  }
1329
1410
  ) });
1330
- return /* @__PURE__ */ jsx297(Field, { validations, children: control });
1411
+ return /* @__PURE__ */ jsx297(Field, { validations, label, required, htmlFor: fieldId, children: control });
1331
1412
  };
1332
1413
  SelectRoot.displayName = "Select";
1333
1414
  var Select = Object.assign(SelectRoot, {
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { L as LegacyStatusType } from '../../status-g_KP3qT6.cjs';
2
+ import { L as LegacyStatusType } from '../../status-njWqQWFV.cjs';
3
3
 
4
4
  type SpinnerSize = "sm" | "md" | "lg";
5
5
  /** Spinner 의 상태 색 */
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { L as LegacyStatusType } from '../../status-g_KP3qT6.js';
2
+ import { L as LegacyStatusType } from '../../status-njWqQWFV.js';
3
3
 
4
4
  type SpinnerSize = "sm" | "md" | "lg";
5
5
  /** Spinner 의 상태 색 */
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/components/Stat/index.ts
21
+ var Stat_exports = {};
22
+ __export(Stat_exports, {
23
+ Stat: () => Stat_default
24
+ });
25
+ module.exports = __toCommonJS(Stat_exports);
26
+
27
+ // src/tokens/interface/status.ts
28
+ var normalizeStatus = (value) => value === "brand" ? "primary" : value;
29
+
30
+ // ../../node_modules/clsx/dist/clsx.mjs
31
+ function r(e) {
32
+ var t, f, n = "";
33
+ if ("string" == typeof e || "number" == typeof e) n += e;
34
+ else if ("object" == typeof e) if (Array.isArray(e)) {
35
+ var o = e.length;
36
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
37
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
38
+ return n;
39
+ }
40
+ function clsx() {
41
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
42
+ return n;
43
+ }
44
+ var clsx_default = clsx;
45
+
46
+ // src/components/Stat/Stat.tsx
47
+ var import_jsx_runtime = require("react/jsx-runtime");
48
+ var ARROW = {
49
+ up: "\u25B2",
50
+ down: "\u25BC",
51
+ flat: "\u2014"
52
+ };
53
+ var Stat = (props) => {
54
+ const {
55
+ label,
56
+ value,
57
+ unit,
58
+ description,
59
+ trend,
60
+ icon,
61
+ size = "md",
62
+ type = "primary"
63
+ } = props;
64
+ const statusType = normalizeStatus(type);
65
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: clsx_default("lib-xplat-stat", size, statusType), children: [
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-label", children: label }),
67
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stat-value-row", children: [
68
+ icon && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "stat-icon", children: icon }),
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "stat-value", children: value }),
70
+ unit && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "stat-unit", children: unit }),
71
+ trend && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: clsx_default("stat-trend", trend.direction), children: [
72
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": "true", children: ARROW[trend.direction] }),
73
+ trend.value
74
+ ] })
75
+ ] }),
76
+ description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stat-description", children: description })
77
+ ] });
78
+ };
79
+ Stat.displayName = "Stat";
80
+ var Stat_default = Stat;
81
+ // Annotate the CommonJS export names for ESM import in node:
82
+ 0 && (module.exports = {
83
+ Stat
84
+ });
@@ -0,0 +1,96 @@
1
+ /* src/components/Stat/stat.scss */
2
+ .lib-xplat-stat {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: var(--spacing-space-1);
6
+ }
7
+ .lib-xplat-stat .stat-label {
8
+ font-family: var(--semantic-typo-caption-1-r-font);
9
+ font-size: var(--semantic-typo-caption-1-r-size);
10
+ line-height: var(--semantic-typo-caption-1-r-lh);
11
+ font-weight: var(--semantic-typo-caption-1-r-weight);
12
+ letter-spacing: var(--semantic-typo-caption-1-r-ls);
13
+ color: var(--semantic-text-tertiary);
14
+ }
15
+ .lib-xplat-stat .stat-value-row {
16
+ display: flex;
17
+ align-items: baseline;
18
+ gap: var(--spacing-space-2);
19
+ }
20
+ .lib-xplat-stat .stat-icon {
21
+ display: flex;
22
+ align-items: center;
23
+ align-self: center;
24
+ color: var(--semantic-icon-secondary);
25
+ }
26
+ .lib-xplat-stat .stat-value {
27
+ font-family: var(--semantic-typo-title-1-sb-font);
28
+ font-size: var(--semantic-typo-title-1-sb-size);
29
+ line-height: var(--semantic-typo-title-1-sb-lh);
30
+ font-weight: var(--semantic-typo-title-1-sb-weight);
31
+ letter-spacing: var(--semantic-typo-title-1-sb-ls);
32
+ color: var(--semantic-text-primary);
33
+ font-variant-numeric: tabular-nums;
34
+ }
35
+ .lib-xplat-stat .stat-unit {
36
+ font-family: var(--semantic-typo-label-2-m-font);
37
+ font-size: var(--semantic-typo-label-2-m-size);
38
+ line-height: var(--semantic-typo-label-2-m-lh);
39
+ font-weight: var(--semantic-typo-label-2-m-weight);
40
+ letter-spacing: var(--semantic-typo-label-2-m-ls);
41
+ color: var(--semantic-text-tertiary);
42
+ }
43
+ .lib-xplat-stat .stat-trend {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ gap: var(--spacing-space-1);
47
+ font-family: var(--semantic-typo-caption-1-m-font);
48
+ font-size: var(--semantic-typo-caption-1-m-size);
49
+ line-height: var(--semantic-typo-caption-1-m-lh);
50
+ font-weight: var(--semantic-typo-caption-1-m-weight);
51
+ letter-spacing: var(--semantic-typo-caption-1-m-ls);
52
+ font-variant-numeric: tabular-nums;
53
+ }
54
+ .lib-xplat-stat .stat-trend.up {
55
+ color: var(--semantic-text-success);
56
+ }
57
+ .lib-xplat-stat .stat-trend.down {
58
+ color: var(--semantic-text-error);
59
+ }
60
+ .lib-xplat-stat .stat-trend.flat {
61
+ color: var(--semantic-text-tertiary);
62
+ }
63
+ .lib-xplat-stat .stat-description {
64
+ font-family: var(--semantic-typo-caption-2-r-font);
65
+ font-size: var(--semantic-typo-caption-2-r-size);
66
+ line-height: var(--semantic-typo-caption-2-r-lh);
67
+ font-weight: var(--semantic-typo-caption-2-r-weight);
68
+ letter-spacing: var(--semantic-typo-caption-2-r-ls);
69
+ color: var(--semantic-text-tertiary);
70
+ }
71
+ .lib-xplat-stat.sm .stat-value {
72
+ font-family: var(--semantic-typo-heading-1-sb-font);
73
+ font-size: var(--semantic-typo-heading-1-sb-size);
74
+ line-height: var(--semantic-typo-heading-1-sb-lh);
75
+ font-weight: var(--semantic-typo-heading-1-sb-weight);
76
+ letter-spacing: var(--semantic-typo-heading-1-sb-ls);
77
+ }
78
+ .lib-xplat-stat.lg .stat-value {
79
+ font-family: var(--semantic-typo-display-2-sb-font);
80
+ font-size: var(--semantic-typo-display-2-sb-size);
81
+ line-height: var(--semantic-typo-display-2-sb-lh);
82
+ font-weight: var(--semantic-typo-display-2-sb-weight);
83
+ letter-spacing: var(--semantic-typo-display-2-sb-ls);
84
+ }
85
+ .lib-xplat-stat.success .stat-value {
86
+ color: var(--semantic-text-success);
87
+ }
88
+ .lib-xplat-stat.error .stat-value {
89
+ color: var(--semantic-text-error);
90
+ }
91
+ .lib-xplat-stat.warning .stat-value {
92
+ color: var(--semantic-text-warning);
93
+ }
94
+ .lib-xplat-stat.info .stat-value {
95
+ color: var(--semantic-text-info);
96
+ }
@@ -0,0 +1,37 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+ import { S as StatusTypeInput } from '../../status-njWqQWFV.cjs';
4
+
5
+ type StatSize = "sm" | "md" | "lg";
6
+ type TrendDirection = "up" | "down" | "flat";
7
+ /** 큰 숫자 하나를 강조해 보여주는 칸. 대시보드의 지표 타일에 쓴다 */
8
+ interface StatProps {
9
+ /** 지표 이름 (예: "총 호출 수") */
10
+ label: React.ReactNode;
11
+ /** 강조할 값. 포맷은 호출부가 정한다 (천 단위 구분·통화 기호 등) */
12
+ value: React.ReactNode;
13
+ /** 값 뒤에 붙는 단위 (예: "원", "회"). 값보다 작게 표시된다 */
14
+ unit?: React.ReactNode;
15
+ /** 값 아래 보조 설명 */
16
+ description?: React.ReactNode;
17
+ /**
18
+ * 증감 표시. `value` 는 이미 포맷된 문자열을 그대로 쓴다 (예: "+12.4%").
19
+ * `direction` 이 색과 화살표를 정한다
20
+ */
21
+ trend?: {
22
+ direction: TrendDirection;
23
+ value: React.ReactNode;
24
+ };
25
+ /** 값 왼쪽에 놓을 아이콘 */
26
+ icon?: React.ReactNode;
27
+ /** 타일 크기 */
28
+ size?: StatSize;
29
+ /** 값 색. 상태를 나타낼 때 쓴다 */
30
+ type?: StatusTypeInput;
31
+ }
32
+ declare const Stat: {
33
+ (props: StatProps): react_jsx_runtime.JSX.Element;
34
+ displayName: string;
35
+ };
36
+
37
+ export { Stat };
@@ -0,0 +1,37 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+ import { S as StatusTypeInput } from '../../status-njWqQWFV.js';
4
+
5
+ type StatSize = "sm" | "md" | "lg";
6
+ type TrendDirection = "up" | "down" | "flat";
7
+ /** 큰 숫자 하나를 강조해 보여주는 칸. 대시보드의 지표 타일에 쓴다 */
8
+ interface StatProps {
9
+ /** 지표 이름 (예: "총 호출 수") */
10
+ label: React.ReactNode;
11
+ /** 강조할 값. 포맷은 호출부가 정한다 (천 단위 구분·통화 기호 등) */
12
+ value: React.ReactNode;
13
+ /** 값 뒤에 붙는 단위 (예: "원", "회"). 값보다 작게 표시된다 */
14
+ unit?: React.ReactNode;
15
+ /** 값 아래 보조 설명 */
16
+ description?: React.ReactNode;
17
+ /**
18
+ * 증감 표시. `value` 는 이미 포맷된 문자열을 그대로 쓴다 (예: "+12.4%").
19
+ * `direction` 이 색과 화살표를 정한다
20
+ */
21
+ trend?: {
22
+ direction: TrendDirection;
23
+ value: React.ReactNode;
24
+ };
25
+ /** 값 왼쪽에 놓을 아이콘 */
26
+ icon?: React.ReactNode;
27
+ /** 타일 크기 */
28
+ size?: StatSize;
29
+ /** 값 색. 상태를 나타낼 때 쓴다 */
30
+ type?: StatusTypeInput;
31
+ }
32
+ declare const Stat: {
33
+ (props: StatProps): react_jsx_runtime.JSX.Element;
34
+ displayName: string;
35
+ };
36
+
37
+ export { Stat };