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