sellmate-design-system-react 0.1.8 → 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 (104) 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 +218 -209
  6. package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
  7. package/dist/components/SBarcodeInput/SBarcodeInput.js +218 -209
  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 +235 -221
  19. package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
  20. package/dist/components/SChipInput/SChipInput.js +235 -221
  21. package/dist/components/SChipInput/SChipInput.js.map +1 -1
  22. package/dist/components/SCircleProgress/SCircleProgress.cjs +53 -55
  23. package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
  24. package/dist/components/SCircleProgress/SCircleProgress.js +53 -55
  25. package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
  26. package/dist/components/SDatePicker/SDatePicker.cjs +223 -214
  27. package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
  28. package/dist/components/SDatePicker/SDatePicker.js +223 -214
  29. package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
  30. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +298 -263
  31. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  32. package/dist/components/SDateRangePicker/SDateRangePicker.js +298 -263
  33. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  34. package/dist/components/SDropdownButton/SDropdownButton.cjs +4 -4
  35. package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
  36. package/dist/components/SDropdownButton/SDropdownButton.js +4 -4
  37. package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
  38. package/dist/components/SField/SField.cjs +218 -21
  39. package/dist/components/SField/SField.cjs.map +1 -1
  40. package/dist/components/SField/SField.d.ts +11 -0
  41. package/dist/components/SField/SField.js +218 -21
  42. package/dist/components/SField/SField.js.map +1 -1
  43. package/dist/components/SFilePicker/SFilePicker.cjs +298 -278
  44. package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
  45. package/dist/components/SFilePicker/SFilePicker.d.ts +4 -2
  46. package/dist/components/SFilePicker/SFilePicker.js +298 -278
  47. package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
  48. package/dist/components/SInput/SInput.cjs +218 -209
  49. package/dist/components/SInput/SInput.cjs.map +1 -1
  50. package/dist/components/SInput/SInput.js +218 -209
  51. package/dist/components/SInput/SInput.js.map +1 -1
  52. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +610 -450
  53. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  54. package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +3 -1
  55. package/dist/components/SKeyValueTable/SKeyValueTable.js +610 -450
  56. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  57. package/dist/components/SLoadingContainer/SLoadingContainer.cjs +61 -55
  58. package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
  59. package/dist/components/SLoadingContainer/SLoadingContainer.js +61 -55
  60. package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
  61. package/dist/components/SLoadingContainer/loading.cjs +61 -55
  62. package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
  63. package/dist/components/SLoadingContainer/loading.js +61 -55
  64. package/dist/components/SLoadingContainer/loading.js.map +1 -1
  65. package/dist/components/SLoadingModal/SLoadingModal.cjs +56 -57
  66. package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
  67. package/dist/components/SLoadingModal/SLoadingModal.js +56 -57
  68. package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
  69. package/dist/components/SNumberInput/SNumberInput.cjs +326 -50
  70. package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
  71. package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
  72. package/dist/components/SNumberInput/SNumberInput.js +326 -50
  73. package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
  74. package/dist/components/SPagination/SPagination.cjs +1 -1
  75. package/dist/components/SPagination/SPagination.cjs.map +1 -1
  76. package/dist/components/SPagination/SPagination.js +1 -1
  77. package/dist/components/SPagination/SPagination.js.map +1 -1
  78. package/dist/components/SSelect/SSelect.cjs +67 -32
  79. package/dist/components/SSelect/SSelect.cjs.map +1 -1
  80. package/dist/components/SSelect/SSelect.js +67 -32
  81. package/dist/components/SSelect/SSelect.js.map +1 -1
  82. package/dist/components/STable/STable.cjs +121 -88
  83. package/dist/components/STable/STable.cjs.map +1 -1
  84. package/dist/components/STable/STable.js +121 -88
  85. package/dist/components/STable/STable.js.map +1 -1
  86. package/dist/components/STextarea/STextarea.cjs +218 -21
  87. package/dist/components/STextarea/STextarea.cjs.map +1 -1
  88. package/dist/components/STextarea/STextarea.js +218 -21
  89. package/dist/components/STextarea/STextarea.js.map +1 -1
  90. package/dist/components/SToast/SToastContainer.cjs +3 -3
  91. package/dist/components/SToast/SToastContainer.cjs.map +1 -1
  92. package/dist/components/SToast/SToastContainer.js +3 -3
  93. package/dist/components/SToast/SToastContainer.js.map +1 -1
  94. package/dist/components/STooltip/STooltip.cjs +209 -14
  95. package/dist/components/STooltip/STooltip.cjs.map +1 -1
  96. package/dist/components/STooltip/STooltip.js +209 -14
  97. package/dist/components/STooltip/STooltip.js.map +1 -1
  98. package/dist/index.cjs +432 -260
  99. package/dist/index.cjs.map +1 -1
  100. package/dist/index.js +432 -260
  101. package/dist/index.js.map +1 -1
  102. package/dist/styles.css +51 -27
  103. package/dist/theme.css +4 -2
  104. package/package.json +1 -1
@@ -2,8 +2,8 @@
2
2
  import { forwardRef, createContext, useState, useRef, useImperativeHandle, useEffect, useContext, useId } from 'react';
3
3
  import { clsx } from 'clsx';
4
4
  import { extendTailwindMerge } from 'tailwind-merge';
5
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
- import * as RPopover from '@radix-ui/react-popover';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+ import * as RPopover2 from '@radix-ui/react-popover';
7
7
 
8
8
  var twMerge = extendTailwindMerge({
9
9
  extend: {
@@ -2875,11 +2875,200 @@ PortalContainerContext.Provider;
2875
2875
  function usePortalContainer() {
2876
2876
  return useContext(PortalContainerContext) ?? void 0;
2877
2877
  }
2878
+
2879
+ // src/components/STag/tag.config.ts
2880
+ var TAG_SIZE_CONFIG = {
2881
+ xs: {
2882
+ height: 20,
2883
+ paddingX: 8,
2884
+ gap: 4,
2885
+ radius: 6,
2886
+ iconSize: 12,
2887
+ fontSize: 11,
2888
+ fontWeight: 500,
2889
+ lineHeight: 18
2890
+ },
2891
+ sm: {
2892
+ height: 24,
2893
+ paddingX: 8,
2894
+ gap: 4,
2895
+ radius: 6,
2896
+ iconSize: 16,
2897
+ fontSize: 12,
2898
+ fontWeight: 700,
2899
+ lineHeight: 20
2900
+ },
2901
+ md: {
2902
+ height: 28,
2903
+ paddingX: 12,
2904
+ gap: 6,
2905
+ radius: 8,
2906
+ iconSize: 16,
2907
+ fontSize: 14,
2908
+ fontWeight: 700,
2909
+ lineHeight: 24
2910
+ }
2911
+ };
2912
+ var STag = forwardRef(function STag2({
2913
+ shape = "square",
2914
+ size = "sm",
2915
+ color = "grey",
2916
+ label = "",
2917
+ icon,
2918
+ iconLeft = true,
2919
+ width,
2920
+ className,
2921
+ style,
2922
+ ...rest
2923
+ }, ref) {
2924
+ const s = TAG_SIZE_CONFIG[size];
2925
+ const contentColor = `var(--cmp-tag-${color}-content)`;
2926
+ const iconNode = icon ? /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.iconSize, color: contentColor }) : null;
2927
+ const resolvedWidth = typeof width === "number" ? `${width}px` : width;
2928
+ return /* @__PURE__ */ jsxs(
2929
+ "span",
2930
+ {
2931
+ ref,
2932
+ "aria-label": label || "tag",
2933
+ className: cn(
2934
+ "box-border inline-flex w-fit select-none items-center justify-center whitespace-nowrap",
2935
+ className
2936
+ ),
2937
+ style: {
2938
+ minHeight: s.height,
2939
+ padding: `0 ${s.paddingX}px`,
2940
+ gap: s.gap,
2941
+ borderRadius: shape === "pill" ? 999 : s.radius,
2942
+ fontSize: s.fontSize,
2943
+ fontWeight: s.fontWeight,
2944
+ lineHeight: `${s.lineHeight}px`,
2945
+ background: `var(--cmp-tag-${color}-bg)`,
2946
+ color: contentColor,
2947
+ ...resolvedWidth != null ? { width: resolvedWidth } : {},
2948
+ ...style
2949
+ },
2950
+ ...rest,
2951
+ children: [
2952
+ icon && iconLeft && iconNode,
2953
+ label && /* @__PURE__ */ jsx("span", { className: "inline-flex min-w-0 items-center justify-center", children: label }),
2954
+ icon && !iconLeft && iconNode
2955
+ ]
2956
+ }
2957
+ );
2958
+ });
2959
+ var SIZE = {
2960
+ xxs: { icon: 12, radius: "sm" },
2961
+ xs: { icon: 16, radius: "sm" },
2962
+ sm: { icon: 20, radius: "sm" },
2963
+ md: { icon: 24, radius: "md" },
2964
+ lg: { icon: 32, radius: "md" }
2965
+ };
2966
+ var TOOLTIP_HIDE_DELAY_MS = 100;
2967
+ function SGhostButton({
2968
+ icon,
2969
+ size = "sm",
2970
+ intent = "default",
2971
+ ariaLabel = "",
2972
+ tooltipText,
2973
+ disabled = false,
2974
+ onClick,
2975
+ className,
2976
+ style
2977
+ }) {
2978
+ const portalContainer = usePortalContainer();
2979
+ const [open, setOpen] = useState(false);
2980
+ const hideTimer = useRef(void 0);
2981
+ const cancelHide = () => {
2982
+ if (hideTimer.current) {
2983
+ clearTimeout(hideTimer.current);
2984
+ hideTimer.current = void 0;
2985
+ }
2986
+ };
2987
+ const openNow = () => {
2988
+ cancelHide();
2989
+ setOpen(true);
2990
+ };
2991
+ const startHide = () => {
2992
+ cancelHide();
2993
+ hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
2994
+ };
2995
+ const s = SIZE[size];
2996
+ const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
2997
+ const btn = /* @__PURE__ */ jsxs(
2998
+ "button",
2999
+ {
3000
+ type: "button",
3001
+ disabled,
3002
+ "aria-disabled": disabled ? "true" : void 0,
3003
+ "aria-label": ariaLabel.trim() || void 0,
3004
+ tabIndex: disabled ? -1 : void 0,
3005
+ onClick: (e) => {
3006
+ if (disabled) {
3007
+ e.preventDefault();
3008
+ return;
3009
+ }
3010
+ onClick?.(e);
3011
+ },
3012
+ className: cn(
3013
+ "group relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
3014
+ disabled ? "cursor-not-allowed" : "cursor-pointer",
3015
+ "focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
3016
+ className
3017
+ ),
3018
+ style: {
3019
+ width: `var(--cmp-ghostButton-${size}-size)`,
3020
+ height: `var(--cmp-ghostButton-${size}-size)`,
3021
+ borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
3022
+ ...style
3023
+ },
3024
+ children: [
3025
+ !disabled && /* @__PURE__ */ jsx(
3026
+ "span",
3027
+ {
3028
+ "aria-hidden": true,
3029
+ className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover:opacity-5",
3030
+ style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
3031
+ }
3032
+ ),
3033
+ /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
3034
+ ]
3035
+ }
3036
+ );
3037
+ const text = (tooltipText ?? "").trim();
3038
+ if (text === "") return btn;
3039
+ return /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: setOpen, children: [
3040
+ /* @__PURE__ */ jsx(RPopover2.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
3041
+ "span",
3042
+ {
3043
+ className: "inline-flex",
3044
+ onMouseEnter: openNow,
3045
+ onMouseLeave: startHide,
3046
+ onFocus: openNow,
3047
+ onBlur: startHide,
3048
+ children: btn
3049
+ }
3050
+ ) }),
3051
+ /* @__PURE__ */ jsx(RPopover2.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx(
3052
+ RPopover2.Content,
3053
+ {
3054
+ side: "top",
3055
+ sideOffset: 4,
3056
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
3057
+ "aria-hidden": true,
3058
+ onOpenAutoFocus: (e) => e.preventDefault(),
3059
+ onMouseEnter: cancelHide,
3060
+ onMouseLeave: startHide,
3061
+ children: /* @__PURE__ */ jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
3062
+ }
3063
+ ) })
3064
+ ] });
3065
+ }
2878
3066
  var HIDE_DELAY_MS = 100;
2879
3067
  function useBody(content, message = []) {
2880
3068
  if (content != null) return content;
2881
3069
  if (message.length === 0) return null;
2882
- return /* @__PURE__ */ jsx(Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsx("div", { children: line }, i)) });
3070
+ const singleLine = message.length === 1;
3071
+ return /* @__PURE__ */ jsx(Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsx("div", { className: singleLine ? "leading-[24px]" : void 0, children: line }, i)) });
2883
3072
  }
2884
3073
  var STooltip = forwardRef(function STooltip2({
2885
3074
  content,
@@ -2937,7 +3126,7 @@ var STooltip = forwardRef(function STooltip2({
2937
3126
  if (disabled || body == null) return /* @__PURE__ */ jsx(Fragment, { children: triggerNode });
2938
3127
  const isHover = trigger === "hover";
2939
3128
  const contentClass = cn(
2940
- "z-50 max-w-xs rounded-[6px] px-[16px] py-[12px] text-[12px] leading-[20px]",
3129
+ "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)]",
2941
3130
  FLOATING_SLIDE_ANIM,
2942
3131
  className
2943
3132
  );
@@ -2947,10 +3136,10 @@ var STooltip = forwardRef(function STooltip2({
2947
3136
  fontWeight: tooltipType === "default" ? 500 : 700,
2948
3137
  ...style
2949
3138
  };
2950
- return /* @__PURE__ */ jsxs(RPopover.Root, { open, onOpenChange: setOpen, children: [
3139
+ return /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: setOpen, children: [
2951
3140
  isHover ? (
2952
3141
  // hover: Anchor(위치 기준) + 직접 mouse 핸들러 — 클릭으로 토글되지 않도록 Trigger 미사용
2953
- /* @__PURE__ */ jsx(RPopover.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
3142
+ /* @__PURE__ */ jsx(RPopover2.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
2954
3143
  "span",
2955
3144
  {
2956
3145
  className: "inline-flex",
@@ -2961,9 +3150,9 @@ var STooltip = forwardRef(function STooltip2({
2961
3150
  children: triggerNode
2962
3151
  }
2963
3152
  ) })
2964
- ) : /* @__PURE__ */ jsx(RPopover.Trigger, { asChild: true, children: triggerNode }),
2965
- /* @__PURE__ */ jsx(RPopover.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxs(
2966
- RPopover.Content,
3153
+ ) : /* @__PURE__ */ jsx(RPopover2.Trigger, { asChild: true, children: triggerNode }),
3154
+ /* @__PURE__ */ jsx(RPopover2.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxs(
3155
+ RPopover2.Content,
2967
3156
  {
2968
3157
  side: placement,
2969
3158
  sideOffset: 8,
@@ -2982,15 +3171,21 @@ var STooltip = forwardRef(function STooltip2({
2982
3171
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-[12px]", children: [
2983
3172
  /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: body }),
2984
3173
  useClose && /* @__PURE__ */ jsx(
2985
- RPopover.Close,
3174
+ SGhostButton,
2986
3175
  {
2987
- "aria-label": "\uB2EB\uAE30",
2988
- className: "shrink-0 cursor-pointer opacity-70 hover:opacity-100",
2989
- children: /* @__PURE__ */ jsx(SIcon, { name: "close", size: 16, color: fg })
3176
+ icon: "close",
3177
+ size: "xs",
3178
+ intent: tooltipType === "default" ? "inverse" : "default",
3179
+ ariaLabel: "\uB2EB\uAE30",
3180
+ className: "shrink-0",
3181
+ onClick: () => {
3182
+ cancelHide();
3183
+ setOpen(false);
3184
+ }
2990
3185
  }
2991
3186
  )
2992
3187
  ] }),
2993
- /* @__PURE__ */ jsx(RPopover.Arrow, { width: 16, height: 12, style: { fill: bg } })
3188
+ /* @__PURE__ */ jsx(RPopover2.Arrow, { width: 16, height: 12, style: { fill: bg } })
2994
3189
  ]
2995
3190
  }
2996
3191
  ) })
@@ -3024,8 +3219,8 @@ function useFormField(field) {
3024
3219
  }, [ctx, name]);
3025
3220
  }
3026
3221
  var FIELD_SIZE = {
3027
- sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 12 },
3028
- md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 12 }
3222
+ sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
3223
+ md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
3029
3224
  };
3030
3225
  var ALIGN = {
3031
3226
  start: "flex-start",
@@ -3040,6 +3235,7 @@ var SField = forwardRef(function SField2({
3040
3235
  labelTooltip = "",
3041
3236
  labelTooltipProps,
3042
3237
  labelWidth,
3238
+ labelHeight,
3043
3239
  size = "sm",
3044
3240
  addonLabel = "",
3045
3241
  addonAlign = "start",
@@ -3054,6 +3250,7 @@ var SField = forwardRef(function SField2({
3054
3250
  readOnly = false,
3055
3251
  width,
3056
3252
  multiline = false,
3253
+ borderless = false,
3057
3254
  children,
3058
3255
  className,
3059
3256
  style,
@@ -3087,8 +3284,8 @@ var SField = forwardRef(function SField2({
3087
3284
  const resolvedSuccess = status === "pass";
3088
3285
  const showError = resolvedError && errorMessage !== "";
3089
3286
  const interactive = !disabled && !readOnly;
3090
- 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)";
3091
- const bgColor = disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
3287
+ 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)";
3288
+ const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
3092
3289
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
3093
3290
  return /* @__PURE__ */ jsx(
3094
3291
  "div",
@@ -3105,7 +3302,7 @@ var SField = forwardRef(function SField2({
3105
3302
  {
3106
3303
  className: "flex shrink-0 items-center font-bold text-(--sys-color-field-text-default)",
3107
3304
  style: {
3108
- height: s.height,
3305
+ height: cssLen(labelHeight) ?? s.height,
3109
3306
  marginRight: s.fieldGap,
3110
3307
  width: cssLen(labelWidth),
3111
3308
  fontSize: s.fontSize,
@@ -3131,14 +3328,14 @@ var SField = forwardRef(function SField2({
3131
3328
  {
3132
3329
  className: cn(
3133
3330
  "relative box-border flex min-w-0 items-stretch overflow-hidden border border-solid border-(--field-bc)",
3134
- 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]",
3331
+ 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]",
3135
3332
  disabled && "cursor-not-allowed"
3136
3333
  ),
3137
3334
  style: {
3138
3335
  // @ts-expect-error CSS 변수
3139
3336
  "--field-bc": borderColor,
3140
3337
  // focused/hovered prop이 명시된 경우 강조를 즉시 반영
3141
- ...interactive && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
3338
+ ...interactive && !borderless && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
3142
3339
  ...multiline ? { minHeight: s.height } : { height: s.height },
3143
3340
  "width": cssLen(width),
3144
3341
  "borderRadius": s.radius,
@@ -3177,200 +3374,12 @@ var SField = forwardRef(function SField2({
3177
3374
  }
3178
3375
  );
3179
3376
  });
3180
-
3181
- // src/components/STag/tag.config.ts
3182
- var TAG_SIZE_CONFIG = {
3183
- xs: {
3184
- height: 20,
3185
- paddingX: 8,
3186
- gap: 4,
3187
- radius: 6,
3188
- iconSize: 12,
3189
- fontSize: 11,
3190
- fontWeight: 500,
3191
- lineHeight: 18
3192
- },
3193
- sm: {
3194
- height: 24,
3195
- paddingX: 8,
3196
- gap: 4,
3197
- radius: 6,
3198
- iconSize: 16,
3199
- fontSize: 12,
3200
- fontWeight: 700,
3201
- lineHeight: 20
3202
- },
3203
- md: {
3204
- height: 28,
3205
- paddingX: 12,
3206
- gap: 6,
3207
- radius: 8,
3208
- iconSize: 16,
3209
- fontSize: 14,
3210
- fontWeight: 700,
3211
- lineHeight: 24
3212
- }
3213
- };
3214
- var STag = forwardRef(function STag2({
3215
- shape = "square",
3216
- size = "sm",
3217
- color = "grey",
3218
- label = "",
3219
- icon,
3220
- iconLeft = true,
3221
- width,
3222
- className,
3223
- style,
3224
- ...rest
3225
- }, ref) {
3226
- const s = TAG_SIZE_CONFIG[size];
3227
- const contentColor = `var(--cmp-tag-${color}-content)`;
3228
- const iconNode = icon ? /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.iconSize, color: contentColor }) : null;
3229
- const resolvedWidth = typeof width === "number" ? `${width}px` : width;
3230
- return /* @__PURE__ */ jsxs(
3231
- "span",
3232
- {
3233
- ref,
3234
- "aria-label": label || "tag",
3235
- className: cn(
3236
- "box-border inline-flex w-fit select-none items-center justify-center whitespace-nowrap",
3237
- className
3238
- ),
3239
- style: {
3240
- minHeight: s.height,
3241
- padding: `0 ${s.paddingX}px`,
3242
- gap: s.gap,
3243
- borderRadius: shape === "pill" ? 999 : s.radius,
3244
- fontSize: s.fontSize,
3245
- fontWeight: s.fontWeight,
3246
- lineHeight: `${s.lineHeight}px`,
3247
- background: `var(--cmp-tag-${color}-bg)`,
3248
- color: contentColor,
3249
- ...resolvedWidth != null ? { width: resolvedWidth } : {},
3250
- ...style
3251
- },
3252
- ...rest,
3253
- children: [
3254
- icon && iconLeft && iconNode,
3255
- label && /* @__PURE__ */ jsx("span", { className: "inline-flex min-w-0 items-center justify-center", children: label }),
3256
- icon && !iconLeft && iconNode
3257
- ]
3258
- }
3259
- );
3260
- });
3261
- var SIZE = {
3262
- xxs: { icon: 12, radius: "sm" },
3263
- xs: { icon: 16, radius: "sm" },
3264
- sm: { icon: 20, radius: "sm" },
3265
- md: { icon: 24, radius: "md" },
3266
- lg: { icon: 32, radius: "md" }
3267
- };
3268
- var TOOLTIP_HIDE_DELAY_MS = 100;
3269
- function SGhostButton({
3270
- icon,
3271
- size = "sm",
3272
- intent = "default",
3273
- ariaLabel = "",
3274
- tooltipText,
3275
- disabled = false,
3276
- onClick,
3277
- className,
3278
- style
3279
- }) {
3280
- const portalContainer = usePortalContainer();
3281
- const [open, setOpen] = useState(false);
3282
- const hideTimer = useRef(void 0);
3283
- const cancelHide = () => {
3284
- if (hideTimer.current) {
3285
- clearTimeout(hideTimer.current);
3286
- hideTimer.current = void 0;
3287
- }
3288
- };
3289
- const openNow = () => {
3290
- cancelHide();
3291
- setOpen(true);
3292
- };
3293
- const startHide = () => {
3294
- cancelHide();
3295
- hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
3296
- };
3297
- const s = SIZE[size];
3298
- const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
3299
- const btn = /* @__PURE__ */ jsxs(
3300
- "button",
3301
- {
3302
- type: "button",
3303
- disabled,
3304
- "aria-disabled": disabled ? "true" : void 0,
3305
- "aria-label": ariaLabel.trim() || void 0,
3306
- tabIndex: disabled ? -1 : void 0,
3307
- onClick: (e) => {
3308
- if (disabled) {
3309
- e.preventDefault();
3310
- return;
3311
- }
3312
- onClick?.(e);
3313
- },
3314
- className: cn(
3315
- "group relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
3316
- disabled ? "cursor-not-allowed" : "cursor-pointer",
3317
- "focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
3318
- className
3319
- ),
3320
- style: {
3321
- width: `var(--cmp-ghostButton-${size}-size)`,
3322
- height: `var(--cmp-ghostButton-${size}-size)`,
3323
- borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
3324
- ...style
3325
- },
3326
- children: [
3327
- !disabled && /* @__PURE__ */ jsx(
3328
- "span",
3329
- {
3330
- "aria-hidden": true,
3331
- className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover:opacity-5",
3332
- style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
3333
- }
3334
- ),
3335
- /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
3336
- ]
3337
- }
3338
- );
3339
- const text = (tooltipText ?? "").trim();
3340
- if (text === "") return btn;
3341
- return /* @__PURE__ */ jsxs(RPopover.Root, { open, onOpenChange: setOpen, children: [
3342
- /* @__PURE__ */ jsx(RPopover.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
3343
- "span",
3344
- {
3345
- className: "inline-flex",
3346
- onMouseEnter: openNow,
3347
- onMouseLeave: startHide,
3348
- onFocus: openNow,
3349
- onBlur: startHide,
3350
- children: btn
3351
- }
3352
- ) }),
3353
- /* @__PURE__ */ jsx(RPopover.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx(
3354
- RPopover.Content,
3355
- {
3356
- side: "top",
3357
- sideOffset: 4,
3358
- className: cn("z-50", FLOATING_SLIDE_ANIM),
3359
- "aria-hidden": true,
3360
- onOpenAutoFocus: (e) => e.preventDefault(),
3361
- onMouseEnter: cancelHide,
3362
- onMouseLeave: startHide,
3363
- children: /* @__PURE__ */ jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
3364
- }
3365
- ) })
3366
- ] });
3367
- }
3368
3377
  var SChip = forwardRef(function SChip2({
3369
3378
  name,
3370
3379
  value = "",
3371
3380
  error: errorProp = false,
3372
3381
  disabled = false,
3373
- editable = true,
3382
+ editable = false,
3374
3383
  showRemove = true,
3375
3384
  placeholder = "",
3376
3385
  rules,
@@ -3523,6 +3532,7 @@ function SFilePicker({
3523
3532
  hovered = false,
3524
3533
  inline = false,
3525
3534
  multiple = false,
3535
+ useMultipleListBox = false,
3526
3536
  accept,
3527
3537
  maxFileSize,
3528
3538
  maxTotalSize,
@@ -3550,8 +3560,8 @@ function SFilePicker({
3550
3560
  const inputRef = useRef(null);
3551
3561
  const [ruleError, setRuleError] = useState("");
3552
3562
  const files = multiple ? Array.isArray(value) ? value : [] : value && !Array.isArray(value) ? value : null;
3553
- const hasSingle = !multiple && !!files;
3554
- const displayText = hasSingle ? files.name : placeholder;
3563
+ const hasFiles = multiple ? files.length > 0 : !!files;
3564
+ const displayText = !hasFiles ? placeholder : multiple ? files.map((f) => f.name).join(", ") : files.name;
3555
3565
  useFormField({
3556
3566
  name,
3557
3567
  validate: () => {
@@ -3615,78 +3625,88 @@ function SFilePicker({
3615
3625
  const padX = size === "md" ? "var(--cmp-filepicker-md-paddingX)" : "var(--cmp-filepicker-sm-paddingX)";
3616
3626
  const iconSize = size === "md" ? 20 : 16;
3617
3627
  const gapCls = size === "md" ? "gap-[var(--cmp-filepicker-md-gap)]" : "gap-[var(--cmp-filepicker-sm-gap)]";
3618
- const content = /* @__PURE__ */ jsxs("div", { className: cn("flex h-full w-full items-center", gapCls), style: { padding: `0 ${padX}` }, children: [
3619
- /* @__PURE__ */ jsx(
3620
- "input",
3621
- {
3622
- ref: inputRef,
3623
- type: "file",
3624
- className: "hidden",
3625
- disabled,
3626
- multiple,
3627
- accept,
3628
- name: resolvedName,
3629
- onChange,
3630
- "aria-label": placeholder
3631
- }
3632
- ),
3633
- /* @__PURE__ */ jsxs(
3634
- "button",
3635
- {
3636
- type: "button",
3637
- disabled,
3638
- onClick: open,
3639
- "aria-label": placeholder,
3640
- className: cn(
3641
- "flex min-w-0 flex-1 items-center bg-transparent text-left",
3642
- gapCls,
3643
- disabled ? "cursor-not-allowed" : "cursor-pointer"
3628
+ const inlinePlaceholder = inline && !disabled && !hasFiles;
3629
+ const content = /* @__PURE__ */ jsxs(
3630
+ "div",
3631
+ {
3632
+ className: cn("group/filepicker flex h-full w-full items-center", gapCls),
3633
+ style: { padding: inline ? 0 : `0 ${padX}` },
3634
+ children: [
3635
+ /* @__PURE__ */ jsx(
3636
+ "input",
3637
+ {
3638
+ ref: inputRef,
3639
+ type: "file",
3640
+ className: "hidden",
3641
+ disabled,
3642
+ multiple,
3643
+ accept,
3644
+ name: resolvedName,
3645
+ onChange,
3646
+ "aria-label": placeholder
3647
+ }
3648
+ ),
3649
+ /* @__PURE__ */ jsxs(
3650
+ "button",
3651
+ {
3652
+ type: "button",
3653
+ disabled,
3654
+ onClick: open,
3655
+ "aria-label": placeholder,
3656
+ className: cn(
3657
+ "flex min-w-0 flex-1 items-center bg-transparent text-left",
3658
+ gapCls,
3659
+ disabled ? "cursor-not-allowed" : "cursor-pointer"
3660
+ ),
3661
+ children: [
3662
+ /* @__PURE__ */ jsx(
3663
+ SIcon,
3664
+ {
3665
+ name: "attachFile",
3666
+ size: iconSize,
3667
+ color: disabled ? "var(--cmp-filepicker-icon-disabled)" : "var(--color-grey-65)"
3668
+ }
3669
+ ),
3670
+ /* @__PURE__ */ jsx(
3671
+ "span",
3672
+ {
3673
+ className: cn(
3674
+ "min-w-0 flex-1 truncate text-[12px]",
3675
+ inlinePlaceholder && "text-[color:var(--cmp-filepicker-text-placeholder)] transition-colors group-hover/filepicker:text-[color:var(--cmp-filepicker-text-default)]"
3676
+ ),
3677
+ style: inlinePlaceholder ? void 0 : {
3678
+ color: disabled ? "var(--cmp-filepicker-text-disabled)" : hasFiles ? "var(--cmp-filepicker-text-default)" : "var(--cmp-filepicker-text-placeholder)"
3679
+ },
3680
+ children: displayText
3681
+ }
3682
+ )
3683
+ ]
3684
+ }
3644
3685
  ),
3645
- children: [
3646
- /* @__PURE__ */ jsx(
3647
- SIcon,
3648
- {
3649
- name: "attachFile",
3650
- size: iconSize,
3651
- color: disabled ? "var(--cmp-filepicker-icon-disabled)" : "var(--color-grey-65)"
3652
- }
3653
- ),
3654
- /* @__PURE__ */ jsx(
3655
- "span",
3656
- {
3657
- className: "min-w-0 flex-1 truncate text-[12px]",
3658
- style: {
3659
- color: disabled ? "var(--cmp-filepicker-text-disabled)" : hasSingle ? "var(--cmp-filepicker-text-default)" : "var(--cmp-filepicker-text-placeholder)"
3660
- },
3661
- children: displayText
3662
- }
3663
- )
3664
- ]
3665
- }
3666
- ),
3667
- !disabled && hasSingle && /* @__PURE__ */ jsx(SGhostButton, { icon: "close", size: "xxs", ariaLabel: "clear", onClick: clear })
3668
- ] });
3669
- const chips = multiple && files.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-[8px] flex flex-wrap gap-[8px]", children: files.map((f, i) => /* @__PURE__ */ jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i)) });
3670
- if (inline) {
3671
- return /* @__PURE__ */ jsxs("div", { className: cn("inline-flex flex-col", className), style, children: [
3672
- /* @__PURE__ */ jsx(
3673
- "div",
3674
- {
3675
- 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)]",
3676
- style: {
3677
- height: "var(--cmp-filepicker-sm-height)",
3678
- width: typeof width === "number" ? `${width}px` : width
3679
- },
3680
- children: content
3681
- }
3686
+ !disabled && hasFiles && /* @__PURE__ */ jsx(SGhostButton, { icon: "close", size: "xxs", ariaLabel: "clear", onClick: clear })
3687
+ ]
3688
+ }
3689
+ );
3690
+ const chips = multiple && files.length > 0 && /* @__PURE__ */ jsx(
3691
+ "div",
3692
+ {
3693
+ className: cn(
3694
+ "mt-[8px] flex gap-[8px]",
3695
+ 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"
3682
3696
  ),
3683
- chips
3684
- ] });
3685
- }
3697
+ style: useMultipleListBox ? {
3698
+ width: typeof width === "number" ? `${width}px` : width,
3699
+ // label 이 있으면 SField 입력 박스 시작 위치(labelWidth + fieldGap)에 맞춰 정렬
3700
+ marginLeft: label && labelWidth != null ? `calc(${typeof labelWidth === "number" ? `${labelWidth}px` : labelWidth} + var(--sys-space-field-${size}-gap))` : void 0
3701
+ } : void 0,
3702
+ children: files.map((f, i) => /* @__PURE__ */ jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i))
3703
+ }
3704
+ );
3686
3705
  return /* @__PURE__ */ jsxs("div", { className, style, children: [
3687
3706
  /* @__PURE__ */ jsx(
3688
3707
  SField,
3689
3708
  {
3709
+ borderless: inline,
3690
3710
  label,
3691
3711
  labelWidth,
3692
3712
  icon,