sellmate-design-system-react 0.1.10 → 0.2.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/SActionModal/SActionModal.cjs +10 -4
- package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
- package/dist/components/SActionModal/SActionModal.js +10 -4
- package/dist/components/SActionModal/SActionModal.js.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs +22 -5
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +3 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.js +22 -5
- package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
- package/dist/components/SCalendar/SCalendar.cjs +9 -3
- package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
- package/dist/components/SCalendar/SCalendar.js +9 -3
- package/dist/components/SCalendar/SCalendar.js.map +1 -1
- package/dist/components/SChip/SChip.cjs +9 -3
- package/dist/components/SChip/SChip.cjs.map +1 -1
- package/dist/components/SChip/SChip.js +9 -3
- package/dist/components/SChip/SChip.js.map +1 -1
- package/dist/components/SChipInput/SChipInput.cjs +44 -13
- package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
- package/dist/components/SChipInput/SChipInput.d.ts +5 -0
- package/dist/components/SChipInput/SChipInput.js +44 -13
- package/dist/components/SChipInput/SChipInput.js.map +1 -1
- package/dist/components/SChipInput/index.d.ts +1 -1
- package/dist/components/SCircleProgress/SCircleProgress.cjs +6 -2
- package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
- package/dist/components/SCircleProgress/SCircleProgress.js +6 -2
- package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.cjs +20 -13
- package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.d.ts +7 -4
- package/dist/components/SConfirmModal/SConfirmModal.js +20 -13
- package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.cjs +22 -5
- package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.d.ts +3 -1
- package/dist/components/SDatePicker/SDatePicker.js +22 -5
- package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs +22 -5
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +3 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.js +22 -5
- package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
- package/dist/components/SDropdownButton/SDropdownButton.cjs +3 -1
- package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
- package/dist/components/SDropdownButton/SDropdownButton.js +3 -1
- package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
- package/dist/components/SField/SField.cjs +20 -5
- package/dist/components/SField/SField.cjs.map +1 -1
- package/dist/components/SField/SField.d.ts +4 -1
- package/dist/components/SField/SField.js +20 -5
- package/dist/components/SField/SField.js.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.cjs +23 -6
- package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.d.ts +3 -1
- package/dist/components/SFilePicker/SFilePicker.js +23 -6
- package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
- package/dist/components/SGhostButton/SGhostButton.cjs +9 -3
- package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
- package/dist/components/SGhostButton/SGhostButton.d.ts +5 -1
- package/dist/components/SGhostButton/SGhostButton.js +9 -3
- package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
- package/dist/components/SGnb/SGnb.cjs +3297 -0
- package/dist/components/SGnb/SGnb.cjs.map +1 -0
- package/dist/components/SGnb/SGnb.d.ts +35 -0
- package/dist/components/SGnb/SGnb.js +3295 -0
- package/dist/components/SGnb/SGnb.js.map +1 -0
- package/dist/components/SGnb/gnb.config.d.ts +63 -0
- package/dist/components/SGnb/index.d.ts +2 -0
- package/dist/components/SGuide/SGuide.cjs +9 -3
- package/dist/components/SGuide/SGuide.cjs.map +1 -1
- package/dist/components/SGuide/SGuide.js +9 -3
- package/dist/components/SGuide/SGuide.js.map +1 -1
- package/dist/components/SInput/SInput.cjs +22 -5
- package/dist/components/SInput/SInput.cjs.map +1 -1
- package/dist/components/SInput/SInput.d.ts +4 -1
- package/dist/components/SInput/SInput.js +22 -5
- package/dist/components/SInput/SInput.js.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs +511 -209
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +27 -6
- package/dist/components/SKeyValueTable/SKeyValueTable.js +510 -208
- package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
- package/dist/components/SLayout/SLayout.cjs +79 -0
- package/dist/components/SLayout/SLayout.cjs.map +1 -0
- package/dist/components/SLayout/SLayout.d.ts +41 -0
- package/dist/components/SLayout/SLayout.js +75 -0
- package/dist/components/SLayout/SLayout.js.map +1 -0
- package/dist/components/SLayout/index.d.ts +1 -0
- package/dist/components/SLoadingContainer/SLoadingContainer.cjs +6 -2
- package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
- package/dist/components/SLoadingContainer/SLoadingContainer.js +6 -2
- package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
- package/dist/components/SLoadingContainer/loading.cjs +6 -2
- package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
- package/dist/components/SLoadingContainer/loading.js +6 -2
- package/dist/components/SLoadingContainer/loading.js.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.cjs +16 -6
- package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.js +16 -6
- package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
- package/dist/components/SModalContainer/SModalContainer.cjs +10 -4
- package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
- package/dist/components/SModalContainer/SModalContainer.d.ts +10 -0
- package/dist/components/SModalContainer/SModalContainer.js +10 -4
- package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.cjs +22 -5
- package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
- package/dist/components/SNumberInput/SNumberInput.js +22 -5
- package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
- package/dist/components/SPage/SPage.cjs +86 -0
- package/dist/components/SPage/SPage.cjs.map +1 -0
- package/dist/components/SPage/SPage.d.ts +13 -0
- package/dist/components/SPage/SPage.js +84 -0
- package/dist/components/SPage/SPage.js.map +1 -0
- package/dist/components/SPage/index.d.ts +1 -0
- package/dist/components/SPopover/SPopover.cjs +9 -3
- package/dist/components/SPopover/SPopover.cjs.map +1 -1
- package/dist/components/SPopover/SPopover.js +9 -3
- package/dist/components/SPopover/SPopover.js.map +1 -1
- package/dist/components/SPortal/SPortal.cjs +3 -1
- package/dist/components/SPortal/SPortal.cjs.map +1 -1
- package/dist/components/SPortal/SPortal.js +3 -1
- package/dist/components/SPortal/SPortal.js.map +1 -1
- package/dist/components/SSelect/SSelect.cjs +545 -265
- package/dist/components/SSelect/SSelect.cjs.map +1 -1
- package/dist/components/SSelect/SSelect.d.ts +4 -2
- package/dist/components/SSelect/SSelect.js +546 -265
- package/dist/components/SSelect/SSelect.js.map +1 -1
- package/dist/components/STable/STable.cjs +552 -268
- package/dist/components/STable/STable.cjs.map +1 -1
- package/dist/components/STable/STable.js +553 -268
- package/dist/components/STable/STable.js.map +1 -1
- package/dist/components/STextLink/STextLink.cjs +2 -0
- package/dist/components/STextLink/STextLink.cjs.map +1 -1
- package/dist/components/STextLink/STextLink.js +2 -0
- package/dist/components/STextLink/STextLink.js.map +1 -1
- package/dist/components/STextarea/STextarea.cjs +22 -5
- package/dist/components/STextarea/STextarea.cjs.map +1 -1
- package/dist/components/STextarea/STextarea.d.ts +2 -0
- package/dist/components/STextarea/STextarea.js +22 -5
- package/dist/components/STextarea/STextarea.js.map +1 -1
- package/dist/components/SToast/SToast.cjs +9 -3
- package/dist/components/SToast/SToast.cjs.map +1 -1
- package/dist/components/SToast/SToast.js +9 -3
- package/dist/components/SToast/SToast.js.map +1 -1
- package/dist/components/SToast/SToastContainer.cjs +9 -3
- package/dist/components/SToast/SToastContainer.cjs.map +1 -1
- package/dist/components/SToast/SToastContainer.js +9 -3
- package/dist/components/SToast/SToastContainer.js.map +1 -1
- package/dist/components/STooltip/STooltip.cjs +9 -3
- package/dist/components/STooltip/STooltip.cjs.map +1 -1
- package/dist/components/STooltip/STooltip.js +9 -3
- package/dist/components/STooltip/STooltip.js.map +1 -1
- package/dist/index.cjs +840 -199
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +832 -200
- package/dist/index.js.map +1 -1
- package/dist/lib/portal-container.d.ts +7 -0
- package/dist/styles.css +253 -63
- package/dist/theme.css +66 -13
- package/package.json +1 -1
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var react = require('react');
|
|
5
|
-
var Ariakit = require('@ariakit/react');
|
|
6
5
|
var clsx = require('clsx');
|
|
7
6
|
var tailwindMerge = require('tailwind-merge');
|
|
8
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -26,7 +25,6 @@ function _interopNamespace(e) {
|
|
|
26
25
|
return Object.freeze(n);
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
var Ariakit__namespace = /*#__PURE__*/_interopNamespace(Ariakit);
|
|
30
28
|
var RPopover2__namespace = /*#__PURE__*/_interopNamespace(RPopover2);
|
|
31
29
|
|
|
32
30
|
var twMerge = tailwindMerge.extendTailwindMerge({
|
|
@@ -40,10 +38,110 @@ function cn(...inputs) {
|
|
|
40
38
|
return twMerge(clsx.clsx(inputs));
|
|
41
39
|
}
|
|
42
40
|
var PortalContainerContext = react.createContext(null);
|
|
41
|
+
var InsideModalContext = react.createContext(false);
|
|
43
42
|
PortalContainerContext.Provider;
|
|
43
|
+
InsideModalContext.Provider;
|
|
44
44
|
function usePortalContainer() {
|
|
45
45
|
return react.useContext(PortalContainerContext) ?? void 0;
|
|
46
46
|
}
|
|
47
|
+
function useInsideModal() {
|
|
48
|
+
return react.useContext(InsideModalContext);
|
|
49
|
+
}
|
|
50
|
+
function runRules(value, rules) {
|
|
51
|
+
if (!rules || rules.length === 0) return "";
|
|
52
|
+
for (const rule of rules) {
|
|
53
|
+
const result = rule(value ?? "");
|
|
54
|
+
if (result !== true) return typeof result === "string" ? result : "\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC12\uC785\uB2C8\uB2E4.";
|
|
55
|
+
}
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
var FormContext = react.createContext(null);
|
|
59
|
+
function useFormContext() {
|
|
60
|
+
return react.useContext(FormContext);
|
|
61
|
+
}
|
|
62
|
+
function useFormField(field) {
|
|
63
|
+
const ctx = useFormContext();
|
|
64
|
+
const cbRef = react.useRef(field);
|
|
65
|
+
cbRef.current = field;
|
|
66
|
+
const name = field.name ?? "";
|
|
67
|
+
react.useEffect(() => {
|
|
68
|
+
if (!ctx) return;
|
|
69
|
+
return ctx.register({
|
|
70
|
+
name,
|
|
71
|
+
validate: () => cbRef.current.validate(),
|
|
72
|
+
resetValidation: () => cbRef.current.resetValidation()
|
|
73
|
+
});
|
|
74
|
+
}, [ctx, name]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/lib/sanitize-inline-html.ts
|
|
78
|
+
var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
|
|
79
|
+
var ALLOWED_ATTRS_BY_TAG = {
|
|
80
|
+
A: /* @__PURE__ */ new Set(["href", "target", "rel", "class"])
|
|
81
|
+
};
|
|
82
|
+
var COMMON_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class"]);
|
|
83
|
+
var SAFE_HREF_RE = /^(https?:|mailto:|tel:|\/|#|\.)/i;
|
|
84
|
+
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
85
|
+
"SCRIPT",
|
|
86
|
+
"STYLE",
|
|
87
|
+
"IFRAME",
|
|
88
|
+
"OBJECT",
|
|
89
|
+
"EMBED",
|
|
90
|
+
"META",
|
|
91
|
+
"LINK",
|
|
92
|
+
"BASE",
|
|
93
|
+
"NOSCRIPT",
|
|
94
|
+
// SVG/MATH 는 foreign-content 파싱 모드로 전환되어 재직렬화 시 다른 트리로
|
|
95
|
+
// 재파싱될 수 있다(mXSS 벡터). 인라인 라벨에 필요 없으므로 통째로 제거한다.
|
|
96
|
+
"SVG",
|
|
97
|
+
"MATH",
|
|
98
|
+
"TEMPLATE"
|
|
99
|
+
]);
|
|
100
|
+
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
101
|
+
var sanitizeNode = (node) => {
|
|
102
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
103
|
+
node.remove();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
107
|
+
const element = node;
|
|
108
|
+
const tagName = element.tagName;
|
|
109
|
+
if (DROP_CONTENT_TAGS.has(tagName)) {
|
|
110
|
+
element.remove();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
|
|
114
|
+
if (ALLOWED_INLINE_TAGS.has(tagName)) {
|
|
115
|
+
if (element.hasAttribute("className")) {
|
|
116
|
+
const val = element.getAttribute("className");
|
|
117
|
+
element.removeAttribute("className");
|
|
118
|
+
if (!element.hasAttribute("class")) element.setAttribute("class", val);
|
|
119
|
+
}
|
|
120
|
+
const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tagName] ?? COMMON_ALLOWED_ATTRS;
|
|
121
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
122
|
+
if (!allowedAttrs.has(attr.name)) element.removeAttribute(attr.name);
|
|
123
|
+
});
|
|
124
|
+
if (tagName === "A") {
|
|
125
|
+
const href = element.getAttribute("href");
|
|
126
|
+
if (href !== null && href !== "" && !SAFE_HREF_RE.test(href)) element.removeAttribute("href");
|
|
127
|
+
if (element.hasAttribute("target")) {
|
|
128
|
+
element.setAttribute("rel", "noopener noreferrer");
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const parent = element.parentNode;
|
|
134
|
+
if (parent == null) return;
|
|
135
|
+
while (element.firstChild) parent.insertBefore(element.firstChild, element);
|
|
136
|
+
parent.removeChild(element);
|
|
137
|
+
};
|
|
138
|
+
var sanitizeInlineHtml = (value) => {
|
|
139
|
+
if (typeof document === "undefined") return escapeHtml(value);
|
|
140
|
+
const template = document.createElement("template");
|
|
141
|
+
template.innerHTML = value;
|
|
142
|
+
Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
|
|
143
|
+
return template.innerHTML;
|
|
144
|
+
};
|
|
47
145
|
|
|
48
146
|
// src/lib/color.ts
|
|
49
147
|
var COLOR_KEYS = [
|
|
@@ -2996,6 +3094,8 @@ function SGhostButton({
|
|
|
2996
3094
|
tooltipText,
|
|
2997
3095
|
disabled = false,
|
|
2998
3096
|
onClick,
|
|
3097
|
+
anchorClassName,
|
|
3098
|
+
anchorStyle,
|
|
2999
3099
|
className,
|
|
3000
3100
|
style
|
|
3001
3101
|
}) {
|
|
@@ -3018,6 +3118,8 @@ function SGhostButton({
|
|
|
3018
3118
|
};
|
|
3019
3119
|
const s = SIZE[size];
|
|
3020
3120
|
const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
|
|
3121
|
+
const text = (tooltipText ?? "").trim();
|
|
3122
|
+
const hasTooltip = text !== "";
|
|
3021
3123
|
const btn = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3022
3124
|
"button",
|
|
3023
3125
|
{
|
|
@@ -3058,13 +3160,13 @@ function SGhostButton({
|
|
|
3058
3160
|
]
|
|
3059
3161
|
}
|
|
3060
3162
|
);
|
|
3061
|
-
|
|
3062
|
-
if (text === "") return btn;
|
|
3163
|
+
if (!hasTooltip) return btn;
|
|
3063
3164
|
return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
|
|
3064
3165
|
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3065
3166
|
"span",
|
|
3066
3167
|
{
|
|
3067
|
-
className: "inline-flex",
|
|
3168
|
+
className: cn("inline-flex", anchorClassName),
|
|
3169
|
+
style: anchorStyle,
|
|
3068
3170
|
onMouseEnter: openNow,
|
|
3069
3171
|
onMouseLeave: startHide,
|
|
3070
3172
|
onFocus: openNow,
|
|
@@ -3216,32 +3318,6 @@ var STooltip = react.forwardRef(function STooltip2({
|
|
|
3216
3318
|
] });
|
|
3217
3319
|
});
|
|
3218
3320
|
STooltip.displayName = "STooltip";
|
|
3219
|
-
function runRules(value, rules) {
|
|
3220
|
-
if (!rules || rules.length === 0) return "";
|
|
3221
|
-
for (const rule of rules) {
|
|
3222
|
-
const result = rule(value ?? "");
|
|
3223
|
-
if (result !== true) return typeof result === "string" ? result : "\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC12\uC785\uB2C8\uB2E4.";
|
|
3224
|
-
}
|
|
3225
|
-
return "";
|
|
3226
|
-
}
|
|
3227
|
-
var FormContext = react.createContext(null);
|
|
3228
|
-
function useFormContext() {
|
|
3229
|
-
return react.useContext(FormContext);
|
|
3230
|
-
}
|
|
3231
|
-
function useFormField(field) {
|
|
3232
|
-
const ctx = useFormContext();
|
|
3233
|
-
const cbRef = react.useRef(field);
|
|
3234
|
-
cbRef.current = field;
|
|
3235
|
-
const name = field.name ?? "";
|
|
3236
|
-
react.useEffect(() => {
|
|
3237
|
-
if (!ctx) return;
|
|
3238
|
-
return ctx.register({
|
|
3239
|
-
name,
|
|
3240
|
-
validate: () => cbRef.current.validate(),
|
|
3241
|
-
resetValidation: () => cbRef.current.resetValidation()
|
|
3242
|
-
});
|
|
3243
|
-
}, [ctx, name]);
|
|
3244
|
-
}
|
|
3245
3321
|
var FIELD_SIZE = {
|
|
3246
3322
|
sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
|
|
3247
3323
|
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
|
|
@@ -3256,6 +3332,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3256
3332
|
name,
|
|
3257
3333
|
label = "",
|
|
3258
3334
|
icon,
|
|
3335
|
+
iconColor,
|
|
3259
3336
|
labelTooltip = "",
|
|
3260
3337
|
labelTooltipProps,
|
|
3261
3338
|
labelWidth,
|
|
@@ -3333,7 +3410,15 @@ var SField = react.forwardRef(function SField2({
|
|
|
3333
3410
|
lineHeight: `${s.lineHeight}px`
|
|
3334
3411
|
},
|
|
3335
3412
|
children: [
|
|
3336
|
-
icon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3413
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3414
|
+
SIcon,
|
|
3415
|
+
{
|
|
3416
|
+
name: icon,
|
|
3417
|
+
size: s.icon,
|
|
3418
|
+
color: iconColor,
|
|
3419
|
+
style: { marginRight: s.labelGap }
|
|
3420
|
+
}
|
|
3421
|
+
),
|
|
3337
3422
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
|
|
3338
3423
|
(labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3339
3424
|
STooltip,
|
|
@@ -3383,7 +3468,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3383
3468
|
borderBottomLeftRadius: s.radius - 1
|
|
3384
3469
|
},
|
|
3385
3470
|
children: [
|
|
3386
|
-
icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon }),
|
|
3471
|
+
icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: iconColor }),
|
|
3387
3472
|
effectiveAddonLabel
|
|
3388
3473
|
]
|
|
3389
3474
|
}
|
|
@@ -3398,6 +3483,77 @@ var SField = react.forwardRef(function SField2({
|
|
|
3398
3483
|
}
|
|
3399
3484
|
);
|
|
3400
3485
|
});
|
|
3486
|
+
function SPortal({
|
|
3487
|
+
open,
|
|
3488
|
+
onClose,
|
|
3489
|
+
onOpenChange,
|
|
3490
|
+
anchorRef,
|
|
3491
|
+
placement = "bottom",
|
|
3492
|
+
align = "start",
|
|
3493
|
+
offset = 4,
|
|
3494
|
+
alignOffset = 0,
|
|
3495
|
+
fit = false,
|
|
3496
|
+
maxHeight,
|
|
3497
|
+
autoClose = false,
|
|
3498
|
+
persistent = false,
|
|
3499
|
+
viewportPadding = 0,
|
|
3500
|
+
onOpenAutoFocus,
|
|
3501
|
+
onCloseAutoFocus,
|
|
3502
|
+
children,
|
|
3503
|
+
className,
|
|
3504
|
+
style,
|
|
3505
|
+
ariaLabel
|
|
3506
|
+
}) {
|
|
3507
|
+
const portalContainer = usePortalContainer();
|
|
3508
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3509
|
+
RPopover2__namespace.Root,
|
|
3510
|
+
{
|
|
3511
|
+
open,
|
|
3512
|
+
onOpenChange: (next) => {
|
|
3513
|
+
onOpenChange?.(next);
|
|
3514
|
+
if (!next) onClose?.();
|
|
3515
|
+
},
|
|
3516
|
+
children: [
|
|
3517
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { virtualRef: anchorRef }),
|
|
3518
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3519
|
+
RPopover2__namespace.Content,
|
|
3520
|
+
{
|
|
3521
|
+
side: placement,
|
|
3522
|
+
align,
|
|
3523
|
+
sideOffset: offset,
|
|
3524
|
+
alignOffset,
|
|
3525
|
+
collisionPadding: viewportPadding,
|
|
3526
|
+
"aria-label": ariaLabel,
|
|
3527
|
+
onPointerDownOutside: (e) => {
|
|
3528
|
+
if (persistent || anchorRef.current?.contains(e.target)) {
|
|
3529
|
+
e.preventDefault();
|
|
3530
|
+
}
|
|
3531
|
+
},
|
|
3532
|
+
onFocusOutside: (e) => {
|
|
3533
|
+
if (persistent || anchorRef.current?.contains(e.target)) {
|
|
3534
|
+
e.preventDefault();
|
|
3535
|
+
}
|
|
3536
|
+
},
|
|
3537
|
+
onEscapeKeyDown: persistent ? (e) => e.preventDefault() : void 0,
|
|
3538
|
+
onOpenAutoFocus,
|
|
3539
|
+
onCloseAutoFocus,
|
|
3540
|
+
onClick: autoClose ? () => onClose?.() : void 0,
|
|
3541
|
+
className: cn(
|
|
3542
|
+
"z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm",
|
|
3543
|
+
maxHeight != null && "overflow-y-auto",
|
|
3544
|
+
fit && "min-w-(--radix-popover-trigger-width)",
|
|
3545
|
+
FLOATING_SLIDE_ANIM,
|
|
3546
|
+
className
|
|
3547
|
+
),
|
|
3548
|
+
style: { maxHeight, ...style },
|
|
3549
|
+
children
|
|
3550
|
+
}
|
|
3551
|
+
) })
|
|
3552
|
+
]
|
|
3553
|
+
}
|
|
3554
|
+
);
|
|
3555
|
+
}
|
|
3556
|
+
SPortal.displayName = "SPortal";
|
|
3401
3557
|
|
|
3402
3558
|
// src/components/SSelect/select.config.ts
|
|
3403
3559
|
function flattenOptions(options, depth = 1) {
|
|
@@ -3432,75 +3588,6 @@ function flattenOptions(options, depth = 1) {
|
|
|
3432
3588
|
var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
|
|
3433
3589
|
var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
|
|
3434
3590
|
var extractText = (html) => html.replace(/<[^>]*>/g, "");
|
|
3435
|
-
|
|
3436
|
-
// src/lib/sanitize-inline-html.ts
|
|
3437
|
-
var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
|
|
3438
|
-
var ALLOWED_ATTRS_BY_TAG = {
|
|
3439
|
-
A: /* @__PURE__ */ new Set(["href", "target", "rel", "class"])
|
|
3440
|
-
};
|
|
3441
|
-
var COMMON_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class"]);
|
|
3442
|
-
var SAFE_HREF_RE = /^(https?:|mailto:|tel:|\/|#|\.)/i;
|
|
3443
|
-
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
3444
|
-
"SCRIPT",
|
|
3445
|
-
"STYLE",
|
|
3446
|
-
"IFRAME",
|
|
3447
|
-
"OBJECT",
|
|
3448
|
-
"EMBED",
|
|
3449
|
-
"META",
|
|
3450
|
-
"LINK",
|
|
3451
|
-
"BASE",
|
|
3452
|
-
"NOSCRIPT",
|
|
3453
|
-
// SVG/MATH 는 foreign-content 파싱 모드로 전환되어 재직렬화 시 다른 트리로
|
|
3454
|
-
// 재파싱될 수 있다(mXSS 벡터). 인라인 라벨에 필요 없으므로 통째로 제거한다.
|
|
3455
|
-
"SVG",
|
|
3456
|
-
"MATH",
|
|
3457
|
-
"TEMPLATE"
|
|
3458
|
-
]);
|
|
3459
|
-
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3460
|
-
var sanitizeNode = (node) => {
|
|
3461
|
-
if (node.nodeType === Node.COMMENT_NODE) {
|
|
3462
|
-
node.remove();
|
|
3463
|
-
return;
|
|
3464
|
-
}
|
|
3465
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
3466
|
-
const element = node;
|
|
3467
|
-
const tagName = element.tagName;
|
|
3468
|
-
if (DROP_CONTENT_TAGS.has(tagName)) {
|
|
3469
|
-
element.remove();
|
|
3470
|
-
return;
|
|
3471
|
-
}
|
|
3472
|
-
Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
|
|
3473
|
-
if (ALLOWED_INLINE_TAGS.has(tagName)) {
|
|
3474
|
-
if (element.hasAttribute("className")) {
|
|
3475
|
-
const val = element.getAttribute("className");
|
|
3476
|
-
element.removeAttribute("className");
|
|
3477
|
-
if (!element.hasAttribute("class")) element.setAttribute("class", val);
|
|
3478
|
-
}
|
|
3479
|
-
const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tagName] ?? COMMON_ALLOWED_ATTRS;
|
|
3480
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
3481
|
-
if (!allowedAttrs.has(attr.name)) element.removeAttribute(attr.name);
|
|
3482
|
-
});
|
|
3483
|
-
if (tagName === "A") {
|
|
3484
|
-
const href = element.getAttribute("href");
|
|
3485
|
-
if (href !== null && href !== "" && !SAFE_HREF_RE.test(href)) element.removeAttribute("href");
|
|
3486
|
-
if (element.hasAttribute("target")) {
|
|
3487
|
-
element.setAttribute("rel", "noopener noreferrer");
|
|
3488
|
-
}
|
|
3489
|
-
}
|
|
3490
|
-
return;
|
|
3491
|
-
}
|
|
3492
|
-
const parent = element.parentNode;
|
|
3493
|
-
if (parent == null) return;
|
|
3494
|
-
while (element.firstChild) parent.insertBefore(element.firstChild, element);
|
|
3495
|
-
parent.removeChild(element);
|
|
3496
|
-
};
|
|
3497
|
-
var sanitizeInlineHtml = (value) => {
|
|
3498
|
-
if (typeof document === "undefined") return escapeHtml(value);
|
|
3499
|
-
const template = document.createElement("template");
|
|
3500
|
-
template.innerHTML = value;
|
|
3501
|
-
Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
|
|
3502
|
-
return template.innerHTML;
|
|
3503
|
-
};
|
|
3504
3591
|
var SEARCH_THRESHOLD = 11;
|
|
3505
3592
|
var SSelect = react.forwardRef(function SSelect2({
|
|
3506
3593
|
value,
|
|
@@ -3524,6 +3611,7 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3524
3611
|
label,
|
|
3525
3612
|
labelWidth,
|
|
3526
3613
|
icon,
|
|
3614
|
+
iconColor,
|
|
3527
3615
|
labelTooltip,
|
|
3528
3616
|
addonLabel,
|
|
3529
3617
|
addonAlign,
|
|
@@ -3535,18 +3623,15 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3535
3623
|
}, ref) {
|
|
3536
3624
|
const multi = type === "multi" || type === "multi_depth";
|
|
3537
3625
|
const portalContainer = usePortalContainer();
|
|
3626
|
+
const insideModal = useInsideModal();
|
|
3538
3627
|
const [search, setSearch] = react.useState("");
|
|
3539
3628
|
const [ruleError, setRuleError] = react.useState("");
|
|
3540
3629
|
const [open, setOpen] = react.useState(false);
|
|
3630
|
+
const [pendingOpen, setPendingOpen] = react.useState(false);
|
|
3631
|
+
const [focusedIndex, setFocusedIndex] = react.useState(-1);
|
|
3541
3632
|
const triggerRef = react.useRef(null);
|
|
3542
|
-
react.
|
|
3543
|
-
|
|
3544
|
-
() => ({
|
|
3545
|
-
focus: () => triggerRef.current?.focus(),
|
|
3546
|
-
open: () => setOpen(true)
|
|
3547
|
-
}),
|
|
3548
|
-
[]
|
|
3549
|
-
);
|
|
3633
|
+
const searchInputRef = react.useRef(null);
|
|
3634
|
+
const listRef = react.useRef(null);
|
|
3550
3635
|
const flat = react.useMemo(() => flattenOptions(options), [options]);
|
|
3551
3636
|
const selectable = react.useMemo(() => selectableOptions(flat), [flat]);
|
|
3552
3637
|
const hasLeaf = react.useMemo(() => flat.some((o) => !o.isGroup), [flat]);
|
|
@@ -3564,6 +3649,46 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3564
3649
|
const arr = Array.isArray(value) ? value : [value];
|
|
3565
3650
|
return arr.map((v) => v != null && typeof v === "object" ? v.value : v).filter((v) => v != null && v !== "").map(String);
|
|
3566
3651
|
}, [value]);
|
|
3652
|
+
const selectedRef = react.useRef(selected);
|
|
3653
|
+
selectedRef.current = selected;
|
|
3654
|
+
const emit = (next) => {
|
|
3655
|
+
if (!onValueChange) return;
|
|
3656
|
+
const mapped = next.map(
|
|
3657
|
+
(v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
|
|
3658
|
+
);
|
|
3659
|
+
onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
|
|
3660
|
+
};
|
|
3661
|
+
const commitOpen = (next) => {
|
|
3662
|
+
setOpen(next);
|
|
3663
|
+
onOpenChange?.(next);
|
|
3664
|
+
if (!next) {
|
|
3665
|
+
setPendingOpen(false);
|
|
3666
|
+
setSearch("");
|
|
3667
|
+
if (rules && rules.length > 0) {
|
|
3668
|
+
setRuleError(runRules(multi ? selectedRef.current : selectedRef.current[0] ?? "", rules));
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
};
|
|
3672
|
+
const requestOpen = (next) => {
|
|
3673
|
+
if (next && insideModal && portalContainer == null) {
|
|
3674
|
+
setPendingOpen(true);
|
|
3675
|
+
return;
|
|
3676
|
+
}
|
|
3677
|
+
commitOpen(next);
|
|
3678
|
+
};
|
|
3679
|
+
react.useEffect(() => {
|
|
3680
|
+
if (!insideModal || portalContainer == null || !pendingOpen) return;
|
|
3681
|
+
setPendingOpen(false);
|
|
3682
|
+
commitOpen(true);
|
|
3683
|
+
}, [insideModal, pendingOpen, portalContainer]);
|
|
3684
|
+
react.useImperativeHandle(
|
|
3685
|
+
ref,
|
|
3686
|
+
() => ({
|
|
3687
|
+
focus: () => triggerRef.current?.focus(),
|
|
3688
|
+
open: () => requestOpen(true)
|
|
3689
|
+
}),
|
|
3690
|
+
[insideModal, portalContainer]
|
|
3691
|
+
);
|
|
3567
3692
|
useFormField({
|
|
3568
3693
|
name,
|
|
3569
3694
|
validate: () => {
|
|
@@ -3573,13 +3698,6 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3573
3698
|
},
|
|
3574
3699
|
resetValidation: () => setRuleError("")
|
|
3575
3700
|
});
|
|
3576
|
-
const emit = (next) => {
|
|
3577
|
-
if (!onValueChange) return;
|
|
3578
|
-
const mapped = next.map(
|
|
3579
|
-
(v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
|
|
3580
|
-
);
|
|
3581
|
-
onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
|
|
3582
|
-
};
|
|
3583
3701
|
const filtered = react.useMemo(() => {
|
|
3584
3702
|
if (!showSearch || search.trim() === "") return flat;
|
|
3585
3703
|
const q = search.trim().toLowerCase();
|
|
@@ -3590,6 +3708,16 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3590
3708
|
const allValues = selectable.map((o) => o.value);
|
|
3591
3709
|
const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
|
|
3592
3710
|
const hasItems = filtered.some((o) => !o.isGroup);
|
|
3711
|
+
const navigableItems = react.useMemo(() => {
|
|
3712
|
+
const items = [];
|
|
3713
|
+
if (multi && useSelectAll && allValues.length > 0) {
|
|
3714
|
+
items.push({ value: "__select_all__", isSelectAll: true });
|
|
3715
|
+
}
|
|
3716
|
+
filtered.forEach((o) => {
|
|
3717
|
+
if (!o.isGroup && !o.disabled) items.push({ value: o.value });
|
|
3718
|
+
});
|
|
3719
|
+
return items;
|
|
3720
|
+
}, [allValues.length, filtered, multi, useSelectAll]);
|
|
3593
3721
|
const triggerText = (() => {
|
|
3594
3722
|
if (selected.length === 0) return placeholder;
|
|
3595
3723
|
if (multi) {
|
|
@@ -3599,26 +3727,119 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3599
3727
|
return labelByValue.get(selected[0]) ?? selected[0];
|
|
3600
3728
|
})();
|
|
3601
3729
|
const isPlaceholder = selected.length === 0;
|
|
3602
|
-
const
|
|
3603
|
-
|
|
3730
|
+
const handleSelect = (nextValue) => {
|
|
3731
|
+
if (multi) {
|
|
3732
|
+
const next = selected.includes(nextValue) ? selected.filter((v) => v !== nextValue) : [...selected, nextValue];
|
|
3733
|
+
emit(next);
|
|
3734
|
+
return;
|
|
3735
|
+
}
|
|
3736
|
+
emit([nextValue]);
|
|
3737
|
+
commitOpen(false);
|
|
3738
|
+
};
|
|
3739
|
+
const handleSelectAll = () => {
|
|
3740
|
+
emit(allSelected ? [] : allValues);
|
|
3741
|
+
};
|
|
3742
|
+
react.useEffect(() => {
|
|
3743
|
+
if (!open) {
|
|
3744
|
+
setFocusedIndex(-1);
|
|
3745
|
+
return;
|
|
3746
|
+
}
|
|
3747
|
+
setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
|
|
3748
|
+
}, [open, navigableItems.length]);
|
|
3749
|
+
react.useEffect(() => {
|
|
3750
|
+
if (!open) return;
|
|
3751
|
+
setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
|
|
3752
|
+
}, [search, open, navigableItems.length]);
|
|
3753
|
+
react.useEffect(() => {
|
|
3754
|
+
if (!open || focusedIndex < 0) return;
|
|
3755
|
+
const list = listRef.current;
|
|
3756
|
+
const focusedEl = list?.querySelector('[data-select-focused="true"]');
|
|
3757
|
+
if (!list || !focusedEl) return;
|
|
3758
|
+
const listRect = list.getBoundingClientRect();
|
|
3759
|
+
const itemRect = focusedEl.getBoundingClientRect();
|
|
3760
|
+
if (itemRect.top < listRect.top) {
|
|
3761
|
+
list.scrollTop += itemRect.top - listRect.top;
|
|
3762
|
+
} else if (itemRect.bottom > listRect.bottom) {
|
|
3763
|
+
list.scrollTop += itemRect.bottom - listRect.bottom;
|
|
3764
|
+
}
|
|
3765
|
+
}, [focusedIndex, open]);
|
|
3766
|
+
const moveFocusedIndex = (direction) => {
|
|
3767
|
+
if (!open || navigableItems.length === 0) return;
|
|
3768
|
+
setFocusedIndex((current) => {
|
|
3769
|
+
if (direction === "next") {
|
|
3770
|
+
return current < 0 ? 0 : (current + 1) % navigableItems.length;
|
|
3771
|
+
}
|
|
3772
|
+
return current <= 0 ? navigableItems.length - 1 : current - 1;
|
|
3773
|
+
});
|
|
3774
|
+
};
|
|
3775
|
+
const selectFocusedItem = () => {
|
|
3776
|
+
if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
|
|
3777
|
+
const focused = navigableItems[focusedIndex];
|
|
3778
|
+
if (focused.isSelectAll) {
|
|
3779
|
+
handleSelectAll();
|
|
3780
|
+
return;
|
|
3781
|
+
}
|
|
3782
|
+
handleSelect(focused.value);
|
|
3783
|
+
};
|
|
3784
|
+
const handleKeyDown = (event) => {
|
|
3785
|
+
if (!open) {
|
|
3786
|
+
if ((event.key === "ArrowDown" || event.key === "ArrowUp") && !effectiveDisabled) {
|
|
3787
|
+
event.preventDefault();
|
|
3788
|
+
requestOpen(true);
|
|
3789
|
+
}
|
|
3790
|
+
return;
|
|
3791
|
+
}
|
|
3792
|
+
if (event.key === "ArrowDown") {
|
|
3793
|
+
event.preventDefault();
|
|
3794
|
+
moveFocusedIndex("next");
|
|
3795
|
+
return;
|
|
3796
|
+
}
|
|
3797
|
+
if (event.key === "ArrowUp") {
|
|
3798
|
+
event.preventDefault();
|
|
3799
|
+
moveFocusedIndex("prev");
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3802
|
+
if (event.key === "Enter") {
|
|
3803
|
+
event.preventDefault();
|
|
3804
|
+
selectFocusedItem();
|
|
3805
|
+
return;
|
|
3806
|
+
}
|
|
3807
|
+
if (event.key === "Escape") {
|
|
3808
|
+
event.preventDefault();
|
|
3809
|
+
commitOpen(false);
|
|
3810
|
+
}
|
|
3811
|
+
};
|
|
3812
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3813
|
+
SField,
|
|
3604
3814
|
{
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3815
|
+
label,
|
|
3816
|
+
labelWidth,
|
|
3817
|
+
icon,
|
|
3818
|
+
iconColor,
|
|
3819
|
+
labelTooltip,
|
|
3820
|
+
labelTooltipProps,
|
|
3821
|
+
addonLabel,
|
|
3822
|
+
addonAlign,
|
|
3823
|
+
hint,
|
|
3824
|
+
error: error || ruleError !== "",
|
|
3825
|
+
status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
|
|
3826
|
+
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
3827
|
+
disabled: effectiveDisabled,
|
|
3828
|
+
width,
|
|
3829
|
+
className,
|
|
3830
|
+
style,
|
|
3616
3831
|
children: [
|
|
3832
|
+
name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selected.join(",") }),
|
|
3617
3833
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3618
|
-
|
|
3834
|
+
"button",
|
|
3619
3835
|
{
|
|
3620
3836
|
ref: triggerRef,
|
|
3837
|
+
type: "button",
|
|
3621
3838
|
disabled: effectiveDisabled,
|
|
3839
|
+
"aria-haspopup": "listbox",
|
|
3840
|
+
"aria-expanded": open,
|
|
3841
|
+
onClick: () => requestOpen(!open),
|
|
3842
|
+
onKeyDown: handleKeyDown,
|
|
3622
3843
|
className: "flex h-full w-full cursor-pointer items-center justify-between gap-[8px] bg-transparent px-[12px] text-left text-[12px] leading-[20px] outline-none disabled:cursor-not-allowed",
|
|
3623
3844
|
style: {
|
|
3624
3845
|
color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
|
|
@@ -3643,178 +3864,237 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3643
3864
|
]
|
|
3644
3865
|
}
|
|
3645
3866
|
),
|
|
3646
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3647
|
-
|
|
3867
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3868
|
+
SPortal,
|
|
3648
3869
|
{
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3870
|
+
open,
|
|
3871
|
+
onClose: () => commitOpen(false),
|
|
3872
|
+
onOpenChange: (next) => {
|
|
3873
|
+
if (next) requestOpen(true);
|
|
3874
|
+
},
|
|
3875
|
+
anchorRef: triggerRef,
|
|
3876
|
+
placement: "bottom",
|
|
3877
|
+
align: "start",
|
|
3878
|
+
offset: 4,
|
|
3879
|
+
fit: !dropdownWidth,
|
|
3880
|
+
onOpenAutoFocus: (event) => {
|
|
3881
|
+
if (!showSearch) {
|
|
3882
|
+
event.preventDefault();
|
|
3883
|
+
return;
|
|
3884
|
+
}
|
|
3885
|
+
event.preventDefault();
|
|
3886
|
+
requestAnimationFrame(() => searchInputRef.current?.focus());
|
|
3887
|
+
},
|
|
3888
|
+
onCloseAutoFocus: (event) => {
|
|
3889
|
+
event.preventDefault();
|
|
3890
|
+
triggerRef.current?.focus();
|
|
3891
|
+
},
|
|
3892
|
+
className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
|
|
3654
3893
|
style: {
|
|
3655
|
-
maxHeight: dropdownHeight,
|
|
3656
3894
|
...dropdownWidth ? { width: dropdownWidth } : {},
|
|
3657
3895
|
maxWidth: maxDropdownWidth
|
|
3658
3896
|
},
|
|
3659
|
-
children:
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3897
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3898
|
+
"div",
|
|
3899
|
+
{
|
|
3900
|
+
ref: listRef,
|
|
3901
|
+
className: "overflow-auto [scrollbar-width:auto] [&::-webkit-scrollbar]:w-[8px] [&::-webkit-scrollbar]:bg-[var(--color-grey-25)] [&::-webkit-scrollbar:horizontal]:h-[8px] [&::-webkit-scrollbar-thumb]:rounded-[4px] [&::-webkit-scrollbar-thumb]:bg-[var(--color-grey-45)] [&::-webkit-scrollbar-track]:bg-[var(--color-grey-25)]",
|
|
3902
|
+
style: { maxHeight: dropdownHeight },
|
|
3903
|
+
onMouseLeave: () => setFocusedIndex(-1),
|
|
3904
|
+
children: [
|
|
3905
|
+
showSearch && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-[1] bg-[var(--cmp-listBox-bg)] px-[8px] py-[4px]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-[6px] rounded-[4px] border border-solid border-[color:var(--color-grey-45)] bg-white px-[8px] py-[4px] focus-within:border-[color:var(--sys-color-field-border-focus)]", children: [
|
|
3906
|
+
/* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
|
|
3907
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3908
|
+
"input",
|
|
3909
|
+
{
|
|
3910
|
+
ref: searchInputRef,
|
|
3911
|
+
value: search,
|
|
3912
|
+
onChange: (e) => setSearch(e.target.value),
|
|
3913
|
+
onKeyDown: handleKeyDown,
|
|
3914
|
+
placeholder: "\uAC80\uC0C9",
|
|
3915
|
+
className: "min-w-0 flex-1 border-none bg-transparent text-[12px] leading-[20px] text-[color:var(--cmp-select-text-default)] outline-none placeholder:text-[color:var(--color-grey-55)]"
|
|
3916
|
+
}
|
|
3917
|
+
),
|
|
3918
|
+
search !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3919
|
+
SGhostButton,
|
|
3920
|
+
{
|
|
3921
|
+
icon: "close",
|
|
3922
|
+
size: "xxs",
|
|
3923
|
+
intent: "default",
|
|
3924
|
+
ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
|
|
3925
|
+
onClick: () => setSearch(""),
|
|
3926
|
+
className: "shrink-0"
|
|
3927
|
+
}
|
|
3928
|
+
)
|
|
3929
|
+
] }) }),
|
|
3930
|
+
hasItems ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3931
|
+
ItemList,
|
|
3932
|
+
{
|
|
3933
|
+
items: filtered,
|
|
3934
|
+
multi,
|
|
3935
|
+
selected,
|
|
3936
|
+
focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
|
|
3937
|
+
showSelectAll: multi && useSelectAll && allValues.length > 0,
|
|
3938
|
+
selectAllLabel: allSelectedLabel,
|
|
3939
|
+
selectAllSelected: allSelected,
|
|
3940
|
+
onSelectAll: handleSelectAll,
|
|
3941
|
+
onSelect: handleSelect,
|
|
3942
|
+
onHoverValue: (value2) => {
|
|
3943
|
+
if (value2 == null) {
|
|
3944
|
+
setFocusedIndex(-1);
|
|
3945
|
+
return;
|
|
3946
|
+
}
|
|
3947
|
+
const index = navigableItems.findIndex(
|
|
3948
|
+
(item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
|
|
3949
|
+
);
|
|
3950
|
+
if (index >= 0) setFocusedIndex(index);
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-[12px] py-[12px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-disabled)]", children: "\uD45C\uC2DC\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." })
|
|
3954
|
+
]
|
|
3955
|
+
}
|
|
3956
|
+
)
|
|
3697
3957
|
}
|
|
3698
3958
|
)
|
|
3699
3959
|
]
|
|
3700
3960
|
}
|
|
3701
3961
|
);
|
|
3702
|
-
const control = showSearch ? /* @__PURE__ */ jsxRuntime.jsx(Ariakit__namespace.ComboboxProvider, { value: search, setValue: setSearch, resetValueOnHide: true, children: selectStore }) : selectStore;
|
|
3703
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3704
|
-
SField,
|
|
3705
|
-
{
|
|
3706
|
-
label,
|
|
3707
|
-
labelWidth,
|
|
3708
|
-
icon,
|
|
3709
|
-
labelTooltip,
|
|
3710
|
-
labelTooltipProps,
|
|
3711
|
-
addonLabel,
|
|
3712
|
-
addonAlign,
|
|
3713
|
-
hint,
|
|
3714
|
-
error: error || ruleError !== "",
|
|
3715
|
-
status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
|
|
3716
|
-
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
3717
|
-
disabled: effectiveDisabled,
|
|
3718
|
-
width,
|
|
3719
|
-
className,
|
|
3720
|
-
style,
|
|
3721
|
-
children: [
|
|
3722
|
-
name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selected.join(",") }),
|
|
3723
|
-
control
|
|
3724
|
-
]
|
|
3725
|
-
}
|
|
3726
|
-
);
|
|
3727
3962
|
});
|
|
3728
3963
|
SSelect.displayName = "SSelect";
|
|
3729
3964
|
function ItemList({
|
|
3730
3965
|
items,
|
|
3731
3966
|
multi,
|
|
3732
3967
|
selected,
|
|
3733
|
-
|
|
3968
|
+
focusedValue,
|
|
3969
|
+
showSelectAll,
|
|
3970
|
+
selectAllLabel,
|
|
3971
|
+
selectAllSelected,
|
|
3972
|
+
onSelectAll,
|
|
3973
|
+
onSelect,
|
|
3974
|
+
onHoverValue
|
|
3734
3975
|
}) {
|
|
3735
3976
|
const itemBase = cn(
|
|
3736
|
-
|
|
3737
|
-
"group flex cursor-pointer select-none items-center gap-[8px] text-[12px] leading-[20px] outline-none",
|
|
3977
|
+
"group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
|
|
3738
3978
|
"text-[color:var(--cmp-listItem-content-default)]",
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
);
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
if (o.isGroup) {
|
|
3746
|
-
const isDepth1 = o.depth === 1;
|
|
3747
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3748
|
-
"div",
|
|
3749
|
-
{
|
|
3750
|
-
className: cn(
|
|
3751
|
-
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
3752
|
-
isDepth1 ? "border-t border-solid border-[color:var(--cmp-listItem-depth1-border-color)] bg-[var(--cmp-listItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listItem-depth2-middle-bg)] font-medium",
|
|
3753
|
-
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
3754
|
-
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
3755
|
-
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
|
|
3756
|
-
o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
|
|
3757
|
-
),
|
|
3758
|
-
style: { paddingLeft: padLeft },
|
|
3759
|
-
children: o.label
|
|
3760
|
-
},
|
|
3761
|
-
o.value
|
|
3762
|
-
);
|
|
3763
|
-
}
|
|
3764
|
-
const isSelected = selected.includes(o.value);
|
|
3765
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3766
|
-
Ariakit__namespace.SelectItem,
|
|
3979
|
+
"disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
|
|
3980
|
+
);
|
|
3981
|
+
const activeClass = "hover:bg-[var(--cmp-listItem-bg-hover)] hover:text-[color:var(--cmp-listItem-content-hover)] focus:bg-[var(--cmp-listItem-bg-hover)] focus:text-[color:var(--cmp-listItem-content-hover)]";
|
|
3982
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
3983
|
+
showSelectAll && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3984
|
+
"button",
|
|
3767
3985
|
{
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3986
|
+
type: "button",
|
|
3987
|
+
"data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
|
|
3988
|
+
"data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
|
|
3771
3989
|
className: cn(
|
|
3772
3990
|
itemBase,
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
|
|
3780
|
-
render: useSearch ? /* @__PURE__ */ jsxRuntime.jsx(Ariakit__namespace.ComboboxItem, {}) : void 0,
|
|
3991
|
+
focusedValue === "__select_all__" && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
|
|
3992
|
+
),
|
|
3993
|
+
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: 12 },
|
|
3994
|
+
onMouseEnter: () => onHoverValue("__select_all__"),
|
|
3995
|
+
onMouseLeave: () => onHoverValue(null),
|
|
3996
|
+
onClick: onSelectAll,
|
|
3781
3997
|
children: [
|
|
3782
|
-
multi && // hover(active) 시 checkbox inverse — 파란 hover 배경 위에서 checked/unchecked 모두 흰 테두리 + 투명 배경
|
|
3783
3998
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3784
3999
|
"span",
|
|
3785
4000
|
{
|
|
3786
4001
|
className: cn(
|
|
3787
4002
|
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
3788
|
-
|
|
3789
|
-
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
3790
|
-
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
3791
|
-
) : cn(
|
|
3792
|
-
"group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
|
|
3793
|
-
isSelected ? "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)] group-data-[active-item]:bg-transparent" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-transparent"
|
|
3794
|
-
)
|
|
4003
|
+
selectAllSelected ? focusedValue === "__select_all__" ? "border-[color:var(--cmp-checkbox-checked-border-inverse)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-[var(--cmp-checkbox-unchecked-bg-default)]"
|
|
3795
4004
|
),
|
|
3796
|
-
children:
|
|
4005
|
+
children: selectAllSelected && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3797
4006
|
SIcon,
|
|
3798
4007
|
{
|
|
3799
4008
|
name: "checkboxCheck",
|
|
3800
4009
|
size: 12,
|
|
3801
|
-
color:
|
|
4010
|
+
color: "var(--cmp-checkbox-checked-icon-default)"
|
|
3802
4011
|
}
|
|
3803
4012
|
)
|
|
3804
4013
|
}
|
|
3805
4014
|
),
|
|
3806
|
-
|
|
3807
|
-
"span",
|
|
3808
|
-
{
|
|
3809
|
-
className: "flex-1 truncate",
|
|
3810
|
-
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
3811
|
-
}
|
|
3812
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
4015
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
3813
4016
|
]
|
|
3814
|
-
}
|
|
3815
|
-
|
|
3816
|
-
)
|
|
3817
|
-
|
|
4017
|
+
}
|
|
4018
|
+
),
|
|
4019
|
+
items.map((o) => {
|
|
4020
|
+
const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
|
|
4021
|
+
if (o.isGroup) {
|
|
4022
|
+
const isDepth1 = o.depth === 1;
|
|
4023
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4024
|
+
"div",
|
|
4025
|
+
{
|
|
4026
|
+
className: cn(
|
|
4027
|
+
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
4028
|
+
isDepth1 ? "border-t border-solid border-[color:var(--cmp-listItem-depth1-border-color)] bg-[var(--cmp-listItem-depth1-bg-default)] font-bold" : "bg-[var(--cmp-listItem-depth2-middle-bg)] font-medium",
|
|
4029
|
+
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
4030
|
+
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
4031
|
+
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
|
|
4032
|
+
o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
|
|
4033
|
+
),
|
|
4034
|
+
style: { paddingLeft: padLeft },
|
|
4035
|
+
children: o.label
|
|
4036
|
+
},
|
|
4037
|
+
o.value
|
|
4038
|
+
);
|
|
4039
|
+
}
|
|
4040
|
+
const isSelected = selected.includes(o.value);
|
|
4041
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4042
|
+
"button",
|
|
4043
|
+
{
|
|
4044
|
+
type: "button",
|
|
4045
|
+
disabled: o.disabled,
|
|
4046
|
+
role: "option",
|
|
4047
|
+
"aria-selected": isSelected,
|
|
4048
|
+
"data-select-focused": focusedValue === o.value ? "true" : void 0,
|
|
4049
|
+
"data-select-active": focusedValue === o.value ? "true" : void 0,
|
|
4050
|
+
className: cn(
|
|
4051
|
+
itemBase,
|
|
4052
|
+
isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
|
|
4053
|
+
focusedValue === o.value && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]",
|
|
4054
|
+
// 비활성 항목은 hover/focus 스타일 대상에서 제외한다.
|
|
4055
|
+
// (:hover 는 disabled 버튼에도 매칭되므로 클래스 자체를 붙이지 않아야 한다)
|
|
4056
|
+
!o.disabled && activeClass
|
|
4057
|
+
),
|
|
4058
|
+
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
|
|
4059
|
+
onMouseEnter: () => onHoverValue(o.value),
|
|
4060
|
+
onMouseLeave: () => onHoverValue(null),
|
|
4061
|
+
onClick: () => onSelect(o.value),
|
|
4062
|
+
children: [
|
|
4063
|
+
multi && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4064
|
+
"span",
|
|
4065
|
+
{
|
|
4066
|
+
className: cn(
|
|
4067
|
+
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
4068
|
+
o.disabled ? (
|
|
4069
|
+
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
4070
|
+
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
4071
|
+
) : cn(
|
|
4072
|
+
isSelected ? focusedValue === o.value ? "border-[color:var(--cmp-checkbox-checked-border-inverse)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-checked-bg-default)] bg-[var(--cmp-checkbox-checked-bg-default)]" : "border-[color:var(--cmp-checkbox-unchecked-border-default)] bg-[var(--cmp-checkbox-unchecked-bg-default)]"
|
|
4073
|
+
)
|
|
4074
|
+
),
|
|
4075
|
+
children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4076
|
+
SIcon,
|
|
4077
|
+
{
|
|
4078
|
+
name: "checkboxCheck",
|
|
4079
|
+
size: 12,
|
|
4080
|
+
color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
|
|
4081
|
+
}
|
|
4082
|
+
)
|
|
4083
|
+
}
|
|
4084
|
+
),
|
|
4085
|
+
isHtmlLabel(o.label) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4086
|
+
"span",
|
|
4087
|
+
{
|
|
4088
|
+
className: "flex-1 truncate",
|
|
4089
|
+
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
4090
|
+
}
|
|
4091
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
4092
|
+
]
|
|
4093
|
+
},
|
|
4094
|
+
o.value
|
|
4095
|
+
);
|
|
4096
|
+
})
|
|
4097
|
+
] });
|
|
3818
4098
|
}
|
|
3819
4099
|
|
|
3820
4100
|
exports.SSelect = SSelect;
|