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.
- 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 +218 -209
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.js +218 -209
- 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 +235 -221
- package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
- package/dist/components/SChipInput/SChipInput.js +235 -221
- package/dist/components/SChipInput/SChipInput.js.map +1 -1
- package/dist/components/SCircleProgress/SCircleProgress.cjs +53 -55
- package/dist/components/SCircleProgress/SCircleProgress.cjs.map +1 -1
- package/dist/components/SCircleProgress/SCircleProgress.js +53 -55
- package/dist/components/SCircleProgress/SCircleProgress.js.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.cjs +223 -214
- package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.js +223 -214
- package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs +298 -263
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.js +298 -263
- 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 +218 -21
- package/dist/components/SField/SField.cjs.map +1 -1
- package/dist/components/SField/SField.d.ts +11 -0
- package/dist/components/SField/SField.js +218 -21
- package/dist/components/SField/SField.js.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.cjs +298 -278
- package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.d.ts +4 -2
- package/dist/components/SFilePicker/SFilePicker.js +298 -278
- package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
- package/dist/components/SInput/SInput.cjs +218 -209
- package/dist/components/SInput/SInput.cjs.map +1 -1
- package/dist/components/SInput/SInput.js +218 -209
- package/dist/components/SInput/SInput.js.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs +610 -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 +610 -450
- package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
- package/dist/components/SLoadingContainer/SLoadingContainer.cjs +61 -55
- package/dist/components/SLoadingContainer/SLoadingContainer.cjs.map +1 -1
- package/dist/components/SLoadingContainer/SLoadingContainer.js +61 -55
- package/dist/components/SLoadingContainer/SLoadingContainer.js.map +1 -1
- package/dist/components/SLoadingContainer/loading.cjs +61 -55
- package/dist/components/SLoadingContainer/loading.cjs.map +1 -1
- package/dist/components/SLoadingContainer/loading.js +61 -55
- package/dist/components/SLoadingContainer/loading.js.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.cjs +56 -57
- package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.js +56 -57
- package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.cjs +326 -50
- package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.d.ts +2 -0
- package/dist/components/SNumberInput/SNumberInput.js +326 -50
- 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/SSelect/SSelect.cjs +67 -32
- package/dist/components/SSelect/SSelect.cjs.map +1 -1
- package/dist/components/SSelect/SSelect.js +67 -32
- package/dist/components/SSelect/SSelect.js.map +1 -1
- package/dist/components/STable/STable.cjs +121 -88
- package/dist/components/STable/STable.cjs.map +1 -1
- package/dist/components/STable/STable.js +121 -88
- package/dist/components/STable/STable.js.map +1 -1
- package/dist/components/STextarea/STextarea.cjs +218 -21
- package/dist/components/STextarea/STextarea.cjs.map +1 -1
- package/dist/components/STextarea/STextarea.js +218 -21
- 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 +209 -14
- package/dist/components/STooltip/STooltip.cjs.map +1 -1
- package/dist/components/STooltip/STooltip.js +209 -14
- package/dist/components/STooltip/STooltip.js.map +1 -1
- package/dist/index.cjs +432 -260
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +432 -260
- package/dist/index.js.map +1 -1
- package/dist/styles.css +51 -27
- package/dist/theme.css +4 -2
- package/package.json +1 -1
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { forwardRef, createContext, useState, useRef, useImperativeHandle, useEffect, useContext } from 'react';
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import { extendTailwindMerge } from 'tailwind-merge';
|
|
5
|
-
import { jsx,
|
|
6
|
-
import * as
|
|
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
|
-
|
|
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(
|
|
3139
|
+
return /* @__PURE__ */ jsxs(RPopover2.Root, { open, onOpenChange: setOpen, children: [
|
|
2951
3140
|
isHover ? (
|
|
2952
3141
|
// hover: Anchor(위치 기준) + 직접 mouse 핸들러 — 클릭으로 토글되지 않도록 Trigger 미사용
|
|
2953
|
-
/* @__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(
|
|
2965
|
-
/* @__PURE__ */ jsx(
|
|
2966
|
-
|
|
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
|
-
|
|
3174
|
+
SGhostButton,
|
|
2986
3175
|
{
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
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(
|
|
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:
|
|
3028
|
-
md: { height: 36, radius: 6, fieldGap: 16, labelGap: 8, fontSize: 14, lineHeight: 24, icon:
|
|
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,
|
|
@@ -3178,8 +3375,8 @@ var SField = forwardRef(function SField2({
|
|
|
3178
3375
|
);
|
|
3179
3376
|
});
|
|
3180
3377
|
var PAD = {
|
|
3181
|
-
sm: { fontSize: 12, stepper: 20, icon: 12 },
|
|
3182
|
-
md: { fontSize: 14, stepper: 24, icon: 14 }
|
|
3378
|
+
sm: { fontSize: 12, lineHeight: 20, stepper: 20, icon: 12 },
|
|
3379
|
+
md: { fontSize: 14, lineHeight: 24, stepper: 24, icon: 14 }
|
|
3183
3380
|
};
|
|
3184
3381
|
var toNumber = (val) => {
|
|
3185
3382
|
if (val === null || val === void 0 || val === "") return null;
|
|
@@ -3197,6 +3394,38 @@ var formatWithCommas = (value) => {
|
|
|
3197
3394
|
const result = isNeg ? "-" + formatted : formatted;
|
|
3198
3395
|
return decPart !== void 0 && decPart !== "" ? result + "." + decPart : result;
|
|
3199
3396
|
};
|
|
3397
|
+
var addCommas = (value) => value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
3398
|
+
var formatInputWithCommas = (value) => {
|
|
3399
|
+
const isNeg = value.startsWith("-");
|
|
3400
|
+
const unsigned = isNeg ? value.slice(1) : value;
|
|
3401
|
+
const hasDecimal = unsigned.includes(".");
|
|
3402
|
+
const [intPart = "", decPart = ""] = unsigned.split(".");
|
|
3403
|
+
const formattedInt = addCommas(intPart === "" ? "0" : intPart);
|
|
3404
|
+
return `${isNeg ? "-" : ""}${formattedInt}${hasDecimal ? `.${decPart}` : ""}`;
|
|
3405
|
+
};
|
|
3406
|
+
var getDecimalLimit = (useDecimal, maxDecimalLength) => {
|
|
3407
|
+
if (!useDecimal || maxDecimalLength === void 0 || !Number.isFinite(maxDecimalLength)) {
|
|
3408
|
+
return void 0;
|
|
3409
|
+
}
|
|
3410
|
+
return Math.max(0, Math.floor(maxDecimalLength));
|
|
3411
|
+
};
|
|
3412
|
+
var applyDecimalLimit = (value, decimalLimit) => {
|
|
3413
|
+
if (value === null || decimalLimit === void 0) return value;
|
|
3414
|
+
return Number(value.toFixed(decimalLimit));
|
|
3415
|
+
};
|
|
3416
|
+
var getLogicalCaret = (value, caret) => {
|
|
3417
|
+
return value.slice(0, caret ?? value.length).replace(/,/g, "").length;
|
|
3418
|
+
};
|
|
3419
|
+
var getDisplayCaret = (value, logicalCaret) => {
|
|
3420
|
+
if (logicalCaret <= 0) return 0;
|
|
3421
|
+
let seen = 0;
|
|
3422
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
3423
|
+
if (value[i] === ",") continue;
|
|
3424
|
+
seen += 1;
|
|
3425
|
+
if (seen >= logicalCaret) return i + 1;
|
|
3426
|
+
}
|
|
3427
|
+
return value.length;
|
|
3428
|
+
};
|
|
3200
3429
|
var SNumberInput = forwardRef(function SNumberInput2({
|
|
3201
3430
|
value,
|
|
3202
3431
|
onValueChange,
|
|
@@ -3206,6 +3435,7 @@ var SNumberInput = forwardRef(function SNumberInput2({
|
|
|
3206
3435
|
step = 1,
|
|
3207
3436
|
useButton = false,
|
|
3208
3437
|
useDecimal = false,
|
|
3438
|
+
maxDecimalLength,
|
|
3209
3439
|
inputPrefix = "",
|
|
3210
3440
|
inputSuffix = "",
|
|
3211
3441
|
inputClass,
|
|
@@ -3237,7 +3467,8 @@ var SNumberInput = forwardRef(function SNumberInput2({
|
|
|
3237
3467
|
}, ref) {
|
|
3238
3468
|
const p = PAD[size];
|
|
3239
3469
|
const padX = `var(--cmp-numberInput-${size}-paddingX)`;
|
|
3240
|
-
const
|
|
3470
|
+
const decimalLimit = getDecimalLimit(useDecimal, maxDecimalLength);
|
|
3471
|
+
const numValue = applyDecimalLimit(toNumber(value), decimalLimit);
|
|
3241
3472
|
const [display, setDisplay] = useState(() => formatWithCommas(numValue));
|
|
3242
3473
|
const [ruleError, setRuleError] = useState("");
|
|
3243
3474
|
const innerRef = useRef(null);
|
|
@@ -3247,8 +3478,9 @@ var SNumberInput = forwardRef(function SNumberInput2({
|
|
|
3247
3478
|
else if (ref) ref.current = el;
|
|
3248
3479
|
};
|
|
3249
3480
|
useEffect(() => {
|
|
3250
|
-
|
|
3251
|
-
|
|
3481
|
+
if (document.activeElement === innerRef.current) return;
|
|
3482
|
+
setDisplay(formatWithCommas(applyDecimalLimit(toNumber(value), decimalLimit)));
|
|
3483
|
+
}, [decimalLimit, value]);
|
|
3252
3484
|
useFormField({
|
|
3253
3485
|
name,
|
|
3254
3486
|
validate: () => {
|
|
@@ -3260,48 +3492,87 @@ var SNumberInput = forwardRef(function SNumberInput2({
|
|
|
3260
3492
|
});
|
|
3261
3493
|
const resolvedError = error || ruleError !== "";
|
|
3262
3494
|
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
3263
|
-
const
|
|
3264
|
-
|
|
3265
|
-
|
|
3495
|
+
const restoreCaret = (nextDisplay, logicalCaret) => {
|
|
3496
|
+
const input = innerRef.current;
|
|
3497
|
+
if (logicalCaret === void 0 || !input || document.activeElement !== input) return;
|
|
3498
|
+
const nextCaret = getDisplayCaret(nextDisplay, logicalCaret);
|
|
3499
|
+
requestAnimationFrame(() => {
|
|
3500
|
+
if (document.activeElement !== input) return;
|
|
3501
|
+
input.setSelectionRange(nextCaret, nextCaret);
|
|
3502
|
+
});
|
|
3503
|
+
};
|
|
3504
|
+
const setDisplayWithCaret = (nextDisplay, logicalCaret) => {
|
|
3505
|
+
setDisplay(nextDisplay);
|
|
3506
|
+
restoreCaret(nextDisplay, logicalCaret);
|
|
3507
|
+
};
|
|
3508
|
+
const resetRejectedInput = (target, logicalCaret) => {
|
|
3509
|
+
target.value = display;
|
|
3510
|
+
restoreCaret(display, Math.min(logicalCaret, display.replace(/,/g, "").length));
|
|
3511
|
+
};
|
|
3512
|
+
const emit = (n, logicalCaret) => {
|
|
3513
|
+
const next = applyDecimalLimit(n, decimalLimit);
|
|
3514
|
+
const nextDisplay = formatWithCommas(next);
|
|
3515
|
+
setDisplayWithCaret(nextDisplay, logicalCaret);
|
|
3516
|
+
onValueChange?.(next);
|
|
3266
3517
|
};
|
|
3267
3518
|
const incDisabled = disabled || readOnly || numValue !== null && numValue >= max;
|
|
3268
3519
|
const decDisabled = disabled || readOnly || numValue !== null && numValue <= min;
|
|
3269
3520
|
const increment = () => {
|
|
3270
3521
|
if (incDisabled) return;
|
|
3271
3522
|
const cur = numValue ?? (min === Number.NEGATIVE_INFINITY ? 0 : min);
|
|
3272
|
-
const next = Math.min(cur + step, max);
|
|
3523
|
+
const next = applyDecimalLimit(Math.min(cur + step, max), decimalLimit);
|
|
3273
3524
|
if (next !== cur) emit(next);
|
|
3274
3525
|
};
|
|
3275
3526
|
const decrement = () => {
|
|
3276
3527
|
if (decDisabled) return;
|
|
3277
3528
|
const cur = numValue ?? (min === Number.NEGATIVE_INFINITY ? 0 : min);
|
|
3278
|
-
const next = Math.max(cur - step, min);
|
|
3529
|
+
const next = applyDecimalLimit(Math.max(cur - step, min), decimalLimit);
|
|
3279
3530
|
if (next !== cur) emit(next);
|
|
3280
3531
|
};
|
|
3281
|
-
const handleInput = (
|
|
3532
|
+
const handleInput = (event) => {
|
|
3533
|
+
const target = event.currentTarget;
|
|
3534
|
+
const raw = target.value;
|
|
3535
|
+
const logicalCaret = getLogicalCaret(raw, target.selectionStart);
|
|
3282
3536
|
if (raw === "") {
|
|
3283
|
-
|
|
3537
|
+
setDisplayWithCaret("", 0);
|
|
3284
3538
|
onValueChange?.(null);
|
|
3285
3539
|
return;
|
|
3286
3540
|
}
|
|
3287
3541
|
const cleaned = raw.replace(/,/g, "");
|
|
3288
3542
|
const decimals = (cleaned.match(/\./g) || []).length;
|
|
3289
|
-
if (decimals > 1)
|
|
3543
|
+
if (decimals > 1) {
|
|
3544
|
+
resetRejectedInput(target, logicalCaret);
|
|
3545
|
+
return;
|
|
3546
|
+
}
|
|
3290
3547
|
if (cleaned === "-" || cleaned === "." || cleaned === "-.") {
|
|
3291
|
-
|
|
3548
|
+
setDisplayWithCaret(cleaned, logicalCaret);
|
|
3292
3549
|
return;
|
|
3293
3550
|
}
|
|
3294
|
-
if (!useDecimal && cleaned.includes("."))
|
|
3551
|
+
if (!useDecimal && cleaned.includes(".")) {
|
|
3552
|
+
resetRejectedInput(target, logicalCaret);
|
|
3553
|
+
return;
|
|
3554
|
+
}
|
|
3555
|
+
if (decimalLimit !== void 0 && cleaned.includes(".")) {
|
|
3556
|
+
const fraction = cleaned.split(".")[1] ?? "";
|
|
3557
|
+
if (fraction.length > decimalLimit) {
|
|
3558
|
+
resetRejectedInput(target, logicalCaret);
|
|
3559
|
+
return;
|
|
3560
|
+
}
|
|
3561
|
+
}
|
|
3295
3562
|
if (cleaned.endsWith(".") && decimals === 1) {
|
|
3296
3563
|
const parsed2 = toNumber(cleaned.slice(0, -1));
|
|
3297
3564
|
if (parsed2 !== null) {
|
|
3298
|
-
|
|
3565
|
+
setDisplayWithCaret(formatInputWithCommas(cleaned), logicalCaret);
|
|
3299
3566
|
return;
|
|
3300
3567
|
}
|
|
3301
3568
|
}
|
|
3302
3569
|
const parsed = toNumber(cleaned);
|
|
3303
|
-
if (parsed === null)
|
|
3304
|
-
|
|
3570
|
+
if (parsed === null) {
|
|
3571
|
+
resetRejectedInput(target, logicalCaret);
|
|
3572
|
+
return;
|
|
3573
|
+
}
|
|
3574
|
+
setDisplayWithCaret(formatInputWithCommas(cleaned), logicalCaret);
|
|
3575
|
+
onValueChange?.(applyDecimalLimit(parsed, decimalLimit));
|
|
3305
3576
|
};
|
|
3306
3577
|
const stepper = (dir) => {
|
|
3307
3578
|
const off = dir === "inc" ? incDisabled : decDisabled;
|
|
@@ -3314,8 +3585,8 @@ var SNumberInput = forwardRef(function SNumberInput2({
|
|
|
3314
3585
|
"aria-label": dir === "inc" ? "\uC99D\uAC00" : "\uAC10\uC18C",
|
|
3315
3586
|
onClick: dir === "inc" ? increment : decrement,
|
|
3316
3587
|
className: cn(
|
|
3317
|
-
"flex flex-shrink-0 items-center justify-center",
|
|
3318
|
-
off ? "cursor-not-allowed" : "cursor-pointer"
|
|
3588
|
+
"flex flex-shrink-0 items-center justify-center border border-solid border-transparent transition-colors",
|
|
3589
|
+
off ? "cursor-not-allowed" : "cursor-pointer hover:border-[var(--color-border-accentLight)]"
|
|
3319
3590
|
),
|
|
3320
3591
|
style: {
|
|
3321
3592
|
width: `var(--cmp-numberInput-stepper-${size}-size)`,
|
|
@@ -3364,24 +3635,26 @@ var SNumberInput = forwardRef(function SNumberInput2({
|
|
|
3364
3635
|
style: {
|
|
3365
3636
|
// 원본 sd-number-input: content gap = padding-x 토큰 (gap 토큰 아님)
|
|
3366
3637
|
gap: `var(--cmp-numberInput-${size}-paddingX)`,
|
|
3367
|
-
padding: `0 ${useButton ? padX : `calc(${padX} * 2)`}
|
|
3638
|
+
padding: `0 ${useButton ? padX : `calc(${padX} * 2)`}`,
|
|
3639
|
+
fontSize: p.fontSize,
|
|
3640
|
+
lineHeight: `${p.lineHeight}px`
|
|
3368
3641
|
},
|
|
3369
3642
|
children: [
|
|
3370
3643
|
useButton && stepper("dec"),
|
|
3371
|
-
inputPrefix !== "" && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", style: {
|
|
3644
|
+
inputPrefix !== "" && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", style: { color: affixColor }, children: inputPrefix }),
|
|
3372
3645
|
/* @__PURE__ */ jsx(
|
|
3373
3646
|
"input",
|
|
3374
3647
|
{
|
|
3375
3648
|
ref: setRefs,
|
|
3376
3649
|
name,
|
|
3377
3650
|
type: "text",
|
|
3378
|
-
inputMode: "numeric",
|
|
3651
|
+
inputMode: useDecimal ? "decimal" : "numeric",
|
|
3379
3652
|
value: display,
|
|
3380
3653
|
placeholder,
|
|
3381
3654
|
disabled,
|
|
3382
3655
|
readOnly,
|
|
3383
3656
|
autoFocus,
|
|
3384
|
-
onChange:
|
|
3657
|
+
onChange: handleInput,
|
|
3385
3658
|
onKeyDown: (e) => {
|
|
3386
3659
|
if (e.key === "ArrowUp") {
|
|
3387
3660
|
e.preventDefault();
|
|
@@ -3393,26 +3666,29 @@ var SNumberInput = forwardRef(function SNumberInput2({
|
|
|
3393
3666
|
},
|
|
3394
3667
|
onFocus: (e) => onFocus?.(e),
|
|
3395
3668
|
onBlur: (e) => {
|
|
3396
|
-
const
|
|
3669
|
+
const cleaned = display.replace(/,/g, "");
|
|
3670
|
+
const n = toNumber(cleaned);
|
|
3397
3671
|
const clamped = n === null ? null : Math.min(Math.max(n, min), max);
|
|
3398
3672
|
if (clamped !== n) emit(clamped);
|
|
3399
|
-
else setDisplay(
|
|
3673
|
+
else setDisplay(n === null ? "" : formatInputWithCommas(cleaned));
|
|
3400
3674
|
if (rules && rules.length > 0) setRuleError(runRules(clamped, rules));
|
|
3401
3675
|
onBlur?.(e);
|
|
3402
3676
|
},
|
|
3403
3677
|
className: cn(
|
|
3404
|
-
|
|
3678
|
+
// h-full 대신 라인박스 높이 그대로 두어 prefix/suffix 와 동일하게 flex 로 수직 센터링
|
|
3679
|
+
// (h-full 이면 input 내부 텍스트 센터링을 브라우저가 수행해 1px 어긋날 수 있음)
|
|
3680
|
+
"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",
|
|
3405
3681
|
inputClass
|
|
3406
3682
|
),
|
|
3407
3683
|
style: {
|
|
3408
|
-
fontSize
|
|
3684
|
+
// fontSize/lineHeight 는 컨테이너에서 상속
|
|
3409
3685
|
textAlign: useButton ? "center" : "right",
|
|
3410
3686
|
color: disabled ? "var(--cmp-numberInput-text-disabled)" : "var(--cmp-numberInput-text-default)",
|
|
3411
3687
|
...inputStyle
|
|
3412
3688
|
}
|
|
3413
3689
|
}
|
|
3414
3690
|
),
|
|
3415
|
-
inputSuffix !== "" && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", style: {
|
|
3691
|
+
inputSuffix !== "" && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", style: { color: affixColor }, children: inputSuffix }),
|
|
3416
3692
|
useButton && stepper("inc")
|
|
3417
3693
|
]
|
|
3418
3694
|
}
|