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