sellmate-design-system-react 0.2.0 → 0.4.0
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/README.md +84 -21
- package/dist/components/SActionModal/SActionModal.cjs +2 -0
- package/dist/components/SActionModal/SActionModal.cjs.map +1 -1
- package/dist/components/SActionModal/SActionModal.js +2 -0
- package/dist/components/SActionModal/SActionModal.js.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs +2 -0
- package/dist/components/SBarcodeInput/SBarcodeInput.cjs.map +1 -1
- package/dist/components/SBarcodeInput/SBarcodeInput.js +2 -0
- package/dist/components/SBarcodeInput/SBarcodeInput.js.map +1 -1
- package/dist/components/SCalendar/SCalendar.cjs +2 -0
- package/dist/components/SCalendar/SCalendar.cjs.map +1 -1
- package/dist/components/SCalendar/SCalendar.js +2 -0
- package/dist/components/SCalendar/SCalendar.js.map +1 -1
- package/dist/components/SChip/SChip.cjs +2 -0
- package/dist/components/SChip/SChip.cjs.map +1 -1
- package/dist/components/SChip/SChip.js +2 -0
- package/dist/components/SChip/SChip.js.map +1 -1
- package/dist/components/SChipInput/SChipInput.cjs +2 -0
- package/dist/components/SChipInput/SChipInput.cjs.map +1 -1
- package/dist/components/SChipInput/SChipInput.js +2 -0
- package/dist/components/SChipInput/SChipInput.js.map +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.cjs +2 -0
- package/dist/components/SConfirmModal/SConfirmModal.cjs.map +1 -1
- package/dist/components/SConfirmModal/SConfirmModal.js +2 -0
- package/dist/components/SConfirmModal/SConfirmModal.js.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.cjs +2 -0
- package/dist/components/SDatePicker/SDatePicker.cjs.map +1 -1
- package/dist/components/SDatePicker/SDatePicker.js +2 -0
- package/dist/components/SDatePicker/SDatePicker.js.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs +2 -0
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.js +2 -0
- package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
- package/dist/components/SField/SField.cjs +2 -0
- package/dist/components/SField/SField.cjs.map +1 -1
- package/dist/components/SField/SField.js +2 -0
- package/dist/components/SField/SField.js.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.cjs +2 -0
- package/dist/components/SFilePicker/SFilePicker.cjs.map +1 -1
- package/dist/components/SFilePicker/SFilePicker.js +2 -0
- package/dist/components/SFilePicker/SFilePicker.js.map +1 -1
- package/dist/components/SGhostButton/SGhostButton.cjs +2 -0
- package/dist/components/SGhostButton/SGhostButton.cjs.map +1 -1
- package/dist/components/SGhostButton/SGhostButton.d.ts +3 -1
- package/dist/components/SGhostButton/SGhostButton.js +2 -0
- package/dist/components/SGhostButton/SGhostButton.js.map +1 -1
- package/dist/components/SGnb/SGnb.cjs +394 -213
- package/dist/components/SGnb/SGnb.cjs.map +1 -1
- package/dist/components/SGnb/SGnb.d.ts +16 -12
- package/dist/components/SGnb/SGnb.js +375 -214
- package/dist/components/SGnb/SGnb.js.map +1 -1
- package/dist/components/SGnb/gnb.config.d.ts +35 -11
- package/dist/components/SGnb/index.d.ts +1 -1
- package/dist/components/SGuide/SGuide.cjs +2 -0
- package/dist/components/SGuide/SGuide.cjs.map +1 -1
- package/dist/components/SGuide/SGuide.js +2 -0
- package/dist/components/SGuide/SGuide.js.map +1 -1
- package/dist/components/SInput/SInput.cjs +2 -0
- package/dist/components/SInput/SInput.cjs.map +1 -1
- package/dist/components/SInput/SInput.js +2 -0
- package/dist/components/SInput/SInput.js.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs +2 -0
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.js +2 -0
- package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
- package/dist/components/SLayout/SLayout.cjs +26 -8
- package/dist/components/SLayout/SLayout.cjs.map +1 -1
- package/dist/components/SLayout/SLayout.d.ts +15 -9
- package/dist/components/SLayout/SLayout.js +26 -8
- package/dist/components/SLayout/SLayout.js.map +1 -1
- package/dist/components/SLayout/index.d.ts +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.cjs +2 -0
- package/dist/components/SLoadingModal/SLoadingModal.cjs.map +1 -1
- package/dist/components/SLoadingModal/SLoadingModal.js +2 -0
- package/dist/components/SLoadingModal/SLoadingModal.js.map +1 -1
- package/dist/components/SModalContainer/SModalContainer.cjs +2 -0
- package/dist/components/SModalContainer/SModalContainer.cjs.map +1 -1
- package/dist/components/SModalContainer/SModalContainer.js +2 -0
- package/dist/components/SModalContainer/SModalContainer.js.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.cjs +2 -0
- package/dist/components/SNumberInput/SNumberInput.cjs.map +1 -1
- package/dist/components/SNumberInput/SNumberInput.js +2 -0
- package/dist/components/SNumberInput/SNumberInput.js.map +1 -1
- package/dist/components/SPage/SPage.cjs +27 -9
- package/dist/components/SPage/SPage.cjs.map +1 -1
- package/dist/components/SPage/SPage.d.ts +2 -2
- package/dist/components/SPage/SPage.js +27 -9
- package/dist/components/SPage/SPage.js.map +1 -1
- package/dist/components/SPopover/SPopover.cjs +2 -0
- package/dist/components/SPopover/SPopover.cjs.map +1 -1
- package/dist/components/SPopover/SPopover.js +2 -0
- package/dist/components/SPopover/SPopover.js.map +1 -1
- package/dist/components/SSelect/SSelect.cjs +2 -0
- package/dist/components/SSelect/SSelect.cjs.map +1 -1
- package/dist/components/SSelect/SSelect.js +2 -0
- package/dist/components/SSelect/SSelect.js.map +1 -1
- package/dist/components/STable/STable.cjs +3 -1
- package/dist/components/STable/STable.cjs.map +1 -1
- package/dist/components/STable/STable.js +3 -1
- package/dist/components/STable/STable.js.map +1 -1
- package/dist/components/STextarea/STextarea.cjs +2 -0
- package/dist/components/STextarea/STextarea.cjs.map +1 -1
- package/dist/components/STextarea/STextarea.js +2 -0
- package/dist/components/STextarea/STextarea.js.map +1 -1
- package/dist/components/STimePicker/STimePicker.cjs +3834 -0
- package/dist/components/STimePicker/STimePicker.cjs.map +1 -0
- package/dist/components/STimePicker/STimePicker.d.ts +50 -0
- package/dist/components/STimePicker/STimePicker.js +3811 -0
- package/dist/components/STimePicker/STimePicker.js.map +1 -0
- package/dist/components/STimePicker/index.d.ts +2 -0
- package/dist/components/STimePicker/timepicker.config.d.ts +23 -0
- package/dist/components/STimeRangePicker/STimeRangePicker.cjs +3908 -0
- package/dist/components/STimeRangePicker/STimeRangePicker.cjs.map +1 -0
- package/dist/components/STimeRangePicker/STimeRangePicker.d.ts +53 -0
- package/dist/components/STimeRangePicker/STimeRangePicker.js +3885 -0
- package/dist/components/STimeRangePicker/STimeRangePicker.js.map +1 -0
- package/dist/components/STimeRangePicker/index.d.ts +1 -0
- package/dist/components/STimeRangePicker/time-range-picker.config.d.ts +9 -0
- package/dist/components/SToast/SToast.cjs +2 -0
- package/dist/components/SToast/SToast.cjs.map +1 -1
- package/dist/components/SToast/SToast.js +2 -0
- package/dist/components/SToast/SToast.js.map +1 -1
- package/dist/components/SToast/SToastContainer.cjs +2 -0
- package/dist/components/SToast/SToastContainer.cjs.map +1 -1
- package/dist/components/SToast/SToastContainer.js +2 -0
- package/dist/components/SToast/SToastContainer.js.map +1 -1
- package/dist/components/STooltip/STooltip.cjs +2 -0
- package/dist/components/STooltip/STooltip.cjs.map +1 -1
- package/dist/components/STooltip/STooltip.js +2 -0
- package/dist/components/STooltip/STooltip.js.map +1 -1
- package/dist/index.cjs +1011 -136
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1006 -136
- package/dist/index.js.map +1 -1
- package/dist/styles.css +475 -74
- package/dist/theme.css +85 -70
- package/package.json +4 -1
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
4
|
var clsx = require('clsx');
|
|
@@ -40,7 +41,6 @@ var RDialog__namespace = /*#__PURE__*/_interopNamespace(RDialog);
|
|
|
40
41
|
var RCheckbox__namespace = /*#__PURE__*/_interopNamespace(RCheckbox);
|
|
41
42
|
var Ariakit__namespace = /*#__PURE__*/_interopNamespace(Ariakit);
|
|
42
43
|
|
|
43
|
-
// src/lib/cn.ts
|
|
44
44
|
var twMerge = tailwindMerge.extendTailwindMerge({
|
|
45
45
|
extend: {
|
|
46
46
|
classGroups: {
|
|
@@ -3943,6 +3943,7 @@ function SGhostButton({
|
|
|
3943
3943
|
size = "sm",
|
|
3944
3944
|
intent = "default",
|
|
3945
3945
|
ariaLabel = "",
|
|
3946
|
+
ariaPressed,
|
|
3946
3947
|
tooltipText,
|
|
3947
3948
|
disabled = false,
|
|
3948
3949
|
onClick,
|
|
@@ -3979,6 +3980,7 @@ function SGhostButton({
|
|
|
3979
3980
|
disabled,
|
|
3980
3981
|
"aria-disabled": disabled ? "true" : void 0,
|
|
3981
3982
|
"aria-label": ariaLabel.trim() || void 0,
|
|
3983
|
+
"aria-pressed": ariaPressed,
|
|
3982
3984
|
tabIndex: disabled ? -1 : void 0,
|
|
3983
3985
|
onClick: (e) => {
|
|
3984
3986
|
if (disabled) {
|
|
@@ -4906,13 +4908,27 @@ var belt = (p) => `var(--cmp-gnbBelt-${p})`;
|
|
|
4906
4908
|
var box = (p) => `var(--cmp-gnbBox-${p})`;
|
|
4907
4909
|
var light = (p) => `var(--cmp-light-${p})`;
|
|
4908
4910
|
var dark = (p) => `var(--cmp-dark-${p})`;
|
|
4909
|
-
var
|
|
4910
|
-
|
|
4911
|
-
|
|
4911
|
+
var GNB_HEADER = {
|
|
4912
|
+
fix: {
|
|
4913
|
+
topHeight: belt("top-height"),
|
|
4914
|
+
topPaddingX: belt("top-paddingX"),
|
|
4915
|
+
topGap: belt("top-gap"),
|
|
4916
|
+
logoHeight: belt("logo-height")
|
|
4917
|
+
},
|
|
4918
|
+
full: {
|
|
4919
|
+
topHeight: box("top-height"),
|
|
4912
4920
|
topPaddingX: box("paddingX"),
|
|
4913
4921
|
topGap: box("gap"),
|
|
4914
4922
|
// 상단바 gap (버튼 간 세부 gap 은 --cmp-gnbBox-button-gap)
|
|
4915
|
-
|
|
4923
|
+
logoHeight: box("logo-height")
|
|
4924
|
+
}
|
|
4925
|
+
};
|
|
4926
|
+
var GNB_MENU = {
|
|
4927
|
+
box: {
|
|
4928
|
+
// menu.body.padding 은 전방향 토큰 → 세로 패딩과 아이템 좌우 여백 양쪽에 쓴다
|
|
4929
|
+
bodyPaddingY: box("menu-body-padding"),
|
|
4930
|
+
itemMarginX: box("menu-body-padding"),
|
|
4931
|
+
itemPaddingY: box("item-paddingY"),
|
|
4916
4932
|
itemPaddingRight: box("item-paddingRight"),
|
|
4917
4933
|
itemGap: box("item-gap"),
|
|
4918
4934
|
itemRadius: box("item-radius"),
|
|
@@ -4932,9 +4948,10 @@ var GNB_LAYOUT = {
|
|
|
4932
4948
|
// token: gnbBox.item.typography.default.lineHeight
|
|
4933
4949
|
},
|
|
4934
4950
|
belt: {
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4951
|
+
bodyPaddingY: belt("body-paddingY"),
|
|
4952
|
+
itemMarginX: "0px",
|
|
4953
|
+
// belt 토큰에 가로 여백 없음 → 풀폭 행 (들여쓰기는 depth*-paddingLeft 전담)
|
|
4954
|
+
itemPaddingY: belt("item-paddingY"),
|
|
4938
4955
|
itemPaddingRight: belt("item-paddingRight"),
|
|
4939
4956
|
itemGap: belt("item-gap"),
|
|
4940
4957
|
itemRadius: "0px",
|
|
@@ -4964,7 +4981,9 @@ var GNB_COLORS = {
|
|
|
4964
4981
|
itemBgHover: light("item-bg-hover"),
|
|
4965
4982
|
itemBgSelected: light("item-bg-selected"),
|
|
4966
4983
|
itemColorDefault: light("item-fg-default"),
|
|
4967
|
-
|
|
4984
|
+
itemColorHover: light("item-fg-hover"),
|
|
4985
|
+
itemColorActive: light("item-fg-selected"),
|
|
4986
|
+
itemChevron: light("item-chevron")
|
|
4968
4987
|
},
|
|
4969
4988
|
dark: {
|
|
4970
4989
|
panelBg: dark("bg"),
|
|
@@ -4973,18 +4992,19 @@ var GNB_COLORS = {
|
|
|
4973
4992
|
itemBgHover: dark("item-bg-hover"),
|
|
4974
4993
|
itemBgSelected: dark("item-bg-selected"),
|
|
4975
4994
|
itemColorDefault: dark("item-fg-default"),
|
|
4976
|
-
|
|
4995
|
+
itemColorHover: dark("item-fg-hover"),
|
|
4996
|
+
itemColorActive: dark("item-fg-selected"),
|
|
4997
|
+
itemChevron: dark("item-chevron")
|
|
4977
4998
|
}
|
|
4978
4999
|
};
|
|
4979
5000
|
var GNB_COMMON = {
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
// 48 (belt 토큰 공유)
|
|
5001
|
+
/** 접힘 레일 폭. header="full" 은 접으면 메뉴만 사라져 레일이 없으므로 fix 전용. */
|
|
5002
|
+
foldWidth: belt("fold-width"),
|
|
5003
|
+
/**
|
|
5004
|
+
* 상단바 아이콘 버튼 사이 간격. fix 레일(세로 스택)의 gap·상하 여백에 쓴다.
|
|
5005
|
+
* 레일 전용 여백 토큰이 따로 없어 같은 값(8px)인 이 토큰으로 재해석했다.
|
|
5006
|
+
*/
|
|
5007
|
+
buttonGap: box("button-gap")
|
|
4988
5008
|
};
|
|
4989
5009
|
var GNB_MENU_WIDTH = 240;
|
|
4990
5010
|
function findGnbAncestors(items, value) {
|
|
@@ -5008,6 +5028,7 @@ function useSLayout() {
|
|
|
5008
5028
|
}
|
|
5009
5029
|
var SLayout = react.forwardRef(function SLayout2({
|
|
5010
5030
|
type = "box",
|
|
5031
|
+
header = "fix",
|
|
5011
5032
|
folded,
|
|
5012
5033
|
defaultFolded = false,
|
|
5013
5034
|
onFoldedChange,
|
|
@@ -5027,22 +5048,23 @@ var SLayout = react.forwardRef(function SLayout2({
|
|
|
5027
5048
|
[isControlled, onFoldedChange]
|
|
5028
5049
|
);
|
|
5029
5050
|
const ctx = react.useMemo(
|
|
5030
|
-
() => ({ type, folded: currentFolded, setFolded }),
|
|
5031
|
-
[type, currentFolded, setFolded]
|
|
5051
|
+
() => ({ type, header, folded: currentFolded, setFolded }),
|
|
5052
|
+
[type, header, currentFolded, setFolded]
|
|
5032
5053
|
);
|
|
5033
|
-
const
|
|
5034
|
-
const frameStyle =
|
|
5054
|
+
const isFullHeader = header === "full";
|
|
5055
|
+
const frameStyle = isFullHeader ? {
|
|
5035
5056
|
gridTemplateColumns: `${currentFolded ? 0 : GNB_MENU_WIDTH}px 1fr`,
|
|
5036
|
-
gridTemplateRows: `${
|
|
5057
|
+
gridTemplateRows: `${GNB_HEADER[header].topHeight} 1fr`
|
|
5037
5058
|
} : void 0;
|
|
5038
5059
|
return /* @__PURE__ */ jsxRuntime.jsx(SLayoutContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5039
5060
|
"div",
|
|
5040
5061
|
{
|
|
5041
5062
|
ref,
|
|
5042
5063
|
"data-type": type,
|
|
5064
|
+
"data-header": header,
|
|
5043
5065
|
className: cn(
|
|
5044
5066
|
"box-border h-full overflow-hidden",
|
|
5045
|
-
|
|
5067
|
+
isFullHeader ? "grid" : "flex flex-row",
|
|
5046
5068
|
className
|
|
5047
5069
|
),
|
|
5048
5070
|
style: { ...frameStyle, ...style },
|
|
@@ -5051,9 +5073,10 @@ var SLayout = react.forwardRef(function SLayout2({
|
|
|
5051
5073
|
}
|
|
5052
5074
|
) });
|
|
5053
5075
|
});
|
|
5054
|
-
var DEPTH_PADDING_LEFT = (
|
|
5076
|
+
var DEPTH_PADDING_LEFT = (M, depth) => depth === 1 ? M.depth1PaddingLeft : depth === 2 ? M.depth2PaddingLeft : M.depth3PaddingLeft;
|
|
5055
5077
|
var SGnb = react.forwardRef(function SGnb2({
|
|
5056
5078
|
type: typeProp,
|
|
5079
|
+
header: headerProp,
|
|
5057
5080
|
color = "light",
|
|
5058
5081
|
items,
|
|
5059
5082
|
value = "",
|
|
@@ -5062,14 +5085,17 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5062
5085
|
onFoldChange,
|
|
5063
5086
|
onLauncherClick,
|
|
5064
5087
|
logo,
|
|
5065
|
-
|
|
5088
|
+
ariaLabel = "global navigation",
|
|
5066
5089
|
className,
|
|
5067
|
-
style
|
|
5090
|
+
style,
|
|
5091
|
+
...rest
|
|
5068
5092
|
}, ref) {
|
|
5069
5093
|
const layout = react.useContext(SLayoutContext);
|
|
5070
5094
|
const type = typeProp ?? layout?.type ?? "box";
|
|
5095
|
+
const header = headerProp ?? layout?.header ?? "fix";
|
|
5071
5096
|
const folded = foldedProp ?? layout?.folded ?? false;
|
|
5072
|
-
const
|
|
5097
|
+
const H = GNB_HEADER[header];
|
|
5098
|
+
const M = GNB_MENU[type];
|
|
5073
5099
|
const C = GNB_COLORS[color];
|
|
5074
5100
|
const [expandedKeys, setExpandedKeys] = react.useState(
|
|
5075
5101
|
() => new Set(findGnbAncestors(items, value))
|
|
@@ -5079,6 +5105,7 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5079
5105
|
const ancestors = findGnbAncestors(items, value);
|
|
5080
5106
|
if (ancestors.length === 0) return;
|
|
5081
5107
|
setExpandedKeys((prev) => {
|
|
5108
|
+
if (ancestors.every((a) => prev.has(a))) return prev;
|
|
5082
5109
|
const next = new Set(prev);
|
|
5083
5110
|
ancestors.forEach((a) => next.add(a));
|
|
5084
5111
|
return next;
|
|
@@ -5103,148 +5130,142 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5103
5130
|
const handleFoldToggle = () => {
|
|
5104
5131
|
const next = !folded;
|
|
5105
5132
|
onFoldChange?.(next);
|
|
5106
|
-
layout?.setFolded(next);
|
|
5133
|
+
if (foldedProp === void 0) layout?.setFolded(next);
|
|
5107
5134
|
};
|
|
5108
|
-
const iconBtnClass = "inline-flex shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-1.5";
|
|
5109
5135
|
const renderItem = (item, depth) => {
|
|
5110
5136
|
const hasChildren = !!(item.children && item.children.length > 0);
|
|
5111
5137
|
const isSelected = !hasChildren && item.value === value;
|
|
5112
5138
|
const isExpanded = expandedKeys.has(item.value);
|
|
5113
|
-
const isHovered = hoveredKey === item.value;
|
|
5114
|
-
const bg = isSelected ? C.itemBgSelected : isHovered && !item.disabled ? C.itemBgHover : C.itemBgDefault;
|
|
5139
|
+
const isHovered = hoveredKey === item.value && !item.disabled;
|
|
5115
5140
|
const itemStyle = {
|
|
5116
|
-
gap:
|
|
5117
|
-
paddingTop:
|
|
5118
|
-
paddingBottom:
|
|
5119
|
-
paddingRight:
|
|
5120
|
-
paddingLeft: DEPTH_PADDING_LEFT(
|
|
5121
|
-
marginLeft:
|
|
5122
|
-
marginRight:
|
|
5123
|
-
borderRadius:
|
|
5124
|
-
backgroundColor:
|
|
5125
|
-
color: isSelected ? C.itemColorActive : C.itemColorDefault,
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5141
|
+
gap: M.itemGap,
|
|
5142
|
+
paddingTop: M.itemPaddingY,
|
|
5143
|
+
paddingBottom: M.itemPaddingY,
|
|
5144
|
+
paddingRight: M.itemPaddingRight,
|
|
5145
|
+
paddingLeft: DEPTH_PADDING_LEFT(M, depth),
|
|
5146
|
+
marginLeft: M.itemMarginX,
|
|
5147
|
+
marginRight: M.itemMarginX,
|
|
5148
|
+
borderRadius: M.itemRadius,
|
|
5149
|
+
backgroundColor: isSelected ? C.itemBgSelected : isHovered ? C.itemBgHover : C.itemBgDefault,
|
|
5150
|
+
color: isSelected ? C.itemColorActive : isHovered ? C.itemColorHover : C.itemColorDefault,
|
|
5151
|
+
// <button> 의 UA 폰트(Arial 13.3px)를 끊고 앱 폰트를 잇는다
|
|
5152
|
+
fontFamily: "inherit",
|
|
5153
|
+
fontSize: depth === 1 ? M.depth1FontSize : M.itemFontSize,
|
|
5154
|
+
lineHeight: `${depth === 1 ? M.depth1LineHeight : M.itemLineHeight}px`,
|
|
5155
|
+
fontWeight: depth === 1 || isSelected ? M.depth1FontWeight : 400,
|
|
5129
5156
|
opacity: item.disabled ? 0.4 : 1,
|
|
5130
5157
|
cursor: item.disabled ? "not-allowed" : "pointer"
|
|
5131
5158
|
};
|
|
5132
|
-
return
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5159
|
+
return (
|
|
5160
|
+
// li 를 flex-col 로 둬야 버튼이 좌우 마진만큼 안쪽으로 들어가고,
|
|
5161
|
+
// 하위 ul 은 마진 없이 전폭을 써서 depth*-paddingLeft 가 들여쓰기를 단독으로 책임진다.
|
|
5162
|
+
/* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-col", children: [
|
|
5163
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5164
|
+
"button",
|
|
5165
|
+
{
|
|
5166
|
+
type: "button",
|
|
5167
|
+
disabled: item.disabled,
|
|
5168
|
+
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
5169
|
+
"aria-current": isSelected ? "page" : void 0,
|
|
5170
|
+
className: "box-border flex select-none items-center border-none bg-transparent text-left transition-colors duration-150",
|
|
5171
|
+
style: itemStyle,
|
|
5172
|
+
onClick: () => handleItemClick(item),
|
|
5173
|
+
onMouseEnter: () => setHoveredKey(item.value),
|
|
5174
|
+
onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
|
|
5175
|
+
children: [
|
|
5176
|
+
depth === 1 && item.icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: item.icon, size: M.depth1Icon, className: "shrink-0" }),
|
|
5177
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap", children: item.label }),
|
|
5178
|
+
item.tag !== void 0 && item.tag !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5179
|
+
STag,
|
|
5180
|
+
{
|
|
5181
|
+
shape: "square",
|
|
5182
|
+
size: "xs",
|
|
5183
|
+
color: item.tagColor ?? "blue",
|
|
5184
|
+
label: item.tag,
|
|
5185
|
+
className: "shrink-0"
|
|
5186
|
+
}
|
|
5187
|
+
),
|
|
5188
|
+
hasChildren && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5189
|
+
SIcon,
|
|
5190
|
+
{
|
|
5191
|
+
name: "chevronDown",
|
|
5192
|
+
size: M.itemArrow,
|
|
5193
|
+
color: C.itemChevron,
|
|
5194
|
+
rotate: isExpanded ? 180 : 0,
|
|
5195
|
+
className: "shrink-0 transition-transform duration-150"
|
|
5196
|
+
}
|
|
5197
|
+
)
|
|
5198
|
+
]
|
|
5199
|
+
}
|
|
5200
|
+
),
|
|
5201
|
+
hasChildren && isExpanded && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: item.children.map((child) => renderItem(child, depth + 1)) })
|
|
5202
|
+
] }, item.value)
|
|
5203
|
+
);
|
|
5204
|
+
};
|
|
5205
|
+
const railFolded = folded && header === "fix";
|
|
5206
|
+
const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth : GNB_MENU_WIDTH;
|
|
5207
|
+
const rootStyle = {
|
|
5208
|
+
width: rootWidth,
|
|
5209
|
+
...header === "full" ? { gridArea: "1 / 1 / -1 / -1", pointerEvents: "none" } : { backgroundColor: C.panelBg },
|
|
5210
|
+
...style
|
|
5179
5211
|
};
|
|
5180
|
-
const railFolded = folded && type === "belt";
|
|
5181
|
-
const rootWidth = type === "box" ? "100%" : folded ? GNB_COMMON.foldWidth : GNB_MENU_WIDTH;
|
|
5182
|
-
const inBoxGrid = !!layout && type === "box";
|
|
5183
|
-
const rootStyle = react.useMemo(
|
|
5184
|
-
() => ({
|
|
5185
|
-
width: rootWidth,
|
|
5186
|
-
...inBoxGrid ? { gridArea: "1 / 1 / -1 / -1" } : null,
|
|
5187
|
-
...style
|
|
5188
|
-
}),
|
|
5189
|
-
[rootWidth, inBoxGrid, style]
|
|
5190
|
-
);
|
|
5191
|
-
const iconColor = C.itemColorDefault;
|
|
5192
5212
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5193
5213
|
"div",
|
|
5194
5214
|
{
|
|
5195
5215
|
ref,
|
|
5196
|
-
className: cn(
|
|
5197
|
-
"box-border flex flex-col",
|
|
5198
|
-
type === "box" && !inBoxGrid ? "h-auto" : "h-full",
|
|
5199
|
-
className
|
|
5200
|
-
),
|
|
5216
|
+
className: cn("box-border flex h-full shrink-0 flex-col", className),
|
|
5201
5217
|
style: rootStyle,
|
|
5202
5218
|
"data-type": type,
|
|
5219
|
+
"data-header": header,
|
|
5203
5220
|
"data-color": color,
|
|
5221
|
+
...rest,
|
|
5204
5222
|
children: [
|
|
5205
5223
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5206
5224
|
"div",
|
|
5207
5225
|
{
|
|
5208
5226
|
className: cn("box-border flex items-center", railFolded && "flex-col"),
|
|
5209
5227
|
style: {
|
|
5210
|
-
gap: railFolded ?
|
|
5211
|
-
height: railFolded ? "auto" :
|
|
5212
|
-
padding: railFolded ?
|
|
5228
|
+
gap: railFolded ? GNB_COMMON.buttonGap : H.topGap,
|
|
5229
|
+
height: railFolded ? "auto" : H.topHeight,
|
|
5230
|
+
padding: railFolded ? `${GNB_COMMON.buttonGap} 0` : `0 ${H.topPaddingX}`,
|
|
5213
5231
|
borderBottom: `1px solid ${C.topBorder}`,
|
|
5214
5232
|
backgroundColor: C.panelBg,
|
|
5215
|
-
|
|
5233
|
+
pointerEvents: "auto",
|
|
5234
|
+
// 아이콘 버튼은 자체 ghostButton 토큰을 쓰므로, 이 색은 logo 슬롯 상속용
|
|
5235
|
+
color: C.itemColorDefault
|
|
5216
5236
|
},
|
|
5217
5237
|
children: [
|
|
5218
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5219
|
-
|
|
5238
|
+
onLauncherClick && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5239
|
+
SGhostButton,
|
|
5220
5240
|
{
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5241
|
+
icon: "launcher",
|
|
5242
|
+
size: "xs",
|
|
5243
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
5244
|
+
ariaLabel: "app launcher",
|
|
5224
5245
|
onClick: onLauncherClick,
|
|
5225
|
-
|
|
5246
|
+
className: "shrink-0"
|
|
5226
5247
|
}
|
|
5227
5248
|
),
|
|
5228
|
-
!railFolded && logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", style: { height:
|
|
5229
|
-
!railFolded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: search }),
|
|
5249
|
+
!railFolded && logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo }),
|
|
5230
5250
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5231
|
-
|
|
5251
|
+
SGhostButton,
|
|
5232
5252
|
{
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
"
|
|
5236
|
-
|
|
5253
|
+
icon: "sidebar",
|
|
5254
|
+
size: "xs",
|
|
5255
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
5256
|
+
ariaLabel: "toggle navigation",
|
|
5257
|
+
ariaPressed: folded,
|
|
5237
5258
|
onClick: handleFoldToggle,
|
|
5238
|
-
|
|
5259
|
+
className: cn("shrink-0", !railFolded && "ml-auto")
|
|
5239
5260
|
}
|
|
5240
5261
|
)
|
|
5241
5262
|
]
|
|
5242
5263
|
}
|
|
5243
5264
|
),
|
|
5244
5265
|
!folded && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5245
|
-
"
|
|
5266
|
+
"nav",
|
|
5246
5267
|
{
|
|
5247
|
-
|
|
5268
|
+
"aria-label": ariaLabel,
|
|
5248
5269
|
className: "flex flex-col overflow-y-auto",
|
|
5249
5270
|
style: {
|
|
5250
5271
|
width: GNB_MENU_WIDTH,
|
|
@@ -5253,11 +5274,12 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5253
5274
|
flexShrink: 1,
|
|
5254
5275
|
flexBasis: "auto",
|
|
5255
5276
|
minHeight: 0,
|
|
5256
|
-
paddingTop:
|
|
5257
|
-
paddingBottom:
|
|
5258
|
-
backgroundColor: C.panelBg
|
|
5277
|
+
paddingTop: M.bodyPaddingY,
|
|
5278
|
+
paddingBottom: M.bodyPaddingY,
|
|
5279
|
+
backgroundColor: C.panelBg,
|
|
5280
|
+
pointerEvents: "auto"
|
|
5259
5281
|
},
|
|
5260
|
-
children: items.map((item) => renderItem(item, 1))
|
|
5282
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: items.map((item) => renderItem(item, 1)) })
|
|
5261
5283
|
}
|
|
5262
5284
|
)
|
|
5263
5285
|
]
|
|
@@ -5266,7 +5288,7 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5266
5288
|
});
|
|
5267
5289
|
var SPage = react.forwardRef(function SPage2({ className, style, children, ...rest }, ref) {
|
|
5268
5290
|
const layout = react.useContext(SLayoutContext);
|
|
5269
|
-
const gridStyle = layout?.
|
|
5291
|
+
const gridStyle = layout?.header === "full" ? { gridColumn: 2, gridRow: 2 } : void 0;
|
|
5270
5292
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5271
5293
|
"main",
|
|
5272
5294
|
{
|
|
@@ -6638,7 +6660,7 @@ var STable = react.forwardRef(function STable2({
|
|
|
6638
6660
|
ref: scrollRef,
|
|
6639
6661
|
onScroll: handleScroll,
|
|
6640
6662
|
className: cn(
|
|
6641
|
-
"relative overflow-auto border border-solid border-[color:var(--cmp-table-border-color)]",
|
|
6663
|
+
"bg-white relative overflow-auto border border-solid border-[color:var(--cmp-table-border-color)]",
|
|
6642
6664
|
radiusClass,
|
|
6643
6665
|
// 원본 sd-table__scroll-container--loading/--no-data: 로딩·데이터 없음 시 스크롤·인터랙션 차단
|
|
6644
6666
|
(isLoading || rowCount === 0) && "overflow-hidden pointer-events-none"
|
|
@@ -9812,13 +9834,862 @@ var SBarcodeInput = react.forwardRef(
|
|
|
9812
9834
|
}
|
|
9813
9835
|
);
|
|
9814
9836
|
|
|
9837
|
+
// src/components/STimePicker/timepicker.config.ts
|
|
9838
|
+
var V2 = (path) => `var(--cmp-timepicker-${path})`;
|
|
9839
|
+
var TIMEPICKER_SIZES = ["sm", "md"];
|
|
9840
|
+
var TIMEPICKER_SIZE_CONFIG = {
|
|
9841
|
+
sm: {
|
|
9842
|
+
height: V2("sm-height"),
|
|
9843
|
+
paddingX: V2("sm-paddingX"),
|
|
9844
|
+
gap: V2("sm-gap"),
|
|
9845
|
+
icon: V2("sm-icon"),
|
|
9846
|
+
radius: V2("sm-radius"),
|
|
9847
|
+
// TODO: component.timepicker 토큰에 typography가 추가되면 CSS 변수로 교체한다.
|
|
9848
|
+
fontSize: 12,
|
|
9849
|
+
lineHeight: 20,
|
|
9850
|
+
minWidth: 80,
|
|
9851
|
+
fieldMinWidth: 128
|
|
9852
|
+
},
|
|
9853
|
+
md: {
|
|
9854
|
+
height: V2("md-height"),
|
|
9855
|
+
paddingX: V2("md-paddingX"),
|
|
9856
|
+
gap: V2("md-gap"),
|
|
9857
|
+
icon: V2("md-icon"),
|
|
9858
|
+
radius: V2("md-radius"),
|
|
9859
|
+
// TODO: component.timepicker 토큰에 typography가 추가되면 CSS 변수로 교체한다.
|
|
9860
|
+
fontSize: 14,
|
|
9861
|
+
lineHeight: 24,
|
|
9862
|
+
minWidth: 96,
|
|
9863
|
+
fieldMinWidth: 160
|
|
9864
|
+
}
|
|
9865
|
+
};
|
|
9866
|
+
function clampTimePart(value, min, max) {
|
|
9867
|
+
if (!Number.isFinite(value)) return min;
|
|
9868
|
+
return Math.min(Math.max(value, min), max);
|
|
9869
|
+
}
|
|
9870
|
+
function padTimePart(value) {
|
|
9871
|
+
return String(value).padStart(2, "0");
|
|
9872
|
+
}
|
|
9873
|
+
function parseTimeValue(value) {
|
|
9874
|
+
const match = /^(\d{1,2}):(\d{1,2})$/.exec(value ?? "");
|
|
9875
|
+
if (!match) return { hour: 0, minute: 0 };
|
|
9876
|
+
return {
|
|
9877
|
+
hour: clampTimePart(Number(match[1]), 0, 23),
|
|
9878
|
+
minute: clampTimePart(Number(match[2]), 0, 59)
|
|
9879
|
+
};
|
|
9880
|
+
}
|
|
9881
|
+
function formatTimeValue(hour, minute) {
|
|
9882
|
+
return `${padTimePart(clampTimePart(hour, 0, 23))}:${padTimePart(clampTimePart(minute, 0, 59))}`;
|
|
9883
|
+
}
|
|
9884
|
+
function formatTimeDisplay(value, type) {
|
|
9885
|
+
if (value == null || value === "") return "";
|
|
9886
|
+
const { hour, minute } = parseTimeValue(value);
|
|
9887
|
+
if (type === "default") return formatTimeValue(hour, minute);
|
|
9888
|
+
const meridiem = hour >= 12 ? "\uC624\uD6C4" : "\uC624\uC804";
|
|
9889
|
+
const displayHour = hour % 12 === 0 ? 12 : hour % 12;
|
|
9890
|
+
return `${meridiem} ${padTimePart(displayHour)}:${padTimePart(minute)}`;
|
|
9891
|
+
}
|
|
9892
|
+
function TimeSelector({
|
|
9893
|
+
value,
|
|
9894
|
+
useMeridiem,
|
|
9895
|
+
minuteStep,
|
|
9896
|
+
onChange
|
|
9897
|
+
}) {
|
|
9898
|
+
const parsed = react.useMemo(() => parseTimeValue(value), [value]);
|
|
9899
|
+
const [hour, setHour] = react.useState(parsed.hour);
|
|
9900
|
+
const [minute, setMinute] = react.useState(parsed.minute);
|
|
9901
|
+
const [editingPart, setEditingPart] = react.useState(null);
|
|
9902
|
+
const [inputDraft, setInputDraft] = react.useState({});
|
|
9903
|
+
react.useEffect(() => {
|
|
9904
|
+
setHour(parsed.hour);
|
|
9905
|
+
setMinute(parsed.minute);
|
|
9906
|
+
}, [parsed.hour, parsed.minute]);
|
|
9907
|
+
const meridiem = hour >= 12 ? "PM" : "AM";
|
|
9908
|
+
const displayHour = useMeridiem ? hour % 12 === 0 ? 12 : hour % 12 : hour;
|
|
9909
|
+
const commit = (nextHour, nextMinute) => {
|
|
9910
|
+
const h = clampTimePart(nextHour, 0, 23);
|
|
9911
|
+
const m = clampTimePart(nextMinute, 0, 59);
|
|
9912
|
+
setHour(h);
|
|
9913
|
+
setMinute(m);
|
|
9914
|
+
onChange(formatTimeValue(h, m));
|
|
9915
|
+
};
|
|
9916
|
+
const setMeridiem = (next) => {
|
|
9917
|
+
if (next === meridiem) return;
|
|
9918
|
+
commit(next === "AM" ? hour - 12 : hour + 12, minute);
|
|
9919
|
+
};
|
|
9920
|
+
const changeMinute = (delta) => {
|
|
9921
|
+
const next = minute + delta;
|
|
9922
|
+
if (next > 59) {
|
|
9923
|
+
commit((hour + 1) % 24, next - 60);
|
|
9924
|
+
return;
|
|
9925
|
+
}
|
|
9926
|
+
if (next < 0) {
|
|
9927
|
+
commit((hour + 23) % 24, next + 60);
|
|
9928
|
+
return;
|
|
9929
|
+
}
|
|
9930
|
+
commit(hour, next);
|
|
9931
|
+
};
|
|
9932
|
+
const changeHour = (delta) => commit((hour + delta + 24) % 24, minute);
|
|
9933
|
+
const sanitizeInput = (next) => next.replace(/\D/g, "").slice(0, 2);
|
|
9934
|
+
const beginEdit = (part, currentValue) => {
|
|
9935
|
+
setEditingPart(part);
|
|
9936
|
+
setInputDraft((prev) => ({ ...prev, [part]: String(currentValue).padStart(2, "0") }));
|
|
9937
|
+
};
|
|
9938
|
+
const commitHourDraft = (draft) => {
|
|
9939
|
+
const clamped = clampTimePart(Number(draft), useMeridiem ? 1 : 0, useMeridiem ? 12 : 23);
|
|
9940
|
+
const nextHour = useMeridiem ? meridiem === "PM" ? clamped === 12 ? 12 : clamped + 12 : clamped === 12 ? 0 : clamped : clamped;
|
|
9941
|
+
commit(nextHour, minute);
|
|
9942
|
+
return clamped;
|
|
9943
|
+
};
|
|
9944
|
+
const commitMinuteDraft = (draft) => {
|
|
9945
|
+
const clamped = clampTimePart(Number(draft), 0, 59);
|
|
9946
|
+
commit(hour, clamped);
|
|
9947
|
+
return clamped;
|
|
9948
|
+
};
|
|
9949
|
+
const endEdit = (part) => {
|
|
9950
|
+
const draft = inputDraft[part];
|
|
9951
|
+
if (part === "hour" && draft != null && draft !== "") {
|
|
9952
|
+
commitHourDraft(draft);
|
|
9953
|
+
}
|
|
9954
|
+
if (part === "minute" && draft != null && draft !== "") {
|
|
9955
|
+
commitMinuteDraft(draft);
|
|
9956
|
+
}
|
|
9957
|
+
setEditingPart(null);
|
|
9958
|
+
setInputDraft({});
|
|
9959
|
+
};
|
|
9960
|
+
const inputStyle = {
|
|
9961
|
+
width: "var(--cmp-timepicker-selector-input-width)",
|
|
9962
|
+
height: "var(--cmp-textinput-sm-height)",
|
|
9963
|
+
borderRadius: "var(--cmp-textinput-sm-radius)",
|
|
9964
|
+
border: "var(--cmp-textinput-borderWidth) solid var(--cmp-textinput-border-default)",
|
|
9965
|
+
color: "var(--cmp-textinput-text-default)",
|
|
9966
|
+
fontSize: 12,
|
|
9967
|
+
lineHeight: "20px"
|
|
9968
|
+
};
|
|
9969
|
+
const renderColumn = (type) => {
|
|
9970
|
+
const isHour = type === "hour";
|
|
9971
|
+
const val = isHour ? displayHour : minute;
|
|
9972
|
+
const displayValue = editingPart === type ? inputDraft[type] ?? "" : String(val).padStart(2, "0");
|
|
9973
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9974
|
+
"div",
|
|
9975
|
+
{
|
|
9976
|
+
className: "flex flex-col items-center justify-center",
|
|
9977
|
+
style: { gap: "var(--cmp-timepicker-selector-time-column-gap)" },
|
|
9978
|
+
children: [
|
|
9979
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9980
|
+
SGhostButton,
|
|
9981
|
+
{
|
|
9982
|
+
icon: "chevronUp",
|
|
9983
|
+
size: "xs",
|
|
9984
|
+
ariaLabel: `${isHour ? "\uC2DC" : "\uBD84"} \uC99D\uAC00`,
|
|
9985
|
+
onClick: () => isHour ? changeHour(1) : changeMinute(minuteStep)
|
|
9986
|
+
}
|
|
9987
|
+
),
|
|
9988
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9989
|
+
"input",
|
|
9990
|
+
{
|
|
9991
|
+
"aria-label": isHour ? "\uC2DC" : "\uBD84",
|
|
9992
|
+
inputMode: "numeric",
|
|
9993
|
+
value: displayValue,
|
|
9994
|
+
placeholder: "-",
|
|
9995
|
+
onFocus: () => beginEdit(type, val),
|
|
9996
|
+
onBlur: () => endEdit(type),
|
|
9997
|
+
onChange: (event) => {
|
|
9998
|
+
const draft = sanitizeInput(event.target.value);
|
|
9999
|
+
setInputDraft((prev) => ({ ...prev, [type]: draft }));
|
|
10000
|
+
if (draft === "") return;
|
|
10001
|
+
const raw = Number(draft);
|
|
10002
|
+
if (isHour) {
|
|
10003
|
+
if (draft.length < 2) return;
|
|
10004
|
+
const clamped2 = commitHourDraft(draft);
|
|
10005
|
+
const displayDraft2 = raw === clamped2 ? draft : String(clamped2);
|
|
10006
|
+
setInputDraft((prev) => ({ ...prev, [type]: displayDraft2 }));
|
|
10007
|
+
return;
|
|
10008
|
+
}
|
|
10009
|
+
if (draft.length < 2) return;
|
|
10010
|
+
const clamped = commitMinuteDraft(draft);
|
|
10011
|
+
const displayDraft = raw === clamped ? draft : String(clamped);
|
|
10012
|
+
setInputDraft((prev) => ({ ...prev, [type]: displayDraft }));
|
|
10013
|
+
},
|
|
10014
|
+
className: "box-border bg-white text-center outline-none",
|
|
10015
|
+
style: inputStyle
|
|
10016
|
+
}
|
|
10017
|
+
),
|
|
10018
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10019
|
+
SGhostButton,
|
|
10020
|
+
{
|
|
10021
|
+
icon: "chevronDown",
|
|
10022
|
+
size: "xs",
|
|
10023
|
+
ariaLabel: `${isHour ? "\uC2DC" : "\uBD84"} \uAC10\uC18C`,
|
|
10024
|
+
onClick: () => isHour ? changeHour(-1) : changeMinute(-minuteStep)
|
|
10025
|
+
}
|
|
10026
|
+
)
|
|
10027
|
+
]
|
|
10028
|
+
}
|
|
10029
|
+
);
|
|
10030
|
+
};
|
|
10031
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10032
|
+
"div",
|
|
10033
|
+
{
|
|
10034
|
+
className: "flex flex-col items-stretch justify-center overflow-hidden",
|
|
10035
|
+
style: {
|
|
10036
|
+
background: "var(--cmp-timepicker-selector-bg)",
|
|
10037
|
+
borderRadius: "var(--cmp-timepicker-selector-radius)",
|
|
10038
|
+
boxShadow: "4px 8px 16px -4px rgba(34,34,34,0.1), 0 0 24px -6px rgba(34,34,34,0.12)"
|
|
10039
|
+
},
|
|
10040
|
+
children: [
|
|
10041
|
+
useMeridiem && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10042
|
+
"div",
|
|
10043
|
+
{
|
|
10044
|
+
className: "flex items-center justify-center",
|
|
10045
|
+
style: {
|
|
10046
|
+
padding: "var(--cmp-timepicker-selector-section-paddingY) var(--cmp-timepicker-selector-section-paddingX)"
|
|
10047
|
+
},
|
|
10048
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "isolate flex", children: ["AM", "PM"].map((item, index) => {
|
|
10049
|
+
const selected = item === meridiem;
|
|
10050
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10051
|
+
"button",
|
|
10052
|
+
{
|
|
10053
|
+
type: "button",
|
|
10054
|
+
onClick: () => setMeridiem(item),
|
|
10055
|
+
className: cn(
|
|
10056
|
+
"box-border h-[var(--cmp-textinput-sm-height)] w-[61px] cursor-pointer border border-solid bg-white text-center text-[12px] font-medium leading-[20px]",
|
|
10057
|
+
index === 0 ? "rounded-l-[var(--cmp-select-sm-radius)]" : "rounded-r-[var(--cmp-select-sm-radius)]",
|
|
10058
|
+
index === 0 ? "-mr-px" : "",
|
|
10059
|
+
selected ? "z-[1] border-[var(--cmp-timepicker-border-focus)] text-[var(--cmp-timepicker-border-focus)]" : "border-[var(--cmp-timepicker-border-default)] text-[var(--cmp-field-hint-color)]"
|
|
10060
|
+
),
|
|
10061
|
+
children: item === "AM" ? "\uC624\uC804" : "\uC624\uD6C4"
|
|
10062
|
+
},
|
|
10063
|
+
item
|
|
10064
|
+
);
|
|
10065
|
+
}) })
|
|
10066
|
+
}
|
|
10067
|
+
),
|
|
10068
|
+
useMeridiem && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px w-full bg-[var(--sys-color-divider-default)]" }),
|
|
10069
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10070
|
+
"div",
|
|
10071
|
+
{
|
|
10072
|
+
className: "flex items-center justify-center",
|
|
10073
|
+
style: {
|
|
10074
|
+
gap: "var(--cmp-timepicker-selector-time-gap)",
|
|
10075
|
+
padding: "var(--cmp-timepicker-selector-section-paddingY) var(--cmp-timepicker-selector-section-paddingX)"
|
|
10076
|
+
},
|
|
10077
|
+
children: [
|
|
10078
|
+
renderColumn("hour"),
|
|
10079
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10080
|
+
"span",
|
|
10081
|
+
{
|
|
10082
|
+
className: "text-center",
|
|
10083
|
+
style: {
|
|
10084
|
+
width: "var(--cmp-timepicker-selector-colon-size)",
|
|
10085
|
+
color: "var(--cmp-field-hint-color)",
|
|
10086
|
+
fontSize: "var(--cmp-timepicker-selector-colon-size)",
|
|
10087
|
+
lineHeight: "var(--cmp-timepicker-selector-colon-size)"
|
|
10088
|
+
},
|
|
10089
|
+
children: ":"
|
|
10090
|
+
}
|
|
10091
|
+
),
|
|
10092
|
+
renderColumn("minute")
|
|
10093
|
+
]
|
|
10094
|
+
}
|
|
10095
|
+
)
|
|
10096
|
+
]
|
|
10097
|
+
}
|
|
10098
|
+
);
|
|
10099
|
+
}
|
|
10100
|
+
function STimePicker({
|
|
10101
|
+
value,
|
|
10102
|
+
onValueChange,
|
|
10103
|
+
onOpenChange,
|
|
10104
|
+
type = "default",
|
|
10105
|
+
size = "sm",
|
|
10106
|
+
placeholder = "00:00",
|
|
10107
|
+
disabled = false,
|
|
10108
|
+
clearable = false,
|
|
10109
|
+
useMeridiem,
|
|
10110
|
+
minuteStep = 1,
|
|
10111
|
+
width,
|
|
10112
|
+
name,
|
|
10113
|
+
rules,
|
|
10114
|
+
status,
|
|
10115
|
+
label,
|
|
10116
|
+
labelWidth,
|
|
10117
|
+
icon,
|
|
10118
|
+
iconColor,
|
|
10119
|
+
labelTooltip,
|
|
10120
|
+
labelTooltipProps,
|
|
10121
|
+
addonLabel,
|
|
10122
|
+
addonAlign,
|
|
10123
|
+
hint,
|
|
10124
|
+
error,
|
|
10125
|
+
errorMessage,
|
|
10126
|
+
className,
|
|
10127
|
+
style
|
|
10128
|
+
}) {
|
|
10129
|
+
const portalContainer = usePortalContainer();
|
|
10130
|
+
const [open, setOpen] = react.useState(false);
|
|
10131
|
+
const [focused, setFocused] = react.useState(false);
|
|
10132
|
+
const [ruleError, setRuleError] = react.useState("");
|
|
10133
|
+
const dims = TIMEPICKER_SIZE_CONFIG[size] ?? TIMEPICKER_SIZE_CONFIG.sm;
|
|
10134
|
+
const normalized = value ? formatTimeValue(parseTimeValue(value).hour, parseTimeValue(value).minute) : null;
|
|
10135
|
+
const effectiveUseMeridiem = useMeridiem ?? type === "midday";
|
|
10136
|
+
const displayText = formatTimeDisplay(normalized, effectiveUseMeridiem ? "midday" : "default");
|
|
10137
|
+
const hasValue = normalized != null && normalized !== "";
|
|
10138
|
+
useFormField({
|
|
10139
|
+
name,
|
|
10140
|
+
validate: () => {
|
|
10141
|
+
const msg = runRules(value, rules);
|
|
10142
|
+
setRuleError(msg);
|
|
10143
|
+
return msg === "";
|
|
10144
|
+
},
|
|
10145
|
+
resetValidation: () => setRuleError("")
|
|
10146
|
+
});
|
|
10147
|
+
const resolvedError = error || ruleError !== "";
|
|
10148
|
+
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
10149
|
+
const commitOpen = (next) => {
|
|
10150
|
+
if (disabled) return;
|
|
10151
|
+
setOpen(next);
|
|
10152
|
+
onOpenChange?.(next);
|
|
10153
|
+
if (!next && rules && rules.length > 0) setRuleError(runRules(value, rules));
|
|
10154
|
+
};
|
|
10155
|
+
const commitValue = (next) => {
|
|
10156
|
+
onValueChange?.(next);
|
|
10157
|
+
if (rules && rules.length > 0) setRuleError(runRules(next, rules));
|
|
10158
|
+
};
|
|
10159
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10160
|
+
SField,
|
|
10161
|
+
{
|
|
10162
|
+
label,
|
|
10163
|
+
labelWidth,
|
|
10164
|
+
icon,
|
|
10165
|
+
iconColor,
|
|
10166
|
+
labelTooltip,
|
|
10167
|
+
labelTooltipProps,
|
|
10168
|
+
addonLabel,
|
|
10169
|
+
addonAlign,
|
|
10170
|
+
size,
|
|
10171
|
+
hint,
|
|
10172
|
+
error: resolvedError,
|
|
10173
|
+
status: resolvedStatus,
|
|
10174
|
+
focused: focused || open,
|
|
10175
|
+
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
10176
|
+
width,
|
|
10177
|
+
disabled,
|
|
10178
|
+
className,
|
|
10179
|
+
style: { minWidth: dims.fieldMinWidth, ...style },
|
|
10180
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: commitOpen, children: [
|
|
10181
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10182
|
+
"button",
|
|
10183
|
+
{
|
|
10184
|
+
type: "button",
|
|
10185
|
+
name,
|
|
10186
|
+
disabled,
|
|
10187
|
+
onFocus: () => setFocused(true),
|
|
10188
|
+
onBlur: () => setFocused(false),
|
|
10189
|
+
className: "flex h-full w-full items-center overflow-hidden bg-transparent outline-none disabled:cursor-not-allowed",
|
|
10190
|
+
style: { padding: `0 ${dims.paddingX}`, gap: dims.gap, borderRadius: dims.radius },
|
|
10191
|
+
children: [
|
|
10192
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10193
|
+
SIcon,
|
|
10194
|
+
{
|
|
10195
|
+
name: "clockOutline",
|
|
10196
|
+
size: dims.icon,
|
|
10197
|
+
color: disabled ? "var(--cmp-timepicker-icon-disabled)" : "var(--cmp-timepicker-icon-default)",
|
|
10198
|
+
className: "flex-shrink-0"
|
|
10199
|
+
}
|
|
10200
|
+
),
|
|
10201
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10202
|
+
"span",
|
|
10203
|
+
{
|
|
10204
|
+
className: "flex-1 truncate text-center",
|
|
10205
|
+
style: {
|
|
10206
|
+
minWidth: dims.minWidth,
|
|
10207
|
+
fontSize: dims.fontSize,
|
|
10208
|
+
lineHeight: `${dims.lineHeight}px`,
|
|
10209
|
+
color: disabled ? "var(--cmp-timepicker-text-disabled)" : hasValue ? "var(--cmp-timepicker-text-default)" : "var(--cmp-field-hint-color)"
|
|
10210
|
+
},
|
|
10211
|
+
children: hasValue ? displayText : placeholder
|
|
10212
|
+
}
|
|
10213
|
+
),
|
|
10214
|
+
clearable && hasValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10215
|
+
SGhostButton,
|
|
10216
|
+
{
|
|
10217
|
+
icon: "close",
|
|
10218
|
+
size: "xs",
|
|
10219
|
+
ariaLabel: "\uC2DC\uAC04 \uC9C0\uC6B0\uAE30",
|
|
10220
|
+
onClick: (event) => {
|
|
10221
|
+
event.stopPropagation();
|
|
10222
|
+
commitValue(null);
|
|
10223
|
+
}
|
|
10224
|
+
}
|
|
10225
|
+
)
|
|
10226
|
+
]
|
|
10227
|
+
}
|
|
10228
|
+
) }),
|
|
10229
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10230
|
+
RPopover2__namespace.Content,
|
|
10231
|
+
{
|
|
10232
|
+
side: "bottom",
|
|
10233
|
+
align: "start",
|
|
10234
|
+
sideOffset: 4,
|
|
10235
|
+
className: cn("z-50", FLOATING_SLIDE_ANIM),
|
|
10236
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10237
|
+
TimeSelector,
|
|
10238
|
+
{
|
|
10239
|
+
value: normalized,
|
|
10240
|
+
useMeridiem: effectiveUseMeridiem,
|
|
10241
|
+
minuteStep,
|
|
10242
|
+
onChange: commitValue
|
|
10243
|
+
}
|
|
10244
|
+
)
|
|
10245
|
+
}
|
|
10246
|
+
) })
|
|
10247
|
+
] })
|
|
10248
|
+
}
|
|
10249
|
+
);
|
|
10250
|
+
}
|
|
10251
|
+
|
|
10252
|
+
// src/components/STimeRangePicker/time-range-picker.config.ts
|
|
10253
|
+
function getTimeMinutes(value) {
|
|
10254
|
+
const { hour, minute } = parseTimeValue(value);
|
|
10255
|
+
return hour * 60 + minute;
|
|
10256
|
+
}
|
|
10257
|
+
function normalizeTimeRangeValue(value, rangeOrder = "strict") {
|
|
10258
|
+
if (value == null) return null;
|
|
10259
|
+
const normalized = value.map((item) => {
|
|
10260
|
+
const parsed = parseTimeValue(item);
|
|
10261
|
+
return formatTimeValue(parsed.hour, parsed.minute);
|
|
10262
|
+
});
|
|
10263
|
+
if (rangeOrder === "strict" && getTimeMinutes(normalized[0]) > getTimeMinutes(normalized[1])) {
|
|
10264
|
+
return [normalized[0], normalized[0]];
|
|
10265
|
+
}
|
|
10266
|
+
return normalized;
|
|
10267
|
+
}
|
|
10268
|
+
function formatTimeRangeDisplay(value, type) {
|
|
10269
|
+
if (value == null || value[0] === "" || value[1] === "") return "";
|
|
10270
|
+
return `${formatTimeDisplay(value[0], type)} ~ ${formatTimeDisplay(value[1], type)}`;
|
|
10271
|
+
}
|
|
10272
|
+
function RangeTimeSelector({
|
|
10273
|
+
value,
|
|
10274
|
+
useMeridiem,
|
|
10275
|
+
minuteStep,
|
|
10276
|
+
rangeOrder,
|
|
10277
|
+
onChange
|
|
10278
|
+
}) {
|
|
10279
|
+
const range = react.useMemo(() => value ?? ["00:00", "23:59"], [value]);
|
|
10280
|
+
const [editingKey, setEditingKey] = react.useState(null);
|
|
10281
|
+
const [inputDraft, setInputDraft] = react.useState({});
|
|
10282
|
+
const autoAdjustedRef = react.useRef(null);
|
|
10283
|
+
const getParts = (side) => parseTimeValue(side === "start" ? range[0] : range[1]);
|
|
10284
|
+
const getMeridiem = (side) => getParts(side).hour >= 12 ? "PM" : "AM";
|
|
10285
|
+
const getDisplayHour = (side) => {
|
|
10286
|
+
const { hour } = getParts(side);
|
|
10287
|
+
if (!useMeridiem) return hour;
|
|
10288
|
+
const displayHour = hour % 12;
|
|
10289
|
+
return displayHour === 0 ? 12 : displayHour;
|
|
10290
|
+
};
|
|
10291
|
+
const commit = (side, hour, minute, direction = "unknown") => {
|
|
10292
|
+
const next = formatTimeValue(hour, minute);
|
|
10293
|
+
onChange(normalizeNextRange(side === "start" ? [next, range[1]] : [range[0], next], side, direction));
|
|
10294
|
+
};
|
|
10295
|
+
const getTimeMinutes2 = (time) => {
|
|
10296
|
+
const { hour, minute } = parseTimeValue(time);
|
|
10297
|
+
return hour * 60 + minute;
|
|
10298
|
+
};
|
|
10299
|
+
const normalizeNextRange = (nextRange, changedSide, direction) => {
|
|
10300
|
+
if (rangeOrder === "allow-cross-day") return nextRange;
|
|
10301
|
+
const [start, end] = nextRange;
|
|
10302
|
+
const [currentStart, currentEnd] = range;
|
|
10303
|
+
if (getTimeMinutes2(start) <= getTimeMinutes2(end)) {
|
|
10304
|
+
const shouldKeepSynced = autoAdjustedRef.current?.side === changedSide && currentStart === currentEnd && start !== end && (direction === "unknown" || autoAdjustedRef.current.direction === direction);
|
|
10305
|
+
if (shouldKeepSynced) {
|
|
10306
|
+
return changedSide === "start" ? [start, start] : [end, end];
|
|
10307
|
+
}
|
|
10308
|
+
autoAdjustedRef.current = null;
|
|
10309
|
+
return nextRange;
|
|
10310
|
+
}
|
|
10311
|
+
autoAdjustedRef.current = {
|
|
10312
|
+
side: changedSide,
|
|
10313
|
+
direction: changedSide === "start" ? "increase" : "decrease"
|
|
10314
|
+
};
|
|
10315
|
+
return changedSide === "start" ? [start, start] : [end, end];
|
|
10316
|
+
};
|
|
10317
|
+
const changeHour = (side, delta) => {
|
|
10318
|
+
const { hour, minute } = getParts(side);
|
|
10319
|
+
commit(side, (hour + delta + 24) % 24, minute, delta > 0 ? "increase" : "decrease");
|
|
10320
|
+
};
|
|
10321
|
+
const changeMinute = (side, delta) => {
|
|
10322
|
+
const { hour, minute } = getParts(side);
|
|
10323
|
+
const next = minute + delta;
|
|
10324
|
+
if (next > 59) {
|
|
10325
|
+
commit(side, (hour + 1) % 24, next - 60, delta > 0 ? "increase" : "decrease");
|
|
10326
|
+
return;
|
|
10327
|
+
}
|
|
10328
|
+
if (next < 0) {
|
|
10329
|
+
commit(side, (hour + 23) % 24, next + 60, delta > 0 ? "increase" : "decrease");
|
|
10330
|
+
return;
|
|
10331
|
+
}
|
|
10332
|
+
commit(side, hour, next, delta > 0 ? "increase" : "decrease");
|
|
10333
|
+
};
|
|
10334
|
+
const setMeridiem = (side, next) => {
|
|
10335
|
+
const current = getMeridiem(side);
|
|
10336
|
+
if (current === next) return;
|
|
10337
|
+
const { hour, minute } = getParts(side);
|
|
10338
|
+
commit(side, next === "AM" ? hour - 12 : hour + 12, minute);
|
|
10339
|
+
};
|
|
10340
|
+
const getEditingKey = (side, part) => `${side}-${part}`;
|
|
10341
|
+
const sanitizeInput = (next) => next.replace(/\D/g, "").slice(0, 2);
|
|
10342
|
+
const beginEdit = (side, part, currentValue) => {
|
|
10343
|
+
const key = getEditingKey(side, part);
|
|
10344
|
+
setEditingKey(key);
|
|
10345
|
+
setInputDraft((prev) => ({ ...prev, [key]: String(currentValue).padStart(2, "0") }));
|
|
10346
|
+
};
|
|
10347
|
+
const commitHourDraft = (side, draft) => {
|
|
10348
|
+
const clamped = clampTimePart(Number(draft), useMeridiem ? 1 : 0, useMeridiem ? 12 : 23);
|
|
10349
|
+
const parts = getParts(side);
|
|
10350
|
+
const meridiem = getMeridiem(side);
|
|
10351
|
+
const nextHour = useMeridiem ? meridiem === "PM" ? clamped === 12 ? 12 : clamped + 12 : clamped === 12 ? 0 : clamped : clamped;
|
|
10352
|
+
commit(side, nextHour, parts.minute);
|
|
10353
|
+
return clamped;
|
|
10354
|
+
};
|
|
10355
|
+
const commitMinuteDraft = (side, draft) => {
|
|
10356
|
+
const { hour } = getParts(side);
|
|
10357
|
+
const clamped = clampTimePart(Number(draft), 0, 59);
|
|
10358
|
+
commit(side, hour, clamped);
|
|
10359
|
+
return clamped;
|
|
10360
|
+
};
|
|
10361
|
+
const endEdit = (side, part) => {
|
|
10362
|
+
const key = getEditingKey(side, part);
|
|
10363
|
+
const draft = inputDraft[key];
|
|
10364
|
+
if (part === "hour" && draft != null && draft !== "") {
|
|
10365
|
+
commitHourDraft(side, draft);
|
|
10366
|
+
}
|
|
10367
|
+
if (part === "minute" && draft != null && draft !== "") {
|
|
10368
|
+
commitMinuteDraft(side, draft);
|
|
10369
|
+
}
|
|
10370
|
+
setEditingKey(null);
|
|
10371
|
+
setInputDraft({});
|
|
10372
|
+
};
|
|
10373
|
+
const inputStyle = {
|
|
10374
|
+
width: "var(--cmp-timepicker-selector-input-width)",
|
|
10375
|
+
height: "var(--cmp-textinput-sm-height)",
|
|
10376
|
+
borderRadius: "var(--cmp-textinput-sm-radius)",
|
|
10377
|
+
border: "var(--cmp-textinput-borderWidth) solid var(--cmp-textinput-border-default)",
|
|
10378
|
+
color: "var(--cmp-textinput-text-default)",
|
|
10379
|
+
fontSize: 12,
|
|
10380
|
+
lineHeight: "20px"
|
|
10381
|
+
};
|
|
10382
|
+
const renderColumn = (side, kind) => {
|
|
10383
|
+
const isHour = kind === "hour";
|
|
10384
|
+
const { minute } = getParts(side);
|
|
10385
|
+
const val = isHour ? getDisplayHour(side) : minute;
|
|
10386
|
+
const currentEditingKey = getEditingKey(side, kind);
|
|
10387
|
+
const displayValue = editingKey === currentEditingKey ? inputDraft[currentEditingKey] ?? "" : String(val).padStart(2, "0");
|
|
10388
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10389
|
+
"div",
|
|
10390
|
+
{
|
|
10391
|
+
className: "flex flex-col items-center justify-center",
|
|
10392
|
+
style: { gap: "var(--cmp-timepicker-selector-time-column-gap)" },
|
|
10393
|
+
children: [
|
|
10394
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10395
|
+
SGhostButton,
|
|
10396
|
+
{
|
|
10397
|
+
icon: "chevronUp",
|
|
10398
|
+
size: "xs",
|
|
10399
|
+
ariaLabel: `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"} \uC99D\uAC00`,
|
|
10400
|
+
onClick: () => isHour ? changeHour(side, 1) : changeMinute(side, minuteStep)
|
|
10401
|
+
}
|
|
10402
|
+
),
|
|
10403
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10404
|
+
"input",
|
|
10405
|
+
{
|
|
10406
|
+
"aria-label": `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"}`,
|
|
10407
|
+
inputMode: "numeric",
|
|
10408
|
+
value: displayValue,
|
|
10409
|
+
placeholder: "-",
|
|
10410
|
+
onFocus: () => beginEdit(side, kind, val),
|
|
10411
|
+
onBlur: () => endEdit(side, kind),
|
|
10412
|
+
onChange: (event) => {
|
|
10413
|
+
const draft = sanitizeInput(event.target.value);
|
|
10414
|
+
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: draft }));
|
|
10415
|
+
if (draft === "") return;
|
|
10416
|
+
const raw = Number(draft);
|
|
10417
|
+
if (isHour) {
|
|
10418
|
+
if (draft.length < 2) return;
|
|
10419
|
+
const clamped2 = commitHourDraft(side, draft);
|
|
10420
|
+
const displayDraft2 = raw === clamped2 ? draft : String(clamped2);
|
|
10421
|
+
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: displayDraft2 }));
|
|
10422
|
+
return;
|
|
10423
|
+
}
|
|
10424
|
+
if (draft.length < 2) return;
|
|
10425
|
+
const clamped = commitMinuteDraft(side, draft);
|
|
10426
|
+
const displayDraft = raw === clamped ? draft : String(clamped);
|
|
10427
|
+
setInputDraft((prev) => ({ ...prev, [currentEditingKey]: displayDraft }));
|
|
10428
|
+
},
|
|
10429
|
+
className: "box-border bg-white text-center outline-none",
|
|
10430
|
+
style: inputStyle
|
|
10431
|
+
}
|
|
10432
|
+
),
|
|
10433
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10434
|
+
SGhostButton,
|
|
10435
|
+
{
|
|
10436
|
+
icon: "chevronDown",
|
|
10437
|
+
size: "xs",
|
|
10438
|
+
ariaLabel: `${side === "start" ? "\uC2DC\uC791" : "\uC885\uB8CC"} ${isHour ? "\uC2DC" : "\uBD84"} \uAC10\uC18C`,
|
|
10439
|
+
onClick: () => isHour ? changeHour(side, -1) : changeMinute(side, -minuteStep)
|
|
10440
|
+
}
|
|
10441
|
+
)
|
|
10442
|
+
]
|
|
10443
|
+
}
|
|
10444
|
+
);
|
|
10445
|
+
};
|
|
10446
|
+
const renderPanel = (side, title) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10447
|
+
"div",
|
|
10448
|
+
{
|
|
10449
|
+
className: "flex flex-col items-center justify-center",
|
|
10450
|
+
style: { gap: "var(--cmp-timepicker-selector-range-section-gap)" },
|
|
10451
|
+
children: [
|
|
10452
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10453
|
+
"div",
|
|
10454
|
+
{
|
|
10455
|
+
className: "flex flex-col items-center",
|
|
10456
|
+
style: { gap: "var(--cmp-timepicker-selector-range-heading-gap)" },
|
|
10457
|
+
children: [
|
|
10458
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-nowrap text-center text-[12px] font-bold leading-[20px] text-[var(--cmp-timepicker-selector-range-heading-color)]", children: title }),
|
|
10459
|
+
useMeridiem && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "isolate flex", children: ["AM", "PM"].map((item, index) => {
|
|
10460
|
+
const selected = item === getMeridiem(side);
|
|
10461
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10462
|
+
"button",
|
|
10463
|
+
{
|
|
10464
|
+
type: "button",
|
|
10465
|
+
onClick: () => setMeridiem(side, item),
|
|
10466
|
+
className: cn(
|
|
10467
|
+
"box-border h-[var(--cmp-textinput-sm-height)] w-[61px] cursor-pointer border border-solid bg-white text-center text-[12px] font-medium leading-[20px]",
|
|
10468
|
+
index === 0 ? "rounded-l-[var(--cmp-select-sm-radius)] -mr-px" : "rounded-r-[var(--cmp-select-sm-radius)]",
|
|
10469
|
+
selected ? "z-[1] border-[var(--cmp-timepicker-border-focus)] text-[var(--cmp-timepicker-border-focus)]" : "border-[var(--cmp-timepicker-border-default)] text-[var(--cmp-field-hint-color)]"
|
|
10470
|
+
),
|
|
10471
|
+
children: item === "AM" ? "\uC624\uC804" : "\uC624\uD6C4"
|
|
10472
|
+
},
|
|
10473
|
+
item
|
|
10474
|
+
);
|
|
10475
|
+
}) })
|
|
10476
|
+
]
|
|
10477
|
+
}
|
|
10478
|
+
),
|
|
10479
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10480
|
+
"div",
|
|
10481
|
+
{
|
|
10482
|
+
className: "flex items-center justify-center",
|
|
10483
|
+
style: {
|
|
10484
|
+
gap: "var(--cmp-timepicker-selector-time-gap)",
|
|
10485
|
+
padding: "0 var(--cmp-timepicker-selector-section-paddingX)"
|
|
10486
|
+
},
|
|
10487
|
+
children: [
|
|
10488
|
+
renderColumn(side, "hour"),
|
|
10489
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10490
|
+
"span",
|
|
10491
|
+
{
|
|
10492
|
+
className: "text-center",
|
|
10493
|
+
style: {
|
|
10494
|
+
width: "var(--cmp-timepicker-selector-colon-size)",
|
|
10495
|
+
color: "var(--cmp-field-hint-color)",
|
|
10496
|
+
fontSize: "var(--cmp-timepicker-selector-colon-size)",
|
|
10497
|
+
lineHeight: "var(--cmp-timepicker-selector-colon-size)"
|
|
10498
|
+
},
|
|
10499
|
+
children: ":"
|
|
10500
|
+
}
|
|
10501
|
+
),
|
|
10502
|
+
renderColumn(side, "minute")
|
|
10503
|
+
]
|
|
10504
|
+
}
|
|
10505
|
+
)
|
|
10506
|
+
]
|
|
10507
|
+
}
|
|
10508
|
+
);
|
|
10509
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10510
|
+
"div",
|
|
10511
|
+
{
|
|
10512
|
+
className: "flex items-start justify-center overflow-hidden",
|
|
10513
|
+
style: {
|
|
10514
|
+
gap: "var(--cmp-timepicker-selector-range-panelGap)",
|
|
10515
|
+
padding: "var(--cmp-timepicker-selector-range-paddingXY)",
|
|
10516
|
+
background: "var(--cmp-timepicker-selector-bg)",
|
|
10517
|
+
borderRadius: "var(--cmp-timepicker-selector-radius)",
|
|
10518
|
+
boxShadow: "var(--shadow-spread-sm), var(--shadow-spread-md)"
|
|
10519
|
+
},
|
|
10520
|
+
children: [
|
|
10521
|
+
renderPanel("start", "\uC2DC\uC791 \uC2DC\uAC04"),
|
|
10522
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "self-stretch w-px bg-[var(--sys-color-divider-default)]" }),
|
|
10523
|
+
renderPanel("end", "\uC885\uB8CC \uC2DC\uAC04")
|
|
10524
|
+
]
|
|
10525
|
+
}
|
|
10526
|
+
);
|
|
10527
|
+
}
|
|
10528
|
+
function STimeRangePicker({
|
|
10529
|
+
value,
|
|
10530
|
+
onValueChange,
|
|
10531
|
+
onOpenChange,
|
|
10532
|
+
type = "default",
|
|
10533
|
+
size = "sm",
|
|
10534
|
+
placeholder = "00:00 ~ 23:59",
|
|
10535
|
+
disabled = false,
|
|
10536
|
+
clearable = false,
|
|
10537
|
+
useMeridiem,
|
|
10538
|
+
minuteStep = 1,
|
|
10539
|
+
rangeOrder = "strict",
|
|
10540
|
+
width,
|
|
10541
|
+
name,
|
|
10542
|
+
rules,
|
|
10543
|
+
status,
|
|
10544
|
+
label,
|
|
10545
|
+
labelWidth,
|
|
10546
|
+
icon,
|
|
10547
|
+
iconColor,
|
|
10548
|
+
labelTooltip,
|
|
10549
|
+
labelTooltipProps,
|
|
10550
|
+
addonLabel,
|
|
10551
|
+
addonAlign,
|
|
10552
|
+
hint,
|
|
10553
|
+
error,
|
|
10554
|
+
errorMessage,
|
|
10555
|
+
className,
|
|
10556
|
+
style
|
|
10557
|
+
}) {
|
|
10558
|
+
const portalContainer = usePortalContainer();
|
|
10559
|
+
const [open, setOpen] = react.useState(false);
|
|
10560
|
+
const [focused, setFocused] = react.useState(false);
|
|
10561
|
+
const [ruleError, setRuleError] = react.useState("");
|
|
10562
|
+
const dims = TIMEPICKER_SIZE_CONFIG[size] ?? TIMEPICKER_SIZE_CONFIG.sm;
|
|
10563
|
+
const normalized = normalizeTimeRangeValue(value ?? null, rangeOrder);
|
|
10564
|
+
const effectiveUseMeridiem = useMeridiem ?? type === "midday";
|
|
10565
|
+
const displayText = formatTimeRangeDisplay(
|
|
10566
|
+
normalized,
|
|
10567
|
+
effectiveUseMeridiem ? "midday" : "default"
|
|
10568
|
+
);
|
|
10569
|
+
const hasValue = normalized != null && normalized[0] !== "" && normalized[1] !== "";
|
|
10570
|
+
useFormField({
|
|
10571
|
+
name,
|
|
10572
|
+
validate: () => {
|
|
10573
|
+
const msg = runRules(value, rules);
|
|
10574
|
+
setRuleError(msg);
|
|
10575
|
+
return msg === "";
|
|
10576
|
+
},
|
|
10577
|
+
resetValidation: () => setRuleError("")
|
|
10578
|
+
});
|
|
10579
|
+
const resolvedError = error || ruleError !== "";
|
|
10580
|
+
const resolvedStatus = status ?? (rules && rules.length > 0 ? ruleError !== "" ? "error" : void 0 : void 0);
|
|
10581
|
+
const commitOpen = (next) => {
|
|
10582
|
+
if (disabled) return;
|
|
10583
|
+
setOpen(next);
|
|
10584
|
+
onOpenChange?.(next);
|
|
10585
|
+
if (!next && rules && rules.length > 0) setRuleError(runRules(value, rules));
|
|
10586
|
+
};
|
|
10587
|
+
const commitValue = (next) => {
|
|
10588
|
+
onValueChange?.(next);
|
|
10589
|
+
if (rules && rules.length > 0) setRuleError(runRules(next, rules));
|
|
10590
|
+
};
|
|
10591
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10592
|
+
SField,
|
|
10593
|
+
{
|
|
10594
|
+
label,
|
|
10595
|
+
labelWidth,
|
|
10596
|
+
icon,
|
|
10597
|
+
iconColor,
|
|
10598
|
+
labelTooltip,
|
|
10599
|
+
labelTooltipProps,
|
|
10600
|
+
addonLabel,
|
|
10601
|
+
addonAlign,
|
|
10602
|
+
size,
|
|
10603
|
+
hint,
|
|
10604
|
+
error: resolvedError,
|
|
10605
|
+
status: resolvedStatus,
|
|
10606
|
+
focused: focused || open,
|
|
10607
|
+
errorMessage: ruleError !== "" ? ruleError : errorMessage,
|
|
10608
|
+
width,
|
|
10609
|
+
disabled,
|
|
10610
|
+
className,
|
|
10611
|
+
style: { minWidth: dims.fieldMinWidth, ...style },
|
|
10612
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(RPopover2__namespace.Root, { open, onOpenChange: commitOpen, children: [
|
|
10613
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10614
|
+
"button",
|
|
10615
|
+
{
|
|
10616
|
+
type: "button",
|
|
10617
|
+
name,
|
|
10618
|
+
disabled,
|
|
10619
|
+
onFocus: () => setFocused(true),
|
|
10620
|
+
onBlur: () => setFocused(false),
|
|
10621
|
+
className: "flex h-full w-full items-center overflow-hidden bg-transparent outline-none disabled:cursor-not-allowed",
|
|
10622
|
+
style: { padding: `0 ${dims.paddingX}`, gap: dims.gap, borderRadius: dims.radius },
|
|
10623
|
+
children: [
|
|
10624
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10625
|
+
SIcon,
|
|
10626
|
+
{
|
|
10627
|
+
name: "clockOutline",
|
|
10628
|
+
size: dims.icon,
|
|
10629
|
+
color: disabled ? "var(--cmp-timepicker-icon-disabled)" : "var(--cmp-timepicker-icon-default)",
|
|
10630
|
+
className: "flex-shrink-0"
|
|
10631
|
+
}
|
|
10632
|
+
),
|
|
10633
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10634
|
+
"span",
|
|
10635
|
+
{
|
|
10636
|
+
className: "flex-1 truncate text-center",
|
|
10637
|
+
style: {
|
|
10638
|
+
minWidth: dims.minWidth,
|
|
10639
|
+
fontSize: dims.fontSize,
|
|
10640
|
+
lineHeight: `${dims.lineHeight}px`,
|
|
10641
|
+
color: disabled ? "var(--cmp-timepicker-text-disabled)" : hasValue ? "var(--cmp-timepicker-text-default)" : "var(--cmp-field-hint-color)"
|
|
10642
|
+
},
|
|
10643
|
+
children: hasValue ? displayText : placeholder
|
|
10644
|
+
}
|
|
10645
|
+
),
|
|
10646
|
+
clearable && hasValue && !disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10647
|
+
SGhostButton,
|
|
10648
|
+
{
|
|
10649
|
+
icon: "close",
|
|
10650
|
+
size: "xs",
|
|
10651
|
+
ariaLabel: "\uC2DC\uAC04 \uBC94\uC704 \uC9C0\uC6B0\uAE30",
|
|
10652
|
+
onClick: (event) => {
|
|
10653
|
+
event.stopPropagation();
|
|
10654
|
+
commitValue(null);
|
|
10655
|
+
}
|
|
10656
|
+
}
|
|
10657
|
+
)
|
|
10658
|
+
]
|
|
10659
|
+
}
|
|
10660
|
+
) }),
|
|
10661
|
+
/* @__PURE__ */ jsxRuntime.jsx(RPopover2__namespace.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10662
|
+
RPopover2__namespace.Content,
|
|
10663
|
+
{
|
|
10664
|
+
side: "bottom",
|
|
10665
|
+
align: "start",
|
|
10666
|
+
sideOffset: 4,
|
|
10667
|
+
className: cn("z-50", FLOATING_SLIDE_ANIM),
|
|
10668
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10669
|
+
RangeTimeSelector,
|
|
10670
|
+
{
|
|
10671
|
+
value: normalized,
|
|
10672
|
+
useMeridiem: effectiveUseMeridiem,
|
|
10673
|
+
minuteStep,
|
|
10674
|
+
rangeOrder,
|
|
10675
|
+
onChange: commitValue
|
|
10676
|
+
}
|
|
10677
|
+
)
|
|
10678
|
+
}
|
|
10679
|
+
) })
|
|
10680
|
+
] })
|
|
10681
|
+
}
|
|
10682
|
+
);
|
|
10683
|
+
}
|
|
10684
|
+
|
|
9815
10685
|
exports.BADGE_COLORS = BADGE_COLORS;
|
|
9816
10686
|
exports.BUTTON_COLORS = BUTTON_COLORS;
|
|
9817
10687
|
exports.BUTTON_SIZES = BUTTON_SIZES;
|
|
9818
10688
|
exports.COLOR_KEYS = COLOR_KEYS;
|
|
9819
10689
|
exports.GNB_COLORS = GNB_COLORS;
|
|
9820
10690
|
exports.GNB_COMMON = GNB_COMMON;
|
|
9821
|
-
exports.
|
|
10691
|
+
exports.GNB_HEADER = GNB_HEADER;
|
|
10692
|
+
exports.GNB_MENU = GNB_MENU;
|
|
9822
10693
|
exports.GNB_MENU_WIDTH = GNB_MENU_WIDTH;
|
|
9823
10694
|
exports.ICONS = ICONS;
|
|
9824
10695
|
exports.SActionModal = SActionModal;
|
|
@@ -9868,6 +10739,8 @@ exports.STabs = STabs;
|
|
|
9868
10739
|
exports.STag = STag;
|
|
9869
10740
|
exports.STextLink = STextLink;
|
|
9870
10741
|
exports.STextarea = STextarea;
|
|
10742
|
+
exports.STimePicker = STimePicker;
|
|
10743
|
+
exports.STimeRangePicker = STimeRangePicker;
|
|
9871
10744
|
exports.SToast = SToast;
|
|
9872
10745
|
exports.SToastContainer = SToastContainer;
|
|
9873
10746
|
exports.SToggle = SToggle;
|
|
@@ -9875,6 +10748,8 @@ exports.STooltip = STooltip;
|
|
|
9875
10748
|
exports.TAG_COLORS = TAG_COLORS;
|
|
9876
10749
|
exports.TAG_SHAPES = TAG_SHAPES;
|
|
9877
10750
|
exports.TAG_SIZES = TAG_SIZES;
|
|
10751
|
+
exports.TIMEPICKER_SIZES = TIMEPICKER_SIZES;
|
|
10752
|
+
exports.TIMEPICKER_SIZE_CONFIG = TIMEPICKER_SIZE_CONFIG;
|
|
9878
10753
|
exports.buttonPreset = buttonPreset;
|
|
9879
10754
|
exports.cn = cn;
|
|
9880
10755
|
exports.findGnbAncestors = findGnbAncestors;
|