sellmate-design-system-react 0.8.3 → 0.9.0

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.
Files changed (73) hide show
  1. package/dist/components/SBarcodeInput/SBarcodeInput.cjs +78 -2
  2. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  3. package/dist/components/SBarcodeInput/SBarcodeInput.js +78 -2
  4. package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
  5. package/dist/components/SChipInput/SChipInput.cjs +78 -2
  6. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  7. package/dist/components/SChipInput/SChipInput.js +78 -2
  8. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  9. package/dist/components/SDatePicker/SDatePicker.cjs +78 -2
  10. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  11. package/dist/components/SDatePicker/SDatePicker.js +78 -2
  12. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  13. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +78 -2
  14. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  15. package/dist/components/SDateRangePicker/SDateRangePicker.js +78 -2
  16. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  17. package/dist/components/SField/SField.cjs +78 -2
  18. package/dist/components/SField/SField.cjs.map +1 -1
  19. package/dist/components/SField/SField.js +78 -2
  20. package/dist/components/SField/SField.js.map +1 -1
  21. package/dist/components/SFilePicker/SFilePicker.cjs +78 -2
  22. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  23. package/dist/components/SFilePicker/SFilePicker.js +78 -2
  24. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  25. package/dist/components/SGnb/SGnb.cjs +58 -16
  26. package/dist/components/SGnb/SGnb.cjs.map +1 -1
  27. package/dist/components/SGnb/SGnb.d.ts +15 -0
  28. package/dist/components/SGnb/SGnb.js +58 -16
  29. package/dist/components/SGnb/SGnb.js.map +1 -1
  30. package/dist/components/SInput/SInput.cjs +78 -2
  31. package/dist/components/SInput/SInput.cjs.map +1 -1
  32. package/dist/components/SInput/SInput.js +78 -2
  33. package/dist/components/SInput/SInput.js.map +1 -1
  34. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +224 -118
  35. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  36. package/dist/components/SKeyValueTable/SKeyValueTable.js +224 -118
  37. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  38. package/dist/components/SNumberInput/SNumberInput.cjs +78 -2
  39. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  40. package/dist/components/SNumberInput/SNumberInput.js +78 -2
  41. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  42. package/dist/components/SSelect/SSelect.cjs +155 -49
  43. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  44. package/dist/components/SSelect/SSelect.js +155 -49
  45. package/dist/components/SSelect/SSelect.js.map +1 -1
  46. package/dist/components/SSelect/select.config.d.ts +7 -0
  47. package/dist/components/STable/STable.cjs +155 -49
  48. package/dist/components/STable/STable.cjs.map +1 -1
  49. package/dist/components/STable/STable.js +155 -49
  50. package/dist/components/STable/STable.js.map +1 -1
  51. package/dist/components/STextarea/STextarea.cjs +78 -2
  52. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  53. package/dist/components/STextarea/STextarea.js +78 -2
  54. package/dist/components/STextarea/STextarea.js.map +1 -1
  55. package/dist/components/STimePicker/STimePicker.cjs +78 -2
  56. package/dist/components/STimePicker/STimePicker.cjs.map +1 -1
  57. package/dist/components/STimePicker/STimePicker.js +78 -2
  58. package/dist/components/STimePicker/STimePicker.js.map +1 -1
  59. package/dist/components/STimeRangePicker/STimeRangePicker.cjs +78 -2
  60. package/dist/components/STimeRangePicker/STimeRangePicker.cjs.map +1 -1
  61. package/dist/components/STimeRangePicker/STimeRangePicker.js +78 -2
  62. package/dist/components/STimeRangePicker/STimeRangePicker.js.map +1 -1
  63. package/dist/components/STooltip/STooltip.cjs +78 -2
  64. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  65. package/dist/components/STooltip/STooltip.d.ts +1 -1
  66. package/dist/components/STooltip/STooltip.js +78 -2
  67. package/dist/components/STooltip/STooltip.js.map +1 -1
  68. package/dist/index.cjs +209 -65
  69. package/dist/index.cjs.map +1 -1
  70. package/dist/index.js +209 -65
  71. package/dist/index.js.map +1 -1
  72. package/dist/styles.css +35 -8
  73. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -4175,7 +4175,14 @@ function useBody(content, message = []) {
4175
4175
  if (content != null) return content;
4176
4176
  if (message.length === 0) return null;
4177
4177
  const singleLine = message.length === 1;
4178
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: singleLine ? "leading-[24px]" : void 0, children: line }, i)) });
4178
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx(
4179
+ "div",
4180
+ {
4181
+ className: singleLine ? "leading-[24px]" : void 0,
4182
+ dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(line) }
4183
+ },
4184
+ i
4185
+ )) });
4179
4186
  }
4180
4187
  var STooltip = react.forwardRef(function STooltip2({
4181
4188
  content,
@@ -4233,7 +4240,7 @@ var STooltip = react.forwardRef(function STooltip2({
4233
4240
  if (disabled || body == null) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: triggerNode });
4234
4241
  const isHover = trigger === "hover";
4235
4242
  const contentClass = cn(
4236
- "z-50 max-w-xs rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px]",
4243
+ "z-50 w-fit whitespace-nowrap rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px]",
4237
4244
  FLOATING_SLIDE_ANIM,
4238
4245
  className
4239
4246
  );
@@ -5188,6 +5195,9 @@ var SGnb = react.forwardRef(function SGnb2({
5188
5195
  onFoldChange,
5189
5196
  onLauncherClick,
5190
5197
  logo,
5198
+ railFooter,
5199
+ menuFooter,
5200
+ foldedFooter,
5191
5201
  ariaLabel = "global navigation",
5192
5202
  className,
5193
5203
  style,
@@ -5341,7 +5351,7 @@ var SGnb = react.forwardRef(function SGnb2({
5341
5351
  className: "shrink-0"
5342
5352
  }
5343
5353
  ),
5344
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap flex flex-no-wrap items-center", children: [
5354
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap flex items-center", children: [
5345
5355
  depth > 1 && iconBranch ? /* @__PURE__ */ jsxRuntime.jsx(
5346
5356
  "div",
5347
5357
  {
@@ -5552,42 +5562,77 @@ var SGnb = react.forwardRef(function SGnb2({
5552
5562
  transition: `transform ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(folded)}`
5553
5563
  },
5554
5564
  children: [
5555
- useRail && // 레일도 내비게이션 landmark nav 세워야 aria-label(레일) 스크린리더에 노출된다
5556
- // (role 없는 ul aria-label 영역명으로 읽히지 않는다). 메뉴 nav 와는 레이블로 구분된다.
5557
- /* @__PURE__ */ jsxRuntime.jsx(
5558
- "nav",
5565
+ useRail && // 레일 컬럼스크롤되는 아이템 목록(nav) 하단 고정 footer 를 한 컬럼에 담는다.
5566
+ // footer 내비게이션이 아니므로 nav(landmark) 밖에 두고, 컬럼 폭·구분선·배경은 래퍼가
5567
+ // 전체 높이에 걸쳐 책임진다(그래야 footer 자리까지 배경·오른쪽 선이 이어진다).
5568
+ /* @__PURE__ */ jsxRuntime.jsxs(
5569
+ "div",
5559
5570
  {
5560
- "aria-label": `${ariaLabel} rail`,
5561
- className: "flex shrink-0 flex-col overflow-y-auto",
5571
+ className: "flex shrink-0 flex-col overflow-hidden",
5562
5572
  style: {
5563
5573
  width: GNB_RAIL_WIDTH,
5564
- padding: GNB_RAIL.padding,
5565
5574
  borderRight: `1px solid ${C.topBorder}`,
5566
5575
  backgroundColor: C.panelBg,
5567
5576
  pointerEvents: "auto"
5568
5577
  },
5569
- children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", style: { gap: GNB_RAIL.gap }, children: railItems.map(renderRailItem) })
5578
+ children: [
5579
+ /* @__PURE__ */ jsxRuntime.jsx(
5580
+ "nav",
5581
+ {
5582
+ "aria-label": `${ariaLabel} rail`,
5583
+ className: cn(
5584
+ "flex min-h-0 flex-1 flex-col overflow-x-hidden overflow-y-auto",
5585
+ SCROLLBAR_CLASS
5586
+ ),
5587
+ style: { padding: GNB_RAIL.padding },
5588
+ children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", style: { gap: GNB_RAIL.gap }, children: railItems.map(renderRailItem) })
5589
+ }
5590
+ ),
5591
+ railFooter && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: railFooter })
5592
+ ]
5570
5593
  }
5571
5594
  ),
5572
- showMenu && /* @__PURE__ */ jsxRuntime.jsx(
5573
- "nav",
5595
+ showMenu && /* @__PURE__ */ jsxRuntime.jsxs(
5596
+ "div",
5574
5597
  {
5575
- "aria-label": ariaLabel,
5576
- className: "flex shrink-0 flex-col overflow-y-auto",
5598
+ className: "flex shrink-0 flex-col overflow-hidden",
5577
5599
  style: {
5578
5600
  width: GNB_MENU_WIDTH,
5579
- paddingTop: M.bodyPaddingY,
5580
- paddingBottom: M.bodyPaddingY,
5581
5601
  backgroundColor: C.panelBg,
5582
5602
  // full 은 상단바가 전폭이라 루트가 오른쪽 선을 못 그린다 → 메뉴 오른쪽(페이지 경계)이 대신 그린다.
5583
5603
  borderRight: header === "full" ? rightBorder : void 0,
5584
5604
  pointerEvents: "auto"
5585
5605
  },
5586
- children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: menuItems.map((item) => renderItem(item, 1)) })
5606
+ children: [
5607
+ /* @__PURE__ */ jsxRuntime.jsx(
5608
+ "nav",
5609
+ {
5610
+ "aria-label": ariaLabel,
5611
+ className: cn(
5612
+ "flex min-h-0 flex-1 flex-col overflow-x-hidden overflow-y-auto",
5613
+ SCROLLBAR_CLASS
5614
+ ),
5615
+ style: {
5616
+ paddingTop: M.bodyPaddingY,
5617
+ paddingBottom: M.bodyPaddingY
5618
+ },
5619
+ children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: menuItems.map((item) => renderItem(item, 1)) })
5620
+ }
5621
+ ),
5622
+ menuFooter && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: menuFooter })
5623
+ ]
5587
5624
  }
5588
5625
  )
5589
5626
  ]
5590
5627
  }
5628
+ ),
5629
+ railFolded && foldedFooter && /* @__PURE__ */ jsxRuntime.jsx(
5630
+ "div",
5631
+ {
5632
+ className: "flex shrink-0 items-center justify-center mb-[24px]",
5633
+ style: { pointerEvents: "auto" },
5634
+ children: foldedFooter
5635
+ }
5591
5636
  )
5592
5637
  ]
5593
5638
  }
@@ -6316,6 +6361,16 @@ function flattenOptions(options, depth = 1) {
6316
6361
  return out;
6317
6362
  }
6318
6363
  var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
6364
+ function collectGroupLeafValues(flat, groupIndex) {
6365
+ const group = flat[groupIndex];
6366
+ if (group == null || !group.isGroup) return [];
6367
+ const values = [];
6368
+ for (let i = groupIndex + 1; i < flat.length; i++) {
6369
+ if (flat[i].depth <= group.depth) break;
6370
+ if (!flat[i].isGroup && !flat[i].disabled) values.push(flat[i].value);
6371
+ }
6372
+ return values;
6373
+ }
6319
6374
  var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
6320
6375
  var extractText = (html) => html.replace(/<[^>]*>/g, "");
6321
6376
  var SEARCH_THRESHOLD = 11;
@@ -6441,16 +6496,31 @@ var SSelect = react.forwardRef(function SSelect2({
6441
6496
  const allValues = selectable.map((o) => o.value);
6442
6497
  const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
6443
6498
  const hasItems = filtered.some((o) => !o.isGroup);
6499
+ const groupLeafValues = react.useMemo(() => {
6500
+ const map = /* @__PURE__ */ new Map();
6501
+ filtered.forEach((o, i) => {
6502
+ if (o.isGroup) map.set(i, collectGroupLeafValues(filtered, i));
6503
+ });
6504
+ return map;
6505
+ }, [filtered]);
6444
6506
  const navigableItems = react.useMemo(() => {
6445
6507
  const items = [];
6446
6508
  if (multi && useSelectAll && allValues.length > 0) {
6447
- items.push({ value: "__select_all__", isSelectAll: true });
6509
+ items.push({ key: "__select_all__", isSelectAll: true });
6448
6510
  }
6449
- filtered.forEach((o) => {
6450
- if (!o.isGroup && !o.disabled) items.push({ value: o.value });
6511
+ filtered.forEach((o, i) => {
6512
+ if (o.disabled) return;
6513
+ if (o.isGroup) {
6514
+ if (!multi) return;
6515
+ const leafValues = groupLeafValues.get(i) ?? [];
6516
+ if (leafValues.length === 0) return;
6517
+ items.push({ key: `__group_${i}__`, isGroup: true, leafValues });
6518
+ } else {
6519
+ items.push({ key: o.value, value: o.value });
6520
+ }
6451
6521
  });
6452
6522
  return items;
6453
- }, [allValues.length, filtered, multi, useSelectAll]);
6523
+ }, [allValues.length, filtered, groupLeafValues, multi, useSelectAll]);
6454
6524
  const triggerText = (() => {
6455
6525
  if (selected.length === 0) return placeholder;
6456
6526
  if (multi) {
@@ -6472,6 +6542,17 @@ var SSelect = react.forwardRef(function SSelect2({
6472
6542
  const handleSelectAll = () => {
6473
6543
  emit2(allSelected ? [] : allValues);
6474
6544
  };
6545
+ const handleToggleGroup = (leafValues) => {
6546
+ if (leafValues.length === 0) return;
6547
+ const selectedSet = new Set(selected);
6548
+ const allSelectedInGroup = leafValues.every((v) => selectedSet.has(v));
6549
+ if (allSelectedInGroup) {
6550
+ const remove = new Set(leafValues);
6551
+ emit2(selected.filter((v) => !remove.has(v)));
6552
+ } else {
6553
+ emit2([...selected, ...leafValues.filter((v) => !selectedSet.has(v))]);
6554
+ }
6555
+ };
6475
6556
  react.useEffect(() => {
6476
6557
  if (!open) {
6477
6558
  setFocusedIndex(-1);
@@ -6512,6 +6593,10 @@ var SSelect = react.forwardRef(function SSelect2({
6512
6593
  handleSelectAll();
6513
6594
  return;
6514
6595
  }
6596
+ if (focused.isGroup) {
6597
+ handleToggleGroup(focused.leafValues ?? []);
6598
+ return;
6599
+ }
6515
6600
  handleSelect(focused.value);
6516
6601
  };
6517
6602
  const handleKeyDown = (event) => {
@@ -6675,20 +6760,20 @@ var SSelect = react.forwardRef(function SSelect2({
6675
6760
  items: filtered,
6676
6761
  multi,
6677
6762
  selected,
6678
- focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
6763
+ groupLeafValues,
6764
+ focusedValue: navigableItems[focusedIndex]?.key,
6679
6765
  showSelectAll: multi && useSelectAll && allValues.length > 0,
6680
6766
  selectAllLabel: allSelectedLabel,
6681
6767
  selectAllSelected: allSelected,
6682
6768
  onSelectAll: handleSelectAll,
6683
6769
  onSelect: handleSelect,
6684
- onHoverValue: (value2) => {
6685
- if (value2 == null) {
6770
+ onToggleGroup: handleToggleGroup,
6771
+ onHoverValue: (key) => {
6772
+ if (key == null) {
6686
6773
  setFocusedIndex(-1);
6687
6774
  return;
6688
6775
  }
6689
- const index = navigableItems.findIndex(
6690
- (item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
6691
- );
6776
+ const index = navigableItems.findIndex((item) => item.key === key);
6692
6777
  if (index >= 0) setFocusedIndex(index);
6693
6778
  }
6694
6779
  }
@@ -6707,20 +6792,23 @@ function ItemList({
6707
6792
  items,
6708
6793
  multi,
6709
6794
  selected,
6795
+ groupLeafValues,
6710
6796
  focusedValue,
6711
6797
  showSelectAll,
6712
6798
  selectAllLabel,
6713
6799
  selectAllSelected,
6714
6800
  onSelectAll,
6715
6801
  onSelect,
6802
+ onToggleGroup,
6716
6803
  onHoverValue
6717
6804
  }) {
6805
+ const selectedSet = react.useMemo(() => new Set(selected), [selected]);
6718
6806
  const itemBase = cn(
6719
6807
  "group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
6720
6808
  "text-[color:var(--cmp-listItem-content-default)]",
6721
6809
  "disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
6722
6810
  );
6723
- 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)]";
6811
+ 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)]";
6724
6812
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
6725
6813
  showSelectAll && /* @__PURE__ */ jsxRuntime.jsxs(
6726
6814
  "button",
@@ -6737,23 +6825,7 @@ function ItemList({
6737
6825
  onMouseLeave: () => onHoverValue(null),
6738
6826
  onClick: onSelectAll,
6739
6827
  children: [
6740
- /* @__PURE__ */ jsxRuntime.jsx(
6741
- "span",
6742
- {
6743
- className: cn(
6744
- "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
6745
- 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)]"
6746
- ),
6747
- children: selectAllSelected && /* @__PURE__ */ jsxRuntime.jsx(
6748
- SIcon,
6749
- {
6750
- name: "checkboxCheck",
6751
- size: 12,
6752
- color: "var(--cmp-checkbox-checked-icon-default)"
6753
- }
6754
- )
6755
- }
6756
- ),
6828
+ /* @__PURE__ */ jsxRuntime.jsx(CheckboxBox, { state: selectAllSelected, focused: focusedValue === "__select_all__" }),
6757
6829
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
6758
6830
  ]
6759
6831
  }
@@ -6763,12 +6835,71 @@ function ItemList({
6763
6835
  const rowKey = `${o.value}-${idx}`;
6764
6836
  if (o.isGroup) {
6765
6837
  const isDepth1 = o.depth === 1;
6838
+ 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";
6839
+ if (multi) {
6840
+ const leafValues = groupLeafValues.get(idx) ?? [];
6841
+ const total = leafValues.length;
6842
+ const selectedCount = leafValues.reduce((n, v) => selectedSet.has(v) ? n + 1 : n, 0);
6843
+ const groupState = total === 0 || selectedCount === 0 ? false : selectedCount === total ? true : null;
6844
+ const groupDisabled = o.disabled || total === 0;
6845
+ const groupFocused = focusedValue === `__group_${idx}__`;
6846
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6847
+ "button",
6848
+ {
6849
+ type: "button",
6850
+ role: "option",
6851
+ "aria-selected": groupState === true,
6852
+ disabled: groupDisabled,
6853
+ "data-select-focused": groupFocused ? "true" : void 0,
6854
+ "data-select-active": groupFocused ? "true" : void 0,
6855
+ className: cn(
6856
+ "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)]",
6857
+ groupTone,
6858
+ groupDisabled ? "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]" : "cursor-pointer",
6859
+ groupDisabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]",
6860
+ // 포커스 하이라이트는 depth 배경보다 뒤에 둬 tailwind-merge 로 이기게 한다.
6861
+ groupFocused && !groupDisabled && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
6862
+ ),
6863
+ style: { paddingLeft: padLeft },
6864
+ onMouseEnter: () => onHoverValue(`__group_${idx}__`),
6865
+ onMouseLeave: () => onHoverValue(null),
6866
+ onClick: () => onToggleGroup(leafValues),
6867
+ children: [
6868
+ /* @__PURE__ */ jsxRuntime.jsx(CheckboxBox, { state: groupState, disabled: groupDisabled, focused: groupFocused }),
6869
+ isHtmlLabel(o.label) ? /* @__PURE__ */ jsxRuntime.jsx(
6870
+ "span",
6871
+ {
6872
+ className: "min-w-0 truncate",
6873
+ dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
6874
+ }
6875
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: o.label }),
6876
+ total > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
6877
+ "span",
6878
+ {
6879
+ className: cn(
6880
+ "shrink-0 font-medium",
6881
+ groupFocused ? "text-white" : "text-[color:var(--cmp-listItem-content-disabled)]"
6882
+ ),
6883
+ children: [
6884
+ "(",
6885
+ selectedCount,
6886
+ "/",
6887
+ total,
6888
+ ")"
6889
+ ]
6890
+ }
6891
+ )
6892
+ ]
6893
+ },
6894
+ rowKey
6895
+ );
6896
+ }
6766
6897
  return /* @__PURE__ */ jsxRuntime.jsx(
6767
6898
  "div",
6768
6899
  {
6769
6900
  className: cn(
6770
6901
  "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
6771
- 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",
6902
+ groupTone,
6772
6903
  // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
6773
6904
  // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
6774
6905
  o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
@@ -6804,25 +6935,11 @@ function ItemList({
6804
6935
  onClick: () => onSelect(o.value),
6805
6936
  children: [
6806
6937
  multi && /* @__PURE__ */ jsxRuntime.jsx(
6807
- "span",
6938
+ CheckboxBox,
6808
6939
  {
6809
- className: cn(
6810
- "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
6811
- o.disabled ? (
6812
- // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
6813
- "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
6814
- ) : cn(
6815
- 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)]"
6816
- )
6817
- ),
6818
- children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
6819
- SIcon,
6820
- {
6821
- name: "checkboxCheck",
6822
- size: 12,
6823
- color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
6824
- }
6825
- )
6940
+ state: isSelected,
6941
+ disabled: o.disabled,
6942
+ focused: focusedValue === o.value
6826
6943
  }
6827
6944
  ),
6828
6945
  isHtmlLabel(o.label) ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -6839,6 +6956,33 @@ function ItemList({
6839
6956
  })
6840
6957
  ] });
6841
6958
  }
6959
+ function CheckboxBox({
6960
+ state: state2,
6961
+ disabled = false,
6962
+ focused = false
6963
+ }) {
6964
+ const filled = state2 === true || state2 === null;
6965
+ return /* @__PURE__ */ jsxRuntime.jsx(
6966
+ "span",
6967
+ {
6968
+ className: cn(
6969
+ "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
6970
+ disabled ? (
6971
+ // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
6972
+ "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
6973
+ ) : 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)]"
6974
+ ),
6975
+ children: filled && /* @__PURE__ */ jsxRuntime.jsx(
6976
+ SIcon,
6977
+ {
6978
+ name: state2 === null ? "checkboxIndeterminate" : "checkboxCheck",
6979
+ size: 12,
6980
+ color: disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
6981
+ }
6982
+ )
6983
+ }
6984
+ );
6985
+ }
6842
6986
  var DEFAULT_ROW_HEIGHT = 44;
6843
6987
  var DENSE_ROW_HEIGHT = 32;
6844
6988
  var SELECTABLE_COLUMN_WIDTH = 48;