sellmate-design-system-react 0.1.10 → 0.2.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 (163) hide show
  1. package/dist/components/SActionModal/SActionModal.cjs +10 -4
  2. package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
  3. package/dist/components/SActionModal/SActionModal.js +10 -4
  4. package/dist/components/SActionModal/SActionModal.js.map +1 -1
  5. package/dist/components/SBarcodeInput/SBarcodeInput.cjs +22 -5
  6. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  7. package/dist/components/SBarcodeInput/SBarcodeInput.d.ts +3 -1
  8. package/dist/components/SBarcodeInput/SBarcodeInput.js +22 -5
  9. package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
  10. package/dist/components/SCalendar/SCalendar.cjs +9 -3
  11. package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
  12. package/dist/components/SCalendar/SCalendar.js +9 -3
  13. package/dist/components/SCalendar/SCalendar.js.map +1 -1
  14. package/dist/components/SChip/SChip.cjs +9 -3
  15. package/dist/components/SChip/SChip.cjs.map +1 -1
  16. package/dist/components/SChip/SChip.js +9 -3
  17. package/dist/components/SChip/SChip.js.map +1 -1
  18. package/dist/components/SChipInput/SChipInput.cjs +44 -13
  19. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  20. package/dist/components/SChipInput/SChipInput.d.ts +5 -0
  21. package/dist/components/SChipInput/SChipInput.js +44 -13
  22. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  23. package/dist/components/SChipInput/index.d.ts +1 -1
  24. package/dist/components/SCircleProgress/SCircleProgress.cjs +6 -2
  25. package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
  26. package/dist/components/SCircleProgress/SCircleProgress.js +6 -2
  27. package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
  28. package/dist/components/SConfirmModal/SConfirmModal.cjs +20 -13
  29. package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
  30. package/dist/components/SConfirmModal/SConfirmModal.d.ts +7 -4
  31. package/dist/components/SConfirmModal/SConfirmModal.js +20 -13
  32. package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
  33. package/dist/components/SDatePicker/SDatePicker.cjs +22 -5
  34. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  35. package/dist/components/SDatePicker/SDatePicker.d.ts +3 -1
  36. package/dist/components/SDatePicker/SDatePicker.js +22 -5
  37. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  38. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +22 -5
  39. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  40. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +3 -1
  41. package/dist/components/SDateRangePicker/SDateRangePicker.js +22 -5
  42. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  43. package/dist/components/SDropdownButton/SDropdownButton.cjs +3 -1
  44. package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
  45. package/dist/components/SDropdownButton/SDropdownButton.js +3 -1
  46. package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
  47. package/dist/components/SField/SField.cjs +20 -5
  48. package/dist/components/SField/SField.cjs.map +1 -1
  49. package/dist/components/SField/SField.d.ts +4 -1
  50. package/dist/components/SField/SField.js +20 -5
  51. package/dist/components/SField/SField.js.map +1 -1
  52. package/dist/components/SFilePicker/SFilePicker.cjs +23 -6
  53. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  54. package/dist/components/SFilePicker/SFilePicker.d.ts +3 -1
  55. package/dist/components/SFilePicker/SFilePicker.js +23 -6
  56. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  57. package/dist/components/SGhostButton/SGhostButton.cjs +9 -3
  58. package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
  59. package/dist/components/SGhostButton/SGhostButton.d.ts +5 -1
  60. package/dist/components/SGhostButton/SGhostButton.js +9 -3
  61. package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
  62. package/dist/components/SGnb/SGnb.cjs +3297 -0
  63. package/dist/components/SGnb/SGnb.cjs.map +1 -0
  64. package/dist/components/SGnb/SGnb.d.ts +35 -0
  65. package/dist/components/SGnb/SGnb.js +3295 -0
  66. package/dist/components/SGnb/SGnb.js.map +1 -0
  67. package/dist/components/SGnb/gnb.config.d.ts +63 -0
  68. package/dist/components/SGnb/index.d.ts +2 -0
  69. package/dist/components/SGuide/SGuide.cjs +9 -3
  70. package/dist/components/SGuide/SGuide.cjs.map +1 -1
  71. package/dist/components/SGuide/SGuide.js +9 -3
  72. package/dist/components/SGuide/SGuide.js.map +1 -1
  73. package/dist/components/SInput/SInput.cjs +22 -5
  74. package/dist/components/SInput/SInput.cjs.map +1 -1
  75. package/dist/components/SInput/SInput.d.ts +4 -1
  76. package/dist/components/SInput/SInput.js +22 -5
  77. package/dist/components/SInput/SInput.js.map +1 -1
  78. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +511 -209
  79. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  80. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +27 -6
  81. package/dist/components/SKeyValueTable/SKeyValueTable.js +510 -208
  82. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  83. package/dist/components/SLayout/SLayout.cjs +79 -0
  84. package/dist/components/SLayout/SLayout.cjs.map +1 -0
  85. package/dist/components/SLayout/SLayout.d.ts +41 -0
  86. package/dist/components/SLayout/SLayout.js +75 -0
  87. package/dist/components/SLayout/SLayout.js.map +1 -0
  88. package/dist/components/SLayout/index.d.ts +1 -0
  89. package/dist/components/SLoadingContainer/SLoadingContainer.cjs +6 -2
  90. package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
  91. package/dist/components/SLoadingContainer/SLoadingContainer.js +6 -2
  92. package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
  93. package/dist/components/SLoadingContainer/loading.cjs +6 -2
  94. package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
  95. package/dist/components/SLoadingContainer/loading.js +6 -2
  96. package/dist/components/SLoadingContainer/loading.js.map +1 -1
  97. package/dist/components/SLoadingModal/SLoadingModal.cjs +16 -6
  98. package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
  99. package/dist/components/SLoadingModal/SLoadingModal.js +16 -6
  100. package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
  101. package/dist/components/SModalContainer/SModalContainer.cjs +10 -4
  102. package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
  103. package/dist/components/SModalContainer/SModalContainer.d.ts +10 -0
  104. package/dist/components/SModalContainer/SModalContainer.js +10 -4
  105. package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
  106. package/dist/components/SNumberInput/SNumberInput.cjs +22 -5
  107. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  108. package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
  109. package/dist/components/SNumberInput/SNumberInput.js +22 -5
  110. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  111. package/dist/components/SPage/SPage.cjs +86 -0
  112. package/dist/components/SPage/SPage.cjs.map +1 -0
  113. package/dist/components/SPage/SPage.d.ts +13 -0
  114. package/dist/components/SPage/SPage.js +84 -0
  115. package/dist/components/SPage/SPage.js.map +1 -0
  116. package/dist/components/SPage/index.d.ts +1 -0
  117. package/dist/components/SPopover/SPopover.cjs +9 -3
  118. package/dist/components/SPopover/SPopover.cjs.map +1 -1
  119. package/dist/components/SPopover/SPopover.js +9 -3
  120. package/dist/components/SPopover/SPopover.js.map +1 -1
  121. package/dist/components/SPortal/SPortal.cjs +3 -1
  122. package/dist/components/SPortal/SPortal.cjs.map +1 -1
  123. package/dist/components/SPortal/SPortal.js +3 -1
  124. package/dist/components/SPortal/SPortal.js.map +1 -1
  125. package/dist/components/SSelect/SSelect.cjs +545 -265
  126. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  127. package/dist/components/SSelect/SSelect.d.ts +4 -2
  128. package/dist/components/SSelect/SSelect.js +546 -265
  129. package/dist/components/SSelect/SSelect.js.map +1 -1
  130. package/dist/components/STable/STable.cjs +552 -268
  131. package/dist/components/STable/STable.cjs.map +1 -1
  132. package/dist/components/STable/STable.js +553 -268
  133. package/dist/components/STable/STable.js.map +1 -1
  134. package/dist/components/STextLink/STextLink.cjs +2 -0
  135. package/dist/components/STextLink/STextLink.cjs.map +1 -1
  136. package/dist/components/STextLink/STextLink.js +2 -0
  137. package/dist/components/STextLink/STextLink.js.map +1 -1
  138. package/dist/components/STextarea/STextarea.cjs +22 -5
  139. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  140. package/dist/components/STextarea/STextarea.d.ts +2 -0
  141. package/dist/components/STextarea/STextarea.js +22 -5
  142. package/dist/components/STextarea/STextarea.js.map +1 -1
  143. package/dist/components/SToast/SToast.cjs +9 -3
  144. package/dist/components/SToast/SToast.cjs.map +1 -1
  145. package/dist/components/SToast/SToast.js +9 -3
  146. package/dist/components/SToast/SToast.js.map +1 -1
  147. package/dist/components/SToast/SToastContainer.cjs +9 -3
  148. package/dist/components/SToast/SToastContainer.cjs.map +1 -1
  149. package/dist/components/SToast/SToastContainer.js +9 -3
  150. package/dist/components/SToast/SToastContainer.js.map +1 -1
  151. package/dist/components/STooltip/STooltip.cjs +9 -3
  152. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  153. package/dist/components/STooltip/STooltip.js +9 -3
  154. package/dist/components/STooltip/STooltip.js.map +1 -1
  155. package/dist/index.cjs +840 -199
  156. package/dist/index.cjs.map +1 -1
  157. package/dist/index.d.ts +3 -1
  158. package/dist/index.js +832 -200
  159. package/dist/index.js.map +1 -1
  160. package/dist/lib/portal-container.d.ts +7 -0
  161. package/dist/styles.css +253 -63
  162. package/dist/theme.css +66 -13
  163. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { clsx } from 'clsx';
2
2
  import { extendTailwindMerge } from 'tailwind-merge';
3
- import { forwardRef, useState, useEffect, useRef, useCallback, createContext, useImperativeHandle, useMemo, useId, useLayoutEffect, useContext, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
3
+ import { forwardRef, useState, useEffect, useRef, useCallback, createContext, useImperativeHandle, useMemo, useContext, useId, useLayoutEffect, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { createPortal } from 'react-dom';
6
6
  import { createRoot } from 'react-dom/client';
@@ -3319,8 +3319,12 @@ var SCircleProgress = forwardRef(
3319
3319
  "span",
3320
3320
  {
3321
3321
  className: cn(
3322
- "text-center text-[16px] font-bold leading-[26px]",
3323
- innerValue && "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
3322
+ "text-center text-[16px] font-bold",
3323
+ innerValue ? (
3324
+ // leading-[normal]: 26px leading 은 폰트 content(19px) 위아래로 홀수(7px)만큼 남아
3325
+ // 3px/4px 로 쪼개지고 글자가 링 중심보다 위로 뜬다. (leading-normal 은 1.5 라 동일 문제)
3326
+ "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 leading-[normal]"
3327
+ ) : "leading-[26px]"
3324
3328
  ),
3325
3329
  style: { color: percentColor },
3326
3330
  children: [
@@ -3505,10 +3509,15 @@ var FLOATING_SLIDE_ANIM = [
3505
3509
  "data-[state=closed]:data-[side=right]:animate-floating-out-right"
3506
3510
  ].join(" ");
3507
3511
  var PortalContainerContext = createContext(null);
3512
+ var InsideModalContext = createContext(false);
3508
3513
  var PortalContainerProvider = PortalContainerContext.Provider;
3514
+ var InsideModalProvider = InsideModalContext.Provider;
3509
3515
  function usePortalContainer() {
3510
3516
  return useContext(PortalContainerContext) ?? void 0;
3511
3517
  }
3518
+ function useInsideModal() {
3519
+ return useContext(InsideModalContext);
3520
+ }
3512
3521
  var SIZE_CONFIG = {
3513
3522
  sm: {
3514
3523
  gap: 4,
@@ -3881,7 +3890,7 @@ var SDropdownButton = forwardRef(
3881
3890
  ...style
3882
3891
  },
3883
3892
  children: [
3884
- /* @__PURE__ */ jsx("span", { className: "truncate", children: label }),
3893
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-center", children: label }),
3885
3894
  caret
3886
3895
  ]
3887
3896
  }
@@ -3909,6 +3918,8 @@ function SGhostButton({
3909
3918
  tooltipText,
3910
3919
  disabled = false,
3911
3920
  onClick,
3921
+ anchorClassName,
3922
+ anchorStyle,
3912
3923
  className,
3913
3924
  style
3914
3925
  }) {
@@ -3931,6 +3942,8 @@ function SGhostButton({
3931
3942
  };
3932
3943
  const s = SIZE2[size];
3933
3944
  const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
3945
+ const text = (tooltipText ?? "").trim();
3946
+ const hasTooltip = text !== "";
3934
3947
  const btn = /* @__PURE__ */ jsxs(
3935
3948
  "button",
3936
3949
  {
@@ -3971,13 +3984,13 @@ function SGhostButton({
3971
3984
  ]
3972
3985
  }
3973
3986
  );
3974
- const text = (tooltipText ?? "").trim();
3975
- if (text === "") return btn;
3987
+ if (!hasTooltip) return btn;
3976
3988
  return /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: setOpen, children: [
3977
3989
  /* @__PURE__ */ jsx(RPopover2.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
3978
3990
  "span",
3979
3991
  {
3980
- className: "inline-flex",
3992
+ className: cn("inline-flex", anchorClassName),
3993
+ style: anchorStyle,
3981
3994
  onMouseEnter: openNow,
3982
3995
  onMouseLeave: startHide,
3983
3996
  onFocus: openNow,
@@ -4378,7 +4391,7 @@ function SPortal({
4378
4391
  onCloseAutoFocus,
4379
4392
  onClick: autoClose ? () => onClose?.() : void 0,
4380
4393
  className: cn(
4381
- "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) py-4 shadow-normal-sm",
4394
+ "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm",
4382
4395
  maxHeight != null && "overflow-y-auto",
4383
4396
  fit && "min-w-(--radix-popover-trigger-width)",
4384
4397
  FLOATING_SLIDE_ANIM,
@@ -4619,7 +4632,7 @@ function SModalContainer({
4619
4632
  className: "absolute right-[16px] top-[16px] z-10"
4620
4633
  }
4621
4634
  ),
4622
- /* @__PURE__ */ jsx(PortalContainerProvider, { value: portalContainer, children })
4635
+ /* @__PURE__ */ jsx(InsideModalProvider, { value: true, children: /* @__PURE__ */ jsx(PortalContainerProvider, { value: portalContainer, children }) })
4623
4636
  ]
4624
4637
  }
4625
4638
  )
@@ -4665,7 +4678,8 @@ function SConfirmModal({
4665
4678
  tagSize = "sm",
4666
4679
  tagColor = "grey",
4667
4680
  slotLabel,
4668
- children,
4681
+ tagSlot,
4682
+ optionSlot,
4669
4683
  mainButtonLabel = "\uD655\uC778",
4670
4684
  mainButtonName,
4671
4685
  subButtonLabel,
@@ -4675,6 +4689,9 @@ function SConfirmModal({
4675
4689
  }) {
4676
4690
  const iconName = TYPE_ICON[type];
4677
4691
  const mainStyle = MAIN_BUTTON_STYLE[mainButtonName ?? DEFAULT_MAIN_BUTTON[type]];
4692
+ const tagNode = tagSlot ?? (tagLabel ? /* @__PURE__ */ jsx(STag, { shape: tagShape, size: tagSize, color: tagColor, label: tagLabel }) : null);
4693
+ const optionNode = optionSlot ?? (slotLabel ? /* @__PURE__ */ jsx("span", { children: slotLabel }) : null);
4694
+ const hasBody = topMessage.length > 0 || bottomMessage.length > 0 || tagNode != null || optionNode != null;
4678
4695
  return /* @__PURE__ */ jsx(
4679
4696
  SModalContainer,
4680
4697
  {
@@ -4700,15 +4717,12 @@ function SConfirmModal({
4700
4717
  }
4701
4718
  )
4702
4719
  ] }),
4703
- (topMessage.length > 0 || bottomMessage.length > 0 || tagLabel || slotLabel || children) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-[20px] text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-modal-confirm-message-color)]", children: [
4720
+ hasBody && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-[20px] text-center text-[12px] font-normal leading-[20px] text-[color:var(--cmp-modal-confirm-message-color)] w-full", children: [
4704
4721
  topMessage.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[2px]", children: topMessage.map((m, i) => /* @__PURE__ */ jsx("p", { className: "m-0", children: m }, i)) }),
4705
- (tagLabel || slotLabel || children) && // content-box: 흰 박스 + 1px 테두리 + 패딩 + 14/700 (원본 sd-confirm-modal__content-box)
4706
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-[8px] rounded-[8px] border border-solid border-[color:var(--color-grey-30)] bg-white px-[16px] py-[12px] text-[14px] font-bold leading-[22px]", children: [
4707
- tagLabel && /* @__PURE__ */ jsx(STag, { shape: tagShape, size: tagSize, color: tagColor, label: tagLabel }),
4708
- slotLabel && /* @__PURE__ */ jsx("span", { children: slotLabel }),
4709
- children
4710
- ] }),
4711
- bottomMessage.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[2px]", children: bottomMessage.map((m, i) => /* @__PURE__ */ jsx("p", { className: "m-0", children: m }, i)) })
4722
+ tagNode != null && // tag 위치 content-box: 흰 박스 + 1px 테두리 + 패딩 + 14/700 (원본 sd-confirm-modal__content-box)
4723
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center gap-[8px] rounded-[8px] border border-solid border-[color:var(--color-grey-30)] bg-white px-[16px] py-[12px] text-[14px] font-bold leading-[22px] w-full", children: tagNode }),
4724
+ bottomMessage.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[2px]", children: bottomMessage.map((m, i) => /* @__PURE__ */ jsx("p", { className: "m-0", children: m }, i)) }),
4725
+ optionNode != null && optionNode
4712
4726
  ] })
4713
4727
  ] }),
4714
4728
  /* @__PURE__ */ jsxs("div", { className: "flex gap-[8px]", children: [
@@ -4859,6 +4873,384 @@ function SLoadingModal({
4859
4873
  );
4860
4874
  }
4861
4875
 
4876
+ // src/components/SGnb/gnb.config.ts
4877
+ var belt = (p) => `var(--cmp-gnbBelt-${p})`;
4878
+ var box = (p) => `var(--cmp-gnbBox-${p})`;
4879
+ var light = (p) => `var(--cmp-light-${p})`;
4880
+ var dark = (p) => `var(--cmp-dark-${p})`;
4881
+ var GNB_LAYOUT = {
4882
+ // 사용자 명명: type="box" → gnbBox 토큰(둥금), type="belt" → gnbBelt 토큰(각짐)
4883
+ box: {
4884
+ topPaddingX: box("paddingX"),
4885
+ topGap: box("gap"),
4886
+ // 상단바 gap (버튼 간 세부 gap 은 --cmp-gnbBox-button-gap)
4887
+ bodyPadding: box("menu-body-padding"),
4888
+ itemPaddingRight: box("item-paddingRight"),
4889
+ itemGap: box("item-gap"),
4890
+ itemRadius: box("item-radius"),
4891
+ itemArrow: box("item-arrow"),
4892
+ depth1PaddingLeft: box("item-depth1-paddingLeft"),
4893
+ depth2PaddingLeft: box("item-depth2-paddingLeft"),
4894
+ depth3PaddingLeft: box("item-depth3-paddingLeft"),
4895
+ depth1Icon: box("item-depth1-icon"),
4896
+ depth1FontSize: 14,
4897
+ // token: gnbBox.item.typography.bold.fontSize
4898
+ depth1LineHeight: 24,
4899
+ // token: gnbBox.item.typography.bold.lineHeight
4900
+ depth1FontWeight: 700,
4901
+ itemFontSize: 14,
4902
+ // token: gnbBox.item.typography.default.fontSize
4903
+ itemLineHeight: 24
4904
+ // token: gnbBox.item.typography.default.lineHeight
4905
+ },
4906
+ belt: {
4907
+ topPaddingX: belt("top-paddingX"),
4908
+ topGap: belt("top-gap"),
4909
+ bodyPadding: belt("body-paddingY"),
4910
+ itemPaddingRight: belt("item-paddingRight"),
4911
+ itemGap: belt("item-gap"),
4912
+ itemRadius: "0px",
4913
+ // belt 토큰에 radius 없음 → 풀폭 행
4914
+ itemArrow: belt("item-icon"),
4915
+ depth1PaddingLeft: belt("item-depth1-paddingLeft"),
4916
+ depth2PaddingLeft: belt("item-depth2-paddingLeft"),
4917
+ depth3PaddingLeft: belt("item-depth3-paddingLeft"),
4918
+ depth1Icon: box("item-depth1-icon"),
4919
+ // belt 토큰에 depth1-icon 없음 → box 값(20) 공유
4920
+ depth1FontSize: 13,
4921
+ // token: gnbBelt.item.depth1.typography.fontSize
4922
+ depth1LineHeight: 22,
4923
+ // token: gnbBelt.item.depth1.typography.lineHeight
4924
+ depth1FontWeight: 700,
4925
+ itemFontSize: 12,
4926
+ // token: gnbBelt.item.typography.default.fontSize
4927
+ itemLineHeight: 20
4928
+ // token: gnbBelt.item.typography.default.lineHeight
4929
+ }
4930
+ };
4931
+ var GNB_COLORS = {
4932
+ light: {
4933
+ panelBg: light("bg"),
4934
+ topBorder: light("divider"),
4935
+ itemBgDefault: light("item-bg-default"),
4936
+ itemBgHover: light("item-bg-hover"),
4937
+ itemBgSelected: light("item-bg-selected"),
4938
+ itemColorDefault: light("item-fg-default"),
4939
+ itemColorActive: light("item-fg-selected")
4940
+ },
4941
+ dark: {
4942
+ panelBg: dark("bg"),
4943
+ topBorder: dark("divider"),
4944
+ itemBgDefault: dark("item-bg-default"),
4945
+ itemBgHover: dark("item-bg-hover"),
4946
+ itemBgSelected: dark("item-bg-selected"),
4947
+ itemColorDefault: dark("item-fg-default"),
4948
+ itemColorActive: dark("item-fg-selected")
4949
+ }
4950
+ };
4951
+ var GNB_COMMON = {
4952
+ topHeight: box("top-height"),
4953
+ // 56 (box=belt 동일)
4954
+ logoHeight: box("logo-height"),
4955
+ // 32
4956
+ itemPaddingY: box("item-paddingY"),
4957
+ // 6
4958
+ foldWidth: belt("fold-width")
4959
+ // 48 (belt 토큰 공유)
4960
+ };
4961
+ var GNB_MENU_WIDTH = 240;
4962
+ function findGnbAncestors(items, value) {
4963
+ const walk = (list, trail) => {
4964
+ for (const item of list) {
4965
+ if (item.value === value) return trail;
4966
+ if (item.children) {
4967
+ const found = walk(item.children, [...trail, item.value]);
4968
+ if (found) return found;
4969
+ }
4970
+ }
4971
+ return null;
4972
+ };
4973
+ return walk(items, []) ?? [];
4974
+ }
4975
+ var SLayoutContext = createContext(null);
4976
+ function useSLayout() {
4977
+ const ctx = useContext(SLayoutContext);
4978
+ if (!ctx) throw new Error("useSLayout \uC740 <SLayout> \uC548\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
4979
+ return ctx;
4980
+ }
4981
+ var SLayout = forwardRef(function SLayout2({
4982
+ type = "box",
4983
+ folded,
4984
+ defaultFolded = false,
4985
+ onFoldedChange,
4986
+ className,
4987
+ style,
4988
+ children,
4989
+ ...rest
4990
+ }, ref) {
4991
+ const [uncontrolledFolded, setUncontrolledFolded] = useState(defaultFolded);
4992
+ const isControlled = folded !== void 0;
4993
+ const currentFolded = isControlled ? folded : uncontrolledFolded;
4994
+ const setFolded = useCallback(
4995
+ (next) => {
4996
+ if (!isControlled) setUncontrolledFolded(next);
4997
+ onFoldedChange?.(next);
4998
+ },
4999
+ [isControlled, onFoldedChange]
5000
+ );
5001
+ const ctx = useMemo(
5002
+ () => ({ type, folded: currentFolded, setFolded }),
5003
+ [type, currentFolded, setFolded]
5004
+ );
5005
+ const isBox = type === "box";
5006
+ const frameStyle = isBox ? {
5007
+ gridTemplateColumns: `${currentFolded ? 0 : GNB_MENU_WIDTH}px 1fr`,
5008
+ gridTemplateRows: `${GNB_COMMON.topHeight} 1fr`
5009
+ } : void 0;
5010
+ return /* @__PURE__ */ jsx(SLayoutContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
5011
+ "div",
5012
+ {
5013
+ ref,
5014
+ "data-type": type,
5015
+ className: cn(
5016
+ "box-border h-full overflow-hidden",
5017
+ isBox ? "grid" : "flex flex-row",
5018
+ className
5019
+ ),
5020
+ style: { ...frameStyle, ...style },
5021
+ ...rest,
5022
+ children
5023
+ }
5024
+ ) });
5025
+ });
5026
+ var DEPTH_PADDING_LEFT = (L, depth) => depth === 1 ? L.depth1PaddingLeft : depth === 2 ? L.depth2PaddingLeft : L.depth3PaddingLeft;
5027
+ var SGnb = forwardRef(function SGnb2({
5028
+ type: typeProp,
5029
+ color = "light",
5030
+ items,
5031
+ value = "",
5032
+ onValueChange,
5033
+ folded: foldedProp,
5034
+ onFoldChange,
5035
+ onLauncherClick,
5036
+ logo,
5037
+ search,
5038
+ className,
5039
+ style
5040
+ }, ref) {
5041
+ const layout = useContext(SLayoutContext);
5042
+ const type = typeProp ?? layout?.type ?? "box";
5043
+ const folded = foldedProp ?? layout?.folded ?? false;
5044
+ const L = GNB_LAYOUT[type];
5045
+ const C = GNB_COLORS[color];
5046
+ const [expandedKeys, setExpandedKeys] = useState(
5047
+ () => new Set(findGnbAncestors(items, value))
5048
+ );
5049
+ const [hoveredKey, setHoveredKey] = useState(null);
5050
+ useEffect(() => {
5051
+ const ancestors = findGnbAncestors(items, value);
5052
+ if (ancestors.length === 0) return;
5053
+ setExpandedKeys((prev) => {
5054
+ const next = new Set(prev);
5055
+ ancestors.forEach((a) => next.add(a));
5056
+ return next;
5057
+ });
5058
+ }, [items, value]);
5059
+ const toggleExpand = (key) => {
5060
+ setExpandedKeys((prev) => {
5061
+ const next = new Set(prev);
5062
+ if (next.has(key)) next.delete(key);
5063
+ else next.add(key);
5064
+ return next;
5065
+ });
5066
+ };
5067
+ const handleItemClick = (item) => {
5068
+ if (item.disabled) return;
5069
+ if (item.children && item.children.length > 0) {
5070
+ toggleExpand(item.value);
5071
+ return;
5072
+ }
5073
+ onValueChange?.(item.value);
5074
+ };
5075
+ const handleFoldToggle = () => {
5076
+ const next = !folded;
5077
+ onFoldChange?.(next);
5078
+ layout?.setFolded(next);
5079
+ };
5080
+ const iconBtnClass = "inline-flex shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-1.5";
5081
+ const renderItem = (item, depth) => {
5082
+ const hasChildren = !!(item.children && item.children.length > 0);
5083
+ const isSelected = !hasChildren && item.value === value;
5084
+ const isExpanded = expandedKeys.has(item.value);
5085
+ const isHovered = hoveredKey === item.value;
5086
+ const bg = isSelected ? C.itemBgSelected : isHovered && !item.disabled ? C.itemBgHover : C.itemBgDefault;
5087
+ const itemStyle = {
5088
+ gap: L.itemGap,
5089
+ paddingTop: GNB_COMMON.itemPaddingY,
5090
+ paddingBottom: GNB_COMMON.itemPaddingY,
5091
+ paddingRight: L.itemPaddingRight,
5092
+ paddingLeft: DEPTH_PADDING_LEFT(L, depth),
5093
+ marginLeft: L.bodyPadding,
5094
+ marginRight: L.bodyPadding,
5095
+ borderRadius: L.itemRadius,
5096
+ backgroundColor: bg,
5097
+ color: isSelected ? C.itemColorActive : C.itemColorDefault,
5098
+ fontSize: depth === 1 ? L.depth1FontSize : L.itemFontSize,
5099
+ lineHeight: `${depth === 1 ? L.depth1LineHeight : L.itemLineHeight}px`,
5100
+ fontWeight: depth === 1 || isSelected ? L.depth1FontWeight : 400,
5101
+ opacity: item.disabled ? 0.4 : 1,
5102
+ cursor: item.disabled ? "not-allowed" : "pointer"
5103
+ };
5104
+ return /* @__PURE__ */ jsxs("div", { children: [
5105
+ /* @__PURE__ */ jsxs(
5106
+ "div",
5107
+ {
5108
+ role: hasChildren ? "button" : "menuitem",
5109
+ tabIndex: item.disabled ? -1 : 0,
5110
+ "aria-disabled": item.disabled || void 0,
5111
+ "aria-expanded": hasChildren ? isExpanded : void 0,
5112
+ "aria-current": isSelected ? "page" : void 0,
5113
+ className: "box-border flex select-none items-center transition-colors duration-150",
5114
+ style: itemStyle,
5115
+ onClick: () => handleItemClick(item),
5116
+ onMouseEnter: () => setHoveredKey(item.value),
5117
+ onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
5118
+ onKeyDown: (e) => {
5119
+ if (e.key === "Enter" || e.key === " ") {
5120
+ e.preventDefault();
5121
+ handleItemClick(item);
5122
+ }
5123
+ },
5124
+ children: [
5125
+ depth === 1 && item.icon && /* @__PURE__ */ jsx(SIcon, { name: item.icon, size: L.depth1Icon, className: "shrink-0" }),
5126
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap", children: item.label }),
5127
+ item.tag !== void 0 && item.tag !== "" && /* @__PURE__ */ jsx(
5128
+ STag,
5129
+ {
5130
+ shape: "square",
5131
+ size: "xs",
5132
+ color: isSelected ? "blue" : "grey",
5133
+ label: item.tag,
5134
+ className: "shrink-0"
5135
+ }
5136
+ ),
5137
+ hasChildren && /* @__PURE__ */ jsx(
5138
+ SIcon,
5139
+ {
5140
+ name: "chevronDown",
5141
+ size: L.itemArrow,
5142
+ rotate: isExpanded ? 180 : 0,
5143
+ className: "shrink-0 transition-transform duration-150"
5144
+ }
5145
+ )
5146
+ ]
5147
+ }
5148
+ ),
5149
+ hasChildren && isExpanded && item.children.map((child) => renderItem(child, depth + 1))
5150
+ ] }, item.value);
5151
+ };
5152
+ const railFolded = folded && type === "belt";
5153
+ const rootWidth = type === "box" ? "100%" : folded ? GNB_COMMON.foldWidth : GNB_MENU_WIDTH;
5154
+ const inBoxGrid = !!layout && type === "box";
5155
+ const rootStyle = useMemo(
5156
+ () => ({
5157
+ width: rootWidth,
5158
+ ...inBoxGrid ? { gridArea: "1 / 1 / -1 / -1" } : null,
5159
+ ...style
5160
+ }),
5161
+ [rootWidth, inBoxGrid, style]
5162
+ );
5163
+ const iconColor = C.itemColorDefault;
5164
+ return /* @__PURE__ */ jsxs(
5165
+ "div",
5166
+ {
5167
+ ref,
5168
+ className: cn(
5169
+ "box-border flex flex-col",
5170
+ type === "box" && !inBoxGrid ? "h-auto" : "h-full",
5171
+ className
5172
+ ),
5173
+ style: rootStyle,
5174
+ "data-type": type,
5175
+ "data-color": color,
5176
+ children: [
5177
+ /* @__PURE__ */ jsxs(
5178
+ "div",
5179
+ {
5180
+ className: cn("box-border flex items-center", railFolded && "flex-col"),
5181
+ style: {
5182
+ gap: railFolded ? 8 : L.topGap,
5183
+ height: railFolded ? "auto" : GNB_COMMON.topHeight,
5184
+ padding: railFolded ? "8px 0" : `0 ${L.topPaddingX}`,
5185
+ borderBottom: `1px solid ${C.topBorder}`,
5186
+ backgroundColor: C.panelBg,
5187
+ color: iconColor
5188
+ },
5189
+ children: [
5190
+ /* @__PURE__ */ jsx(
5191
+ "button",
5192
+ {
5193
+ type: "button",
5194
+ "aria-label": "app launcher",
5195
+ className: iconBtnClass,
5196
+ onClick: onLauncherClick,
5197
+ children: /* @__PURE__ */ jsx(SIcon, { name: "launcher", size: 20 })
5198
+ }
5199
+ ),
5200
+ !railFolded && logo && /* @__PURE__ */ jsx("div", { className: "flex items-center", style: { height: GNB_COMMON.logoHeight }, children: logo }),
5201
+ !railFolded && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: search }),
5202
+ /* @__PURE__ */ jsx(
5203
+ "button",
5204
+ {
5205
+ type: "button",
5206
+ "aria-label": "toggle navigation",
5207
+ "aria-pressed": folded,
5208
+ className: cn(iconBtnClass, !railFolded && "ml-auto"),
5209
+ onClick: handleFoldToggle,
5210
+ children: /* @__PURE__ */ jsx(SIcon, { name: "sidebar", size: 20 })
5211
+ }
5212
+ )
5213
+ ]
5214
+ }
5215
+ ),
5216
+ !folded && /* @__PURE__ */ jsx(
5217
+ "div",
5218
+ {
5219
+ role: "menu",
5220
+ className: "flex flex-col overflow-y-auto",
5221
+ style: {
5222
+ width: GNB_MENU_WIDTH,
5223
+ alignSelf: "flex-start",
5224
+ flexGrow: 1,
5225
+ flexShrink: 1,
5226
+ flexBasis: "auto",
5227
+ minHeight: 0,
5228
+ paddingTop: L.bodyPadding,
5229
+ paddingBottom: L.bodyPadding,
5230
+ backgroundColor: C.panelBg
5231
+ },
5232
+ children: items.map((item) => renderItem(item, 1))
5233
+ }
5234
+ )
5235
+ ]
5236
+ }
5237
+ );
5238
+ });
5239
+ var SPage = forwardRef(function SPage2({ className, style, children, ...rest }, ref) {
5240
+ const layout = useContext(SLayoutContext);
5241
+ const gridStyle = layout?.type === "box" ? { gridColumn: 2, gridRow: 2 } : void 0;
5242
+ return /* @__PURE__ */ jsx(
5243
+ "main",
5244
+ {
5245
+ ref,
5246
+ className: cn("box-border min-h-0 min-w-0 flex-1 overflow-auto p-24", className),
5247
+ style: { ...gridStyle, ...style },
5248
+ ...rest,
5249
+ children
5250
+ }
5251
+ );
5252
+ });
5253
+
4862
5254
  // src/components/STabs/tabs.config.ts
4863
5255
  var TAB_MAIN_SIZE = {
4864
5256
  md: { height: 36, paddingX: 24 },
@@ -5209,6 +5601,7 @@ var SField = forwardRef(function SField2({
5209
5601
  name,
5210
5602
  label = "",
5211
5603
  icon,
5604
+ iconColor,
5212
5605
  labelTooltip = "",
5213
5606
  labelTooltipProps,
5214
5607
  labelWidth,
@@ -5286,7 +5679,15 @@ var SField = forwardRef(function SField2({
5286
5679
  lineHeight: `${s.lineHeight}px`
5287
5680
  },
5288
5681
  children: [
5289
- icon && /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, style: { marginRight: s.labelGap } }),
5682
+ icon && /* @__PURE__ */ jsx(
5683
+ SIcon,
5684
+ {
5685
+ name: icon,
5686
+ size: s.icon,
5687
+ color: iconColor,
5688
+ style: { marginRight: s.labelGap }
5689
+ }
5690
+ ),
5290
5691
  /* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
5291
5692
  (labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsx(
5292
5693
  STooltip,
@@ -5336,7 +5737,7 @@ var SField = forwardRef(function SField2({
5336
5737
  borderBottomLeftRadius: s.radius - 1
5337
5738
  },
5338
5739
  children: [
5339
- icon && !label && /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon }),
5740
+ icon && !label && /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, color: iconColor }),
5340
5741
  effectiveAddonLabel
5341
5742
  ]
5342
5743
  }
@@ -5408,6 +5809,7 @@ var SSelect = forwardRef(function SSelect2({
5408
5809
  label,
5409
5810
  labelWidth,
5410
5811
  icon,
5812
+ iconColor,
5411
5813
  labelTooltip,
5412
5814
  addonLabel,
5413
5815
  addonAlign,
@@ -5419,18 +5821,15 @@ var SSelect = forwardRef(function SSelect2({
5419
5821
  }, ref) {
5420
5822
  const multi = type === "multi" || type === "multi_depth";
5421
5823
  const portalContainer = usePortalContainer();
5824
+ const insideModal = useInsideModal();
5422
5825
  const [search, setSearch] = useState("");
5423
5826
  const [ruleError, setRuleError] = useState("");
5424
5827
  const [open, setOpen] = useState(false);
5828
+ const [pendingOpen, setPendingOpen] = useState(false);
5829
+ const [focusedIndex, setFocusedIndex] = useState(-1);
5425
5830
  const triggerRef = useRef(null);
5426
- useImperativeHandle(
5427
- ref,
5428
- () => ({
5429
- focus: () => triggerRef.current?.focus(),
5430
- open: () => setOpen(true)
5431
- }),
5432
- []
5433
- );
5831
+ const searchInputRef = useRef(null);
5832
+ const listRef = useRef(null);
5434
5833
  const flat = useMemo(() => flattenOptions(options), [options]);
5435
5834
  const selectable = useMemo(() => selectableOptions(flat), [flat]);
5436
5835
  const hasLeaf = useMemo(() => flat.some((o) => !o.isGroup), [flat]);
@@ -5448,6 +5847,46 @@ var SSelect = forwardRef(function SSelect2({
5448
5847
  const arr = Array.isArray(value) ? value : [value];
5449
5848
  return arr.map((v) => v != null && typeof v === "object" ? v.value : v).filter((v) => v != null && v !== "").map(String);
5450
5849
  }, [value]);
5850
+ const selectedRef = useRef(selected);
5851
+ selectedRef.current = selected;
5852
+ const emit2 = (next) => {
5853
+ if (!onValueChange) return;
5854
+ const mapped = next.map(
5855
+ (v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
5856
+ );
5857
+ onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
5858
+ };
5859
+ const commitOpen = (next) => {
5860
+ setOpen(next);
5861
+ onOpenChange?.(next);
5862
+ if (!next) {
5863
+ setPendingOpen(false);
5864
+ setSearch("");
5865
+ if (rules && rules.length > 0) {
5866
+ setRuleError(runRules(multi ? selectedRef.current : selectedRef.current[0] ?? "", rules));
5867
+ }
5868
+ }
5869
+ };
5870
+ const requestOpen = (next) => {
5871
+ if (next && insideModal && portalContainer == null) {
5872
+ setPendingOpen(true);
5873
+ return;
5874
+ }
5875
+ commitOpen(next);
5876
+ };
5877
+ useEffect(() => {
5878
+ if (!insideModal || portalContainer == null || !pendingOpen) return;
5879
+ setPendingOpen(false);
5880
+ commitOpen(true);
5881
+ }, [insideModal, pendingOpen, portalContainer]);
5882
+ useImperativeHandle(
5883
+ ref,
5884
+ () => ({
5885
+ focus: () => triggerRef.current?.focus(),
5886
+ open: () => requestOpen(true)
5887
+ }),
5888
+ [insideModal, portalContainer]
5889
+ );
5451
5890
  useFormField({
5452
5891
  name,
5453
5892
  validate: () => {
@@ -5457,13 +5896,6 @@ var SSelect = forwardRef(function SSelect2({
5457
5896
  },
5458
5897
  resetValidation: () => setRuleError("")
5459
5898
  });
5460
- const emit2 = (next) => {
5461
- if (!onValueChange) return;
5462
- const mapped = next.map(
5463
- (v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
5464
- );
5465
- onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
5466
- };
5467
5899
  const filtered = useMemo(() => {
5468
5900
  if (!showSearch || search.trim() === "") return flat;
5469
5901
  const q = search.trim().toLowerCase();
@@ -5474,6 +5906,16 @@ var SSelect = forwardRef(function SSelect2({
5474
5906
  const allValues = selectable.map((o) => o.value);
5475
5907
  const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
5476
5908
  const hasItems = filtered.some((o) => !o.isGroup);
5909
+ const navigableItems = useMemo(() => {
5910
+ const items = [];
5911
+ if (multi && useSelectAll && allValues.length > 0) {
5912
+ items.push({ value: "__select_all__", isSelectAll: true });
5913
+ }
5914
+ filtered.forEach((o) => {
5915
+ if (!o.isGroup && !o.disabled) items.push({ value: o.value });
5916
+ });
5917
+ return items;
5918
+ }, [allValues.length, filtered, multi, useSelectAll]);
5477
5919
  const triggerText = (() => {
5478
5920
  if (selected.length === 0) return placeholder;
5479
5921
  if (multi) {
@@ -5483,26 +5925,119 @@ var SSelect = forwardRef(function SSelect2({
5483
5925
  return labelByValue.get(selected[0]) ?? selected[0];
5484
5926
  })();
5485
5927
  const isPlaceholder = selected.length === 0;
5486
- const selectStore = /* @__PURE__ */ jsxs(
5487
- Ariakit.SelectProvider,
5928
+ const handleSelect = (nextValue) => {
5929
+ if (multi) {
5930
+ const next = selected.includes(nextValue) ? selected.filter((v) => v !== nextValue) : [...selected, nextValue];
5931
+ emit2(next);
5932
+ return;
5933
+ }
5934
+ emit2([nextValue]);
5935
+ commitOpen(false);
5936
+ };
5937
+ const handleSelectAll = () => {
5938
+ emit2(allSelected ? [] : allValues);
5939
+ };
5940
+ useEffect(() => {
5941
+ if (!open) {
5942
+ setFocusedIndex(-1);
5943
+ return;
5944
+ }
5945
+ setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
5946
+ }, [open, navigableItems.length]);
5947
+ useEffect(() => {
5948
+ if (!open) return;
5949
+ setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
5950
+ }, [search, open, navigableItems.length]);
5951
+ useEffect(() => {
5952
+ if (!open || focusedIndex < 0) return;
5953
+ const list = listRef.current;
5954
+ const focusedEl = list?.querySelector('[data-select-focused="true"]');
5955
+ if (!list || !focusedEl) return;
5956
+ const listRect = list.getBoundingClientRect();
5957
+ const itemRect = focusedEl.getBoundingClientRect();
5958
+ if (itemRect.top < listRect.top) {
5959
+ list.scrollTop += itemRect.top - listRect.top;
5960
+ } else if (itemRect.bottom > listRect.bottom) {
5961
+ list.scrollTop += itemRect.bottom - listRect.bottom;
5962
+ }
5963
+ }, [focusedIndex, open]);
5964
+ const moveFocusedIndex = (direction) => {
5965
+ if (!open || navigableItems.length === 0) return;
5966
+ setFocusedIndex((current) => {
5967
+ if (direction === "next") {
5968
+ return current < 0 ? 0 : (current + 1) % navigableItems.length;
5969
+ }
5970
+ return current <= 0 ? navigableItems.length - 1 : current - 1;
5971
+ });
5972
+ };
5973
+ const selectFocusedItem = () => {
5974
+ if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
5975
+ const focused = navigableItems[focusedIndex];
5976
+ if (focused.isSelectAll) {
5977
+ handleSelectAll();
5978
+ return;
5979
+ }
5980
+ handleSelect(focused.value);
5981
+ };
5982
+ const handleKeyDown = (event) => {
5983
+ if (!open) {
5984
+ if ((event.key === "ArrowDown" || event.key === "ArrowUp") && !effectiveDisabled) {
5985
+ event.preventDefault();
5986
+ requestOpen(true);
5987
+ }
5988
+ return;
5989
+ }
5990
+ if (event.key === "ArrowDown") {
5991
+ event.preventDefault();
5992
+ moveFocusedIndex("next");
5993
+ return;
5994
+ }
5995
+ if (event.key === "ArrowUp") {
5996
+ event.preventDefault();
5997
+ moveFocusedIndex("prev");
5998
+ return;
5999
+ }
6000
+ if (event.key === "Enter") {
6001
+ event.preventDefault();
6002
+ selectFocusedItem();
6003
+ return;
6004
+ }
6005
+ if (event.key === "Escape") {
6006
+ event.preventDefault();
6007
+ commitOpen(false);
6008
+ }
6009
+ };
6010
+ return /* @__PURE__ */ jsxs(
6011
+ SField,
5488
6012
  {
5489
- animated: portalContainer == null ? 150 : false,
5490
- value: multi ? selected : selected[0] ?? "",
5491
- setValue: (v) => emit2(Array.isArray(v) ? v : v === "" ? [] : [v]),
5492
- open,
5493
- setOpen: (o) => {
5494
- setOpen(o);
5495
- onOpenChange?.(o);
5496
- if (!o && rules && rules.length > 0) {
5497
- setRuleError(runRules(multi ? selected : selected[0] ?? "", rules));
5498
- }
5499
- },
6013
+ label,
6014
+ labelWidth,
6015
+ icon,
6016
+ iconColor,
6017
+ labelTooltip,
6018
+ labelTooltipProps,
6019
+ addonLabel,
6020
+ addonAlign,
6021
+ hint,
6022
+ error: error || ruleError !== "",
6023
+ status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
6024
+ errorMessage: ruleError !== "" ? ruleError : errorMessage,
6025
+ disabled: effectiveDisabled,
6026
+ width,
6027
+ className,
6028
+ style,
5500
6029
  children: [
6030
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected.join(",") }),
5501
6031
  /* @__PURE__ */ jsxs(
5502
- Ariakit.Select,
6032
+ "button",
5503
6033
  {
5504
6034
  ref: triggerRef,
6035
+ type: "button",
5505
6036
  disabled: effectiveDisabled,
6037
+ "aria-haspopup": "listbox",
6038
+ "aria-expanded": open,
6039
+ onClick: () => requestOpen(!open),
6040
+ onKeyDown: handleKeyDown,
5506
6041
  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",
5507
6042
  style: {
5508
6043
  color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
@@ -5527,178 +6062,237 @@ var SSelect = forwardRef(function SSelect2({
5527
6062
  ]
5528
6063
  }
5529
6064
  ),
5530
- /* @__PURE__ */ jsxs(
5531
- Ariakit.SelectPopover,
6065
+ /* @__PURE__ */ jsx(
6066
+ SPortal,
5532
6067
  {
5533
- portal: true,
5534
- portalElement: portalContainer,
5535
- gutter: 4,
5536
- sameWidth: !dropdownWidth,
5537
- className: "z-50 overflow-auto rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)] data-[enter]:animate-floating-in-bottom data-[leave]:animate-floating-out-bottom",
6068
+ open,
6069
+ onClose: () => commitOpen(false),
6070
+ onOpenChange: (next) => {
6071
+ if (next) requestOpen(true);
6072
+ },
6073
+ anchorRef: triggerRef,
6074
+ placement: "bottom",
6075
+ align: "start",
6076
+ offset: 4,
6077
+ fit: !dropdownWidth,
6078
+ onOpenAutoFocus: (event) => {
6079
+ if (!showSearch) {
6080
+ event.preventDefault();
6081
+ return;
6082
+ }
6083
+ event.preventDefault();
6084
+ requestAnimationFrame(() => searchInputRef.current?.focus());
6085
+ },
6086
+ onCloseAutoFocus: (event) => {
6087
+ event.preventDefault();
6088
+ triggerRef.current?.focus();
6089
+ },
6090
+ className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
5538
6091
  style: {
5539
- maxHeight: dropdownHeight,
5540
6092
  ...dropdownWidth ? { width: dropdownWidth } : {},
5541
6093
  maxWidth: maxDropdownWidth
5542
6094
  },
5543
- children: [
5544
- showSearch && // 원본 sd-select-list-item-search: sticky + 검색 아이콘 + 테두리 input + focus-within + clear
5545
- /* @__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: [
5546
- /* @__PURE__ */ jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
5547
- /* @__PURE__ */ jsx(
5548
- Ariakit.Combobox,
5549
- {
5550
- autoSelect: true,
5551
- placeholder: "\uAC80\uC0C9",
5552
- 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)]"
5553
- }
5554
- ),
5555
- search !== "" && /* @__PURE__ */ jsx(
5556
- SGhostButton,
5557
- {
5558
- icon: "close",
5559
- size: "xxs",
5560
- intent: "default",
5561
- ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
5562
- onClick: () => setSearch(""),
5563
- className: "shrink-0"
5564
- }
5565
- )
5566
- ] }) }),
5567
- multi && useSelectAll && /* @__PURE__ */ jsx(
5568
- "button",
5569
- {
5570
- type: "button",
5571
- className: "flex w-full items-center px-[12px] py-[4px] text-left text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)] hover:bg-[var(--cmp-listItem-bg-hover)] hover:text-[color:var(--cmp-listItem-content-hover)]",
5572
- onClick: () => emit2(allSelected ? [] : allValues),
5573
- children: allSelectedLabel
5574
- }
5575
- ),
5576
- hasItems ? /* @__PURE__ */ jsx(ItemList, { items: filtered, multi, selected, useSearch: showSearch }) : (
5577
- // 원본 sd-select-listbox__empty (표시할 항목이 없을 때)
5578
- /* @__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." })
5579
- )
5580
- ]
6095
+ children: /* @__PURE__ */ jsxs(
6096
+ "div",
6097
+ {
6098
+ ref: listRef,
6099
+ 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)]",
6100
+ style: { maxHeight: dropdownHeight },
6101
+ onMouseLeave: () => setFocusedIndex(-1),
6102
+ children: [
6103
+ 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: [
6104
+ /* @__PURE__ */ jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
6105
+ /* @__PURE__ */ jsx(
6106
+ "input",
6107
+ {
6108
+ ref: searchInputRef,
6109
+ value: search,
6110
+ onChange: (e) => setSearch(e.target.value),
6111
+ onKeyDown: handleKeyDown,
6112
+ placeholder: "\uAC80\uC0C9",
6113
+ 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)]"
6114
+ }
6115
+ ),
6116
+ search !== "" && /* @__PURE__ */ jsx(
6117
+ SGhostButton,
6118
+ {
6119
+ icon: "close",
6120
+ size: "xxs",
6121
+ intent: "default",
6122
+ ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
6123
+ onClick: () => setSearch(""),
6124
+ className: "shrink-0"
6125
+ }
6126
+ )
6127
+ ] }) }),
6128
+ hasItems ? /* @__PURE__ */ jsx(
6129
+ ItemList,
6130
+ {
6131
+ items: filtered,
6132
+ multi,
6133
+ selected,
6134
+ focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
6135
+ showSelectAll: multi && useSelectAll && allValues.length > 0,
6136
+ selectAllLabel: allSelectedLabel,
6137
+ selectAllSelected: allSelected,
6138
+ onSelectAll: handleSelectAll,
6139
+ onSelect: handleSelect,
6140
+ onHoverValue: (value2) => {
6141
+ if (value2 == null) {
6142
+ setFocusedIndex(-1);
6143
+ return;
6144
+ }
6145
+ const index = navigableItems.findIndex(
6146
+ (item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
6147
+ );
6148
+ if (index >= 0) setFocusedIndex(index);
6149
+ }
6150
+ }
6151
+ ) : /* @__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." })
6152
+ ]
6153
+ }
6154
+ )
5581
6155
  }
5582
6156
  )
5583
6157
  ]
5584
6158
  }
5585
6159
  );
5586
- const control = showSearch ? /* @__PURE__ */ jsx(Ariakit.ComboboxProvider, { value: search, setValue: setSearch, resetValueOnHide: true, children: selectStore }) : selectStore;
5587
- return /* @__PURE__ */ jsxs(
5588
- SField,
5589
- {
5590
- label,
5591
- labelWidth,
5592
- icon,
5593
- labelTooltip,
5594
- labelTooltipProps,
5595
- addonLabel,
5596
- addonAlign,
5597
- hint,
5598
- error: error || ruleError !== "",
5599
- status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
5600
- errorMessage: ruleError !== "" ? ruleError : errorMessage,
5601
- disabled: effectiveDisabled,
5602
- width,
5603
- className,
5604
- style,
5605
- children: [
5606
- name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected.join(",") }),
5607
- control
5608
- ]
5609
- }
5610
- );
5611
6160
  });
5612
6161
  SSelect.displayName = "SSelect";
5613
6162
  function ItemList({
5614
6163
  items,
5615
6164
  multi,
5616
6165
  selected,
5617
- useSearch
6166
+ focusedValue,
6167
+ showSelectAll,
6168
+ selectAllLabel,
6169
+ selectAllSelected,
6170
+ onSelectAll,
6171
+ onSelect,
6172
+ onHoverValue
5618
6173
  }) {
5619
6174
  const itemBase = cn(
5620
- // group: 체크박스 inverse(group-data-[active-item])를 위해 필요
5621
- "group flex cursor-pointer select-none items-center gap-[8px] text-[12px] leading-[20px] outline-none",
6175
+ "group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
5622
6176
  "text-[color:var(--cmp-listItem-content-default)]",
5623
- // Ariakit은 disabled 항목에 aria-disabled를 붙이므로 aria-disabled 변형으로 스타일 (data-disabled 아님)
5624
- "aria-disabled:cursor-not-allowed aria-disabled:text-[color:var(--cmp-listItem-content-disabled)]"
5625
- );
5626
- const activeClass = "data-[active-item]:bg-[var(--cmp-listItem-bg-hover)] data-[active-item]:text-[color:var(--cmp-listItem-content-hover)]";
5627
- return /* @__PURE__ */ jsx(Fragment, { children: items.map((o) => {
5628
- const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
5629
- if (o.isGroup) {
5630
- const isDepth1 = o.depth === 1;
5631
- return /* @__PURE__ */ jsx(
5632
- "div",
5633
- {
5634
- className: cn(
5635
- "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
5636
- 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",
5637
- // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
5638
- // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
5639
- o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
5640
- o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
5641
- ),
5642
- style: { paddingLeft: padLeft },
5643
- children: o.label
5644
- },
5645
- o.value
5646
- );
5647
- }
5648
- const isSelected = selected.includes(o.value);
5649
- return /* @__PURE__ */ jsxs(
5650
- Ariakit.SelectItem,
6177
+ "disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
6178
+ );
6179
+ 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)]";
6180
+ return /* @__PURE__ */ jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
6181
+ showSelectAll && /* @__PURE__ */ jsxs(
6182
+ "button",
5651
6183
  {
5652
- value: o.value,
5653
- disabled: o.disabled,
5654
- accessibleWhenDisabled: true,
6184
+ type: "button",
6185
+ "data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
6186
+ "data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
5655
6187
  className: cn(
5656
6188
  itemBase,
5657
- isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
5658
- // Ariakit은 disabled 항목도 hover 시 active(data-active-item) 처리하므로,
5659
- // 비활성 항목에는 hover 하이라이트를 적용하지 않아 회색 비활성 스타일을 유지한다.
5660
- // (원본 Stencil의 `--focused...:not(--disabled)` 규칙과 동일한 의도)
5661
- !o.disabled && activeClass
6189
+ focusedValue === "__select_all__" && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
5662
6190
  ),
5663
- style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
5664
- render: useSearch ? /* @__PURE__ */ jsx(Ariakit.ComboboxItem, {}) : void 0,
6191
+ style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: 12 },
6192
+ onMouseEnter: () => onHoverValue("__select_all__"),
6193
+ onMouseLeave: () => onHoverValue(null),
6194
+ onClick: onSelectAll,
5665
6195
  children: [
5666
- multi && // hover(active) 시 checkbox inverse — 파란 hover 배경 위에서 checked/unchecked 모두 흰 테두리 + 투명 배경
5667
6196
  /* @__PURE__ */ jsx(
5668
6197
  "span",
5669
6198
  {
5670
6199
  className: cn(
5671
6200
  "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
5672
- o.disabled ? (
5673
- // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
5674
- "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
5675
- ) : cn(
5676
- "group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
5677
- 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"
5678
- )
6201
+ 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)]"
5679
6202
  ),
5680
- children: isSelected && /* @__PURE__ */ jsx(
6203
+ children: selectAllSelected && /* @__PURE__ */ jsx(
5681
6204
  SIcon,
5682
6205
  {
5683
6206
  name: "checkboxCheck",
5684
6207
  size: 12,
5685
- color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
6208
+ color: "var(--cmp-checkbox-checked-icon-default)"
5686
6209
  }
5687
6210
  )
5688
6211
  }
5689
6212
  ),
5690
- isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
5691
- "span",
5692
- {
5693
- className: "flex-1 truncate",
5694
- dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
5695
- }
5696
- ) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
6213
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
5697
6214
  ]
5698
- },
5699
- o.value
5700
- );
5701
- }) });
6215
+ }
6216
+ ),
6217
+ items.map((o) => {
6218
+ const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
6219
+ if (o.isGroup) {
6220
+ const isDepth1 = o.depth === 1;
6221
+ return /* @__PURE__ */ jsx(
6222
+ "div",
6223
+ {
6224
+ className: cn(
6225
+ "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
6226
+ 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",
6227
+ // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
6228
+ // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
6229
+ o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
6230
+ o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
6231
+ ),
6232
+ style: { paddingLeft: padLeft },
6233
+ children: o.label
6234
+ },
6235
+ o.value
6236
+ );
6237
+ }
6238
+ const isSelected = selected.includes(o.value);
6239
+ return /* @__PURE__ */ jsxs(
6240
+ "button",
6241
+ {
6242
+ type: "button",
6243
+ disabled: o.disabled,
6244
+ role: "option",
6245
+ "aria-selected": isSelected,
6246
+ "data-select-focused": focusedValue === o.value ? "true" : void 0,
6247
+ "data-select-active": focusedValue === o.value ? "true" : void 0,
6248
+ className: cn(
6249
+ itemBase,
6250
+ isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
6251
+ focusedValue === o.value && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]",
6252
+ // 비활성 항목은 hover/focus 스타일 대상에서 제외한다.
6253
+ // (:hover 는 disabled 버튼에도 매칭되므로 클래스 자체를 붙이지 않아야 한다)
6254
+ !o.disabled && activeClass
6255
+ ),
6256
+ style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
6257
+ onMouseEnter: () => onHoverValue(o.value),
6258
+ onMouseLeave: () => onHoverValue(null),
6259
+ onClick: () => onSelect(o.value),
6260
+ children: [
6261
+ multi && /* @__PURE__ */ jsx(
6262
+ "span",
6263
+ {
6264
+ className: cn(
6265
+ "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
6266
+ o.disabled ? (
6267
+ // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
6268
+ "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
6269
+ ) : cn(
6270
+ 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)]"
6271
+ )
6272
+ ),
6273
+ children: isSelected && /* @__PURE__ */ jsx(
6274
+ SIcon,
6275
+ {
6276
+ name: "checkboxCheck",
6277
+ size: 12,
6278
+ color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
6279
+ }
6280
+ )
6281
+ }
6282
+ ),
6283
+ isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
6284
+ "span",
6285
+ {
6286
+ className: "flex-1 truncate",
6287
+ dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
6288
+ }
6289
+ ) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
6290
+ ]
6291
+ },
6292
+ o.value
6293
+ );
6294
+ })
6295
+ ] });
5702
6296
  }
5703
6297
  var DEFAULT_ROW_HEIGHT = 44;
5704
6298
  var DENSE_ROW_HEIGHT = 32;
@@ -5974,7 +6568,7 @@ var STable = forwardRef(function STable2({
5974
6568
  };
5975
6569
  const radiusClass = radius === "full" ? "rounded-none" : radius === "useTop" ? "rounded-b-[var(--cmp-table-radius)] rounded-t-none" : "rounded-[var(--cmp-table-radius)]";
5976
6570
  const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-body-paddingY-default)";
5977
- const cellPadX = dense ? "var(--cmp-table-body-paddingX-default)" : "var(--cmp-table-body-paddingX-wide)";
6571
+ const cellPadX = "var(--cmp-table-body-paddingX-wide)";
5978
6572
  const rowH = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-body-default-height)";
5979
6573
  const totalColSpan = visibleCols.length + (selectable ? 1 : 0);
5980
6574
  const stickyInfo = (i) => {
@@ -6182,10 +6776,10 @@ var STable = forwardRef(function STable2({
6182
6776
  "div",
6183
6777
  {
6184
6778
  "aria-hidden": true,
6185
- className: "pointer-events-none absolute left-0 right-0 top-0 z-[210] h-[var(--cmp-table-header-height)] bg-white/60"
6779
+ className: "pointer-events-none absolute left-0 right-0 top-0 z-[31] h-[var(--cmp-table-header-height)] bg-white/60"
6186
6780
  }
6187
6781
  ),
6188
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-0 left-0 right-0 top-[var(--cmp-table-header-height)] z-[200] flex items-center justify-center bg-white/60 text-[12px] text-[color:var(--color-grey-65)]", children: /* @__PURE__ */ jsx("div", { className: "flex min-h-[60px] w-full items-center justify-center", children: noDataLabel }) })
6782
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-0 left-0 right-0 top-[var(--cmp-table-header-height)] z-[30] flex items-center justify-center bg-white/60 text-[12px] text-[color:var(--color-grey-65)]", children: /* @__PURE__ */ jsx("div", { className: "flex min-h-[60px] w-full items-center justify-center", children: noDataLabel }) })
6189
6783
  ] }),
6190
6784
  isLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-white/60", children: /* @__PURE__ */ jsx(SCircleProgress, { indeterminate: true }) })
6191
6785
  ]
@@ -6221,6 +6815,7 @@ var SInput = forwardRef(function SInput2({
6221
6815
  type = "text",
6222
6816
  size = "sm",
6223
6817
  icon,
6818
+ iconColor,
6224
6819
  prefix,
6225
6820
  suffix,
6226
6821
  clearable = false,
@@ -6274,6 +6869,7 @@ var SInput = forwardRef(function SInput2({
6274
6869
  labelTooltip,
6275
6870
  labelTooltipProps,
6276
6871
  icon,
6872
+ iconColor,
6277
6873
  size,
6278
6874
  hint,
6279
6875
  error: resolvedError,
@@ -6375,6 +6971,7 @@ var STextarea = forwardRef(function STextarea2({
6375
6971
  label,
6376
6972
  labelWidth,
6377
6973
  icon,
6974
+ iconColor,
6378
6975
  labelTooltip,
6379
6976
  labelTooltipProps,
6380
6977
  addonLabel,
@@ -6411,6 +7008,7 @@ var STextarea = forwardRef(function STextarea2({
6411
7008
  label,
6412
7009
  labelWidth,
6413
7010
  icon,
7011
+ iconColor,
6414
7012
  labelTooltip,
6415
7013
  labelTooltipProps,
6416
7014
  addonLabel,
@@ -6543,6 +7141,7 @@ var SNumberInput = forwardRef(function SNumberInput2({
6543
7141
  label,
6544
7142
  labelWidth,
6545
7143
  icon,
7144
+ iconColor,
6546
7145
  labelTooltip,
6547
7146
  labelTooltipProps,
6548
7147
  addonLabel,
@@ -6701,6 +7300,7 @@ var SNumberInput = forwardRef(function SNumberInput2({
6701
7300
  label,
6702
7301
  labelWidth,
6703
7302
  icon,
7303
+ iconColor,
6704
7304
  labelTooltip,
6705
7305
  labelTooltipProps,
6706
7306
  addonLabel,
@@ -7176,6 +7776,7 @@ function SDatePicker({
7176
7776
  label,
7177
7777
  labelWidth,
7178
7778
  icon,
7779
+ iconColor,
7179
7780
  labelTooltip,
7180
7781
  labelTooltipProps,
7181
7782
  addonLabel,
@@ -7214,6 +7815,7 @@ function SDatePicker({
7214
7815
  label,
7215
7816
  labelWidth,
7216
7817
  icon,
7818
+ iconColor,
7217
7819
  labelTooltip,
7218
7820
  labelTooltipProps,
7219
7821
  addonLabel,
@@ -7482,6 +8084,7 @@ function SDateRangePicker({
7482
8084
  label,
7483
8085
  labelWidth,
7484
8086
  icon,
8087
+ iconColor,
7485
8088
  labelTooltip,
7486
8089
  labelTooltipProps,
7487
8090
  addonLabel,
@@ -7524,6 +8127,7 @@ function SDateRangePicker({
7524
8127
  label,
7525
8128
  labelWidth,
7526
8129
  icon,
8130
+ iconColor,
7527
8131
  labelTooltip,
7528
8132
  labelTooltipProps,
7529
8133
  addonLabel,
@@ -7785,6 +8389,7 @@ function SFilePicker({
7785
8389
  label,
7786
8390
  labelWidth,
7787
8391
  icon,
8392
+ iconColor,
7788
8393
  labelTooltip,
7789
8394
  labelTooltipProps,
7790
8395
  addonLabel,
@@ -7954,7 +8559,7 @@ function SFilePicker({
7954
8559
  unmountOnHide: true,
7955
8560
  getAnchorRect: () => contentBoxRef.current?.getBoundingClientRect() ?? null,
7956
8561
  style: { minWidth: contentBoxRef.current?.offsetWidth },
7957
- className: "z-50 box-border flex flex-col items-stretch gap-[8px] rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] p-[8px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
8562
+ className: "z-50 box-border flex flex-col items-start gap-[8px] rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] p-[8px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
7958
8563
  children: filesArr.map((f, i) => /* @__PURE__ */ jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i))
7959
8564
  }
7960
8565
  )
@@ -7968,6 +8573,7 @@ function SFilePicker({
7968
8573
  label,
7969
8574
  labelWidth,
7970
8575
  icon,
8576
+ iconColor,
7971
8577
  labelTooltip,
7972
8578
  labelTooltipProps,
7973
8579
  addonLabel,
@@ -7990,21 +8596,26 @@ function SFilePicker({
7990
8596
  var cssLen2 = (v) => v == null ? void 0 : typeof v === "number" ? `${v}px` : v;
7991
8597
  function SKeyValueTable({
7992
8598
  fields = [],
8599
+ values = {},
7993
8600
  search = false,
7994
8601
  radius = "default",
7995
- dense = false,
7996
8602
  onChange,
7997
8603
  onSearch,
7998
8604
  className,
7999
8605
  style
8000
8606
  }) {
8001
8607
  if (fields.length === 0) return null;
8002
- const emit2 = (name, value) => onChange?.({ name, value });
8003
- const cellHeight = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-keyValue-height)";
8004
- const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-keyValue-body-paddingY)";
8608
+ const emit2 = (name, value) => onChange?.({
8609
+ name,
8610
+ value,
8611
+ values: { ...values, [name]: value }
8612
+ });
8613
+ const cellHeight = "var(--cmp-table-keyValue-height)";
8614
+ const cellPadY = "var(--cmp-table-keyValue-body-paddingY)";
8005
8615
  const renderField = (f) => {
8006
8616
  if (f.render) return f.render;
8007
- const opt = f.options ?? {};
8617
+ const opt = { ...f.options ?? {} };
8618
+ if (Object.prototype.hasOwnProperty.call(values, f.name)) opt.value = values[f.name];
8008
8619
  const up = (v) => emit2(f.name, v);
8009
8620
  switch (f.type) {
8010
8621
  case "input":
@@ -8116,7 +8727,10 @@ function SKeyValueTable({
8116
8727
  rowSpan: f.tdRowSpan && f.tdRowSpan > 1 ? f.tdRowSpan : void 0,
8117
8728
  colSpan: f.tdColSpan && f.tdColSpan > 1 ? f.tdColSpan : void 0,
8118
8729
  className: cn(
8119
- "box-border bg-[var(--sys-color-bg-frame)] px-[var(--cmp-table-keyValue-body-paddingX-default)] align-middle",
8730
+ "box-border bg-[var(--sys-color-bg-frame)] align-middle",
8731
+ // text 타입(및 미지정 기본값)은 읽기 전용 콘텐츠라 넓은 paddingX(wide),
8732
+ // 폼 필드 셀은 default paddingX 유지
8733
+ f.type == null || f.type === "text" ? "px-[var(--cmp-table-keyValue-body-paddingX-wide)]" : "px-[var(--cmp-table-keyValue-body-paddingX-default)]",
8120
8734
  cellBorder,
8121
8735
  f.tdClass
8122
8736
  ),
@@ -8609,6 +9223,7 @@ var SChipInput = forwardRef(function SChipInput2({
8609
9223
  error,
8610
9224
  useReset = false,
8611
9225
  maxCount,
9226
+ metaPlacement = "end",
8612
9227
  duplicateLabel,
8613
9228
  suggestions = [],
8614
9229
  loadingSuggestions = false,
@@ -8622,6 +9237,7 @@ var SChipInput = forwardRef(function SChipInput2({
8622
9237
  width,
8623
9238
  status,
8624
9239
  icon,
9240
+ iconColor,
8625
9241
  labelTooltip,
8626
9242
  labelTooltipProps,
8627
9243
  className,
@@ -8756,6 +9372,7 @@ var SChipInput = forwardRef(function SChipInput2({
8756
9372
  labelWidth,
8757
9373
  labelHeight: `calc(var(--cmp-chipInput-${size}-minHeight) + 2px)`,
8758
9374
  icon,
9375
+ iconColor,
8759
9376
  labelTooltip,
8760
9377
  labelTooltipProps,
8761
9378
  size,
@@ -8903,7 +9520,11 @@ var SChipInput = forwardRef(function SChipInput2({
8903
9520
  showMetaInfo && /* @__PURE__ */ jsxs(
8904
9521
  "div",
8905
9522
  {
8906
- className: "s-chip-input__meta ml-auto flex items-center gap-8",
9523
+ className: cn(
9524
+ "s-chip-input__meta flex items-center gap-8",
9525
+ metaPlacement === "end" && "ml-auto"
9526
+ ),
9527
+ "data-placement": metaPlacement,
8907
9528
  onMouseDown: (e) => e.preventDefault(),
8908
9529
  children: [
8909
9530
  /* @__PURE__ */ jsx(
@@ -8913,11 +9534,20 @@ var SChipInput = forwardRef(function SChipInput2({
8913
9534
  style: { width: 1, height: 12, backgroundColor: "#E1E1E1" }
8914
9535
  }
8915
9536
  ),
8916
- showMaxCount && /* @__PURE__ */ jsxs("span", { className: "s-chip-input__max-count text-12 text-(--cmp-chipInput-text-placeholder)", children: [
8917
- "\uCD5C\uB300 ",
8918
- maxCount,
8919
- "\uAC1C"
8920
- ] }),
9537
+ showMaxCount && /* @__PURE__ */ jsxs(
9538
+ "span",
9539
+ {
9540
+ className: cn(
9541
+ "s-chip-input__max-count text-12",
9542
+ isMax ? "text-(--sys-color-fg-warning)" : "text-(--cmp-chipInput-text-placeholder)"
9543
+ ),
9544
+ children: [
9545
+ "\uCD5C\uB300 ",
9546
+ maxCount,
9547
+ "\uAC1C"
9548
+ ]
9549
+ }
9550
+ ),
8921
9551
  showResetLink && /* @__PURE__ */ jsx(
8922
9552
  STextLink,
8923
9553
  {
@@ -8954,7 +9584,7 @@ var SChipInput = forwardRef(function SChipInput2({
8954
9584
  onMouseDown: (e) => e.preventDefault(),
8955
9585
  children: [
8956
9586
  loading2 && /* @__PURE__ */ jsx("div", { className: "s-chip-input__suggestions-empty p-12 text-center text-12 leading-20 text-(--color-grey-65)", children: "\uBD88\uB7EC\uC624\uB294 \uC911..." }),
8957
- !effectiveAutocomplete ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-8 p-12", children: items.map((item, i) => {
9587
+ !effectiveAutocomplete ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-8 px-12 py-6", children: items.map((item, i) => {
8958
9588
  const selected = values.includes(item);
8959
9589
  const active = i === activeIndex && !selected;
8960
9590
  return /* @__PURE__ */ jsx(
@@ -9043,6 +9673,7 @@ var SBarcodeInput = forwardRef(
9043
9673
  label,
9044
9674
  labelWidth,
9045
9675
  icon,
9676
+ iconColor,
9046
9677
  labelTooltip,
9047
9678
  labelTooltipProps,
9048
9679
  addonLabel,
@@ -9074,6 +9705,7 @@ var SBarcodeInput = forwardRef(
9074
9705
  label,
9075
9706
  labelWidth,
9076
9707
  icon,
9708
+ iconColor,
9077
9709
  labelTooltip,
9078
9710
  labelTooltipProps,
9079
9711
  addonLabel,
@@ -9152,6 +9784,6 @@ var SBarcodeInput = forwardRef(
9152
9784
  }
9153
9785
  );
9154
9786
 
9155
- export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, ICONS, SActionModal, SBadge, SBarcodeInput, SButton, SCalendar, SCallout, SCard, SCheckbox, SChip, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDateRangePicker, SDivider, SDropdownButton, SField, SFilePicker, SForm, SGhostButton, SGuide, SIcon, SInput, SKeyValueTable, SLinearProgress, SLoadingContainer, SLoadingModal, SLoadingViewport, SModalContainer, SNumberInput, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioGroup, SSelect, SSwitch, STable, STabs, STag, STextLink, STextarea, SToast, SToastContainer, SToggle, STooltip, TAG_COLORS, TAG_SHAPES, TAG_SIZES, buttonPreset, cn, isColorKey, loading, resolveColor };
9787
+ export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, GNB_COLORS, GNB_COMMON, GNB_LAYOUT, GNB_MENU_WIDTH, ICONS, SActionModal, SBadge, SBarcodeInput, SButton, SCalendar, SCallout, SCard, SCheckbox, SChip, SChipInput, SCircleProgress, SConfirmModal, SDatePicker, SDateRangePicker, SDivider, SDropdownButton, SField, SFilePicker, SForm, SGhostButton, SGnb, SGuide, SIcon, SInput, SKeyValueTable, SLayout, SLayoutContext, SLinearProgress, SLoadingContainer, SLoadingModal, SLoadingViewport, SNumberInput, SPage, SPagination, SPopover, SPopup, SPortal, SRadio, SRadioButton, SRadioGroup, SSelect, SSwitch, STable, STabs, STag, STextLink, STextarea, SToast, SToastContainer, SToggle, STooltip, TAG_COLORS, TAG_SHAPES, TAG_SIZES, buttonPreset, cn, findGnbAncestors, isColorKey, loading, resolveColor, useSLayout };
9156
9788
  //# sourceMappingURL=index.js.map
9157
9789
  //# sourceMappingURL=index.js.map