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
@@ -4,8 +4,8 @@ import { clsx } from 'clsx';
4
4
  import { extendTailwindMerge } from 'tailwind-merge';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import * as RPopover2 from '@radix-ui/react-popover';
7
- import * as Ariakit from '@ariakit/react';
8
7
  import * as RCheckbox from '@radix-ui/react-checkbox';
8
+ import * as Ariakit from '@ariakit/react';
9
9
 
10
10
  var twMerge = extendTailwindMerge({
11
11
  extend: {
@@ -2873,10 +2873,15 @@ var FLOATING_SLIDE_ANIM = [
2873
2873
  "data-[state=closed]:data-[side=right]:animate-floating-out-right"
2874
2874
  ].join(" ");
2875
2875
  var PortalContainerContext = createContext(null);
2876
+ var InsideModalContext = createContext(false);
2876
2877
  PortalContainerContext.Provider;
2878
+ InsideModalContext.Provider;
2877
2879
  function usePortalContainer() {
2878
2880
  return useContext(PortalContainerContext) ?? void 0;
2879
2881
  }
2882
+ function useInsideModal() {
2883
+ return useContext(InsideModalContext);
2884
+ }
2880
2885
 
2881
2886
  // src/components/STag/tag.config.ts
2882
2887
  var TAG_SIZE_CONFIG = {
@@ -2974,6 +2979,8 @@ function SGhostButton({
2974
2979
  tooltipText,
2975
2980
  disabled = false,
2976
2981
  onClick,
2982
+ anchorClassName,
2983
+ anchorStyle,
2977
2984
  className,
2978
2985
  style
2979
2986
  }) {
@@ -2996,6 +3003,8 @@ function SGhostButton({
2996
3003
  };
2997
3004
  const s = SIZE[size];
2998
3005
  const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
3006
+ const text = (tooltipText ?? "").trim();
3007
+ const hasTooltip = text !== "";
2999
3008
  const btn = /* @__PURE__ */ jsxs(
3000
3009
  "button",
3001
3010
  {
@@ -3036,13 +3045,13 @@ function SGhostButton({
3036
3045
  ]
3037
3046
  }
3038
3047
  );
3039
- const text = (tooltipText ?? "").trim();
3040
- if (text === "") return btn;
3048
+ if (!hasTooltip) return btn;
3041
3049
  return /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: setOpen, children: [
3042
3050
  /* @__PURE__ */ jsx(RPopover2.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
3043
3051
  "span",
3044
3052
  {
3045
- className: "inline-flex",
3053
+ className: cn("inline-flex", anchorClassName),
3054
+ style: anchorStyle,
3046
3055
  onMouseEnter: openNow,
3047
3056
  onMouseLeave: startHide,
3048
3057
  onFocus: openNow,
@@ -3400,6 +3409,7 @@ var SField = forwardRef(function SField2({
3400
3409
  name,
3401
3410
  label = "",
3402
3411
  icon,
3412
+ iconColor,
3403
3413
  labelTooltip = "",
3404
3414
  labelTooltipProps,
3405
3415
  labelWidth,
@@ -3477,7 +3487,15 @@ var SField = forwardRef(function SField2({
3477
3487
  lineHeight: `${s.lineHeight}px`
3478
3488
  },
3479
3489
  children: [
3480
- icon && /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, style: { marginRight: s.labelGap } }),
3490
+ icon && /* @__PURE__ */ jsx(
3491
+ SIcon,
3492
+ {
3493
+ name: icon,
3494
+ size: s.icon,
3495
+ color: iconColor,
3496
+ style: { marginRight: s.labelGap }
3497
+ }
3498
+ ),
3481
3499
  /* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
3482
3500
  (labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsx(
3483
3501
  STooltip,
@@ -3527,7 +3545,7 @@ var SField = forwardRef(function SField2({
3527
3545
  borderBottomLeftRadius: s.radius - 1
3528
3546
  },
3529
3547
  children: [
3530
- icon && !label && /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon }),
3548
+ icon && !label && /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, color: iconColor }),
3531
3549
  effectiveAddonLabel
3532
3550
  ]
3533
3551
  }
@@ -3553,6 +3571,7 @@ var SInput = forwardRef(function SInput2({
3553
3571
  type = "text",
3554
3572
  size = "sm",
3555
3573
  icon,
3574
+ iconColor,
3556
3575
  prefix,
3557
3576
  suffix,
3558
3577
  clearable = false,
@@ -3606,6 +3625,7 @@ var SInput = forwardRef(function SInput2({
3606
3625
  labelTooltip,
3607
3626
  labelTooltipProps,
3608
3627
  icon,
3628
+ iconColor,
3609
3629
  size,
3610
3630
  hint,
3611
3631
  error: resolvedError,
@@ -3707,6 +3727,7 @@ var STextarea = forwardRef(function STextarea2({
3707
3727
  label,
3708
3728
  labelWidth,
3709
3729
  icon,
3730
+ iconColor,
3710
3731
  labelTooltip,
3711
3732
  labelTooltipProps,
3712
3733
  addonLabel,
@@ -3743,6 +3764,7 @@ var STextarea = forwardRef(function STextarea2({
3743
3764
  label,
3744
3765
  labelWidth,
3745
3766
  icon,
3767
+ iconColor,
3746
3768
  labelTooltip,
3747
3769
  labelTooltipProps,
3748
3770
  addonLabel,
@@ -3875,6 +3897,7 @@ var SNumberInput = forwardRef(function SNumberInput2({
3875
3897
  label,
3876
3898
  labelWidth,
3877
3899
  icon,
3900
+ iconColor,
3878
3901
  labelTooltip,
3879
3902
  labelTooltipProps,
3880
3903
  addonLabel,
@@ -4033,6 +4056,7 @@ var SNumberInput = forwardRef(function SNumberInput2({
4033
4056
  label,
4034
4057
  labelWidth,
4035
4058
  icon,
4059
+ iconColor,
4036
4060
  labelTooltip,
4037
4061
  labelTooltipProps,
4038
4062
  addonLabel,
@@ -4118,40 +4142,6 @@ var SNumberInput = forwardRef(function SNumberInput2({
4118
4142
  );
4119
4143
  });
4120
4144
 
4121
- // src/components/SSelect/select.config.ts
4122
- function flattenOptions(options, depth = 1) {
4123
- const out = [];
4124
- for (const o of options) {
4125
- const hasChildren = Array.isArray(o.children) && o.children.length > 0;
4126
- if (hasChildren) {
4127
- const childFlat = flattenOptions(o.children, depth + 1);
4128
- const allChildrenDisabled = childFlat.every((c) => c.disabled);
4129
- out.push({
4130
- value: String(o.value),
4131
- label: o.label,
4132
- disabled: !!o.disabled || allChildrenDisabled,
4133
- depth,
4134
- isGroup: true,
4135
- raw: o.value
4136
- });
4137
- out.push(...childFlat);
4138
- } else {
4139
- out.push({
4140
- value: String(o.value),
4141
- label: o.label,
4142
- disabled: !!o.disabled,
4143
- depth,
4144
- isGroup: false,
4145
- raw: o.value
4146
- });
4147
- }
4148
- }
4149
- return out;
4150
- }
4151
- var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
4152
- var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
4153
- var extractText = (html) => html.replace(/<[^>]*>/g, "");
4154
-
4155
4145
  // src/lib/sanitize-inline-html.ts
4156
4146
  var ALLOWED_INLINE_TAGS = /* @__PURE__ */ new Set(["A", "B", "STRONG", "I", "EM", "BR", "SPAN"]);
4157
4147
  var ALLOWED_ATTRS_BY_TAG = {
@@ -4220,6 +4210,111 @@ var sanitizeInlineHtml = (value) => {
4220
4210
  Array.from(template.content.childNodes).forEach((child) => sanitizeNode(child));
4221
4211
  return template.innerHTML;
4222
4212
  };
4213
+ function SPortal({
4214
+ open,
4215
+ onClose,
4216
+ onOpenChange,
4217
+ anchorRef,
4218
+ placement = "bottom",
4219
+ align = "start",
4220
+ offset = 4,
4221
+ alignOffset = 0,
4222
+ fit = false,
4223
+ maxHeight,
4224
+ autoClose = false,
4225
+ persistent = false,
4226
+ viewportPadding = 0,
4227
+ onOpenAutoFocus,
4228
+ onCloseAutoFocus,
4229
+ children,
4230
+ className,
4231
+ style,
4232
+ ariaLabel
4233
+ }) {
4234
+ const portalContainer = usePortalContainer();
4235
+ return /* @__PURE__ */ jsxs(
4236
+ RPopover2.Root,
4237
+ {
4238
+ open,
4239
+ onOpenChange: (next) => {
4240
+ onOpenChange?.(next);
4241
+ if (!next) onClose?.();
4242
+ },
4243
+ children: [
4244
+ /* @__PURE__ */ jsx(RPopover2.Anchor, { virtualRef: anchorRef }),
4245
+ /* @__PURE__ */ jsx(RPopover2.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx(
4246
+ RPopover2.Content,
4247
+ {
4248
+ side: placement,
4249
+ align,
4250
+ sideOffset: offset,
4251
+ alignOffset,
4252
+ collisionPadding: viewportPadding,
4253
+ "aria-label": ariaLabel,
4254
+ onPointerDownOutside: (e) => {
4255
+ if (persistent || anchorRef.current?.contains(e.target)) {
4256
+ e.preventDefault();
4257
+ }
4258
+ },
4259
+ onFocusOutside: (e) => {
4260
+ if (persistent || anchorRef.current?.contains(e.target)) {
4261
+ e.preventDefault();
4262
+ }
4263
+ },
4264
+ onEscapeKeyDown: persistent ? (e) => e.preventDefault() : void 0,
4265
+ onOpenAutoFocus,
4266
+ onCloseAutoFocus,
4267
+ onClick: autoClose ? () => onClose?.() : void 0,
4268
+ className: cn(
4269
+ "z-50 overflow-hidden rounded-(--cmp-listBox-radius) bg-(--cmp-listBox-bg) shadow-normal-sm",
4270
+ maxHeight != null && "overflow-y-auto",
4271
+ fit && "min-w-(--radix-popover-trigger-width)",
4272
+ FLOATING_SLIDE_ANIM,
4273
+ className
4274
+ ),
4275
+ style: { maxHeight, ...style },
4276
+ children
4277
+ }
4278
+ ) })
4279
+ ]
4280
+ }
4281
+ );
4282
+ }
4283
+ SPortal.displayName = "SPortal";
4284
+
4285
+ // src/components/SSelect/select.config.ts
4286
+ function flattenOptions(options, depth = 1) {
4287
+ const out = [];
4288
+ for (const o of options) {
4289
+ const hasChildren = Array.isArray(o.children) && o.children.length > 0;
4290
+ if (hasChildren) {
4291
+ const childFlat = flattenOptions(o.children, depth + 1);
4292
+ const allChildrenDisabled = childFlat.every((c) => c.disabled);
4293
+ out.push({
4294
+ value: String(o.value),
4295
+ label: o.label,
4296
+ disabled: !!o.disabled || allChildrenDisabled,
4297
+ depth,
4298
+ isGroup: true,
4299
+ raw: o.value
4300
+ });
4301
+ out.push(...childFlat);
4302
+ } else {
4303
+ out.push({
4304
+ value: String(o.value),
4305
+ label: o.label,
4306
+ disabled: !!o.disabled,
4307
+ depth,
4308
+ isGroup: false,
4309
+ raw: o.value
4310
+ });
4311
+ }
4312
+ }
4313
+ return out;
4314
+ }
4315
+ var selectableOptions = (flat) => flat.filter((o) => !o.isGroup && !o.disabled);
4316
+ var isHtmlLabel = (label) => /<[a-z][\s\S]*>/i.test(label);
4317
+ var extractText = (html) => html.replace(/<[^>]*>/g, "");
4223
4318
  var SEARCH_THRESHOLD = 11;
4224
4319
  var SSelect = forwardRef(function SSelect2({
4225
4320
  value,
@@ -4243,6 +4338,7 @@ var SSelect = forwardRef(function SSelect2({
4243
4338
  label,
4244
4339
  labelWidth,
4245
4340
  icon,
4341
+ iconColor,
4246
4342
  labelTooltip,
4247
4343
  addonLabel,
4248
4344
  addonAlign,
@@ -4254,18 +4350,15 @@ var SSelect = forwardRef(function SSelect2({
4254
4350
  }, ref) {
4255
4351
  const multi = type === "multi" || type === "multi_depth";
4256
4352
  const portalContainer = usePortalContainer();
4353
+ const insideModal = useInsideModal();
4257
4354
  const [search, setSearch] = useState("");
4258
4355
  const [ruleError, setRuleError] = useState("");
4259
4356
  const [open, setOpen] = useState(false);
4357
+ const [pendingOpen, setPendingOpen] = useState(false);
4358
+ const [focusedIndex, setFocusedIndex] = useState(-1);
4260
4359
  const triggerRef = useRef(null);
4261
- useImperativeHandle(
4262
- ref,
4263
- () => ({
4264
- focus: () => triggerRef.current?.focus(),
4265
- open: () => setOpen(true)
4266
- }),
4267
- []
4268
- );
4360
+ const searchInputRef = useRef(null);
4361
+ const listRef = useRef(null);
4269
4362
  const flat = useMemo(() => flattenOptions(options), [options]);
4270
4363
  const selectable = useMemo(() => selectableOptions(flat), [flat]);
4271
4364
  const hasLeaf = useMemo(() => flat.some((o) => !o.isGroup), [flat]);
@@ -4283,6 +4376,46 @@ var SSelect = forwardRef(function SSelect2({
4283
4376
  const arr = Array.isArray(value) ? value : [value];
4284
4377
  return arr.map((v) => v != null && typeof v === "object" ? v.value : v).filter((v) => v != null && v !== "").map(String);
4285
4378
  }, [value]);
4379
+ const selectedRef = useRef(selected);
4380
+ selectedRef.current = selected;
4381
+ const emit = (next) => {
4382
+ if (!onValueChange) return;
4383
+ const mapped = next.map(
4384
+ (v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
4385
+ );
4386
+ onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
4387
+ };
4388
+ const commitOpen = (next) => {
4389
+ setOpen(next);
4390
+ onOpenChange?.(next);
4391
+ if (!next) {
4392
+ setPendingOpen(false);
4393
+ setSearch("");
4394
+ if (rules && rules.length > 0) {
4395
+ setRuleError(runRules(multi ? selectedRef.current : selectedRef.current[0] ?? "", rules));
4396
+ }
4397
+ }
4398
+ };
4399
+ const requestOpen = (next) => {
4400
+ if (next && insideModal && portalContainer == null) {
4401
+ setPendingOpen(true);
4402
+ return;
4403
+ }
4404
+ commitOpen(next);
4405
+ };
4406
+ useEffect(() => {
4407
+ if (!insideModal || portalContainer == null || !pendingOpen) return;
4408
+ setPendingOpen(false);
4409
+ commitOpen(true);
4410
+ }, [insideModal, pendingOpen, portalContainer]);
4411
+ useImperativeHandle(
4412
+ ref,
4413
+ () => ({
4414
+ focus: () => triggerRef.current?.focus(),
4415
+ open: () => requestOpen(true)
4416
+ }),
4417
+ [insideModal, portalContainer]
4418
+ );
4286
4419
  useFormField({
4287
4420
  name,
4288
4421
  validate: () => {
@@ -4292,13 +4425,6 @@ var SSelect = forwardRef(function SSelect2({
4292
4425
  },
4293
4426
  resetValidation: () => setRuleError("")
4294
4427
  });
4295
- const emit = (next) => {
4296
- if (!onValueChange) return;
4297
- const mapped = next.map(
4298
- (v) => emitValue ? rawByValue.get(v) ?? v : optionByValue.get(v) ?? { value: v, label: v }
4299
- );
4300
- onValueChange(multi ? mapped : mapped[0] ?? (emitValue ? "" : null));
4301
- };
4302
4428
  const filtered = useMemo(() => {
4303
4429
  if (!showSearch || search.trim() === "") return flat;
4304
4430
  const q = search.trim().toLowerCase();
@@ -4309,6 +4435,16 @@ var SSelect = forwardRef(function SSelect2({
4309
4435
  const allValues = selectable.map((o) => o.value);
4310
4436
  const allSelected = allValues.length > 0 && allValues.every((v) => selected.includes(v));
4311
4437
  const hasItems = filtered.some((o) => !o.isGroup);
4438
+ const navigableItems = useMemo(() => {
4439
+ const items = [];
4440
+ if (multi && useSelectAll && allValues.length > 0) {
4441
+ items.push({ value: "__select_all__", isSelectAll: true });
4442
+ }
4443
+ filtered.forEach((o) => {
4444
+ if (!o.isGroup && !o.disabled) items.push({ value: o.value });
4445
+ });
4446
+ return items;
4447
+ }, [allValues.length, filtered, multi, useSelectAll]);
4312
4448
  const triggerText = (() => {
4313
4449
  if (selected.length === 0) return placeholder;
4314
4450
  if (multi) {
@@ -4318,26 +4454,119 @@ var SSelect = forwardRef(function SSelect2({
4318
4454
  return labelByValue.get(selected[0]) ?? selected[0];
4319
4455
  })();
4320
4456
  const isPlaceholder = selected.length === 0;
4321
- const selectStore = /* @__PURE__ */ jsxs(
4322
- Ariakit.SelectProvider,
4457
+ const handleSelect = (nextValue) => {
4458
+ if (multi) {
4459
+ const next = selected.includes(nextValue) ? selected.filter((v) => v !== nextValue) : [...selected, nextValue];
4460
+ emit(next);
4461
+ return;
4462
+ }
4463
+ emit([nextValue]);
4464
+ commitOpen(false);
4465
+ };
4466
+ const handleSelectAll = () => {
4467
+ emit(allSelected ? [] : allValues);
4468
+ };
4469
+ useEffect(() => {
4470
+ if (!open) {
4471
+ setFocusedIndex(-1);
4472
+ return;
4473
+ }
4474
+ setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
4475
+ }, [open, navigableItems.length]);
4476
+ useEffect(() => {
4477
+ if (!open) return;
4478
+ setFocusedIndex(navigableItems.length > 0 ? 0 : -1);
4479
+ }, [search, open, navigableItems.length]);
4480
+ useEffect(() => {
4481
+ if (!open || focusedIndex < 0) return;
4482
+ const list = listRef.current;
4483
+ const focusedEl = list?.querySelector('[data-select-focused="true"]');
4484
+ if (!list || !focusedEl) return;
4485
+ const listRect = list.getBoundingClientRect();
4486
+ const itemRect = focusedEl.getBoundingClientRect();
4487
+ if (itemRect.top < listRect.top) {
4488
+ list.scrollTop += itemRect.top - listRect.top;
4489
+ } else if (itemRect.bottom > listRect.bottom) {
4490
+ list.scrollTop += itemRect.bottom - listRect.bottom;
4491
+ }
4492
+ }, [focusedIndex, open]);
4493
+ const moveFocusedIndex = (direction) => {
4494
+ if (!open || navigableItems.length === 0) return;
4495
+ setFocusedIndex((current) => {
4496
+ if (direction === "next") {
4497
+ return current < 0 ? 0 : (current + 1) % navigableItems.length;
4498
+ }
4499
+ return current <= 0 ? navigableItems.length - 1 : current - 1;
4500
+ });
4501
+ };
4502
+ const selectFocusedItem = () => {
4503
+ if (focusedIndex < 0 || focusedIndex >= navigableItems.length) return;
4504
+ const focused = navigableItems[focusedIndex];
4505
+ if (focused.isSelectAll) {
4506
+ handleSelectAll();
4507
+ return;
4508
+ }
4509
+ handleSelect(focused.value);
4510
+ };
4511
+ const handleKeyDown = (event) => {
4512
+ if (!open) {
4513
+ if ((event.key === "ArrowDown" || event.key === "ArrowUp") && !effectiveDisabled) {
4514
+ event.preventDefault();
4515
+ requestOpen(true);
4516
+ }
4517
+ return;
4518
+ }
4519
+ if (event.key === "ArrowDown") {
4520
+ event.preventDefault();
4521
+ moveFocusedIndex("next");
4522
+ return;
4523
+ }
4524
+ if (event.key === "ArrowUp") {
4525
+ event.preventDefault();
4526
+ moveFocusedIndex("prev");
4527
+ return;
4528
+ }
4529
+ if (event.key === "Enter") {
4530
+ event.preventDefault();
4531
+ selectFocusedItem();
4532
+ return;
4533
+ }
4534
+ if (event.key === "Escape") {
4535
+ event.preventDefault();
4536
+ commitOpen(false);
4537
+ }
4538
+ };
4539
+ return /* @__PURE__ */ jsxs(
4540
+ SField,
4323
4541
  {
4324
- animated: portalContainer == null ? 150 : false,
4325
- value: multi ? selected : selected[0] ?? "",
4326
- setValue: (v) => emit(Array.isArray(v) ? v : v === "" ? [] : [v]),
4327
- open,
4328
- setOpen: (o) => {
4329
- setOpen(o);
4330
- onOpenChange?.(o);
4331
- if (!o && rules && rules.length > 0) {
4332
- setRuleError(runRules(multi ? selected : selected[0] ?? "", rules));
4333
- }
4334
- },
4542
+ label,
4543
+ labelWidth,
4544
+ icon,
4545
+ iconColor,
4546
+ labelTooltip,
4547
+ labelTooltipProps,
4548
+ addonLabel,
4549
+ addonAlign,
4550
+ hint,
4551
+ error: error || ruleError !== "",
4552
+ status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
4553
+ errorMessage: ruleError !== "" ? ruleError : errorMessage,
4554
+ disabled: effectiveDisabled,
4555
+ width,
4556
+ className,
4557
+ style,
4335
4558
  children: [
4559
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected.join(",") }),
4336
4560
  /* @__PURE__ */ jsxs(
4337
- Ariakit.Select,
4561
+ "button",
4338
4562
  {
4339
4563
  ref: triggerRef,
4564
+ type: "button",
4340
4565
  disabled: effectiveDisabled,
4566
+ "aria-haspopup": "listbox",
4567
+ "aria-expanded": open,
4568
+ onClick: () => requestOpen(!open),
4569
+ onKeyDown: handleKeyDown,
4341
4570
  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",
4342
4571
  style: {
4343
4572
  color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
@@ -4362,178 +4591,237 @@ var SSelect = forwardRef(function SSelect2({
4362
4591
  ]
4363
4592
  }
4364
4593
  ),
4365
- /* @__PURE__ */ jsxs(
4366
- Ariakit.SelectPopover,
4594
+ /* @__PURE__ */ jsx(
4595
+ SPortal,
4367
4596
  {
4368
- portal: true,
4369
- portalElement: portalContainer,
4370
- gutter: 4,
4371
- sameWidth: !dropdownWidth,
4372
- 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",
4597
+ open,
4598
+ onClose: () => commitOpen(false),
4599
+ onOpenChange: (next) => {
4600
+ if (next) requestOpen(true);
4601
+ },
4602
+ anchorRef: triggerRef,
4603
+ placement: "bottom",
4604
+ align: "start",
4605
+ offset: 4,
4606
+ fit: !dropdownWidth,
4607
+ onOpenAutoFocus: (event) => {
4608
+ if (!showSearch) {
4609
+ event.preventDefault();
4610
+ return;
4611
+ }
4612
+ event.preventDefault();
4613
+ requestAnimationFrame(() => searchInputRef.current?.focus());
4614
+ },
4615
+ onCloseAutoFocus: (event) => {
4616
+ event.preventDefault();
4617
+ triggerRef.current?.focus();
4618
+ },
4619
+ className: "rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
4373
4620
  style: {
4374
- maxHeight: dropdownHeight,
4375
4621
  ...dropdownWidth ? { width: dropdownWidth } : {},
4376
4622
  maxWidth: maxDropdownWidth
4377
4623
  },
4378
- children: [
4379
- showSearch && // 원본 sd-select-list-item-search: sticky + 검색 아이콘 + 테두리 input + focus-within + clear
4380
- /* @__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: [
4381
- /* @__PURE__ */ jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
4382
- /* @__PURE__ */ jsx(
4383
- Ariakit.Combobox,
4384
- {
4385
- autoSelect: true,
4386
- placeholder: "\uAC80\uC0C9",
4387
- 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)]"
4388
- }
4389
- ),
4390
- search !== "" && /* @__PURE__ */ jsx(
4391
- SGhostButton,
4392
- {
4393
- icon: "close",
4394
- size: "xxs",
4395
- intent: "default",
4396
- ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
4397
- onClick: () => setSearch(""),
4398
- className: "shrink-0"
4399
- }
4400
- )
4401
- ] }) }),
4402
- multi && useSelectAll && /* @__PURE__ */ jsx(
4403
- "button",
4404
- {
4405
- type: "button",
4406
- 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)]",
4407
- onClick: () => emit(allSelected ? [] : allValues),
4408
- children: allSelectedLabel
4409
- }
4410
- ),
4411
- hasItems ? /* @__PURE__ */ jsx(ItemList, { items: filtered, multi, selected, useSearch: showSearch }) : (
4412
- // 원본 sd-select-listbox__empty (표시할 항목이 없을 때)
4413
- /* @__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." })
4414
- )
4415
- ]
4624
+ children: /* @__PURE__ */ jsxs(
4625
+ "div",
4626
+ {
4627
+ ref: listRef,
4628
+ 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)]",
4629
+ style: { maxHeight: dropdownHeight },
4630
+ onMouseLeave: () => setFocusedIndex(-1),
4631
+ children: [
4632
+ 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: [
4633
+ /* @__PURE__ */ jsx(SIcon, { name: "search", size: 16, color: "var(--color-grey-70)", className: "shrink-0" }),
4634
+ /* @__PURE__ */ jsx(
4635
+ "input",
4636
+ {
4637
+ ref: searchInputRef,
4638
+ value: search,
4639
+ onChange: (e) => setSearch(e.target.value),
4640
+ onKeyDown: handleKeyDown,
4641
+ placeholder: "\uAC80\uC0C9",
4642
+ 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)]"
4643
+ }
4644
+ ),
4645
+ search !== "" && /* @__PURE__ */ jsx(
4646
+ SGhostButton,
4647
+ {
4648
+ icon: "close",
4649
+ size: "xxs",
4650
+ intent: "default",
4651
+ ariaLabel: "\uAC80\uC0C9\uC5B4 \uC9C0\uC6B0\uAE30",
4652
+ onClick: () => setSearch(""),
4653
+ className: "shrink-0"
4654
+ }
4655
+ )
4656
+ ] }) }),
4657
+ hasItems ? /* @__PURE__ */ jsx(
4658
+ ItemList,
4659
+ {
4660
+ items: filtered,
4661
+ multi,
4662
+ selected,
4663
+ focusedValue: navigableItems[focusedIndex]?.isSelectAll ? "__select_all__" : navigableItems[focusedIndex]?.value,
4664
+ showSelectAll: multi && useSelectAll && allValues.length > 0,
4665
+ selectAllLabel: allSelectedLabel,
4666
+ selectAllSelected: allSelected,
4667
+ onSelectAll: handleSelectAll,
4668
+ onSelect: handleSelect,
4669
+ onHoverValue: (value2) => {
4670
+ if (value2 == null) {
4671
+ setFocusedIndex(-1);
4672
+ return;
4673
+ }
4674
+ const index = navigableItems.findIndex(
4675
+ (item) => item.isSelectAll ? value2 === "__select_all__" : item.value === value2
4676
+ );
4677
+ if (index >= 0) setFocusedIndex(index);
4678
+ }
4679
+ }
4680
+ ) : /* @__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." })
4681
+ ]
4682
+ }
4683
+ )
4416
4684
  }
4417
4685
  )
4418
4686
  ]
4419
4687
  }
4420
4688
  );
4421
- const control = showSearch ? /* @__PURE__ */ jsx(Ariakit.ComboboxProvider, { value: search, setValue: setSearch, resetValueOnHide: true, children: selectStore }) : selectStore;
4422
- return /* @__PURE__ */ jsxs(
4423
- SField,
4424
- {
4425
- label,
4426
- labelWidth,
4427
- icon,
4428
- labelTooltip,
4429
- labelTooltipProps,
4430
- addonLabel,
4431
- addonAlign,
4432
- hint,
4433
- error: error || ruleError !== "",
4434
- status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
4435
- errorMessage: ruleError !== "" ? ruleError : errorMessage,
4436
- disabled: effectiveDisabled,
4437
- width,
4438
- className,
4439
- style,
4440
- children: [
4441
- name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected.join(",") }),
4442
- control
4443
- ]
4444
- }
4445
- );
4446
4689
  });
4447
4690
  SSelect.displayName = "SSelect";
4448
4691
  function ItemList({
4449
4692
  items,
4450
4693
  multi,
4451
4694
  selected,
4452
- useSearch
4695
+ focusedValue,
4696
+ showSelectAll,
4697
+ selectAllLabel,
4698
+ selectAllSelected,
4699
+ onSelectAll,
4700
+ onSelect,
4701
+ onHoverValue
4453
4702
  }) {
4454
4703
  const itemBase = cn(
4455
- // group: 체크박스 inverse(group-data-[active-item])를 위해 필요
4456
- "group flex cursor-pointer select-none items-center gap-[8px] text-[12px] leading-[20px] outline-none",
4704
+ "group flex w-full cursor-pointer select-none items-center gap-[8px] border-none bg-transparent text-left text-[12px] leading-[20px] outline-none",
4457
4705
  "text-[color:var(--cmp-listItem-content-default)]",
4458
- // Ariakit은 disabled 항목에 aria-disabled를 붙이므로 aria-disabled 변형으로 스타일 (data-disabled 아님)
4459
- "aria-disabled:cursor-not-allowed aria-disabled:text-[color:var(--cmp-listItem-content-disabled)]"
4706
+ "disabled:cursor-not-allowed disabled:text-[color:var(--cmp-listItem-content-disabled)]"
4460
4707
  );
4461
- const activeClass = "data-[active-item]:bg-[var(--cmp-listItem-bg-hover)] data-[active-item]:text-[color:var(--cmp-listItem-content-hover)]";
4462
- return /* @__PURE__ */ jsx(Fragment, { children: items.map((o) => {
4463
- const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
4464
- if (o.isGroup) {
4465
- const isDepth1 = o.depth === 1;
4466
- return /* @__PURE__ */ jsx(
4467
- "div",
4468
- {
4469
- className: cn(
4470
- "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
4471
- 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",
4472
- // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
4473
- // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
4474
- o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
4475
- o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
4476
- ),
4477
- style: { paddingLeft: padLeft },
4478
- children: o.label
4479
- },
4480
- o.value
4481
- );
4482
- }
4483
- const isSelected = selected.includes(o.value);
4484
- return /* @__PURE__ */ jsxs(
4485
- Ariakit.SelectItem,
4708
+ 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)]";
4709
+ return /* @__PURE__ */ jsxs("div", { role: "listbox", "aria-multiselectable": multi || void 0, children: [
4710
+ showSelectAll && /* @__PURE__ */ jsxs(
4711
+ "button",
4486
4712
  {
4487
- value: o.value,
4488
- disabled: o.disabled,
4489
- accessibleWhenDisabled: true,
4713
+ type: "button",
4714
+ "data-select-focused": focusedValue === "__select_all__" ? "true" : void 0,
4715
+ "data-select-active": focusedValue === "__select_all__" ? "true" : void 0,
4490
4716
  className: cn(
4491
4717
  itemBase,
4492
- isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
4493
- // Ariakit은 disabled 항목도 hover 시 active(data-active-item) 처리하므로,
4494
- // 비활성 항목에는 hover 하이라이트를 적용하지 않아 회색 비활성 스타일을 유지한다.
4495
- // (원본 Stencil의 `--focused...:not(--disabled)` 규칙과 동일한 의도)
4496
- !o.disabled && activeClass
4718
+ focusedValue === "__select_all__" && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]"
4497
4719
  ),
4498
- style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
4499
- render: useSearch ? /* @__PURE__ */ jsx(Ariakit.ComboboxItem, {}) : void 0,
4720
+ style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: 12 },
4721
+ onMouseEnter: () => onHoverValue("__select_all__"),
4722
+ onMouseLeave: () => onHoverValue(null),
4723
+ onClick: onSelectAll,
4500
4724
  children: [
4501
- multi && // hover(active) 시 checkbox inverse — 파란 hover 배경 위에서 checked/unchecked 모두 흰 테두리 + 투명 배경
4502
4725
  /* @__PURE__ */ jsx(
4503
4726
  "span",
4504
4727
  {
4505
4728
  className: cn(
4506
4729
  "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
4507
- o.disabled ? (
4508
- // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
4509
- "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
4510
- ) : cn(
4511
- "group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
4512
- 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"
4513
- )
4730
+ 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)]"
4514
4731
  ),
4515
- children: isSelected && /* @__PURE__ */ jsx(
4732
+ children: selectAllSelected && /* @__PURE__ */ jsx(
4516
4733
  SIcon,
4517
4734
  {
4518
4735
  name: "checkboxCheck",
4519
4736
  size: 12,
4520
- color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
4737
+ color: "var(--cmp-checkbox-checked-icon-default)"
4521
4738
  }
4522
4739
  )
4523
4740
  }
4524
4741
  ),
4525
- isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
4526
- "span",
4527
- {
4528
- className: "flex-1 truncate",
4529
- dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
4530
- }
4531
- ) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
4742
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: selectAllLabel })
4532
4743
  ]
4533
- },
4534
- o.value
4535
- );
4536
- }) });
4744
+ }
4745
+ ),
4746
+ items.map((o) => {
4747
+ const padLeft = o.depth === 1 ? 12 : o.depth === 2 ? 20 : 28;
4748
+ if (o.isGroup) {
4749
+ const isDepth1 = o.depth === 1;
4750
+ return /* @__PURE__ */ jsx(
4751
+ "div",
4752
+ {
4753
+ className: cn(
4754
+ "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
4755
+ 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",
4756
+ // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
4757
+ // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
4758
+ o.disabled && "cursor-not-allowed text-[color:var(--cmp-listItem-content-disabled)]",
4759
+ o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
4760
+ ),
4761
+ style: { paddingLeft: padLeft },
4762
+ children: o.label
4763
+ },
4764
+ o.value
4765
+ );
4766
+ }
4767
+ const isSelected = selected.includes(o.value);
4768
+ return /* @__PURE__ */ jsxs(
4769
+ "button",
4770
+ {
4771
+ type: "button",
4772
+ disabled: o.disabled,
4773
+ role: "option",
4774
+ "aria-selected": isSelected,
4775
+ "data-select-focused": focusedValue === o.value ? "true" : void 0,
4776
+ "data-select-active": focusedValue === o.value ? "true" : void 0,
4777
+ className: cn(
4778
+ itemBase,
4779
+ isSelected && !multi && "font-bold text-[color:var(--cmp-listItem-content-selected)]",
4780
+ focusedValue === o.value && "bg-[var(--cmp-listItem-bg-hover)] text-[color:var(--cmp-listItem-content-hover)]",
4781
+ // 비활성 항목은 hover/focus 스타일 대상에서 제외한다.
4782
+ // (:hover 는 disabled 버튼에도 매칭되므로 클래스 자체를 붙이지 않아야 한다)
4783
+ !o.disabled && activeClass
4784
+ ),
4785
+ style: { paddingTop: 4, paddingBottom: 4, paddingRight: 12, paddingLeft: padLeft },
4786
+ onMouseEnter: () => onHoverValue(o.value),
4787
+ onMouseLeave: () => onHoverValue(null),
4788
+ onClick: () => onSelect(o.value),
4789
+ children: [
4790
+ multi && /* @__PURE__ */ jsx(
4791
+ "span",
4792
+ {
4793
+ className: cn(
4794
+ "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
4795
+ o.disabled ? (
4796
+ // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
4797
+ "cursor-not-allowed border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
4798
+ ) : cn(
4799
+ 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)]"
4800
+ )
4801
+ ),
4802
+ children: isSelected && /* @__PURE__ */ jsx(
4803
+ SIcon,
4804
+ {
4805
+ name: "checkboxCheck",
4806
+ size: 12,
4807
+ color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
4808
+ }
4809
+ )
4810
+ }
4811
+ ),
4812
+ isHtmlLabel(o.label) ? /* @__PURE__ */ jsx(
4813
+ "span",
4814
+ {
4815
+ className: "flex-1 truncate",
4816
+ dangerouslySetInnerHTML: { __html: sanitizeInlineHtml(o.label) }
4817
+ }
4818
+ ) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: o.label })
4819
+ ]
4820
+ },
4821
+ o.value
4822
+ );
4823
+ })
4824
+ ] });
4537
4825
  }
4538
4826
  function SRadio({
4539
4827
  val,
@@ -4996,6 +5284,7 @@ function SDatePicker({
4996
5284
  label,
4997
5285
  labelWidth,
4998
5286
  icon,
5287
+ iconColor,
4999
5288
  labelTooltip,
5000
5289
  labelTooltipProps,
5001
5290
  addonLabel,
@@ -5034,6 +5323,7 @@ function SDatePicker({
5034
5323
  label,
5035
5324
  labelWidth,
5036
5325
  icon,
5326
+ iconColor,
5037
5327
  labelTooltip,
5038
5328
  labelTooltipProps,
5039
5329
  addonLabel,
@@ -5302,6 +5592,7 @@ function SDateRangePicker({
5302
5592
  label,
5303
5593
  labelWidth,
5304
5594
  icon,
5595
+ iconColor,
5305
5596
  labelTooltip,
5306
5597
  labelTooltipProps,
5307
5598
  addonLabel,
@@ -5344,6 +5635,7 @@ function SDateRangePicker({
5344
5635
  label,
5345
5636
  labelWidth,
5346
5637
  icon,
5638
+ iconColor,
5347
5639
  labelTooltip,
5348
5640
  labelTooltipProps,
5349
5641
  addonLabel,
@@ -5605,6 +5897,7 @@ function SFilePicker({
5605
5897
  label,
5606
5898
  labelWidth,
5607
5899
  icon,
5900
+ iconColor,
5608
5901
  labelTooltip,
5609
5902
  labelTooltipProps,
5610
5903
  addonLabel,
@@ -5774,7 +6067,7 @@ function SFilePicker({
5774
6067
  unmountOnHide: true,
5775
6068
  getAnchorRect: () => contentBoxRef.current?.getBoundingClientRect() ?? null,
5776
6069
  style: { minWidth: contentBoxRef.current?.offsetWidth },
5777
- 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)]",
6070
+ 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)]",
5778
6071
  children: filesArr.map((f, i) => /* @__PURE__ */ jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i))
5779
6072
  }
5780
6073
  )
@@ -5788,6 +6081,7 @@ function SFilePicker({
5788
6081
  label,
5789
6082
  labelWidth,
5790
6083
  icon,
6084
+ iconColor,
5791
6085
  labelTooltip,
5792
6086
  labelTooltipProps,
5793
6087
  addonLabel,
@@ -5810,21 +6104,26 @@ function SFilePicker({
5810
6104
  var cssLen2 = (v) => v == null ? void 0 : typeof v === "number" ? `${v}px` : v;
5811
6105
  function SKeyValueTable({
5812
6106
  fields = [],
6107
+ values = {},
5813
6108
  search = false,
5814
6109
  radius = "default",
5815
- dense = false,
5816
6110
  onChange,
5817
6111
  onSearch,
5818
6112
  className,
5819
6113
  style
5820
6114
  }) {
5821
6115
  if (fields.length === 0) return null;
5822
- const emit = (name, value) => onChange?.({ name, value });
5823
- const cellHeight = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-keyValue-height)";
5824
- const cellPadY = dense ? "var(--cmp-table-body-paddingY-dense)" : "var(--cmp-table-keyValue-body-paddingY)";
6116
+ const emit = (name, value) => onChange?.({
6117
+ name,
6118
+ value,
6119
+ values: { ...values, [name]: value }
6120
+ });
6121
+ const cellHeight = "var(--cmp-table-keyValue-height)";
6122
+ const cellPadY = "var(--cmp-table-keyValue-body-paddingY)";
5825
6123
  const renderField = (f) => {
5826
6124
  if (f.render) return f.render;
5827
- const opt = f.options ?? {};
6125
+ const opt = { ...f.options ?? {} };
6126
+ if (Object.prototype.hasOwnProperty.call(values, f.name)) opt.value = values[f.name];
5828
6127
  const up = (v) => emit(f.name, v);
5829
6128
  switch (f.type) {
5830
6129
  case "input":
@@ -5936,7 +6235,10 @@ function SKeyValueTable({
5936
6235
  rowSpan: f.tdRowSpan && f.tdRowSpan > 1 ? f.tdRowSpan : void 0,
5937
6236
  colSpan: f.tdColSpan && f.tdColSpan > 1 ? f.tdColSpan : void 0,
5938
6237
  className: cn(
5939
- "box-border bg-[var(--sys-color-bg-frame)] px-[var(--cmp-table-keyValue-body-paddingX-default)] align-middle",
6238
+ "box-border bg-[var(--sys-color-bg-frame)] align-middle",
6239
+ // text 타입(및 미지정 기본값)은 읽기 전용 콘텐츠라 넓은 paddingX(wide),
6240
+ // 폼 필드 셀은 default paddingX 유지
6241
+ f.type == null || f.type === "text" ? "px-[var(--cmp-table-keyValue-body-paddingX-wide)]" : "px-[var(--cmp-table-keyValue-body-paddingX-default)]",
5940
6242
  cellBorder,
5941
6243
  f.tdClass
5942
6244
  ),