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.
- package/dist/components/SActionModal/SActionModal.cjs +1 -1
- package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
- package/dist/components/SActionModal/SActionModal.js +1 -1
- package/dist/components/SActionModal/SActionModal.js.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs +217 -208
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.js +217 -208
- package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
- package/dist/components/SCalendar/SCalendar.cjs +1 -1
- package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
- package/dist/components/SCalendar/SCalendar.js +1 -1
- package/dist/components/SCalendar/SCalendar.js.map +1 -1
- package/dist/components/SChip/SChip.cjs +1 -1
- package/dist/components/SChip/SChip.cjs.map +1 -1
- package/dist/components/SChip/SChip.d.ts +1 -1
- package/dist/components/SChip/SChip.js +1 -1
- package/dist/components/SChip/SChip.js.map +1 -1
- package/dist/components/SChipInput/SChipInput.cjs +234 -220
- package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
- package/dist/components/SChipInput/SChipInput.js +234 -220
- package/dist/components/SChipInput/SChipInput.js.map +1 -1
- package/dist/components/SCircleProgress/SCircleProgress.cjs +31 -14
- package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
- package/dist/components/SCircleProgress/SCircleProgress.d.ts +2 -0
- package/dist/components/SCircleProgress/SCircleProgress.js +31 -14
- package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.cjs +222 -213
- package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.js +222 -213
- package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs +297 -262
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.js +297 -262
- package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
- package/dist/components/SDropdownButton/SDropdownButton.cjs +4 -4
- package/dist/components/SDropdownButton/SDropdownButton.cjs.map +1 -1
- package/dist/components/SDropdownButton/SDropdownButton.js +4 -4
- package/dist/components/SDropdownButton/SDropdownButton.js.map +1 -1
- package/dist/components/SField/SField.cjs +217 -20
- package/dist/components/SField/SField.cjs.map +1 -1
- package/dist/components/SField/SField.d.ts +11 -0
- package/dist/components/SField/SField.js +217 -20
- package/dist/components/SField/SField.js.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.cjs +315 -276
- package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.d.ts +4 -2
- package/dist/components/SFilePicker/SFilePicker.js +314 -276
- package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
- package/dist/components/SInput/SInput.cjs +217 -208
- package/dist/components/SInput/SInput.cjs.map +1 -1
- package/dist/components/SInput/SInput.js +217 -208
- package/dist/components/SInput/SInput.js.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs +631 -450
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.d.ts +3 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.js +631 -450
- package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
- package/dist/components/SLoadingContainer/SLoadingContainer.cjs +39 -14
- package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
- package/dist/components/SLoadingContainer/SLoadingContainer.js +39 -14
- package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
- package/dist/components/SLoadingContainer/loading.cjs +39 -14
- package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
- package/dist/components/SLoadingContainer/loading.js +39 -14
- package/dist/components/SLoadingContainer/loading.js.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.cjs +37 -16
- package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.d.ts +3 -1
- package/dist/components/SLoadingModal/SLoadingModal.js +37 -16
- package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.cjs +325 -49
- package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
- package/dist/components/SNumberInput/SNumberInput.js +325 -49
- package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
- package/dist/components/SPagination/SPagination.cjs +1 -1
- package/dist/components/SPagination/SPagination.cjs.map +1 -1
- package/dist/components/SPagination/SPagination.js +1 -1
- package/dist/components/SPagination/SPagination.js.map +1 -1
- package/dist/components/SRadio/SRadioGroup.cjs +1 -1
- package/dist/components/SRadio/SRadioGroup.cjs.map +1 -1
- package/dist/components/SRadio/SRadioGroup.js +1 -1
- package/dist/components/SRadio/SRadioGroup.js.map +1 -1
- package/dist/components/SSelect/SSelect.cjs +71 -32
- package/dist/components/SSelect/SSelect.cjs.map +1 -1
- package/dist/components/SSelect/SSelect.js +71 -32
- package/dist/components/SSelect/SSelect.js.map +1 -1
- package/dist/components/STable/STable.cjs +108 -49
- package/dist/components/STable/STable.cjs.map +1 -1
- package/dist/components/STable/STable.js +108 -49
- package/dist/components/STable/STable.js.map +1 -1
- package/dist/components/STextarea/STextarea.cjs +217 -20
- package/dist/components/STextarea/STextarea.cjs.map +1 -1
- package/dist/components/STextarea/STextarea.js +217 -20
- package/dist/components/STextarea/STextarea.js.map +1 -1
- package/dist/components/SToast/SToastContainer.cjs +3 -3
- package/dist/components/SToast/SToastContainer.cjs.map +1 -1
- package/dist/components/SToast/SToastContainer.js +3 -3
- package/dist/components/SToast/SToastContainer.js.map +1 -1
- package/dist/components/STooltip/STooltip.cjs +208 -13
- package/dist/components/STooltip/STooltip.cjs.map +1 -1
- package/dist/components/STooltip/STooltip.js +208 -13
- package/dist/components/STooltip/STooltip.js.map +1 -1
- package/dist/index.cjs +439 -221
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +439 -221
- package/dist/index.js.map +1 -1
- package/dist/styles.css +113 -48
- package/dist/theme.css +49 -23
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ var react = require('react');
|
|
|
5
5
|
var clsx = require('clsx');
|
|
6
6
|
var tailwindMerge = require('tailwind-merge');
|
|
7
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
-
var
|
|
8
|
+
var RPopover2 = require('@radix-ui/react-popover');
|
|
9
9
|
|
|
10
10
|
function _interopNamespace(e) {
|
|
11
11
|
if (e && e.__esModule) return e;
|
|
@@ -25,7 +25,7 @@ function _interopNamespace(e) {
|
|
|
25
25
|
return Object.freeze(n);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
var
|
|
28
|
+
var RPopover2__namespace = /*#__PURE__*/_interopNamespace(RPopover2);
|
|
29
29
|
|
|
30
30
|
var twMerge = tailwindMerge.extendTailwindMerge({
|
|
31
31
|
extend: {
|
|
@@ -2897,11 +2897,200 @@ PortalContainerContext.Provider;
|
|
|
2897
2897
|
function usePortalContainer() {
|
|
2898
2898
|
return react.useContext(PortalContainerContext) ?? void 0;
|
|
2899
2899
|
}
|
|
2900
|
+
|
|
2901
|
+
// src/components/STag/tag.config.ts
|
|
2902
|
+
var TAG_SIZE_CONFIG = {
|
|
2903
|
+
xs: {
|
|
2904
|
+
height: 20,
|
|
2905
|
+
paddingX: 8,
|
|
2906
|
+
gap: 4,
|
|
2907
|
+
radius: 6,
|
|
2908
|
+
iconSize: 12,
|
|
2909
|
+
fontSize: 11,
|
|
2910
|
+
fontWeight: 500,
|
|
2911
|
+
lineHeight: 18
|
|
2912
|
+
},
|
|
2913
|
+
sm: {
|
|
2914
|
+
height: 24,
|
|
2915
|
+
paddingX: 8,
|
|
2916
|
+
gap: 4,
|
|
2917
|
+
radius: 6,
|
|
2918
|
+
iconSize: 16,
|
|
2919
|
+
fontSize: 12,
|
|
2920
|
+
fontWeight: 700,
|
|
2921
|
+
lineHeight: 20
|
|
2922
|
+
},
|
|
2923
|
+
md: {
|
|
2924
|
+
height: 28,
|
|
2925
|
+
paddingX: 12,
|
|
2926
|
+
gap: 6,
|
|
2927
|
+
radius: 8,
|
|
2928
|
+
iconSize: 16,
|
|
2929
|
+
fontSize: 14,
|
|
2930
|
+
fontWeight: 700,
|
|
2931
|
+
lineHeight: 24
|
|
2932
|
+
}
|
|
2933
|
+
};
|
|
2934
|
+
var STag = react.forwardRef(function STag2({
|
|
2935
|
+
shape = "square",
|
|
2936
|
+
size = "sm",
|
|
2937
|
+
color = "grey",
|
|
2938
|
+
label = "",
|
|
2939
|
+
icon,
|
|
2940
|
+
iconLeft = true,
|
|
2941
|
+
width,
|
|
2942
|
+
className,
|
|
2943
|
+
style,
|
|
2944
|
+
...rest
|
|
2945
|
+
}, ref) {
|
|
2946
|
+
const s = TAG_SIZE_CONFIG[size];
|
|
2947
|
+
const contentColor = `var(--cmp-tag-${color}-content)`;
|
|
2948
|
+
const iconNode = icon ? /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.iconSize, color: contentColor }) : null;
|
|
2949
|
+
const resolvedWidth = typeof width === "number" ? `${width}px` : width;
|
|
2950
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2951
|
+
"span",
|
|
2952
|
+
{
|
|
2953
|
+
ref,
|
|
2954
|
+
"aria-label": label || "tag",
|
|
2955
|
+
className: cn(
|
|
2956
|
+
"box-border inline-flex w-fit select-none items-center justify-center whitespace-nowrap",
|
|
2957
|
+
className
|
|
2958
|
+
),
|
|
2959
|
+
style: {
|
|
2960
|
+
minHeight: s.height,
|
|
2961
|
+
padding: `0 ${s.paddingX}px`,
|
|
2962
|
+
gap: s.gap,
|
|
2963
|
+
borderRadius: shape === "pill" ? 999 : s.radius,
|
|
2964
|
+
fontSize: s.fontSize,
|
|
2965
|
+
fontWeight: s.fontWeight,
|
|
2966
|
+
lineHeight: `${s.lineHeight}px`,
|
|
2967
|
+
background: `var(--cmp-tag-${color}-bg)`,
|
|
2968
|
+
color: contentColor,
|
|
2969
|
+
...resolvedWidth != null ? { width: resolvedWidth } : {},
|
|
2970
|
+
...style
|
|
2971
|
+
},
|
|
2972
|
+
...rest,
|
|
2973
|
+
children: [
|
|
2974
|
+
icon && iconLeft && iconNode,
|
|
2975
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex min-w-0 items-center justify-center", children: label }),
|
|
2976
|
+
icon && !iconLeft && iconNode
|
|
2977
|
+
]
|
|
2978
|
+
}
|
|
2979
|
+
);
|
|
2980
|
+
});
|
|
2981
|
+
var SIZE = {
|
|
2982
|
+
xxs: { icon: 12, radius: "sm" },
|
|
2983
|
+
xs: { icon: 16, radius: "sm" },
|
|
2984
|
+
sm: { icon: 20, radius: "sm" },
|
|
2985
|
+
md: { icon: 24, radius: "md" },
|
|
2986
|
+
lg: { icon: 32, radius: "md" }
|
|
2987
|
+
};
|
|
2988
|
+
var TOOLTIP_HIDE_DELAY_MS = 100;
|
|
2989
|
+
function SGhostButton({
|
|
2990
|
+
icon,
|
|
2991
|
+
size = "sm",
|
|
2992
|
+
intent = "default",
|
|
2993
|
+
ariaLabel = "",
|
|
2994
|
+
tooltipText,
|
|
2995
|
+
disabled = false,
|
|
2996
|
+
onClick,
|
|
2997
|
+
className,
|
|
2998
|
+
style
|
|
2999
|
+
}) {
|
|
3000
|
+
const portalContainer = usePortalContainer();
|
|
3001
|
+
const [open, setOpen] = react.useState(false);
|
|
3002
|
+
const hideTimer = react.useRef(void 0);
|
|
3003
|
+
const cancelHide = () => {
|
|
3004
|
+
if (hideTimer.current) {
|
|
3005
|
+
clearTimeout(hideTimer.current);
|
|
3006
|
+
hideTimer.current = void 0;
|
|
3007
|
+
}
|
|
3008
|
+
};
|
|
3009
|
+
const openNow = () => {
|
|
3010
|
+
cancelHide();
|
|
3011
|
+
setOpen(true);
|
|
3012
|
+
};
|
|
3013
|
+
const startHide = () => {
|
|
3014
|
+
cancelHide();
|
|
3015
|
+
hideTimer.current = setTimeout(() => setOpen(false), TOOLTIP_HIDE_DELAY_MS);
|
|
3016
|
+
};
|
|
3017
|
+
const s = SIZE[size];
|
|
3018
|
+
const contentColor = disabled ? intent === "inverse" ? "var(--cmp-ghostButton-inverse-disabled)" : "var(--cmp-ghostButton-icon-disabled)" : `var(--cmp-ghostButton-${intent}-content)`;
|
|
3019
|
+
const btn = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3020
|
+
"button",
|
|
3021
|
+
{
|
|
3022
|
+
type: "button",
|
|
3023
|
+
disabled,
|
|
3024
|
+
"aria-disabled": disabled ? "true" : void 0,
|
|
3025
|
+
"aria-label": ariaLabel.trim() || void 0,
|
|
3026
|
+
tabIndex: disabled ? -1 : void 0,
|
|
3027
|
+
onClick: (e) => {
|
|
3028
|
+
if (disabled) {
|
|
3029
|
+
e.preventDefault();
|
|
3030
|
+
return;
|
|
3031
|
+
}
|
|
3032
|
+
onClick?.(e);
|
|
3033
|
+
},
|
|
3034
|
+
className: cn(
|
|
3035
|
+
"group relative box-border inline-flex items-center justify-center border-none bg-transparent p-0 transition-shadow",
|
|
3036
|
+
disabled ? "cursor-not-allowed" : "cursor-pointer",
|
|
3037
|
+
"focus-visible:outline-none focus-visible:shadow-[0_0_0_2px_var(--cmp-ghostButton-action-hover-bg)]",
|
|
3038
|
+
className
|
|
3039
|
+
),
|
|
3040
|
+
style: {
|
|
3041
|
+
width: `var(--cmp-ghostButton-${size}-size)`,
|
|
3042
|
+
height: `var(--cmp-ghostButton-${size}-size)`,
|
|
3043
|
+
borderRadius: `var(--cmp-ghostButton-radius-${s.radius})`,
|
|
3044
|
+
...style
|
|
3045
|
+
},
|
|
3046
|
+
children: [
|
|
3047
|
+
!disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3048
|
+
"span",
|
|
3049
|
+
{
|
|
3050
|
+
"aria-hidden": true,
|
|
3051
|
+
className: "pointer-events-none absolute inset-0 rounded-[inherit] opacity-0 transition-opacity duration-200 group-hover:opacity-5",
|
|
3052
|
+
style: { background: `var(--cmp-ghostButton-${intent}-hover-bg)` }
|
|
3053
|
+
}
|
|
3054
|
+
),
|
|
3055
|
+
/* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: icon, size: s.icon, color: contentColor })
|
|
3056
|
+
]
|
|
3057
|
+
}
|
|
3058
|
+
);
|
|
3059
|
+
const text = (tooltipText ?? "").trim();
|
|
3060
|
+
if (text === "") return btn;
|
|
3061
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
|
|
3062
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3063
|
+
"span",
|
|
3064
|
+
{
|
|
3065
|
+
className: "inline-flex",
|
|
3066
|
+
onMouseEnter: openNow,
|
|
3067
|
+
onMouseLeave: startHide,
|
|
3068
|
+
onFocus: openNow,
|
|
3069
|
+
onBlur: startHide,
|
|
3070
|
+
children: btn
|
|
3071
|
+
}
|
|
3072
|
+
) }),
|
|
3073
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3074
|
+
RPopover2__namespace.Content,
|
|
3075
|
+
{
|
|
3076
|
+
side: "top",
|
|
3077
|
+
sideOffset: 4,
|
|
3078
|
+
className: cn("z-50", FLOATING_SLIDE_ANIM),
|
|
3079
|
+
"aria-hidden": true,
|
|
3080
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
3081
|
+
onMouseEnter: cancelHide,
|
|
3082
|
+
onMouseLeave: startHide,
|
|
3083
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(STag, { shape: "square", size: "xs", color: "grey", label: text })
|
|
3084
|
+
}
|
|
3085
|
+
) })
|
|
3086
|
+
] });
|
|
3087
|
+
}
|
|
2900
3088
|
var HIDE_DELAY_MS = 100;
|
|
2901
3089
|
function useBody(content, message = []) {
|
|
2902
3090
|
if (content != null) return content;
|
|
2903
3091
|
if (message.length === 0) return null;
|
|
2904
|
-
|
|
3092
|
+
const singleLine = message.length === 1;
|
|
3093
|
+
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
3094
|
}
|
|
2906
3095
|
var STooltip = react.forwardRef(function STooltip2({
|
|
2907
3096
|
content,
|
|
@@ -2969,10 +3158,10 @@ var STooltip = react.forwardRef(function STooltip2({
|
|
|
2969
3158
|
fontWeight: tooltipType === "default" ? 500 : 700,
|
|
2970
3159
|
...style
|
|
2971
3160
|
};
|
|
2972
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3161
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: setOpen, children: [
|
|
2973
3162
|
isHover ? (
|
|
2974
3163
|
// hover: Anchor(위치 기준) + 직접 mouse 핸들러 — 클릭으로 토글되지 않도록 Trigger 미사용
|
|
2975
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3164
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Anchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2976
3165
|
"span",
|
|
2977
3166
|
{
|
|
2978
3167
|
className: "inline-flex",
|
|
@@ -2983,9 +3172,9 @@ var STooltip = react.forwardRef(function STooltip2({
|
|
|
2983
3172
|
children: triggerNode
|
|
2984
3173
|
}
|
|
2985
3174
|
) })
|
|
2986
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2987
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2988
|
-
|
|
3175
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: triggerNode }),
|
|
3176
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3177
|
+
RPopover2__namespace.Content,
|
|
2989
3178
|
{
|
|
2990
3179
|
side: placement,
|
|
2991
3180
|
sideOffset: 8,
|
|
@@ -3004,15 +3193,21 @@ var STooltip = react.forwardRef(function STooltip2({
|
|
|
3004
3193
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-[12px]", children: [
|
|
3005
3194
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: body }),
|
|
3006
3195
|
useClose && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3007
|
-
|
|
3196
|
+
SGhostButton,
|
|
3008
3197
|
{
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3198
|
+
icon: "close",
|
|
3199
|
+
size: "xs",
|
|
3200
|
+
intent: tooltipType === "default" ? "inverse" : "default",
|
|
3201
|
+
ariaLabel: "\uB2EB\uAE30",
|
|
3202
|
+
className: "shrink-0",
|
|
3203
|
+
onClick: () => {
|
|
3204
|
+
cancelHide();
|
|
3205
|
+
setOpen(false);
|
|
3206
|
+
}
|
|
3012
3207
|
}
|
|
3013
3208
|
)
|
|
3014
3209
|
] }),
|
|
3015
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3210
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Arrow, { width: 16, height: 12, style: { fill: bg } })
|
|
3016
3211
|
]
|
|
3017
3212
|
}
|
|
3018
3213
|
) })
|
|
@@ -3046,8 +3241,8 @@ function useFormField(field) {
|
|
|
3046
3241
|
}, [ctx, name]);
|
|
3047
3242
|
}
|
|
3048
3243
|
var FIELD_SIZE = {
|
|
3049
|
-
sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon:
|
|
3050
|
-
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon:
|
|
3244
|
+
sm: { height: 28, radius: 4, fieldGap: 12, labelGap: 6, fontSize: 12, lineHeight: 20, icon: 16 },
|
|
3245
|
+
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon: 16 }
|
|
3051
3246
|
};
|
|
3052
3247
|
var ALIGN = {
|
|
3053
3248
|
start: "flex-start",
|
|
@@ -3062,6 +3257,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3062
3257
|
labelTooltip = "",
|
|
3063
3258
|
labelTooltipProps,
|
|
3064
3259
|
labelWidth,
|
|
3260
|
+
labelHeight,
|
|
3065
3261
|
size = "sm",
|
|
3066
3262
|
addonLabel = "",
|
|
3067
3263
|
addonAlign = "start",
|
|
@@ -3076,6 +3272,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3076
3272
|
readOnly = false,
|
|
3077
3273
|
width,
|
|
3078
3274
|
multiline = false,
|
|
3275
|
+
borderless = false,
|
|
3079
3276
|
children,
|
|
3080
3277
|
className,
|
|
3081
3278
|
style,
|
|
@@ -3109,8 +3306,8 @@ var SField = react.forwardRef(function SField2({
|
|
|
3109
3306
|
const resolvedSuccess = status === "pass";
|
|
3110
3307
|
const showError = resolvedError && errorMessage !== "";
|
|
3111
3308
|
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)";
|
|
3309
|
+
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)";
|
|
3310
|
+
const bgColor = borderless ? "transparent" : disabled ? "var(--color-grey-30)" : readOnly ? "var(--sys-color-field-bg-readOnly)" : "var(--sys-color-field-bg-default)";
|
|
3114
3311
|
const textColor = disabled ? "var(--color-grey-65)" : readOnly ? "var(--sys-color-field-text-readOnly)" : "var(--sys-color-field-text-default)";
|
|
3115
3312
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3116
3313
|
"div",
|
|
@@ -3127,7 +3324,7 @@ var SField = react.forwardRef(function SField2({
|
|
|
3127
3324
|
{
|
|
3128
3325
|
className: "flex shrink-0 items-center font-bold text-(--sys-color-field-text-default)",
|
|
3129
3326
|
style: {
|
|
3130
|
-
height: s.height,
|
|
3327
|
+
height: cssLen(labelHeight) ?? s.height,
|
|
3131
3328
|
marginRight: s.fieldGap,
|
|
3132
3329
|
width: cssLen(labelWidth),
|
|
3133
3330
|
fontSize: s.fontSize,
|
|
@@ -3153,14 +3350,14 @@ var SField = react.forwardRef(function SField2({
|
|
|
3153
3350
|
{
|
|
3154
3351
|
className: cn(
|
|
3155
3352
|
"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]",
|
|
3353
|
+
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
3354
|
disabled && "cursor-not-allowed"
|
|
3158
3355
|
),
|
|
3159
3356
|
style: {
|
|
3160
3357
|
// @ts-expect-error CSS 변수
|
|
3161
3358
|
"--field-bc": borderColor,
|
|
3162
3359
|
// focused/hovered prop이 명시된 경우 강조를 즉시 반영
|
|
3163
|
-
...interactive && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
|
|
3360
|
+
...interactive && !borderless && focused ? { boxShadow: "0px 0px 4px 0px #0071ff66" } : {},
|
|
3164
3361
|
...multiline ? { minHeight: s.height } : { height: s.height },
|
|
3165
3362
|
"width": cssLen(width),
|
|
3166
3363
|
"borderRadius": s.radius,
|
|
@@ -3200,8 +3397,8 @@ var SField = react.forwardRef(function SField2({
|
|
|
3200
3397
|
);
|
|
3201
3398
|
});
|
|
3202
3399
|
var PAD = {
|
|
3203
|
-
sm: { fontSize: 12, stepper: 20, icon: 12 },
|
|
3204
|
-
md: { fontSize: 14, stepper: 24, icon: 14 }
|
|
3400
|
+
sm: { fontSize: 12, lineHeight: 20, stepper: 20, icon: 12 },
|
|
3401
|
+
md: { fontSize: 14, lineHeight: 24, stepper: 24, icon: 14 }
|
|
3205
3402
|
};
|
|
3206
3403
|
var toNumber = (val) => {
|
|
3207
3404
|
if (val === null || val === void 0 || val === "") return null;
|
|
@@ -3219,6 +3416,38 @@ var formatWithCommas = (value) => {
|
|
|
3219
3416
|
const result = isNeg ? "-" + formatted : formatted;
|
|
3220
3417
|
return decPart !== void 0 && decPart !== "" ? result + "." + decPart : result;
|
|
3221
3418
|
};
|
|
3419
|
+
var addCommas = (value) => value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
3420
|
+
var formatInputWithCommas = (value) => {
|
|
3421
|
+
const isNeg = value.startsWith("-");
|
|
3422
|
+
const unsigned = isNeg ? value.slice(1) : value;
|
|
3423
|
+
const hasDecimal = unsigned.includes(".");
|
|
3424
|
+
const [intPart = "", decPart = ""] = unsigned.split(".");
|
|
3425
|
+
const formattedInt = addCommas(intPart === "" ? "0" : intPart);
|
|
3426
|
+
return `${isNeg ? "-" : ""}${formattedInt}${hasDecimal ? `.${decPart}` : ""}`;
|
|
3427
|
+
};
|
|
3428
|
+
var getDecimalLimit = (useDecimal, maxDecimalLength) => {
|
|
3429
|
+
if (!useDecimal || maxDecimalLength === void 0 || !Number.isFinite(maxDecimalLength)) {
|
|
3430
|
+
return void 0;
|
|
3431
|
+
}
|
|
3432
|
+
return Math.max(0, Math.floor(maxDecimalLength));
|
|
3433
|
+
};
|
|
3434
|
+
var applyDecimalLimit = (value, decimalLimit) => {
|
|
3435
|
+
if (value === null || decimalLimit === void 0) return value;
|
|
3436
|
+
return Number(value.toFixed(decimalLimit));
|
|
3437
|
+
};
|
|
3438
|
+
var getLogicalCaret = (value, caret) => {
|
|
3439
|
+
return value.slice(0, caret ?? value.length).replace(/,/g, "").length;
|
|
3440
|
+
};
|
|
3441
|
+
var getDisplayCaret = (value, logicalCaret) => {
|
|
3442
|
+
if (logicalCaret <= 0) return 0;
|
|
3443
|
+
let seen = 0;
|
|
3444
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
3445
|
+
if (value[i] === ",") continue;
|
|
3446
|
+
seen += 1;
|
|
3447
|
+
if (seen >= logicalCaret) return i + 1;
|
|
3448
|
+
}
|
|
3449
|
+
return value.length;
|
|
3450
|
+
};
|
|
3222
3451
|
var SNumberInput = react.forwardRef(function SNumberInput2({
|
|
3223
3452
|
value,
|
|
3224
3453
|
onValueChange,
|
|
@@ -3228,6 +3457,7 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
|
|
|
3228
3457
|
step = 1,
|
|
3229
3458
|
useButton = false,
|
|
3230
3459
|
useDecimal = false,
|
|
3460
|
+
maxDecimalLength,
|
|
3231
3461
|
inputPrefix = "",
|
|
3232
3462
|
inputSuffix = "",
|
|
3233
3463
|
inputClass,
|
|
@@ -3259,7 +3489,8 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
|
|
|
3259
3489
|
}, ref) {
|
|
3260
3490
|
const p = PAD[size];
|
|
3261
3491
|
const padX = `var(--cmp-numberInput-${size}-paddingX)`;
|
|
3262
|
-
const
|
|
3492
|
+
const decimalLimit = getDecimalLimit(useDecimal, maxDecimalLength);
|
|
3493
|
+
const numValue = applyDecimalLimit(toNumber(value), decimalLimit);
|
|
3263
3494
|
const [display, setDisplay] = react.useState(() => formatWithCommas(numValue));
|
|
3264
3495
|
const [ruleError, setRuleError] = react.useState("");
|
|
3265
3496
|
const innerRef = react.useRef(null);
|
|
@@ -3269,8 +3500,9 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
|
|
|
3269
3500
|
else if (ref) ref.current = el;
|
|
3270
3501
|
};
|
|
3271
3502
|
react.useEffect(() => {
|
|
3272
|
-
|
|
3273
|
-
|
|
3503
|
+
if (document.activeElement === innerRef.current) return;
|
|
3504
|
+
setDisplay(formatWithCommas(applyDecimalLimit(toNumber(value), decimalLimit)));
|
|
3505
|
+
}, [decimalLimit, value]);
|
|
3274
3506
|
useFormField({
|
|
3275
3507
|
name,
|
|
3276
3508
|
validate: () => {
|
|
@@ -3282,48 +3514,87 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
|
|
|
3282
3514
|
});
|
|
3283
3515
|
const resolvedError = error || ruleError !== "";
|
|
3284
3516
|
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
3285
|
-
const
|
|
3286
|
-
|
|
3287
|
-
|
|
3517
|
+
const restoreCaret = (nextDisplay, logicalCaret) => {
|
|
3518
|
+
const input = innerRef.current;
|
|
3519
|
+
if (logicalCaret === void 0 || !input || document.activeElement !== input) return;
|
|
3520
|
+
const nextCaret = getDisplayCaret(nextDisplay, logicalCaret);
|
|
3521
|
+
requestAnimationFrame(() => {
|
|
3522
|
+
if (document.activeElement !== input) return;
|
|
3523
|
+
input.setSelectionRange(nextCaret, nextCaret);
|
|
3524
|
+
});
|
|
3525
|
+
};
|
|
3526
|
+
const setDisplayWithCaret = (nextDisplay, logicalCaret) => {
|
|
3527
|
+
setDisplay(nextDisplay);
|
|
3528
|
+
restoreCaret(nextDisplay, logicalCaret);
|
|
3529
|
+
};
|
|
3530
|
+
const resetRejectedInput = (target, logicalCaret) => {
|
|
3531
|
+
target.value = display;
|
|
3532
|
+
restoreCaret(display, Math.min(logicalCaret, display.replace(/,/g, "").length));
|
|
3533
|
+
};
|
|
3534
|
+
const emit = (n, logicalCaret) => {
|
|
3535
|
+
const next = applyDecimalLimit(n, decimalLimit);
|
|
3536
|
+
const nextDisplay = formatWithCommas(next);
|
|
3537
|
+
setDisplayWithCaret(nextDisplay, logicalCaret);
|
|
3538
|
+
onValueChange?.(next);
|
|
3288
3539
|
};
|
|
3289
3540
|
const incDisabled = disabled || readOnly || numValue !== null && numValue >= max;
|
|
3290
3541
|
const decDisabled = disabled || readOnly || numValue !== null && numValue <= min;
|
|
3291
3542
|
const increment = () => {
|
|
3292
3543
|
if (incDisabled) return;
|
|
3293
3544
|
const cur = numValue ?? (min === Number.NEGATIVE_INFINITY ? 0 : min);
|
|
3294
|
-
const next = Math.min(cur + step, max);
|
|
3545
|
+
const next = applyDecimalLimit(Math.min(cur + step, max), decimalLimit);
|
|
3295
3546
|
if (next !== cur) emit(next);
|
|
3296
3547
|
};
|
|
3297
3548
|
const decrement = () => {
|
|
3298
3549
|
if (decDisabled) return;
|
|
3299
3550
|
const cur = numValue ?? (min === Number.NEGATIVE_INFINITY ? 0 : min);
|
|
3300
|
-
const next = Math.max(cur - step, min);
|
|
3551
|
+
const next = applyDecimalLimit(Math.max(cur - step, min), decimalLimit);
|
|
3301
3552
|
if (next !== cur) emit(next);
|
|
3302
3553
|
};
|
|
3303
|
-
const handleInput = (
|
|
3554
|
+
const handleInput = (event) => {
|
|
3555
|
+
const target = event.currentTarget;
|
|
3556
|
+
const raw = target.value;
|
|
3557
|
+
const logicalCaret = getLogicalCaret(raw, target.selectionStart);
|
|
3304
3558
|
if (raw === "") {
|
|
3305
|
-
|
|
3559
|
+
setDisplayWithCaret("", 0);
|
|
3306
3560
|
onValueChange?.(null);
|
|
3307
3561
|
return;
|
|
3308
3562
|
}
|
|
3309
3563
|
const cleaned = raw.replace(/,/g, "");
|
|
3310
3564
|
const decimals = (cleaned.match(/\./g) || []).length;
|
|
3311
|
-
if (decimals > 1)
|
|
3565
|
+
if (decimals > 1) {
|
|
3566
|
+
resetRejectedInput(target, logicalCaret);
|
|
3567
|
+
return;
|
|
3568
|
+
}
|
|
3312
3569
|
if (cleaned === "-" || cleaned === "." || cleaned === "-.") {
|
|
3313
|
-
|
|
3570
|
+
setDisplayWithCaret(cleaned, logicalCaret);
|
|
3314
3571
|
return;
|
|
3315
3572
|
}
|
|
3316
|
-
if (!useDecimal && cleaned.includes("."))
|
|
3573
|
+
if (!useDecimal && cleaned.includes(".")) {
|
|
3574
|
+
resetRejectedInput(target, logicalCaret);
|
|
3575
|
+
return;
|
|
3576
|
+
}
|
|
3577
|
+
if (decimalLimit !== void 0 && cleaned.includes(".")) {
|
|
3578
|
+
const fraction = cleaned.split(".")[1] ?? "";
|
|
3579
|
+
if (fraction.length > decimalLimit) {
|
|
3580
|
+
resetRejectedInput(target, logicalCaret);
|
|
3581
|
+
return;
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3317
3584
|
if (cleaned.endsWith(".") && decimals === 1) {
|
|
3318
3585
|
const parsed2 = toNumber(cleaned.slice(0, -1));
|
|
3319
3586
|
if (parsed2 !== null) {
|
|
3320
|
-
|
|
3587
|
+
setDisplayWithCaret(formatInputWithCommas(cleaned), logicalCaret);
|
|
3321
3588
|
return;
|
|
3322
3589
|
}
|
|
3323
3590
|
}
|
|
3324
3591
|
const parsed = toNumber(cleaned);
|
|
3325
|
-
if (parsed === null)
|
|
3326
|
-
|
|
3592
|
+
if (parsed === null) {
|
|
3593
|
+
resetRejectedInput(target, logicalCaret);
|
|
3594
|
+
return;
|
|
3595
|
+
}
|
|
3596
|
+
setDisplayWithCaret(formatInputWithCommas(cleaned), logicalCaret);
|
|
3597
|
+
onValueChange?.(applyDecimalLimit(parsed, decimalLimit));
|
|
3327
3598
|
};
|
|
3328
3599
|
const stepper = (dir) => {
|
|
3329
3600
|
const off = dir === "inc" ? incDisabled : decDisabled;
|
|
@@ -3336,8 +3607,8 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
|
|
|
3336
3607
|
"aria-label": dir === "inc" ? "\uC99D\uAC00" : "\uAC10\uC18C",
|
|
3337
3608
|
onClick: dir === "inc" ? increment : decrement,
|
|
3338
3609
|
className: cn(
|
|
3339
|
-
"flex flex-shrink-0 items-center justify-center",
|
|
3340
|
-
off ? "cursor-not-allowed" : "cursor-pointer"
|
|
3610
|
+
"flex flex-shrink-0 items-center justify-center border border-solid border-transparent transition-colors",
|
|
3611
|
+
off ? "cursor-not-allowed" : "cursor-pointer hover:border-[var(--color-border-accentLight)]"
|
|
3341
3612
|
),
|
|
3342
3613
|
style: {
|
|
3343
3614
|
width: `var(--cmp-numberInput-stepper-${size}-size)`,
|
|
@@ -3386,24 +3657,26 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
|
|
|
3386
3657
|
style: {
|
|
3387
3658
|
// 원본 sd-number-input: content gap = padding-x 토큰 (gap 토큰 아님)
|
|
3388
3659
|
gap: `var(--cmp-numberInput-${size}-paddingX)`,
|
|
3389
|
-
padding: `0 ${useButton ? padX : `calc(${padX} * 2)`}
|
|
3660
|
+
padding: `0 ${useButton ? padX : `calc(${padX} * 2)`}`,
|
|
3661
|
+
fontSize: p.fontSize,
|
|
3662
|
+
lineHeight: `${p.lineHeight}px`
|
|
3390
3663
|
},
|
|
3391
3664
|
children: [
|
|
3392
3665
|
useButton && stepper("dec"),
|
|
3393
|
-
inputPrefix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: {
|
|
3666
|
+
inputPrefix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: { color: affixColor }, children: inputPrefix }),
|
|
3394
3667
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3395
3668
|
"input",
|
|
3396
3669
|
{
|
|
3397
3670
|
ref: setRefs,
|
|
3398
3671
|
name,
|
|
3399
3672
|
type: "text",
|
|
3400
|
-
inputMode: "numeric",
|
|
3673
|
+
inputMode: useDecimal ? "decimal" : "numeric",
|
|
3401
3674
|
value: display,
|
|
3402
3675
|
placeholder,
|
|
3403
3676
|
disabled,
|
|
3404
3677
|
readOnly,
|
|
3405
3678
|
autoFocus,
|
|
3406
|
-
onChange:
|
|
3679
|
+
onChange: handleInput,
|
|
3407
3680
|
onKeyDown: (e) => {
|
|
3408
3681
|
if (e.key === "ArrowUp") {
|
|
3409
3682
|
e.preventDefault();
|
|
@@ -3415,26 +3688,29 @@ var SNumberInput = react.forwardRef(function SNumberInput2({
|
|
|
3415
3688
|
},
|
|
3416
3689
|
onFocus: (e) => onFocus?.(e),
|
|
3417
3690
|
onBlur: (e) => {
|
|
3418
|
-
const
|
|
3691
|
+
const cleaned = display.replace(/,/g, "");
|
|
3692
|
+
const n = toNumber(cleaned);
|
|
3419
3693
|
const clamped = n === null ? null : Math.min(Math.max(n, min), max);
|
|
3420
3694
|
if (clamped !== n) emit(clamped);
|
|
3421
|
-
else setDisplay(
|
|
3695
|
+
else setDisplay(n === null ? "" : formatInputWithCommas(cleaned));
|
|
3422
3696
|
if (rules && rules.length > 0) setRuleError(runRules(clamped, rules));
|
|
3423
3697
|
onBlur?.(e);
|
|
3424
3698
|
},
|
|
3425
3699
|
className: cn(
|
|
3426
|
-
|
|
3700
|
+
// h-full 대신 라인박스 높이 그대로 두어 prefix/suffix 와 동일하게 flex 로 수직 센터링
|
|
3701
|
+
// (h-full 이면 input 내부 텍스트 센터링을 브라우저가 수행해 1px 어긋날 수 있음)
|
|
3702
|
+
"min-w-0 flex-1 border-0 bg-transparent p-0 outline-none placeholder:text-[color:var(--cmp-numberInput-text-placeholder)] disabled:cursor-not-allowed",
|
|
3427
3703
|
inputClass
|
|
3428
3704
|
),
|
|
3429
3705
|
style: {
|
|
3430
|
-
fontSize
|
|
3706
|
+
// fontSize/lineHeight 는 컨테이너에서 상속
|
|
3431
3707
|
textAlign: useButton ? "center" : "right",
|
|
3432
3708
|
color: disabled ? "var(--cmp-numberInput-text-disabled)" : "var(--cmp-numberInput-text-default)",
|
|
3433
3709
|
...inputStyle
|
|
3434
3710
|
}
|
|
3435
3711
|
}
|
|
3436
3712
|
),
|
|
3437
|
-
inputSuffix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: {
|
|
3713
|
+
inputSuffix !== "" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", style: { color: affixColor }, children: inputSuffix }),
|
|
3438
3714
|
useButton && stepper("inc")
|
|
3439
3715
|
]
|
|
3440
3716
|
}
|