@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.
- 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 +57 -16
- package/dist/components/Dropdown/index.css +3 -0
- package/dist/components/Dropdown/index.js +57 -16
- package/dist/components/Editor/index.cjs +779 -50
- package/dist/components/Editor/index.css +100 -0
- package/dist/components/Editor/index.d.cts +1 -1
- package/dist/components/Editor/index.d.ts +1 -1
- package/dist/components/Editor/index.js +779 -50
- 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 +55 -13
- package/dist/components/PopOver/index.css +3 -0
- package/dist/components/PopOver/index.js +55 -13
- 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 +120 -39
- package/dist/components/Select/index.css +57 -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 +120 -39
- 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 +461 -0
- package/dist/components/TimePicker/index.css +175 -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 +424 -0
- package/dist/components/index.cjs +1500 -398
- package/dist/components/index.css +385 -0
- package/dist/components/index.d.cts +3 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.js +1498 -398
- package/dist/index.cjs +1524 -422
- package/dist/index.css +385 -0
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1522 -422
- 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 +141 -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
|
@@ -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
|
|
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)
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
window.removeEventListener("
|
|
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
|
-
|
|
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
|
|
1141
|
-
var SelectContext =
|
|
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
|
|
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 =
|
|
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
|
|
1194
|
-
|
|
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] =
|
|
1198
|
-
const [uncontrolledLabel, setUncontrolledLabel] =
|
|
1199
|
-
const [uncontrolledValue, setUncontrolledValue] =
|
|
1200
|
-
const controlledLabel =
|
|
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 =
|
|
1208
|
-
const contentRef =
|
|
1209
|
-
const [mounted, setMounted] =
|
|
1210
|
-
const [visible, setVisible] =
|
|
1211
|
-
|
|
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
|
-
|
|
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
|
|
1225
|
-
const
|
|
1226
|
-
|
|
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 =
|
|
1230
|
-
(
|
|
1296
|
+
const setSelected = React6.useCallback(
|
|
1297
|
+
(label2, itemValue) => {
|
|
1231
1298
|
if (!isControlled) {
|
|
1232
|
-
setUncontrolledLabel(
|
|
1299
|
+
setUncontrolledLabel(label2);
|
|
1233
1300
|
setUncontrolledValue(itemValue);
|
|
1234
1301
|
}
|
|
1235
|
-
onChange?.(itemValue,
|
|
1302
|
+
onChange?.(itemValue, label2);
|
|
1236
1303
|
},
|
|
1237
1304
|
[isControlled, onChange]
|
|
1238
1305
|
);
|
|
1239
|
-
const ctxValue =
|
|
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__ */
|
|
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:
|
|
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, {
|
|
@@ -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 };
|