sellmate-design-system-react 0.1.7 → 0.1.9

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 (146) hide show
  1. package/dist/components/SActionModal/SActionModal.cjs +11 -3
  2. package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
  3. package/dist/components/SActionModal/SActionModal.js +11 -3
  4. package/dist/components/SActionModal/SActionModal.js.map +1 -1
  5. package/dist/components/SBarcodeInput/SBarcodeInput.cjs +226 -209
  6. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  7. package/dist/components/SBarcodeInput/SBarcodeInput.js +226 -209
  8. package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
  9. package/dist/components/SCalendar/SCalendar.cjs +9 -1
  10. package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
  11. package/dist/components/SCalendar/SCalendar.js +9 -1
  12. package/dist/components/SCalendar/SCalendar.js.map +1 -1
  13. package/dist/components/SChip/SChip.cjs +9 -1
  14. package/dist/components/SChip/SChip.cjs.map +1 -1
  15. package/dist/components/SChip/SChip.d.ts +1 -1
  16. package/dist/components/SChip/SChip.js +9 -1
  17. package/dist/components/SChip/SChip.js.map +1 -1
  18. package/dist/components/SChipInput/SChipInput.cjs +260 -235
  19. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  20. package/dist/components/SChipInput/SChipInput.js +259 -235
  21. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  22. package/dist/components/SCircleProgress/SCircleProgress.cjs +56 -58
  23. package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
  24. package/dist/components/SCircleProgress/SCircleProgress.js +56 -58
  25. package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
  26. package/dist/components/SConfirmModal/SConfirmModal.cjs +9 -1
  27. package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
  28. package/dist/components/SConfirmModal/SConfirmModal.js +9 -1
  29. package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
  30. package/dist/components/SDatePicker/SDatePicker.cjs +247 -221
  31. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  32. package/dist/components/SDatePicker/SDatePicker.js +247 -221
  33. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  34. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +308 -256
  35. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  36. package/dist/components/SDateRangePicker/SDateRangePicker.js +308 -256
  37. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  38. package/dist/components/SDropdownButton/SDropdownButton.cjs +51 -4
  39. package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
  40. package/dist/components/SDropdownButton/SDropdownButton.js +52 -5
  41. package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
  42. package/dist/components/SField/SField.cjs +226 -21
  43. package/dist/components/SField/SField.cjs.map +1 -1
  44. package/dist/components/SField/SField.d.ts +11 -0
  45. package/dist/components/SField/SField.js +226 -21
  46. package/dist/components/SField/SField.js.map +1 -1
  47. package/dist/components/SFilePicker/SFilePicker.cjs +306 -278
  48. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  49. package/dist/components/SFilePicker/SFilePicker.d.ts +4 -2
  50. package/dist/components/SFilePicker/SFilePicker.js +306 -278
  51. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  52. package/dist/components/SGhostButton/SGhostButton.cjs +8 -0
  53. package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
  54. package/dist/components/SGhostButton/SGhostButton.js +8 -0
  55. package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
  56. package/dist/components/SGuide/SGuide.cjs +12 -1
  57. package/dist/components/SGuide/SGuide.cjs.map +1 -1
  58. package/dist/components/SGuide/SGuide.js +12 -1
  59. package/dist/components/SGuide/SGuide.js.map +1 -1
  60. package/dist/components/SInput/SInput.cjs +226 -209
  61. package/dist/components/SInput/SInput.cjs.map +1 -1
  62. package/dist/components/SInput/SInput.js +226 -209
  63. package/dist/components/SInput/SInput.js.map +1 -1
  64. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +640 -453
  65. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  66. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +3 -1
  67. package/dist/components/SKeyValueTable/SKeyValueTable.js +640 -453
  68. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  69. package/dist/components/SLoadingContainer/SLoadingContainer.cjs +64 -58
  70. package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
  71. package/dist/components/SLoadingContainer/SLoadingContainer.js +64 -58
  72. package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
  73. package/dist/components/SLoadingContainer/loading.cjs +64 -58
  74. package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
  75. package/dist/components/SLoadingContainer/loading.js +64 -58
  76. package/dist/components/SLoadingContainer/loading.js.map +1 -1
  77. package/dist/components/SLoadingModal/SLoadingModal.cjs +68 -61
  78. package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
  79. package/dist/components/SLoadingModal/SLoadingModal.js +68 -61
  80. package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
  81. package/dist/components/SModalContainer/SModalContainer.cjs +9 -1
  82. package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
  83. package/dist/components/SModalContainer/SModalContainer.js +9 -1
  84. package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
  85. package/dist/components/SNumberInput/SNumberInput.cjs +334 -50
  86. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  87. package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
  88. package/dist/components/SNumberInput/SNumberInput.js +334 -50
  89. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  90. package/dist/components/SPagination/SPagination.cjs +1 -1
  91. package/dist/components/SPagination/SPagination.cjs.map +1 -1
  92. package/dist/components/SPagination/SPagination.js +1 -1
  93. package/dist/components/SPagination/SPagination.js.map +1 -1
  94. package/dist/components/SPopover/SPopover.cjs +20 -3
  95. package/dist/components/SPopover/SPopover.cjs.map +1 -1
  96. package/dist/components/SPopover/SPopover.js +20 -4
  97. package/dist/components/SPopover/SPopover.js.map +1 -1
  98. package/dist/components/SPopup/SPopup.cjs +2 -2
  99. package/dist/components/SPopup/SPopup.cjs.map +1 -1
  100. package/dist/components/SPopup/SPopup.js +2 -2
  101. package/dist/components/SPopup/SPopup.js.map +1 -1
  102. package/dist/components/SPortal/SPortal.cjs +8 -0
  103. package/dist/components/SPortal/SPortal.cjs.map +1 -1
  104. package/dist/components/SPortal/SPortal.js +8 -0
  105. package/dist/components/SPortal/SPortal.js.map +1 -1
  106. package/dist/components/SSelect/SSelect.cjs +76 -32
  107. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  108. package/dist/components/SSelect/SSelect.js +76 -32
  109. package/dist/components/SSelect/SSelect.js.map +1 -1
  110. package/dist/components/STable/STable.cjs +133 -91
  111. package/dist/components/STable/STable.cjs.map +1 -1
  112. package/dist/components/STable/STable.js +133 -91
  113. package/dist/components/STable/STable.js.map +1 -1
  114. package/dist/components/STextLink/STextLink.cjs +137 -3
  115. package/dist/components/STextLink/STextLink.cjs.map +1 -1
  116. package/dist/components/STextLink/STextLink.d.ts +1 -1
  117. package/dist/components/STextLink/STextLink.js +118 -4
  118. package/dist/components/STextLink/STextLink.js.map +1 -1
  119. package/dist/components/STextarea/STextarea.cjs +226 -21
  120. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  121. package/dist/components/STextarea/STextarea.js +226 -21
  122. package/dist/components/STextarea/STextarea.js.map +1 -1
  123. package/dist/components/SToast/SToast.cjs +8 -0
  124. package/dist/components/SToast/SToast.cjs.map +1 -1
  125. package/dist/components/SToast/SToast.js +8 -0
  126. package/dist/components/SToast/SToast.js.map +1 -1
  127. package/dist/components/SToast/SToastContainer.cjs +11 -3
  128. package/dist/components/SToast/SToastContainer.cjs.map +1 -1
  129. package/dist/components/SToast/SToastContainer.js +11 -3
  130. package/dist/components/SToast/SToastContainer.js.map +1 -1
  131. package/dist/components/STooltip/STooltip.cjs +217 -14
  132. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  133. package/dist/components/STooltip/STooltip.js +217 -14
  134. package/dist/components/STooltip/STooltip.js.map +1 -1
  135. package/dist/index.cjs +532 -300
  136. package/dist/index.cjs.map +1 -1
  137. package/dist/index.js +531 -300
  138. package/dist/index.js.map +1 -1
  139. package/dist/lib/floating-anim.cjs +8 -0
  140. package/dist/lib/floating-anim.cjs.map +1 -1
  141. package/dist/lib/floating-anim.d.ts +5 -1
  142. package/dist/lib/floating-anim.js +8 -0
  143. package/dist/lib/floating-anim.js.map +1 -1
  144. package/dist/styles.css +175 -66
  145. package/dist/theme.css +49 -22
  146. package/package.json +1 -1
@@ -5,7 +5,7 @@ var react = require('react');
5
5
  var clsx = require('clsx');
6
6
  var tailwindMerge = require('tailwind-merge');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
- var RPopover = require('@radix-ui/react-popover');
8
+ var RPopover2 = require('@radix-ui/react-popover');
9
9
  var Ariakit = require('@ariakit/react');
10
10
  var RCheckbox = require('@radix-ui/react-checkbox');
11
11
 
@@ -27,7 +27,7 @@ function _interopNamespace(e) {
27
27
  return Object.freeze(n);
28
28
  }
29
29
 
30
- var RPopover__namespace = /*#__PURE__*/_interopNamespace(RPopover);
30
+ var RPopover2__namespace = /*#__PURE__*/_interopNamespace(RPopover2);
31
31
  var Ariakit__namespace = /*#__PURE__*/_interopNamespace(Ariakit);
32
32
  var RCheckbox__namespace = /*#__PURE__*/_interopNamespace(RCheckbox);
33
33
 
@@ -2880,12 +2880,20 @@ var SIcon = react.forwardRef(function SIcon2({ name, size = 24, color, rotate, l
2880
2880
  // src/lib/floating-anim.ts
2881
2881
  var FLOATING_SLIDE_ANIM = [
2882
2882
  "data-[state=open]:data-[side=top]:animate-floating-in-top",
2883
+ "data-[state=delayed-open]:data-[side=top]:animate-floating-in-top",
2884
+ "data-[state=instant-open]:data-[side=top]:animate-floating-in-top",
2883
2885
  "data-[state=closed]:data-[side=top]:animate-floating-out-top",
2884
2886
  "data-[state=open]:data-[side=bottom]:animate-floating-in-bottom",
2887
+ "data-[state=delayed-open]:data-[side=bottom]:animate-floating-in-bottom",
2888
+ "data-[state=instant-open]:data-[side=bottom]:animate-floating-in-bottom",
2885
2889
  "data-[state=closed]:data-[side=bottom]:animate-floating-out-bottom",
2886
2890
  "data-[state=open]:data-[side=left]:animate-floating-in-left",
2891
+ "data-[state=delayed-open]:data-[side=left]:animate-floating-in-left",
2892
+ "data-[state=instant-open]:data-[side=left]:animate-floating-in-left",
2887
2893
  "data-[state=closed]:data-[side=left]:animate-floating-out-left",
2888
2894
  "data-[state=open]:data-[side=right]:animate-floating-in-right",
2895
+ "data-[state=delayed-open]:data-[side=right]:animate-floating-in-right",
2896
+ "data-[state=instant-open]:data-[side=right]:animate-floating-in-right",
2889
2897
  "data-[state=closed]:data-[side=right]:animate-floating-out-right"
2890
2898
  ].join(" ");
2891
2899
  var PortalContainerContext = react.createContext(null);
@@ -2893,11 +2901,200 @@ PortalContainerContext.Provider;
2893
2901
  function usePortalContainer() {
2894
2902
  return react.useContext(PortalContainerContext) ?? void 0;
2895
2903
  }
2904
+
2905
+ // src/components/STag/tag.config.ts
2906
+ var TAG_SIZE_CONFIG = {
2907
+ xs: {
2908
+ height: 20,
2909
+ paddingX: 8,
2910
+ gap: 4,
2911
+ radius: 6,
2912
+ iconSize: 12,
2913
+ fontSize: 11,
2914
+ fontWeight: 500,
2915
+ lineHeight: 18
2916
+ },
2917
+ sm: {
2918
+ height: 24,
2919
+ paddingX: 8,
2920
+ gap: 4,
2921
+ radius: 6,
2922
+ iconSize: 16,
2923
+ fontSize: 12,
2924
+ fontWeight: 700,
2925
+ lineHeight: 20
2926
+ },
2927
+ md: {
2928
+ height: 28,
2929
+ paddingX: 12,
2930
+ gap: 6,
2931
+ radius: 8,
2932
+ iconSize: 16,
2933
+ fontSize: 14,
2934
+ fontWeight: 700,
2935
+ lineHeight: 24
2936
+ }
2937
+ };
2938
+ var STag = react.forwardRef(function STag2({
2939
+ shape = "square",
2940
+ size = "sm",
2941
+ color = "grey",
2942
+ label = "",
2943
+ icon,
2944
+ iconLeft = true,
2945
+ width,
2946
+ className,
2947
+ style,
2948
+ ...rest
2949
+ }, ref) {
2950
+ const s = TAG_SIZE_CONFIG[size];
2951
+ const contentColor = `var(--cmp-tag-${color}-content)`;
2952
+ const iconNode = icon ? /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.iconSize, color: contentColor }) : null;
2953
+ const resolvedWidth = typeof width === "number" ? `${width}px` : width;
2954
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2955
+ "span",
2956
+ {
2957
+ ref,
2958
+ "aria-label": label || "tag",
2959
+ className: cn(
2960
+ "box-border inline-flex w-fit select-none items-center justify-center whitespace-nowrap",
2961
+ className
2962
+ ),
2963
+ style: {
2964
+ minHeight: s.height,
2965
+ padding: `0 ${s.paddingX}px`,
2966
+ gap: s.gap,
2967
+ borderRadius: shape === "pill" ? 999 : s.radius,
2968
+ fontSize: s.fontSize,
2969
+ fontWeight: s.fontWeight,
2970
+ lineHeight: `${s.lineHeight}px`,
2971
+ background: `var(--cmp-tag-${color}-bg)`,
2972
+ color: contentColor,
2973
+ ...resolvedWidth != null ? { width: resolvedWidth } : {},
2974
+ ...style
2975
+ },
2976
+ ...rest,
2977
+ children: [
2978
+ icon && iconLeft && iconNode,
2979
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex min-w-0 items-center justify-center", children: label }),
2980
+ icon && !iconLeft && iconNode
2981
+ ]
2982
+ }
2983
+ );
2984
+ });
2985
+ var SIZE = {
2986
+ xxs: { icon: 12, radius: "sm" },
2987
+ xs: { icon: 16, radius: "sm" },
2988
+ sm: { icon: 20, radius: "sm" },
2989
+ md: { icon: 24, radius: "md" },
2990
+ lg: { icon: 32, radius: "md" }
2991
+ };
2992
+ var TOOLTIP_HIDE_DELAY_MS = 100;
2993
+ function SGhostButton({
2994
+ icon,
2995
+ size = "sm",
2996
+ intent = "default",
2997
+ ariaLabel = "",
2998
+ tooltipText,
2999
+ disabled = false,
3000
+ onClick,
3001
+ className,
3002
+ style
3003
+ }) {
3004
+ const portalContainer = usePortalContainer();
3005
+ const [open, setOpen] = react.useState(false);
3006
+ const hideTimer = react.useRef(void 0);
3007
+ const cancelHide = () => {
3008
+ if (hideTimer.current) {
3009
+ clearTimeout(hideTimer.current);
3010
+ hideTimer.current = void 0;
3011
+ }
3012
+ };
3013
+ const openNow = () => {
3014
+ cancelHide();
3015
+ setOpen(true);
3016
+ };
3017
+ const startHide = () => {
3018
+ cancelHide();
3019
+ hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
3020
+ };
3021
+ const s = SIZE[size];
3022
+ const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
3023
+ const btn = /* @__PURE__ */ jsxRuntime.jsxs(
3024
+ "button",
3025
+ {
3026
+ type: "button",
3027
+ disabled,
3028
+ "aria-disabled": disabled ? "true" : void 0,
3029
+ "aria-label": ariaLabel.trim() || void 0,
3030
+ tabIndex: disabled ? -1 : void 0,
3031
+ onClick: (e) => {
3032
+ if (disabled) {
3033
+ e.preventDefault();
3034
+ return;
3035
+ }
3036
+ onClick?.(e);
3037
+ },
3038
+ className: cn(
3039
+ "group relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
3040
+ disabled ? "cursor-not-allowed" : "cursor-pointer",
3041
+ "focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
3042
+ className
3043
+ ),
3044
+ style: {
3045
+ width: `var(--cmp-ghostButton-${size}-size)`,
3046
+ height: `var(--cmp-ghostButton-${size}-size)`,
3047
+ borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
3048
+ ...style
3049
+ },
3050
+ children: [
3051
+ !disabled && /* @__PURE__ */ jsxRuntime.jsx(
3052
+ "span",
3053
+ {
3054
+ "aria-hidden": true,
3055
+ className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover:opacity-5",
3056
+ style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
3057
+ }
3058
+ ),
3059
+ /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
3060
+ ]
3061
+ }
3062
+ );
3063
+ const text = (tooltipText ?? "").trim();
3064
+ if (text === "") return btn;
3065
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
3066
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3067
+ "span",
3068
+ {
3069
+ className: "inline-flex",
3070
+ onMouseEnter: openNow,
3071
+ onMouseLeave: startHide,
3072
+ onFocus: openNow,
3073
+ onBlur: startHide,
3074
+ children: btn
3075
+ }
3076
+ ) }),
3077
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
3078
+ RPopover2__namespace.Content,
3079
+ {
3080
+ side: "top",
3081
+ sideOffset: 4,
3082
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
3083
+ "aria-hidden": true,
3084
+ onOpenAutoFocus: (e) => e.preventDefault(),
3085
+ onMouseEnter: cancelHide,
3086
+ onMouseLeave: startHide,
3087
+ children: /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
3088
+ }
3089
+ ) })
3090
+ ] });
3091
+ }
2896
3092
  var HIDE_DELAY_MS = 100;
2897
3093
  function useBody(content, message = []) {
2898
3094
  if (content != null) return content;
2899
3095
  if (message.length === 0) return null;
2900
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: line }, i)) });
3096
+ const singleLine = message.length === 1;
3097
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: singleLine ? "leading-[24px]" : void 0, children: line }, i)) });
2901
3098
  }
2902
3099
  var STooltip = react.forwardRef(function STooltip2({
2903
3100
  content,
@@ -2955,7 +3152,7 @@ var STooltip = react.forwardRef(function STooltip2({
2955
3152
  if (disabled || body == null) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: triggerNode });
2956
3153
  const isHover = trigger === "hover";
2957
3154
  const contentClass = cn(
2958
- "z-50 max-w-xs rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px]",
3155
+ "z-50 max-w-xs rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
2959
3156
  FLOATING_SLIDE_ANIM,
2960
3157
  className
2961
3158
  );
@@ -2965,10 +3162,10 @@ var STooltip = react.forwardRef(function STooltip2({
2965
3162
  fontWeight: tooltipType === "default" ? 500 : 700,
2966
3163
  ...style
2967
3164
  };
2968
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover__namespace.Root, { open, onOpenChange: setOpen, children: [
3165
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
2969
3166
  isHover ? (
2970
3167
  // hover: Anchor(위치 기준) + 직접 mouse 핸들러 — 클릭으로 토글되지 않도록 Trigger 미사용
2971
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3168
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
2972
3169
  "span",
2973
3170
  {
2974
3171
  className: "inline-flex",
@@ -2979,9 +3176,9 @@ var STooltip = react.forwardRef(function STooltip2({
2979
3176
  children: triggerNode
2980
3177
  }
2981
3178
  ) })
2982
- ) : /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Trigger, { asChild: true, children: triggerNode }),
2983
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
2984
- RPopover__namespace.Content,
3179
+ ) : /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: triggerNode }),
3180
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
3181
+ RPopover2__namespace.Content,
2985
3182
  {
2986
3183
  side: placement,
2987
3184
  sideOffset: 8,
@@ -3000,15 +3197,21 @@ var STooltip = react.forwardRef(function STooltip2({
3000
3197
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-[12px]", children: [
3001
3198
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: body }),
3002
3199
  useClose && /* @__PURE__ */ jsxRuntime.jsx(
3003
- RPopover__namespace.Close,
3200
+ SGhostButton,
3004
3201
  {
3005
- "aria-label": "\uB2EB\uAE30",
3006
- className: "shrink-0 cursor-pointer opacity-70 hover:opacity-100",
3007
- children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "close", size: 16, color: fg })
3202
+ icon: "close",
3203
+ size: "xs",
3204
+ intent: tooltipType === "default" ? "inverse" : "default",
3205
+ ariaLabel: "\uB2EB\uAE30",
3206
+ className: "shrink-0",
3207
+ onClick: () => {
3208
+ cancelHide();
3209
+ setOpen(false);
3210
+ }
3008
3211
  }
3009
3212
  )
3010
3213
  ] }),
3011
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
3214
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
3012
3215
  ]
3013
3216
  }
3014
3217
  ) })
@@ -3208,8 +3411,8 @@ function useFormField(field) {
3208
3411
  }, [ctx, name]);
3209
3412
  }
3210
3413
  var FIELD_SIZE = {
3211
- sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 12 },
3212
- md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 12 }
3414
+ sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
3415
+ md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
3213
3416
  };
3214
3417
  var ALIGN = {
3215
3418
  start: "flex-start",
@@ -3224,6 +3427,7 @@ var SField = react.forwardRef(function SField2({
3224
3427
  labelTooltip = "",
3225
3428
  labelTooltipProps,
3226
3429
  labelWidth,
3430
+ labelHeight,
3227
3431
  size = "sm",
3228
3432
  addonLabel = "",
3229
3433
  addonAlign = "start",
@@ -3238,6 +3442,7 @@ var SField = react.forwardRef(function SField2({
3238
3442
  readOnly = false,
3239
3443
  width,
3240
3444
  multiline = false,
3445
+ borderless = false,
3241
3446
  children,
3242
3447
  className,
3243
3448
  style,
@@ -3271,8 +3476,8 @@ var SField = react.forwardRef(function SField2({
3271
3476
  const resolvedSuccess = status === "pass";
3272
3477
  const showError = resolvedError && errorMessage !== "";
3273
3478
  const interactive = !disabled && !readOnly;
3274
- const borderColor = disabled ? "var(--color-grey-45)" : interactive && focused ? "var(--sys-color-field-border-focus)" : resolvedSuccess ? "var(--sys-color-field-border-success)" : resolvedError ? "var(--sys-color-field-border-danger)" : "var(--sys-color-field-border-default)";
3275
- const bgColor = disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
3479
+ const borderColor = borderless ? "transparent" : disabled ? "var(--color-grey-45)" : interactive && focused ? "var(--sys-color-field-border-focus)" : resolvedSuccess ? "var(--sys-color-field-border-success)" : resolvedError ? "var(--sys-color-field-border-danger)" : "var(--sys-color-field-border-default)";
3480
+ const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
3276
3481
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
3277
3482
  return /* @__PURE__ */ jsxRuntime.jsx(
3278
3483
  "div",
@@ -3288,267 +3493,79 @@ var SField = react.forwardRef(function SField2({
3288
3493
  "div",
3289
3494
  {
3290
3495
  className: "flex shrink-0 items-center font-bold text-(--sys-color-field-text-default)",
3291
- style: {
3292
- height: s.height,
3293
- marginRight: s.fieldGap,
3294
- width: cssLen(labelWidth),
3295
- fontSize: s.fontSize,
3296
- lineHeight: `${s.lineHeight}px`
3297
- },
3298
- children: [
3299
- icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, style: { marginRight: s.labelGap } }),
3300
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
3301
- (labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsxRuntime.jsx(
3302
- STooltip,
3303
- {
3304
- message: labelTooltip !== "" ? [labelTooltip] : void 0,
3305
- iconSize: s.icon,
3306
- ...labelTooltipProps
3307
- }
3308
- ) })
3309
- ]
3310
- }
3311
- ),
3312
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
3313
- /* @__PURE__ */ jsxRuntime.jsxs(
3314
- "div",
3315
- {
3316
- className: cn(
3317
- "relative box-border flex min-w-0 items-stretch overflow-hidden border border-solid border-(--field-bc)",
3318
- interactive && "hover:border-(--sys-color-field-border-focus) hover:shadow-[0px_0px_4px_0px_#0071ff66] focus-within:border-(--sys-color-field-border-focus) focus-within:shadow-[0px_0px_4px_0px_#0071ff66]",
3319
- disabled && "cursor-not-allowed"
3320
- ),
3321
- style: {
3322
- // @ts-expect-error CSS 변수
3323
- "--field-bc": borderColor,
3324
- // focused/hovered prop이 명시된 경우 강조를 즉시 반영
3325
- ...interactive && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
3326
- ...multiline ? { minHeight: s.height } : { height: s.height },
3327
- "width": cssLen(width),
3328
- "borderRadius": s.radius,
3329
- "background": bgColor,
3330
- "color": textColor
3331
- },
3332
- "data-focused": focused || void 0,
3333
- "data-hovered": hovered || void 0,
3334
- children: [
3335
- effectiveAddonLabel !== "" && /* @__PURE__ */ jsxRuntime.jsxs(
3336
- "div",
3337
- {
3338
- className: "flex shrink-0 items-center gap-(--cmp-field-addonLabel-gap) whitespace-nowrap border-r border-solid px-(--cmp-field-addonLabel-paddingX) text-12 font-medium leading-20",
3339
- style: {
3340
- // 원본 sd-field: labelWidth가 addon 폭(--sd-field-addon-width)에도 적용됨
3341
- width: cssLen(labelWidth),
3342
- justifyContent: ALIGN[addonAlign],
3343
- background: "var(--cmp-field-addonLabel-bg)",
3344
- borderRightColor: disabled ? "var(--cmp-field-addonLabel-border-disabled)" : "var(--cmp-field-addonLabel-border-default)",
3345
- borderTopLeftRadius: s.radius - 1,
3346
- borderBottomLeftRadius: s.radius - 1
3347
- },
3348
- children: [
3349
- icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon }),
3350
- effectiveAddonLabel
3351
- ]
3352
- }
3353
- ),
3354
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-0 flex-1", children })
3355
- ]
3356
- }
3357
- ),
3358
- showError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-(--cmp-field-contents-gap) text-12 font-normal leading-20 text-(--cmp-field-errorMessage-color)", children: errorMessage }) : hint !== "" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-(--cmp-field-contents-gap) text-12 font-normal leading-20 text-(--cmp-field-hint-color)", children: hint }) : null
3359
- ] })
3360
- ] })
3361
- }
3362
- );
3363
- });
3364
-
3365
- // src/components/STag/tag.config.ts
3366
- var TAG_SIZE_CONFIG = {
3367
- xs: {
3368
- height: 20,
3369
- paddingX: 8,
3370
- gap: 4,
3371
- radius: 6,
3372
- iconSize: 12,
3373
- fontSize: 11,
3374
- fontWeight: 500,
3375
- lineHeight: 18
3376
- },
3377
- sm: {
3378
- height: 24,
3379
- paddingX: 8,
3380
- gap: 4,
3381
- radius: 6,
3382
- iconSize: 16,
3383
- fontSize: 12,
3384
- fontWeight: 700,
3385
- lineHeight: 20
3386
- },
3387
- md: {
3388
- height: 28,
3389
- paddingX: 12,
3390
- gap: 6,
3391
- radius: 8,
3392
- iconSize: 16,
3393
- fontSize: 14,
3394
- fontWeight: 700,
3395
- lineHeight: 24
3396
- }
3397
- };
3398
- var STag = react.forwardRef(function STag2({
3399
- shape = "square",
3400
- size = "sm",
3401
- color = "grey",
3402
- label = "",
3403
- icon,
3404
- iconLeft = true,
3405
- width,
3406
- className,
3407
- style,
3408
- ...rest
3409
- }, ref) {
3410
- const s = TAG_SIZE_CONFIG[size];
3411
- const contentColor = `var(--cmp-tag-${color}-content)`;
3412
- const iconNode = icon ? /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.iconSize, color: contentColor }) : null;
3413
- const resolvedWidth = typeof width === "number" ? `${width}px` : width;
3414
- return /* @__PURE__ */ jsxRuntime.jsxs(
3415
- "span",
3416
- {
3417
- ref,
3418
- "aria-label": label || "tag",
3419
- className: cn(
3420
- "box-border inline-flex w-fit select-none items-center justify-center whitespace-nowrap",
3421
- className
3422
- ),
3423
- style: {
3424
- minHeight: s.height,
3425
- padding: `0 ${s.paddingX}px`,
3426
- gap: s.gap,
3427
- borderRadius: shape === "pill" ? 999 : s.radius,
3428
- fontSize: s.fontSize,
3429
- fontWeight: s.fontWeight,
3430
- lineHeight: `${s.lineHeight}px`,
3431
- background: `var(--cmp-tag-${color}-bg)`,
3432
- color: contentColor,
3433
- ...resolvedWidth != null ? { width: resolvedWidth } : {},
3434
- ...style
3435
- },
3436
- ...rest,
3437
- children: [
3438
- icon && iconLeft && iconNode,
3439
- label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex min-w-0 items-center justify-center", children: label }),
3440
- icon && !iconLeft && iconNode
3441
- ]
3442
- }
3443
- );
3444
- });
3445
- var SIZE = {
3446
- xxs: { icon: 12, radius: "sm" },
3447
- xs: { icon: 16, radius: "sm" },
3448
- sm: { icon: 20, radius: "sm" },
3449
- md: { icon: 24, radius: "md" },
3450
- lg: { icon: 32, radius: "md" }
3451
- };
3452
- var TOOLTIP_HIDE_DELAY_MS = 100;
3453
- function SGhostButton({
3454
- icon,
3455
- size = "sm",
3456
- intent = "default",
3457
- ariaLabel = "",
3458
- tooltipText,
3459
- disabled = false,
3460
- onClick,
3461
- className,
3462
- style
3463
- }) {
3464
- const portalContainer = usePortalContainer();
3465
- const [open, setOpen] = react.useState(false);
3466
- const hideTimer = react.useRef(void 0);
3467
- const cancelHide = () => {
3468
- if (hideTimer.current) {
3469
- clearTimeout(hideTimer.current);
3470
- hideTimer.current = void 0;
3471
- }
3472
- };
3473
- const openNow = () => {
3474
- cancelHide();
3475
- setOpen(true);
3476
- };
3477
- const startHide = () => {
3478
- cancelHide();
3479
- hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
3480
- };
3481
- const s = SIZE[size];
3482
- const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
3483
- const btn = /* @__PURE__ */ jsxRuntime.jsxs(
3484
- "button",
3485
- {
3486
- type: "button",
3487
- disabled,
3488
- "aria-disabled": disabled ? "true" : void 0,
3489
- "aria-label": ariaLabel.trim() || void 0,
3490
- tabIndex: disabled ? -1 : void 0,
3491
- onClick: (e) => {
3492
- if (disabled) {
3493
- e.preventDefault();
3494
- return;
3495
- }
3496
- onClick?.(e);
3497
- },
3498
- className: cn(
3499
- "group relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
3500
- disabled ? "cursor-not-allowed" : "cursor-pointer",
3501
- "focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
3502
- className
3503
- ),
3504
- style: {
3505
- width: `var(--cmp-ghostButton-${size}-size)`,
3506
- height: `var(--cmp-ghostButton-${size}-size)`,
3507
- borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
3508
- ...style
3509
- },
3510
- children: [
3511
- !disabled && /* @__PURE__ */ jsxRuntime.jsx(
3512
- "span",
3513
- {
3514
- "aria-hidden": true,
3515
- className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover:opacity-5",
3516
- style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
3496
+ style: {
3497
+ height: cssLen(labelHeight) ?? s.height,
3498
+ marginRight: s.fieldGap,
3499
+ width: cssLen(labelWidth),
3500
+ fontSize: s.fontSize,
3501
+ lineHeight: `${s.lineHeight}px`
3502
+ },
3503
+ children: [
3504
+ icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, style: { marginRight: s.labelGap } }),
3505
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 overflow-hidden text-ellipsis whitespace-nowrap", children: label }),
3506
+ (labelTooltip !== "" || labelTooltipProps) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", style: { marginLeft: s.labelGap }, children: /* @__PURE__ */ jsxRuntime.jsx(
3507
+ STooltip,
3508
+ {
3509
+ message: labelTooltip !== "" ? [labelTooltip] : void 0,
3510
+ iconSize: s.icon,
3511
+ ...labelTooltipProps
3512
+ }
3513
+ ) })
3514
+ ]
3517
3515
  }
3518
3516
  ),
3519
- /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
3520
- ]
3517
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
3518
+ /* @__PURE__ */ jsxRuntime.jsxs(
3519
+ "div",
3520
+ {
3521
+ className: cn(
3522
+ "relative box-border flex min-w-0 items-stretch overflow-hidden border border-solid border-(--field-bc)",
3523
+ interactive && !borderless && "hover:border-(--sys-color-field-border-focus) hover:shadow-[0px_0px_4px_0px_#0071ff66] focus-within:border-(--sys-color-field-border-focus) focus-within:shadow-[0px_0px_4px_0px_#0071ff66]",
3524
+ disabled && "cursor-not-allowed"
3525
+ ),
3526
+ style: {
3527
+ // @ts-expect-error CSS 변수
3528
+ "--field-bc": borderColor,
3529
+ // focused/hovered prop이 명시된 경우 강조를 즉시 반영
3530
+ ...interactive && !borderless && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
3531
+ ...multiline ? { minHeight: s.height } : { height: s.height },
3532
+ "width": cssLen(width),
3533
+ "borderRadius": s.radius,
3534
+ "background": bgColor,
3535
+ "color": textColor
3536
+ },
3537
+ "data-focused": focused || void 0,
3538
+ "data-hovered": hovered || void 0,
3539
+ children: [
3540
+ effectiveAddonLabel !== "" && /* @__PURE__ */ jsxRuntime.jsxs(
3541
+ "div",
3542
+ {
3543
+ className: "flex shrink-0 items-center gap-(--cmp-field-addonLabel-gap) whitespace-nowrap border-r border-solid px-(--cmp-field-addonLabel-paddingX) text-12 font-medium leading-20",
3544
+ style: {
3545
+ // 원본 sd-field: labelWidth가 addon 폭(--sd-field-addon-width)에도 적용됨
3546
+ width: cssLen(labelWidth),
3547
+ justifyContent: ALIGN[addonAlign],
3548
+ background: "var(--cmp-field-addonLabel-bg)",
3549
+ borderRightColor: disabled ? "var(--cmp-field-addonLabel-border-disabled)" : "var(--cmp-field-addonLabel-border-default)",
3550
+ borderTopLeftRadius: s.radius - 1,
3551
+ borderBottomLeftRadius: s.radius - 1
3552
+ },
3553
+ children: [
3554
+ icon && !label && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon }),
3555
+ effectiveAddonLabel
3556
+ ]
3557
+ }
3558
+ ),
3559
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-0 flex-1", children })
3560
+ ]
3561
+ }
3562
+ ),
3563
+ showError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-(--cmp-field-contents-gap) text-12 font-normal leading-20 text-(--cmp-field-errorMessage-color)", children: errorMessage }) : hint !== "" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-(--cmp-field-contents-gap) text-12 font-normal leading-20 text-(--cmp-field-hint-color)", children: hint }) : null
3564
+ ] })
3565
+ ] })
3521
3566
  }
3522
3567
  );
3523
- const text = (tooltipText ?? "").trim();
3524
- if (text === "") return btn;
3525
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover__namespace.Root, { open, onOpenChange: setOpen, children: [
3526
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3527
- "span",
3528
- {
3529
- className: "inline-flex",
3530
- onMouseEnter: openNow,
3531
- onMouseLeave: startHide,
3532
- onFocus: openNow,
3533
- onBlur: startHide,
3534
- children: btn
3535
- }
3536
- ) }),
3537
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
3538
- RPopover__namespace.Content,
3539
- {
3540
- side: "top",
3541
- sideOffset: 4,
3542
- className: cn("z-50", FLOATING_SLIDE_ANIM),
3543
- "aria-hidden": true,
3544
- onOpenAutoFocus: (e) => e.preventDefault(),
3545
- onMouseEnter: cancelHide,
3546
- onMouseLeave: startHide,
3547
- children: /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
3548
- }
3549
- ) })
3550
- ] });
3551
- }
3568
+ });
3552
3569
  var FONT = {
3553
3570
  sm: { fontSize: 12, lineHeight: 20, paddingX: "var(--cmp-textinput-sm-paddingX)" },
3554
3571
  md: { fontSize: 14, lineHeight: 24, paddingX: "var(--cmp-textinput-md-paddingX)" }
@@ -3803,8 +3820,8 @@ var STextarea = react.forwardRef(function STextarea2({
3803
3820
  );
3804
3821
  });
3805
3822
  var PAD2 = {
3806
- sm: { fontSize: 12, stepper: 20, icon: 12 },
3807
- md: { fontSize: 14, stepper: 24, icon: 14 }
3823
+ sm: { fontSize: 12, lineHeight: 20, stepper: 20, icon: 12 },
3824
+ md: { fontSize: 14, lineHeight: 24, stepper: 24, icon: 14 }
3808
3825
  };
3809
3826
  var toNumber = (val) => {
3810
3827
  if (val === null || val === void 0 || val === "") return null;
@@ -3822,6 +3839,38 @@ var formatWithCommas = (value) => {
3822
3839
  const result = isNeg ? "-" + formatted : formatted;
3823
3840
  return decPart !== void 0 && decPart !== "" ? result + "." + decPart : result;
3824
3841
  };
3842
+ var addCommas = (value) => value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
3843
+ var formatInputWithCommas = (value) => {
3844
+ const isNeg = value.startsWith("-");
3845
+ const unsigned = isNeg ? value.slice(1) : value;
3846
+ const hasDecimal = unsigned.includes(".");
3847
+ const [intPart = "", decPart = ""] = unsigned.split(".");
3848
+ const formattedInt = addCommas(intPart === "" ? "0" : intPart);
3849
+ return `${isNeg ? "-" : ""}${formattedInt}${hasDecimal ? `.${decPart}` : ""}`;
3850
+ };
3851
+ var getDecimalLimit = (useDecimal, maxDecimalLength) => {
3852
+ if (!useDecimal || maxDecimalLength === void 0 || !Number.isFinite(maxDecimalLength)) {
3853
+ return void 0;
3854
+ }
3855
+ return Math.max(0, Math.floor(maxDecimalLength));
3856
+ };
3857
+ var applyDecimalLimit = (value, decimalLimit) => {
3858
+ if (value === null || decimalLimit === void 0) return value;
3859
+ return Number(value.toFixed(decimalLimit));
3860
+ };
3861
+ var getLogicalCaret = (value, caret) => {
3862
+ return value.slice(0, caret ?? value.length).replace(/,/g, "").length;
3863
+ };
3864
+ var getDisplayCaret = (value, logicalCaret) => {
3865
+ if (logicalCaret <= 0) return 0;
3866
+ let seen = 0;
3867
+ for (let i = 0; i < value.length; i += 1) {
3868
+ if (value[i] === ",") continue;
3869
+ seen += 1;
3870
+ if (seen >= logicalCaret) return i + 1;
3871
+ }
3872
+ return value.length;
3873
+ };
3825
3874
  var SNumberInput = react.forwardRef(function SNumberInput2({
3826
3875
  value,
3827
3876
  onValueChange,
@@ -3831,6 +3880,7 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
3831
3880
  step = 1,
3832
3881
  useButton = false,
3833
3882
  useDecimal = false,
3883
+ maxDecimalLength,
3834
3884
  inputPrefix = "",
3835
3885
  inputSuffix = "",
3836
3886
  inputClass,
@@ -3862,7 +3912,8 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
3862
3912
  }, ref) {
3863
3913
  const p = PAD2[size];
3864
3914
  const padX = `var(--cmp-numberInput-${size}-paddingX)`;
3865
- const numValue = toNumber(value);
3915
+ const decimalLimit = getDecimalLimit(useDecimal, maxDecimalLength);
3916
+ const numValue = applyDecimalLimit(toNumber(value), decimalLimit);
3866
3917
  const [display, setDisplay] = react.useState(() => formatWithCommas(numValue));
3867
3918
  const [ruleError, setRuleError] = react.useState("");
3868
3919
  const innerRef = react.useRef(null);
@@ -3872,8 +3923,9 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
3872
3923
  else if (ref) ref.current = el;
3873
3924
  };
3874
3925
  react.useEffect(() => {
3875
- setDisplay(formatWithCommas(toNumber(value)));
3876
- }, [value]);
3926
+ if (document.activeElement === innerRef.current) return;
3927
+ setDisplay(formatWithCommas(applyDecimalLimit(toNumber(value), decimalLimit)));
3928
+ }, [decimalLimit, value]);
3877
3929
  useFormField({
3878
3930
  name,
3879
3931
  validate: () => {
@@ -3885,48 +3937,87 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
3885
3937
  });
3886
3938
  const resolvedError = error || ruleError !== "";
3887
3939
  const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
3888
- const emit = (n) => {
3889
- setDisplay(formatWithCommas(n));
3890
- onValueChange?.(n);
3940
+ const restoreCaret = (nextDisplay, logicalCaret) => {
3941
+ const input = innerRef.current;
3942
+ if (logicalCaret === void 0 || !input || document.activeElement !== input) return;
3943
+ const nextCaret = getDisplayCaret(nextDisplay, logicalCaret);
3944
+ requestAnimationFrame(() => {
3945
+ if (document.activeElement !== input) return;
3946
+ input.setSelectionRange(nextCaret, nextCaret);
3947
+ });
3948
+ };
3949
+ const setDisplayWithCaret = (nextDisplay, logicalCaret) => {
3950
+ setDisplay(nextDisplay);
3951
+ restoreCaret(nextDisplay, logicalCaret);
3952
+ };
3953
+ const resetRejectedInput = (target, logicalCaret) => {
3954
+ target.value = display;
3955
+ restoreCaret(display, Math.min(logicalCaret, display.replace(/,/g, "").length));
3956
+ };
3957
+ const emit = (n, logicalCaret) => {
3958
+ const next = applyDecimalLimit(n, decimalLimit);
3959
+ const nextDisplay = formatWithCommas(next);
3960
+ setDisplayWithCaret(nextDisplay, logicalCaret);
3961
+ onValueChange?.(next);
3891
3962
  };
3892
3963
  const incDisabled = disabled || readOnly || numValue !== null && numValue >= max;
3893
3964
  const decDisabled = disabled || readOnly || numValue !== null && numValue <= min;
3894
3965
  const increment = () => {
3895
3966
  if (incDisabled) return;
3896
3967
  const cur = numValue ?? (min === Number.NEGATIVE_INFINITY ? 0 : min);
3897
- const next = Math.min(cur + step, max);
3968
+ const next = applyDecimalLimit(Math.min(cur + step, max), decimalLimit);
3898
3969
  if (next !== cur) emit(next);
3899
3970
  };
3900
3971
  const decrement = () => {
3901
3972
  if (decDisabled) return;
3902
3973
  const cur = numValue ?? (min === Number.NEGATIVE_INFINITY ? 0 : min);
3903
- const next = Math.max(cur - step, min);
3974
+ const next = applyDecimalLimit(Math.max(cur - step, min), decimalLimit);
3904
3975
  if (next !== cur) emit(next);
3905
3976
  };
3906
- const handleInput = (raw) => {
3977
+ const handleInput = (event) => {
3978
+ const target = event.currentTarget;
3979
+ const raw = target.value;
3980
+ const logicalCaret = getLogicalCaret(raw, target.selectionStart);
3907
3981
  if (raw === "") {
3908
- setDisplay("");
3982
+ setDisplayWithCaret("", 0);
3909
3983
  onValueChange?.(null);
3910
3984
  return;
3911
3985
  }
3912
3986
  const cleaned = raw.replace(/,/g, "");
3913
3987
  const decimals = (cleaned.match(/\./g) || []).length;
3914
- if (decimals > 1) return;
3988
+ if (decimals > 1) {
3989
+ resetRejectedInput(target, logicalCaret);
3990
+ return;
3991
+ }
3915
3992
  if (cleaned === "-" || cleaned === "." || cleaned === "-.") {
3916
- setDisplay(cleaned);
3993
+ setDisplayWithCaret(cleaned, logicalCaret);
3917
3994
  return;
3918
3995
  }
3919
- if (!useDecimal && cleaned.includes(".")) return;
3996
+ if (!useDecimal && cleaned.includes(".")) {
3997
+ resetRejectedInput(target, logicalCaret);
3998
+ return;
3999
+ }
4000
+ if (decimalLimit !== void 0 && cleaned.includes(".")) {
4001
+ const fraction = cleaned.split(".")[1] ?? "";
4002
+ if (fraction.length > decimalLimit) {
4003
+ resetRejectedInput(target, logicalCaret);
4004
+ return;
4005
+ }
4006
+ }
3920
4007
  if (cleaned.endsWith(".") && decimals === 1) {
3921
4008
  const parsed2 = toNumber(cleaned.slice(0, -1));
3922
4009
  if (parsed2 !== null) {
3923
- setDisplay(formatWithCommas(parsed2) + ".");
4010
+ setDisplayWithCaret(formatInputWithCommas(cleaned), logicalCaret);
3924
4011
  return;
3925
4012
  }
3926
4013
  }
3927
4014
  const parsed = toNumber(cleaned);
3928
- if (parsed === null) return;
3929
- emit(parsed);
4015
+ if (parsed === null) {
4016
+ resetRejectedInput(target, logicalCaret);
4017
+ return;
4018
+ }
4019
+ setDisplayWithCaret(formatInputWithCommas(cleaned), logicalCaret);
4020
+ onValueChange?.(applyDecimalLimit(parsed, decimalLimit));
3930
4021
  };
3931
4022
  const stepper = (dir) => {
3932
4023
  const off = dir === "inc" ? incDisabled : decDisabled;
@@ -3939,8 +4030,8 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
3939
4030
  "aria-label": dir === "inc" ? "\uC99D\uAC00" : "\uAC10\uC18C",
3940
4031
  onClick: dir === "inc" ? increment : decrement,
3941
4032
  className: cn(
3942
- "flex flex-shrink-0 items-center justify-center",
3943
- off ? "cursor-not-allowed" : "cursor-pointer"
4033
+ "flex flex-shrink-0 items-center justify-center border border-solid border-transparent transition-colors",
4034
+ off ? "cursor-not-allowed" : "cursor-pointer hover:border-[var(--color-border-accentLight)]"
3944
4035
  ),
3945
4036
  style: {
3946
4037
  width: `var(--cmp-numberInput-stepper-${size}-size)`,
@@ -3989,24 +4080,26 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
3989
4080
  style: {
3990
4081
  // 원본 sd-number-input: content gap = padding-x 토큰 (gap 토큰 아님)
3991
4082
  gap: `var(--cmp-numberInput-${size}-paddingX)`,
3992
- padding: `0 ${useButton ? padX : `calc(${padX} * 2)`}`
4083
+ padding: `0 ${useButton ? padX : `calc(${padX} * 2)`}`,
4084
+ fontSize: p.fontSize,
4085
+ lineHeight: `${p.lineHeight}px`
3993
4086
  },
3994
4087
  children: [
3995
4088
  useButton && stepper("dec"),
3996
- inputPrefix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: { fontSize: p.fontSize, color: affixColor }, children: inputPrefix }),
4089
+ inputPrefix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: { color: affixColor }, children: inputPrefix }),
3997
4090
  /* @__PURE__ */ jsxRuntime.jsx(
3998
4091
  "input",
3999
4092
  {
4000
4093
  ref: setRefs,
4001
4094
  name,
4002
4095
  type: "text",
4003
- inputMode: "numeric",
4096
+ inputMode: useDecimal ? "decimal" : "numeric",
4004
4097
  value: display,
4005
4098
  placeholder,
4006
4099
  disabled,
4007
4100
  readOnly,
4008
4101
  autoFocus,
4009
- onChange: (e) => handleInput(e.target.value),
4102
+ onChange: handleInput,
4010
4103
  onKeyDown: (e) => {
4011
4104
  if (e.key === "ArrowUp") {
4012
4105
  e.preventDefault();
@@ -4018,26 +4111,29 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
4018
4111
  },
4019
4112
  onFocus: (e) => onFocus?.(e),
4020
4113
  onBlur: (e) => {
4021
- const n = toNumber(display);
4114
+ const cleaned = display.replace(/,/g, "");
4115
+ const n = toNumber(cleaned);
4022
4116
  const clamped = n === null ? null : Math.min(Math.max(n, min), max);
4023
4117
  if (clamped !== n) emit(clamped);
4024
- else setDisplay(formatWithCommas(n));
4118
+ else setDisplay(n === null ? "" : formatInputWithCommas(cleaned));
4025
4119
  if (rules && rules.length > 0) setRuleError(runRules(clamped, rules));
4026
4120
  onBlur?.(e);
4027
4121
  },
4028
4122
  className: cn(
4029
- "h-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-none placeholder:text-[color:var(--cmp-numberInput-text-placeholder)] disabled:cursor-not-allowed",
4123
+ // h-full 대신 라인박스 높이 그대로 두어 prefix/suffix 동일하게 flex 로 수직 센터링
4124
+ // (h-full 이면 input 내부 텍스트 센터링을 브라우저가 수행해 1px 어긋날 수 있음)
4125
+ "min-w-0 flex-1 border-0 bg-transparent p-0 outline-none placeholder:text-[color:var(--cmp-numberInput-text-placeholder)] disabled:cursor-not-allowed",
4030
4126
  inputClass
4031
4127
  ),
4032
4128
  style: {
4033
- fontSize: p.fontSize,
4129
+ // fontSize/lineHeight 는 컨테이너에서 상속
4034
4130
  textAlign: useButton ? "center" : "right",
4035
4131
  color: disabled ? "var(--cmp-numberInput-text-disabled)" : "var(--cmp-numberInput-text-default)",
4036
4132
  ...inputStyle
4037
4133
  }
4038
4134
  }
4039
4135
  ),
4040
- inputSuffix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: { fontSize: p.fontSize, color: affixColor }, children: inputSuffix }),
4136
+ inputSuffix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: { color: affixColor }, children: inputSuffix }),
4041
4137
  useButton && stepper("inc")
4042
4138
  ]
4043
4139
  }
@@ -4051,15 +4147,28 @@ function flattenOptions(options, depth = 1) {
4051
4147
  const out = [];
4052
4148
  for (const o of options) {
4053
4149
  const hasChildren = Array.isArray(o.children) && o.children.length > 0;
4054
- out.push({
4055
- value: String(o.value),
4056
- label: o.label,
4057
- disabled: !!o.disabled,
4058
- depth,
4059
- isGroup: hasChildren,
4060
- raw: o.value
4061
- });
4062
- if (hasChildren) out.push(...flattenOptions(o.children, depth + 1));
4150
+ if (hasChildren) {
4151
+ const childFlat = flattenOptions(o.children, depth + 1);
4152
+ const allChildrenDisabled = childFlat.every((c) => c.disabled);
4153
+ out.push({
4154
+ value: String(o.value),
4155
+ label: o.label,
4156
+ disabled: !!o.disabled || allChildrenDisabled,
4157
+ depth,
4158
+ isGroup: true,
4159
+ raw: o.value
4160
+ });
4161
+ out.push(...childFlat);
4162
+ } else {
4163
+ out.push({
4164
+ value: String(o.value),
4165
+ label: o.label,
4166
+ disabled: !!o.disabled,
4167
+ depth,
4168
+ isGroup: false,
4169
+ raw: o.value
4170
+ });
4171
+ }
4063
4172
  }
4064
4173
  return out;
4065
4174
  }
@@ -4183,6 +4292,9 @@ var SSelect = react.forwardRef(function SSelect2({
4183
4292
  );
4184
4293
  const flat = react.useMemo(() => flattenOptions(options), [options]);
4185
4294
  const selectable = react.useMemo(() => selectableOptions(flat), [flat]);
4295
+ const hasLeaf = react.useMemo(() => flat.some((o) => !o.isGroup), [flat]);
4296
+ const allOptionsDisabled = hasLeaf && selectable.length === 0;
4297
+ const effectiveDisabled = disabled || allOptionsDisabled;
4186
4298
  const showSearch = useSearch && selectable.length >= SEARCH_THRESHOLD;
4187
4299
  const labelByValue = react.useMemo(() => new Map(flat.map((o) => [o.value, o.label])), [flat]);
4188
4300
  const rawByValue = react.useMemo(() => new Map(flat.map((o) => [o.value, o.raw])), [flat]);
@@ -4233,6 +4345,7 @@ var SSelect = react.forwardRef(function SSelect2({
4233
4345
  const selectStore = /* @__PURE__ */ jsxRuntime.jsxs(
4234
4346
  Ariakit__namespace.SelectProvider,
4235
4347
  {
4348
+ animated: portalContainer == null ? 150 : false,
4236
4349
  value: multi ? selected : selected[0] ?? "",
4237
4350
  setValue: (v) => emit(Array.isArray(v) ? v : v === "" ? [] : [v]),
4238
4351
  open,
@@ -4248,10 +4361,10 @@ var SSelect = react.forwardRef(function SSelect2({
4248
4361
  Ariakit__namespace.Select,
4249
4362
  {
4250
4363
  ref: triggerRef,
4251
- disabled,
4364
+ disabled: effectiveDisabled,
4252
4365
  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",
4253
4366
  style: {
4254
- color: disabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
4367
+ color: effectiveDisabled ? "var(--cmp-select-text-disabled)" : isPlaceholder ? "var(--cmp-select-icon-default)" : "var(--cmp-select-text-default)"
4255
4368
  },
4256
4369
  children: [
4257
4370
  isHtmlLabel(triggerText) ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -4266,7 +4379,7 @@ var SSelect = react.forwardRef(function SSelect2({
4266
4379
  {
4267
4380
  name: "chevronDown",
4268
4381
  size: 12,
4269
- color: disabled ? "var(--cmp-select-icon-disabled)" : "var(--cmp-select-icon-default)",
4382
+ color: effectiveDisabled ? "var(--cmp-select-icon-disabled)" : "var(--cmp-select-icon-default)",
4270
4383
  className: cn("shrink-0 transition-transform duration-200", open && "rotate-180")
4271
4384
  }
4272
4385
  )
@@ -4280,7 +4393,7 @@ var SSelect = react.forwardRef(function SSelect2({
4280
4393
  portalElement: portalContainer,
4281
4394
  gutter: 4,
4282
4395
  sameWidth: !dropdownWidth,
4283
- className: "z-50 overflow-auto rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] py-[4px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]",
4396
+ 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",
4284
4397
  style: {
4285
4398
  maxHeight: dropdownHeight,
4286
4399
  ...dropdownWidth ? { width: dropdownWidth } : {},
@@ -4321,7 +4434,7 @@ var SSelect = react.forwardRef(function SSelect2({
4321
4434
  ),
4322
4435
  hasItems ? /* @__PURE__ */ jsxRuntime.jsx(ItemList, { items: filtered, multi, selected, useSearch: showSearch }) : (
4323
4436
  // 원본 sd-select-listbox__empty (표시할 항목이 없을 때)
4324
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-[12px] py-[12px] text-center text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-disabled)]", children: "\uD45C\uC2DC\uD560 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." })
4437
+ /* @__PURE__ */ jsxRuntime.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." })
4325
4438
  )
4326
4439
  ]
4327
4440
  }
@@ -4344,7 +4457,7 @@ var SSelect = react.forwardRef(function SSelect2({
4344
4457
  error: error || ruleError !== "",
4345
4458
  status: rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0,
4346
4459
  errorMessage: ruleError !== "" ? ruleError : errorMessage,
4347
- disabled,
4460
+ disabled: effectiveDisabled,
4348
4461
  width,
4349
4462
  className,
4350
4463
  style,
@@ -4379,7 +4492,11 @@ function ItemList({
4379
4492
  {
4380
4493
  className: cn(
4381
4494
  "select-none py-[4px] pr-[12px] text-[12px] leading-[20px] text-[color:var(--cmp-listItem-content-default)]",
4382
- 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"
4495
+ 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",
4496
+ // 하위가 전부 비활성이면(select.config 에서 bubble-up) 어미 그룹도 비활성 처리:
4497
+ // 텍스트는 회색(#888), depth1 그룹은 파란 배경 대신 회색(#EEE)으로 (depth2 는 #F6F6F6 유지)
4498
+ o.disabled && "text-[color:var(--cmp-listItem-content-disabled)]",
4499
+ o.disabled && isDepth1 && "bg-[var(--cmp-listItem-depth1-bg-disabled)]"
4383
4500
  ),
4384
4501
  style: { paddingLeft: padLeft },
4385
4502
  children: o.label
@@ -4406,15 +4523,21 @@ function ItemList({
4406
4523
  "span",
4407
4524
  {
4408
4525
  className: cn(
4409
- "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
4410
- 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"
4526
+ "flex h-[16px] w-[16px] flex-shrink-0 items-center justify-center rounded-[2px] border border-solid",
4527
+ o.disabled ? (
4528
+ // 비활성: 회색 채움(#E1E1E1) + 회색 테두리(#CCCCCC)
4529
+ "border-[color:var(--cmp-checkbox-border-disabled)] bg-[var(--cmp-checkbox-bg-disabled)]"
4530
+ ) : cn(
4531
+ "group-data-[active-item]:border-[color:var(--cmp-checkbox-checked-border-inverse)]",
4532
+ 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"
4533
+ )
4411
4534
  ),
4412
4535
  children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(
4413
4536
  SIcon,
4414
4537
  {
4415
4538
  name: "checkboxCheck",
4416
4539
  size: 12,
4417
- color: "var(--cmp-checkbox-checked-icon-default)"
4540
+ color: o.disabled ? "var(--cmp-checkbox-checked-icon-disabled)" : "var(--cmp-checkbox-checked-icon-default)"
4418
4541
  }
4419
4542
  )
4420
4543
  }
@@ -4820,7 +4943,7 @@ function SCalendar({
4820
4943
  "span",
4821
4944
  {
4822
4945
  className: cn(
4823
- "flex h-[var(--cmp-datepicker-calendar-day-circle-size)] w-[var(--cmp-datepicker-calendar-day-circle-size)] items-center justify-center rounded-[var(--cmp-datepicker-calendar-day-circle-radius)] border border-solid border-transparent text-[14px]",
4946
+ "flex h-[var(--cmp-datepicker-calendar-day-circle-size)] w-[var(--cmp-datepicker-calendar-day-circle-size)] items-center justify-center rounded-[var(--cmp-datepicker-calendar-day-circle-radius)] border border-solid border-transparent text-[14px] leading-[var(--cmp-datepicker-calendar-day-circle-size)]",
4824
4947
  selected ? "bg-[var(--cmp-datepicker-calendar-day-select-bg)] text-[color:var(--cmp-datepicker-calendar-day-select-text)]" : disabled ? "text-[color:var(--cmp-datepicker-calendar-day-disabled-text)]" : "text-[color:var(--cmp-datepicker-calendar-day-default-text)] hover:border-[color:var(--cmp-datepicker-calendar-day-hover-border)]",
4825
4948
  isToday && !disabled && "font-bold"
4826
4949
  ),
@@ -4945,8 +5068,8 @@ function SDatePicker({
4945
5068
  disabled,
4946
5069
  className,
4947
5070
  style,
4948
- children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover__namespace.Root, { open, onOpenChange: (o) => !disabled && setOpen(o), children: [
4949
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
5071
+ children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: (o) => !disabled && setOpen(o), children: [
5072
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
4950
5073
  "button",
4951
5074
  {
4952
5075
  type: "button",
@@ -4982,16 +5105,25 @@ function SDatePicker({
4982
5105
  ]
4983
5106
  }
4984
5107
  ) }),
4985
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Content, { side: "bottom", align: "start", sideOffset: 4, className: "z-50", children: /* @__PURE__ */ jsxRuntime.jsx(
4986
- SCalendar,
4987
- {
4988
- value: value ?? void 0,
4989
- selectable,
4990
- elevated: true,
4991
- onValueChange: select,
4992
- onViewChange
5108
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
5109
+ RPopover2__namespace.Content,
5110
+ {
5111
+ side: "bottom",
5112
+ align: "start",
5113
+ sideOffset: 4,
5114
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
5115
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5116
+ SCalendar,
5117
+ {
5118
+ value: value ?? void 0,
5119
+ selectable,
5120
+ elevated: true,
5121
+ onValueChange: select,
5122
+ onViewChange
5123
+ }
5124
+ )
4993
5125
  }
4994
- ) }) })
5126
+ ) })
4995
5127
  ] })
4996
5128
  }
4997
5129
  );
@@ -5001,6 +5133,7 @@ function RangeCalendar({
5001
5133
  selectable,
5002
5134
  maxRange,
5003
5135
  onSelect,
5136
+ onPendingStartChange,
5004
5137
  onViewChange
5005
5138
  }) {
5006
5139
  const today = todayStr();
@@ -5063,9 +5196,11 @@ function RangeCalendar({
5063
5196
  setStart(d);
5064
5197
  setEnd(null);
5065
5198
  setHover(null);
5199
+ onPendingStartChange?.(d);
5066
5200
  return;
5067
5201
  }
5068
5202
  setEnd(d);
5203
+ onPendingStartChange?.(null);
5069
5204
  onSelect([start, d]);
5070
5205
  };
5071
5206
  const GRID_COLS = "grid grid-cols-[repeat(7,var(--cmp-datepicker-calendar-day-width))]";
@@ -5200,8 +5335,11 @@ function SDateRangePicker({
5200
5335
  const portalContainer = usePortalContainer();
5201
5336
  const [open, setOpen] = react.useState(false);
5202
5337
  const [ruleError, setRuleError] = react.useState("");
5338
+ const [pendingStart, setPendingStart] = react.useState(null);
5203
5339
  const hasValue = value != null && value[0] !== "" && value[1] !== "";
5204
- const display = hasValue ? `${value[0]} ~ ${value[1]}` : placeholder;
5340
+ const hasDisplay = pendingStart != null || hasValue;
5341
+ const startText = pendingStart ?? (hasValue ? value[0] : "");
5342
+ const endText = pendingStart ? "" : hasValue ? value[1] : "";
5205
5343
  useFormField({
5206
5344
  name,
5207
5345
  validate: () => {
@@ -5215,6 +5353,7 @@ function SDateRangePicker({
5215
5353
  const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
5216
5354
  const dims = size === "md" ? { padX: 16, gap: 12, icon: 20, minW: 104, fontSize: 14, lineHeight: 24, radius: 6 } : { padX: 12, gap: 8, icon: 16, minW: 80, fontSize: 12, lineHeight: 20, radius: 4 };
5217
5355
  const select = (r) => {
5356
+ setPendingStart(null);
5218
5357
  onValueChange?.(r);
5219
5358
  if (rules && rules.length > 0) setRuleError(runRules(r, rules));
5220
5359
  setOpen(false);
@@ -5239,52 +5378,80 @@ function SDateRangePicker({
5239
5378
  disabled,
5240
5379
  className,
5241
5380
  style,
5242
- children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover__namespace.Root, { open, onOpenChange: (o) => !disabled && setOpen(o), children: [
5243
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
5244
- "button",
5245
- {
5246
- type: "button",
5247
- name,
5248
- disabled,
5249
- className: "flex h-full w-full items-center overflow-hidden bg-transparent outline-none disabled:cursor-not-allowed",
5250
- style: { padding: `0 ${dims.padX}px`, gap: dims.gap, borderRadius: dims.radius },
5251
- children: [
5252
- /* @__PURE__ */ jsxRuntime.jsx(
5253
- SIcon,
5254
- {
5255
- name: "date",
5256
- size: dims.icon,
5257
- color: disabled ? "var(--cmp-datepicker-icon-disabled)" : "var(--cmp-datepicker-icon-default)",
5258
- className: "flex-shrink-0"
5259
- }
5260
- ),
5261
- /* @__PURE__ */ jsxRuntime.jsx(
5262
- "span",
5263
- {
5264
- className: "flex-1 truncate text-center",
5265
- style: {
5266
- minWidth: dims.minW,
5267
- fontSize: dims.fontSize,
5268
- lineHeight: `${dims.lineHeight}px`,
5269
- color: disabled ? "var(--cmp-datepicker-text-disabled)" : hasValue ? "var(--cmp-datepicker-text-default)" : "var(--cmp-field-hint-color)"
5270
- },
5271
- children: display
5272
- }
5273
- )
5274
- ]
5275
- }
5276
- ) }),
5277
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Content, { side: "bottom", align: "start", sideOffset: 4, className: "z-50", children: /* @__PURE__ */ jsxRuntime.jsx(
5278
- RangeCalendar,
5279
- {
5280
- value: value ?? null,
5281
- selectable,
5282
- maxRange,
5283
- onSelect: select,
5284
- onViewChange
5285
- }
5286
- ) }) })
5287
- ] })
5381
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
5382
+ RPopover2__namespace.Root,
5383
+ {
5384
+ open,
5385
+ onOpenChange: (o) => {
5386
+ if (disabled) return;
5387
+ setOpen(o);
5388
+ if (!o) setPendingStart(null);
5389
+ },
5390
+ children: [
5391
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
5392
+ "button",
5393
+ {
5394
+ type: "button",
5395
+ name,
5396
+ disabled,
5397
+ className: "flex h-full w-full items-center overflow-hidden bg-transparent outline-none disabled:cursor-not-allowed",
5398
+ style: { padding: `0 ${dims.padX}px`, gap: dims.gap, borderRadius: dims.radius },
5399
+ children: [
5400
+ /* @__PURE__ */ jsxRuntime.jsx(
5401
+ SIcon,
5402
+ {
5403
+ name: "date",
5404
+ size: dims.icon,
5405
+ color: disabled ? "var(--cmp-datepicker-icon-disabled)" : "var(--cmp-datepicker-icon-default)",
5406
+ className: "flex-shrink-0"
5407
+ }
5408
+ ),
5409
+ /* @__PURE__ */ jsxRuntime.jsx(
5410
+ "span",
5411
+ {
5412
+ className: cn(
5413
+ "flex-1 items-center",
5414
+ hasDisplay ? "flex" : "block truncate text-center"
5415
+ ),
5416
+ style: {
5417
+ minWidth: dims.minW,
5418
+ fontSize: dims.fontSize,
5419
+ lineHeight: `${dims.lineHeight}px`,
5420
+ color: disabled ? "var(--cmp-datepicker-text-disabled)" : hasDisplay ? "var(--cmp-datepicker-text-default)" : "var(--cmp-field-hint-color)"
5421
+ },
5422
+ children: hasDisplay ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5423
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-right", children: startText }),
5424
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-[4px]", children: "~" }),
5425
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: endText })
5426
+ ] }) : placeholder
5427
+ }
5428
+ )
5429
+ ]
5430
+ }
5431
+ ) }),
5432
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
5433
+ RPopover2__namespace.Content,
5434
+ {
5435
+ side: "bottom",
5436
+ align: "start",
5437
+ sideOffset: 4,
5438
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
5439
+ children: /* @__PURE__ */ jsxRuntime.jsx(
5440
+ RangeCalendar,
5441
+ {
5442
+ value: value ?? null,
5443
+ selectable,
5444
+ maxRange,
5445
+ onSelect: select,
5446
+ onPendingStartChange: setPendingStart,
5447
+ onViewChange
5448
+ }
5449
+ )
5450
+ }
5451
+ ) })
5452
+ ]
5453
+ }
5454
+ )
5288
5455
  }
5289
5456
  );
5290
5457
  }
@@ -5293,7 +5460,7 @@ var SChip = react.forwardRef(function SChip2({
5293
5460
  value = "",
5294
5461
  error: errorProp = false,
5295
5462
  disabled = false,
5296
- editable = true,
5463
+ editable = false,
5297
5464
  showRemove = true,
5298
5465
  placeholder = "",
5299
5466
  rules,
@@ -5446,6 +5613,7 @@ function SFilePicker({
5446
5613
  hovered = false,
5447
5614
  inline = false,
5448
5615
  multiple = false,
5616
+ useMultipleListBox = false,
5449
5617
  accept,
5450
5618
  maxFileSize,
5451
5619
  maxTotalSize,
@@ -5473,8 +5641,8 @@ function SFilePicker({
5473
5641
  const inputRef = react.useRef(null);
5474
5642
  const [ruleError, setRuleError] = react.useState("");
5475
5643
  const files = multiple ? Array.isArray(value) ? value : [] : value && !Array.isArray(value) ? value : null;
5476
- const hasSingle = !multiple && !!files;
5477
- const displayText = hasSingle ? files.name : placeholder;
5644
+ const hasFiles = multiple ? files.length > 0 : !!files;
5645
+ const displayText = !hasFiles ? placeholder : multiple ? files.map((f) => f.name).join(", ") : files.name;
5478
5646
  useFormField({
5479
5647
  name,
5480
5648
  validate: () => {
@@ -5538,78 +5706,88 @@ function SFilePicker({
5538
5706
  const padX = size === "md" ? "var(--cmp-filepicker-md-paddingX)" : "var(--cmp-filepicker-sm-paddingX)";
5539
5707
  const iconSize = size === "md" ? 20 : 16;
5540
5708
  const gapCls = size === "md" ? "gap-[var(--cmp-filepicker-md-gap)]" : "gap-[var(--cmp-filepicker-sm-gap)]";
5541
- const content = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex h-full w-full items-center", gapCls), style: { padding: `0 ${padX}` }, children: [
5542
- /* @__PURE__ */ jsxRuntime.jsx(
5543
- "input",
5544
- {
5545
- ref: inputRef,
5546
- type: "file",
5547
- className: "hidden",
5548
- disabled,
5549
- multiple,
5550
- accept,
5551
- name: resolvedName,
5552
- onChange,
5553
- "aria-label": placeholder
5554
- }
5555
- ),
5556
- /* @__PURE__ */ jsxRuntime.jsxs(
5557
- "button",
5558
- {
5559
- type: "button",
5560
- disabled,
5561
- onClick: open,
5562
- "aria-label": placeholder,
5563
- className: cn(
5564
- "flex min-w-0 flex-1 items-center bg-transparent text-left",
5565
- gapCls,
5566
- disabled ? "cursor-not-allowed" : "cursor-pointer"
5709
+ const inlinePlaceholder = inline && !disabled && !hasFiles;
5710
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(
5711
+ "div",
5712
+ {
5713
+ className: cn("group/filepicker flex h-full w-full items-center", gapCls),
5714
+ style: { padding: inline ? 0 : `0 ${padX}` },
5715
+ children: [
5716
+ /* @__PURE__ */ jsxRuntime.jsx(
5717
+ "input",
5718
+ {
5719
+ ref: inputRef,
5720
+ type: "file",
5721
+ className: "hidden",
5722
+ disabled,
5723
+ multiple,
5724
+ accept,
5725
+ name: resolvedName,
5726
+ onChange,
5727
+ "aria-label": placeholder
5728
+ }
5567
5729
  ),
5568
- children: [
5569
- /* @__PURE__ */ jsxRuntime.jsx(
5570
- SIcon,
5571
- {
5572
- name: "attachFile",
5573
- size: iconSize,
5574
- color: disabled ? "var(--cmp-filepicker-icon-disabled)" : "var(--color-grey-65)"
5575
- }
5576
- ),
5577
- /* @__PURE__ */ jsxRuntime.jsx(
5578
- "span",
5579
- {
5580
- className: "min-w-0 flex-1 truncate text-[12px]",
5581
- style: {
5582
- color: disabled ? "var(--cmp-filepicker-text-disabled)" : hasSingle ? "var(--cmp-filepicker-text-default)" : "var(--cmp-filepicker-text-placeholder)"
5583
- },
5584
- children: displayText
5585
- }
5586
- )
5587
- ]
5588
- }
5589
- ),
5590
- !disabled && hasSingle && /* @__PURE__ */ jsxRuntime.jsx(SGhostButton, { icon: "close", size: "xxs", ariaLabel: "clear", onClick: clear })
5591
- ] });
5592
- const chips = multiple && files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-[8px] flex flex-wrap gap-[8px]", children: files.map((f, i) => /* @__PURE__ */ jsxRuntime.jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i)) });
5593
- if (inline) {
5594
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("inline-flex flex-col", className), style, children: [
5595
- /* @__PURE__ */ jsxRuntime.jsx(
5596
- "div",
5597
- {
5598
- className: "box-border flex items-center rounded-[var(--cmp-filepicker-sm-radius)] border border-solid border-[color:var(--cmp-filepicker-border-default)] bg-[var(--cmp-filepicker-bg-default)]",
5599
- style: {
5600
- height: "var(--cmp-filepicker-sm-height)",
5601
- width: typeof width === "number" ? `${width}px` : width
5602
- },
5603
- children: content
5604
- }
5730
+ /* @__PURE__ */ jsxRuntime.jsxs(
5731
+ "button",
5732
+ {
5733
+ type: "button",
5734
+ disabled,
5735
+ onClick: open,
5736
+ "aria-label": placeholder,
5737
+ className: cn(
5738
+ "flex min-w-0 flex-1 items-center bg-transparent text-left",
5739
+ gapCls,
5740
+ disabled ? "cursor-not-allowed" : "cursor-pointer"
5741
+ ),
5742
+ children: [
5743
+ /* @__PURE__ */ jsxRuntime.jsx(
5744
+ SIcon,
5745
+ {
5746
+ name: "attachFile",
5747
+ size: iconSize,
5748
+ color: disabled ? "var(--cmp-filepicker-icon-disabled)" : "var(--color-grey-65)"
5749
+ }
5750
+ ),
5751
+ /* @__PURE__ */ jsxRuntime.jsx(
5752
+ "span",
5753
+ {
5754
+ className: cn(
5755
+ "min-w-0 flex-1 truncate text-[12px]",
5756
+ inlinePlaceholder && "text-[color:var(--cmp-filepicker-text-placeholder)] transition-colors group-hover/filepicker:text-[color:var(--cmp-filepicker-text-default)]"
5757
+ ),
5758
+ style: inlinePlaceholder ? void 0 : {
5759
+ color: disabled ? "var(--cmp-filepicker-text-disabled)" : hasFiles ? "var(--cmp-filepicker-text-default)" : "var(--cmp-filepicker-text-placeholder)"
5760
+ },
5761
+ children: displayText
5762
+ }
5763
+ )
5764
+ ]
5765
+ }
5766
+ ),
5767
+ !disabled && hasFiles && /* @__PURE__ */ jsxRuntime.jsx(SGhostButton, { icon: "close", size: "xxs", ariaLabel: "clear", onClick: clear })
5768
+ ]
5769
+ }
5770
+ );
5771
+ const chips = multiple && files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
5772
+ "div",
5773
+ {
5774
+ className: cn(
5775
+ "mt-[8px] flex gap-[8px]",
5776
+ useMultipleListBox ? "box-border flex-col items-start rounded-[var(--cmp-listBox-radius)] bg-[var(--cmp-listBox-bg)] p-[8px] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.1)]" : "flex-wrap"
5605
5777
  ),
5606
- chips
5607
- ] });
5608
- }
5778
+ style: useMultipleListBox ? {
5779
+ width: typeof width === "number" ? `${width}px` : width,
5780
+ // label 이 있으면 SField 입력 박스 시작 위치(labelWidth + fieldGap)에 맞춰 정렬
5781
+ marginLeft: label && labelWidth != null ? `calc(${typeof labelWidth === "number" ? `${labelWidth}px` : labelWidth} + var(--sys-space-field-${size}-gap))` : void 0
5782
+ } : void 0,
5783
+ children: files.map((f, i) => /* @__PURE__ */ jsxRuntime.jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i))
5784
+ }
5785
+ );
5609
5786
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style, children: [
5610
5787
  /* @__PURE__ */ jsxRuntime.jsx(
5611
5788
  SField,
5612
5789
  {
5790
+ borderless: inline,
5613
5791
  label,
5614
5792
  labelWidth,
5615
5793
  icon,
@@ -5637,6 +5815,7 @@ function SKeyValueTable({
5637
5815
  fields = [],
5638
5816
  search = false,
5639
5817
  radius = "default",
5818
+ dense = false,
5640
5819
  onChange,
5641
5820
  onSearch,
5642
5821
  className,
@@ -5644,6 +5823,8 @@ function SKeyValueTable({
5644
5823
  }) {
5645
5824
  if (fields.length === 0) return null;
5646
5825
  const emit = (name, value) => onChange?.({ name, value });
5826
+ const cellHeight = dense ? "var(--cmp-table-body-dense-height)" : "var(--cmp-table-keyValue-height)";
5827
+ const cellPadY = dense ? "var(--cmp-table-body-dense-paddingY)" : "var(--cmp-table-keyValue-body-paddingY)";
5647
5828
  const renderField = (f) => {
5648
5829
  if (f.render) return f.render;
5649
5830
  const opt = f.options ?? {};
@@ -5724,10 +5905,15 @@ function SKeyValueTable({
5724
5905
  rowSpan: f.thRowSpan && f.thRowSpan > 1 ? f.thRowSpan : void 0,
5725
5906
  colSpan: f.thColSpan && f.thColSpan > 1 ? f.thColSpan : void 0,
5726
5907
  className: cn(
5727
- "h-[var(--cmp-table-keyValue-height)] box-border bg-[var(--cmp-table-header-bg)] px-[var(--cmp-table-header-paddingX)] py-[var(--cmp-table-keyValue-body-padding)] text-left align-middle",
5908
+ "box-border bg-[var(--cmp-table-header-bg)] px-[var(--cmp-table-header-paddingX)] text-left align-middle",
5728
5909
  cellBorder,
5729
5910
  f.thClass
5730
5911
  ),
5912
+ style: {
5913
+ height: cellHeight,
5914
+ paddingTop: cellPadY,
5915
+ paddingBottom: cellPadY
5916
+ },
5731
5917
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full w-full items-center justify-between gap-[var(--cmp-table-header-gap)]", children: [
5732
5918
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-[8px]", children: [
5733
5919
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] font-bold leading-[20px] text-[color:var(--sys-color-fg-primary)]", children: f.label }),
@@ -5753,10 +5939,11 @@ function SKeyValueTable({
5753
5939
  rowSpan: f.tdRowSpan && f.tdRowSpan > 1 ? f.tdRowSpan : void 0,
5754
5940
  colSpan: f.tdColSpan && f.tdColSpan > 1 ? f.tdColSpan : void 0,
5755
5941
  className: cn(
5756
- "h-[var(--cmp-table-keyValue-height)] box-border bg-[var(--sys-color-bg-frame)] p-[var(--cmp-table-keyValue-body-padding)] align-middle",
5942
+ "box-border bg-[var(--sys-color-bg-frame)] px-[var(--cmp-table-keyValue-body-paddingX-default)] align-middle",
5757
5943
  cellBorder,
5758
5944
  f.tdClass
5759
5945
  ),
5946
+ style: { height: cellHeight, paddingTop: cellPadY, paddingBottom: cellPadY },
5760
5947
  children: renderField(f)
5761
5948
  },
5762
5949
  `td-${r}-${c}`