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,6 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { createContext, forwardRef, useState, useRef, useImperativeHandle, useMemo,
|
|
3
|
-
import * as Ariakit from '@ariakit/react';
|
|
2
|
+
import { createContext, forwardRef, useState, useRef, useImperativeHandle, useMemo, useEffect, useContext } from 'react';
|
|
4
3
|
import { clsx } from 'clsx';
|
|
5
4
|
import { extendTailwindMerge } from 'tailwind-merge';
|
|
6
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -17,10 +16,110 @@ function cn(...inputs) {
|
|
|
17
16
|
return twMerge(clsx(inputs));
|
|
18
17
|
}
|
|
19
18
|
var PortalContainerContext = createContext(null);
|
|
19
|
+
var InsideModalContext = createContext(false);
|
|
20
20
|
PortalContainerContext.Provider;
|
|
21
|
+
InsideModalContext.Provider;
|
|
21
22
|
function usePortalContainer() {
|
|
22
23
|
return useContext(PortalContainerContext) ?? void 0;
|
|
23
24
|
}
|
|
25
|
+
function useInsideModal() {
|
|
26
|
+
return useContext(InsideModalContext);
|
|
27
|
+
}
|
|
28
|
+
function runRules(value, rules) {
|
|
29
|
+
if (!rules || rules.length === 0) return "";
|
|
30
|
+
for (const rule of rules) {
|
|
31
|
+
const result = rule(value ?? "");
|
|
32
|
+
if (result !== true) return typeof result === "string" ? result : "\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC12\uC785\uB2C8\uB2E4.";
|
|
33
|
+
}
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
var FormContext = createContext(null);
|
|
37
|
+
function useFormContext() {
|
|
38
|
+
return useContext(FormContext);
|
|
39
|
+
}
|
|
40
|
+
function useFormField(field) {
|
|
41
|
+
const ctx = useFormContext();
|
|
42
|
+
const cbRef = useRef(field);
|
|
43
|
+
cbRef.current = field;
|
|
44
|
+
const name = field.name ?? "";
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!ctx) return;
|
|
47
|
+
return ctx.register({
|
|
48
|
+
name,
|
|
49
|
+
validate: () => cbRef.current.validate(),
|
|
50
|
+
resetValidation: () => cbRef.current.resetValidation()
|
|
51
|
+
});
|
|
52
|
+
}, [ctx, name]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/lib/sanitize-inline-html.ts
|
|
56
|
+
var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
|
|
57
|
+
var ALLOWED_ATTRS_BY_TAG = {
|
|
58
|
+
A: /* @__PURE__ */ new Set(["href", "target", "rel", "class"])
|
|
59
|
+
};
|
|
60
|
+
var COMMON_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class"]);
|
|
61
|
+
var SAFE_HREF_RE = /^(https?:|mailto:|tel:|\/|#|\.)/i;
|
|
62
|
+
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
63
|
+
"SCRIPT",
|
|
64
|
+
"STYLE",
|
|
65
|
+
"IFRAME",
|
|
66
|
+
"OBJECT",
|
|
67
|
+
"EMBED",
|
|
68
|
+
"META",
|
|
69
|
+
"LINK",
|
|
70
|
+
"BASE",
|
|
71
|
+
"NOSCRIPT",
|
|
72
|
+
// SVG/MATH 는 foreign-content 파싱 모드로 전환되어 재직렬화 시 다른 트리로
|
|
73
|
+
// 재파싱될 수 있다(mXSS 벡터). 인라인 라벨에 필요 없으므로 통째로 제거한다.
|
|
74
|
+
"SVG",
|
|
75
|
+
"MATH",
|
|
76
|
+
"TEMPLATE"
|
|
77
|
+
]);
|
|
78
|
+
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
79
|
+
var sanitizeNode = (node) => {
|
|
80
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
81
|
+
node.remove();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
85
|
+
const element = node;
|
|
86
|
+
const tagName = element.tagName;
|
|
87
|
+
if (DROP_CONTENT_TAGS.has(tagName)) {
|
|
88
|
+
element.remove();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
|
|
92
|
+
if (ALLOWED_INLINE_TAGS.has(tagName)) {
|
|
93
|
+
if (element.hasAttribute("className")) {
|
|
94
|
+
const val = element.getAttribute("className");
|
|
95
|
+
element.removeAttribute("className");
|
|
96
|
+
if (!element.hasAttribute("class")) element.setAttribute("class", val);
|
|
97
|
+
}
|
|
98
|
+
const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tagName] ?? COMMON_ALLOWED_ATTRS;
|
|
99
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
100
|
+
if (!allowedAttrs.has(attr.name)) element.removeAttribute(attr.name);
|
|
101
|
+
});
|
|
102
|
+
if (tagName === "A") {
|
|
103
|
+
const href = element.getAttribute("href");
|
|
104
|
+
if (href !== null && href !== "" && !SAFE_HREF_RE.test(href)) element.removeAttribute("href");
|
|
105
|
+
if (element.hasAttribute("target")) {
|
|
106
|
+
element.setAttribute("rel", "noopener noreferrer");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const parent = element.parentNode;
|
|
112
|
+
if (parent == null) return;
|
|
113
|
+
while (element.firstChild) parent.insertBefore(element.firstChild, element);
|
|
114
|
+
parent.removeChild(element);
|
|
115
|
+
};
|
|
116
|
+
var sanitizeInlineHtml = (value) => {
|
|
117
|
+
if (typeof document === "undefined") return escapeHtml(value);
|
|
118
|
+
const template = document.createElement("template");
|
|
119
|
+
template.innerHTML = value;
|
|
120
|
+
Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
|
|
121
|
+
return template.innerHTML;
|
|
122
|
+
};
|
|
24
123
|
|
|
25
124
|
// src/lib/color.ts
|
|
26
125
|
var COLOR_KEYS = [
|
|
@@ -3193,32 +3292,6 @@ var STooltip = forwardRef(function STooltip2({
|
|
|
3193
3292
|
] });
|
|
3194
3293
|
});
|
|
3195
3294
|
STooltip.displayName = "STooltip";
|
|
3196
|
-
function runRules(value, rules) {
|
|
3197
|
-
if (!rules || rules.length === 0) return "";
|
|
3198
|
-
for (const rule of rules) {
|
|
3199
|
-
const result = rule(value ?? "");
|
|
3200
|
-
if (result !== true) return typeof result === "string" ? result : "\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uAC12\uC785\uB2C8\uB2E4.";
|
|
3201
|
-
}
|
|
3202
|
-
return "";
|
|
3203
|
-
}
|
|
3204
|
-
var FormContext = createContext(null);
|
|
3205
|
-
function useFormContext() {
|
|
3206
|
-
return useContext(FormContext);
|
|
3207
|
-
}
|
|
3208
|
-
function useFormField(field) {
|
|
3209
|
-
const ctx = useFormContext();
|
|
3210
|
-
const cbRef = useRef(field);
|
|
3211
|
-
cbRef.current = field;
|
|
3212
|
-
const name = field.name ?? "";
|
|
3213
|
-
useEffect(() => {
|
|
3214
|
-
if (!ctx) return;
|
|
3215
|
-
return ctx.register({
|
|
3216
|
-
name,
|
|
3217
|
-
validate: () => cbRef.current.validate(),
|
|
3218
|
-
resetValidation: () => cbRef.current.resetValidation()
|
|
3219
|
-
});
|
|
3220
|
-
}, [ctx, name]);
|
|
3221
|
-
}
|
|
3222
3295
|
var FIELD_SIZE = {
|
|
3223
3296
|
sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
|
|
3224
3297
|
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
|
|
@@ -3233,6 +3306,7 @@ var SField = forwardRef(function SField2({
|
|
|
3233
3306
|
name,
|
|
3234
3307
|
label = "",
|
|
3235
3308
|
icon,
|
|
3309
|
+
iconColor,
|
|
3236
3310
|
labelTooltip = "",
|
|
3237
3311
|
labelTooltipProps,
|
|
3238
3312
|
labelWidth,
|
|
@@ -3310,7 +3384,15 @@ var SField = forwardRef(function SField2({
|
|
|
3310
3384
|
lineHeight: `${s.lineHeight}px`
|
|
3311
3385
|
},
|
|
3312
3386
|
children: [
|
|
3313
|
-
icon && /* @__PURE__ */ jsx(
|
|
3387
|
+
icon && /* @__PURE__ */ jsx(
|
|
3388
|
+
SIcon,
|
|
3389
|
+
{
|
|
3390
|
+
name: icon,
|
|
3391
|
+
size: s.icon,
|
|
3392
|
+
color: iconColor,
|
|
3393
|
+
style: { marginRight: s.labelGap }
|
|
3394
|
+
}
|
|
3395
|
+
),
|
|
3314
3396
|
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
|
|
3315
3397
|
(labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsx(
|
|
3316
3398
|
STooltip,
|
|
@@ -3360,7 +3442,7 @@ var SField = forwardRef(function SField2({
|
|
|
3360
3442
|
borderBottomLeftRadius: s.radius - 1
|
|
3361
3443
|
},
|
|
3362
3444
|
children: [
|
|
3363
|
-
icon && !label && /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon }),
|
|
3445
|
+
icon && !label && /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, color: iconColor }),
|
|
3364
3446
|
effectiveAddonLabel
|
|
3365
3447
|
]
|
|
3366
3448
|
}
|
|
@@ -3375,6 +3457,77 @@ var SField = forwardRef(function SField2({
|
|
|
3375
3457
|
}
|
|
3376
3458
|
);
|
|
3377
3459
|
});
|
|
3460
|
+
function SPortal({
|
|
3461
|
+
open,
|
|
3462
|
+
onClose,
|
|
3463
|
+
onOpenChange,
|
|
3464
|
+
anchorRef,
|
|
3465
|
+
placement = "bottom",
|
|
3466
|
+
align = "start",
|
|
3467
|
+
offset = 4,
|
|
3468
|
+
alignOffset = 0,
|
|
3469
|
+
fit = false,
|
|
3470
|
+
maxHeight,
|
|
3471
|
+
autoClose = false,
|
|
3472
|
+
persistent = false,
|
|
3473
|
+
viewportPadding = 0,
|
|
3474
|
+
onOpenAutoFocus,
|
|
3475
|
+
onCloseAutoFocus,
|
|
3476
|
+
children,
|
|
3477
|
+
className,
|
|
3478
|
+
style,
|
|
3479
|
+
ariaLabel
|
|
3480
|
+
}) {
|
|
3481
|
+
const portalContainer = usePortalContainer();
|
|
3482
|
+
return /* @__PURE__ */ jsxs(
|
|
3483
|
+
RPopover2.Root,
|
|
3484
|
+
{
|
|
3485
|
+
open,
|
|
3486
|
+
onOpenChange: (next) => {
|
|
3487
|
+
onOpenChange?.(next);
|
|
3488
|
+
if (!next) onClose?.();
|
|
3489
|
+
},
|
|
3490
|
+
children: [
|
|
3491
|
+
/* @__PURE__ */ jsx(RPopover2.Anchor, { virtualRef: anchorRef }),
|
|
3492
|
+
/* @__PURE__ */ jsx(RPopover2.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx(
|
|
3493
|
+
RPopover2.Content,
|
|
3494
|
+
{
|
|
3495
|
+
side: placement,
|
|
3496
|
+
align,
|
|
3497
|
+
sideOffset: offset,
|
|
3498
|
+
alignOffset,
|
|
3499
|
+
collisionPadding: viewportPadding,
|
|
3500
|
+
"aria-label": ariaLabel,
|
|
3501
|
+
onPointerDownOutside: (e) => {
|
|
3502
|
+
if (persistent || anchorRef.current?.contains(e.target)) {
|
|
3503
|
+
e.preventDefault();
|
|
3504
|
+
}
|
|
3505
|
+
},
|
|
3506
|
+
onFocusOutside: (e) => {
|
|
3507
|
+
if (persistent || anchorRef.current?.contains(e.target)) {
|
|
3508
|
+
e.preventDefault();
|
|
3509
|
+
}
|
|
3510
|
+
},
|
|
3511
|
+
onEscapeKeyDown: persistent ? (e) => e.preventDefault() : void 0,
|
|
3512
|
+
onOpenAutoFocus,
|
|
3513
|
+
onCloseAutoFocus,
|
|
3514
|
+
onClick: autoClose ? () => onClose?.() : void 0,
|
|
3515
|
+
className: cn(
|
|
3516
|
+
"z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm",
|
|
3517
|
+
maxHeight != null && "overflow-y-auto",
|
|
3518
|
+
fit && "min-w-(--radix-popover-trigger-width)",
|
|
3519
|
+
FLOATING_SLIDE_ANIM,
|
|
3520
|
+
className
|
|
3521
|
+
),
|
|
3522
|
+
style: { maxHeight, ...style },
|
|
3523
|
+
children
|
|
3524
|
+
}
|
|
3525
|
+
) })
|
|
3526
|
+
]
|
|
3527
|
+
}
|
|
3528
|
+
);
|
|
3529
|
+
}
|
|
3530
|
+
SPortal.displayName = "SPortal";
|
|
3378
3531
|
|
|
3379
3532
|
// src/components/SSelect/select.config.ts
|
|
3380
3533
|
function flattenOptions(options, depth = 1) {
|
|
@@ -3409,75 +3562,6 @@ function flattenOptions(options, depth = 1) {
|
|
|
3409
3562
|
var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
|
|
3410
3563
|
var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
|
|
3411
3564
|
var extractText = (html) => html.replace(/<[^>]*>/g, "");
|
|
3412
|
-
|
|
3413
|
-
// src/lib/sanitize-inline-html.ts
|
|
3414
|
-
var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
|
|
3415
|
-
var ALLOWED_ATTRS_BY_TAG = {
|
|
3416
|
-
A: /* @__PURE__ */ new Set(["href", "target", "rel", "class"])
|
|
3417
|
-
};
|
|
3418
|
-
var COMMON_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class"]);
|
|
3419
|
-
var SAFE_HREF_RE = /^(https?:|mailto:|tel:|\/|#|\.)/i;
|
|
3420
|
-
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
3421
|
-
"SCRIPT",
|
|
3422
|
-
"STYLE",
|
|
3423
|
-
"IFRAME",
|
|
3424
|
-
"OBJECT",
|
|
3425
|
-
"EMBED",
|
|
3426
|
-
"META",
|
|
3427
|
-
"LINK",
|
|
3428
|
-
"BASE",
|
|
3429
|
-
"NOSCRIPT",
|
|
3430
|
-
// SVG/MATH 는 foreign-content 파싱 모드로 전환되어 재직렬화 시 다른 트리로
|
|
3431
|
-
// 재파싱될 수 있다(mXSS 벡터). 인라인 라벨에 필요 없으므로 통째로 제거한다.
|
|
3432
|
-
"SVG",
|
|
3433
|
-
"MATH",
|
|
3434
|
-
"TEMPLATE"
|
|
3435
|
-
]);
|
|
3436
|
-
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
3437
|
-
var sanitizeNode = (node) => {
|
|
3438
|
-
if (node.nodeType === Node.COMMENT_NODE) {
|
|
3439
|
-
node.remove();
|
|
3440
|
-
return;
|
|
3441
|
-
}
|
|
3442
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
3443
|
-
const element = node;
|
|
3444
|
-
const tagName = element.tagName;
|
|
3445
|
-
if (DROP_CONTENT_TAGS.has(tagName)) {
|
|
3446
|
-
element.remove();
|
|
3447
|
-
return;
|
|
3448
|
-
}
|
|
3449
|
-
Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
|
|
3450
|
-
if (ALLOWED_INLINE_TAGS.has(tagName)) {
|
|
3451
|
-
if (element.hasAttribute("className")) {
|
|
3452
|
-
const val = element.getAttribute("className");
|
|
3453
|
-
element.removeAttribute("className");
|
|
3454
|
-
if (!element.hasAttribute("class")) element.setAttribute("class", val);
|
|
3455
|
-
}
|
|
3456
|
-
const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tagName] ?? COMMON_ALLOWED_ATTRS;
|
|
3457
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
3458
|
-
if (!allowedAttrs.has(attr.name)) element.removeAttribute(attr.name);
|
|
3459
|
-
});
|
|
3460
|
-
if (tagName === "A") {
|
|
3461
|
-
const href = element.getAttribute("href");
|
|
3462
|
-
if (href !== null && href !== "" && !SAFE_HREF_RE.test(href)) element.removeAttribute("href");
|
|
3463
|
-
if (element.hasAttribute("target")) {
|
|
3464
|
-
element.setAttribute("rel", "noopener noreferrer");
|
|
3465
|
-
}
|
|
3466
|
-
}
|
|
3467
|
-
return;
|
|
3468
|
-
}
|
|
3469
|
-
const parent = element.parentNode;
|
|
3470
|
-
if (parent == null) return;
|
|
3471
|
-
while (element.firstChild) parent.insertBefore(element.firstChild, element);
|
|
3472
|
-
parent.removeChild(element);
|
|
3473
|
-
};
|
|
3474
|
-
var sanitizeInlineHtml = (value) => {
|
|
3475
|
-
if (typeof document === "undefined") return escapeHtml(value);
|
|
3476
|
-
const template = document.createElement("template");
|
|
3477
|
-
template.innerHTML = value;
|
|
3478
|
-
Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
|
|
3479
|
-
return template.innerHTML;
|
|
3480
|
-
};
|
|
3481
3565
|
var SEARCH_THRESHOLD = 11;
|
|
3482
3566
|
var SSelect = forwardRef(function SSelect2({
|
|
3483
3567
|
value,
|
|
@@ -3501,6 +3585,7 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
3501
3585
|
label,
|
|
3502
3586
|
labelWidth,
|
|
3503
3587
|
icon,
|
|
3588
|
+
iconColor,
|
|
3504
3589
|
labelTooltip,
|
|
3505
3590
|
addonLabel,
|
|
3506
3591
|
addonAlign,
|
|
@@ -3512,18 +3597,15 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
3512
3597
|
}, ref) {
|
|
3513
3598
|
const multi = type === "multi" || type === "multi_depth";
|
|
3514
3599
|
const portalContainer = usePortalContainer();
|
|
3600
|
+
const insideModal = useInsideModal();
|
|
3515
3601
|
const [search, setSearch] = useState("");
|
|
3516
3602
|
const [ruleError, setRuleError] = useState("");
|
|
3517
3603
|
const [open, setOpen] = useState(false);
|
|
3604
|
+
const [pendingOpen, setPendingOpen] = useState(false);
|
|
3605
|
+
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
3518
3606
|
const triggerRef = useRef(null);
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
() => ({
|
|
3522
|
-
focus: () => triggerRef.current?.focus(),
|
|
3523
|
-
open: () => setOpen(true)
|
|
3524
|
-
}),
|
|
3525
|
-
[]
|
|
3526
|
-
);
|
|
3607
|
+
const searchInputRef = useRef(null);
|
|
3608
|
+
const listRef = useRef(null);
|
|
3527
3609
|
const flat = useMemo(() => flattenOptions(options), [options]);
|
|
3528
3610
|
const selectable = useMemo(() => selectableOptions(flat), [flat]);
|
|
3529
3611
|
const hasLeaf = useMemo(() => flat.some((o) => !o.isGroup), [flat]);
|
|
@@ -3541,6 +3623,46 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
3541
3623
|
const arr = Array.isArray(value) ? value : [value];
|
|
3542
3624
|
return arr.map((v) => v != null && typeof v === "object" ? v.value : v).filter((v) => v != null && v !== "").map(String);
|
|
3543
3625
|
}, [value]);
|
|
3626
|
+
const selectedRef = useRef(selected);
|
|
3627
|
+
selectedRef.current = selected;
|
|
3628
|
+
const emit = (next) => {
|
|
3629
|
+
if (!onValueChange) return;
|
|
3630
|
+
const mapped = next.map(
|
|
3631
|
+
(v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
|
|
3632
|
+
);
|
|
3633
|
+
onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
|
|
3634
|
+
};
|
|
3635
|
+
const commitOpen = (next) => {
|
|
3636
|
+
setOpen(next);
|
|
3637
|
+
onOpenChange?.(next);
|
|
3638
|
+
if (!next) {
|
|
3639
|
+
setPendingOpen(false);
|
|
3640
|
+
setSearch("");
|
|
3641
|
+
if (rules && rules.length > 0) {
|
|
3642
|
+
setRuleError(runRules(multi ? selectedRef.current : selectedRef.current[0] ?? "", rules));
|
|
3643
|
+
}
|
|
3644
|
+
}
|
|
3645
|
+
};
|
|
3646
|
+
const requestOpen = (next) => {
|
|
3647
|
+
if (next && insideModal && portalContainer == null) {
|
|
3648
|
+
setPendingOpen(true);
|
|
3649
|
+
return;
|
|
3650
|
+
}
|
|
3651
|
+
commitOpen(next);
|
|
3652
|
+
};
|
|
3653
|
+
useEffect(() => {
|
|
3654
|
+
if (!insideModal || portalContainer == null || !pendingOpen) return;
|
|
3655
|
+
setPendingOpen(false);
|
|
3656
|
+
commitOpen(true);
|
|
3657
|
+
}, [insideModal, pendingOpen, portalContainer]);
|
|
3658
|
+
useImperativeHandle(
|
|
3659
|
+
ref,
|
|
3660
|
+
() => ({
|
|
3661
|
+
focus: () => triggerRef.current?.focus(),
|
|
3662
|
+
open: () => requestOpen(true)
|
|
3663
|
+
}),
|
|
3664
|
+
[insideModal, portalContainer]
|
|
3665
|
+
);
|
|
3544
3666
|
useFormField({
|
|
3545
3667
|
name,
|
|
3546
3668
|
validate: () => {
|
|
@@ -3550,13 +3672,6 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
3550
3672
|
},
|
|
3551
3673
|
resetValidation: () => setRuleError("")
|
|
3552
3674
|
});
|
|
3553
|
-
const emit = (next) => {
|
|
3554
|
-
if (!onValueChange) return;
|
|
3555
|
-
const mapped = next.map(
|
|
3556
|
-
(v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
|
|
3557
|
-
);
|
|
3558
|
-
onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
|
|
3559
|
-
};
|
|
3560
3675
|
const filtered = useMemo(() => {
|
|
3561
3676
|
if (!showSearch || search.trim() === "") return flat;
|
|
3562
3677
|
const q = search.trim().toLowerCase();
|
|
@@ -3567,6 +3682,16 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
3567
3682
|
const allValues = selectable.map((o) => o.value);
|
|
3568
3683
|
const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
|
|
3569
3684
|
const hasItems = filtered.some((o) => !o.isGroup);
|
|
3685
|
+
const navigableItems = useMemo(() => {
|
|
3686
|
+
const items = [];
|
|
3687
|
+
if (multi && useSelectAll && allValues.length > 0) {
|
|
3688
|
+
items.push({ value: "__select_all__", isSelectAll: true });
|
|
3689
|
+
}
|
|
3690
|
+
filtered.forEach((o) => {
|
|
3691
|
+
if (!o.isGroup && !o.disabled) items.push({ value: o.value });
|
|
3692
|
+
});
|
|
3693
|
+
return items;
|
|
3694
|
+
}, [allValues.length, filtered, multi, useSelectAll]);
|
|
3570
3695
|
const triggerText = (() => {
|
|
3571
3696
|
if (selected.length === 0) return placeholder;
|
|
3572
3697
|
if (multi) {
|
|
@@ -3576,26 +3701,119 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
3576
3701
|
return labelByValue.get(selected[0]) ?? selected[0];
|
|
3577
3702
|
})();
|
|
3578
3703
|
const isPlaceholder = selected.length === 0;
|
|
3579
|
-
const
|
|
3580
|
-
|
|
3704
|
+
const handleSelect = (nextValue) => {
|
|
3705
|
+
if (multi) {
|
|
3706
|
+
const next = selected.includes(nextValue) ? selected.filter((v) => v !== nextValue) : [...selected, nextValue];
|
|
3707
|
+
emit(next);
|
|
3708
|
+
return;
|
|
3709
|
+
}
|
|
3710
|
+
emit([nextValue]);
|
|
3711
|
+
commitOpen(false);
|
|
3712
|
+
};
|
|
3713
|
+
const handleSelectAll = () => {
|
|
3714
|
+
emit(allSelected ? [] : allValues);
|
|
3715
|
+
};
|
|
3716
|
+
useEffect(() => {
|
|
3717
|
+
if (!open) {
|
|
3718
|
+
setFocusedIndex(-1);
|
|
3719
|
+
return;
|
|
3720
|
+
}
|
|
3721
|
+
setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
|
|
3722
|
+
}, [open, navigableItems.length]);
|
|
3723
|
+
useEffect(() => {
|
|
3724
|
+
if (!open) return;
|
|
3725
|
+
setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
|
|
3726
|
+
}, [search, open, navigableItems.length]);
|
|
3727
|
+
useEffect(() => {
|
|
3728
|
+
if (!open || focusedIndex < 0) return;
|
|
3729
|
+
const list = listRef.current;
|
|
3730
|
+
const focusedEl = list?.querySelector('[data-select-focused="true"]');
|
|
3731
|
+
if (!list || !focusedEl) return;
|
|
3732
|
+
const listRect = list.getBoundingClientRect();
|
|
3733
|
+
const itemRect = focusedEl.getBoundingClientRect();
|
|
3734
|
+
if (itemRect.top < listRect.top) {
|
|
3735
|
+
list.scrollTop += itemRect.top - listRect.top;
|
|
3736
|
+
} else if (itemRect.bottom > listRect.bottom) {
|
|
3737
|
+
list.scrollTop += itemRect.bottom - listRect.bottom;
|
|
3738
|
+
}
|
|
3739
|
+
}, [focusedIndex, open]);
|
|
3740
|
+
const moveFocusedIndex = (direction) => {
|
|
3741
|
+
if (!open || navigableItems.length === 0) return;
|
|
3742
|
+
setFocusedIndex((current) => {
|
|
3743
|
+
if (direction === "next") {
|
|
3744
|
+
return current < 0 ? 0 : (current + 1) % navigableItems.length;
|
|
3745
|
+
}
|
|
3746
|
+
return current <= 0 ? navigableItems.length - 1 : current - 1;
|
|
3747
|
+
});
|
|
3748
|
+
};
|
|
3749
|
+
const selectFocusedItem = () => {
|
|
3750
|
+
if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
|
|
3751
|
+
const focused = navigableItems[focusedIndex];
|
|
3752
|
+
if (focused.isSelectAll) {
|
|
3753
|
+
handleSelectAll();
|
|
3754
|
+
return;
|
|
3755
|
+
}
|
|
3756
|
+
handleSelect(focused.value);
|
|
3757
|
+
};
|
|
3758
|
+
const handleKeyDown = (event) => {
|
|
3759
|
+
if (!open) {
|
|
3760
|
+
if ((event.key === "ArrowDown" || event.key === "ArrowUp") && !effectiveDisabled) {
|
|
3761
|
+
event.preventDefault();
|
|
3762
|
+
requestOpen(true);
|
|
3763
|
+
}
|
|
3764
|
+
return;
|
|
3765
|
+
}
|
|
3766
|
+
if (event.key === "ArrowDown") {
|
|
3767
|
+
event.preventDefault();
|
|
3768
|
+
moveFocusedIndex("next");
|
|
3769
|
+
return;
|
|
3770
|
+
}
|
|
3771
|
+
if (event.key === "ArrowUp") {
|
|
3772
|
+
event.preventDefault();
|
|
3773
|
+
moveFocusedIndex("prev");
|
|
3774
|
+
return;
|
|
3775
|
+
}
|
|
3776
|
+
if (event.key === "Enter") {
|
|
3777
|
+
event.preventDefault();
|
|
3778
|
+
selectFocusedItem();
|
|
3779
|
+
return;
|
|
3780
|
+
}
|
|
3781
|
+
if (event.key === "Escape") {
|
|
3782
|
+
event.preventDefault();
|
|
3783
|
+
commitOpen(false);
|
|
3784
|
+
}
|
|
3785
|
+
};
|
|
3786
|
+
return /* @__PURE__ */ jsxs(
|
|
3787
|
+
SField,
|
|
3581
3788
|
{
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3789
|
+
label,
|
|
3790
|
+
labelWidth,
|
|
3791
|
+
icon,
|
|
3792
|
+
iconColor,
|
|
3793
|
+
labelTooltip,
|
|
3794
|
+
labelTooltipProps,
|
|
3795
|
+
addonLabel,
|
|
3796
|
+
addonAlign,
|
|
3797
|
+
hint,
|
|
3798
|
+
error: error || ruleError !== "",
|
|
3799
|
+
status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
|
|
3800
|
+
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
3801
|
+
disabled: effectiveDisabled,
|
|
3802
|
+
width,
|
|
3803
|
+
className,
|
|
3804
|
+
style,
|
|
3593
3805
|
children: [
|
|
3806
|
+
name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected.join(",") }),
|
|
3594
3807
|
/* @__PURE__ */ jsxs(
|
|
3595
|
-
|
|
3808
|
+
"button",
|
|
3596
3809
|
{
|
|
3597
3810
|
ref: triggerRef,
|
|
3811
|
+
type: "button",
|
|
3598
3812
|
disabled: effectiveDisabled,
|
|
3813
|
+
"aria-haspopup": "listbox",
|
|
3814
|
+
"aria-expanded": open,
|
|
3815
|
+
onClick: () => requestOpen(!open),
|
|
3816
|
+
onKeyDown: handleKeyDown,
|
|
3599
3817
|
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",
|
|
3600
3818
|
style: {
|
|
3601
3819
|
color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
|
|
@@ -3620,178 +3838,229 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
3620
3838
|
]
|
|
3621
3839
|
}
|
|
3622
3840
|
),
|
|
3623
|
-
/* @__PURE__ */
|
|
3624
|
-
|
|
3841
|
+
/* @__PURE__ */ jsx(
|
|
3842
|
+
SPortal,
|
|
3625
3843
|
{
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3844
|
+
open,
|
|
3845
|
+
onClose: () => commitOpen(false),
|
|
3846
|
+
onOpenChange: (next) => {
|
|
3847
|
+
if (next) requestOpen(true);
|
|
3848
|
+
},
|
|
3849
|
+
anchorRef: triggerRef,
|
|
3850
|
+
placement: "bottom",
|
|
3851
|
+
align: "start",
|
|
3852
|
+
offset: 4,
|
|
3853
|
+
fit: !dropdownWidth,
|
|
3854
|
+
onOpenAutoFocus: (event) => {
|
|
3855
|
+
if (!showSearch) {
|
|
3856
|
+
event.preventDefault();
|
|
3857
|
+
return;
|
|
3858
|
+
}
|
|
3859
|
+
event.preventDefault();
|
|
3860
|
+
requestAnimationFrame(() => searchInputRef.current?.focus());
|
|
3861
|
+
},
|
|
3862
|
+
onCloseAutoFocus: (event) => {
|
|
3863
|
+
event.preventDefault();
|
|
3864
|
+
triggerRef.current?.focus();
|
|
3865
|
+
},
|
|
3866
|
+
className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] py-[4px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
|
|
3631
3867
|
style: {
|
|
3632
|
-
maxHeight: dropdownHeight,
|
|
3633
3868
|
...dropdownWidth ? { width: dropdownWidth } : {},
|
|
3634
3869
|
maxWidth: maxDropdownWidth
|
|
3635
3870
|
},
|
|
3636
|
-
children:
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3871
|
+
children: /* @__PURE__ */ jsxs(
|
|
3872
|
+
"div",
|
|
3873
|
+
{
|
|
3874
|
+
ref: listRef,
|
|
3875
|
+
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)]",
|
|
3876
|
+
style: { maxHeight: dropdownHeight },
|
|
3877
|
+
onMouseLeave: () => setFocusedIndex(-1),
|
|
3878
|
+
children: [
|
|
3879
|
+
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: [
|
|
3880
|
+
/* @__PURE__ */ jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
|
|
3881
|
+
/* @__PURE__ */ jsx(
|
|
3882
|
+
"input",
|
|
3883
|
+
{
|
|
3884
|
+
ref: searchInputRef,
|
|
3885
|
+
value: search,
|
|
3886
|
+
onChange: (e) => setSearch(e.target.value),
|
|
3887
|
+
onKeyDown: handleKeyDown,
|
|
3888
|
+
placeholder: "\uAC80\uC0C9",
|
|
3889
|
+
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)]"
|
|
3890
|
+
}
|
|
3891
|
+
),
|
|
3892
|
+
search !== "" && /* @__PURE__ */ jsx(
|
|
3893
|
+
SGhostButton,
|
|
3894
|
+
{
|
|
3895
|
+
icon: "close",
|
|
3896
|
+
size: "xxs",
|
|
3897
|
+
intent: "default",
|
|
3898
|
+
ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
|
|
3899
|
+
onClick: () => setSearch(""),
|
|
3900
|
+
className: "shrink-0"
|
|
3901
|
+
}
|
|
3902
|
+
)
|
|
3903
|
+
] }) }),
|
|
3904
|
+
hasItems ? /* @__PURE__ */ jsx(
|
|
3905
|
+
ItemList,
|
|
3906
|
+
{
|
|
3907
|
+
items: filtered,
|
|
3908
|
+
multi,
|
|
3909
|
+
selected,
|
|
3910
|
+
focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
|
|
3911
|
+
showSelectAll: multi && useSelectAll && allValues.length > 0,
|
|
3912
|
+
selectAllLabel: allSelectedLabel,
|
|
3913
|
+
selectAllSelected: allSelected,
|
|
3914
|
+
onSelectAll: handleSelectAll,
|
|
3915
|
+
onSelect: handleSelect,
|
|
3916
|
+
onHoverValue: (value2) => {
|
|
3917
|
+
const index = navigableItems.findIndex(
|
|
3918
|
+
(item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
|
|
3919
|
+
);
|
|
3920
|
+
if (index >= 0) setFocusedIndex(index);
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
) : /* @__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." })
|
|
3924
|
+
]
|
|
3925
|
+
}
|
|
3926
|
+
)
|
|
3674
3927
|
}
|
|
3675
3928
|
)
|
|
3676
3929
|
]
|
|
3677
3930
|
}
|
|
3678
3931
|
);
|
|
3679
|
-
const control = showSearch ? /* @__PURE__ */ jsx(Ariakit.ComboboxProvider, { value: search, setValue: setSearch, resetValueOnHide: true, children: selectStore }) : selectStore;
|
|
3680
|
-
return /* @__PURE__ */ jsxs(
|
|
3681
|
-
SField,
|
|
3682
|
-
{
|
|
3683
|
-
label,
|
|
3684
|
-
labelWidth,
|
|
3685
|
-
icon,
|
|
3686
|
-
labelTooltip,
|
|
3687
|
-
labelTooltipProps,
|
|
3688
|
-
addonLabel,
|
|
3689
|
-
addonAlign,
|
|
3690
|
-
hint,
|
|
3691
|
-
error: error || ruleError !== "",
|
|
3692
|
-
status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
|
|
3693
|
-
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
3694
|
-
disabled: effectiveDisabled,
|
|
3695
|
-
width,
|
|
3696
|
-
className,
|
|
3697
|
-
style,
|
|
3698
|
-
children: [
|
|
3699
|
-
name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected.join(",") }),
|
|
3700
|
-
control
|
|
3701
|
-
]
|
|
3702
|
-
}
|
|
3703
|
-
);
|
|
3704
3932
|
});
|
|
3705
3933
|
SSelect.displayName = "SSelect";
|
|
3706
3934
|
function ItemList({
|
|
3707
3935
|
items,
|
|
3708
3936
|
multi,
|
|
3709
3937
|
selected,
|
|
3710
|
-
|
|
3938
|
+
focusedValue,
|
|
3939
|
+
showSelectAll,
|
|
3940
|
+
selectAllLabel,
|
|
3941
|
+
selectAllSelected,
|
|
3942
|
+
onSelectAll,
|
|
3943
|
+
onSelect,
|
|
3944
|
+
onHoverValue
|
|
3711
3945
|
}) {
|
|
3712
3946
|
const itemBase = cn(
|
|
3713
|
-
|
|
3714
|
-
"group flex cursor-pointer select-none items-center gap-[8px] text-[12px] leading-[20px] outline-none",
|
|
3947
|
+
"group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
|
|
3715
3948
|
"text-[color:var(--cmp-listItem-content-default)]",
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
);
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
if (o.isGroup) {
|
|
3723
|
-
const isDepth1 = o.depth === 1;
|
|
3724
|
-
return /* @__PURE__ */ jsx(
|
|
3725
|
-
"div",
|
|
3726
|
-
{
|
|
3727
|
-
className: cn(
|
|
3728
|
-
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
3729
|
-
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",
|
|
3730
|
-
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
3731
|
-
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
3732
|
-
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
|
|
3733
|
-
o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
|
|
3734
|
-
),
|
|
3735
|
-
style: { paddingLeft: padLeft },
|
|
3736
|
-
children: o.label
|
|
3737
|
-
},
|
|
3738
|
-
o.value
|
|
3739
|
-
);
|
|
3740
|
-
}
|
|
3741
|
-
const isSelected = selected.includes(o.value);
|
|
3742
|
-
return /* @__PURE__ */ jsxs(
|
|
3743
|
-
Ariakit.SelectItem,
|
|
3949
|
+
"disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
|
|
3950
|
+
);
|
|
3951
|
+
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)]";
|
|
3952
|
+
return /* @__PURE__ */ jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
3953
|
+
showSelectAll && /* @__PURE__ */ jsxs(
|
|
3954
|
+
"button",
|
|
3744
3955
|
{
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3956
|
+
type: "button",
|
|
3957
|
+
"data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
|
|
3958
|
+
"data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
|
|
3748
3959
|
className: cn(
|
|
3749
3960
|
itemBase,
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
),
|
|
3756
|
-
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
|
|
3757
|
-
render: useSearch ? /* @__PURE__ */ jsx(Ariakit.ComboboxItem, {}) : void 0,
|
|
3961
|
+
focusedValue === "__select_all__" && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
|
|
3962
|
+
),
|
|
3963
|
+
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: 12 },
|
|
3964
|
+
onMouseEnter: () => onHoverValue("__select_all__"),
|
|
3965
|
+
onClick: onSelectAll,
|
|
3758
3966
|
children: [
|
|
3759
|
-
multi && // hover(active) 시 checkbox inverse — 파란 hover 배경 위에서 checked/unchecked 모두 흰 테두리 + 투명 배경
|
|
3760
3967
|
/* @__PURE__ */ jsx(
|
|
3761
3968
|
"span",
|
|
3762
3969
|
{
|
|
3763
3970
|
className: cn(
|
|
3764
3971
|
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
3765
|
-
|
|
3766
|
-
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
3767
|
-
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
3768
|
-
) : cn(
|
|
3769
|
-
"group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
|
|
3770
|
-
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"
|
|
3771
|
-
)
|
|
3972
|
+
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)]"
|
|
3772
3973
|
),
|
|
3773
|
-
children:
|
|
3974
|
+
children: selectAllSelected && /* @__PURE__ */ jsx(
|
|
3774
3975
|
SIcon,
|
|
3775
3976
|
{
|
|
3776
3977
|
name: "checkboxCheck",
|
|
3777
3978
|
size: 12,
|
|
3778
|
-
color:
|
|
3979
|
+
color: "var(--cmp-checkbox-checked-icon-default)"
|
|
3779
3980
|
}
|
|
3780
3981
|
)
|
|
3781
3982
|
}
|
|
3782
3983
|
),
|
|
3783
|
-
|
|
3784
|
-
"span",
|
|
3785
|
-
{
|
|
3786
|
-
className: "flex-1 truncate",
|
|
3787
|
-
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
3788
|
-
}
|
|
3789
|
-
) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
3984
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
3790
3985
|
]
|
|
3791
|
-
}
|
|
3792
|
-
|
|
3793
|
-
)
|
|
3794
|
-
|
|
3986
|
+
}
|
|
3987
|
+
),
|
|
3988
|
+
items.map((o) => {
|
|
3989
|
+
const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
|
|
3990
|
+
if (o.isGroup) {
|
|
3991
|
+
const isDepth1 = o.depth === 1;
|
|
3992
|
+
return /* @__PURE__ */ jsx(
|
|
3993
|
+
"div",
|
|
3994
|
+
{
|
|
3995
|
+
className: cn(
|
|
3996
|
+
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
3997
|
+
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",
|
|
3998
|
+
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
3999
|
+
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
4000
|
+
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
|
|
4001
|
+
o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
|
|
4002
|
+
),
|
|
4003
|
+
style: { paddingLeft: padLeft },
|
|
4004
|
+
children: o.label
|
|
4005
|
+
},
|
|
4006
|
+
o.value
|
|
4007
|
+
);
|
|
4008
|
+
}
|
|
4009
|
+
const isSelected = selected.includes(o.value);
|
|
4010
|
+
return /* @__PURE__ */ jsxs(
|
|
4011
|
+
"button",
|
|
4012
|
+
{
|
|
4013
|
+
type: "button",
|
|
4014
|
+
disabled: o.disabled,
|
|
4015
|
+
role: "option",
|
|
4016
|
+
"aria-selected": isSelected,
|
|
4017
|
+
"data-select-focused": focusedValue === o.value ? "true" : void 0,
|
|
4018
|
+
"data-select-active": focusedValue === o.value ? "true" : void 0,
|
|
4019
|
+
className: cn(
|
|
4020
|
+
itemBase,
|
|
4021
|
+
isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
|
|
4022
|
+
focusedValue === o.value && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]",
|
|
4023
|
+
activeClass
|
|
4024
|
+
),
|
|
4025
|
+
style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
|
|
4026
|
+
onMouseEnter: () => onHoverValue(o.value),
|
|
4027
|
+
onClick: () => onSelect(o.value),
|
|
4028
|
+
children: [
|
|
4029
|
+
multi && /* @__PURE__ */ jsx(
|
|
4030
|
+
"span",
|
|
4031
|
+
{
|
|
4032
|
+
className: cn(
|
|
4033
|
+
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
4034
|
+
o.disabled ? (
|
|
4035
|
+
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
4036
|
+
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
4037
|
+
) : cn(
|
|
4038
|
+
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)]"
|
|
4039
|
+
)
|
|
4040
|
+
),
|
|
4041
|
+
children: isSelected && /* @__PURE__ */ jsx(
|
|
4042
|
+
SIcon,
|
|
4043
|
+
{
|
|
4044
|
+
name: "checkboxCheck",
|
|
4045
|
+
size: 12,
|
|
4046
|
+
color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
|
|
4047
|
+
}
|
|
4048
|
+
)
|
|
4049
|
+
}
|
|
4050
|
+
),
|
|
4051
|
+
isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
|
|
4052
|
+
"span",
|
|
4053
|
+
{
|
|
4054
|
+
className: "flex-1 truncate",
|
|
4055
|
+
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
4056
|
+
}
|
|
4057
|
+
) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
4058
|
+
]
|
|
4059
|
+
},
|
|
4060
|
+
o.value
|
|
4061
|
+
);
|
|
4062
|
+
})
|
|
4063
|
+
] });
|
|
3795
4064
|
}
|
|
3796
4065
|
|
|
3797
4066
|
export { SSelect };
|