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
|
@@ -6,7 +6,6 @@ var clsx = require('clsx');
|
|
|
6
6
|
var tailwindMerge = require('tailwind-merge');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
8
|
var RCheckbox = require('@radix-ui/react-checkbox');
|
|
9
|
-
var Ariakit = require('@ariakit/react');
|
|
10
9
|
var RPopover2 = require('@radix-ui/react-popover');
|
|
11
10
|
|
|
12
11
|
function _interopNamespace(e) {
|
|
@@ -28,7 +27,6 @@ function _interopNamespace(e) {
|
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
var RCheckbox__namespace = /*#__PURE__*/_interopNamespace(RCheckbox);
|
|
31
|
-
var Ariakit__namespace = /*#__PURE__*/_interopNamespace(Ariakit);
|
|
32
30
|
var RPopover2__namespace = /*#__PURE__*/_interopNamespace(RPopover2);
|
|
33
31
|
|
|
34
32
|
var twMerge = tailwindMerge.extendTailwindMerge({
|
|
@@ -3135,10 +3133,110 @@ function SPagination({
|
|
|
3135
3133
|
);
|
|
3136
3134
|
}
|
|
3137
3135
|
var PortalContainerContext = react.createContext(null);
|
|
3136
|
+
var InsideModalContext = react.createContext(false);
|
|
3138
3137
|
PortalContainerContext.Provider;
|
|
3138
|
+
InsideModalContext.Provider;
|
|
3139
3139
|
function usePortalContainer() {
|
|
3140
3140
|
return react.useContext(PortalContainerContext) ?? void 0;
|
|
3141
3141
|
}
|
|
3142
|
+
function useInsideModal() {
|
|
3143
|
+
return react.useContext(InsideModalContext);
|
|
3144
|
+
}
|
|
3145
|
+
function runRules(value, rules) {
|
|
3146
|
+
if (!rules || rules.length === 0) return "";
|
|
3147
|
+
for (const rule of rules) {
|
|
3148
|
+
const result = rule(value ?? "");
|
|
3149
|
+
if (result !== true) return typeof result === "string" ? result : "\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC12\uC785\uB2C8\uB2E4.";
|
|
3150
|
+
}
|
|
3151
|
+
return "";
|
|
3152
|
+
}
|
|
3153
|
+
var FormContext = react.createContext(null);
|
|
3154
|
+
function useFormContext() {
|
|
3155
|
+
return react.useContext(FormContext);
|
|
3156
|
+
}
|
|
3157
|
+
function useFormField(field) {
|
|
3158
|
+
const ctx = useFormContext();
|
|
3159
|
+
const cbRef = react.useRef(field);
|
|
3160
|
+
cbRef.current = field;
|
|
3161
|
+
const name = field.name ?? "";
|
|
3162
|
+
react.useEffect(() => {
|
|
3163
|
+
if (!ctx) return;
|
|
3164
|
+
return ctx.register({
|
|
3165
|
+
name,
|
|
3166
|
+
validate: () => cbRef.current.validate(),
|
|
3167
|
+
resetValidation: () => cbRef.current.resetValidation()
|
|
3168
|
+
});
|
|
3169
|
+
}, [ctx, name]);
|
|
3170
|
+
}
|
|
3171
|
+
|
|
3172
|
+
// src/lib/sanitize-inline-html.ts
|
|
3173
|
+
var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
|
|
3174
|
+
var ALLOWED_ATTRS_BY_TAG = {
|
|
3175
|
+
A: /* @__PURE__ */ new Set(["href", "target", "rel", "class"])
|
|
3176
|
+
};
|
|
3177
|
+
var COMMON_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class"]);
|
|
3178
|
+
var SAFE_HREF_RE = /^(https?:|mailto:|tel:|\/|#|\.)/i;
|
|
3179
|
+
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
3180
|
+
"SCRIPT",
|
|
3181
|
+
"STYLE",
|
|
3182
|
+
"IFRAME",
|
|
3183
|
+
"OBJECT",
|
|
3184
|
+
"EMBED",
|
|
3185
|
+
"META",
|
|
3186
|
+
"LINK",
|
|
3187
|
+
"BASE",
|
|
3188
|
+
"NOSCRIPT",
|
|
3189
|
+
// SVG/MATH 는 foreign-content 파싱 모드로 전환되어 재직렬화 시 다른 트리로
|
|
3190
|
+
// 재파싱될 수 있다(mXSS 벡터). 인라인 라벨에 필요 없으므로 통째로 제거한다.
|
|
3191
|
+
"SVG",
|
|
3192
|
+
"MATH",
|
|
3193
|
+
"TEMPLATE"
|
|
3194
|
+
]);
|
|
3195
|
+
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3196
|
+
var sanitizeNode = (node) => {
|
|
3197
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
3198
|
+
node.remove();
|
|
3199
|
+
return;
|
|
3200
|
+
}
|
|
3201
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
3202
|
+
const element = node;
|
|
3203
|
+
const tagName = element.tagName;
|
|
3204
|
+
if (DROP_CONTENT_TAGS.has(tagName)) {
|
|
3205
|
+
element.remove();
|
|
3206
|
+
return;
|
|
3207
|
+
}
|
|
3208
|
+
Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
|
|
3209
|
+
if (ALLOWED_INLINE_TAGS.has(tagName)) {
|
|
3210
|
+
if (element.hasAttribute("className")) {
|
|
3211
|
+
const val = element.getAttribute("className");
|
|
3212
|
+
element.removeAttribute("className");
|
|
3213
|
+
if (!element.hasAttribute("class")) element.setAttribute("class", val);
|
|
3214
|
+
}
|
|
3215
|
+
const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tagName] ?? COMMON_ALLOWED_ATTRS;
|
|
3216
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
3217
|
+
if (!allowedAttrs.has(attr.name)) element.removeAttribute(attr.name);
|
|
3218
|
+
});
|
|
3219
|
+
if (tagName === "A") {
|
|
3220
|
+
const href = element.getAttribute("href");
|
|
3221
|
+
if (href !== null && href !== "" && !SAFE_HREF_RE.test(href)) element.removeAttribute("href");
|
|
3222
|
+
if (element.hasAttribute("target")) {
|
|
3223
|
+
element.setAttribute("rel", "noopener noreferrer");
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
const parent = element.parentNode;
|
|
3229
|
+
if (parent == null) return;
|
|
3230
|
+
while (element.firstChild) parent.insertBefore(element.firstChild, element);
|
|
3231
|
+
parent.removeChild(element);
|
|
3232
|
+
};
|
|
3233
|
+
var sanitizeInlineHtml = (value) => {
|
|
3234
|
+
if (typeof document === "undefined") return escapeHtml(value);
|
|
3235
|
+
const template = document.createElement("template");
|
|
3236
|
+
template.innerHTML = value;
|
|
3237
|
+
Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
|
|
3238
|
+
return template.innerHTML;
|
|
3239
|
+
};
|
|
3142
3240
|
|
|
3143
3241
|
// src/lib/floating-anim.ts
|
|
3144
3242
|
var FLOATING_SLIDE_ANIM = [
|
|
@@ -3476,32 +3574,6 @@ var STooltip = react.forwardRef(function STooltip2({
|
|
|
3476
3574
|
] });
|
|
3477
3575
|
});
|
|
3478
3576
|
STooltip.displayName = "STooltip";
|
|
3479
|
-
function runRules(value, rules) {
|
|
3480
|
-
if (!rules || rules.length === 0) return "";
|
|
3481
|
-
for (const rule of rules) {
|
|
3482
|
-
const result = rule(value ?? "");
|
|
3483
|
-
if (result !== true) return typeof result === "string" ? result : "\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC12\uC785\uB2C8\uB2E4.";
|
|
3484
|
-
}
|
|
3485
|
-
return "";
|
|
3486
|
-
}
|
|
3487
|
-
var FormContext = react.createContext(null);
|
|
3488
|
-
function useFormContext() {
|
|
3489
|
-
return react.useContext(FormContext);
|
|
3490
|
-
}
|
|
3491
|
-
function useFormField(field) {
|
|
3492
|
-
const ctx = useFormContext();
|
|
3493
|
-
const cbRef = react.useRef(field);
|
|
3494
|
-
cbRef.current = field;
|
|
3495
|
-
const name = field.name ?? "";
|
|
3496
|
-
react.useEffect(() => {
|
|
3497
|
-
if (!ctx) return;
|
|
3498
|
-
return ctx.register({
|
|
3499
|
-
name,
|
|
3500
|
-
validate: () => cbRef.current.validate(),
|
|
3501
|
-
resetValidation: () => cbRef.current.resetValidation()
|
|
3502
|
-
});
|
|
3503
|
-
}, [ctx, name]);
|
|
3504
|
-
}
|
|
3505
3577
|
var FIELD_SIZE = {
|
|
3506
3578
|
sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
|
|
3507
3579
|
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
|
|
@@ -3516,6 +3588,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3516
3588
|
name,
|
|
3517
3589
|
label = "",
|
|
3518
3590
|
icon,
|
|
3591
|
+
iconColor,
|
|
3519
3592
|
labelTooltip = "",
|
|
3520
3593
|
labelTooltipProps,
|
|
3521
3594
|
labelWidth,
|
|
@@ -3593,7 +3666,15 @@ var SField = react.forwardRef(function SField2({
|
|
|
3593
3666
|
lineHeight: `${s.lineHeight}px`
|
|
3594
3667
|
},
|
|
3595
3668
|
children: [
|
|
3596
|
-
icon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3669
|
+
icon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3670
|
+
SIcon,
|
|
3671
|
+
{
|
|
3672
|
+
name: icon,
|
|
3673
|
+
size: s.icon,
|
|
3674
|
+
color: iconColor,
|
|
3675
|
+
style: { marginRight: s.labelGap }
|
|
3676
|
+
}
|
|
3677
|
+
),
|
|
3597
3678
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
|
|
3598
3679
|
(labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3599
3680
|
STooltip,
|
|
@@ -3643,7 +3724,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3643
3724
|
borderBottomLeftRadius: s.radius - 1
|
|
3644
3725
|
},
|
|
3645
3726
|
children: [
|
|
3646
|
-
icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon }),
|
|
3727
|
+
icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: iconColor }),
|
|
3647
3728
|
effectiveAddonLabel
|
|
3648
3729
|
]
|
|
3649
3730
|
}
|
|
@@ -3658,6 +3739,77 @@ var SField = react.forwardRef(function SField2({
|
|
|
3658
3739
|
}
|
|
3659
3740
|
);
|
|
3660
3741
|
});
|
|
3742
|
+
function SPortal({
|
|
3743
|
+
open,
|
|
3744
|
+
onClose,
|
|
3745
|
+
onOpenChange,
|
|
3746
|
+
anchorRef,
|
|
3747
|
+
placement = "bottom",
|
|
3748
|
+
align = "start",
|
|
3749
|
+
offset = 4,
|
|
3750
|
+
alignOffset = 0,
|
|
3751
|
+
fit = false,
|
|
3752
|
+
maxHeight,
|
|
3753
|
+
autoClose = false,
|
|
3754
|
+
persistent = false,
|
|
3755
|
+
viewportPadding = 0,
|
|
3756
|
+
onOpenAutoFocus,
|
|
3757
|
+
onCloseAutoFocus,
|
|
3758
|
+
children,
|
|
3759
|
+
className,
|
|
3760
|
+
style,
|
|
3761
|
+
ariaLabel
|
|
3762
|
+
}) {
|
|
3763
|
+
const portalContainer = usePortalContainer();
|
|
3764
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3765
|
+
RPopover2__namespace.Root,
|
|
3766
|
+
{
|
|
3767
|
+
open,
|
|
3768
|
+
onOpenChange: (next) => {
|
|
3769
|
+
onOpenChange?.(next);
|
|
3770
|
+
if (!next) onClose?.();
|
|
3771
|
+
},
|
|
3772
|
+
children: [
|
|
3773
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { virtualRef: anchorRef }),
|
|
3774
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3775
|
+
RPopover2__namespace.Content,
|
|
3776
|
+
{
|
|
3777
|
+
side: placement,
|
|
3778
|
+
align,
|
|
3779
|
+
sideOffset: offset,
|
|
3780
|
+
alignOffset,
|
|
3781
|
+
collisionPadding: viewportPadding,
|
|
3782
|
+
"aria-label": ariaLabel,
|
|
3783
|
+
onPointerDownOutside: (e) => {
|
|
3784
|
+
if (persistent || anchorRef.current?.contains(e.target)) {
|
|
3785
|
+
e.preventDefault();
|
|
3786
|
+
}
|
|
3787
|
+
},
|
|
3788
|
+
onFocusOutside: (e) => {
|
|
3789
|
+
if (persistent || anchorRef.current?.contains(e.target)) {
|
|
3790
|
+
e.preventDefault();
|
|
3791
|
+
}
|
|
3792
|
+
},
|
|
3793
|
+
onEscapeKeyDown: persistent ? (e) => e.preventDefault() : void 0,
|
|
3794
|
+
onOpenAutoFocus,
|
|
3795
|
+
onCloseAutoFocus,
|
|
3796
|
+
onClick: autoClose ? () => onClose?.() : void 0,
|
|
3797
|
+
className: cn(
|
|
3798
|
+
"z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm",
|
|
3799
|
+
maxHeight != null && "overflow-y-auto",
|
|
3800
|
+
fit && "min-w-(--radix-popover-trigger-width)",
|
|
3801
|
+
FLOATING_SLIDE_ANIM,
|
|
3802
|
+
className
|
|
3803
|
+
),
|
|
3804
|
+
style: { maxHeight, ...style },
|
|
3805
|
+
children
|
|
3806
|
+
}
|
|
3807
|
+
) })
|
|
3808
|
+
]
|
|
3809
|
+
}
|
|
3810
|
+
);
|
|
3811
|
+
}
|
|
3812
|
+
SPortal.displayName = "SPortal";
|
|
3661
3813
|
|
|
3662
3814
|
// src/components/SSelect/select.config.ts
|
|
3663
3815
|
function flattenOptions(options, depth = 1) {
|
|
@@ -3692,75 +3844,6 @@ function flattenOptions(options, depth = 1) {
|
|
|
3692
3844
|
var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
|
|
3693
3845
|
var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
|
|
3694
3846
|
var extractText = (html) => html.replace(/<[^>]*>/g, "");
|
|
3695
|
-
|
|
3696
|
-
// src/lib/sanitize-inline-html.ts
|
|
3697
|
-
var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
|
|
3698
|
-
var ALLOWED_ATTRS_BY_TAG = {
|
|
3699
|
-
A: /* @__PURE__ */ new Set(["href", "target", "rel", "class"])
|
|
3700
|
-
};
|
|
3701
|
-
var COMMON_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class"]);
|
|
3702
|
-
var SAFE_HREF_RE = /^(https?:|mailto:|tel:|\/|#|\.)/i;
|
|
3703
|
-
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
3704
|
-
"SCRIPT",
|
|
3705
|
-
"STYLE",
|
|
3706
|
-
"IFRAME",
|
|
3707
|
-
"OBJECT",
|
|
3708
|
-
"EMBED",
|
|
3709
|
-
"META",
|
|
3710
|
-
"LINK",
|
|
3711
|
-
"BASE",
|
|
3712
|
-
"NOSCRIPT",
|
|
3713
|
-
// SVG/MATH 는 foreign-content 파싱 모드로 전환되어 재직렬화 시 다른 트리로
|
|
3714
|
-
// 재파싱될 수 있다(mXSS 벡터). 인라인 라벨에 필요 없으므로 통째로 제거한다.
|
|
3715
|
-
"SVG",
|
|
3716
|
-
"MATH",
|
|
3717
|
-
"TEMPLATE"
|
|
3718
|
-
]);
|
|
3719
|
-
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3720
|
-
var sanitizeNode = (node) => {
|
|
3721
|
-
if (node.nodeType === Node.COMMENT_NODE) {
|
|
3722
|
-
node.remove();
|
|
3723
|
-
return;
|
|
3724
|
-
}
|
|
3725
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
3726
|
-
const element = node;
|
|
3727
|
-
const tagName = element.tagName;
|
|
3728
|
-
if (DROP_CONTENT_TAGS.has(tagName)) {
|
|
3729
|
-
element.remove();
|
|
3730
|
-
return;
|
|
3731
|
-
}
|
|
3732
|
-
Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
|
|
3733
|
-
if (ALLOWED_INLINE_TAGS.has(tagName)) {
|
|
3734
|
-
if (element.hasAttribute("className")) {
|
|
3735
|
-
const val = element.getAttribute("className");
|
|
3736
|
-
element.removeAttribute("className");
|
|
3737
|
-
if (!element.hasAttribute("class")) element.setAttribute("class", val);
|
|
3738
|
-
}
|
|
3739
|
-
const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tagName] ?? COMMON_ALLOWED_ATTRS;
|
|
3740
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
3741
|
-
if (!allowedAttrs.has(attr.name)) element.removeAttribute(attr.name);
|
|
3742
|
-
});
|
|
3743
|
-
if (tagName === "A") {
|
|
3744
|
-
const href = element.getAttribute("href");
|
|
3745
|
-
if (href !== null && href !== "" && !SAFE_HREF_RE.test(href)) element.removeAttribute("href");
|
|
3746
|
-
if (element.hasAttribute("target")) {
|
|
3747
|
-
element.setAttribute("rel", "noopener noreferrer");
|
|
3748
|
-
}
|
|
3749
|
-
}
|
|
3750
|
-
return;
|
|
3751
|
-
}
|
|
3752
|
-
const parent = element.parentNode;
|
|
3753
|
-
if (parent == null) return;
|
|
3754
|
-
while (element.firstChild) parent.insertBefore(element.firstChild, element);
|
|
3755
|
-
parent.removeChild(element);
|
|
3756
|
-
};
|
|
3757
|
-
var sanitizeInlineHtml = (value) => {
|
|
3758
|
-
if (typeof document === "undefined") return escapeHtml(value);
|
|
3759
|
-
const template = document.createElement("template");
|
|
3760
|
-
template.innerHTML = value;
|
|
3761
|
-
Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
|
|
3762
|
-
return template.innerHTML;
|
|
3763
|
-
};
|
|
3764
3847
|
var SEARCH_THRESHOLD = 11;
|
|
3765
3848
|
var SSelect = react.forwardRef(function SSelect2({
|
|
3766
3849
|
value,
|
|
@@ -3784,6 +3867,7 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3784
3867
|
label,
|
|
3785
3868
|
labelWidth,
|
|
3786
3869
|
icon,
|
|
3870
|
+
iconColor,
|
|
3787
3871
|
labelTooltip,
|
|
3788
3872
|
addonLabel,
|
|
3789
3873
|
addonAlign,
|
|
@@ -3795,18 +3879,15 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3795
3879
|
}, ref) {
|
|
3796
3880
|
const multi = type === "multi" || type === "multi_depth";
|
|
3797
3881
|
const portalContainer = usePortalContainer();
|
|
3882
|
+
const insideModal = useInsideModal();
|
|
3798
3883
|
const [search, setSearch] = react.useState("");
|
|
3799
3884
|
const [ruleError, setRuleError] = react.useState("");
|
|
3800
3885
|
const [open, setOpen] = react.useState(false);
|
|
3886
|
+
const [pendingOpen, setPendingOpen] = react.useState(false);
|
|
3887
|
+
const [focusedIndex, setFocusedIndex] = react.useState(-1);
|
|
3801
3888
|
const triggerRef = react.useRef(null);
|
|
3802
|
-
react.
|
|
3803
|
-
|
|
3804
|
-
() => ({
|
|
3805
|
-
focus: () => triggerRef.current?.focus(),
|
|
3806
|
-
open: () => setOpen(true)
|
|
3807
|
-
}),
|
|
3808
|
-
[]
|
|
3809
|
-
);
|
|
3889
|
+
const searchInputRef = react.useRef(null);
|
|
3890
|
+
const listRef = react.useRef(null);
|
|
3810
3891
|
const flat = react.useMemo(() => flattenOptions(options), [options]);
|
|
3811
3892
|
const selectable = react.useMemo(() => selectableOptions(flat), [flat]);
|
|
3812
3893
|
const hasLeaf = react.useMemo(() => flat.some((o) => !o.isGroup), [flat]);
|
|
@@ -3824,6 +3905,46 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3824
3905
|
const arr = Array.isArray(value) ? value : [value];
|
|
3825
3906
|
return arr.map((v) => v != null && typeof v === "object" ? v.value : v).filter((v) => v != null && v !== "").map(String);
|
|
3826
3907
|
}, [value]);
|
|
3908
|
+
const selectedRef = react.useRef(selected);
|
|
3909
|
+
selectedRef.current = selected;
|
|
3910
|
+
const emit = (next) => {
|
|
3911
|
+
if (!onValueChange) return;
|
|
3912
|
+
const mapped = next.map(
|
|
3913
|
+
(v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
|
|
3914
|
+
);
|
|
3915
|
+
onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
|
|
3916
|
+
};
|
|
3917
|
+
const commitOpen = (next) => {
|
|
3918
|
+
setOpen(next);
|
|
3919
|
+
onOpenChange?.(next);
|
|
3920
|
+
if (!next) {
|
|
3921
|
+
setPendingOpen(false);
|
|
3922
|
+
setSearch("");
|
|
3923
|
+
if (rules && rules.length > 0) {
|
|
3924
|
+
setRuleError(runRules(multi ? selectedRef.current : selectedRef.current[0] ?? "", rules));
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
};
|
|
3928
|
+
const requestOpen = (next) => {
|
|
3929
|
+
if (next && insideModal && portalContainer == null) {
|
|
3930
|
+
setPendingOpen(true);
|
|
3931
|
+
return;
|
|
3932
|
+
}
|
|
3933
|
+
commitOpen(next);
|
|
3934
|
+
};
|
|
3935
|
+
react.useEffect(() => {
|
|
3936
|
+
if (!insideModal || portalContainer == null || !pendingOpen) return;
|
|
3937
|
+
setPendingOpen(false);
|
|
3938
|
+
commitOpen(true);
|
|
3939
|
+
}, [insideModal, pendingOpen, portalContainer]);
|
|
3940
|
+
react.useImperativeHandle(
|
|
3941
|
+
ref,
|
|
3942
|
+
() => ({
|
|
3943
|
+
focus: () => triggerRef.current?.focus(),
|
|
3944
|
+
open: () => requestOpen(true)
|
|
3945
|
+
}),
|
|
3946
|
+
[insideModal, portalContainer]
|
|
3947
|
+
);
|
|
3827
3948
|
useFormField({
|
|
3828
3949
|
name,
|
|
3829
3950
|
validate: () => {
|
|
@@ -3833,13 +3954,6 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3833
3954
|
},
|
|
3834
3955
|
resetValidation: () => setRuleError("")
|
|
3835
3956
|
});
|
|
3836
|
-
const emit = (next) => {
|
|
3837
|
-
if (!onValueChange) return;
|
|
3838
|
-
const mapped = next.map(
|
|
3839
|
-
(v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
|
|
3840
|
-
);
|
|
3841
|
-
onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
|
|
3842
|
-
};
|
|
3843
3957
|
const filtered = react.useMemo(() => {
|
|
3844
3958
|
if (!showSearch || search.trim() === "") return flat;
|
|
3845
3959
|
const q = search.trim().toLowerCase();
|
|
@@ -3850,6 +3964,16 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3850
3964
|
const allValues = selectable.map((o) => o.value);
|
|
3851
3965
|
const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
|
|
3852
3966
|
const hasItems = filtered.some((o) => !o.isGroup);
|
|
3967
|
+
const navigableItems = react.useMemo(() => {
|
|
3968
|
+
const items = [];
|
|
3969
|
+
if (multi && useSelectAll && allValues.length > 0) {
|
|
3970
|
+
items.push({ value: "__select_all__", isSelectAll: true });
|
|
3971
|
+
}
|
|
3972
|
+
filtered.forEach((o) => {
|
|
3973
|
+
if (!o.isGroup && !o.disabled) items.push({ value: o.value });
|
|
3974
|
+
});
|
|
3975
|
+
return items;
|
|
3976
|
+
}, [allValues.length, filtered, multi, useSelectAll]);
|
|
3853
3977
|
const triggerText = (() => {
|
|
3854
3978
|
if (selected.length === 0) return placeholder;
|
|
3855
3979
|
if (multi) {
|
|
@@ -3859,26 +3983,119 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3859
3983
|
return labelByValue.get(selected[0]) ?? selected[0];
|
|
3860
3984
|
})();
|
|
3861
3985
|
const isPlaceholder = selected.length === 0;
|
|
3862
|
-
const
|
|
3863
|
-
|
|
3986
|
+
const handleSelect = (nextValue) => {
|
|
3987
|
+
if (multi) {
|
|
3988
|
+
const next = selected.includes(nextValue) ? selected.filter((v) => v !== nextValue) : [...selected, nextValue];
|
|
3989
|
+
emit(next);
|
|
3990
|
+
return;
|
|
3991
|
+
}
|
|
3992
|
+
emit([nextValue]);
|
|
3993
|
+
commitOpen(false);
|
|
3994
|
+
};
|
|
3995
|
+
const handleSelectAll = () => {
|
|
3996
|
+
emit(allSelected ? [] : allValues);
|
|
3997
|
+
};
|
|
3998
|
+
react.useEffect(() => {
|
|
3999
|
+
if (!open) {
|
|
4000
|
+
setFocusedIndex(-1);
|
|
4001
|
+
return;
|
|
4002
|
+
}
|
|
4003
|
+
setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
|
|
4004
|
+
}, [open, navigableItems.length]);
|
|
4005
|
+
react.useEffect(() => {
|
|
4006
|
+
if (!open) return;
|
|
4007
|
+
setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
|
|
4008
|
+
}, [search, open, navigableItems.length]);
|
|
4009
|
+
react.useEffect(() => {
|
|
4010
|
+
if (!open || focusedIndex < 0) return;
|
|
4011
|
+
const list = listRef.current;
|
|
4012
|
+
const focusedEl = list?.querySelector('[data-select-focused="true"]');
|
|
4013
|
+
if (!list || !focusedEl) return;
|
|
4014
|
+
const listRect = list.getBoundingClientRect();
|
|
4015
|
+
const itemRect = focusedEl.getBoundingClientRect();
|
|
4016
|
+
if (itemRect.top < listRect.top) {
|
|
4017
|
+
list.scrollTop += itemRect.top - listRect.top;
|
|
4018
|
+
} else if (itemRect.bottom > listRect.bottom) {
|
|
4019
|
+
list.scrollTop += itemRect.bottom - listRect.bottom;
|
|
4020
|
+
}
|
|
4021
|
+
}, [focusedIndex, open]);
|
|
4022
|
+
const moveFocusedIndex = (direction) => {
|
|
4023
|
+
if (!open || navigableItems.length === 0) return;
|
|
4024
|
+
setFocusedIndex((current) => {
|
|
4025
|
+
if (direction === "next") {
|
|
4026
|
+
return current < 0 ? 0 : (current + 1) % navigableItems.length;
|
|
4027
|
+
}
|
|
4028
|
+
return current <= 0 ? navigableItems.length - 1 : current - 1;
|
|
4029
|
+
});
|
|
4030
|
+
};
|
|
4031
|
+
const selectFocusedItem = () => {
|
|
4032
|
+
if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
|
|
4033
|
+
const focused = navigableItems[focusedIndex];
|
|
4034
|
+
if (focused.isSelectAll) {
|
|
4035
|
+
handleSelectAll();
|
|
4036
|
+
return;
|
|
4037
|
+
}
|
|
4038
|
+
handleSelect(focused.value);
|
|
4039
|
+
};
|
|
4040
|
+
const handleKeyDown = (event) => {
|
|
4041
|
+
if (!open) {
|
|
4042
|
+
if ((event.key === "ArrowDown" || event.key === "ArrowUp") && !effectiveDisabled) {
|
|
4043
|
+
event.preventDefault();
|
|
4044
|
+
requestOpen(true);
|
|
4045
|
+
}
|
|
4046
|
+
return;
|
|
4047
|
+
}
|
|
4048
|
+
if (event.key === "ArrowDown") {
|
|
4049
|
+
event.preventDefault();
|
|
4050
|
+
moveFocusedIndex("next");
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
if (event.key === "ArrowUp") {
|
|
4054
|
+
event.preventDefault();
|
|
4055
|
+
moveFocusedIndex("prev");
|
|
4056
|
+
return;
|
|
4057
|
+
}
|
|
4058
|
+
if (event.key === "Enter") {
|
|
4059
|
+
event.preventDefault();
|
|
4060
|
+
selectFocusedItem();
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
if (event.key === "Escape") {
|
|
4064
|
+
event.preventDefault();
|
|
4065
|
+
commitOpen(false);
|
|
4066
|
+
}
|
|
4067
|
+
};
|
|
4068
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4069
|
+
SField,
|
|
3864
4070
|
{
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
4071
|
+
label,
|
|
4072
|
+
labelWidth,
|
|
4073
|
+
icon,
|
|
4074
|
+
iconColor,
|
|
4075
|
+
labelTooltip,
|
|
4076
|
+
labelTooltipProps,
|
|
4077
|
+
addonLabel,
|
|
4078
|
+
addonAlign,
|
|
4079
|
+
hint,
|
|
4080
|
+
error: error || ruleError !== "",
|
|
4081
|
+
status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
|
|
4082
|
+
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
4083
|
+
disabled: effectiveDisabled,
|
|
4084
|
+
width,
|
|
4085
|
+
className,
|
|
4086
|
+
style,
|
|
3876
4087
|
children: [
|
|
4088
|
+
name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selected.join(",") }),
|
|
3877
4089
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3878
|
-
|
|
4090
|
+
"button",
|
|
3879
4091
|
{
|
|
3880
4092
|
ref: triggerRef,
|
|
4093
|
+
type: "button",
|
|
3881
4094
|
disabled: effectiveDisabled,
|
|
4095
|
+
"aria-haspopup": "listbox",
|
|
4096
|
+
"aria-expanded": open,
|
|
4097
|
+
onClick: () => requestOpen(!open),
|
|
4098
|
+
onKeyDown: handleKeyDown,
|
|
3882
4099
|
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",
|
|
3883
4100
|
style: {
|
|
3884
4101
|
color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
|
|
@@ -3903,178 +4120,229 @@ var SSelect = react.forwardRef(function SSelect2({
|
|
|
3903
4120
|
]
|
|
3904
4121
|
}
|
|
3905
4122
|
),
|
|
3906
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3907
|
-
|
|
4123
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4124
|
+
SPortal,
|
|
3908
4125
|
{
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
4126
|
+
open,
|
|
4127
|
+
onClose: () => commitOpen(false),
|
|
4128
|
+
onOpenChange: (next) => {
|
|
4129
|
+
if (next) requestOpen(true);
|
|
4130
|
+
},
|
|
4131
|
+
anchorRef: triggerRef,
|
|
4132
|
+
placement: "bottom",
|
|
4133
|
+
align: "start",
|
|
4134
|
+
offset: 4,
|
|
4135
|
+
fit: !dropdownWidth,
|
|
4136
|
+
onOpenAutoFocus: (event) => {
|
|
4137
|
+
if (!showSearch) {
|
|
4138
|
+
event.preventDefault();
|
|
4139
|
+
return;
|
|
4140
|
+
}
|
|
4141
|
+
event.preventDefault();
|
|
4142
|
+
requestAnimationFrame(() => searchInputRef.current?.focus());
|
|
4143
|
+
},
|
|
4144
|
+
onCloseAutoFocus: (event) => {
|
|
4145
|
+
event.preventDefault();
|
|
4146
|
+
triggerRef.current?.focus();
|
|
4147
|
+
},
|
|
4148
|
+
className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] py-[4px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
|
|
3914
4149
|
style: {
|
|
3915
|
-
maxHeight: dropdownHeight,
|
|
3916
4150
|
...dropdownWidth ? { width: dropdownWidth } : {},
|
|
3917
4151
|
maxWidth: maxDropdownWidth
|
|
3918
4152
|
},
|
|
3919
|
-
children:
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
4153
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4154
|
+
"div",
|
|
4155
|
+
{
|
|
4156
|
+
ref: listRef,
|
|
4157
|
+
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)]",
|
|
4158
|
+
style: { maxHeight: dropdownHeight },
|
|
4159
|
+
onMouseLeave: () => setFocusedIndex(-1),
|
|
4160
|
+
children: [
|
|
4161
|
+
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: [
|
|
4162
|
+
/* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
|
|
4163
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4164
|
+
"input",
|
|
4165
|
+
{
|
|
4166
|
+
ref: searchInputRef,
|
|
4167
|
+
value: search,
|
|
4168
|
+
onChange: (e) => setSearch(e.target.value),
|
|
4169
|
+
onKeyDown: handleKeyDown,
|
|
4170
|
+
placeholder: "\uAC80\uC0C9",
|
|
4171
|
+
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)]"
|
|
4172
|
+
}
|
|
4173
|
+
),
|
|
4174
|
+
search !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4175
|
+
SGhostButton,
|
|
4176
|
+
{
|
|
4177
|
+
icon: "close",
|
|
4178
|
+
size: "xxs",
|
|
4179
|
+
intent: "default",
|
|
4180
|
+
ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
|
|
4181
|
+
onClick: () => setSearch(""),
|
|
4182
|
+
className: "shrink-0"
|
|
4183
|
+
}
|
|
4184
|
+
)
|
|
4185
|
+
] }) }),
|
|
4186
|
+
hasItems ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4187
|
+
ItemList,
|
|
4188
|
+
{
|
|
4189
|
+
items: filtered,
|
|
4190
|
+
multi,
|
|
4191
|
+
selected,
|
|
4192
|
+
focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
|
|
4193
|
+
showSelectAll: multi && useSelectAll && allValues.length > 0,
|
|
4194
|
+
selectAllLabel: allSelectedLabel,
|
|
4195
|
+
selectAllSelected: allSelected,
|
|
4196
|
+
onSelectAll: handleSelectAll,
|
|
4197
|
+
onSelect: handleSelect,
|
|
4198
|
+
onHoverValue: (value2) => {
|
|
4199
|
+
const index = navigableItems.findIndex(
|
|
4200
|
+
(item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
|
|
4201
|
+
);
|
|
4202
|
+
if (index >= 0) setFocusedIndex(index);
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
) : /* @__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." })
|
|
4206
|
+
]
|
|
4207
|
+
}
|
|
4208
|
+
)
|
|
3957
4209
|
}
|
|
3958
4210
|
)
|
|
3959
4211
|
]
|
|
3960
4212
|
}
|
|
3961
4213
|
);
|
|
3962
|
-
const control = showSearch ? /* @__PURE__ */ jsxRuntime.jsx(Ariakit__namespace.ComboboxProvider, { value: search, setValue: setSearch, resetValueOnHide: true, children: selectStore }) : selectStore;
|
|
3963
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3964
|
-
SField,
|
|
3965
|
-
{
|
|
3966
|
-
label,
|
|
3967
|
-
labelWidth,
|
|
3968
|
-
icon,
|
|
3969
|
-
labelTooltip,
|
|
3970
|
-
labelTooltipProps,
|
|
3971
|
-
addonLabel,
|
|
3972
|
-
addonAlign,
|
|
3973
|
-
hint,
|
|
3974
|
-
error: error || ruleError !== "",
|
|
3975
|
-
status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
|
|
3976
|
-
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
3977
|
-
disabled: effectiveDisabled,
|
|
3978
|
-
width,
|
|
3979
|
-
className,
|
|
3980
|
-
style,
|
|
3981
|
-
children: [
|
|
3982
|
-
name && /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name, value: selected.join(",") }),
|
|
3983
|
-
control
|
|
3984
|
-
]
|
|
3985
|
-
}
|
|
3986
|
-
);
|
|
3987
4214
|
});
|
|
3988
4215
|
SSelect.displayName = "SSelect";
|
|
3989
4216
|
function ItemList({
|
|
3990
4217
|
items,
|
|
3991
4218
|
multi,
|
|
3992
4219
|
selected,
|
|
3993
|
-
|
|
4220
|
+
focusedValue,
|
|
4221
|
+
showSelectAll,
|
|
4222
|
+
selectAllLabel,
|
|
4223
|
+
selectAllSelected,
|
|
4224
|
+
onSelectAll,
|
|
4225
|
+
onSelect,
|
|
4226
|
+
onHoverValue
|
|
3994
4227
|
}) {
|
|
3995
4228
|
const itemBase = cn(
|
|
3996
|
-
|
|
3997
|
-
"group flex cursor-pointer select-none items-center gap-[8px] text-[12px] leading-[20px] outline-none",
|
|
4229
|
+
"group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
|
|
3998
4230
|
"text-[color:var(--cmp-listItem-content-default)]",
|
|
3999
|
-
|
|
4000
|
-
"aria-disabled:cursor-not-allowed aria-disabled:text-[color:var(--cmp-listItem-content-disabled)]"
|
|
4231
|
+
"disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
|
|
4001
4232
|
);
|
|
4002
|
-
const activeClass = "
|
|
4003
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
const isDepth1 = o.depth === 1;
|
|
4007
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4008
|
-
"div",
|
|
4009
|
-
{
|
|
4010
|
-
className: cn(
|
|
4011
|
-
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
4012
|
-
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",
|
|
4013
|
-
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
4014
|
-
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
4015
|
-
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
|
|
4016
|
-
o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
|
|
4017
|
-
),
|
|
4018
|
-
style: { paddingLeft: padLeft },
|
|
4019
|
-
children: o.label
|
|
4020
|
-
},
|
|
4021
|
-
o.value
|
|
4022
|
-
);
|
|
4023
|
-
}
|
|
4024
|
-
const isSelected = selected.includes(o.value);
|
|
4025
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4026
|
-
Ariakit__namespace.SelectItem,
|
|
4233
|
+
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)]";
|
|
4234
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
4235
|
+
showSelectAll && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4236
|
+
"button",
|
|
4027
4237
|
{
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4238
|
+
type: "button",
|
|
4239
|
+
"data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
|
|
4240
|
+
"data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
|
|
4031
4241
|
className: cn(
|
|
4032
4242
|
itemBase,
|
|
4033
|
-
|
|
4034
|
-
// Ariakit은 disabled 항목도 hover 시 active(data-active-item) 처리하므로,
|
|
4035
|
-
// 비활성 항목에는 hover 하이라이트를 적용하지 않아 회색 비활성 스타일을 유지한다.
|
|
4036
|
-
// (원본 Stencil의 `--focused...:not(--disabled)` 규칙과 동일한 의도)
|
|
4037
|
-
!o.disabled && activeClass
|
|
4243
|
+
focusedValue === "__select_all__" && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
|
|
4038
4244
|
),
|
|
4039
|
-
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft:
|
|
4040
|
-
|
|
4245
|
+
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: 12 },
|
|
4246
|
+
onMouseEnter: () => onHoverValue("__select_all__"),
|
|
4247
|
+
onClick: onSelectAll,
|
|
4041
4248
|
children: [
|
|
4042
|
-
multi && // hover(active) 시 checkbox inverse — 파란 hover 배경 위에서 checked/unchecked 모두 흰 테두리 + 투명 배경
|
|
4043
4249
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4044
4250
|
"span",
|
|
4045
4251
|
{
|
|
4046
4252
|
className: cn(
|
|
4047
4253
|
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
4048
|
-
|
|
4049
|
-
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
4050
|
-
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
4051
|
-
) : cn(
|
|
4052
|
-
"group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
|
|
4053
|
-
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"
|
|
4054
|
-
)
|
|
4254
|
+
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)]"
|
|
4055
4255
|
),
|
|
4056
|
-
children:
|
|
4256
|
+
children: selectAllSelected && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4057
4257
|
SIcon,
|
|
4058
4258
|
{
|
|
4059
4259
|
name: "checkboxCheck",
|
|
4060
4260
|
size: 12,
|
|
4061
|
-
color:
|
|
4261
|
+
color: "var(--cmp-checkbox-checked-icon-default)"
|
|
4062
4262
|
}
|
|
4063
4263
|
)
|
|
4064
4264
|
}
|
|
4065
4265
|
),
|
|
4066
|
-
|
|
4067
|
-
"span",
|
|
4068
|
-
{
|
|
4069
|
-
className: "flex-1 truncate",
|
|
4070
|
-
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
4071
|
-
}
|
|
4072
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
4266
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
4073
4267
|
]
|
|
4074
|
-
}
|
|
4075
|
-
|
|
4076
|
-
)
|
|
4077
|
-
|
|
4268
|
+
}
|
|
4269
|
+
),
|
|
4270
|
+
items.map((o) => {
|
|
4271
|
+
const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
|
|
4272
|
+
if (o.isGroup) {
|
|
4273
|
+
const isDepth1 = o.depth === 1;
|
|
4274
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4275
|
+
"div",
|
|
4276
|
+
{
|
|
4277
|
+
className: cn(
|
|
4278
|
+
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
4279
|
+
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",
|
|
4280
|
+
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
4281
|
+
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
4282
|
+
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
|
|
4283
|
+
o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
|
|
4284
|
+
),
|
|
4285
|
+
style: { paddingLeft: padLeft },
|
|
4286
|
+
children: o.label
|
|
4287
|
+
},
|
|
4288
|
+
o.value
|
|
4289
|
+
);
|
|
4290
|
+
}
|
|
4291
|
+
const isSelected = selected.includes(o.value);
|
|
4292
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4293
|
+
"button",
|
|
4294
|
+
{
|
|
4295
|
+
type: "button",
|
|
4296
|
+
disabled: o.disabled,
|
|
4297
|
+
role: "option",
|
|
4298
|
+
"aria-selected": isSelected,
|
|
4299
|
+
"data-select-focused": focusedValue === o.value ? "true" : void 0,
|
|
4300
|
+
"data-select-active": focusedValue === o.value ? "true" : void 0,
|
|
4301
|
+
className: cn(
|
|
4302
|
+
itemBase,
|
|
4303
|
+
isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
|
|
4304
|
+
focusedValue === o.value && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]",
|
|
4305
|
+
activeClass
|
|
4306
|
+
),
|
|
4307
|
+
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
|
|
4308
|
+
onMouseEnter: () => onHoverValue(o.value),
|
|
4309
|
+
onClick: () => onSelect(o.value),
|
|
4310
|
+
children: [
|
|
4311
|
+
multi && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4312
|
+
"span",
|
|
4313
|
+
{
|
|
4314
|
+
className: cn(
|
|
4315
|
+
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
4316
|
+
o.disabled ? (
|
|
4317
|
+
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
4318
|
+
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
4319
|
+
) : cn(
|
|
4320
|
+
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)]"
|
|
4321
|
+
)
|
|
4322
|
+
),
|
|
4323
|
+
children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4324
|
+
SIcon,
|
|
4325
|
+
{
|
|
4326
|
+
name: "checkboxCheck",
|
|
4327
|
+
size: 12,
|
|
4328
|
+
color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
|
|
4329
|
+
}
|
|
4330
|
+
)
|
|
4331
|
+
}
|
|
4332
|
+
),
|
|
4333
|
+
isHtmlLabel(o.label) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4334
|
+
"span",
|
|
4335
|
+
{
|
|
4336
|
+
className: "flex-1 truncate",
|
|
4337
|
+
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
4338
|
+
}
|
|
4339
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
4340
|
+
]
|
|
4341
|
+
},
|
|
4342
|
+
o.value
|
|
4343
|
+
);
|
|
4344
|
+
})
|
|
4345
|
+
] });
|
|
4078
4346
|
}
|
|
4079
4347
|
var DEFAULT_ROW_HEIGHT = 44;
|
|
4080
4348
|
var DENSE_ROW_HEIGHT = 32;
|
|
@@ -4350,7 +4618,7 @@ var STable = react.forwardRef(function STable2({
|
|
|
4350
4618
|
};
|
|
4351
4619
|
const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
|
|
4352
4620
|
const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
|
|
4353
|
-
const cellPadX =
|
|
4621
|
+
const cellPadX = "var(--cmp-table-body-paddingX-wide)";
|
|
4354
4622
|
const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
|
|
4355
4623
|
const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
|
|
4356
4624
|
const stickyInfo = (i) => {
|