sellmate-design-system-react 0.4.0 → 0.6.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 +20 -0
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs +180 -37
- package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +6 -1
- package/dist/components/SDateRangePicker/SDateRangePicker.js +180 -37
- package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
- package/dist/components/SGnb/SGnb.cjs +329 -100
- package/dist/components/SGnb/SGnb.cjs.map +1 -1
- package/dist/components/SGnb/SGnb.d.ts +24 -5
- package/dist/components/SGnb/SGnb.js +330 -101
- package/dist/components/SGnb/SGnb.js.map +1 -1
- package/dist/components/SGnb/gnb.config.d.ts +46 -12
- package/dist/components/SGnb/index.d.ts +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs +180 -37
- package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
- package/dist/components/SKeyValueTable/SKeyValueTable.js +180 -37
- package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
- package/dist/components/SLayout/SLayout.cjs +37 -21
- package/dist/components/SLayout/SLayout.cjs.map +1 -1
- package/dist/components/SLayout/SLayout.d.ts +20 -1
- package/dist/components/SLayout/SLayout.js +37 -21
- package/dist/components/SLayout/SLayout.js.map +1 -1
- package/dist/components/SPage/SPage.cjs +38 -22
- package/dist/components/SPage/SPage.cjs.map +1 -1
- package/dist/components/SPage/SPage.js +38 -22
- package/dist/components/SPage/SPage.js.map +1 -1
- package/dist/components/STabs/STabs.cjs +15 -26
- package/dist/components/STabs/STabs.cjs.map +1 -1
- package/dist/components/STabs/STabs.d.ts +1 -1
- package/dist/components/STabs/STabs.js +15 -26
- package/dist/components/STabs/STabs.js.map +1 -1
- package/dist/components/STimePicker/STimePicker.cjs.map +1 -1
- package/dist/components/STimePicker/STimePicker.js.map +1 -1
- package/dist/components/STimeRangePicker/STimeRangePicker.cjs +3 -1
- package/dist/components/STimeRangePicker/STimeRangePicker.cjs.map +1 -1
- package/dist/components/STimeRangePicker/STimeRangePicker.js +3 -1
- package/dist/components/STimeRangePicker/STimeRangePicker.js.map +1 -1
- package/dist/index.cjs +531 -164
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +528 -165
- package/dist/index.js.map +1 -1
- package/dist/styles.css +73 -4
- package/dist/theme.css +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -4876,73 +4876,70 @@ function SLoadingModal({
|
|
|
4876
4876
|
}
|
|
4877
4877
|
|
|
4878
4878
|
// src/components/SGnb/gnb.config.ts
|
|
4879
|
-
var
|
|
4880
|
-
var box = (p) => `var(--cmp-
|
|
4881
|
-
var
|
|
4882
|
-
var
|
|
4879
|
+
var gnb = (p) => `var(--cmp-gnb-${p})`;
|
|
4880
|
+
var box = (p) => `var(--cmp-gnb-box-${p})`;
|
|
4881
|
+
var belt = (p) => `var(--cmp-gnb-belt-${p})`;
|
|
4882
|
+
var light = (p) => `var(--cmp-gnb-light-${p})`;
|
|
4883
|
+
var dark = (p) => `var(--cmp-gnb-dark-${p})`;
|
|
4884
|
+
var HEADER_TOP = {
|
|
4885
|
+
topHeight: gnb("top-height"),
|
|
4886
|
+
topPaddingX: gnb("top-paddingX"),
|
|
4887
|
+
topGap: gnb("top-gap"),
|
|
4888
|
+
logoHeight: gnb("logo-height")
|
|
4889
|
+
};
|
|
4883
4890
|
var GNB_HEADER = {
|
|
4884
|
-
fix:
|
|
4885
|
-
|
|
4886
|
-
topPaddingX: belt("top-paddingX"),
|
|
4887
|
-
topGap: belt("top-gap"),
|
|
4888
|
-
logoHeight: belt("logo-height")
|
|
4889
|
-
},
|
|
4890
|
-
full: {
|
|
4891
|
-
topHeight: box("top-height"),
|
|
4892
|
-
topPaddingX: box("paddingX"),
|
|
4893
|
-
topGap: box("gap"),
|
|
4894
|
-
// 상단바 gap (버튼 간 세부 gap 은 --cmp-gnbBox-button-gap)
|
|
4895
|
-
logoHeight: box("logo-height")
|
|
4896
|
-
}
|
|
4891
|
+
fix: HEADER_TOP,
|
|
4892
|
+
full: HEADER_TOP
|
|
4897
4893
|
};
|
|
4898
4894
|
var GNB_MENU = {
|
|
4899
4895
|
box: {
|
|
4900
|
-
//
|
|
4901
|
-
bodyPaddingY:
|
|
4902
|
-
itemMarginX:
|
|
4903
|
-
|
|
4896
|
+
// 바디 패딩·세로 패딩·chevron 은 gnb 로 통합된 공통 토큰
|
|
4897
|
+
bodyPaddingY: gnb("body-padding"),
|
|
4898
|
+
itemMarginX: gnb("body-padding"),
|
|
4899
|
+
// 전방향 토큰 → 아이템 좌우 여백에도 사용
|
|
4900
|
+
itemPaddingY: gnb("item-paddingY"),
|
|
4904
4901
|
itemPaddingRight: box("item-paddingRight"),
|
|
4905
4902
|
itemGap: box("item-gap"),
|
|
4906
4903
|
itemRadius: box("item-radius"),
|
|
4907
|
-
itemArrow:
|
|
4904
|
+
itemArrow: gnb("item-chevron-size"),
|
|
4908
4905
|
depth1PaddingLeft: box("item-depth1-paddingLeft"),
|
|
4909
4906
|
depth2PaddingLeft: box("item-depth2-paddingLeft"),
|
|
4910
4907
|
depth3PaddingLeft: box("item-depth3-paddingLeft"),
|
|
4911
4908
|
depth1Icon: box("item-depth1-icon"),
|
|
4912
4909
|
depth1FontSize: 14,
|
|
4913
|
-
// token:
|
|
4910
|
+
// token: gnb.box.item.typography.selected.fontSize
|
|
4914
4911
|
depth1LineHeight: 24,
|
|
4915
|
-
// token:
|
|
4912
|
+
// token: gnb.box.item.typography.selected.lineHeight
|
|
4916
4913
|
depth1FontWeight: 700,
|
|
4917
4914
|
itemFontSize: 14,
|
|
4918
|
-
// token:
|
|
4915
|
+
// token: gnb.box.item.typography.default.fontSize
|
|
4919
4916
|
itemLineHeight: 24
|
|
4920
|
-
// token:
|
|
4917
|
+
// token: gnb.box.item.typography.default.lineHeight
|
|
4921
4918
|
},
|
|
4922
4919
|
belt: {
|
|
4923
|
-
bodyPaddingY:
|
|
4920
|
+
bodyPaddingY: gnb("body-padding"),
|
|
4924
4921
|
itemMarginX: "0px",
|
|
4925
4922
|
// belt 토큰에 가로 여백 없음 → 풀폭 행 (들여쓰기는 depth*-paddingLeft 전담)
|
|
4926
|
-
itemPaddingY:
|
|
4923
|
+
itemPaddingY: gnb("item-paddingY"),
|
|
4927
4924
|
itemPaddingRight: belt("item-paddingRight"),
|
|
4928
4925
|
itemGap: belt("item-gap"),
|
|
4929
4926
|
itemRadius: "0px",
|
|
4930
4927
|
// belt 토큰에 radius 없음 → 풀폭 행
|
|
4931
|
-
itemArrow:
|
|
4928
|
+
itemArrow: gnb("item-chevron-size"),
|
|
4932
4929
|
depth1PaddingLeft: belt("item-depth1-paddingLeft"),
|
|
4933
4930
|
depth2PaddingLeft: belt("item-depth2-paddingLeft"),
|
|
4934
4931
|
depth3PaddingLeft: belt("item-depth3-paddingLeft"),
|
|
4935
|
-
depth1Icon:
|
|
4936
|
-
// belt
|
|
4932
|
+
depth1Icon: belt("item-depth1-icon"),
|
|
4933
|
+
// belt 전용 depth1 아이콘 토큰(16)
|
|
4937
4934
|
depth1FontSize: 13,
|
|
4938
|
-
// token:
|
|
4935
|
+
// token: gnb.belt.item.depth1.typography.fontSize
|
|
4939
4936
|
depth1LineHeight: 22,
|
|
4940
|
-
// token:
|
|
4937
|
+
// token: gnb.belt.item.depth1.typography.lineHeight
|
|
4941
4938
|
depth1FontWeight: 700,
|
|
4942
4939
|
itemFontSize: 12,
|
|
4943
|
-
// token:
|
|
4940
|
+
// token: gnb.belt.item.typography.default.fontSize
|
|
4944
4941
|
itemLineHeight: 20
|
|
4945
|
-
// token:
|
|
4942
|
+
// token: gnb.belt.item.typography.default.lineHeight
|
|
4946
4943
|
}
|
|
4947
4944
|
};
|
|
4948
4945
|
var GNB_COLORS = {
|
|
@@ -4955,7 +4952,9 @@ var GNB_COLORS = {
|
|
|
4955
4952
|
itemColorDefault: light("item-fg-default"),
|
|
4956
4953
|
itemColorHover: light("item-fg-hover"),
|
|
4957
4954
|
itemColorActive: light("item-fg-selected"),
|
|
4958
|
-
itemChevron: light("item-chevron")
|
|
4955
|
+
itemChevron: light("item-chevron"),
|
|
4956
|
+
itemIcon: "grey_65",
|
|
4957
|
+
railFgDefault: light("rail-fg-default")
|
|
4959
4958
|
},
|
|
4960
4959
|
dark: {
|
|
4961
4960
|
panelBg: dark("bg"),
|
|
@@ -4966,19 +4965,41 @@ var GNB_COLORS = {
|
|
|
4966
4965
|
itemColorDefault: dark("item-fg-default"),
|
|
4967
4966
|
itemColorHover: dark("item-fg-hover"),
|
|
4968
4967
|
itemColorActive: dark("item-fg-selected"),
|
|
4969
|
-
itemChevron: dark("item-chevron")
|
|
4968
|
+
itemChevron: dark("item-chevron"),
|
|
4969
|
+
itemIcon: "grey_55",
|
|
4970
|
+
railFgDefault: dark("rail-fg-default")
|
|
4970
4971
|
}
|
|
4971
4972
|
};
|
|
4972
4973
|
var GNB_COMMON = {
|
|
4973
4974
|
/** 접힘 레일 폭. header="full" 은 접으면 메뉴만 사라져 레일이 없으므로 fix 전용. */
|
|
4974
|
-
foldWidth:
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4975
|
+
foldWidth: gnb("fold-width")
|
|
4976
|
+
};
|
|
4977
|
+
var GNB_RAIL = {
|
|
4978
|
+
padding: gnb("rail-padding"),
|
|
4979
|
+
gap: gnb("rail-gap"),
|
|
4980
|
+
itemWidth: gnb("rail-item-width"),
|
|
4981
|
+
itemPaddingTop: gnb("rail-item-paddingTop"),
|
|
4982
|
+
itemPaddingBottom: gnb("rail-item-paddingBottom"),
|
|
4983
|
+
itemRadius: gnb("rail-item-radius"),
|
|
4984
|
+
itemIcon: gnb("rail-item-icon"),
|
|
4985
|
+
// 타이포 (theme.css 에 gnb 타이포 변수 없음 → 토큰 값 직접 명시)
|
|
4986
|
+
itemFontSize: 11,
|
|
4987
|
+
// token: gnb.rail.item.typography.default.fontSize
|
|
4988
|
+
itemLineHeight: 18,
|
|
4989
|
+
// token: gnb.rail.item.typography.default.lineHeight
|
|
4990
|
+
itemFontWeight: 500,
|
|
4991
|
+
// token: gnb.rail.item.typography.default.fontWeight
|
|
4992
|
+
itemFontWeightSelected: 700
|
|
4993
|
+
// token: gnb.rail.item.typography.selected.fontWeight
|
|
4994
|
+
};
|
|
4995
|
+
var GNB_RAIL_WIDTH = 68;
|
|
4996
|
+
var GNB_FOLD_MS = 200;
|
|
4997
|
+
var GNB_EXPAND_MS = 200;
|
|
4981
4998
|
var GNB_MENU_WIDTH = 240;
|
|
4999
|
+
function findGnbRailValue(items, value) {
|
|
5000
|
+
if (items.some((item) => item.value === value)) return value;
|
|
5001
|
+
return findGnbAncestors(items, value)[0];
|
|
5002
|
+
}
|
|
4982
5003
|
function findGnbAncestors(items, value) {
|
|
4983
5004
|
const walk = (list, trail) => {
|
|
4984
5005
|
for (const item of list) {
|
|
@@ -5001,6 +5022,7 @@ function useSLayout() {
|
|
|
5001
5022
|
var SLayout = forwardRef(function SLayout2({
|
|
5002
5023
|
type = "box",
|
|
5003
5024
|
header = "fix",
|
|
5025
|
+
useRail = false,
|
|
5004
5026
|
folded,
|
|
5005
5027
|
defaultFolded = false,
|
|
5006
5028
|
onFoldedChange,
|
|
@@ -5019,21 +5041,40 @@ var SLayout = forwardRef(function SLayout2({
|
|
|
5019
5041
|
},
|
|
5020
5042
|
[isControlled, onFoldedChange]
|
|
5021
5043
|
);
|
|
5044
|
+
const [gnbState, setGnbState] = useState({ forceFull: false, menuVisible: true });
|
|
5045
|
+
const reportGnbState = useCallback(
|
|
5046
|
+
(next) => setGnbState(
|
|
5047
|
+
(prev) => prev.forceFull === next.forceFull && prev.menuVisible === next.menuVisible ? prev : next
|
|
5048
|
+
),
|
|
5049
|
+
[]
|
|
5050
|
+
);
|
|
5051
|
+
const effectiveHeader = header === "full" || gnbState.forceFull ? "full" : "fix";
|
|
5022
5052
|
const ctx = useMemo(
|
|
5023
|
-
() => ({
|
|
5024
|
-
|
|
5053
|
+
() => ({
|
|
5054
|
+
type,
|
|
5055
|
+
header: effectiveHeader,
|
|
5056
|
+
useRail,
|
|
5057
|
+
folded: currentFolded,
|
|
5058
|
+
setFolded,
|
|
5059
|
+
reportGnbState
|
|
5060
|
+
}),
|
|
5061
|
+
[type, effectiveHeader, useRail, currentFolded, setFolded, reportGnbState]
|
|
5025
5062
|
);
|
|
5026
|
-
const isFullHeader =
|
|
5063
|
+
const isFullHeader = effectiveHeader === "full";
|
|
5064
|
+
const gnbColumnWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (gnbState.menuVisible ? GNB_MENU_WIDTH : 0);
|
|
5027
5065
|
const frameStyle = isFullHeader ? {
|
|
5028
|
-
gridTemplateColumns: `${currentFolded ? 0 :
|
|
5029
|
-
gridTemplateRows: `${GNB_HEADER[
|
|
5066
|
+
gridTemplateColumns: `${currentFolded ? 0 : gnbColumnWidth}px 1fr`,
|
|
5067
|
+
gridTemplateRows: `${GNB_HEADER[effectiveHeader].topHeight} 1fr`,
|
|
5068
|
+
// 메뉴가 미끄러지는 동안 페이지 열도 같은 길이로 따라와야 한 덩어리로 읽힌다.
|
|
5069
|
+
// (fix 는 SGnb 가 제 폭을 직접 전환하므로 프레임이 손댈 게 없다)
|
|
5070
|
+
transition: `grid-template-columns ${GNB_FOLD_MS}ms ease-out`
|
|
5030
5071
|
} : void 0;
|
|
5031
5072
|
return /* @__PURE__ */ jsx(SLayoutContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(
|
|
5032
5073
|
"div",
|
|
5033
5074
|
{
|
|
5034
5075
|
ref,
|
|
5035
5076
|
"data-type": type,
|
|
5036
|
-
"data-header":
|
|
5077
|
+
"data-header": effectiveHeader,
|
|
5037
5078
|
className: cn(
|
|
5038
5079
|
"box-border h-full overflow-hidden",
|
|
5039
5080
|
isFullHeader ? "grid" : "flex flex-row",
|
|
@@ -5051,8 +5092,10 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
5051
5092
|
header: headerProp,
|
|
5052
5093
|
color = "light",
|
|
5053
5094
|
items,
|
|
5095
|
+
useRail: useRailProp,
|
|
5054
5096
|
value = "",
|
|
5055
5097
|
onValueChange,
|
|
5098
|
+
onRailChange,
|
|
5056
5099
|
folded: foldedProp,
|
|
5057
5100
|
onFoldChange,
|
|
5058
5101
|
onLauncherClick,
|
|
@@ -5064,8 +5107,10 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
5064
5107
|
}, ref) {
|
|
5065
5108
|
const layout = useContext(SLayoutContext);
|
|
5066
5109
|
const type = typeProp ?? layout?.type ?? "box";
|
|
5067
|
-
const header = headerProp ?? layout?.header ?? "fix";
|
|
5068
5110
|
const folded = foldedProp ?? layout?.folded ?? false;
|
|
5111
|
+
const useRail = useRailProp ?? layout?.useRail ?? false;
|
|
5112
|
+
const railHasLeaf = useRail && items.some((item) => !item.children || item.children.length === 0);
|
|
5113
|
+
const header = railHasLeaf ? "full" : headerProp ?? layout?.header ?? "fix";
|
|
5069
5114
|
const H = GNB_HEADER[header];
|
|
5070
5115
|
const M = GNB_MENU[type];
|
|
5071
5116
|
const C = GNB_COLORS[color];
|
|
@@ -5073,6 +5118,9 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
5073
5118
|
() => new Set(findGnbAncestors(items, value))
|
|
5074
5119
|
);
|
|
5075
5120
|
const [hoveredKey, setHoveredKey] = useState(null);
|
|
5121
|
+
const [activeRail, setActiveRail] = useState(
|
|
5122
|
+
() => findGnbRailValue(items, value) ?? items[0]?.value
|
|
5123
|
+
);
|
|
5076
5124
|
useEffect(() => {
|
|
5077
5125
|
const ancestors = findGnbAncestors(items, value);
|
|
5078
5126
|
if (ancestors.length === 0) return;
|
|
@@ -5083,6 +5131,15 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
5083
5131
|
return next;
|
|
5084
5132
|
});
|
|
5085
5133
|
}, [items, value]);
|
|
5134
|
+
useEffect(() => {
|
|
5135
|
+
const owner = findGnbRailValue(items, value);
|
|
5136
|
+
if (owner !== void 0) setActiveRail(owner);
|
|
5137
|
+
}, [items, value]);
|
|
5138
|
+
useEffect(() => {
|
|
5139
|
+
setActiveRail(
|
|
5140
|
+
(prev) => prev !== void 0 && items.some((item) => item.value === prev) ? prev : items[0]?.value
|
|
5141
|
+
);
|
|
5142
|
+
}, [items]);
|
|
5086
5143
|
const toggleExpand = (key) => {
|
|
5087
5144
|
setExpandedKeys((prev) => {
|
|
5088
5145
|
const next = new Set(prev);
|
|
@@ -5099,16 +5156,58 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
5099
5156
|
}
|
|
5100
5157
|
onValueChange?.(item.value);
|
|
5101
5158
|
};
|
|
5159
|
+
const handleRailClick = (item) => {
|
|
5160
|
+
if (item.disabled) return;
|
|
5161
|
+
setActiveRail(item.value);
|
|
5162
|
+
onRailChange?.(item.value);
|
|
5163
|
+
if (!item.children || item.children.length === 0) onValueChange?.(item.value);
|
|
5164
|
+
};
|
|
5102
5165
|
const handleFoldToggle = () => {
|
|
5103
5166
|
const next = !folded;
|
|
5104
5167
|
onFoldChange?.(next);
|
|
5105
5168
|
if (foldedProp === void 0) layout?.setFolded(next);
|
|
5106
5169
|
};
|
|
5107
|
-
const
|
|
5170
|
+
const renderRailItem = (item) => {
|
|
5171
|
+
const isActive = item.value === activeRail;
|
|
5172
|
+
const isHovered = hoveredKey === item.value && !item.disabled;
|
|
5173
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
5174
|
+
"button",
|
|
5175
|
+
{
|
|
5176
|
+
type: "button",
|
|
5177
|
+
disabled: item.disabled,
|
|
5178
|
+
"aria-current": isActive ? "true" : void 0,
|
|
5179
|
+
className: "box-border flex select-none flex-col items-center border-none bg-transparent transition-colors duration-150",
|
|
5180
|
+
style: {
|
|
5181
|
+
width: GNB_RAIL.itemWidth,
|
|
5182
|
+
paddingTop: GNB_RAIL.itemPaddingTop,
|
|
5183
|
+
paddingBottom: GNB_RAIL.itemPaddingBottom,
|
|
5184
|
+
borderRadius: GNB_RAIL.itemRadius,
|
|
5185
|
+
backgroundColor: isActive ? C.itemBgSelected : isHovered ? C.itemBgHover : C.itemBgDefault,
|
|
5186
|
+
color: isActive ? C.itemColorActive : C.railFgDefault,
|
|
5187
|
+
// <button> 의 UA 폰트를 끊고 앱 폰트를 잇는다 (메뉴 아이템과 같은 이유)
|
|
5188
|
+
fontFamily: "inherit",
|
|
5189
|
+
fontSize: GNB_RAIL.itemFontSize,
|
|
5190
|
+
lineHeight: `${GNB_RAIL.itemLineHeight}px`,
|
|
5191
|
+
fontWeight: isActive ? GNB_RAIL.itemFontWeightSelected : GNB_RAIL.itemFontWeight,
|
|
5192
|
+
opacity: item.disabled ? 0.4 : 1,
|
|
5193
|
+
cursor: item.disabled ? "not-allowed" : "pointer"
|
|
5194
|
+
},
|
|
5195
|
+
onClick: () => handleRailClick(item),
|
|
5196
|
+
onMouseEnter: () => setHoveredKey(item.value),
|
|
5197
|
+
onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
|
|
5198
|
+
children: [
|
|
5199
|
+
item.icon && /* @__PURE__ */ jsx(SIcon, { name: item.icon, size: GNB_RAIL.itemIcon, className: "shrink-0" }),
|
|
5200
|
+
/* @__PURE__ */ jsx("span", { className: "w-full overflow-hidden text-ellipsis whitespace-nowrap text-center", children: item.label })
|
|
5201
|
+
]
|
|
5202
|
+
}
|
|
5203
|
+
) }, item.value);
|
|
5204
|
+
};
|
|
5205
|
+
const renderItem = (item, depth, branchHasIcon = false) => {
|
|
5108
5206
|
const hasChildren = !!(item.children && item.children.length > 0);
|
|
5109
5207
|
const isSelected = !hasChildren && item.value === value;
|
|
5110
5208
|
const isExpanded = expandedKeys.has(item.value);
|
|
5111
5209
|
const isHovered = hoveredKey === item.value && !item.disabled;
|
|
5210
|
+
const iconBranch = depth === 1 ? !!item.icon : branchHasIcon;
|
|
5112
5211
|
const itemStyle = {
|
|
5113
5212
|
gap: M.itemGap,
|
|
5114
5213
|
paddingTop: M.itemPaddingY,
|
|
@@ -5145,8 +5244,11 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
5145
5244
|
onMouseEnter: () => setHoveredKey(item.value),
|
|
5146
5245
|
onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
|
|
5147
5246
|
children: [
|
|
5148
|
-
depth === 1 && item.icon && /* @__PURE__ */ jsx(SIcon, { name: item.icon, size: M.depth1Icon, className: "shrink-0" }),
|
|
5149
|
-
/* @__PURE__ */
|
|
5247
|
+
depth === 1 && item.icon && /* @__PURE__ */ jsx(SIcon, { name: item.icon, size: M.depth1Icon, color: C.itemIcon, className: "shrink-0" }),
|
|
5248
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap flex flex-no-wrap items-center", children: [
|
|
5249
|
+
depth > 1 && iconBranch ? /* @__PURE__ */ jsx("div", { className: "w-[20px] h-[24px] flex items-center justify-center mr-[8px]", children: "-" }) : null,
|
|
5250
|
+
item.label
|
|
5251
|
+
] }),
|
|
5150
5252
|
item.tag !== void 0 && item.tag !== "" && /* @__PURE__ */ jsx(
|
|
5151
5253
|
STag,
|
|
5152
5254
|
{
|
|
@@ -5164,94 +5266,221 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
5164
5266
|
size: M.itemArrow,
|
|
5165
5267
|
color: C.itemChevron,
|
|
5166
5268
|
rotate: isExpanded ? 180 : 0,
|
|
5167
|
-
className: "shrink-0 transition-transform duration-
|
|
5269
|
+
className: "shrink-0 transition-transform duration-200"
|
|
5168
5270
|
}
|
|
5169
5271
|
)
|
|
5170
5272
|
]
|
|
5171
5273
|
}
|
|
5172
5274
|
),
|
|
5173
|
-
hasChildren &&
|
|
5275
|
+
hasChildren && /* @__PURE__ */ jsx(
|
|
5276
|
+
"div",
|
|
5277
|
+
{
|
|
5278
|
+
className: "grid",
|
|
5279
|
+
style: {
|
|
5280
|
+
gridTemplateRows: isExpanded ? "1fr" : "0fr",
|
|
5281
|
+
transition: `grid-template-rows ${GNB_EXPAND_MS}ms ease-out`
|
|
5282
|
+
},
|
|
5283
|
+
children: /* @__PURE__ */ jsx(
|
|
5284
|
+
"ul",
|
|
5285
|
+
{
|
|
5286
|
+
className: "m-0 flex min-h-0 list-none flex-col overflow-hidden p-0",
|
|
5287
|
+
style: {
|
|
5288
|
+
visibility: isExpanded ? "visible" : "hidden",
|
|
5289
|
+
transition: `visibility 0s linear ${isExpanded ? "0s" : `${GNB_EXPAND_MS}ms`}`
|
|
5290
|
+
},
|
|
5291
|
+
children: item.children.map((child) => renderItem(child, depth + 1, iconBranch))
|
|
5292
|
+
}
|
|
5293
|
+
)
|
|
5294
|
+
}
|
|
5295
|
+
)
|
|
5174
5296
|
] }, item.value)
|
|
5175
5297
|
);
|
|
5176
5298
|
};
|
|
5299
|
+
const railItems = useRail ? items : [];
|
|
5300
|
+
const menuItems = useRail ? items.find((item) => item.value === activeRail)?.children ?? [] : items;
|
|
5301
|
+
const showMenu = !useRail || menuItems.length > 0;
|
|
5302
|
+
const bodyWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (showMenu ? GNB_MENU_WIDTH : 0);
|
|
5303
|
+
useEffect(() => {
|
|
5304
|
+
layout?.reportGnbState({ forceFull: railHasLeaf, menuVisible: showMenu });
|
|
5305
|
+
}, [layout, railHasLeaf, showMenu]);
|
|
5177
5306
|
const railFolded = folded && header === "fix";
|
|
5178
|
-
const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth :
|
|
5307
|
+
const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth : bodyWidth;
|
|
5179
5308
|
const rootStyle = {
|
|
5180
5309
|
width: rootWidth,
|
|
5310
|
+
transition: `width ${GNB_FOLD_MS}ms ease-out`,
|
|
5181
5311
|
...header === "full" ? { gridArea: "1 / 1 / -1 / -1", pointerEvents: "none" } : { backgroundColor: C.panelBg },
|
|
5182
5312
|
...style
|
|
5183
5313
|
};
|
|
5314
|
+
const hideAfterSlide = (hidden) => `visibility 0s linear ${hidden ? `${GNB_FOLD_MS}ms` : "0s"}`;
|
|
5315
|
+
const foldInline = !!logo;
|
|
5316
|
+
const sideFoldStyle = {
|
|
5317
|
+
opacity: railFolded ? 0 : 1,
|
|
5318
|
+
position: railFolded ? "absolute" : "static",
|
|
5319
|
+
visibility: railFolded ? "hidden" : "visible",
|
|
5320
|
+
transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}`
|
|
5321
|
+
};
|
|
5184
5322
|
return /* @__PURE__ */ jsxs(
|
|
5185
5323
|
"div",
|
|
5186
5324
|
{
|
|
5187
5325
|
ref,
|
|
5188
|
-
className: cn("box-border flex h-full shrink-0 flex-col", className),
|
|
5326
|
+
className: cn("box-border flex h-full shrink-0 flex-col overflow-hidden", className),
|
|
5189
5327
|
style: rootStyle,
|
|
5190
5328
|
"data-type": type,
|
|
5191
5329
|
"data-header": header,
|
|
5192
5330
|
"data-color": color,
|
|
5193
5331
|
...rest,
|
|
5194
5332
|
children: [
|
|
5195
|
-
/* @__PURE__ */
|
|
5333
|
+
/* @__PURE__ */ jsx(
|
|
5196
5334
|
"div",
|
|
5197
5335
|
{
|
|
5198
|
-
className:
|
|
5336
|
+
className: "relative box-border flex shrink-0 items-center overflow-hidden",
|
|
5199
5337
|
style: {
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5338
|
+
height: H.topHeight,
|
|
5339
|
+
// 인라인 간격은 컨테이너 gap 대신 각 요소 margin 으로 준다 — 런처↔폴드(8px)와
|
|
5340
|
+
// 폴드↔로고(top-gap)를 다르게 두기 위함. 접히면 런처·로고가 흐름에서 빠져 margin 도 무의미해진다.
|
|
5341
|
+
padding: `0 ${railFolded ? GNB_RAIL.padding : H.topPaddingX}`,
|
|
5203
5342
|
borderBottom: `1px solid ${C.topBorder}`,
|
|
5204
5343
|
backgroundColor: C.panelBg,
|
|
5205
5344
|
pointerEvents: "auto",
|
|
5345
|
+
transition: `padding ${GNB_FOLD_MS}ms ease-out`,
|
|
5206
5346
|
// 아이콘 버튼은 자체 ghostButton 토큰을 쓰므로, 이 색은 logo 슬롯 상속용
|
|
5207
5347
|
color: C.itemColorDefault
|
|
5208
5348
|
},
|
|
5209
|
-
children:
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5349
|
+
children: foldInline ? (
|
|
5350
|
+
// 런처(있을 때)·폴드·로고를 흐름에 나란히 둔다 → 폴드 버튼이 (런처와) 로고 사이에 선다.
|
|
5351
|
+
// 접히면 런처·로고가 페이드하며 흐름에서 빠지고(sideFoldStyle), 폴드 버튼만 남아 레일 가운데에 선다.
|
|
5352
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5353
|
+
onLauncherClick && /* @__PURE__ */ jsx(
|
|
5354
|
+
SGhostButton,
|
|
5355
|
+
{
|
|
5356
|
+
icon: "launcher",
|
|
5357
|
+
size: "xs",
|
|
5358
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
5359
|
+
ariaLabel: "app launcher",
|
|
5360
|
+
onClick: onLauncherClick,
|
|
5361
|
+
className: "shrink-0",
|
|
5362
|
+
style: { ...sideFoldStyle, marginRight: "8px" }
|
|
5363
|
+
}
|
|
5364
|
+
),
|
|
5365
|
+
/* @__PURE__ */ jsx(
|
|
5366
|
+
SGhostButton,
|
|
5367
|
+
{
|
|
5368
|
+
icon: "sidebar",
|
|
5369
|
+
size: "xs",
|
|
5370
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
5371
|
+
ariaLabel: "toggle navigation",
|
|
5372
|
+
ariaPressed: folded,
|
|
5373
|
+
onClick: handleFoldToggle,
|
|
5374
|
+
className: "shrink-0"
|
|
5375
|
+
}
|
|
5376
|
+
),
|
|
5377
|
+
/* @__PURE__ */ jsx(
|
|
5378
|
+
"div",
|
|
5379
|
+
{
|
|
5380
|
+
className: "flex shrink-0 items-center",
|
|
5381
|
+
style: { height: H.logoHeight, marginLeft: H.topGap, ...sideFoldStyle },
|
|
5382
|
+
children: logo
|
|
5383
|
+
}
|
|
5384
|
+
)
|
|
5385
|
+
] })
|
|
5386
|
+
) : (
|
|
5387
|
+
// 런처·로고는 좌측 그룹(흐름에서 빼 폭 축소 중 폴드 버튼을 밀어내지 않는다),
|
|
5388
|
+
// 폴드 버튼은 우측 끝(ml-auto)이라 폭·패딩이 줄면 저절로 레일 가운데에 선다.
|
|
5389
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5390
|
+
(onLauncherClick || logo) && /* @__PURE__ */ jsxs(
|
|
5391
|
+
"div",
|
|
5392
|
+
{
|
|
5393
|
+
className: "absolute inset-y-0 flex items-center",
|
|
5394
|
+
style: {
|
|
5395
|
+
left: H.topPaddingX,
|
|
5396
|
+
gap: H.topGap,
|
|
5397
|
+
opacity: railFolded ? 0 : 1,
|
|
5398
|
+
transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}, transform ${GNB_FOLD_MS}ms ease-out`,
|
|
5399
|
+
// 메뉴와 같은 방향으로 빠지되, 좁은 레일 안에서 꼬리가 비치지 않게 페이드가 거리를 대신한다
|
|
5400
|
+
transform: railFolded ? `translateX(-${H.topPaddingX})` : "none",
|
|
5401
|
+
visibility: railFolded ? "hidden" : "visible"
|
|
5402
|
+
},
|
|
5403
|
+
children: [
|
|
5404
|
+
onLauncherClick && /* @__PURE__ */ jsx(
|
|
5405
|
+
SGhostButton,
|
|
5406
|
+
{
|
|
5407
|
+
icon: "launcher",
|
|
5408
|
+
size: "xs",
|
|
5409
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
5410
|
+
ariaLabel: "app launcher",
|
|
5411
|
+
onClick: onLauncherClick,
|
|
5412
|
+
className: "shrink-0"
|
|
5413
|
+
}
|
|
5414
|
+
),
|
|
5415
|
+
logo && /* @__PURE__ */ jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo })
|
|
5416
|
+
]
|
|
5417
|
+
}
|
|
5418
|
+
),
|
|
5419
|
+
/* @__PURE__ */ jsx(
|
|
5420
|
+
SGhostButton,
|
|
5421
|
+
{
|
|
5422
|
+
icon: "sidebar",
|
|
5423
|
+
size: "xs",
|
|
5424
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
5425
|
+
ariaLabel: "toggle navigation",
|
|
5426
|
+
ariaPressed: folded,
|
|
5427
|
+
onClick: handleFoldToggle,
|
|
5428
|
+
className: "ml-auto shrink-0"
|
|
5429
|
+
}
|
|
5430
|
+
)
|
|
5431
|
+
] })
|
|
5432
|
+
)
|
|
5235
5433
|
}
|
|
5236
5434
|
),
|
|
5237
|
-
|
|
5238
|
-
"
|
|
5435
|
+
/* @__PURE__ */ jsxs(
|
|
5436
|
+
"div",
|
|
5239
5437
|
{
|
|
5240
|
-
"
|
|
5241
|
-
className: "flex flex-col overflow-y-auto",
|
|
5438
|
+
className: "flex flex-row",
|
|
5242
5439
|
style: {
|
|
5243
|
-
width:
|
|
5440
|
+
width: bodyWidth,
|
|
5244
5441
|
alignSelf: "flex-start",
|
|
5245
5442
|
flexGrow: 1,
|
|
5246
5443
|
flexShrink: 1,
|
|
5247
5444
|
flexBasis: "auto",
|
|
5248
5445
|
minHeight: 0,
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
pointerEvents: "auto"
|
|
5446
|
+
transform: folded ? "translateX(-100%)" : "none",
|
|
5447
|
+
visibility: folded ? "hidden" : "visible",
|
|
5448
|
+
transition: `transform ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(folded)}`
|
|
5253
5449
|
},
|
|
5254
|
-
children:
|
|
5450
|
+
children: [
|
|
5451
|
+
useRail && // 레일도 내비게이션 landmark — nav 로 세워야 aria-label(레일)이 스크린리더에 노출된다
|
|
5452
|
+
// (role 없는 ul 의 aria-label 은 영역명으로 읽히지 않는다). 메뉴 nav 와는 레이블로 구분된다.
|
|
5453
|
+
/* @__PURE__ */ jsx(
|
|
5454
|
+
"nav",
|
|
5455
|
+
{
|
|
5456
|
+
"aria-label": `${ariaLabel} rail`,
|
|
5457
|
+
className: "flex shrink-0 flex-col overflow-y-auto",
|
|
5458
|
+
style: {
|
|
5459
|
+
width: GNB_RAIL_WIDTH,
|
|
5460
|
+
padding: GNB_RAIL.padding,
|
|
5461
|
+
borderRight: `1px solid ${C.topBorder}`,
|
|
5462
|
+
backgroundColor: C.panelBg,
|
|
5463
|
+
pointerEvents: "auto"
|
|
5464
|
+
},
|
|
5465
|
+
children: /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col p-0", style: { gap: GNB_RAIL.gap }, children: railItems.map(renderRailItem) })
|
|
5466
|
+
}
|
|
5467
|
+
),
|
|
5468
|
+
showMenu && /* @__PURE__ */ jsx(
|
|
5469
|
+
"nav",
|
|
5470
|
+
{
|
|
5471
|
+
"aria-label": ariaLabel,
|
|
5472
|
+
className: "flex shrink-0 flex-col overflow-y-auto",
|
|
5473
|
+
style: {
|
|
5474
|
+
width: GNB_MENU_WIDTH,
|
|
5475
|
+
paddingTop: M.bodyPaddingY,
|
|
5476
|
+
paddingBottom: M.bodyPaddingY,
|
|
5477
|
+
backgroundColor: C.panelBg,
|
|
5478
|
+
pointerEvents: "auto"
|
|
5479
|
+
},
|
|
5480
|
+
children: /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: menuItems.map((item) => renderItem(item, 1)) })
|
|
5481
|
+
}
|
|
5482
|
+
)
|
|
5483
|
+
]
|
|
5255
5484
|
}
|
|
5256
5485
|
)
|
|
5257
5486
|
]
|
|
@@ -5265,7 +5494,7 @@ var SPage = forwardRef(function SPage2({ className, style, children, ...rest },
|
|
|
5265
5494
|
"main",
|
|
5266
5495
|
{
|
|
5267
5496
|
ref,
|
|
5268
|
-
className: cn("box-border min-h-0 min-w-0 flex-1 overflow-auto p-
|
|
5497
|
+
className: cn("box-border min-h-0 min-w-0 flex-1 overflow-auto p-20", className),
|
|
5269
5498
|
style: { ...gridStyle, ...style },
|
|
5270
5499
|
...rest,
|
|
5271
5500
|
children
|
|
@@ -5307,6 +5536,7 @@ function STabs({
|
|
|
5307
5536
|
}) {
|
|
5308
5537
|
const s = TAB_MAIN_SIZE[size];
|
|
5309
5538
|
const blue = TAB_MAIN_COLORS.selected.border;
|
|
5539
|
+
const isVertical = isSub && vertical;
|
|
5310
5540
|
const tabRefs = useRef([]);
|
|
5311
5541
|
const moveTo = (target) => {
|
|
5312
5542
|
if (target < 0 || target >= tabs.length) return;
|
|
@@ -5314,19 +5544,17 @@ function STabs({
|
|
|
5314
5544
|
tabRefs.current[target]?.focus();
|
|
5315
5545
|
};
|
|
5316
5546
|
const containerStyle = { ...style };
|
|
5317
|
-
if (!isSub) {
|
|
5318
|
-
if (vertical) containerStyle.borderRight = `1px solid ${blue}`;
|
|
5319
|
-
else containerStyle.borderBottom = `1px solid ${blue}`;
|
|
5320
|
-
}
|
|
5547
|
+
if (!isSub) containerStyle.borderBottom = `1px solid ${blue}`;
|
|
5321
5548
|
return /* @__PURE__ */ jsx(
|
|
5322
5549
|
"div",
|
|
5323
5550
|
{
|
|
5324
5551
|
role: "tablist",
|
|
5325
|
-
"aria-orientation":
|
|
5552
|
+
"aria-orientation": isVertical ? "vertical" : "horizontal",
|
|
5326
5553
|
className: cn(
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5554
|
+
// main(가로): baseline(하단 divider)이 100% 너비로 채워지도록 w-full
|
|
5555
|
+
isSub ? "inline-flex w-fit" : "flex w-full",
|
|
5556
|
+
isVertical ? "flex-col items-stretch" : "flex-row",
|
|
5557
|
+
isSub ? isVertical ? "gap-[16px]" : "gap-[24px]" : "gap-[4px]",
|
|
5330
5558
|
className
|
|
5331
5559
|
),
|
|
5332
5560
|
style: containerStyle,
|
|
@@ -5355,22 +5583,12 @@ function STabs({
|
|
|
5355
5583
|
color: selected ? TAB_MAIN_COLORS.selected.text : TAB_MAIN_COLORS.default.text,
|
|
5356
5584
|
border: `1px solid ${border}`,
|
|
5357
5585
|
// 폴더형: 미선택 탭은 baseline 위에, 선택 탭은 baseline(파란선)을 1px 덮어 연결된 것처럼 보이게
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
borderRight: selected ? `1px solid ${selectedBg}` : "none",
|
|
5365
|
-
marginRight: selected ? -1 : 0
|
|
5366
|
-
} : {
|
|
5367
|
-
borderTopLeftRadius: 6,
|
|
5368
|
-
borderTopRightRadius: 6,
|
|
5369
|
-
borderBottomLeftRadius: 0,
|
|
5370
|
-
borderBottomRightRadius: 0,
|
|
5371
|
-
borderBottom: selected ? `1px solid ${selectedBg}` : "none",
|
|
5372
|
-
marginBottom: selected ? -1 : 0
|
|
5373
|
-
}
|
|
5586
|
+
borderTopLeftRadius: 6,
|
|
5587
|
+
borderTopRightRadius: 6,
|
|
5588
|
+
borderBottomLeftRadius: 0,
|
|
5589
|
+
borderBottomRightRadius: 0,
|
|
5590
|
+
borderBottom: selected ? `1px solid ${selectedBg}` : "none",
|
|
5591
|
+
marginBottom: selected ? -1 : 0
|
|
5374
5592
|
};
|
|
5375
5593
|
}
|
|
5376
5594
|
return /* @__PURE__ */ jsxs(
|
|
@@ -5390,8 +5608,8 @@ function STabs({
|
|
|
5390
5608
|
select();
|
|
5391
5609
|
return;
|
|
5392
5610
|
}
|
|
5393
|
-
const nextKey =
|
|
5394
|
-
const prevKey =
|
|
5611
|
+
const nextKey = isVertical ? "ArrowDown" : "ArrowRight";
|
|
5612
|
+
const prevKey = isVertical ? "ArrowUp" : "ArrowLeft";
|
|
5395
5613
|
if (e.key === nextKey) {
|
|
5396
5614
|
e.preventDefault();
|
|
5397
5615
|
moveTo((i + 1) % tabs.length);
|
|
@@ -7912,25 +8130,45 @@ function SDatePicker({
|
|
|
7912
8130
|
}
|
|
7913
8131
|
);
|
|
7914
8132
|
}
|
|
8133
|
+
function splitDateTime(v) {
|
|
8134
|
+
if (!v) return { date: null, hour: "00", minute: "00" };
|
|
8135
|
+
const [date = "", time = ""] = v.split(" ");
|
|
8136
|
+
const [hour = "00", minute = "00"] = time.split(":");
|
|
8137
|
+
return { date: date || null, hour, minute };
|
|
8138
|
+
}
|
|
7915
8139
|
function RangeCalendar({
|
|
7916
8140
|
value,
|
|
7917
8141
|
selectable,
|
|
7918
8142
|
maxRange,
|
|
8143
|
+
useTimePicker = false,
|
|
7919
8144
|
onSelect,
|
|
7920
8145
|
onPendingStartChange,
|
|
7921
8146
|
onViewChange
|
|
7922
8147
|
}) {
|
|
7923
8148
|
const today = todayStr();
|
|
7924
|
-
const
|
|
8149
|
+
const initial = splitDateTime(value?.[0]);
|
|
8150
|
+
const base = initial.date || today;
|
|
7925
8151
|
const [y, setY] = useState(Number(base.split("-")[0]));
|
|
7926
8152
|
const [m, setM] = useState(Number(base.split("-")[1]));
|
|
7927
|
-
const [start, setStart] = useState(
|
|
7928
|
-
const [end, setEnd] = useState(value?.[1]
|
|
8153
|
+
const [start, setStart] = useState(initial.date);
|
|
8154
|
+
const [end, setEnd] = useState(splitDateTime(value?.[1]).date);
|
|
7929
8155
|
const [hover, setHover] = useState(null);
|
|
8156
|
+
const [sH, setSH] = useState(initial.hour);
|
|
8157
|
+
const [sM, setSM] = useState(initial.minute);
|
|
8158
|
+
const [eH, setEH] = useState(splitDateTime(value?.[1]).hour);
|
|
8159
|
+
const [eM, setEM] = useState(splitDateTime(value?.[1]).minute);
|
|
8160
|
+
const lastEmitted = useRef("");
|
|
7930
8161
|
useEffect(() => {
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
8162
|
+
if (useTimePicker && JSON.stringify(value) === lastEmitted.current) return;
|
|
8163
|
+
const s = splitDateTime(value?.[0]);
|
|
8164
|
+
const e = splitDateTime(value?.[1]);
|
|
8165
|
+
setStart(s.date);
|
|
8166
|
+
setEnd(e.date);
|
|
8167
|
+
setSH(s.hour);
|
|
8168
|
+
setSM(s.minute);
|
|
8169
|
+
setEH(e.hour);
|
|
8170
|
+
setEM(e.minute);
|
|
8171
|
+
}, [value, useTimePicker]);
|
|
7934
8172
|
const setBase = (ny, nm) => {
|
|
7935
8173
|
setY(ny);
|
|
7936
8174
|
setM(nm);
|
|
@@ -7974,18 +8212,53 @@ function RangeCalendar({
|
|
|
7974
8212
|
const hasRange = !!(rStart && rEnd && rStart !== rEnd);
|
|
7975
8213
|
const inRange = (d) => !!(rStart && rEnd && d > rStart && d < rEnd);
|
|
7976
8214
|
const RANGE_BG = "var(--cmp-datepicker-calendar-range-bg)";
|
|
8215
|
+
const pad = (v) => String(Number(v) || 0).padStart(2, "0");
|
|
8216
|
+
const emit2 = (dateStart, dateEnd, time) => {
|
|
8217
|
+
const t = time ?? { sH, sM, eH, eM };
|
|
8218
|
+
const range = useTimePicker ? [`${dateStart} ${pad(t.sH)}:${pad(t.sM)}`, `${dateEnd} ${pad(t.eH)}:${pad(t.eM)}`] : [dateStart, dateEnd];
|
|
8219
|
+
lastEmitted.current = JSON.stringify(range);
|
|
8220
|
+
onSelect(range);
|
|
8221
|
+
};
|
|
7977
8222
|
const click = (d) => {
|
|
7978
8223
|
if (isDisabled(d)) return;
|
|
7979
8224
|
if (!start || complete || d < start) {
|
|
7980
8225
|
setStart(d);
|
|
7981
8226
|
setEnd(null);
|
|
7982
8227
|
setHover(null);
|
|
8228
|
+
if (useTimePicker) {
|
|
8229
|
+
setSH("00");
|
|
8230
|
+
setSM("00");
|
|
8231
|
+
setEH("23");
|
|
8232
|
+
setEM("59");
|
|
8233
|
+
}
|
|
7983
8234
|
onPendingStartChange?.(d);
|
|
7984
8235
|
return;
|
|
7985
8236
|
}
|
|
7986
8237
|
setEnd(d);
|
|
7987
8238
|
onPendingStartChange?.(null);
|
|
7988
|
-
|
|
8239
|
+
emit2(start, d, useTimePicker ? { sH, sM, eH: "23", eM: "59" } : void 0);
|
|
8240
|
+
};
|
|
8241
|
+
const changeTime = (which, part, raw) => {
|
|
8242
|
+
const digits = raw.replace(/\D/g, "").slice(0, 2);
|
|
8243
|
+
const max = part === "h" ? 23 : 59;
|
|
8244
|
+
const next = digits !== "" && Number(digits) > max ? String(max) : digits;
|
|
8245
|
+
const nextTime = { sH, sM, eH, eM };
|
|
8246
|
+
if (which === "start") nextTime[part === "h" ? "sH" : "sM"] = next;
|
|
8247
|
+
else nextTime[part === "h" ? "eH" : "eM"] = next;
|
|
8248
|
+
setSH(nextTime.sH);
|
|
8249
|
+
setSM(nextTime.sM);
|
|
8250
|
+
setEH(nextTime.eH);
|
|
8251
|
+
setEM(nextTime.eM);
|
|
8252
|
+
if (start && end) emit2(start, end, nextTime);
|
|
8253
|
+
};
|
|
8254
|
+
const blurTime = (which, part) => {
|
|
8255
|
+
if (which === "start") {
|
|
8256
|
+
if (part === "h") setSH(pad(sH));
|
|
8257
|
+
else setSM(pad(sM));
|
|
8258
|
+
} else {
|
|
8259
|
+
if (part === "h") setEH(pad(eH));
|
|
8260
|
+
else setEM(pad(eM));
|
|
8261
|
+
}
|
|
7989
8262
|
};
|
|
7990
8263
|
const GRID_COLS = "grid grid-cols-[repeat(7,var(--cmp-datepicker-calendar-day-width))]";
|
|
7991
8264
|
const renderPanel = (gy, gm, isLeft) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-[var(--cmp-datepicker-calendar-gap)]", children: [
|
|
@@ -8060,35 +8333,120 @@ function RangeCalendar({
|
|
|
8060
8333
|
);
|
|
8061
8334
|
}) })
|
|
8062
8335
|
] });
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8336
|
+
const renderTimeSection = (which, date, suffix) => {
|
|
8337
|
+
const active = !!date;
|
|
8338
|
+
const [yy = "", mm = "", dd = ""] = date ? date.split("-") : [];
|
|
8339
|
+
const hour = which === "start" ? sH : eH;
|
|
8340
|
+
const minute = which === "start" ? sM : eM;
|
|
8341
|
+
const num = (n, strip) => active && n !== "" ? strip ? String(Number(n)) : n : "-";
|
|
8342
|
+
const INPUT_CLS = "h-[var(--cmp-datepicker-sm-height)] w-[40px] rounded-[var(--cmp-datepicker-sm-radius)] border border-solid border-[color:var(--cmp-datepicker-border-default)] bg-[var(--cmp-datepicker-bg-default)] text-center text-[12px] font-medium leading-[20px] text-[color:var(--cmp-datepicker-text-default)] outline-none placeholder:text-[color:var(--cmp-datepicker-text-disabled)] focus:border-[color:var(--cmp-datepicker-border-focus)] disabled:cursor-not-allowed";
|
|
8343
|
+
const LABEL = "text-[color:var(--cmp-datepicker-calendar-time-date-label)]";
|
|
8344
|
+
const UNIT = "text-[color:var(--cmp-datepicker-calendar-time-date-unit)]";
|
|
8345
|
+
return /* @__PURE__ */ jsxs(
|
|
8346
|
+
"div",
|
|
8347
|
+
{
|
|
8348
|
+
className: cn(
|
|
8349
|
+
"flex flex-1 items-center justify-center gap-[var(--cmp-datepicker-calendar-time-section-gap)] rounded-[var(--cmp-datepicker-calendar-time-radius)] px-[var(--cmp-datepicker-calendar-time-paddingX)] py-[var(--cmp-datepicker-calendar-time-paddingY)] text-[12px] font-medium leading-[20px]",
|
|
8350
|
+
active ? "bg-[var(--cmp-datepicker-calendar-time-bg-selected)]" : "bg-[var(--cmp-datepicker-calendar-time-bg-default)]"
|
|
8351
|
+
),
|
|
8352
|
+
children: [
|
|
8353
|
+
/* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-[var(--cmp-datepicker-calendar-time-date-gap)]", children: [
|
|
8354
|
+
/* @__PURE__ */ jsx("span", { className: LABEL, children: num(yy, false) }),
|
|
8355
|
+
/* @__PURE__ */ jsx("span", { className: UNIT, children: "\uB144" }),
|
|
8356
|
+
/* @__PURE__ */ jsx("span", { className: LABEL, children: num(mm, true) }),
|
|
8357
|
+
/* @__PURE__ */ jsx("span", { className: UNIT, children: "\uC6D4" }),
|
|
8358
|
+
/* @__PURE__ */ jsx("span", { className: LABEL, children: num(dd, true) }),
|
|
8359
|
+
/* @__PURE__ */ jsx("span", { className: UNIT, children: "\uC77C" })
|
|
8360
|
+
] }),
|
|
8361
|
+
/* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-[var(--cmp-datepicker-calendar-time-unit-gap)]", children: [
|
|
8362
|
+
/* @__PURE__ */ jsx(
|
|
8363
|
+
"input",
|
|
8364
|
+
{
|
|
8365
|
+
type: "text",
|
|
8366
|
+
inputMode: "numeric",
|
|
8367
|
+
maxLength: 2,
|
|
8368
|
+
"aria-label": `${suffix} \uC2DC`,
|
|
8369
|
+
disabled: !active,
|
|
8370
|
+
value: active ? hour : "",
|
|
8371
|
+
placeholder: "--",
|
|
8372
|
+
onChange: (e) => changeTime(which, "h", e.target.value),
|
|
8373
|
+
onBlur: () => blurTime(which, "h"),
|
|
8374
|
+
className: INPUT_CLS
|
|
8375
|
+
}
|
|
8376
|
+
),
|
|
8377
|
+
/* @__PURE__ */ jsx("span", { className: LABEL, children: ":" }),
|
|
8378
|
+
/* @__PURE__ */ jsx(
|
|
8379
|
+
"input",
|
|
8380
|
+
{
|
|
8381
|
+
type: "text",
|
|
8382
|
+
inputMode: "numeric",
|
|
8383
|
+
maxLength: 2,
|
|
8384
|
+
"aria-label": `${suffix} \uBD84`,
|
|
8385
|
+
disabled: !active,
|
|
8386
|
+
value: active ? minute : "",
|
|
8387
|
+
placeholder: "--",
|
|
8388
|
+
onChange: (e) => changeTime(which, "m", e.target.value),
|
|
8389
|
+
onBlur: () => blurTime(which, "m"),
|
|
8390
|
+
className: INPUT_CLS
|
|
8391
|
+
}
|
|
8392
|
+
)
|
|
8393
|
+
] }),
|
|
8394
|
+
/* @__PURE__ */ jsx("span", { className: UNIT, children: suffix })
|
|
8395
|
+
]
|
|
8396
|
+
}
|
|
8397
|
+
);
|
|
8398
|
+
};
|
|
8399
|
+
return /* @__PURE__ */ jsxs(
|
|
8400
|
+
"div",
|
|
8401
|
+
{
|
|
8402
|
+
className: cn(
|
|
8403
|
+
"inline-flex flex-col gap-[var(--cmp-datepicker-calendar-gap)] rounded-[var(--cmp-datepicker-calendar-radius)] bg-[var(--cmp-datepicker-calendar-bg)] p-[var(--cmp-datepicker-calendar-paddingXY)] shadow-[2px_2px_12px_2px_rgba(0,0,0,0.2)] select-none",
|
|
8404
|
+
useTimePicker ? "h-auto" : "h-[364px]"
|
|
8088
8405
|
),
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8406
|
+
children: [
|
|
8407
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
|
|
8408
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-[var(--cmp-datepicker-calendar-gap)] text-[14px] font-medium leading-[24px]", children: [
|
|
8409
|
+
/* @__PURE__ */ jsx(SGhostButton, { icon: "chevronLeft", size: "xxs", ariaLabel: "prevYear", onClick: goPrevYear }),
|
|
8410
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex min-w-[40px] items-center justify-center text-center", children: y }),
|
|
8411
|
+
/* @__PURE__ */ jsx(SGhostButton, { icon: "chevronRight", size: "xxs", ariaLabel: "nextYear", onClick: goNextYear })
|
|
8412
|
+
] }),
|
|
8413
|
+
/* @__PURE__ */ jsx(
|
|
8414
|
+
"button",
|
|
8415
|
+
{
|
|
8416
|
+
type: "button",
|
|
8417
|
+
onClick: goToday,
|
|
8418
|
+
className: "absolute right-0 h-[24px] min-w-[37px] cursor-pointer bg-transparent text-center text-[12px] font-medium leading-[24px] text-[color:var(--cmp-datepicker-calendar-day-default-text)] hover:underline",
|
|
8419
|
+
children: "\uC624\uB298"
|
|
8420
|
+
}
|
|
8421
|
+
)
|
|
8422
|
+
] }),
|
|
8423
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-range-panelGap)]", children: [
|
|
8424
|
+
renderPanel(y, m, true),
|
|
8425
|
+
/* @__PURE__ */ jsx(
|
|
8426
|
+
"span",
|
|
8427
|
+
{
|
|
8428
|
+
className: "w-px shrink-0 self-stretch bg-[var(--cmp-datepicker-calendar-range-divider)]",
|
|
8429
|
+
"aria-hidden": true
|
|
8430
|
+
}
|
|
8431
|
+
),
|
|
8432
|
+
renderPanel(nextMonthYM.year, nextMonthYM.month, false)
|
|
8433
|
+
] }),
|
|
8434
|
+
useTimePicker && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8435
|
+
/* @__PURE__ */ jsx(
|
|
8436
|
+
"span",
|
|
8437
|
+
{
|
|
8438
|
+
className: "h-px w-full bg-[var(--cmp-datepicker-calendar-range-divider)]",
|
|
8439
|
+
"aria-hidden": true
|
|
8440
|
+
}
|
|
8441
|
+
),
|
|
8442
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-[var(--cmp-datepicker-calendar-time-gap)]", children: [
|
|
8443
|
+
renderTimeSection("start", start, "\uBD80\uD130"),
|
|
8444
|
+
renderTimeSection("end", end, "\uAE4C\uC9C0")
|
|
8445
|
+
] })
|
|
8446
|
+
] })
|
|
8447
|
+
]
|
|
8448
|
+
}
|
|
8449
|
+
);
|
|
8092
8450
|
}
|
|
8093
8451
|
function SDateRangePicker({
|
|
8094
8452
|
value,
|
|
@@ -8098,6 +8456,7 @@ function SDateRangePicker({
|
|
|
8098
8456
|
placeholder = "YYYY-MM-DD ~ YYYY-MM-DD",
|
|
8099
8457
|
selectable,
|
|
8100
8458
|
maxRange,
|
|
8459
|
+
useTimePicker = false,
|
|
8101
8460
|
disabled = false,
|
|
8102
8461
|
width,
|
|
8103
8462
|
name,
|
|
@@ -8141,8 +8500,9 @@ function SDateRangePicker({
|
|
|
8141
8500
|
setPendingStart(null);
|
|
8142
8501
|
onValueChange?.(r);
|
|
8143
8502
|
if (rules && rules.length > 0) setRuleError(runRules(r, rules));
|
|
8144
|
-
setOpen(false);
|
|
8503
|
+
if (!useTimePicker) setOpen(false);
|
|
8145
8504
|
};
|
|
8505
|
+
const resolvedPlaceholder = useTimePicker && placeholder === "YYYY-MM-DD ~ YYYY-MM-DD" ? "YYYY-MM-DD HH:mm ~ YYYY-MM-DD HH:mm" : placeholder;
|
|
8146
8506
|
return /* @__PURE__ */ jsx(
|
|
8147
8507
|
SField,
|
|
8148
8508
|
{
|
|
@@ -8209,7 +8569,7 @@ function SDateRangePicker({
|
|
|
8209
8569
|
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-right", children: startText }),
|
|
8210
8570
|
/* @__PURE__ */ jsx("span", { className: "px-[4px]", children: "~" }),
|
|
8211
8571
|
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: endText })
|
|
8212
|
-
] }) :
|
|
8572
|
+
] }) : resolvedPlaceholder
|
|
8213
8573
|
}
|
|
8214
8574
|
)
|
|
8215
8575
|
]
|
|
@@ -8228,6 +8588,7 @@ function SDateRangePicker({
|
|
|
8228
8588
|
value: value ?? null,
|
|
8229
8589
|
selectable,
|
|
8230
8590
|
maxRange,
|
|
8591
|
+
useTimePicker,
|
|
8231
8592
|
onSelect: select,
|
|
8232
8593
|
onPendingStartChange: setPendingStart,
|
|
8233
8594
|
onViewChange
|
|
@@ -10262,7 +10623,9 @@ function RangeTimeSelector({
|
|
|
10262
10623
|
};
|
|
10263
10624
|
const commit = (side, hour, minute, direction = "unknown") => {
|
|
10264
10625
|
const next = formatTimeValue(hour, minute);
|
|
10265
|
-
onChange(
|
|
10626
|
+
onChange(
|
|
10627
|
+
normalizeNextRange(side === "start" ? [next, range[1]] : [range[0], next], side, direction)
|
|
10628
|
+
);
|
|
10266
10629
|
};
|
|
10267
10630
|
const getTimeMinutes2 = (time) => {
|
|
10268
10631
|
const { hour, minute } = parseTimeValue(time);
|
|
@@ -10654,6 +11017,6 @@ function STimeRangePicker({
|
|
|
10654
11017
|
);
|
|
10655
11018
|
}
|
|
10656
11019
|
|
|
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 };
|
|
11020
|
+
export { BADGE_COLORS, BUTTON_COLORS, BUTTON_SIZES, COLOR_KEYS, GNB_COLORS, GNB_COMMON, GNB_FOLD_MS, GNB_HEADER, GNB_MENU, GNB_MENU_WIDTH, GNB_RAIL, GNB_RAIL_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, findGnbRailValue, isColorKey, loading, resolveColor, useSLayout };
|
|
10658
11021
|
//# sourceMappingURL=index.js.map
|
|
10659
11022
|
//# sourceMappingURL=index.js.map
|