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
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import { forwardRef, createContext, useState, useRef, useImperativeHandle, useEffect, useContext, useId } from 'react';
3
+ import * as Ariakit from '@ariakit/react';
3
4
  import { clsx } from 'clsx';
4
5
  import { extendTailwindMerge } from 'tailwind-merge';
5
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
6
- import * as RPopover from '@radix-ui/react-popover';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
+ import * as RPopover2 from '@radix-ui/react-popover';
7
8
 
8
9
  var twMerge = extendTailwindMerge({
9
10
  extend: {
@@ -2875,11 +2876,200 @@ PortalContainerContext.Provider;
2875
2876
  function usePortalContainer() {
2876
2877
  return useContext(PortalContainerContext) ?? void 0;
2877
2878
  }
2879
+
2880
+ // src/components/STag/tag.config.ts
2881
+ var TAG_SIZE_CONFIG = {
2882
+ xs: {
2883
+ height: 20,
2884
+ paddingX: 8,
2885
+ gap: 4,
2886
+ radius: 6,
2887
+ iconSize: 12,
2888
+ fontSize: 11,
2889
+ fontWeight: 500,
2890
+ lineHeight: 18
2891
+ },
2892
+ sm: {
2893
+ height: 24,
2894
+ paddingX: 8,
2895
+ gap: 4,
2896
+ radius: 6,
2897
+ iconSize: 16,
2898
+ fontSize: 12,
2899
+ fontWeight: 700,
2900
+ lineHeight: 20
2901
+ },
2902
+ md: {
2903
+ height: 28,
2904
+ paddingX: 12,
2905
+ gap: 6,
2906
+ radius: 8,
2907
+ iconSize: 16,
2908
+ fontSize: 14,
2909
+ fontWeight: 700,
2910
+ lineHeight: 24
2911
+ }
2912
+ };
2913
+ var STag = forwardRef(function STag2({
2914
+ shape = "square",
2915
+ size = "sm",
2916
+ color = "grey",
2917
+ label = "",
2918
+ icon,
2919
+ iconLeft = true,
2920
+ width,
2921
+ className,
2922
+ style,
2923
+ ...rest
2924
+ }, ref) {
2925
+ const s = TAG_SIZE_CONFIG[size];
2926
+ const contentColor = `var(--cmp-tag-${color}-content)`;
2927
+ const iconNode = icon ? /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.iconSize, color: contentColor }) : null;
2928
+ const resolvedWidth = typeof width === "number" ? `${width}px` : width;
2929
+ return /* @__PURE__ */ jsxs(
2930
+ "span",
2931
+ {
2932
+ ref,
2933
+ "aria-label": label || "tag",
2934
+ className: cn(
2935
+ "box-border inline-flex w-fit select-none items-center justify-center whitespace-nowrap",
2936
+ className
2937
+ ),
2938
+ style: {
2939
+ minHeight: s.height,
2940
+ padding: `0 ${s.paddingX}px`,
2941
+ gap: s.gap,
2942
+ borderRadius: shape === "pill" ? 999 : s.radius,
2943
+ fontSize: s.fontSize,
2944
+ fontWeight: s.fontWeight,
2945
+ lineHeight: `${s.lineHeight}px`,
2946
+ background: `var(--cmp-tag-${color}-bg)`,
2947
+ color: contentColor,
2948
+ ...resolvedWidth != null ? { width: resolvedWidth } : {},
2949
+ ...style
2950
+ },
2951
+ ...rest,
2952
+ children: [
2953
+ icon && iconLeft && iconNode,
2954
+ label && /* @__PURE__ */ jsx("span", { className: "inline-flex min-w-0 items-center justify-center", children: label }),
2955
+ icon && !iconLeft && iconNode
2956
+ ]
2957
+ }
2958
+ );
2959
+ });
2960
+ var SIZE = {
2961
+ xxs: { icon: 12, radius: "sm" },
2962
+ xs: { icon: 16, radius: "sm" },
2963
+ sm: { icon: 20, radius: "sm" },
2964
+ md: { icon: 24, radius: "md" },
2965
+ lg: { icon: 32, radius: "md" }
2966
+ };
2967
+ var TOOLTIP_HIDE_DELAY_MS = 100;
2968
+ function SGhostButton({
2969
+ icon,
2970
+ size = "sm",
2971
+ intent = "default",
2972
+ ariaLabel = "",
2973
+ tooltipText,
2974
+ disabled = false,
2975
+ onClick,
2976
+ className,
2977
+ style
2978
+ }) {
2979
+ const portalContainer = usePortalContainer();
2980
+ const [open, setOpen] = useState(false);
2981
+ const hideTimer = useRef(void 0);
2982
+ const cancelHide = () => {
2983
+ if (hideTimer.current) {
2984
+ clearTimeout(hideTimer.current);
2985
+ hideTimer.current = void 0;
2986
+ }
2987
+ };
2988
+ const openNow = () => {
2989
+ cancelHide();
2990
+ setOpen(true);
2991
+ };
2992
+ const startHide = () => {
2993
+ cancelHide();
2994
+ hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
2995
+ };
2996
+ const s = SIZE[size];
2997
+ const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
2998
+ const btn = /* @__PURE__ */ jsxs(
2999
+ "button",
3000
+ {
3001
+ type: "button",
3002
+ disabled,
3003
+ "aria-disabled": disabled ? "true" : void 0,
3004
+ "aria-label": ariaLabel.trim() || void 0,
3005
+ tabIndex: disabled ? -1 : void 0,
3006
+ onClick: (e) => {
3007
+ if (disabled) {
3008
+ e.preventDefault();
3009
+ return;
3010
+ }
3011
+ onClick?.(e);
3012
+ },
3013
+ className: cn(
3014
+ "group relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
3015
+ disabled ? "cursor-not-allowed" : "cursor-pointer",
3016
+ "focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
3017
+ className
3018
+ ),
3019
+ style: {
3020
+ width: `var(--cmp-ghostButton-${size}-size)`,
3021
+ height: `var(--cmp-ghostButton-${size}-size)`,
3022
+ borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
3023
+ ...style
3024
+ },
3025
+ children: [
3026
+ !disabled && /* @__PURE__ */ jsx(
3027
+ "span",
3028
+ {
3029
+ "aria-hidden": true,
3030
+ className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover:opacity-5",
3031
+ style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
3032
+ }
3033
+ ),
3034
+ /* @__PURE__ */ jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
3035
+ ]
3036
+ }
3037
+ );
3038
+ const text = (tooltipText ?? "").trim();
3039
+ if (text === "") return btn;
3040
+ return /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: setOpen, children: [
3041
+ /* @__PURE__ */ jsx(RPopover2.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
3042
+ "span",
3043
+ {
3044
+ className: "inline-flex",
3045
+ onMouseEnter: openNow,
3046
+ onMouseLeave: startHide,
3047
+ onFocus: openNow,
3048
+ onBlur: startHide,
3049
+ children: btn
3050
+ }
3051
+ ) }),
3052
+ /* @__PURE__ */ jsx(RPopover2.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx(
3053
+ RPopover2.Content,
3054
+ {
3055
+ side: "top",
3056
+ sideOffset: 4,
3057
+ className: cn("z-50", FLOATING_SLIDE_ANIM),
3058
+ "aria-hidden": true,
3059
+ onOpenAutoFocus: (e) => e.preventDefault(),
3060
+ onMouseEnter: cancelHide,
3061
+ onMouseLeave: startHide,
3062
+ children: /* @__PURE__ */ jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
3063
+ }
3064
+ ) })
3065
+ ] });
3066
+ }
2878
3067
  var HIDE_DELAY_MS = 100;
2879
3068
  function useBody(content, message = []) {
2880
3069
  if (content != null) return content;
2881
3070
  if (message.length === 0) return null;
2882
- return /* @__PURE__ */ jsx(Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsx("div", { children: line }, i)) });
3071
+ const singleLine = message.length === 1;
3072
+ return /* @__PURE__ */ jsx(Fragment, { children: message.map((line, i) => /* @__PURE__ */ jsx("div", { className: singleLine ? "leading-[24px]" : void 0, children: line }, i)) });
2883
3073
  }
2884
3074
  var STooltip = forwardRef(function STooltip2({
2885
3075
  content,
@@ -2947,10 +3137,10 @@ var STooltip = forwardRef(function STooltip2({
2947
3137
  fontWeight: tooltipType === "default" ? 500 : 700,
2948
3138
  ...style
2949
3139
  };
2950
- return /* @__PURE__ */ jsxs(RPopover.Root, { open, onOpenChange: setOpen, children: [
3140
+ return /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: setOpen, children: [
2951
3141
  isHover ? (
2952
3142
  // hover: Anchor(위치 기준) + 직접 mouse 핸들러 — 클릭으로 토글되지 않도록 Trigger 미사용
2953
- /* @__PURE__ */ jsx(RPopover.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
3143
+ /* @__PURE__ */ jsx(RPopover2.Anchor, { asChild: true, children: /* @__PURE__ */ jsx(
2954
3144
  "span",
2955
3145
  {
2956
3146
  className: "inline-flex",
@@ -2961,9 +3151,9 @@ var STooltip = forwardRef(function STooltip2({
2961
3151
  children: triggerNode
2962
3152
  }
2963
3153
  ) })
2964
- ) : /* @__PURE__ */ jsx(RPopover.Trigger, { asChild: true, children: triggerNode }),
2965
- /* @__PURE__ */ jsx(RPopover.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxs(
2966
- RPopover.Content,
3154
+ ) : /* @__PURE__ */ jsx(RPopover2.Trigger, { asChild: true, children: triggerNode }),
3155
+ /* @__PURE__ */ jsx(RPopover2.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxs(
3156
+ RPopover2.Content,
2967
3157
  {
2968
3158
  side: placement,
2969
3159
  sideOffset: 8,
@@ -2982,15 +3172,21 @@ var STooltip = forwardRef(function STooltip2({
2982
3172
  /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-[12px]", children: [
2983
3173
  /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: body }),
2984
3174
  useClose && /* @__PURE__ */ jsx(
2985
- RPopover.Close,
3175
+ SGhostButton,
2986
3176
  {
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 })
3177
+ icon: "close",
3178
+ size: "xs",
3179
+ intent: tooltipType === "default" ? "inverse" : "default",
3180
+ ariaLabel: "\uB2EB\uAE30",
3181
+ className: "shrink-0",
3182
+ onClick: () => {
3183
+ cancelHide();
3184
+ setOpen(false);
3185
+ }
2990
3186
  }
2991
3187
  )
2992
3188
  ] }),
2993
- /* @__PURE__ */ jsx(RPopover.Arrow, { width: 16, height: 12, style: { fill: bg } })
3189
+ /* @__PURE__ */ jsx(RPopover2.Arrow, { width: 16, height: 12, style: { fill: bg } })
2994
3190
  ]
2995
3191
  }
2996
3192
  ) })
@@ -3024,8 +3220,8 @@ function useFormField(field) {
3024
3220
  }, [ctx, name]);
3025
3221
  }
3026
3222
  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 }
3223
+ sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
3224
+ md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
3029
3225
  };
3030
3226
  var ALIGN = {
3031
3227
  start: "flex-start",
@@ -3040,6 +3236,7 @@ var SField = forwardRef(function SField2({
3040
3236
  labelTooltip = "",
3041
3237
  labelTooltipProps,
3042
3238
  labelWidth,
3239
+ labelHeight,
3043
3240
  size = "sm",
3044
3241
  addonLabel = "",
3045
3242
  addonAlign = "start",
@@ -3054,6 +3251,7 @@ var SField = forwardRef(function SField2({
3054
3251
  readOnly = false,
3055
3252
  width,
3056
3253
  multiline = false,
3254
+ borderless = false,
3057
3255
  children,
3058
3256
  className,
3059
3257
  style,
@@ -3087,8 +3285,8 @@ var SField = forwardRef(function SField2({
3087
3285
  const resolvedSuccess = status === "pass";
3088
3286
  const showError = resolvedError && errorMessage !== "";
3089
3287
  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)";
3288
+ 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)";
3289
+ const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
3092
3290
  const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
3093
3291
  return /* @__PURE__ */ jsx(
3094
3292
  "div",
@@ -3105,7 +3303,7 @@ var SField = forwardRef(function SField2({
3105
3303
  {
3106
3304
  className: "flex shrink-0 items-center font-bold text-(--sys-color-field-text-default)",
3107
3305
  style: {
3108
- height: s.height,
3306
+ height: cssLen(labelHeight) ?? s.height,
3109
3307
  marginRight: s.fieldGap,
3110
3308
  width: cssLen(labelWidth),
3111
3309
  fontSize: s.fontSize,
@@ -3131,14 +3329,14 @@ var SField = forwardRef(function SField2({
3131
3329
  {
3132
3330
  className: cn(
3133
3331
  "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]",
3332
+ 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
3333
  disabled && "cursor-not-allowed"
3136
3334
  ),
3137
3335
  style: {
3138
3336
  // @ts-expect-error CSS 변수
3139
3337
  "--field-bc": borderColor,
3140
3338
  // focused/hovered prop이 명시된 경우 강조를 즉시 반영
3141
- ...interactive && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
3339
+ ...interactive && !borderless && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
3142
3340
  ...multiline ? { minHeight: s.height } : { height: s.height },
3143
3341
  "width": cssLen(width),
3144
3342
  "borderRadius": s.radius,
@@ -3177,200 +3375,12 @@ var SField = forwardRef(function SField2({
3177
3375
  }
3178
3376
  );
3179
3377
  });
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
3378
  var SChip = forwardRef(function SChip2({
3369
3379
  name,
3370
3380
  value = "",
3371
3381
  error: errorProp = false,
3372
3382
  disabled = false,
3373
- editable = true,
3383
+ editable = false,
3374
3384
  showRemove = true,
3375
3385
  placeholder = "",
3376
3386
  rules,
@@ -3523,6 +3533,7 @@ function SFilePicker({
3523
3533
  hovered = false,
3524
3534
  inline = false,
3525
3535
  multiple = false,
3536
+ useMultipleListBox = false,
3526
3537
  accept,
3527
3538
  maxFileSize,
3528
3539
  maxTotalSize,
@@ -3548,10 +3559,13 @@ function SFilePicker({
3548
3559
  const autoId = useId();
3549
3560
  const resolvedName = name ?? autoId;
3550
3561
  const inputRef = useRef(null);
3562
+ const contentBoxRef = useRef(null);
3551
3563
  const [ruleError, setRuleError] = useState("");
3564
+ const [listOpen, setListOpen] = useState(false);
3565
+ const portalContainer = usePortalContainer();
3552
3566
  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;
3567
+ const hasFiles = multiple ? files.length > 0 : !!files;
3568
+ const displayText = !hasFiles ? placeholder : multiple ? files.map((f) => f.name).join(", ") : files.name;
3555
3569
  useFormField({
3556
3570
  name,
3557
3571
  validate: () => {
@@ -3602,10 +3616,12 @@ function SFilePicker({
3602
3616
  e.stopPropagation();
3603
3617
  if (disabled) return;
3604
3618
  onValueChange?.(multiple ? [] : null);
3619
+ setListOpen(false);
3605
3620
  };
3606
3621
  const removeChip = (i) => {
3607
3622
  const next = files.filter((_, idx) => idx !== i);
3608
3623
  onValueChange?.(next);
3624
+ if (next.length === 0) setListOpen(false);
3609
3625
  };
3610
3626
  const open = () => {
3611
3627
  if (!disabled) inputRef.current?.click();
@@ -3615,78 +3631,100 @@ function SFilePicker({
3615
3631
  const padX = size === "md" ? "var(--cmp-filepicker-md-paddingX)" : "var(--cmp-filepicker-sm-paddingX)";
3616
3632
  const iconSize = size === "md" ? 20 : 16;
3617
3633
  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: [
3634
+ const inlinePlaceholder = inline && !disabled && !hasFiles;
3635
+ const showListbox = multiple && useMultipleListBox;
3636
+ const filesArr = multiple ? files : [];
3637
+ const contentInner = /* @__PURE__ */ jsxs(
3638
+ "div",
3639
+ {
3640
+ ref: contentBoxRef,
3641
+ className: cn("group/filepicker flex h-full w-full items-center", gapCls),
3642
+ style: { padding: inline ? 0 : `0 ${padX}` },
3643
+ children: [
3644
+ /* @__PURE__ */ jsx(
3645
+ "input",
3646
+ {
3647
+ ref: inputRef,
3648
+ type: "file",
3649
+ className: "hidden",
3650
+ disabled,
3651
+ multiple,
3652
+ accept,
3653
+ name: resolvedName,
3654
+ onChange,
3655
+ "aria-label": placeholder
3656
+ }
3657
+ ),
3658
+ /* @__PURE__ */ jsxs(
3659
+ "button",
3660
+ {
3661
+ type: "button",
3662
+ disabled,
3663
+ onClick: open,
3664
+ "aria-label": placeholder,
3665
+ className: cn(
3666
+ "flex min-w-0 flex-1 items-center bg-transparent text-left",
3667
+ gapCls,
3668
+ disabled ? "cursor-not-allowed" : "cursor-pointer"
3669
+ ),
3670
+ children: [
3671
+ /* @__PURE__ */ jsx(
3672
+ SIcon,
3673
+ {
3674
+ name: "attachFile",
3675
+ size: iconSize,
3676
+ color: disabled ? "var(--cmp-filepicker-icon-disabled)" : "var(--color-grey-65)"
3677
+ }
3678
+ ),
3679
+ /* @__PURE__ */ jsx(
3680
+ "span",
3681
+ {
3682
+ className: cn(
3683
+ "min-w-0 flex-1 truncate text-[12px]",
3684
+ inlinePlaceholder && "text-[color:var(--cmp-filepicker-text-placeholder)] transition-colors group-hover/filepicker:text-[color:var(--cmp-filepicker-text-default)]"
3685
+ ),
3686
+ style: inlinePlaceholder ? void 0 : {
3687
+ color: disabled ? "var(--cmp-filepicker-text-disabled)" : hasFiles ? "var(--cmp-filepicker-text-default)" : "var(--cmp-filepicker-text-placeholder)"
3688
+ },
3689
+ children: displayText
3690
+ }
3691
+ )
3692
+ ]
3693
+ }
3694
+ ),
3695
+ showListbox && hasFiles && !disabled && /* @__PURE__ */ jsx(
3696
+ Ariakit.PopoverDisclosure,
3697
+ {
3698
+ render: /* @__PURE__ */ jsx(SGhostButton, { icon: "caretDown", size: "xxs", ariaLabel: "toggle file list" }),
3699
+ className: cn("shrink-0 transition-transform duration-150", listOpen && "rotate-180")
3700
+ }
3701
+ ),
3702
+ !disabled && hasFiles && /* @__PURE__ */ jsx(SGhostButton, { icon: "close", size: "xxs", ariaLabel: "clear", onClick: clear })
3703
+ ]
3704
+ }
3705
+ );
3706
+ const content = showListbox ? /* @__PURE__ */ jsxs(Ariakit.PopoverProvider, { open: listOpen, setOpen: setListOpen, placement: "bottom-start", children: [
3707
+ contentInner,
3619
3708
  /* @__PURE__ */ jsx(
3620
- "input",
3709
+ Ariakit.Popover,
3621
3710
  {
3622
- ref: inputRef,
3623
- type: "file",
3624
- className: "hidden",
3625
- disabled,
3626
- multiple,
3627
- accept,
3628
- name: resolvedName,
3629
- onChange,
3630
- "aria-label": placeholder
3711
+ portal: true,
3712
+ portalElement: portalContainer,
3713
+ gutter: 4,
3714
+ unmountOnHide: true,
3715
+ getAnchorRect: () => contentBoxRef.current?.getBoundingClientRect() ?? null,
3716
+ style: { minWidth: contentBoxRef.current?.offsetWidth },
3717
+ 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)]",
3718
+ children: filesArr.map((f, i) => /* @__PURE__ */ jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i))
3631
3719
  }
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"
3644
- ),
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
- }
3682
- ),
3683
- chips
3684
- ] });
3685
- }
3720
+ )
3721
+ ] }) : contentInner;
3722
+ const chips = multiple && !useMultipleListBox && filesArr.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-[8px] flex flex-wrap gap-[8px]", children: filesArr.map((f, i) => /* @__PURE__ */ jsx(SChip, { value: f.name, editable: false, onRemove: () => removeChip(i) }, i)) });
3686
3723
  return /* @__PURE__ */ jsxs("div", { className, style, children: [
3687
3724
  /* @__PURE__ */ jsx(
3688
3725
  SField,
3689
3726
  {
3727
+ borderless: inline,
3690
3728
  label,
3691
3729
  labelWidth,
3692
3730
  icon,