sellmate-design-system-react 0.1.10 → 0.1.11
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 +3 -1
- package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
- package/dist/components/SActionModal/SActionModal.js +3 -1
- package/dist/components/SActionModal/SActionModal.js.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs +15 -2
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +3 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.js +15 -2
- package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
- package/dist/components/SCalendar/SCalendar.cjs +2 -0
- package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
- package/dist/components/SCalendar/SCalendar.js +2 -0
- package/dist/components/SCalendar/SCalendar.js.map +1 -1
- package/dist/components/SChip/SChip.cjs +2 -0
- package/dist/components/SChip/SChip.cjs.map +1 -1
- package/dist/components/SChip/SChip.js +2 -0
- package/dist/components/SChip/SChip.js.map +1 -1
- package/dist/components/SChipInput/SChipInput.cjs +21 -3
- package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
- package/dist/components/SChipInput/SChipInput.d.ts +5 -0
- package/dist/components/SChipInput/SChipInput.js +21 -3
- package/dist/components/SChipInput/SChipInput.js.map +1 -1
- package/dist/components/SChipInput/index.d.ts +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.cjs +13 -10
- package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.d.ts +7 -4
- package/dist/components/SConfirmModal/SConfirmModal.js +13 -10
- package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.cjs +15 -2
- package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.d.ts +3 -1
- package/dist/components/SDatePicker/SDatePicker.js +15 -2
- package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs +15 -2
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +3 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.js +15 -2
- package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
- package/dist/components/SDropdownButton/SDropdownButton.cjs +2 -0
- package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
- package/dist/components/SDropdownButton/SDropdownButton.js +2 -0
- package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
- package/dist/components/SField/SField.cjs +13 -2
- package/dist/components/SField/SField.cjs.map +1 -1
- package/dist/components/SField/SField.d.ts +4 -1
- package/dist/components/SField/SField.js +13 -2
- package/dist/components/SField/SField.js.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.cjs +16 -3
- package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.d.ts +3 -1
- package/dist/components/SFilePicker/SFilePicker.js +16 -3
- package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
- package/dist/components/SGhostButton/SGhostButton.cjs +2 -0
- package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
- package/dist/components/SGhostButton/SGhostButton.js +2 -0
- package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
- package/dist/components/SGuide/SGuide.cjs +2 -0
- package/dist/components/SGuide/SGuide.cjs.map +1 -1
- package/dist/components/SGuide/SGuide.js +2 -0
- package/dist/components/SGuide/SGuide.js.map +1 -1
- package/dist/components/SInput/SInput.cjs +15 -2
- package/dist/components/SInput/SInput.cjs.map +1 -1
- package/dist/components/SInput/SInput.d.ts +4 -1
- package/dist/components/SInput/SInput.js +15 -2
- package/dist/components/SInput/SInput.js.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs +496 -206
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +27 -6
- package/dist/components/SKeyValueTable/SKeyValueTable.js +495 -205
- package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.cjs +3 -1
- package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.js +3 -1
- package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
- package/dist/components/SModalContainer/SModalContainer.cjs +3 -1
- package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
- package/dist/components/SModalContainer/SModalContainer.js +3 -1
- package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.cjs +15 -2
- package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
- package/dist/components/SNumberInput/SNumberInput.js +15 -2
- package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
- package/dist/components/SPopover/SPopover.cjs +2 -0
- package/dist/components/SPopover/SPopover.cjs.map +1 -1
- package/dist/components/SPopover/SPopover.js +2 -0
- package/dist/components/SPopover/SPopover.js.map +1 -1
- package/dist/components/SPortal/SPortal.cjs +2 -0
- package/dist/components/SPortal/SPortal.cjs.map +1 -1
- package/dist/components/SPortal/SPortal.js +2 -0
- package/dist/components/SPortal/SPortal.js.map +1 -1
- package/dist/components/SSelect/SSelect.cjs +530 -262
- package/dist/components/SSelect/SSelect.cjs.map +1 -1
- package/dist/components/SSelect/SSelect.d.ts +4 -2
- package/dist/components/SSelect/SSelect.js +531 -262
- package/dist/components/SSelect/SSelect.js.map +1 -1
- package/dist/components/STable/STable.cjs +529 -261
- package/dist/components/STable/STable.cjs.map +1 -1
- package/dist/components/STable/STable.js +530 -261
- 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 +15 -2
- package/dist/components/STextarea/STextarea.cjs.map +1 -1
- package/dist/components/STextarea/STextarea.d.ts +2 -0
- package/dist/components/STextarea/STextarea.js +15 -2
- package/dist/components/STextarea/STextarea.js.map +1 -1
- package/dist/components/SToast/SToast.cjs +2 -0
- package/dist/components/SToast/SToast.cjs.map +1 -1
- package/dist/components/SToast/SToast.js +2 -0
- package/dist/components/SToast/SToast.js.map +1 -1
- package/dist/components/SToast/SToastContainer.cjs +2 -0
- package/dist/components/SToast/SToastContainer.cjs.map +1 -1
- package/dist/components/SToast/SToastContainer.js +2 -0
- package/dist/components/SToast/SToastContainer.js.map +1 -1
- package/dist/components/STooltip/STooltip.cjs +2 -0
- package/dist/components/STooltip/STooltip.cjs.map +1 -1
- package/dist/components/STooltip/STooltip.js +2 -0
- package/dist/components/STooltip/STooltip.js.map +1 -1
- package/dist/index.cjs +412 -184
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +413 -184
- package/dist/index.js.map +1 -1
- package/dist/lib/portal-container.d.ts +7 -0
- package/dist/styles.css +61 -37
- 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 = [
|
|
@@ -3216,32 +3314,6 @@ var STooltip = react.forwardRef(function STooltip2({
|
|
|
3216
3314
|
] });
|
|
3217
3315
|
});
|
|
3218
3316
|
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
3317
|
var FIELD_SIZE = {
|
|
3246
3318
|
sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
|
|
3247
3319
|
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
|
|
@@ -3256,6 +3328,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3256
3328
|
name,
|
|
3257
3329
|
label = "",
|
|
3258
3330
|
icon,
|
|
3331
|
+
iconColor,
|
|
3259
3332
|
labelTooltip = "",
|
|
3260
3333
|
labelTooltipProps,
|
|
3261
3334
|
labelWidth,
|
|
@@ -3333,7 +3406,15 @@ var SField = react.forwardRef(function SField2({
|
|
|
3333
3406
|
lineHeight: `${s.lineHeight}px`
|
|
3334
3407
|
},
|
|
3335
3408
|
children: [
|
|
3336
|
-
icon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3409
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3410
|
+
SIcon,
|
|
3411
|
+
{
|
|
3412
|
+
name: icon,
|
|
3413
|
+
size: s.icon,
|
|
3414
|
+
color: iconColor,
|
|
3415
|
+
style: { marginRight: s.labelGap }
|
|
3416
|
+
}
|
|
3417
|
+
),
|
|
3337
3418
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
|
|
3338
3419
|
(labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3339
3420
|
STooltip,
|
|
@@ -3383,7 +3464,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3383
3464
|
borderBottomLeftRadius: s.radius - 1
|
|
3384
3465
|
},
|
|
3385
3466
|
children: [
|
|
3386
|
-
icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon }),
|
|
3467
|
+
icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: iconColor }),
|
|
3387
3468
|
effectiveAddonLabel
|
|
3388
3469
|
]
|
|
3389
3470
|
}
|
|
@@ -3398,6 +3479,77 @@ var SField = react.forwardRef(function SField2({
|
|
|
3398
3479
|
}
|
|
3399
3480
|
);
|
|
3400
3481
|
});
|
|
3482
|
+
function SPortal({
|
|
3483
|
+
open,
|
|
3484
|
+
onClose,
|
|
3485
|
+
onOpenChange,
|
|
3486
|
+
anchorRef,
|
|
3487
|
+
placement = "bottom",
|
|
3488
|
+
align = "start",
|
|
3489
|
+
offset = 4,
|
|
3490
|
+
alignOffset = 0,
|
|
3491
|
+
fit = false,
|
|
3492
|
+
maxHeight,
|
|
3493
|
+
autoClose = false,
|
|
3494
|
+
persistent = false,
|
|
3495
|
+
viewportPadding = 0,
|
|
3496
|
+
onOpenAutoFocus,
|
|
3497
|
+
onCloseAutoFocus,
|
|
3498
|
+
children,
|
|
3499
|
+
className,
|
|
3500
|
+
style,
|
|
3501
|
+
ariaLabel
|
|
3502
|
+
}) {
|
|
3503
|
+
const portalContainer = usePortalContainer();
|
|
3504
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3505
|
+
RPopover2__namespace.Root,
|
|
3506
|
+
{
|
|
3507
|
+
open,
|
|
3508
|
+
onOpenChange: (next) => {
|
|
3509
|
+
onOpenChange?.(next);
|
|
3510
|
+
if (!next) onClose?.();
|
|
3511
|
+
},
|
|
3512
|
+
children: [
|
|
3513
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { virtualRef: anchorRef }),
|
|
3514
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3515
|
+
RPopover2__namespace.Content,
|
|
3516
|
+
{
|
|
3517
|
+
side: placement,
|
|
3518
|
+
align,
|
|
3519
|
+
sideOffset: offset,
|
|
3520
|
+
alignOffset,
|
|
3521
|
+
collisionPadding: viewportPadding,
|
|
3522
|
+
"aria-label": ariaLabel,
|
|
3523
|
+
onPointerDownOutside: (e) => {
|
|
3524
|
+
if (persistent || anchorRef.current?.contains(e.target)) {
|
|
3525
|
+
e.preventDefault();
|
|
3526
|
+
}
|
|
3527
|
+
},
|
|
3528
|
+
onFocusOutside: (e) => {
|
|
3529
|
+
if (persistent || anchorRef.current?.contains(e.target)) {
|
|
3530
|
+
e.preventDefault();
|
|
3531
|
+
}
|
|
3532
|
+
},
|
|
3533
|
+
onEscapeKeyDown: persistent ? (e) => e.preventDefault() : void 0,
|
|
3534
|
+
onOpenAutoFocus,
|
|
3535
|
+
onCloseAutoFocus,
|
|
3536
|
+
onClick: autoClose ? () => onClose?.() : void 0,
|
|
3537
|
+
className: cn(
|
|
3538
|
+
"z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm",
|
|
3539
|
+
maxHeight != null && "overflow-y-auto",
|
|
3540
|
+
fit && "min-w-(--radix-popover-trigger-width)",
|
|
3541
|
+
FLOATING_SLIDE_ANIM,
|
|
3542
|
+
className
|
|
3543
|
+
),
|
|
3544
|
+
style: { maxHeight, ...style },
|
|
3545
|
+
children
|
|
3546
|
+
}
|
|
3547
|
+
) })
|
|
3548
|
+
]
|
|
3549
|
+
}
|
|
3550
|
+
);
|
|
3551
|
+
}
|
|
3552
|
+
SPortal.displayName = "SPortal";
|
|
3401
3553
|
|
|
3402
3554
|
// src/components/SSelect/select.config.ts
|
|
3403
3555
|
function flattenOptions(options, depth = 1) {
|
|
@@ -3432,75 +3584,6 @@ function flattenOptions(options, depth = 1) {
|
|
|
3432
3584
|
var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
|
|
3433
3585
|
var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
|
|
3434
3586
|
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
3587
|
var SEARCH_THRESHOLD = 11;
|
|
3505
3588
|
var SSelect = react.forwardRef(function SSelect2({
|
|
3506
3589
|
value,
|
|
@@ -3524,6 +3607,7 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3524
3607
|
label,
|
|
3525
3608
|
labelWidth,
|
|
3526
3609
|
icon,
|
|
3610
|
+
iconColor,
|
|
3527
3611
|
labelTooltip,
|
|
3528
3612
|
addonLabel,
|
|
3529
3613
|
addonAlign,
|
|
@@ -3535,18 +3619,15 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3535
3619
|
}, ref) {
|
|
3536
3620
|
const multi = type === "multi" || type === "multi_depth";
|
|
3537
3621
|
const portalContainer = usePortalContainer();
|
|
3622
|
+
const insideModal = useInsideModal();
|
|
3538
3623
|
const [search, setSearch] = react.useState("");
|
|
3539
3624
|
const [ruleError, setRuleError] = react.useState("");
|
|
3540
3625
|
const [open, setOpen] = react.useState(false);
|
|
3626
|
+
const [pendingOpen, setPendingOpen] = react.useState(false);
|
|
3627
|
+
const [focusedIndex, setFocusedIndex] = react.useState(-1);
|
|
3541
3628
|
const triggerRef = react.useRef(null);
|
|
3542
|
-
react.
|
|
3543
|
-
|
|
3544
|
-
() => ({
|
|
3545
|
-
focus: () => triggerRef.current?.focus(),
|
|
3546
|
-
open: () => setOpen(true)
|
|
3547
|
-
}),
|
|
3548
|
-
[]
|
|
3549
|
-
);
|
|
3629
|
+
const searchInputRef = react.useRef(null);
|
|
3630
|
+
const listRef = react.useRef(null);
|
|
3550
3631
|
const flat = react.useMemo(() => flattenOptions(options), [options]);
|
|
3551
3632
|
const selectable = react.useMemo(() => selectableOptions(flat), [flat]);
|
|
3552
3633
|
const hasLeaf = react.useMemo(() => flat.some((o) => !o.isGroup), [flat]);
|
|
@@ -3564,6 +3645,46 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3564
3645
|
const arr = Array.isArray(value) ? value : [value];
|
|
3565
3646
|
return arr.map((v) => v != null && typeof v === "object" ? v.value : v).filter((v) => v != null && v !== "").map(String);
|
|
3566
3647
|
}, [value]);
|
|
3648
|
+
const selectedRef = react.useRef(selected);
|
|
3649
|
+
selectedRef.current = selected;
|
|
3650
|
+
const emit = (next) => {
|
|
3651
|
+
if (!onValueChange) return;
|
|
3652
|
+
const mapped = next.map(
|
|
3653
|
+
(v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
|
|
3654
|
+
);
|
|
3655
|
+
onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
|
|
3656
|
+
};
|
|
3657
|
+
const commitOpen = (next) => {
|
|
3658
|
+
setOpen(next);
|
|
3659
|
+
onOpenChange?.(next);
|
|
3660
|
+
if (!next) {
|
|
3661
|
+
setPendingOpen(false);
|
|
3662
|
+
setSearch("");
|
|
3663
|
+
if (rules && rules.length > 0) {
|
|
3664
|
+
setRuleError(runRules(multi ? selectedRef.current : selectedRef.current[0] ?? "", rules));
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
};
|
|
3668
|
+
const requestOpen = (next) => {
|
|
3669
|
+
if (next && insideModal && portalContainer == null) {
|
|
3670
|
+
setPendingOpen(true);
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3673
|
+
commitOpen(next);
|
|
3674
|
+
};
|
|
3675
|
+
react.useEffect(() => {
|
|
3676
|
+
if (!insideModal || portalContainer == null || !pendingOpen) return;
|
|
3677
|
+
setPendingOpen(false);
|
|
3678
|
+
commitOpen(true);
|
|
3679
|
+
}, [insideModal, pendingOpen, portalContainer]);
|
|
3680
|
+
react.useImperativeHandle(
|
|
3681
|
+
ref,
|
|
3682
|
+
() => ({
|
|
3683
|
+
focus: () => triggerRef.current?.focus(),
|
|
3684
|
+
open: () => requestOpen(true)
|
|
3685
|
+
}),
|
|
3686
|
+
[insideModal, portalContainer]
|
|
3687
|
+
);
|
|
3567
3688
|
useFormField({
|
|
3568
3689
|
name,
|
|
3569
3690
|
validate: () => {
|
|
@@ -3573,13 +3694,6 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3573
3694
|
},
|
|
3574
3695
|
resetValidation: () => setRuleError("")
|
|
3575
3696
|
});
|
|
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
3697
|
const filtered = react.useMemo(() => {
|
|
3584
3698
|
if (!showSearch || search.trim() === "") return flat;
|
|
3585
3699
|
const q = search.trim().toLowerCase();
|
|
@@ -3590,6 +3704,16 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3590
3704
|
const allValues = selectable.map((o) => o.value);
|
|
3591
3705
|
const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
|
|
3592
3706
|
const hasItems = filtered.some((o) => !o.isGroup);
|
|
3707
|
+
const navigableItems = react.useMemo(() => {
|
|
3708
|
+
const items = [];
|
|
3709
|
+
if (multi && useSelectAll && allValues.length > 0) {
|
|
3710
|
+
items.push({ value: "__select_all__", isSelectAll: true });
|
|
3711
|
+
}
|
|
3712
|
+
filtered.forEach((o) => {
|
|
3713
|
+
if (!o.isGroup && !o.disabled) items.push({ value: o.value });
|
|
3714
|
+
});
|
|
3715
|
+
return items;
|
|
3716
|
+
}, [allValues.length, filtered, multi, useSelectAll]);
|
|
3593
3717
|
const triggerText = (() => {
|
|
3594
3718
|
if (selected.length === 0) return placeholder;
|
|
3595
3719
|
if (multi) {
|
|
@@ -3599,26 +3723,119 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3599
3723
|
return labelByValue.get(selected[0]) ?? selected[0];
|
|
3600
3724
|
})();
|
|
3601
3725
|
const isPlaceholder = selected.length === 0;
|
|
3602
|
-
const
|
|
3603
|
-
|
|
3726
|
+
const handleSelect = (nextValue) => {
|
|
3727
|
+
if (multi) {
|
|
3728
|
+
const next = selected.includes(nextValue) ? selected.filter((v) => v !== nextValue) : [...selected, nextValue];
|
|
3729
|
+
emit(next);
|
|
3730
|
+
return;
|
|
3731
|
+
}
|
|
3732
|
+
emit([nextValue]);
|
|
3733
|
+
commitOpen(false);
|
|
3734
|
+
};
|
|
3735
|
+
const handleSelectAll = () => {
|
|
3736
|
+
emit(allSelected ? [] : allValues);
|
|
3737
|
+
};
|
|
3738
|
+
react.useEffect(() => {
|
|
3739
|
+
if (!open) {
|
|
3740
|
+
setFocusedIndex(-1);
|
|
3741
|
+
return;
|
|
3742
|
+
}
|
|
3743
|
+
setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
|
|
3744
|
+
}, [open, navigableItems.length]);
|
|
3745
|
+
react.useEffect(() => {
|
|
3746
|
+
if (!open) return;
|
|
3747
|
+
setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
|
|
3748
|
+
}, [search, open, navigableItems.length]);
|
|
3749
|
+
react.useEffect(() => {
|
|
3750
|
+
if (!open || focusedIndex < 0) return;
|
|
3751
|
+
const list = listRef.current;
|
|
3752
|
+
const focusedEl = list?.querySelector('[data-select-focused="true"]');
|
|
3753
|
+
if (!list || !focusedEl) return;
|
|
3754
|
+
const listRect = list.getBoundingClientRect();
|
|
3755
|
+
const itemRect = focusedEl.getBoundingClientRect();
|
|
3756
|
+
if (itemRect.top < listRect.top) {
|
|
3757
|
+
list.scrollTop += itemRect.top - listRect.top;
|
|
3758
|
+
} else if (itemRect.bottom > listRect.bottom) {
|
|
3759
|
+
list.scrollTop += itemRect.bottom - listRect.bottom;
|
|
3760
|
+
}
|
|
3761
|
+
}, [focusedIndex, open]);
|
|
3762
|
+
const moveFocusedIndex = (direction) => {
|
|
3763
|
+
if (!open || navigableItems.length === 0) return;
|
|
3764
|
+
setFocusedIndex((current) => {
|
|
3765
|
+
if (direction === "next") {
|
|
3766
|
+
return current < 0 ? 0 : (current + 1) % navigableItems.length;
|
|
3767
|
+
}
|
|
3768
|
+
return current <= 0 ? navigableItems.length - 1 : current - 1;
|
|
3769
|
+
});
|
|
3770
|
+
};
|
|
3771
|
+
const selectFocusedItem = () => {
|
|
3772
|
+
if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
|
|
3773
|
+
const focused = navigableItems[focusedIndex];
|
|
3774
|
+
if (focused.isSelectAll) {
|
|
3775
|
+
handleSelectAll();
|
|
3776
|
+
return;
|
|
3777
|
+
}
|
|
3778
|
+
handleSelect(focused.value);
|
|
3779
|
+
};
|
|
3780
|
+
const handleKeyDown = (event) => {
|
|
3781
|
+
if (!open) {
|
|
3782
|
+
if ((event.key === "ArrowDown" || event.key === "ArrowUp") && !effectiveDisabled) {
|
|
3783
|
+
event.preventDefault();
|
|
3784
|
+
requestOpen(true);
|
|
3785
|
+
}
|
|
3786
|
+
return;
|
|
3787
|
+
}
|
|
3788
|
+
if (event.key === "ArrowDown") {
|
|
3789
|
+
event.preventDefault();
|
|
3790
|
+
moveFocusedIndex("next");
|
|
3791
|
+
return;
|
|
3792
|
+
}
|
|
3793
|
+
if (event.key === "ArrowUp") {
|
|
3794
|
+
event.preventDefault();
|
|
3795
|
+
moveFocusedIndex("prev");
|
|
3796
|
+
return;
|
|
3797
|
+
}
|
|
3798
|
+
if (event.key === "Enter") {
|
|
3799
|
+
event.preventDefault();
|
|
3800
|
+
selectFocusedItem();
|
|
3801
|
+
return;
|
|
3802
|
+
}
|
|
3803
|
+
if (event.key === "Escape") {
|
|
3804
|
+
event.preventDefault();
|
|
3805
|
+
commitOpen(false);
|
|
3806
|
+
}
|
|
3807
|
+
};
|
|
3808
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3809
|
+
SField,
|
|
3604
3810
|
{
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3811
|
+
label,
|
|
3812
|
+
labelWidth,
|
|
3813
|
+
icon,
|
|
3814
|
+
iconColor,
|
|
3815
|
+
labelTooltip,
|
|
3816
|
+
labelTooltipProps,
|
|
3817
|
+
addonLabel,
|
|
3818
|
+
addonAlign,
|
|
3819
|
+
hint,
|
|
3820
|
+
error: error || ruleError !== "",
|
|
3821
|
+
status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
|
|
3822
|
+
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
3823
|
+
disabled: effectiveDisabled,
|
|
3824
|
+
width,
|
|
3825
|
+
className,
|
|
3826
|
+
style,
|
|
3616
3827
|
children: [
|
|
3828
|
+
name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selected.join(",") }),
|
|
3617
3829
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3618
|
-
|
|
3830
|
+
"button",
|
|
3619
3831
|
{
|
|
3620
3832
|
ref: triggerRef,
|
|
3833
|
+
type: "button",
|
|
3621
3834
|
disabled: effectiveDisabled,
|
|
3835
|
+
"aria-haspopup": "listbox",
|
|
3836
|
+
"aria-expanded": open,
|
|
3837
|
+
onClick: () => requestOpen(!open),
|
|
3838
|
+
onKeyDown: handleKeyDown,
|
|
3622
3839
|
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
3840
|
style: {
|
|
3624
3841
|
color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
|
|
@@ -3643,178 +3860,229 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3643
3860
|
]
|
|
3644
3861
|
}
|
|
3645
3862
|
),
|
|
3646
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3647
|
-
|
|
3863
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3864
|
+
SPortal,
|
|
3648
3865
|
{
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3866
|
+
open,
|
|
3867
|
+
onClose: () => commitOpen(false),
|
|
3868
|
+
onOpenChange: (next) => {
|
|
3869
|
+
if (next) requestOpen(true);
|
|
3870
|
+
},
|
|
3871
|
+
anchorRef: triggerRef,
|
|
3872
|
+
placement: "bottom",
|
|
3873
|
+
align: "start",
|
|
3874
|
+
offset: 4,
|
|
3875
|
+
fit: !dropdownWidth,
|
|
3876
|
+
onOpenAutoFocus: (event) => {
|
|
3877
|
+
if (!showSearch) {
|
|
3878
|
+
event.preventDefault();
|
|
3879
|
+
return;
|
|
3880
|
+
}
|
|
3881
|
+
event.preventDefault();
|
|
3882
|
+
requestAnimationFrame(() => searchInputRef.current?.focus());
|
|
3883
|
+
},
|
|
3884
|
+
onCloseAutoFocus: (event) => {
|
|
3885
|
+
event.preventDefault();
|
|
3886
|
+
triggerRef.current?.focus();
|
|
3887
|
+
},
|
|
3888
|
+
className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] py-[4px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
|
|
3654
3889
|
style: {
|
|
3655
|
-
maxHeight: dropdownHeight,
|
|
3656
3890
|
...dropdownWidth ? { width: dropdownWidth } : {},
|
|
3657
3891
|
maxWidth: maxDropdownWidth
|
|
3658
3892
|
},
|
|
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
|
-
|
|
3893
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3894
|
+
"div",
|
|
3895
|
+
{
|
|
3896
|
+
ref: listRef,
|
|
3897
|
+
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)]",
|
|
3898
|
+
style: { maxHeight: dropdownHeight },
|
|
3899
|
+
onMouseLeave: () => setFocusedIndex(-1),
|
|
3900
|
+
children: [
|
|
3901
|
+
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: [
|
|
3902
|
+
/* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
|
|
3903
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3904
|
+
"input",
|
|
3905
|
+
{
|
|
3906
|
+
ref: searchInputRef,
|
|
3907
|
+
value: search,
|
|
3908
|
+
onChange: (e) => setSearch(e.target.value),
|
|
3909
|
+
onKeyDown: handleKeyDown,
|
|
3910
|
+
placeholder: "\uAC80\uC0C9",
|
|
3911
|
+
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)]"
|
|
3912
|
+
}
|
|
3913
|
+
),
|
|
3914
|
+
search !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3915
|
+
SGhostButton,
|
|
3916
|
+
{
|
|
3917
|
+
icon: "close",
|
|
3918
|
+
size: "xxs",
|
|
3919
|
+
intent: "default",
|
|
3920
|
+
ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
|
|
3921
|
+
onClick: () => setSearch(""),
|
|
3922
|
+
className: "shrink-0"
|
|
3923
|
+
}
|
|
3924
|
+
)
|
|
3925
|
+
] }) }),
|
|
3926
|
+
hasItems ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3927
|
+
ItemList,
|
|
3928
|
+
{
|
|
3929
|
+
items: filtered,
|
|
3930
|
+
multi,
|
|
3931
|
+
selected,
|
|
3932
|
+
focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
|
|
3933
|
+
showSelectAll: multi && useSelectAll && allValues.length > 0,
|
|
3934
|
+
selectAllLabel: allSelectedLabel,
|
|
3935
|
+
selectAllSelected: allSelected,
|
|
3936
|
+
onSelectAll: handleSelectAll,
|
|
3937
|
+
onSelect: handleSelect,
|
|
3938
|
+
onHoverValue: (value2) => {
|
|
3939
|
+
const index = navigableItems.findIndex(
|
|
3940
|
+
(item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
|
|
3941
|
+
);
|
|
3942
|
+
if (index >= 0) setFocusedIndex(index);
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
) : /* @__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." })
|
|
3946
|
+
]
|
|
3947
|
+
}
|
|
3948
|
+
)
|
|
3697
3949
|
}
|
|
3698
3950
|
)
|
|
3699
3951
|
]
|
|
3700
3952
|
}
|
|
3701
3953
|
);
|
|
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
3954
|
});
|
|
3728
3955
|
SSelect.displayName = "SSelect";
|
|
3729
3956
|
function ItemList({
|
|
3730
3957
|
items,
|
|
3731
3958
|
multi,
|
|
3732
3959
|
selected,
|
|
3733
|
-
|
|
3960
|
+
focusedValue,
|
|
3961
|
+
showSelectAll,
|
|
3962
|
+
selectAllLabel,
|
|
3963
|
+
selectAllSelected,
|
|
3964
|
+
onSelectAll,
|
|
3965
|
+
onSelect,
|
|
3966
|
+
onHoverValue
|
|
3734
3967
|
}) {
|
|
3735
3968
|
const itemBase = cn(
|
|
3736
|
-
|
|
3737
|
-
"group flex cursor-pointer select-none items-center gap-[8px] text-[12px] leading-[20px] outline-none",
|
|
3969
|
+
"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
3970
|
"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,
|
|
3971
|
+
"disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
|
|
3972
|
+
);
|
|
3973
|
+
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)]";
|
|
3974
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
3975
|
+
showSelectAll && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3976
|
+
"button",
|
|
3767
3977
|
{
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3978
|
+
type: "button",
|
|
3979
|
+
"data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
|
|
3980
|
+
"data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
|
|
3771
3981
|
className: cn(
|
|
3772
3982
|
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,
|
|
3983
|
+
focusedValue === "__select_all__" && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
|
|
3984
|
+
),
|
|
3985
|
+
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: 12 },
|
|
3986
|
+
onMouseEnter: () => onHoverValue("__select_all__"),
|
|
3987
|
+
onClick: onSelectAll,
|
|
3781
3988
|
children: [
|
|
3782
|
-
multi && // hover(active) 시 checkbox inverse — 파란 hover 배경 위에서 checked/unchecked 모두 흰 테두리 + 투명 배경
|
|
3783
3989
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3784
3990
|
"span",
|
|
3785
3991
|
{
|
|
3786
3992
|
className: cn(
|
|
3787
3993
|
"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
|
-
)
|
|
3994
|
+
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
3995
|
),
|
|
3796
|
-
children:
|
|
3996
|
+
children: selectAllSelected && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3797
3997
|
SIcon,
|
|
3798
3998
|
{
|
|
3799
3999
|
name: "checkboxCheck",
|
|
3800
4000
|
size: 12,
|
|
3801
|
-
color:
|
|
4001
|
+
color: "var(--cmp-checkbox-checked-icon-default)"
|
|
3802
4002
|
}
|
|
3803
4003
|
)
|
|
3804
4004
|
}
|
|
3805
4005
|
),
|
|
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 })
|
|
4006
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
3813
4007
|
]
|
|
3814
|
-
}
|
|
3815
|
-
|
|
3816
|
-
)
|
|
3817
|
-
|
|
4008
|
+
}
|
|
4009
|
+
),
|
|
4010
|
+
items.map((o) => {
|
|
4011
|
+
const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
|
|
4012
|
+
if (o.isGroup) {
|
|
4013
|
+
const isDepth1 = o.depth === 1;
|
|
4014
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4015
|
+
"div",
|
|
4016
|
+
{
|
|
4017
|
+
className: cn(
|
|
4018
|
+
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
4019
|
+
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",
|
|
4020
|
+
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
4021
|
+
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
4022
|
+
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
|
|
4023
|
+
o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
|
|
4024
|
+
),
|
|
4025
|
+
style: { paddingLeft: padLeft },
|
|
4026
|
+
children: o.label
|
|
4027
|
+
},
|
|
4028
|
+
o.value
|
|
4029
|
+
);
|
|
4030
|
+
}
|
|
4031
|
+
const isSelected = selected.includes(o.value);
|
|
4032
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4033
|
+
"button",
|
|
4034
|
+
{
|
|
4035
|
+
type: "button",
|
|
4036
|
+
disabled: o.disabled,
|
|
4037
|
+
role: "option",
|
|
4038
|
+
"aria-selected": isSelected,
|
|
4039
|
+
"data-select-focused": focusedValue === o.value ? "true" : void 0,
|
|
4040
|
+
"data-select-active": focusedValue === o.value ? "true" : void 0,
|
|
4041
|
+
className: cn(
|
|
4042
|
+
itemBase,
|
|
4043
|
+
isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
|
|
4044
|
+
focusedValue === o.value && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]",
|
|
4045
|
+
activeClass
|
|
4046
|
+
),
|
|
4047
|
+
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
|
|
4048
|
+
onMouseEnter: () => onHoverValue(o.value),
|
|
4049
|
+
onClick: () => onSelect(o.value),
|
|
4050
|
+
children: [
|
|
4051
|
+
multi && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4052
|
+
"span",
|
|
4053
|
+
{
|
|
4054
|
+
className: cn(
|
|
4055
|
+
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
4056
|
+
o.disabled ? (
|
|
4057
|
+
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
4058
|
+
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
4059
|
+
) : cn(
|
|
4060
|
+
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)]"
|
|
4061
|
+
)
|
|
4062
|
+
),
|
|
4063
|
+
children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4064
|
+
SIcon,
|
|
4065
|
+
{
|
|
4066
|
+
name: "checkboxCheck",
|
|
4067
|
+
size: 12,
|
|
4068
|
+
color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
|
|
4069
|
+
}
|
|
4070
|
+
)
|
|
4071
|
+
}
|
|
4072
|
+
),
|
|
4073
|
+
isHtmlLabel(o.label) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4074
|
+
"span",
|
|
4075
|
+
{
|
|
4076
|
+
className: "flex-1 truncate",
|
|
4077
|
+
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
4078
|
+
}
|
|
4079
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
4080
|
+
]
|
|
4081
|
+
},
|
|
4082
|
+
o.value
|
|
4083
|
+
);
|
|
4084
|
+
})
|
|
4085
|
+
] });
|
|
3818
4086
|
}
|
|
3819
4087
|
|
|
3820
4088
|
exports.SSelect = SSelect;
|