sellmate-design-system-react 0.1.8 → 0.1.10

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 (110) hide show
  1. package/dist/components/SActionModal/SActionModal.cjs +1 -1
  2. package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
  3. package/dist/components/SActionModal/SActionModal.js +1 -1
  4. package/dist/components/SActionModal/SActionModal.js.map +1 -1
  5. package/dist/components/SBarcodeInput/SBarcodeInput.cjs +217 -208
  6. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  7. package/dist/components/SBarcodeInput/SBarcodeInput.js +217 -208
  8. package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
  9. package/dist/components/SCalendar/SCalendar.cjs +1 -1
  10. package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
  11. package/dist/components/SCalendar/SCalendar.js +1 -1
  12. package/dist/components/SCalendar/SCalendar.js.map +1 -1
  13. package/dist/components/SChip/SChip.cjs +1 -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 +1 -1
  17. package/dist/components/SChip/SChip.js.map +1 -1
  18. package/dist/components/SChipInput/SChipInput.cjs +234 -220
  19. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  20. package/dist/components/SChipInput/SChipInput.js +234 -220
  21. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  22. package/dist/components/SCircleProgress/SCircleProgress.cjs +31 -14
  23. package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
  24. package/dist/components/SCircleProgress/SCircleProgress.d.ts +2 -0
  25. package/dist/components/SCircleProgress/SCircleProgress.js +31 -14
  26. package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
  27. package/dist/components/SDatePicker/SDatePicker.cjs +222 -213
  28. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  29. package/dist/components/SDatePicker/SDatePicker.js +222 -213
  30. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  31. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +297 -262
  32. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  33. package/dist/components/SDateRangePicker/SDateRangePicker.js +297 -262
  34. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  35. package/dist/components/SDropdownButton/SDropdownButton.cjs +4 -4
  36. package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
  37. package/dist/components/SDropdownButton/SDropdownButton.js +4 -4
  38. package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
  39. package/dist/components/SField/SField.cjs +217 -20
  40. package/dist/components/SField/SField.cjs.map +1 -1
  41. package/dist/components/SField/SField.d.ts +11 -0
  42. package/dist/components/SField/SField.js +217 -20
  43. package/dist/components/SField/SField.js.map +1 -1
  44. package/dist/components/SFilePicker/SFilePicker.cjs +315 -276
  45. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  46. package/dist/components/SFilePicker/SFilePicker.d.ts +4 -2
  47. package/dist/components/SFilePicker/SFilePicker.js +314 -276
  48. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  49. package/dist/components/SInput/SInput.cjs +217 -208
  50. package/dist/components/SInput/SInput.cjs.map +1 -1
  51. package/dist/components/SInput/SInput.js +217 -208
  52. package/dist/components/SInput/SInput.js.map +1 -1
  53. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +631 -450
  54. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  55. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +3 -1
  56. package/dist/components/SKeyValueTable/SKeyValueTable.js +631 -450
  57. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  58. package/dist/components/SLoadingContainer/SLoadingContainer.cjs +39 -14
  59. package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
  60. package/dist/components/SLoadingContainer/SLoadingContainer.js +39 -14
  61. package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
  62. package/dist/components/SLoadingContainer/loading.cjs +39 -14
  63. package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
  64. package/dist/components/SLoadingContainer/loading.js +39 -14
  65. package/dist/components/SLoadingContainer/loading.js.map +1 -1
  66. package/dist/components/SLoadingModal/SLoadingModal.cjs +37 -16
  67. package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
  68. package/dist/components/SLoadingModal/SLoadingModal.d.ts +3 -1
  69. package/dist/components/SLoadingModal/SLoadingModal.js +37 -16
  70. package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
  71. package/dist/components/SNumberInput/SNumberInput.cjs +325 -49
  72. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  73. package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
  74. package/dist/components/SNumberInput/SNumberInput.js +325 -49
  75. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  76. package/dist/components/SPagination/SPagination.cjs +1 -1
  77. package/dist/components/SPagination/SPagination.cjs.map +1 -1
  78. package/dist/components/SPagination/SPagination.js +1 -1
  79. package/dist/components/SPagination/SPagination.js.map +1 -1
  80. package/dist/components/SRadio/SRadioGroup.cjs +1 -1
  81. package/dist/components/SRadio/SRadioGroup.cjs.map +1 -1
  82. package/dist/components/SRadio/SRadioGroup.js +1 -1
  83. package/dist/components/SRadio/SRadioGroup.js.map +1 -1
  84. package/dist/components/SSelect/SSelect.cjs +71 -32
  85. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  86. package/dist/components/SSelect/SSelect.js +71 -32
  87. package/dist/components/SSelect/SSelect.js.map +1 -1
  88. package/dist/components/STable/STable.cjs +108 -49
  89. package/dist/components/STable/STable.cjs.map +1 -1
  90. package/dist/components/STable/STable.js +108 -49
  91. package/dist/components/STable/STable.js.map +1 -1
  92. package/dist/components/STextarea/STextarea.cjs +217 -20
  93. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  94. package/dist/components/STextarea/STextarea.js +217 -20
  95. package/dist/components/STextarea/STextarea.js.map +1 -1
  96. package/dist/components/SToast/SToastContainer.cjs +3 -3
  97. package/dist/components/SToast/SToastContainer.cjs.map +1 -1
  98. package/dist/components/SToast/SToastContainer.js +3 -3
  99. package/dist/components/SToast/SToastContainer.js.map +1 -1
  100. package/dist/components/STooltip/STooltip.cjs +208 -13
  101. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  102. package/dist/components/STooltip/STooltip.js +208 -13
  103. package/dist/components/STooltip/STooltip.js.map +1 -1
  104. package/dist/index.cjs +439 -221
  105. package/dist/index.cjs.map +1 -1
  106. package/dist/index.js +439 -221
  107. package/dist/index.js.map +1 -1
  108. package/dist/styles.css +113 -48
  109. package/dist/theme.css +49 -23
  110. package/package.json +1 -1
@@ -2,10 +2,11 @@
2
2
  'use strict';
3
3
 
4
4
  var react = require('react');
5
+ var Ariakit = require('@ariakit/react');
5
6
  var clsx = require('clsx');
6
7
  var tailwindMerge = require('tailwind-merge');
7
8
  var jsxRuntime = require('react/jsx-runtime');
8
- var RPopover = require('@radix-ui/react-popover');
9
+ var RPopover2 = require('@radix-ui/react-popover');
9
10
 
10
11
  function _interopNamespace(e) {
11
12
  if (e && e.__esModule) return e;
@@ -25,7 +26,8 @@ function _interopNamespace(e) {
25
26
  return Object.freeze(n);
26
27
  }
27
28
 
28
- var RPopover__namespace = /*#__PURE__*/_interopNamespace(RPopover);
29
+ var Ariakit__namespace = /*#__PURE__*/_interopNamespace(Ariakit);
30
+ var RPopover2__namespace = /*#__PURE__*/_interopNamespace(RPopover2);
29
31
 
30
32
  var twMerge = tailwindMerge.extendTailwindMerge({
31
33
  extend: {
@@ -2897,11 +2899,200 @@ PortalContainerContext.Provider;
2897
2899
  function usePortalContainer() {
2898
2900
  return react.useContext(PortalContainerContext) ?? void 0;
2899
2901
  }
2902
+
2903
+ // src/components/STag/tag.config.ts
2904
+ var TAG_SIZE_CONFIG = {
2905
+ xs: {
2906
+ height: 20,
2907
+ paddingX: 8,
2908
+ gap: 4,
2909
+ radius: 6,
2910
+ iconSize: 12,
2911
+ fontSize: 11,
2912
+ fontWeight: 500,
2913
+ lineHeight: 18
2914
+ },
2915
+ sm: {
2916
+ height: 24,
2917
+ paddingX: 8,
2918
+ gap: 4,
2919
+ radius: 6,
2920
+ iconSize: 16,
2921
+ fontSize: 12,
2922
+ fontWeight: 700,
2923
+ lineHeight: 20
2924
+ },
2925
+ md: {
2926
+ height: 28,
2927
+ paddingX: 12,
2928
+ gap: 6,
2929
+ radius: 8,
2930
+ iconSize: 16,
2931
+ fontSize: 14,
2932
+ fontWeight: 700,
2933
+ lineHeight: 24
2934
+ }
2935
+ };
2936
+ var STag = react.forwardRef(function STag2({
2937
+ shape = "square",
2938
+ size = "sm",
2939
+ color = "grey",
2940
+ label = "",
2941
+ icon,
2942
+ iconLeft = true,
2943
+ width,
2944
+ className,
2945
+ style,
2946
+ ...rest
2947
+ }, ref) {
2948
+ const s = TAG_SIZE_CONFIG[size];
2949
+ const contentColor = `var(--cmp-tag-${color}-content)`;
2950
+ const iconNode = icon ? /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.iconSize, color: contentColor }) : null;
2951
+ const resolvedWidth = typeof width === "number" ? `${width}px` : width;
2952
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2953
+ "span",
2954
+ {
2955
+ ref,
2956
+ "aria-label": label || "tag",
2957
+ className: cn(
2958
+ "box-border inline-flex w-fit select-none items-center justify-center whitespace-nowrap",
2959
+ className
2960
+ ),
2961
+ style: {
2962
+ minHeight: s.height,
2963
+ padding: `0 ${s.paddingX}px`,
2964
+ gap: s.gap,
2965
+ borderRadius: shape === "pill" ? 999 : s.radius,
2966
+ fontSize: s.fontSize,
2967
+ fontWeight: s.fontWeight,
2968
+ lineHeight: `${s.lineHeight}px`,
2969
+ background: `var(--cmp-tag-${color}-bg)`,
2970
+ color: contentColor,
2971
+ ...resolvedWidth != null ? { width: resolvedWidth } : {},
2972
+ ...style
2973
+ },
2974
+ ...rest,
2975
+ children: [
2976
+ icon && iconLeft && iconNode,
2977
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex min-w-0 items-center justify-center", children: label }),
2978
+ icon && !iconLeft && iconNode
2979
+ ]
2980
+ }
2981
+ );
2982
+ });
2983
+ var SIZE = {
2984
+ xxs: { icon: 12, radius: "sm" },
2985
+ xs: { icon: 16, radius: "sm" },
2986
+ sm: { icon: 20, radius: "sm" },
2987
+ md: { icon: 24, radius: "md" },
2988
+ lg: { icon: 32, radius: "md" }
2989
+ };
2990
+ var TOOLTIP_HIDE_DELAY_MS = 100;
2991
+ function SGhostButton({
2992
+ icon,
2993
+ size = "sm",
2994
+ intent = "default",
2995
+ ariaLabel = "",
2996
+ tooltipText,
2997
+ disabled = false,
2998
+ onClick,
2999
+ className,
3000
+ style
3001
+ }) {
3002
+ const portalContainer = usePortalContainer();
3003
+ const [open, setOpen] = react.useState(false);
3004
+ const hideTimer = react.useRef(void 0);
3005
+ const cancelHide = () => {
3006
+ if (hideTimer.current) {
3007
+ clearTimeout(hideTimer.current);
3008
+ hideTimer.current = void 0;
3009
+ }
3010
+ };
3011
+ const openNow = () => {
3012
+ cancelHide();
3013
+ setOpen(true);
3014
+ };
3015
+ const startHide = () => {
3016
+ cancelHide();
3017
+ hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
3018
+ };
3019
+ const s = SIZE[size];
3020
+ const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
3021
+ const btn = /* @__PURE__ */ jsxRuntime.jsxs(
3022
+ "button",
3023
+ {
3024
+ type: "button",
3025
+ disabled,
3026
+ "aria-disabled": disabled ? "true" : void 0,
3027
+ "aria-label": ariaLabel.trim() || void 0,
3028
+ tabIndex: disabled ? -1 : void 0,
3029
+ onClick: (e) => {
3030
+ if (disabled) {
3031
+ e.preventDefault();
3032
+ return;
3033
+ }
3034
+ onClick?.(e);
3035
+ },
3036
+ className: cn(
3037
+ "group relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
3038
+ disabled ? "cursor-not-allowed" : "cursor-pointer",
3039
+ "focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
3040
+ className
3041
+ ),
3042
+ style: {
3043
+ width: `var(--cmp-ghostButton-${size}-size)`,
3044
+ height: `var(--cmp-ghostButton-${size}-size)`,
3045
+ borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
3046
+ ...style
3047
+ },
3048
+ children: [
3049
+ !disabled && /* @__PURE__ */ jsxRuntime.jsx(
3050
+ "span",
3051
+ {
3052
+ "aria-hidden": true,
3053
+ className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover:opacity-5",
3054
+ style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
3055
+ }
3056
+ ),
3057
+ /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
3058
+ ]
3059
+ }
3060
+ );
3061
+ const text = (tooltipText ?? "").trim();
3062
+ if (text === "") return btn;
3063
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
3064
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3065
+ "span",
3066
+ {
3067
+ className: "inline-flex",
3068
+ onMouseEnter: openNow,
3069
+ onMouseLeave: startHide,
3070
+ onFocus: openNow,
3071
+ onBlur: startHide,
3072
+ children: btn
3073
+ }
3074
+ ) }),
3075
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
3076
+ RPopover2__namespace.Content,
3077
+ {
3078
+ side: "top",
3079
+ sideOffset: 4,
3080
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
3081
+ "aria-hidden": true,
3082
+ onOpenAutoFocus: (e) => e.preventDefault(),
3083
+ onMouseEnter: cancelHide,
3084
+ onMouseLeave: startHide,
3085
+ children: /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
3086
+ }
3087
+ ) })
3088
+ ] });
3089
+ }
2900
3090
  var HIDE_DELAY_MS = 100;
2901
3091
  function useBody(content, message = []) {
2902
3092
  if (content != null) return content;
2903
3093
  if (message.length === 0) return null;
2904
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { children: line }, i)) });
3094
+ const singleLine = message.length === 1;
3095
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: singleLine ? "leading-[24px]" : void 0, children: line }, i)) });
2905
3096
  }
2906
3097
  var STooltip = react.forwardRef(function STooltip2({
2907
3098
  content,
@@ -2969,10 +3160,10 @@ var STooltip = react.forwardRef(function STooltip2({
2969
3160
  fontWeight: tooltipType === "default" ? 500 : 700,
2970
3161
  ...style
2971
3162
  };
2972
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover__namespace.Root, { open, onOpenChange: setOpen, children: [
3163
+ return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
2973
3164
  isHover ? (
2974
3165
  // hover: Anchor(위치 기준) + 직접 mouse 핸들러 — 클릭으로 토글되지 않도록 Trigger 미사용
2975
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3166
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
2976
3167
  "span",
2977
3168
  {
2978
3169
  className: "inline-flex",
@@ -2983,9 +3174,9 @@ var STooltip = react.forwardRef(function STooltip2({
2983
3174
  children: triggerNode
2984
3175
  }
2985
3176
  ) })
2986
- ) : /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Trigger, { asChild: true, children: triggerNode }),
2987
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
2988
- RPopover__namespace.Content,
3177
+ ) : /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: triggerNode }),
3178
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
3179
+ RPopover2__namespace.Content,
2989
3180
  {
2990
3181
  side: placement,
2991
3182
  sideOffset: 8,
@@ -3004,15 +3195,21 @@ var STooltip = react.forwardRef(function STooltip2({
3004
3195
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-[12px]", children: [
3005
3196
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: body }),
3006
3197
  useClose && /* @__PURE__ */ jsxRuntime.jsx(
3007
- RPopover__namespace.Close,
3198
+ SGhostButton,
3008
3199
  {
3009
- "aria-label": "\uB2EB\uAE30",
3010
- className: "shrink-0 cursor-pointer opacity-70 hover:opacity-100",
3011
- children: /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: "close", size: 16, color: fg })
3200
+ icon: "close",
3201
+ size: "xs",
3202
+ intent: tooltipType === "default" ? "inverse" : "default",
3203
+ ariaLabel: "\uB2EB\uAE30",
3204
+ className: "shrink-0",
3205
+ onClick: () => {
3206
+ cancelHide();
3207
+ setOpen(false);
3208
+ }
3012
3209
  }
3013
3210
  )
3014
3211
  ] }),
3015
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
3212
+ /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
3016
3213
  ]
3017
3214
  }
3018
3215
  ) })
@@ -3046,8 +3243,8 @@ function useFormField(field) {
3046
3243
  }, [ctx, name]);
3047
3244
  }
3048
3245
  var FIELD_SIZE = {
3049
- sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 12 },
3050
- md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 12 }
3246
+ sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
3247
+ md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
3051
3248
  };
3052
3249
  var ALIGN = {
3053
3250
  start: "flex-start",
@@ -3062,6 +3259,7 @@ var SField = react.forwardRef(function SField2({
3062
3259
  labelTooltip = "",
3063
3260
  labelTooltipProps,
3064
3261
  labelWidth,
3262
+ labelHeight,
3065
3263
  size = "sm",
3066
3264
  addonLabel = "",
3067
3265
  addonAlign = "start",
@@ -3076,6 +3274,7 @@ var SField = react.forwardRef(function SField2({
3076
3274
  readOnly = false,
3077
3275
  width,
3078
3276
  multiline = false,
3277
+ borderless = false,
3079
3278
  children,
3080
3279
  className,
3081
3280
  style,
@@ -3109,8 +3308,8 @@ var SField = react.forwardRef(function SField2({
3109
3308
  const resolvedSuccess = status === "pass";
3110
3309
  const showError = resolvedError && errorMessage !== "";
3111
3310
  const interactive = !disabled && !readOnly;
3112
- 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)";
3113
- const bgColor = disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
3311
+ 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)";
3312
+ const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
3114
3313
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
3115
3314
  return /* @__PURE__ */ jsxRuntime.jsx(
3116
3315
  "div",
@@ -3127,7 +3326,7 @@ var SField = react.forwardRef(function SField2({
3127
3326
  {
3128
3327
  className: "flex shrink-0 items-center font-bold text-(--sys-color-field-text-default)",
3129
3328
  style: {
3130
- height: s.height,
3329
+ height: cssLen(labelHeight) ?? s.height,
3131
3330
  marginRight: s.fieldGap,
3132
3331
  width: cssLen(labelWidth),
3133
3332
  fontSize: s.fontSize,
@@ -3153,14 +3352,14 @@ var SField = react.forwardRef(function SField2({
3153
3352
  {
3154
3353
  className: cn(
3155
3354
  "relative box-border flex min-w-0 items-stretch overflow-hidden border border-solid border-(--field-bc)",
3156
- 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]",
3355
+ 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]",
3157
3356
  disabled && "cursor-not-allowed"
3158
3357
  ),
3159
3358
  style: {
3160
3359
  // @ts-expect-error CSS 변수
3161
3360
  "--field-bc": borderColor,
3162
3361
  // focused/hovered prop이 명시된 경우 강조를 즉시 반영
3163
- ...interactive && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
3362
+ ...interactive && !borderless && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
3164
3363
  ...multiline ? { minHeight: s.height } : { height: s.height },
3165
3364
  "width": cssLen(width),
3166
3365
  "borderRadius": s.radius,
@@ -3199,200 +3398,12 @@ var SField = react.forwardRef(function SField2({
3199
3398
  }
3200
3399
  );
3201
3400
  });
3202
-
3203
- // src/components/STag/tag.config.ts
3204
- var TAG_SIZE_CONFIG = {
3205
- xs: {
3206
- height: 20,
3207
- paddingX: 8,
3208
- gap: 4,
3209
- radius: 6,
3210
- iconSize: 12,
3211
- fontSize: 11,
3212
- fontWeight: 500,
3213
- lineHeight: 18
3214
- },
3215
- sm: {
3216
- height: 24,
3217
- paddingX: 8,
3218
- gap: 4,
3219
- radius: 6,
3220
- iconSize: 16,
3221
- fontSize: 12,
3222
- fontWeight: 700,
3223
- lineHeight: 20
3224
- },
3225
- md: {
3226
- height: 28,
3227
- paddingX: 12,
3228
- gap: 6,
3229
- radius: 8,
3230
- iconSize: 16,
3231
- fontSize: 14,
3232
- fontWeight: 700,
3233
- lineHeight: 24
3234
- }
3235
- };
3236
- var STag = react.forwardRef(function STag2({
3237
- shape = "square",
3238
- size = "sm",
3239
- color = "grey",
3240
- label = "",
3241
- icon,
3242
- iconLeft = true,
3243
- width,
3244
- className,
3245
- style,
3246
- ...rest
3247
- }, ref) {
3248
- const s = TAG_SIZE_CONFIG[size];
3249
- const contentColor = `var(--cmp-tag-${color}-content)`;
3250
- const iconNode = icon ? /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.iconSize, color: contentColor }) : null;
3251
- const resolvedWidth = typeof width === "number" ? `${width}px` : width;
3252
- return /* @__PURE__ */ jsxRuntime.jsxs(
3253
- "span",
3254
- {
3255
- ref,
3256
- "aria-label": label || "tag",
3257
- className: cn(
3258
- "box-border inline-flex w-fit select-none items-center justify-center whitespace-nowrap",
3259
- className
3260
- ),
3261
- style: {
3262
- minHeight: s.height,
3263
- padding: `0 ${s.paddingX}px`,
3264
- gap: s.gap,
3265
- borderRadius: shape === "pill" ? 999 : s.radius,
3266
- fontSize: s.fontSize,
3267
- fontWeight: s.fontWeight,
3268
- lineHeight: `${s.lineHeight}px`,
3269
- background: `var(--cmp-tag-${color}-bg)`,
3270
- color: contentColor,
3271
- ...resolvedWidth != null ? { width: resolvedWidth } : {},
3272
- ...style
3273
- },
3274
- ...rest,
3275
- children: [
3276
- icon && iconLeft && iconNode,
3277
- label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex min-w-0 items-center justify-center", children: label }),
3278
- icon && !iconLeft && iconNode
3279
- ]
3280
- }
3281
- );
3282
- });
3283
- var SIZE = {
3284
- xxs: { icon: 12, radius: "sm" },
3285
- xs: { icon: 16, radius: "sm" },
3286
- sm: { icon: 20, radius: "sm" },
3287
- md: { icon: 24, radius: "md" },
3288
- lg: { icon: 32, radius: "md" }
3289
- };
3290
- var TOOLTIP_HIDE_DELAY_MS = 100;
3291
- function SGhostButton({
3292
- icon,
3293
- size = "sm",
3294
- intent = "default",
3295
- ariaLabel = "",
3296
- tooltipText,
3297
- disabled = false,
3298
- onClick,
3299
- className,
3300
- style
3301
- }) {
3302
- const portalContainer = usePortalContainer();
3303
- const [open, setOpen] = react.useState(false);
3304
- const hideTimer = react.useRef(void 0);
3305
- const cancelHide = () => {
3306
- if (hideTimer.current) {
3307
- clearTimeout(hideTimer.current);
3308
- hideTimer.current = void 0;
3309
- }
3310
- };
3311
- const openNow = () => {
3312
- cancelHide();
3313
- setOpen(true);
3314
- };
3315
- const startHide = () => {
3316
- cancelHide();
3317
- hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
3318
- };
3319
- const s = SIZE[size];
3320
- const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
3321
- const btn = /* @__PURE__ */ jsxRuntime.jsxs(
3322
- "button",
3323
- {
3324
- type: "button",
3325
- disabled,
3326
- "aria-disabled": disabled ? "true" : void 0,
3327
- "aria-label": ariaLabel.trim() || void 0,
3328
- tabIndex: disabled ? -1 : void 0,
3329
- onClick: (e) => {
3330
- if (disabled) {
3331
- e.preventDefault();
3332
- return;
3333
- }
3334
- onClick?.(e);
3335
- },
3336
- className: cn(
3337
- "group relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
3338
- disabled ? "cursor-not-allowed" : "cursor-pointer",
3339
- "focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
3340
- className
3341
- ),
3342
- style: {
3343
- width: `var(--cmp-ghostButton-${size}-size)`,
3344
- height: `var(--cmp-ghostButton-${size}-size)`,
3345
- borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
3346
- ...style
3347
- },
3348
- children: [
3349
- !disabled && /* @__PURE__ */ jsxRuntime.jsx(
3350
- "span",
3351
- {
3352
- "aria-hidden": true,
3353
- className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover:opacity-5",
3354
- style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
3355
- }
3356
- ),
3357
- /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
3358
- ]
3359
- }
3360
- );
3361
- const text = (tooltipText ?? "").trim();
3362
- if (text === "") return btn;
3363
- return /* @__PURE__ */ jsxRuntime.jsxs(RPopover__namespace.Root, { open, onOpenChange: setOpen, children: [
3364
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3365
- "span",
3366
- {
3367
- className: "inline-flex",
3368
- onMouseEnter: openNow,
3369
- onMouseLeave: startHide,
3370
- onFocus: openNow,
3371
- onBlur: startHide,
3372
- children: btn
3373
- }
3374
- ) }),
3375
- /* @__PURE__ */ jsxRuntime.jsx(RPopover__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
3376
- RPopover__namespace.Content,
3377
- {
3378
- side: "top",
3379
- sideOffset: 4,
3380
- className: cn("z-50", FLOATING_SLIDE_ANIM),
3381
- "aria-hidden": true,
3382
- onOpenAutoFocus: (e) => e.preventDefault(),
3383
- onMouseEnter: cancelHide,
3384
- onMouseLeave: startHide,
3385
- children: /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
3386
- }
3387
- ) })
3388
- ] });
3389
- }
3390
3401
  var SChip = react.forwardRef(function SChip2({
3391
3402
  name,
3392
3403
  value = "",
3393
3404
  error: errorProp = false,
3394
3405
  disabled = false,
3395
- editable = true,
3406
+ editable = false,
3396
3407
  showRemove = true,
3397
3408
  placeholder = "",
3398
3409
  rules,
@@ -3545,6 +3556,7 @@ function SFilePicker({
3545
3556
  hovered = false,
3546
3557
  inline = false,
3547
3558
  multiple = false,
3559
+ useMultipleListBox = false,
3548
3560
  accept,
3549
3561
  maxFileSize,
3550
3562
  maxTotalSize,
@@ -3570,10 +3582,13 @@ function SFilePicker({
3570
3582
  const autoId = react.useId();
3571
3583
  const resolvedName = name ?? autoId;
3572
3584
  const inputRef = react.useRef(null);
3585
+ const contentBoxRef = react.useRef(null);
3573
3586
  const [ruleError, setRuleError] = react.useState("");
3587
+ const [listOpen, setListOpen] = react.useState(false);
3588
+ const portalContainer = usePortalContainer();
3574
3589
  const files = multiple ? Array.isArray(value) ? value : [] : value && !Array.isArray(value) ? value : null;
3575
- const hasSingle = !multiple && !!files;
3576
- const displayText = hasSingle ? files.name : placeholder;
3590
+ const hasFiles = multiple ? files.length > 0 : !!files;
3591
+ const displayText = !hasFiles ? placeholder : multiple ? files.map((f) => f.name).join(", ") : files.name;
3577
3592
  useFormField({
3578
3593
  name,
3579
3594
  validate: () => {
@@ -3624,10 +3639,12 @@ function SFilePicker({
3624
3639
  e.stopPropagation();
3625
3640
  if (disabled) return;
3626
3641
  onValueChange?.(multiple ? [] : null);
3642
+ setListOpen(false);
3627
3643
  };
3628
3644
  const removeChip = (i) => {
3629
3645
  const next = files.filter((_, idx) => idx !== i);
3630
3646
  onValueChange?.(next);
3647
+ if (next.length === 0) setListOpen(false);
3631
3648
  };
3632
3649
  const open = () => {
3633
3650
  if (!disabled) inputRef.current?.click();
@@ -3637,78 +3654,100 @@ function SFilePicker({
3637
3654
  const padX = size === "md" ? "var(--cmp-filepicker-md-paddingX)" : "var(--cmp-filepicker-sm-paddingX)";
3638
3655
  const iconSize = size === "md" ? 20 : 16;
3639
3656
  const gapCls = size === "md" ? "gap-[var(--cmp-filepicker-md-gap)]" : "gap-[var(--cmp-filepicker-sm-gap)]";
3640
- const content = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex h-full w-full items-center", gapCls), style: { padding: `0 ${padX}` }, children: [
3657
+ const inlinePlaceholder = inline && !disabled && !hasFiles;
3658
+ const showListbox = multiple && useMultipleListBox;
3659
+ const filesArr = multiple ? files : [];
3660
+ const contentInner = /* @__PURE__ */ jsxRuntime.jsxs(
3661
+ "div",
3662
+ {
3663
+ ref: contentBoxRef,
3664
+ className: cn("group/filepicker flex h-full w-full items-center", gapCls),
3665
+ style: { padding: inline ? 0 : `0 ${padX}` },
3666
+ children: [
3667
+ /* @__PURE__ */ jsxRuntime.jsx(
3668
+ "input",
3669
+ {
3670
+ ref: inputRef,
3671
+ type: "file",
3672
+ className: "hidden",
3673
+ disabled,
3674
+ multiple,
3675
+ accept,
3676
+ name: resolvedName,
3677
+ onChange,
3678
+ "aria-label": placeholder
3679
+ }
3680
+ ),
3681
+ /* @__PURE__ */ jsxRuntime.jsxs(
3682
+ "button",
3683
+ {
3684
+ type: "button",
3685
+ disabled,
3686
+ onClick: open,
3687
+ "aria-label": placeholder,
3688
+ className: cn(
3689
+ "flex min-w-0 flex-1 items-center bg-transparent text-left",
3690
+ gapCls,
3691
+ disabled ? "cursor-not-allowed" : "cursor-pointer"
3692
+ ),
3693
+ children: [
3694
+ /* @__PURE__ */ jsxRuntime.jsx(
3695
+ SIcon,
3696
+ {
3697
+ name: "attachFile",
3698
+ size: iconSize,
3699
+ color: disabled ? "var(--cmp-filepicker-icon-disabled)" : "var(--color-grey-65)"
3700
+ }
3701
+ ),
3702
+ /* @__PURE__ */ jsxRuntime.jsx(
3703
+ "span",
3704
+ {
3705
+ className: cn(
3706
+ "min-w-0 flex-1 truncate text-[12px]",
3707
+ inlinePlaceholder && "text-[color:var(--cmp-filepicker-text-placeholder)] transition-colors group-hover/filepicker:text-[color:var(--cmp-filepicker-text-default)]"
3708
+ ),
3709
+ style: inlinePlaceholder ? void 0 : {
3710
+ color: disabled ? "var(--cmp-filepicker-text-disabled)" : hasFiles ? "var(--cmp-filepicker-text-default)" : "var(--cmp-filepicker-text-placeholder)"
3711
+ },
3712
+ children: displayText
3713
+ }
3714
+ )
3715
+ ]
3716
+ }
3717
+ ),
3718
+ showListbox && hasFiles && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
3719
+ Ariakit__namespace.PopoverDisclosure,
3720
+ {
3721
+ render: /* @__PURE__ */ jsxRuntime.jsx(SGhostButton, { icon: "caretDown", size: "xxs", ariaLabel: "toggle file list" }),
3722
+ className: cn("shrink-0 transition-transform duration-150", listOpen && "rotate-180")
3723
+ }
3724
+ ),
3725
+ !disabled && hasFiles && /* @__PURE__ */ jsxRuntime.jsx(SGhostButton, { icon: "close", size: "xxs", ariaLabel: "clear", onClick: clear })
3726
+ ]
3727
+ }
3728
+ );
3729
+ const content = showListbox ? /* @__PURE__ */ jsxRuntime.jsxs(Ariakit__namespace.PopoverProvider, { open: listOpen, setOpen: setListOpen, placement: "bottom-start", children: [
3730
+ contentInner,
3641
3731
  /* @__PURE__ */ jsxRuntime.jsx(
3642
- "input",
3732
+ Ariakit__namespace.Popover,
3643
3733
  {
3644
- ref: inputRef,
3645
- type: "file",
3646
- className: "hidden",
3647
- disabled,
3648
- multiple,
3649
- accept,
3650
- name: resolvedName,
3651
- onChange,
3652
- "aria-label": placeholder
3734
+ portal: true,
3735
+ portalElement: portalContainer,
3736
+ gutter: 4,
3737
+ unmountOnHide: true,
3738
+ getAnchorRect: () => contentBoxRef.current?.getBoundingClientRect() ?? null,
3739
+ style: { minWidth: contentBoxRef.current?.offsetWidth },
3740
+ 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)]",
3741
+ children: filesArr.map((f, i) => /* @__PURE__ */ jsxRuntime.jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i))
3653
3742
  }
3654
- ),
3655
- /* @__PURE__ */ jsxRuntime.jsxs(
3656
- "button",
3657
- {
3658
- type: "button",
3659
- disabled,
3660
- onClick: open,
3661
- "aria-label": placeholder,
3662
- className: cn(
3663
- "flex min-w-0 flex-1 items-center bg-transparent text-left",
3664
- gapCls,
3665
- disabled ? "cursor-not-allowed" : "cursor-pointer"
3666
- ),
3667
- children: [
3668
- /* @__PURE__ */ jsxRuntime.jsx(
3669
- SIcon,
3670
- {
3671
- name: "attachFile",
3672
- size: iconSize,
3673
- color: disabled ? "var(--cmp-filepicker-icon-disabled)" : "var(--color-grey-65)"
3674
- }
3675
- ),
3676
- /* @__PURE__ */ jsxRuntime.jsx(
3677
- "span",
3678
- {
3679
- className: "min-w-0 flex-1 truncate text-[12px]",
3680
- style: {
3681
- color: disabled ? "var(--cmp-filepicker-text-disabled)" : hasSingle ? "var(--cmp-filepicker-text-default)" : "var(--cmp-filepicker-text-placeholder)"
3682
- },
3683
- children: displayText
3684
- }
3685
- )
3686
- ]
3687
- }
3688
- ),
3689
- !disabled && hasSingle && /* @__PURE__ */ jsxRuntime.jsx(SGhostButton, { icon: "close", size: "xxs", ariaLabel: "clear", onClick: clear })
3690
- ] });
3691
- 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)) });
3692
- if (inline) {
3693
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("inline-flex flex-col", className), style, children: [
3694
- /* @__PURE__ */ jsxRuntime.jsx(
3695
- "div",
3696
- {
3697
- 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)]",
3698
- style: {
3699
- height: "var(--cmp-filepicker-sm-height)",
3700
- width: typeof width === "number" ? `${width}px` : width
3701
- },
3702
- children: content
3703
- }
3704
- ),
3705
- chips
3706
- ] });
3707
- }
3743
+ )
3744
+ ] }) : contentInner;
3745
+ const chips = multiple && !useMultipleListBox && filesArr.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-[8px] flex flex-wrap gap-[8px]", children: filesArr.map((f, i) => /* @__PURE__ */ jsxRuntime.jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i)) });
3708
3746
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style, children: [
3709
3747
  /* @__PURE__ */ jsxRuntime.jsx(
3710
3748
  SField,
3711
3749
  {
3750
+ borderless: inline,
3712
3751
  label,
3713
3752
  labelWidth,
3714
3753
  icon,