sellmate-design-system-react 0.4.0 → 0.5.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/dist/components/SGnb/SGnb.cjs +238 -79
- package/dist/components/SGnb/SGnb.cjs.map +1 -1
- package/dist/components/SGnb/SGnb.d.ts +17 -4
- package/dist/components/SGnb/SGnb.js +238 -79
- 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/SLayout/SLayout.cjs +20 -19
- package/dist/components/SLayout/SLayout.cjs.map +1 -1
- package/dist/components/SLayout/SLayout.d.ts +4 -0
- package/dist/components/SLayout/SLayout.js +20 -19
- package/dist/components/SLayout/SLayout.js.map +1 -1
- package/dist/components/SPage/SPage.cjs +21 -20
- package/dist/components/SPage/SPage.cjs.map +1 -1
- package/dist/components/SPage/SPage.js +21 -20
- 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 +261 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +258 -108
- package/dist/index.js.map +1 -1
- package/dist/styles.css +12 -4
- package/dist/theme.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4904,73 +4904,70 @@ function SLoadingModal({
|
|
|
4904
4904
|
}
|
|
4905
4905
|
|
|
4906
4906
|
// src/components/SGnb/gnb.config.ts
|
|
4907
|
-
var
|
|
4908
|
-
var box = (p) => `var(--cmp-
|
|
4909
|
-
var
|
|
4910
|
-
var
|
|
4907
|
+
var gnb = (p) => `var(--cmp-gnb-${p})`;
|
|
4908
|
+
var box = (p) => `var(--cmp-gnb-box-${p})`;
|
|
4909
|
+
var belt = (p) => `var(--cmp-gnb-belt-${p})`;
|
|
4910
|
+
var light = (p) => `var(--cmp-gnb-light-${p})`;
|
|
4911
|
+
var dark = (p) => `var(--cmp-gnb-dark-${p})`;
|
|
4912
|
+
var HEADER_TOP = {
|
|
4913
|
+
topHeight: gnb("top-height"),
|
|
4914
|
+
topPaddingX: gnb("top-paddingX"),
|
|
4915
|
+
topGap: gnb("top-gap"),
|
|
4916
|
+
logoHeight: gnb("logo-height")
|
|
4917
|
+
};
|
|
4911
4918
|
var GNB_HEADER = {
|
|
4912
|
-
fix:
|
|
4913
|
-
|
|
4914
|
-
topPaddingX: belt("top-paddingX"),
|
|
4915
|
-
topGap: belt("top-gap"),
|
|
4916
|
-
logoHeight: belt("logo-height")
|
|
4917
|
-
},
|
|
4918
|
-
full: {
|
|
4919
|
-
topHeight: box("top-height"),
|
|
4920
|
-
topPaddingX: box("paddingX"),
|
|
4921
|
-
topGap: box("gap"),
|
|
4922
|
-
// 상단바 gap (버튼 간 세부 gap 은 --cmp-gnbBox-button-gap)
|
|
4923
|
-
logoHeight: box("logo-height")
|
|
4924
|
-
}
|
|
4919
|
+
fix: HEADER_TOP,
|
|
4920
|
+
full: HEADER_TOP
|
|
4925
4921
|
};
|
|
4926
4922
|
var GNB_MENU = {
|
|
4927
4923
|
box: {
|
|
4928
|
-
//
|
|
4929
|
-
bodyPaddingY:
|
|
4930
|
-
itemMarginX:
|
|
4931
|
-
|
|
4924
|
+
// 바디 패딩·세로 패딩·chevron 은 gnb 로 통합된 공통 토큰
|
|
4925
|
+
bodyPaddingY: gnb("body-padding"),
|
|
4926
|
+
itemMarginX: gnb("body-padding"),
|
|
4927
|
+
// 전방향 토큰 → 아이템 좌우 여백에도 사용
|
|
4928
|
+
itemPaddingY: gnb("item-paddingY"),
|
|
4932
4929
|
itemPaddingRight: box("item-paddingRight"),
|
|
4933
4930
|
itemGap: box("item-gap"),
|
|
4934
4931
|
itemRadius: box("item-radius"),
|
|
4935
|
-
itemArrow:
|
|
4932
|
+
itemArrow: gnb("item-chevron-size"),
|
|
4936
4933
|
depth1PaddingLeft: box("item-depth1-paddingLeft"),
|
|
4937
4934
|
depth2PaddingLeft: box("item-depth2-paddingLeft"),
|
|
4938
4935
|
depth3PaddingLeft: box("item-depth3-paddingLeft"),
|
|
4939
4936
|
depth1Icon: box("item-depth1-icon"),
|
|
4940
4937
|
depth1FontSize: 14,
|
|
4941
|
-
// token:
|
|
4938
|
+
// token: gnb.box.item.typography.selected.fontSize
|
|
4942
4939
|
depth1LineHeight: 24,
|
|
4943
|
-
// token:
|
|
4940
|
+
// token: gnb.box.item.typography.selected.lineHeight
|
|
4944
4941
|
depth1FontWeight: 700,
|
|
4945
4942
|
itemFontSize: 14,
|
|
4946
|
-
// token:
|
|
4943
|
+
// token: gnb.box.item.typography.default.fontSize
|
|
4947
4944
|
itemLineHeight: 24
|
|
4948
|
-
// token:
|
|
4945
|
+
// token: gnb.box.item.typography.default.lineHeight
|
|
4949
4946
|
},
|
|
4950
4947
|
belt: {
|
|
4951
|
-
bodyPaddingY:
|
|
4948
|
+
bodyPaddingY: gnb("body-padding"),
|
|
4952
4949
|
itemMarginX: "0px",
|
|
4953
4950
|
// belt 토큰에 가로 여백 없음 → 풀폭 행 (들여쓰기는 depth*-paddingLeft 전담)
|
|
4954
|
-
itemPaddingY:
|
|
4951
|
+
itemPaddingY: gnb("item-paddingY"),
|
|
4955
4952
|
itemPaddingRight: belt("item-paddingRight"),
|
|
4956
4953
|
itemGap: belt("item-gap"),
|
|
4957
4954
|
itemRadius: "0px",
|
|
4958
4955
|
// belt 토큰에 radius 없음 → 풀폭 행
|
|
4959
|
-
itemArrow:
|
|
4956
|
+
itemArrow: gnb("item-chevron-size"),
|
|
4960
4957
|
depth1PaddingLeft: belt("item-depth1-paddingLeft"),
|
|
4961
4958
|
depth2PaddingLeft: belt("item-depth2-paddingLeft"),
|
|
4962
4959
|
depth3PaddingLeft: belt("item-depth3-paddingLeft"),
|
|
4963
|
-
depth1Icon:
|
|
4964
|
-
// belt
|
|
4960
|
+
depth1Icon: belt("item-depth1-icon"),
|
|
4961
|
+
// belt 전용 depth1 아이콘 토큰(16)
|
|
4965
4962
|
depth1FontSize: 13,
|
|
4966
|
-
// token:
|
|
4963
|
+
// token: gnb.belt.item.depth1.typography.fontSize
|
|
4967
4964
|
depth1LineHeight: 22,
|
|
4968
|
-
// token:
|
|
4965
|
+
// token: gnb.belt.item.depth1.typography.lineHeight
|
|
4969
4966
|
depth1FontWeight: 700,
|
|
4970
4967
|
itemFontSize: 12,
|
|
4971
|
-
// token:
|
|
4968
|
+
// token: gnb.belt.item.typography.default.fontSize
|
|
4972
4969
|
itemLineHeight: 20
|
|
4973
|
-
// token:
|
|
4970
|
+
// token: gnb.belt.item.typography.default.lineHeight
|
|
4974
4971
|
}
|
|
4975
4972
|
};
|
|
4976
4973
|
var GNB_COLORS = {
|
|
@@ -4983,7 +4980,9 @@ var GNB_COLORS = {
|
|
|
4983
4980
|
itemColorDefault: light("item-fg-default"),
|
|
4984
4981
|
itemColorHover: light("item-fg-hover"),
|
|
4985
4982
|
itemColorActive: light("item-fg-selected"),
|
|
4986
|
-
itemChevron: light("item-chevron")
|
|
4983
|
+
itemChevron: light("item-chevron"),
|
|
4984
|
+
itemIcon: "grey_65",
|
|
4985
|
+
railFgDefault: light("rail-fg-default")
|
|
4987
4986
|
},
|
|
4988
4987
|
dark: {
|
|
4989
4988
|
panelBg: dark("bg"),
|
|
@@ -4994,19 +4993,41 @@ var GNB_COLORS = {
|
|
|
4994
4993
|
itemColorDefault: dark("item-fg-default"),
|
|
4995
4994
|
itemColorHover: dark("item-fg-hover"),
|
|
4996
4995
|
itemColorActive: dark("item-fg-selected"),
|
|
4997
|
-
itemChevron: dark("item-chevron")
|
|
4996
|
+
itemChevron: dark("item-chevron"),
|
|
4997
|
+
itemIcon: "grey_55",
|
|
4998
|
+
railFgDefault: dark("rail-fg-default")
|
|
4998
4999
|
}
|
|
4999
5000
|
};
|
|
5000
5001
|
var GNB_COMMON = {
|
|
5001
5002
|
/** 접힘 레일 폭. header="full" 은 접으면 메뉴만 사라져 레일이 없으므로 fix 전용. */
|
|
5002
|
-
foldWidth:
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5003
|
+
foldWidth: gnb("fold-width")
|
|
5004
|
+
};
|
|
5005
|
+
var GNB_RAIL = {
|
|
5006
|
+
padding: gnb("rail-padding"),
|
|
5007
|
+
gap: gnb("rail-gap"),
|
|
5008
|
+
itemWidth: gnb("rail-item-width"),
|
|
5009
|
+
itemPaddingTop: gnb("rail-item-paddingTop"),
|
|
5010
|
+
itemPaddingBottom: gnb("rail-item-paddingBottom"),
|
|
5011
|
+
itemRadius: gnb("rail-item-radius"),
|
|
5012
|
+
itemIcon: gnb("rail-item-icon"),
|
|
5013
|
+
// 타이포 (theme.css 에 gnb 타이포 변수 없음 → 토큰 값 직접 명시)
|
|
5014
|
+
itemFontSize: 11,
|
|
5015
|
+
// token: gnb.rail.item.typography.default.fontSize
|
|
5016
|
+
itemLineHeight: 18,
|
|
5017
|
+
// token: gnb.rail.item.typography.default.lineHeight
|
|
5018
|
+
itemFontWeight: 500,
|
|
5019
|
+
// token: gnb.rail.item.typography.default.fontWeight
|
|
5020
|
+
itemFontWeightSelected: 700
|
|
5021
|
+
// token: gnb.rail.item.typography.selected.fontWeight
|
|
5022
|
+
};
|
|
5023
|
+
var GNB_RAIL_WIDTH = 68;
|
|
5024
|
+
var GNB_FOLD_MS = 200;
|
|
5025
|
+
var GNB_EXPAND_MS = 200;
|
|
5009
5026
|
var GNB_MENU_WIDTH = 240;
|
|
5027
|
+
function findGnbRailValue(items, value) {
|
|
5028
|
+
if (items.some((item) => item.value === value)) return value;
|
|
5029
|
+
return findGnbAncestors(items, value)[0];
|
|
5030
|
+
}
|
|
5010
5031
|
function findGnbAncestors(items, value) {
|
|
5011
5032
|
const walk = (list, trail) => {
|
|
5012
5033
|
for (const item of list) {
|
|
@@ -5029,6 +5050,7 @@ function useSLayout() {
|
|
|
5029
5050
|
var SLayout = react.forwardRef(function SLayout2({
|
|
5030
5051
|
type = "box",
|
|
5031
5052
|
header = "fix",
|
|
5053
|
+
useRail = false,
|
|
5032
5054
|
folded,
|
|
5033
5055
|
defaultFolded = false,
|
|
5034
5056
|
onFoldedChange,
|
|
@@ -5048,13 +5070,17 @@ var SLayout = react.forwardRef(function SLayout2({
|
|
|
5048
5070
|
[isControlled, onFoldedChange]
|
|
5049
5071
|
);
|
|
5050
5072
|
const ctx = react.useMemo(
|
|
5051
|
-
() => ({ type, header, folded: currentFolded, setFolded }),
|
|
5052
|
-
[type, header, currentFolded, setFolded]
|
|
5073
|
+
() => ({ type, header, useRail, folded: currentFolded, setFolded }),
|
|
5074
|
+
[type, header, useRail, currentFolded, setFolded]
|
|
5053
5075
|
);
|
|
5054
5076
|
const isFullHeader = header === "full";
|
|
5077
|
+
const gnbColumnWidth = useRail ? GNB_RAIL_WIDTH + GNB_MENU_WIDTH : GNB_MENU_WIDTH;
|
|
5055
5078
|
const frameStyle = isFullHeader ? {
|
|
5056
|
-
gridTemplateColumns: `${currentFolded ? 0 :
|
|
5057
|
-
gridTemplateRows: `${GNB_HEADER[header].topHeight} 1fr
|
|
5079
|
+
gridTemplateColumns: `${currentFolded ? 0 : gnbColumnWidth}px 1fr`,
|
|
5080
|
+
gridTemplateRows: `${GNB_HEADER[header].topHeight} 1fr`,
|
|
5081
|
+
// 메뉴가 미끄러지는 동안 페이지 열도 같은 길이로 따라와야 한 덩어리로 읽힌다.
|
|
5082
|
+
// (fix 는 SGnb 가 제 폭을 직접 전환하므로 프레임이 손댈 게 없다)
|
|
5083
|
+
transition: `grid-template-columns ${GNB_FOLD_MS}ms ease-out`
|
|
5058
5084
|
} : void 0;
|
|
5059
5085
|
return /* @__PURE__ */ jsxRuntime.jsx(SLayoutContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5060
5086
|
"div",
|
|
@@ -5079,8 +5105,10 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5079
5105
|
header: headerProp,
|
|
5080
5106
|
color = "light",
|
|
5081
5107
|
items,
|
|
5108
|
+
useRail: useRailProp,
|
|
5082
5109
|
value = "",
|
|
5083
5110
|
onValueChange,
|
|
5111
|
+
onRailChange,
|
|
5084
5112
|
folded: foldedProp,
|
|
5085
5113
|
onFoldChange,
|
|
5086
5114
|
onLauncherClick,
|
|
@@ -5094,6 +5122,7 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5094
5122
|
const type = typeProp ?? layout?.type ?? "box";
|
|
5095
5123
|
const header = headerProp ?? layout?.header ?? "fix";
|
|
5096
5124
|
const folded = foldedProp ?? layout?.folded ?? false;
|
|
5125
|
+
const useRail = useRailProp ?? layout?.useRail ?? false;
|
|
5097
5126
|
const H = GNB_HEADER[header];
|
|
5098
5127
|
const M = GNB_MENU[type];
|
|
5099
5128
|
const C = GNB_COLORS[color];
|
|
@@ -5101,6 +5130,9 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5101
5130
|
() => new Set(findGnbAncestors(items, value))
|
|
5102
5131
|
);
|
|
5103
5132
|
const [hoveredKey, setHoveredKey] = react.useState(null);
|
|
5133
|
+
const [activeRail, setActiveRail] = react.useState(
|
|
5134
|
+
() => findGnbRailValue(items, value) ?? items[0]?.value
|
|
5135
|
+
);
|
|
5104
5136
|
react.useEffect(() => {
|
|
5105
5137
|
const ancestors = findGnbAncestors(items, value);
|
|
5106
5138
|
if (ancestors.length === 0) return;
|
|
@@ -5111,6 +5143,15 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5111
5143
|
return next;
|
|
5112
5144
|
});
|
|
5113
5145
|
}, [items, value]);
|
|
5146
|
+
react.useEffect(() => {
|
|
5147
|
+
const owner = findGnbRailValue(items, value);
|
|
5148
|
+
if (owner !== void 0) setActiveRail(owner);
|
|
5149
|
+
}, [items, value]);
|
|
5150
|
+
react.useEffect(() => {
|
|
5151
|
+
setActiveRail(
|
|
5152
|
+
(prev) => prev !== void 0 && items.some((item) => item.value === prev) ? prev : items[0]?.value
|
|
5153
|
+
);
|
|
5154
|
+
}, [items]);
|
|
5114
5155
|
const toggleExpand = (key) => {
|
|
5115
5156
|
setExpandedKeys((prev) => {
|
|
5116
5157
|
const next = new Set(prev);
|
|
@@ -5127,16 +5168,58 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5127
5168
|
}
|
|
5128
5169
|
onValueChange?.(item.value);
|
|
5129
5170
|
};
|
|
5171
|
+
const handleRailClick = (item) => {
|
|
5172
|
+
if (item.disabled) return;
|
|
5173
|
+
setActiveRail(item.value);
|
|
5174
|
+
onRailChange?.(item.value);
|
|
5175
|
+
if (!item.children || item.children.length === 0) onValueChange?.(item.value);
|
|
5176
|
+
};
|
|
5130
5177
|
const handleFoldToggle = () => {
|
|
5131
5178
|
const next = !folded;
|
|
5132
5179
|
onFoldChange?.(next);
|
|
5133
5180
|
if (foldedProp === void 0) layout?.setFolded(next);
|
|
5134
5181
|
};
|
|
5135
|
-
const
|
|
5182
|
+
const renderRailItem = (item) => {
|
|
5183
|
+
const isActive = item.value === activeRail;
|
|
5184
|
+
const isHovered = hoveredKey === item.value && !item.disabled;
|
|
5185
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5186
|
+
"button",
|
|
5187
|
+
{
|
|
5188
|
+
type: "button",
|
|
5189
|
+
disabled: item.disabled,
|
|
5190
|
+
"aria-current": isActive ? "true" : void 0,
|
|
5191
|
+
className: "box-border flex select-none flex-col items-center border-none bg-transparent transition-colors duration-150",
|
|
5192
|
+
style: {
|
|
5193
|
+
width: GNB_RAIL.itemWidth,
|
|
5194
|
+
paddingTop: GNB_RAIL.itemPaddingTop,
|
|
5195
|
+
paddingBottom: GNB_RAIL.itemPaddingBottom,
|
|
5196
|
+
borderRadius: GNB_RAIL.itemRadius,
|
|
5197
|
+
backgroundColor: isActive ? C.itemBgSelected : isHovered ? C.itemBgHover : C.itemBgDefault,
|
|
5198
|
+
color: isActive ? C.itemColorActive : C.railFgDefault,
|
|
5199
|
+
// <button> 의 UA 폰트를 끊고 앱 폰트를 잇는다 (메뉴 아이템과 같은 이유)
|
|
5200
|
+
fontFamily: "inherit",
|
|
5201
|
+
fontSize: GNB_RAIL.itemFontSize,
|
|
5202
|
+
lineHeight: `${GNB_RAIL.itemLineHeight}px`,
|
|
5203
|
+
fontWeight: isActive ? GNB_RAIL.itemFontWeightSelected : GNB_RAIL.itemFontWeight,
|
|
5204
|
+
opacity: item.disabled ? 0.4 : 1,
|
|
5205
|
+
cursor: item.disabled ? "not-allowed" : "pointer"
|
|
5206
|
+
},
|
|
5207
|
+
onClick: () => handleRailClick(item),
|
|
5208
|
+
onMouseEnter: () => setHoveredKey(item.value),
|
|
5209
|
+
onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
|
|
5210
|
+
children: [
|
|
5211
|
+
item.icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: item.icon, size: GNB_RAIL.itemIcon, className: "shrink-0" }),
|
|
5212
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-full overflow-hidden text-ellipsis whitespace-nowrap text-center", children: item.label })
|
|
5213
|
+
]
|
|
5214
|
+
}
|
|
5215
|
+
) }, item.value);
|
|
5216
|
+
};
|
|
5217
|
+
const renderItem = (item, depth, branchHasIcon = false) => {
|
|
5136
5218
|
const hasChildren = !!(item.children && item.children.length > 0);
|
|
5137
5219
|
const isSelected = !hasChildren && item.value === value;
|
|
5138
5220
|
const isExpanded = expandedKeys.has(item.value);
|
|
5139
5221
|
const isHovered = hoveredKey === item.value && !item.disabled;
|
|
5222
|
+
const iconBranch = depth === 1 ? !!item.icon : branchHasIcon;
|
|
5140
5223
|
const itemStyle = {
|
|
5141
5224
|
gap: M.itemGap,
|
|
5142
5225
|
paddingTop: M.itemPaddingY,
|
|
@@ -5173,8 +5256,11 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5173
5256
|
onMouseEnter: () => setHoveredKey(item.value),
|
|
5174
5257
|
onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
|
|
5175
5258
|
children: [
|
|
5176
|
-
depth === 1 && item.icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: item.icon, size: M.depth1Icon, className: "shrink-0" }),
|
|
5177
|
-
/* @__PURE__ */ jsxRuntime.
|
|
5259
|
+
depth === 1 && item.icon && /* @__PURE__ */ jsxRuntime.jsx(SIcon, { name: item.icon, size: M.depth1Icon, color: C.itemIcon, className: "shrink-0" }),
|
|
5260
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap flex flex-no-wrap items-center", children: [
|
|
5261
|
+
depth > 1 && iconBranch ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-[20px] h-[24px] flex items-center justify-center mr-[8px]", children: "-" }) : null,
|
|
5262
|
+
item.label
|
|
5263
|
+
] }),
|
|
5178
5264
|
item.tag !== void 0 && item.tag !== "" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5179
5265
|
STag,
|
|
5180
5266
|
{
|
|
@@ -5192,28 +5278,53 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5192
5278
|
size: M.itemArrow,
|
|
5193
5279
|
color: C.itemChevron,
|
|
5194
5280
|
rotate: isExpanded ? 180 : 0,
|
|
5195
|
-
className: "shrink-0 transition-transform duration-
|
|
5281
|
+
className: "shrink-0 transition-transform duration-200"
|
|
5196
5282
|
}
|
|
5197
5283
|
)
|
|
5198
5284
|
]
|
|
5199
5285
|
}
|
|
5200
5286
|
),
|
|
5201
|
-
hasChildren &&
|
|
5287
|
+
hasChildren && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5288
|
+
"div",
|
|
5289
|
+
{
|
|
5290
|
+
className: "grid",
|
|
5291
|
+
style: {
|
|
5292
|
+
gridTemplateRows: isExpanded ? "1fr" : "0fr",
|
|
5293
|
+
transition: `grid-template-rows ${GNB_EXPAND_MS}ms ease-out`
|
|
5294
|
+
},
|
|
5295
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5296
|
+
"ul",
|
|
5297
|
+
{
|
|
5298
|
+
className: "m-0 flex min-h-0 list-none flex-col overflow-hidden p-0",
|
|
5299
|
+
style: {
|
|
5300
|
+
visibility: isExpanded ? "visible" : "hidden",
|
|
5301
|
+
transition: `visibility 0s linear ${isExpanded ? "0s" : `${GNB_EXPAND_MS}ms`}`
|
|
5302
|
+
},
|
|
5303
|
+
children: item.children.map((child) => renderItem(child, depth + 1, iconBranch))
|
|
5304
|
+
}
|
|
5305
|
+
)
|
|
5306
|
+
}
|
|
5307
|
+
)
|
|
5202
5308
|
] }, item.value)
|
|
5203
5309
|
);
|
|
5204
5310
|
};
|
|
5311
|
+
const railItems = useRail ? items : [];
|
|
5312
|
+
const menuItems = useRail ? items.find((item) => item.value === activeRail)?.children ?? [] : items;
|
|
5313
|
+
const bodyWidth = useRail ? GNB_RAIL_WIDTH + GNB_MENU_WIDTH : GNB_MENU_WIDTH;
|
|
5205
5314
|
const railFolded = folded && header === "fix";
|
|
5206
|
-
const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth :
|
|
5315
|
+
const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth : bodyWidth;
|
|
5207
5316
|
const rootStyle = {
|
|
5208
5317
|
width: rootWidth,
|
|
5318
|
+
transition: `width ${GNB_FOLD_MS}ms ease-out`,
|
|
5209
5319
|
...header === "full" ? { gridArea: "1 / 1 / -1 / -1", pointerEvents: "none" } : { backgroundColor: C.panelBg },
|
|
5210
5320
|
...style
|
|
5211
5321
|
};
|
|
5322
|
+
const hideAfterSlide = (hidden) => `visibility 0s linear ${hidden ? `${GNB_FOLD_MS}ms` : "0s"}`;
|
|
5212
5323
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5213
5324
|
"div",
|
|
5214
5325
|
{
|
|
5215
5326
|
ref,
|
|
5216
|
-
className: cn("box-border flex h-full shrink-0 flex-col", className),
|
|
5327
|
+
className: cn("box-border flex h-full shrink-0 flex-col overflow-hidden", className),
|
|
5217
5328
|
style: rootStyle,
|
|
5218
5329
|
"data-type": type,
|
|
5219
5330
|
"data-header": header,
|
|
@@ -5223,30 +5334,47 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5223
5334
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5224
5335
|
"div",
|
|
5225
5336
|
{
|
|
5226
|
-
className:
|
|
5337
|
+
className: "relative box-border flex shrink-0 items-center overflow-hidden",
|
|
5227
5338
|
style: {
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
padding: railFolded ? `${GNB_COMMON.buttonGap} 0` : `0 ${H.topPaddingX}`,
|
|
5339
|
+
height: H.topHeight,
|
|
5340
|
+
padding: `0 ${railFolded ? GNB_RAIL.padding : H.topPaddingX}`,
|
|
5231
5341
|
borderBottom: `1px solid ${C.topBorder}`,
|
|
5232
5342
|
backgroundColor: C.panelBg,
|
|
5233
5343
|
pointerEvents: "auto",
|
|
5344
|
+
transition: `padding ${GNB_FOLD_MS}ms ease-out`,
|
|
5234
5345
|
// 아이콘 버튼은 자체 ghostButton 토큰을 쓰므로, 이 색은 logo 슬롯 상속용
|
|
5235
5346
|
color: C.itemColorDefault
|
|
5236
5347
|
},
|
|
5237
5348
|
children: [
|
|
5238
|
-
onLauncherClick && /* @__PURE__ */ jsxRuntime.
|
|
5239
|
-
|
|
5349
|
+
(onLauncherClick || logo) && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5350
|
+
"div",
|
|
5240
5351
|
{
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5352
|
+
className: "absolute inset-y-0 flex items-center",
|
|
5353
|
+
style: {
|
|
5354
|
+
left: H.topPaddingX,
|
|
5355
|
+
gap: H.topGap,
|
|
5356
|
+
opacity: railFolded ? 0 : 1,
|
|
5357
|
+
transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}, transform ${GNB_FOLD_MS}ms ease-out`,
|
|
5358
|
+
// 메뉴와 같은 방향으로 빠지되, 좁은 레일 안에서 꼬리가 비치지 않게 페이드가 거리를 대신한다
|
|
5359
|
+
transform: railFolded ? `translateX(-${H.topPaddingX})` : "none",
|
|
5360
|
+
visibility: railFolded ? "hidden" : "visible"
|
|
5361
|
+
},
|
|
5362
|
+
children: [
|
|
5363
|
+
onLauncherClick && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5364
|
+
SGhostButton,
|
|
5365
|
+
{
|
|
5366
|
+
icon: "launcher",
|
|
5367
|
+
size: "xs",
|
|
5368
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
5369
|
+
ariaLabel: "app launcher",
|
|
5370
|
+
onClick: onLauncherClick,
|
|
5371
|
+
className: "shrink-0"
|
|
5372
|
+
}
|
|
5373
|
+
),
|
|
5374
|
+
logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo })
|
|
5375
|
+
]
|
|
5247
5376
|
}
|
|
5248
5377
|
),
|
|
5249
|
-
!railFolded && logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo }),
|
|
5250
5378
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5251
5379
|
SGhostButton,
|
|
5252
5380
|
{
|
|
@@ -5256,30 +5384,61 @@ var SGnb = react.forwardRef(function SGnb2({
|
|
|
5256
5384
|
ariaLabel: "toggle navigation",
|
|
5257
5385
|
ariaPressed: folded,
|
|
5258
5386
|
onClick: handleFoldToggle,
|
|
5259
|
-
className:
|
|
5387
|
+
className: "ml-auto shrink-0"
|
|
5260
5388
|
}
|
|
5261
5389
|
)
|
|
5262
5390
|
]
|
|
5263
5391
|
}
|
|
5264
5392
|
),
|
|
5265
|
-
|
|
5266
|
-
"
|
|
5393
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5394
|
+
"div",
|
|
5267
5395
|
{
|
|
5268
|
-
"
|
|
5269
|
-
className: "flex flex-col overflow-y-auto",
|
|
5396
|
+
className: "flex flex-row",
|
|
5270
5397
|
style: {
|
|
5271
|
-
width:
|
|
5398
|
+
width: bodyWidth,
|
|
5272
5399
|
alignSelf: "flex-start",
|
|
5273
5400
|
flexGrow: 1,
|
|
5274
5401
|
flexShrink: 1,
|
|
5275
5402
|
flexBasis: "auto",
|
|
5276
5403
|
minHeight: 0,
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
pointerEvents: "auto"
|
|
5404
|
+
transform: folded ? "translateX(-100%)" : "none",
|
|
5405
|
+
visibility: folded ? "hidden" : "visible",
|
|
5406
|
+
transition: `transform ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(folded)}`
|
|
5281
5407
|
},
|
|
5282
|
-
children:
|
|
5408
|
+
children: [
|
|
5409
|
+
useRail && // 레일도 내비게이션 landmark — nav 로 세워야 aria-label(레일)이 스크린리더에 노출된다
|
|
5410
|
+
// (role 없는 ul 의 aria-label 은 영역명으로 읽히지 않는다). 메뉴 nav 와는 레이블로 구분된다.
|
|
5411
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5412
|
+
"nav",
|
|
5413
|
+
{
|
|
5414
|
+
"aria-label": `${ariaLabel} rail`,
|
|
5415
|
+
className: "flex shrink-0 flex-col overflow-y-auto",
|
|
5416
|
+
style: {
|
|
5417
|
+
width: GNB_RAIL_WIDTH,
|
|
5418
|
+
padding: GNB_RAIL.padding,
|
|
5419
|
+
borderRight: `1px solid ${C.topBorder}`,
|
|
5420
|
+
backgroundColor: C.panelBg,
|
|
5421
|
+
pointerEvents: "auto"
|
|
5422
|
+
},
|
|
5423
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", style: { gap: GNB_RAIL.gap }, children: railItems.map(renderRailItem) })
|
|
5424
|
+
}
|
|
5425
|
+
),
|
|
5426
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5427
|
+
"nav",
|
|
5428
|
+
{
|
|
5429
|
+
"aria-label": ariaLabel,
|
|
5430
|
+
className: "flex shrink-0 flex-col overflow-y-auto",
|
|
5431
|
+
style: {
|
|
5432
|
+
width: GNB_MENU_WIDTH,
|
|
5433
|
+
paddingTop: M.bodyPaddingY,
|
|
5434
|
+
paddingBottom: M.bodyPaddingY,
|
|
5435
|
+
backgroundColor: C.panelBg,
|
|
5436
|
+
pointerEvents: "auto"
|
|
5437
|
+
},
|
|
5438
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: menuItems.map((item) => renderItem(item, 1)) })
|
|
5439
|
+
}
|
|
5440
|
+
)
|
|
5441
|
+
]
|
|
5283
5442
|
}
|
|
5284
5443
|
)
|
|
5285
5444
|
]
|
|
@@ -5293,7 +5452,7 @@ var SPage = react.forwardRef(function SPage2({ className, style, children, ...re
|
|
|
5293
5452
|
"main",
|
|
5294
5453
|
{
|
|
5295
5454
|
ref,
|
|
5296
|
-
className: cn("box-border min-h-0 min-w-0 flex-1 overflow-auto p-
|
|
5455
|
+
className: cn("box-border min-h-0 min-w-0 flex-1 overflow-auto p-20", className),
|
|
5297
5456
|
style: { ...gridStyle, ...style },
|
|
5298
5457
|
...rest,
|
|
5299
5458
|
children
|
|
@@ -5335,6 +5494,7 @@ function STabs({
|
|
|
5335
5494
|
}) {
|
|
5336
5495
|
const s = TAB_MAIN_SIZE[size];
|
|
5337
5496
|
const blue = TAB_MAIN_COLORS.selected.border;
|
|
5497
|
+
const isVertical = isSub && vertical;
|
|
5338
5498
|
const tabRefs = react.useRef([]);
|
|
5339
5499
|
const moveTo = (target) => {
|
|
5340
5500
|
if (target < 0 || target >= tabs.length) return;
|
|
@@ -5342,19 +5502,17 @@ function STabs({
|
|
|
5342
5502
|
tabRefs.current[target]?.focus();
|
|
5343
5503
|
};
|
|
5344
5504
|
const containerStyle = { ...style };
|
|
5345
|
-
if (!isSub) {
|
|
5346
|
-
if (vertical) containerStyle.borderRight = `1px solid ${blue}`;
|
|
5347
|
-
else containerStyle.borderBottom = `1px solid ${blue}`;
|
|
5348
|
-
}
|
|
5505
|
+
if (!isSub) containerStyle.borderBottom = `1px solid ${blue}`;
|
|
5349
5506
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5350
5507
|
"div",
|
|
5351
5508
|
{
|
|
5352
5509
|
role: "tablist",
|
|
5353
|
-
"aria-orientation":
|
|
5510
|
+
"aria-orientation": isVertical ? "vertical" : "horizontal",
|
|
5354
5511
|
className: cn(
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5512
|
+
// main(가로): baseline(하단 divider)이 100% 너비로 채워지도록 w-full
|
|
5513
|
+
isSub ? "inline-flex w-fit" : "flex w-full",
|
|
5514
|
+
isVertical ? "flex-col items-stretch" : "flex-row",
|
|
5515
|
+
isSub ? isVertical ? "gap-[16px]" : "gap-[24px]" : "gap-[4px]",
|
|
5358
5516
|
className
|
|
5359
5517
|
),
|
|
5360
5518
|
style: containerStyle,
|
|
@@ -5383,22 +5541,12 @@ function STabs({
|
|
|
5383
5541
|
color: selected ? TAB_MAIN_COLORS.selected.text : TAB_MAIN_COLORS.default.text,
|
|
5384
5542
|
border: `1px solid ${border}`,
|
|
5385
5543
|
// 폴더형: 미선택 탭은 baseline 위에, 선택 탭은 baseline(파란선)을 1px 덮어 연결된 것처럼 보이게
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
borderRight: selected ? `1px solid ${selectedBg}` : "none",
|
|
5393
|
-
marginRight: selected ? -1 : 0
|
|
5394
|
-
} : {
|
|
5395
|
-
borderTopLeftRadius: 6,
|
|
5396
|
-
borderTopRightRadius: 6,
|
|
5397
|
-
borderBottomLeftRadius: 0,
|
|
5398
|
-
borderBottomRightRadius: 0,
|
|
5399
|
-
borderBottom: selected ? `1px solid ${selectedBg}` : "none",
|
|
5400
|
-
marginBottom: selected ? -1 : 0
|
|
5401
|
-
}
|
|
5544
|
+
borderTopLeftRadius: 6,
|
|
5545
|
+
borderTopRightRadius: 6,
|
|
5546
|
+
borderBottomLeftRadius: 0,
|
|
5547
|
+
borderBottomRightRadius: 0,
|
|
5548
|
+
borderBottom: selected ? `1px solid ${selectedBg}` : "none",
|
|
5549
|
+
marginBottom: selected ? -1 : 0
|
|
5402
5550
|
};
|
|
5403
5551
|
}
|
|
5404
5552
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -5418,8 +5566,8 @@ function STabs({
|
|
|
5418
5566
|
select();
|
|
5419
5567
|
return;
|
|
5420
5568
|
}
|
|
5421
|
-
const nextKey =
|
|
5422
|
-
const prevKey =
|
|
5569
|
+
const nextKey = isVertical ? "ArrowDown" : "ArrowRight";
|
|
5570
|
+
const prevKey = isVertical ? "ArrowUp" : "ArrowLeft";
|
|
5423
5571
|
if (e.key === nextKey) {
|
|
5424
5572
|
e.preventDefault();
|
|
5425
5573
|
moveTo((i + 1) % tabs.length);
|
|
@@ -10290,7 +10438,9 @@ function RangeTimeSelector({
|
|
|
10290
10438
|
};
|
|
10291
10439
|
const commit = (side, hour, minute, direction = "unknown") => {
|
|
10292
10440
|
const next = formatTimeValue(hour, minute);
|
|
10293
|
-
onChange(
|
|
10441
|
+
onChange(
|
|
10442
|
+
normalizeNextRange(side === "start" ? [next, range[1]] : [range[0], next], side, direction)
|
|
10443
|
+
);
|
|
10294
10444
|
};
|
|
10295
10445
|
const getTimeMinutes2 = (time) => {
|
|
10296
10446
|
const { hour, minute } = parseTimeValue(time);
|
|
@@ -10688,9 +10838,12 @@ exports.BUTTON_SIZES = BUTTON_SIZES;
|
|
|
10688
10838
|
exports.COLOR_KEYS = COLOR_KEYS;
|
|
10689
10839
|
exports.GNB_COLORS = GNB_COLORS;
|
|
10690
10840
|
exports.GNB_COMMON = GNB_COMMON;
|
|
10841
|
+
exports.GNB_FOLD_MS = GNB_FOLD_MS;
|
|
10691
10842
|
exports.GNB_HEADER = GNB_HEADER;
|
|
10692
10843
|
exports.GNB_MENU = GNB_MENU;
|
|
10693
10844
|
exports.GNB_MENU_WIDTH = GNB_MENU_WIDTH;
|
|
10845
|
+
exports.GNB_RAIL = GNB_RAIL;
|
|
10846
|
+
exports.GNB_RAIL_WIDTH = GNB_RAIL_WIDTH;
|
|
10694
10847
|
exports.ICONS = ICONS;
|
|
10695
10848
|
exports.SActionModal = SActionModal;
|
|
10696
10849
|
exports.SBadge = SBadge;
|
|
@@ -10753,6 +10906,7 @@ exports.TIMEPICKER_SIZE_CONFIG = TIMEPICKER_SIZE_CONFIG;
|
|
|
10753
10906
|
exports.buttonPreset = buttonPreset;
|
|
10754
10907
|
exports.cn = cn;
|
|
10755
10908
|
exports.findGnbAncestors = findGnbAncestors;
|
|
10909
|
+
exports.findGnbRailValue = findGnbRailValue;
|
|
10756
10910
|
exports.isColorKey = isColorKey;
|
|
10757
10911
|
exports.loading = loading;
|
|
10758
10912
|
exports.resolveColor = resolveColor;
|