sellmate-design-system-react 0.8.2 → 0.8.4
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/SBarcodeInput/SBarcodeInput.cjs +78 -2
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.js +78 -2
- package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
- package/dist/components/SCheckbox/SCheckbox.cjs +1 -1
- package/dist/components/SCheckbox/SCheckbox.cjs.map +1 -1
- package/dist/components/SCheckbox/SCheckbox.js +1 -1
- package/dist/components/SCheckbox/SCheckbox.js.map +1 -1
- package/dist/components/SChipInput/SChipInput.cjs +78 -2
- package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
- package/dist/components/SChipInput/SChipInput.js +78 -2
- package/dist/components/SChipInput/SChipInput.js.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.cjs +78 -2
- package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.js +78 -2
- package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs +78 -2
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.js +78 -2
- package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
- package/dist/components/SField/SField.cjs +78 -2
- package/dist/components/SField/SField.cjs.map +1 -1
- package/dist/components/SField/SField.js +78 -2
- package/dist/components/SField/SField.js.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.cjs +78 -2
- package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.js +78 -2
- package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
- package/dist/components/SInput/SInput.cjs +78 -2
- package/dist/components/SInput/SInput.cjs.map +1 -1
- package/dist/components/SInput/SInput.js +78 -2
- package/dist/components/SInput/SInput.js.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs +229 -122
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.js +229 -122
- package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.cjs +78 -2
- package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.js +78 -2
- package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
- package/dist/components/SSelect/SSelect.cjs +159 -52
- package/dist/components/SSelect/SSelect.cjs.map +1 -1
- package/dist/components/SSelect/SSelect.js +159 -52
- package/dist/components/SSelect/SSelect.js.map +1 -1
- package/dist/components/SSelect/select.config.d.ts +7 -0
- package/dist/components/STable/STable.cjs +160 -53
- package/dist/components/STable/STable.cjs.map +1 -1
- package/dist/components/STable/STable.js +160 -53
- package/dist/components/STable/STable.js.map +1 -1
- package/dist/components/STextarea/STextarea.cjs +78 -2
- package/dist/components/STextarea/STextarea.cjs.map +1 -1
- package/dist/components/STextarea/STextarea.js +78 -2
- package/dist/components/STextarea/STextarea.js.map +1 -1
- package/dist/components/STimePicker/STimePicker.cjs +78 -2
- package/dist/components/STimePicker/STimePicker.cjs.map +1 -1
- package/dist/components/STimePicker/STimePicker.js +78 -2
- package/dist/components/STimePicker/STimePicker.js.map +1 -1
- package/dist/components/STimeRangePicker/STimeRangePicker.cjs +78 -2
- package/dist/components/STimeRangePicker/STimeRangePicker.cjs.map +1 -1
- package/dist/components/STimeRangePicker/STimeRangePicker.js +78 -2
- package/dist/components/STimeRangePicker/STimeRangePicker.js.map +1 -1
- package/dist/components/STooltip/STooltip.cjs +78 -2
- package/dist/components/STooltip/STooltip.cjs.map +1 -1
- package/dist/components/STooltip/STooltip.d.ts +1 -1
- package/dist/components/STooltip/STooltip.js +78 -2
- package/dist/components/STooltip/STooltip.js.map +1 -1
- package/dist/index.cjs +160 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +160 -53
- package/dist/index.js.map +1 -1
- package/dist/styles.css +4 -8
- package/package.json +1 -1
|
@@ -2883,6 +2883,75 @@ function useInsideModal() {
|
|
|
2883
2883
|
return useContext(InsideModalContext);
|
|
2884
2884
|
}
|
|
2885
2885
|
|
|
2886
|
+
// src/lib/sanitize-inline-html.ts
|
|
2887
|
+
var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
|
|
2888
|
+
var ALLOWED_ATTRS_BY_TAG = {
|
|
2889
|
+
A: /* @__PURE__ */ new Set(["href", "target", "rel", "class"])
|
|
2890
|
+
};
|
|
2891
|
+
var COMMON_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class"]);
|
|
2892
|
+
var SAFE_HREF_RE = /^(https?:|mailto:|tel:|\/|#|\.)/i;
|
|
2893
|
+
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
2894
|
+
"SCRIPT",
|
|
2895
|
+
"STYLE",
|
|
2896
|
+
"IFRAME",
|
|
2897
|
+
"OBJECT",
|
|
2898
|
+
"EMBED",
|
|
2899
|
+
"META",
|
|
2900
|
+
"LINK",
|
|
2901
|
+
"BASE",
|
|
2902
|
+
"NOSCRIPT",
|
|
2903
|
+
// SVG/MATH 는 foreign-content 파싱 모드로 전환되어 재직렬화 시 다른 트리로
|
|
2904
|
+
// 재파싱될 수 있다(mXSS 벡터). 인라인 라벨에 필요 없으므로 통째로 제거한다.
|
|
2905
|
+
"SVG",
|
|
2906
|
+
"MATH",
|
|
2907
|
+
"TEMPLATE"
|
|
2908
|
+
]);
|
|
2909
|
+
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2910
|
+
var sanitizeNode = (node) => {
|
|
2911
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
2912
|
+
node.remove();
|
|
2913
|
+
return;
|
|
2914
|
+
}
|
|
2915
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
2916
|
+
const element = node;
|
|
2917
|
+
const tagName = element.tagName;
|
|
2918
|
+
if (DROP_CONTENT_TAGS.has(tagName)) {
|
|
2919
|
+
element.remove();
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
|
|
2923
|
+
if (ALLOWED_INLINE_TAGS.has(tagName)) {
|
|
2924
|
+
if (element.hasAttribute("className")) {
|
|
2925
|
+
const val = element.getAttribute("className");
|
|
2926
|
+
element.removeAttribute("className");
|
|
2927
|
+
if (!element.hasAttribute("class")) element.setAttribute("class", val);
|
|
2928
|
+
}
|
|
2929
|
+
const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tagName] ?? COMMON_ALLOWED_ATTRS;
|
|
2930
|
+
Array.from(element.attributes).forEach((attr) => {
|
|
2931
|
+
if (!allowedAttrs.has(attr.name)) element.removeAttribute(attr.name);
|
|
2932
|
+
});
|
|
2933
|
+
if (tagName === "A") {
|
|
2934
|
+
const href = element.getAttribute("href");
|
|
2935
|
+
if (href !== null && href !== "" && !SAFE_HREF_RE.test(href)) element.removeAttribute("href");
|
|
2936
|
+
if (element.hasAttribute("target")) {
|
|
2937
|
+
element.setAttribute("rel", "noopener noreferrer");
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
const parent = element.parentNode;
|
|
2943
|
+
if (parent == null) return;
|
|
2944
|
+
while (element.firstChild) parent.insertBefore(element.firstChild, element);
|
|
2945
|
+
parent.removeChild(element);
|
|
2946
|
+
};
|
|
2947
|
+
var sanitizeInlineHtml = (value) => {
|
|
2948
|
+
if (typeof document === "undefined") return escapeHtml(value);
|
|
2949
|
+
const template = document.createElement("template");
|
|
2950
|
+
template.innerHTML = value;
|
|
2951
|
+
Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
|
|
2952
|
+
return template.innerHTML;
|
|
2953
|
+
};
|
|
2954
|
+
|
|
2886
2955
|
// src/components/STag/tag.config.ts
|
|
2887
2956
|
var TAG_SIZE_CONFIG = {
|
|
2888
2957
|
xs: {
|
|
@@ -3081,7 +3150,14 @@ function useBody(content, message = []) {
|
|
|
3081
3150
|
if (content != null) return content;
|
|
3082
3151
|
if (message.length === 0) return null;
|
|
3083
3152
|
const singleLine = message.length === 1;
|
|
3084
|
-
return /* @__PURE__ */ jsx(Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsx(
|
|
3153
|
+
return /* @__PURE__ */ jsx(Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsx(
|
|
3154
|
+
"div",
|
|
3155
|
+
{
|
|
3156
|
+
className: singleLine ? "leading-[24px]" : void 0,
|
|
3157
|
+
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(line) }
|
|
3158
|
+
},
|
|
3159
|
+
i
|
|
3160
|
+
)) });
|
|
3085
3161
|
}
|
|
3086
3162
|
var STooltip = forwardRef(function STooltip2({
|
|
3087
3163
|
content,
|
|
@@ -3139,7 +3215,7 @@ var STooltip = forwardRef(function STooltip2({
|
|
|
3139
3215
|
if (disabled || body == null) return /* @__PURE__ */ jsx(Fragment, { children: triggerNode });
|
|
3140
3216
|
const isHover = trigger === "hover";
|
|
3141
3217
|
const contentClass = cn(
|
|
3142
|
-
"z-50
|
|
3218
|
+
"z-50 w-fit whitespace-nowrap rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px]",
|
|
3143
3219
|
FLOATING_SLIDE_ANIM,
|
|
3144
3220
|
className
|
|
3145
3221
|
);
|
|
@@ -4163,75 +4239,6 @@ var FIREFOX_SCROLLBAR_CLASS = "supports-[not_selector(::-webkit-scrollbar)]:[scr
|
|
|
4163
4239
|
var SCROLLBAR_CLASS = `${FIREFOX_SCROLLBAR_CLASS} [&::-webkit-scrollbar]:h-[12px] [&::-webkit-scrollbar]:w-[12px] [&::-webkit-scrollbar]:bg-transparent [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:border-2 [&::-webkit-scrollbar-thumb]:border-solid [&::-webkit-scrollbar-thumb]:border-transparent [&::-webkit-scrollbar-thumb]:bg-[var(--color-grey-45)] [&::-webkit-scrollbar-thumb]:bg-clip-padding`;
|
|
4164
4240
|
var SCROLLBAR_TRACK_BG_CLASS = "supports-[not_selector(::-webkit-scrollbar)]:[scrollbar-color:var(--color-grey-45)_var(--color-grey-10)] [&::-webkit-scrollbar]:bg-[var(--color-grey-10)] [&::-webkit-scrollbar-track]:bg-[var(--color-grey-10)]";
|
|
4165
4241
|
var SCROLLBAR_TRACK_BORDER_CLASS = "[&::-webkit-scrollbar-track:vertical]:[border-left:1px_solid_var(--sys-color-border-default)] [&::-webkit-scrollbar-track:horizontal]:[border-top:1px_solid_var(--sys-color-border-default)] [&::-webkit-scrollbar-thumb:vertical]:[border-left-width:3px] [&::-webkit-scrollbar-thumb:horizontal]:[border-top-width:3px]";
|
|
4166
|
-
|
|
4167
|
-
// src/lib/sanitize-inline-html.ts
|
|
4168
|
-
var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
|
|
4169
|
-
var ALLOWED_ATTRS_BY_TAG = {
|
|
4170
|
-
A: /* @__PURE__ */ new Set(["href", "target", "rel", "class"])
|
|
4171
|
-
};
|
|
4172
|
-
var COMMON_ALLOWED_ATTRS = /* @__PURE__ */ new Set(["class"]);
|
|
4173
|
-
var SAFE_HREF_RE = /^(https?:|mailto:|tel:|\/|#|\.)/i;
|
|
4174
|
-
var DROP_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
4175
|
-
"SCRIPT",
|
|
4176
|
-
"STYLE",
|
|
4177
|
-
"IFRAME",
|
|
4178
|
-
"OBJECT",
|
|
4179
|
-
"EMBED",
|
|
4180
|
-
"META",
|
|
4181
|
-
"LINK",
|
|
4182
|
-
"BASE",
|
|
4183
|
-
"NOSCRIPT",
|
|
4184
|
-
// SVG/MATH 는 foreign-content 파싱 모드로 전환되어 재직렬화 시 다른 트리로
|
|
4185
|
-
// 재파싱될 수 있다(mXSS 벡터). 인라인 라벨에 필요 없으므로 통째로 제거한다.
|
|
4186
|
-
"SVG",
|
|
4187
|
-
"MATH",
|
|
4188
|
-
"TEMPLATE"
|
|
4189
|
-
]);
|
|
4190
|
-
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4191
|
-
var sanitizeNode = (node) => {
|
|
4192
|
-
if (node.nodeType === Node.COMMENT_NODE) {
|
|
4193
|
-
node.remove();
|
|
4194
|
-
return;
|
|
4195
|
-
}
|
|
4196
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return;
|
|
4197
|
-
const element = node;
|
|
4198
|
-
const tagName = element.tagName;
|
|
4199
|
-
if (DROP_CONTENT_TAGS.has(tagName)) {
|
|
4200
|
-
element.remove();
|
|
4201
|
-
return;
|
|
4202
|
-
}
|
|
4203
|
-
Array.from(element.childNodes).forEach((child) => sanitizeNode(child));
|
|
4204
|
-
if (ALLOWED_INLINE_TAGS.has(tagName)) {
|
|
4205
|
-
if (element.hasAttribute("className")) {
|
|
4206
|
-
const val = element.getAttribute("className");
|
|
4207
|
-
element.removeAttribute("className");
|
|
4208
|
-
if (!element.hasAttribute("class")) element.setAttribute("class", val);
|
|
4209
|
-
}
|
|
4210
|
-
const allowedAttrs = ALLOWED_ATTRS_BY_TAG[tagName] ?? COMMON_ALLOWED_ATTRS;
|
|
4211
|
-
Array.from(element.attributes).forEach((attr) => {
|
|
4212
|
-
if (!allowedAttrs.has(attr.name)) element.removeAttribute(attr.name);
|
|
4213
|
-
});
|
|
4214
|
-
if (tagName === "A") {
|
|
4215
|
-
const href = element.getAttribute("href");
|
|
4216
|
-
if (href !== null && href !== "" && !SAFE_HREF_RE.test(href)) element.removeAttribute("href");
|
|
4217
|
-
if (element.hasAttribute("target")) {
|
|
4218
|
-
element.setAttribute("rel", "noopener noreferrer");
|
|
4219
|
-
}
|
|
4220
|
-
}
|
|
4221
|
-
return;
|
|
4222
|
-
}
|
|
4223
|
-
const parent = element.parentNode;
|
|
4224
|
-
if (parent == null) return;
|
|
4225
|
-
while (element.firstChild) parent.insertBefore(element.firstChild, element);
|
|
4226
|
-
parent.removeChild(element);
|
|
4227
|
-
};
|
|
4228
|
-
var sanitizeInlineHtml = (value) => {
|
|
4229
|
-
if (typeof document === "undefined") return escapeHtml(value);
|
|
4230
|
-
const template = document.createElement("template");
|
|
4231
|
-
template.innerHTML = value;
|
|
4232
|
-
Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
|
|
4233
|
-
return template.innerHTML;
|
|
4234
|
-
};
|
|
4235
4242
|
function SPortal({
|
|
4236
4243
|
open,
|
|
4237
4244
|
onClose,
|
|
@@ -4338,6 +4345,16 @@ function flattenOptions(options, depth = 1) {
|
|
|
4338
4345
|
return out;
|
|
4339
4346
|
}
|
|
4340
4347
|
var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
|
|
4348
|
+
function collectGroupLeafValues(flat, groupIndex) {
|
|
4349
|
+
const group = flat[groupIndex];
|
|
4350
|
+
if (group == null || !group.isGroup) return [];
|
|
4351
|
+
const values = [];
|
|
4352
|
+
for (let i = groupIndex + 1; i < flat.length; i++) {
|
|
4353
|
+
if (flat[i].depth <= group.depth) break;
|
|
4354
|
+
if (!flat[i].isGroup && !flat[i].disabled) values.push(flat[i].value);
|
|
4355
|
+
}
|
|
4356
|
+
return values;
|
|
4357
|
+
}
|
|
4341
4358
|
var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
|
|
4342
4359
|
var extractText = (html) => html.replace(/<[^>]*>/g, "");
|
|
4343
4360
|
var SEARCH_THRESHOLD = 11;
|
|
@@ -4463,16 +4480,31 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
4463
4480
|
const allValues = selectable.map((o) => o.value);
|
|
4464
4481
|
const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
|
|
4465
4482
|
const hasItems = filtered.some((o) => !o.isGroup);
|
|
4483
|
+
const groupLeafValues = useMemo(() => {
|
|
4484
|
+
const map = /* @__PURE__ */ new Map();
|
|
4485
|
+
filtered.forEach((o, i) => {
|
|
4486
|
+
if (o.isGroup) map.set(i, collectGroupLeafValues(filtered, i));
|
|
4487
|
+
});
|
|
4488
|
+
return map;
|
|
4489
|
+
}, [filtered]);
|
|
4466
4490
|
const navigableItems = useMemo(() => {
|
|
4467
4491
|
const items = [];
|
|
4468
4492
|
if (multi && useSelectAll && allValues.length > 0) {
|
|
4469
|
-
items.push({
|
|
4493
|
+
items.push({ key: "__select_all__", isSelectAll: true });
|
|
4470
4494
|
}
|
|
4471
|
-
filtered.forEach((o) => {
|
|
4472
|
-
if (
|
|
4495
|
+
filtered.forEach((o, i) => {
|
|
4496
|
+
if (o.disabled) return;
|
|
4497
|
+
if (o.isGroup) {
|
|
4498
|
+
if (!multi) return;
|
|
4499
|
+
const leafValues = groupLeafValues.get(i) ?? [];
|
|
4500
|
+
if (leafValues.length === 0) return;
|
|
4501
|
+
items.push({ key: `__group_${i}__`, isGroup: true, leafValues });
|
|
4502
|
+
} else {
|
|
4503
|
+
items.push({ key: o.value, value: o.value });
|
|
4504
|
+
}
|
|
4473
4505
|
});
|
|
4474
4506
|
return items;
|
|
4475
|
-
}, [allValues.length, filtered, multi, useSelectAll]);
|
|
4507
|
+
}, [allValues.length, filtered, groupLeafValues, multi, useSelectAll]);
|
|
4476
4508
|
const triggerText = (() => {
|
|
4477
4509
|
if (selected.length === 0) return placeholder;
|
|
4478
4510
|
if (multi) {
|
|
@@ -4494,6 +4526,17 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
4494
4526
|
const handleSelectAll = () => {
|
|
4495
4527
|
emit(allSelected ? [] : allValues);
|
|
4496
4528
|
};
|
|
4529
|
+
const handleToggleGroup = (leafValues) => {
|
|
4530
|
+
if (leafValues.length === 0) return;
|
|
4531
|
+
const selectedSet = new Set(selected);
|
|
4532
|
+
const allSelectedInGroup = leafValues.every((v) => selectedSet.has(v));
|
|
4533
|
+
if (allSelectedInGroup) {
|
|
4534
|
+
const remove = new Set(leafValues);
|
|
4535
|
+
emit(selected.filter((v) => !remove.has(v)));
|
|
4536
|
+
} else {
|
|
4537
|
+
emit([...selected, ...leafValues.filter((v) => !selectedSet.has(v))]);
|
|
4538
|
+
}
|
|
4539
|
+
};
|
|
4497
4540
|
useEffect(() => {
|
|
4498
4541
|
if (!open) {
|
|
4499
4542
|
setFocusedIndex(-1);
|
|
@@ -4534,6 +4577,10 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
4534
4577
|
handleSelectAll();
|
|
4535
4578
|
return;
|
|
4536
4579
|
}
|
|
4580
|
+
if (focused.isGroup) {
|
|
4581
|
+
handleToggleGroup(focused.leafValues ?? []);
|
|
4582
|
+
return;
|
|
4583
|
+
}
|
|
4537
4584
|
handleSelect(focused.value);
|
|
4538
4585
|
};
|
|
4539
4586
|
const handleKeyDown = (event) => {
|
|
@@ -4697,20 +4744,20 @@ var SSelect = forwardRef(function SSelect2({
|
|
|
4697
4744
|
items: filtered,
|
|
4698
4745
|
multi,
|
|
4699
4746
|
selected,
|
|
4700
|
-
|
|
4747
|
+
groupLeafValues,
|
|
4748
|
+
focusedValue: navigableItems[focusedIndex]?.key,
|
|
4701
4749
|
showSelectAll: multi && useSelectAll && allValues.length > 0,
|
|
4702
4750
|
selectAllLabel: allSelectedLabel,
|
|
4703
4751
|
selectAllSelected: allSelected,
|
|
4704
4752
|
onSelectAll: handleSelectAll,
|
|
4705
4753
|
onSelect: handleSelect,
|
|
4706
|
-
|
|
4707
|
-
|
|
4754
|
+
onToggleGroup: handleToggleGroup,
|
|
4755
|
+
onHoverValue: (key) => {
|
|
4756
|
+
if (key == null) {
|
|
4708
4757
|
setFocusedIndex(-1);
|
|
4709
4758
|
return;
|
|
4710
4759
|
}
|
|
4711
|
-
const index = navigableItems.findIndex(
|
|
4712
|
-
(item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
|
|
4713
|
-
);
|
|
4760
|
+
const index = navigableItems.findIndex((item) => item.key === key);
|
|
4714
4761
|
if (index >= 0) setFocusedIndex(index);
|
|
4715
4762
|
}
|
|
4716
4763
|
}
|
|
@@ -4729,20 +4776,23 @@ function ItemList({
|
|
|
4729
4776
|
items,
|
|
4730
4777
|
multi,
|
|
4731
4778
|
selected,
|
|
4779
|
+
groupLeafValues,
|
|
4732
4780
|
focusedValue,
|
|
4733
4781
|
showSelectAll,
|
|
4734
4782
|
selectAllLabel,
|
|
4735
4783
|
selectAllSelected,
|
|
4736
4784
|
onSelectAll,
|
|
4737
4785
|
onSelect,
|
|
4786
|
+
onToggleGroup,
|
|
4738
4787
|
onHoverValue
|
|
4739
4788
|
}) {
|
|
4789
|
+
const selectedSet = useMemo(() => new Set(selected), [selected]);
|
|
4740
4790
|
const itemBase = cn(
|
|
4741
4791
|
"group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
|
|
4742
4792
|
"text-[color:var(--cmp-listItem-content-default)]",
|
|
4743
4793
|
"disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
|
|
4744
4794
|
);
|
|
4745
|
-
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)]";
|
|
4795
|
+
const activeClass = "hover:bg-[var(--cmp-listItem-bg-hover)] hover:text-[color:var(--cmp-listItem-content-hover)] focus-visible:bg-[var(--cmp-listItem-bg-hover)] focus-visible:text-[color:var(--cmp-listItem-content-hover)]";
|
|
4746
4796
|
return /* @__PURE__ */ jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
|
|
4747
4797
|
showSelectAll && /* @__PURE__ */ jsxs(
|
|
4748
4798
|
"button",
|
|
@@ -4759,37 +4809,81 @@ function ItemList({
|
|
|
4759
4809
|
onMouseLeave: () => onHoverValue(null),
|
|
4760
4810
|
onClick: onSelectAll,
|
|
4761
4811
|
children: [
|
|
4762
|
-
/* @__PURE__ */ jsx(
|
|
4763
|
-
"span",
|
|
4764
|
-
{
|
|
4765
|
-
className: cn(
|
|
4766
|
-
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
4767
|
-
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)]"
|
|
4768
|
-
),
|
|
4769
|
-
children: selectAllSelected && /* @__PURE__ */ jsx(
|
|
4770
|
-
SIcon,
|
|
4771
|
-
{
|
|
4772
|
-
name: "checkboxCheck",
|
|
4773
|
-
size: 12,
|
|
4774
|
-
color: "var(--cmp-checkbox-checked-icon-default)"
|
|
4775
|
-
}
|
|
4776
|
-
)
|
|
4777
|
-
}
|
|
4778
|
-
),
|
|
4812
|
+
/* @__PURE__ */ jsx(CheckboxBox, { state: selectAllSelected, focused: focusedValue === "__select_all__" }),
|
|
4779
4813
|
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
|
|
4780
4814
|
]
|
|
4781
4815
|
}
|
|
4782
4816
|
),
|
|
4783
|
-
items.map((o) => {
|
|
4817
|
+
items.map((o, idx) => {
|
|
4784
4818
|
const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
|
|
4819
|
+
const rowKey = `${o.value}-${idx}`;
|
|
4785
4820
|
if (o.isGroup) {
|
|
4786
4821
|
const isDepth1 = o.depth === 1;
|
|
4822
|
+
const groupTone = 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";
|
|
4823
|
+
if (multi) {
|
|
4824
|
+
const leafValues = groupLeafValues.get(idx) ?? [];
|
|
4825
|
+
const total = leafValues.length;
|
|
4826
|
+
const selectedCount = leafValues.reduce((n, v) => selectedSet.has(v) ? n + 1 : n, 0);
|
|
4827
|
+
const groupState = total === 0 || selectedCount === 0 ? false : selectedCount === total ? true : null;
|
|
4828
|
+
const groupDisabled = o.disabled || total === 0;
|
|
4829
|
+
const groupFocused = focusedValue === `__group_${idx}__`;
|
|
4830
|
+
return /* @__PURE__ */ jsxs(
|
|
4831
|
+
"button",
|
|
4832
|
+
{
|
|
4833
|
+
type: "button",
|
|
4834
|
+
role: "option",
|
|
4835
|
+
"aria-selected": groupState === true,
|
|
4836
|
+
disabled: groupDisabled,
|
|
4837
|
+
"data-select-focused": groupFocused ? "true" : void 0,
|
|
4838
|
+
"data-select-active": groupFocused ? "true" : void 0,
|
|
4839
|
+
className: cn(
|
|
4840
|
+
"flex w-full select-none items-center gap-[8px] border-none py-[4px] pr-[12px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
4841
|
+
groupTone,
|
|
4842
|
+
groupDisabled ? "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]" : "cursor-pointer",
|
|
4843
|
+
groupDisabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]",
|
|
4844
|
+
// 포커스 하이라이트는 depth 배경보다 뒤에 둬 tailwind-merge 로 이기게 한다.
|
|
4845
|
+
groupFocused && !groupDisabled && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
|
|
4846
|
+
),
|
|
4847
|
+
style: { paddingLeft: padLeft },
|
|
4848
|
+
onMouseEnter: () => onHoverValue(`__group_${idx}__`),
|
|
4849
|
+
onMouseLeave: () => onHoverValue(null),
|
|
4850
|
+
onClick: () => onToggleGroup(leafValues),
|
|
4851
|
+
children: [
|
|
4852
|
+
/* @__PURE__ */ jsx(CheckboxBox, { state: groupState, disabled: groupDisabled, focused: groupFocused }),
|
|
4853
|
+
isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
|
|
4854
|
+
"span",
|
|
4855
|
+
{
|
|
4856
|
+
className: "min-w-0 truncate",
|
|
4857
|
+
dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
|
|
4858
|
+
}
|
|
4859
|
+
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: o.label }),
|
|
4860
|
+
total > 0 && /* @__PURE__ */ jsxs(
|
|
4861
|
+
"span",
|
|
4862
|
+
{
|
|
4863
|
+
className: cn(
|
|
4864
|
+
"shrink-0 font-medium",
|
|
4865
|
+
groupFocused ? "text-white" : "text-[color:var(--cmp-listItem-content-disabled)]"
|
|
4866
|
+
),
|
|
4867
|
+
children: [
|
|
4868
|
+
"(",
|
|
4869
|
+
selectedCount,
|
|
4870
|
+
"/",
|
|
4871
|
+
total,
|
|
4872
|
+
")"
|
|
4873
|
+
]
|
|
4874
|
+
}
|
|
4875
|
+
)
|
|
4876
|
+
]
|
|
4877
|
+
},
|
|
4878
|
+
rowKey
|
|
4879
|
+
);
|
|
4880
|
+
}
|
|
4787
4881
|
return /* @__PURE__ */ jsx(
|
|
4788
4882
|
"div",
|
|
4789
4883
|
{
|
|
4790
4884
|
className: cn(
|
|
4791
4885
|
"select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
|
|
4792
|
-
|
|
4886
|
+
groupTone,
|
|
4793
4887
|
// 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
|
|
4794
4888
|
// 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
|
|
4795
4889
|
o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
|
|
@@ -4798,7 +4892,7 @@ function ItemList({
|
|
|
4798
4892
|
style: { paddingLeft: padLeft },
|
|
4799
4893
|
children: o.label
|
|
4800
4894
|
},
|
|
4801
|
-
|
|
4895
|
+
rowKey
|
|
4802
4896
|
);
|
|
4803
4897
|
}
|
|
4804
4898
|
const isSelected = selected.includes(o.value);
|
|
@@ -4825,25 +4919,11 @@ function ItemList({
|
|
|
4825
4919
|
onClick: () => onSelect(o.value),
|
|
4826
4920
|
children: [
|
|
4827
4921
|
multi && /* @__PURE__ */ jsx(
|
|
4828
|
-
|
|
4922
|
+
CheckboxBox,
|
|
4829
4923
|
{
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
4834
|
-
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
4835
|
-
) : cn(
|
|
4836
|
-
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)]"
|
|
4837
|
-
)
|
|
4838
|
-
),
|
|
4839
|
-
children: isSelected && /* @__PURE__ */ jsx(
|
|
4840
|
-
SIcon,
|
|
4841
|
-
{
|
|
4842
|
-
name: "checkboxCheck",
|
|
4843
|
-
size: 12,
|
|
4844
|
-
color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
|
|
4845
|
-
}
|
|
4846
|
-
)
|
|
4924
|
+
state: isSelected,
|
|
4925
|
+
disabled: o.disabled,
|
|
4926
|
+
focused: focusedValue === o.value
|
|
4847
4927
|
}
|
|
4848
4928
|
),
|
|
4849
4929
|
isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
|
|
@@ -4855,11 +4935,38 @@ function ItemList({
|
|
|
4855
4935
|
) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
|
|
4856
4936
|
]
|
|
4857
4937
|
},
|
|
4858
|
-
|
|
4938
|
+
rowKey
|
|
4859
4939
|
);
|
|
4860
4940
|
})
|
|
4861
4941
|
] });
|
|
4862
4942
|
}
|
|
4943
|
+
function CheckboxBox({
|
|
4944
|
+
state,
|
|
4945
|
+
disabled = false,
|
|
4946
|
+
focused = false
|
|
4947
|
+
}) {
|
|
4948
|
+
const filled = state === true || state === null;
|
|
4949
|
+
return /* @__PURE__ */ jsx(
|
|
4950
|
+
"span",
|
|
4951
|
+
{
|
|
4952
|
+
className: cn(
|
|
4953
|
+
"flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
|
|
4954
|
+
disabled ? (
|
|
4955
|
+
// 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
|
|
4956
|
+
"cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
|
|
4957
|
+
) : filled ? focused ? "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)]"
|
|
4958
|
+
),
|
|
4959
|
+
children: filled && /* @__PURE__ */ jsx(
|
|
4960
|
+
SIcon,
|
|
4961
|
+
{
|
|
4962
|
+
name: state === null ? "checkboxIndeterminate" : "checkboxCheck",
|
|
4963
|
+
size: 12,
|
|
4964
|
+
color: disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
|
|
4965
|
+
}
|
|
4966
|
+
)
|
|
4967
|
+
}
|
|
4968
|
+
);
|
|
4969
|
+
}
|
|
4863
4970
|
function SRadio({
|
|
4864
4971
|
val,
|
|
4865
4972
|
value,
|
|
@@ -5047,7 +5154,7 @@ function SCheckbox({
|
|
|
5047
5154
|
}
|
|
5048
5155
|
),
|
|
5049
5156
|
label && // 고스트(500) 텍스트로 너비를 고정 → 두께 400↔500 전환 시 레이아웃이 밀리지 않음
|
|
5050
|
-
/* @__PURE__ */ jsxs("span", { className: "grid text-[12px] leading-[20px]", style: { color: labelColor }, children: [
|
|
5157
|
+
/* @__PURE__ */ jsxs("span", { className: "grid text-nowrap text-[12px] leading-[20px]", style: { color: labelColor }, children: [
|
|
5051
5158
|
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "invisible font-medium", style: { gridArea: "1 / 1" }, children: label }),
|
|
5052
5159
|
/* @__PURE__ */ jsx("span", { style: { gridArea: "1 / 1", fontWeight: isChecked ? 500 : 400 }, children: label })
|
|
5053
5160
|
] })
|