@x-plat/design-system 0.10.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.
- package/dist/components/AutoResizeTextArea/index.cjs +14 -5
- package/dist/components/AutoResizeTextArea/index.css +15 -0
- package/dist/components/AutoResizeTextArea/index.d.cts +4 -0
- package/dist/components/AutoResizeTextArea/index.d.ts +4 -0
- package/dist/components/AutoResizeTextArea/index.js +14 -5
- package/dist/components/Badge/index.d.cts +1 -1
- package/dist/components/Badge/index.d.ts +1 -1
- package/dist/components/ChatInput/index.cjs +20 -7
- package/dist/components/ChatInput/index.css +15 -0
- package/dist/components/ChatInput/index.d.cts +4 -0
- package/dist/components/ChatInput/index.d.ts +4 -0
- package/dist/components/ChatInput/index.js +20 -7
- package/dist/components/CheckBox/index.cjs +8 -3
- package/dist/components/CheckBox/index.css +15 -0
- package/dist/components/CheckBox/index.d.cts +1 -1
- package/dist/components/CheckBox/index.d.ts +1 -1
- package/dist/components/CheckBox/index.js +8 -3
- package/dist/components/DatePicker/index.cjs +179 -64
- package/dist/components/DatePicker/index.css +19 -0
- package/dist/components/DatePicker/index.js +179 -64
- package/dist/components/Drawer/index.cjs +83 -4
- package/dist/components/Drawer/index.js +83 -4
- package/dist/components/Dropdown/index.cjs +35 -7
- package/dist/components/Dropdown/index.js +35 -7
- package/dist/components/Editor/index.cjs +178 -21
- package/dist/components/Editor/index.js +178 -21
- package/dist/components/FieldMessage/index.cjs +8 -3
- package/dist/components/FieldMessage/index.css +15 -0
- package/dist/components/FieldMessage/index.d.cts +11 -4
- package/dist/components/FieldMessage/index.d.ts +11 -4
- package/dist/components/FieldMessage/index.js +8 -3
- package/dist/components/FileUpload/index.cjs +8 -3
- package/dist/components/FileUpload/index.css +15 -0
- package/dist/components/FileUpload/index.js +8 -3
- package/dist/components/ImageSelector/index.cjs +8 -3
- package/dist/components/ImageSelector/index.css +15 -0
- package/dist/components/ImageSelector/index.js +8 -3
- package/dist/components/Input/index.cjs +15 -5
- package/dist/components/Input/index.css +15 -0
- package/dist/components/Input/index.d.cts +4 -0
- package/dist/components/Input/index.d.ts +4 -0
- package/dist/components/Input/index.js +15 -5
- package/dist/components/Modal/index.cjs +84 -5
- package/dist/components/Modal/index.js +84 -5
- package/dist/components/NotificationBadge/index.d.cts +1 -1
- package/dist/components/NotificationBadge/index.d.ts +1 -1
- package/dist/components/Pagination/index.d.cts +1 -1
- package/dist/components/Pagination/index.d.ts +1 -1
- package/dist/components/PopOver/index.cjs +34 -6
- package/dist/components/PopOver/index.js +34 -6
- package/dist/components/Progress/index.d.cts +1 -1
- package/dist/components/Progress/index.d.ts +1 -1
- package/dist/components/Radio/index.cjs +8 -3
- package/dist/components/Radio/index.css +15 -0
- package/dist/components/Radio/index.d.cts +1 -1
- package/dist/components/Radio/index.d.ts +1 -1
- package/dist/components/Radio/index.js +8 -3
- package/dist/components/Select/index.cjs +99 -31
- package/dist/components/Select/index.css +54 -0
- package/dist/components/Select/index.d.cts +11 -0
- package/dist/components/Select/index.d.ts +11 -0
- package/dist/components/Select/index.js +99 -31
- package/dist/components/Spinner/index.d.cts +1 -1
- package/dist/components/Spinner/index.d.ts +1 -1
- package/dist/components/Stat/index.cjs +84 -0
- package/dist/components/Stat/index.css +96 -0
- package/dist/components/Stat/index.d.cts +37 -0
- package/dist/components/Stat/index.d.ts +37 -0
- package/dist/components/Stat/index.js +57 -0
- package/dist/components/Steps/index.d.cts +1 -1
- package/dist/components/Steps/index.d.ts +1 -1
- package/dist/components/Switch/index.cjs +14 -5
- package/dist/components/Switch/index.css +15 -0
- package/dist/components/Switch/index.d.cts +6 -1
- package/dist/components/Switch/index.d.ts +6 -1
- package/dist/components/Switch/index.js +14 -5
- package/dist/components/Tab/index.cjs +49 -25
- package/dist/components/Tab/index.css +4 -0
- package/dist/components/Tab/index.js +49 -25
- package/dist/components/TextArea/index.cjs +14 -4
- package/dist/components/TextArea/index.css +15 -0
- package/dist/components/TextArea/index.d.cts +4 -0
- package/dist/components/TextArea/index.d.ts +4 -0
- package/dist/components/TextArea/index.js +14 -4
- package/dist/components/TimePicker/index.cjs +448 -0
- package/dist/components/TimePicker/index.css +172 -0
- package/dist/components/TimePicker/index.d.cts +42 -0
- package/dist/components/TimePicker/index.d.ts +42 -0
- package/dist/components/TimePicker/index.js +411 -0
- package/dist/components/index.cjs +873 -359
- package/dist/components/index.css +275 -0
- package/dist/components/index.d.cts +3 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.js +871 -359
- package/dist/index.cjs +897 -383
- package/dist/index.css +275 -0
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +895 -383
- package/dist/status-njWqQWFV.d.cts +20 -0
- package/dist/status-njWqQWFV.d.ts +20 -0
- package/guidelines/AGENT_PROMPT.md +3 -3
- package/guidelines/Guidelines.md +3 -0
- package/guidelines/components/editor.md +86 -0
- package/guidelines/components/form.md +19 -0
- package/guidelines/components/stat.md +31 -0
- package/guidelines/components/time-picker.md +49 -0
- package/package.json +1 -1
- package/dist/status-g_KP3qT6.d.cts +0 -8
- package/dist/status-g_KP3qT6.d.ts +0 -8
|
@@ -36,7 +36,7 @@ __export(Select_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(Select_exports);
|
|
37
37
|
|
|
38
38
|
// src/components/Select/Select.tsx
|
|
39
|
-
var
|
|
39
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
40
40
|
|
|
41
41
|
// src/tokens/hooks/Portal.tsx
|
|
42
42
|
var import_react_dom = __toESM(require("react-dom"), 1);
|
|
@@ -133,6 +133,30 @@ var useClickOutside = (refs, handler, enabled = true) => {
|
|
|
133
133
|
};
|
|
134
134
|
var useClickOutside_default = useClickOutside;
|
|
135
135
|
|
|
136
|
+
// src/tokens/hooks/useOverlayKeyboard.ts
|
|
137
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
138
|
+
var FOCUSABLE = [
|
|
139
|
+
"a[href]",
|
|
140
|
+
"button:not([disabled])",
|
|
141
|
+
"input:not([disabled]):not([type='hidden'])",
|
|
142
|
+
"select:not([disabled])",
|
|
143
|
+
"textarea:not([disabled])",
|
|
144
|
+
"[tabindex]:not([tabindex='-1'])"
|
|
145
|
+
].join(",");
|
|
146
|
+
var useEscapeKey = (enabled, onEscape) => {
|
|
147
|
+
import_react3.default.useEffect(() => {
|
|
148
|
+
if (!enabled) return;
|
|
149
|
+
const onKey = (e) => {
|
|
150
|
+
if (e.key !== "Escape") return;
|
|
151
|
+
if (e.defaultPrevented) return;
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
onEscape();
|
|
154
|
+
};
|
|
155
|
+
document.addEventListener("keydown", onKey);
|
|
156
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
157
|
+
}, [enabled, onEscape]);
|
|
158
|
+
};
|
|
159
|
+
|
|
136
160
|
// src/tokens/svg/action/CopyIcon.tsx
|
|
137
161
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
138
162
|
|
|
@@ -1163,27 +1187,32 @@ var FieldMessage = (props) => {
|
|
|
1163
1187
|
};
|
|
1164
1188
|
FieldMessage.displayName = "FieldMessage";
|
|
1165
1189
|
var Field = (props) => {
|
|
1166
|
-
const { validations, children } = props;
|
|
1167
|
-
|
|
1190
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
1191
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
1192
|
+
if (!label && !hasMessages) return /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(import_jsx_runtime295.Fragment, { children });
|
|
1168
1193
|
return /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)("div", { className: "lib-xplat-field", children: [
|
|
1194
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime295.jsxs)("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
1195
|
+
label,
|
|
1196
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime295.jsx)("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
1197
|
+
] }),
|
|
1169
1198
|
children,
|
|
1170
|
-
/* @__PURE__ */ (0, import_jsx_runtime295.jsx)(FieldMessage, { validations })
|
|
1199
|
+
hasMessages && /* @__PURE__ */ (0, import_jsx_runtime295.jsx)(FieldMessage, { validations })
|
|
1171
1200
|
] });
|
|
1172
1201
|
};
|
|
1173
1202
|
Field.displayName = "Field";
|
|
1174
1203
|
var hasError = (validations) => !!validations?.some((v) => v.status === "error");
|
|
1175
1204
|
|
|
1176
1205
|
// src/components/Select/context.ts
|
|
1177
|
-
var
|
|
1178
|
-
var SelectContext =
|
|
1206
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
1207
|
+
var SelectContext = import_react4.default.createContext(null);
|
|
1179
1208
|
var context_default = SelectContext;
|
|
1180
1209
|
|
|
1181
1210
|
// src/components/Select/SelectItem.tsx
|
|
1182
|
-
var
|
|
1211
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
1183
1212
|
var import_jsx_runtime296 = require("react/jsx-runtime");
|
|
1184
1213
|
var SelectItem = (props) => {
|
|
1185
1214
|
const { children, value, onClick, disabled = false } = props;
|
|
1186
|
-
const ctx =
|
|
1215
|
+
const ctx = import_react5.default.useContext(context_default);
|
|
1187
1216
|
const handleClick = (e) => {
|
|
1188
1217
|
e.preventDefault();
|
|
1189
1218
|
e.stopPropagation();
|
|
@@ -1223,32 +1252,37 @@ var SelectRoot = (props) => {
|
|
|
1223
1252
|
onChange,
|
|
1224
1253
|
children,
|
|
1225
1254
|
disabled = false,
|
|
1255
|
+
searchable = false,
|
|
1256
|
+
searchPlaceholder = "\uAC80\uC0C9",
|
|
1226
1257
|
error = false,
|
|
1227
1258
|
validations,
|
|
1259
|
+
label,
|
|
1260
|
+
required,
|
|
1228
1261
|
size = "md"
|
|
1229
1262
|
} = props;
|
|
1230
|
-
const
|
|
1231
|
-
|
|
1263
|
+
const fieldId = import_react6.default.useId();
|
|
1264
|
+
const itemChildren = import_react6.default.Children.toArray(children).filter(
|
|
1265
|
+
(child) => import_react6.default.isValidElement(child) && child.type === SelectItem_default
|
|
1232
1266
|
);
|
|
1233
1267
|
const isControlled = valueProp !== void 0;
|
|
1234
|
-
const [isOpen, setOpen] =
|
|
1235
|
-
const [uncontrolledLabel, setUncontrolledLabel] =
|
|
1236
|
-
const [uncontrolledValue, setUncontrolledValue] =
|
|
1237
|
-
const controlledLabel =
|
|
1268
|
+
const [isOpen, setOpen] = import_react6.default.useState(false);
|
|
1269
|
+
const [uncontrolledLabel, setUncontrolledLabel] = import_react6.default.useState(null);
|
|
1270
|
+
const [uncontrolledValue, setUncontrolledValue] = import_react6.default.useState(void 0);
|
|
1271
|
+
const controlledLabel = import_react6.default.useMemo(() => {
|
|
1238
1272
|
if (!isControlled) return null;
|
|
1239
1273
|
const match = itemChildren.find((child) => child.props.value === valueProp);
|
|
1240
1274
|
return match ? match.props.children : null;
|
|
1241
1275
|
}, [isControlled, valueProp, itemChildren]);
|
|
1242
1276
|
const selectedLabel = isControlled ? controlledLabel : uncontrolledLabel;
|
|
1243
1277
|
const selectedValue = isControlled ? valueProp : uncontrolledValue;
|
|
1244
|
-
const triggerRef =
|
|
1245
|
-
const contentRef =
|
|
1246
|
-
const [mounted, setMounted] =
|
|
1247
|
-
const [visible, setVisible] =
|
|
1248
|
-
|
|
1278
|
+
const triggerRef = import_react6.default.useRef(null);
|
|
1279
|
+
const contentRef = import_react6.default.useRef(null);
|
|
1280
|
+
const [mounted, setMounted] = import_react6.default.useState(false);
|
|
1281
|
+
const [visible, setVisible] = import_react6.default.useState(false);
|
|
1282
|
+
import_react6.default.useEffect(() => {
|
|
1249
1283
|
if (disabled && isOpen) setOpen(false);
|
|
1250
1284
|
}, [disabled, isOpen]);
|
|
1251
|
-
|
|
1285
|
+
import_react6.default.useEffect(() => {
|
|
1252
1286
|
if (isOpen) {
|
|
1253
1287
|
setMounted(true);
|
|
1254
1288
|
const t2 = setTimeout(() => setVisible(true), 1);
|
|
@@ -1258,22 +1292,42 @@ var SelectRoot = (props) => {
|
|
|
1258
1292
|
const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS);
|
|
1259
1293
|
return () => clearTimeout(t);
|
|
1260
1294
|
}, [isOpen]);
|
|
1261
|
-
const
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1295
|
+
const [query, setQuery] = import_react6.default.useState("");
|
|
1296
|
+
const textOf = (node) => {
|
|
1297
|
+
if (node === null || node === void 0 || typeof node === "boolean") return "";
|
|
1298
|
+
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
1299
|
+
if (Array.isArray(node)) return node.map(textOf).join("");
|
|
1300
|
+
if (import_react6.default.isValidElement(node)) return textOf(node.props.children);
|
|
1301
|
+
return "";
|
|
1302
|
+
};
|
|
1303
|
+
const visibleItems = import_react6.default.useMemo(() => {
|
|
1304
|
+
if (!searchable || !query.trim()) return itemChildren;
|
|
1305
|
+
const q = query.trim().toLowerCase();
|
|
1306
|
+
return itemChildren.filter((c) => textOf(c.props.children).toLowerCase().includes(q));
|
|
1307
|
+
}, [searchable, query, itemChildren]);
|
|
1308
|
+
const open = import_react6.default.useCallback(() => setOpen(true), []);
|
|
1309
|
+
const close = import_react6.default.useCallback(() => {
|
|
1310
|
+
setOpen(false);
|
|
1311
|
+
setQuery("");
|
|
1312
|
+
}, []);
|
|
1313
|
+
const toggle = import_react6.default.useCallback(() => setOpen((prev) => !prev), []);
|
|
1264
1314
|
useClickOutside_default([contentRef, triggerRef], close, isOpen);
|
|
1315
|
+
useEscapeKey(isOpen, () => {
|
|
1316
|
+
close();
|
|
1317
|
+
triggerRef.current?.focus({ preventScroll: true });
|
|
1318
|
+
});
|
|
1265
1319
|
const position = useAutoPosition_default(triggerRef, contentRef, mounted);
|
|
1266
|
-
const setSelected =
|
|
1267
|
-
(
|
|
1320
|
+
const setSelected = import_react6.default.useCallback(
|
|
1321
|
+
(label2, itemValue) => {
|
|
1268
1322
|
if (!isControlled) {
|
|
1269
|
-
setUncontrolledLabel(
|
|
1323
|
+
setUncontrolledLabel(label2);
|
|
1270
1324
|
setUncontrolledValue(itemValue);
|
|
1271
1325
|
}
|
|
1272
|
-
onChange?.(itemValue,
|
|
1326
|
+
onChange?.(itemValue, label2);
|
|
1273
1327
|
},
|
|
1274
1328
|
[isControlled, onChange]
|
|
1275
1329
|
);
|
|
1276
|
-
const ctxValue =
|
|
1330
|
+
const ctxValue = import_react6.default.useMemo(
|
|
1277
1331
|
() => ({
|
|
1278
1332
|
isOpen,
|
|
1279
1333
|
mounted,
|
|
@@ -1309,6 +1363,7 @@ var SelectRoot = (props) => {
|
|
|
1309
1363
|
/* @__PURE__ */ (0, import_jsx_runtime297.jsxs)(
|
|
1310
1364
|
"div",
|
|
1311
1365
|
{
|
|
1366
|
+
id: fieldId,
|
|
1312
1367
|
ref: triggerRef,
|
|
1313
1368
|
className: clsx_default(
|
|
1314
1369
|
"select-trigger",
|
|
@@ -1351,20 +1406,33 @@ var SelectRoot = (props) => {
|
|
|
1351
1406
|
]
|
|
1352
1407
|
}
|
|
1353
1408
|
),
|
|
1354
|
-
mounted && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime297.
|
|
1409
|
+
mounted && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(Portal_default, { children: /* @__PURE__ */ (0, import_jsx_runtime297.jsxs)(
|
|
1355
1410
|
"div",
|
|
1356
1411
|
{
|
|
1357
1412
|
className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass),
|
|
1358
1413
|
ref: contentRef,
|
|
1359
1414
|
style: { ...position.position, width: triggerRef.current?.offsetWidth },
|
|
1360
1415
|
role: "listbox",
|
|
1361
|
-
children:
|
|
1416
|
+
children: [
|
|
1417
|
+
searchable && /* @__PURE__ */ (0, import_jsx_runtime297.jsx)("div", { className: "select-search", children: /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(
|
|
1418
|
+
"input",
|
|
1419
|
+
{
|
|
1420
|
+
type: "text",
|
|
1421
|
+
value: query,
|
|
1422
|
+
placeholder: searchPlaceholder,
|
|
1423
|
+
onChange: (e) => setQuery(e.target.value),
|
|
1424
|
+
autoFocus: true,
|
|
1425
|
+
"aria-label": searchPlaceholder
|
|
1426
|
+
}
|
|
1427
|
+
) }),
|
|
1428
|
+
/* @__PURE__ */ (0, import_jsx_runtime297.jsx)(context_default.Provider, { value: ctxValue, children: visibleItems.length > 0 ? visibleItems : /* @__PURE__ */ (0, import_jsx_runtime297.jsx)("div", { className: "select-empty", children: "\uACB0\uACFC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4" }) })
|
|
1429
|
+
]
|
|
1362
1430
|
}
|
|
1363
1431
|
) })
|
|
1364
1432
|
]
|
|
1365
1433
|
}
|
|
1366
1434
|
) });
|
|
1367
|
-
return /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(Field, { validations, children: control });
|
|
1435
|
+
return /* @__PURE__ */ (0, import_jsx_runtime297.jsx)(Field, { validations, label, required, htmlFor: fieldId, children: control });
|
|
1368
1436
|
};
|
|
1369
1437
|
SelectRoot.displayName = "Select";
|
|
1370
1438
|
var Select = Object.assign(SelectRoot, {
|
|
@@ -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;
|
|
@@ -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
|
|
2
|
+
import React6 from "react";
|
|
3
3
|
|
|
4
4
|
// src/tokens/hooks/Portal.tsx
|
|
5
5
|
import ReactDOM from "react-dom";
|
|
@@ -96,6 +96,30 @@ var useClickOutside = (refs, handler, enabled = true) => {
|
|
|
96
96
|
};
|
|
97
97
|
var useClickOutside_default = useClickOutside;
|
|
98
98
|
|
|
99
|
+
// src/tokens/hooks/useOverlayKeyboard.ts
|
|
100
|
+
import React3 from "react";
|
|
101
|
+
var FOCUSABLE = [
|
|
102
|
+
"a[href]",
|
|
103
|
+
"button:not([disabled])",
|
|
104
|
+
"input:not([disabled]):not([type='hidden'])",
|
|
105
|
+
"select:not([disabled])",
|
|
106
|
+
"textarea:not([disabled])",
|
|
107
|
+
"[tabindex]:not([tabindex='-1'])"
|
|
108
|
+
].join(",");
|
|
109
|
+
var useEscapeKey = (enabled, onEscape) => {
|
|
110
|
+
React3.useEffect(() => {
|
|
111
|
+
if (!enabled) return;
|
|
112
|
+
const onKey = (e) => {
|
|
113
|
+
if (e.key !== "Escape") return;
|
|
114
|
+
if (e.defaultPrevented) return;
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
onEscape();
|
|
117
|
+
};
|
|
118
|
+
document.addEventListener("keydown", onKey);
|
|
119
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
120
|
+
}, [enabled, onEscape]);
|
|
121
|
+
};
|
|
122
|
+
|
|
99
123
|
// src/tokens/svg/action/CopyIcon.tsx
|
|
100
124
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
101
125
|
|
|
@@ -1126,27 +1150,32 @@ var FieldMessage = (props) => {
|
|
|
1126
1150
|
};
|
|
1127
1151
|
FieldMessage.displayName = "FieldMessage";
|
|
1128
1152
|
var Field = (props) => {
|
|
1129
|
-
const { validations, children } = props;
|
|
1130
|
-
|
|
1153
|
+
const { label, required = false, htmlFor, validations, children } = props;
|
|
1154
|
+
const hasMessages = !!validations && validations.length > 0;
|
|
1155
|
+
if (!label && !hasMessages) return /* @__PURE__ */ jsx295(Fragment, { children });
|
|
1131
1156
|
return /* @__PURE__ */ jsxs189("div", { className: "lib-xplat-field", children: [
|
|
1157
|
+
label && /* @__PURE__ */ jsxs189("label", { className: "lib-xplat-field-label", htmlFor, children: [
|
|
1158
|
+
label,
|
|
1159
|
+
required && /* @__PURE__ */ jsx295("span", { className: "required", "aria-hidden": "true", children: "*" })
|
|
1160
|
+
] }),
|
|
1132
1161
|
children,
|
|
1133
|
-
/* @__PURE__ */ jsx295(FieldMessage, { validations })
|
|
1162
|
+
hasMessages && /* @__PURE__ */ jsx295(FieldMessage, { validations })
|
|
1134
1163
|
] });
|
|
1135
1164
|
};
|
|
1136
1165
|
Field.displayName = "Field";
|
|
1137
1166
|
var hasError = (validations) => !!validations?.some((v) => v.status === "error");
|
|
1138
1167
|
|
|
1139
1168
|
// src/components/Select/context.ts
|
|
1140
|
-
import
|
|
1141
|
-
var SelectContext =
|
|
1169
|
+
import React4 from "react";
|
|
1170
|
+
var SelectContext = React4.createContext(null);
|
|
1142
1171
|
var context_default = SelectContext;
|
|
1143
1172
|
|
|
1144
1173
|
// src/components/Select/SelectItem.tsx
|
|
1145
|
-
import
|
|
1174
|
+
import React5 from "react";
|
|
1146
1175
|
import { jsx as jsx296 } from "react/jsx-runtime";
|
|
1147
1176
|
var SelectItem = (props) => {
|
|
1148
1177
|
const { children, value, onClick, disabled = false } = props;
|
|
1149
|
-
const ctx =
|
|
1178
|
+
const ctx = React5.useContext(context_default);
|
|
1150
1179
|
const handleClick = (e) => {
|
|
1151
1180
|
e.preventDefault();
|
|
1152
1181
|
e.stopPropagation();
|
|
@@ -1186,32 +1215,37 @@ var SelectRoot = (props) => {
|
|
|
1186
1215
|
onChange,
|
|
1187
1216
|
children,
|
|
1188
1217
|
disabled = false,
|
|
1218
|
+
searchable = false,
|
|
1219
|
+
searchPlaceholder = "\uAC80\uC0C9",
|
|
1189
1220
|
error = false,
|
|
1190
1221
|
validations,
|
|
1222
|
+
label,
|
|
1223
|
+
required,
|
|
1191
1224
|
size = "md"
|
|
1192
1225
|
} = props;
|
|
1193
|
-
const
|
|
1194
|
-
|
|
1226
|
+
const fieldId = React6.useId();
|
|
1227
|
+
const itemChildren = React6.Children.toArray(children).filter(
|
|
1228
|
+
(child) => React6.isValidElement(child) && child.type === SelectItem_default
|
|
1195
1229
|
);
|
|
1196
1230
|
const isControlled = valueProp !== void 0;
|
|
1197
|
-
const [isOpen, setOpen] =
|
|
1198
|
-
const [uncontrolledLabel, setUncontrolledLabel] =
|
|
1199
|
-
const [uncontrolledValue, setUncontrolledValue] =
|
|
1200
|
-
const controlledLabel =
|
|
1231
|
+
const [isOpen, setOpen] = React6.useState(false);
|
|
1232
|
+
const [uncontrolledLabel, setUncontrolledLabel] = React6.useState(null);
|
|
1233
|
+
const [uncontrolledValue, setUncontrolledValue] = React6.useState(void 0);
|
|
1234
|
+
const controlledLabel = React6.useMemo(() => {
|
|
1201
1235
|
if (!isControlled) return null;
|
|
1202
1236
|
const match = itemChildren.find((child) => child.props.value === valueProp);
|
|
1203
1237
|
return match ? match.props.children : null;
|
|
1204
1238
|
}, [isControlled, valueProp, itemChildren]);
|
|
1205
1239
|
const selectedLabel = isControlled ? controlledLabel : uncontrolledLabel;
|
|
1206
1240
|
const selectedValue = isControlled ? valueProp : uncontrolledValue;
|
|
1207
|
-
const triggerRef =
|
|
1208
|
-
const contentRef =
|
|
1209
|
-
const [mounted, setMounted] =
|
|
1210
|
-
const [visible, setVisible] =
|
|
1211
|
-
|
|
1241
|
+
const triggerRef = React6.useRef(null);
|
|
1242
|
+
const contentRef = React6.useRef(null);
|
|
1243
|
+
const [mounted, setMounted] = React6.useState(false);
|
|
1244
|
+
const [visible, setVisible] = React6.useState(false);
|
|
1245
|
+
React6.useEffect(() => {
|
|
1212
1246
|
if (disabled && isOpen) setOpen(false);
|
|
1213
1247
|
}, [disabled, isOpen]);
|
|
1214
|
-
|
|
1248
|
+
React6.useEffect(() => {
|
|
1215
1249
|
if (isOpen) {
|
|
1216
1250
|
setMounted(true);
|
|
1217
1251
|
const t2 = setTimeout(() => setVisible(true), 1);
|
|
@@ -1221,22 +1255,42 @@ var SelectRoot = (props) => {
|
|
|
1221
1255
|
const t = setTimeout(() => setMounted(false), ANIMATION_DURATION_MS);
|
|
1222
1256
|
return () => clearTimeout(t);
|
|
1223
1257
|
}, [isOpen]);
|
|
1224
|
-
const
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1258
|
+
const [query, setQuery] = React6.useState("");
|
|
1259
|
+
const textOf = (node) => {
|
|
1260
|
+
if (node === null || node === void 0 || typeof node === "boolean") return "";
|
|
1261
|
+
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
1262
|
+
if (Array.isArray(node)) return node.map(textOf).join("");
|
|
1263
|
+
if (React6.isValidElement(node)) return textOf(node.props.children);
|
|
1264
|
+
return "";
|
|
1265
|
+
};
|
|
1266
|
+
const visibleItems = React6.useMemo(() => {
|
|
1267
|
+
if (!searchable || !query.trim()) return itemChildren;
|
|
1268
|
+
const q = query.trim().toLowerCase();
|
|
1269
|
+
return itemChildren.filter((c) => textOf(c.props.children).toLowerCase().includes(q));
|
|
1270
|
+
}, [searchable, query, itemChildren]);
|
|
1271
|
+
const open = React6.useCallback(() => setOpen(true), []);
|
|
1272
|
+
const close = React6.useCallback(() => {
|
|
1273
|
+
setOpen(false);
|
|
1274
|
+
setQuery("");
|
|
1275
|
+
}, []);
|
|
1276
|
+
const toggle = React6.useCallback(() => setOpen((prev) => !prev), []);
|
|
1227
1277
|
useClickOutside_default([contentRef, triggerRef], close, isOpen);
|
|
1278
|
+
useEscapeKey(isOpen, () => {
|
|
1279
|
+
close();
|
|
1280
|
+
triggerRef.current?.focus({ preventScroll: true });
|
|
1281
|
+
});
|
|
1228
1282
|
const position = useAutoPosition_default(triggerRef, contentRef, mounted);
|
|
1229
|
-
const setSelected =
|
|
1230
|
-
(
|
|
1283
|
+
const setSelected = React6.useCallback(
|
|
1284
|
+
(label2, itemValue) => {
|
|
1231
1285
|
if (!isControlled) {
|
|
1232
|
-
setUncontrolledLabel(
|
|
1286
|
+
setUncontrolledLabel(label2);
|
|
1233
1287
|
setUncontrolledValue(itemValue);
|
|
1234
1288
|
}
|
|
1235
|
-
onChange?.(itemValue,
|
|
1289
|
+
onChange?.(itemValue, label2);
|
|
1236
1290
|
},
|
|
1237
1291
|
[isControlled, onChange]
|
|
1238
1292
|
);
|
|
1239
|
-
const ctxValue =
|
|
1293
|
+
const ctxValue = React6.useMemo(
|
|
1240
1294
|
() => ({
|
|
1241
1295
|
isOpen,
|
|
1242
1296
|
mounted,
|
|
@@ -1272,6 +1326,7 @@ var SelectRoot = (props) => {
|
|
|
1272
1326
|
/* @__PURE__ */ jsxs190(
|
|
1273
1327
|
"div",
|
|
1274
1328
|
{
|
|
1329
|
+
id: fieldId,
|
|
1275
1330
|
ref: triggerRef,
|
|
1276
1331
|
className: clsx_default(
|
|
1277
1332
|
"select-trigger",
|
|
@@ -1314,20 +1369,33 @@ var SelectRoot = (props) => {
|
|
|
1314
1369
|
]
|
|
1315
1370
|
}
|
|
1316
1371
|
),
|
|
1317
|
-
mounted && /* @__PURE__ */ jsx297(Portal_default, { children: /* @__PURE__ */
|
|
1372
|
+
mounted && /* @__PURE__ */ jsx297(Portal_default, { children: /* @__PURE__ */ jsxs190(
|
|
1318
1373
|
"div",
|
|
1319
1374
|
{
|
|
1320
1375
|
className: clsx_default("lib-xplat-select-content", size, position.direction, stateClass),
|
|
1321
1376
|
ref: contentRef,
|
|
1322
1377
|
style: { ...position.position, width: triggerRef.current?.offsetWidth },
|
|
1323
1378
|
role: "listbox",
|
|
1324
|
-
children:
|
|
1379
|
+
children: [
|
|
1380
|
+
searchable && /* @__PURE__ */ jsx297("div", { className: "select-search", children: /* @__PURE__ */ jsx297(
|
|
1381
|
+
"input",
|
|
1382
|
+
{
|
|
1383
|
+
type: "text",
|
|
1384
|
+
value: query,
|
|
1385
|
+
placeholder: searchPlaceholder,
|
|
1386
|
+
onChange: (e) => setQuery(e.target.value),
|
|
1387
|
+
autoFocus: true,
|
|
1388
|
+
"aria-label": searchPlaceholder
|
|
1389
|
+
}
|
|
1390
|
+
) }),
|
|
1391
|
+
/* @__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" }) })
|
|
1392
|
+
]
|
|
1325
1393
|
}
|
|
1326
1394
|
) })
|
|
1327
1395
|
]
|
|
1328
1396
|
}
|
|
1329
1397
|
) });
|
|
1330
|
-
return /* @__PURE__ */ jsx297(Field, { validations, children: control });
|
|
1398
|
+
return /* @__PURE__ */ jsx297(Field, { validations, label, required, htmlFor: fieldId, children: control });
|
|
1331
1399
|
};
|
|
1332
1400
|
SelectRoot.displayName = "Select";
|
|
1333
1401
|
var Select = Object.assign(SelectRoot, {
|
|
@@ -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
|
+
});
|