sellmate-design-system-react 0.5.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 +124 -54
- package/dist/components/SGnb/SGnb.cjs.map +1 -1
- package/dist/components/SGnb/SGnb.d.ts +7 -1
- package/dist/components/SGnb/SGnb.js +125 -55
- package/dist/components/SGnb/SGnb.js.map +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 +21 -6
- package/dist/components/SLayout/SLayout.cjs.map +1 -1
- package/dist/components/SLayout/SLayout.d.ts +17 -2
- package/dist/components/SLayout/SLayout.js +21 -6
- package/dist/components/SLayout/SLayout.js.map +1 -1
- package/dist/components/SPage/SPage.cjs +21 -6
- package/dist/components/SPage/SPage.cjs.map +1 -1
- package/dist/components/SPage/SPage.js +21 -6
- package/dist/components/SPage/SPage.js.map +1 -1
- package/dist/index.cjs +303 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +303 -90
- package/dist/index.js.map +1 -1
- package/dist/styles.css +61 -0
- package/package.json +3 -1
|
@@ -3,7 +3,10 @@ import { type SGnbType, type SGnbHeader, type SGnbColor, type SGnbMenuItem } fro
|
|
|
3
3
|
export interface SGnbProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color'> {
|
|
4
4
|
/** 메뉴 스타일: box(라운드) / belt(풀폭 행). 미지정 시 SLayout 의 type 을 따른다. */
|
|
5
5
|
type?: SGnbType;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* 상단바 구조: fix(GNB 폭 고정) / full(레이아웃 전폭). 미지정 시 SLayout 의 header 를 따른다.
|
|
8
|
+
* 단, useRail 이고 레일에 children 없는 아이템이 있으면 메뉴 폭이 출렁이므로 full 로 강제된다.
|
|
9
|
+
*/
|
|
7
10
|
header?: SGnbHeader;
|
|
8
11
|
/** 색상 테마: light / dark */
|
|
9
12
|
color?: SGnbColor;
|
|
@@ -12,6 +15,7 @@ export interface SGnbProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color'>
|
|
|
12
15
|
/**
|
|
13
16
|
* 좌측 레일 사용 여부. 켜면 items 의 첫 depth 가 아이콘+라벨 버튼의 세로 레일로 서고,
|
|
14
17
|
* 선택된 레일 아이템의 children 이 오른쪽 메뉴 패널에 깔린다. 미지정 시 SLayout 의 useRail 을 따른다.
|
|
18
|
+
* children 없는 레일 아이템이 활성이면 메뉴 패널은 렌더되지 않고 레일만 남는다.
|
|
15
19
|
*/
|
|
16
20
|
useRail?: boolean;
|
|
17
21
|
/** 현재 선택된 아이템 value */
|
|
@@ -42,6 +46,8 @@ export interface SGnbProps extends Omit<HTMLAttributes<HTMLDivElement>, 'color'>
|
|
|
42
46
|
*
|
|
43
47
|
* useRail 이면 items 의 첫 depth 가 좌측 레일(아이콘+라벨 68px)로 서고, 선택된 레일 아이템의
|
|
44
48
|
* children 이 메뉴 패널의 depth1 이 된다 — 트리는 그대로 두고 어디부터 그릴지만 바뀐다.
|
|
49
|
+
* children 없는 레일 아이템은 자기 자신이 선택(리프)이라 메뉴 패널을 렌더하지 않는다. 이런 리프가
|
|
50
|
+
* 하나라도 있으면 활성에 따라 메뉴 폭이 출렁이므로 header 는 full 로 강제해 상단바를 전폭에 고정한다.
|
|
45
51
|
*
|
|
46
52
|
* 접힘/펼침은 GNB_FOLD_MS 동안의 슬라이드다 — 레일·메뉴가 함께 왼쪽으로 빠져나가고 펼치면 오른쪽에서 들어온다.
|
|
47
53
|
* 페이지 쪽 폭은 fix 면 이 컴포넌트의 폭 전환이, full 이면 SLayout 의 메뉴 열 전환이 같은 길이로 맞춘다.
|
|
@@ -3,7 +3,7 @@ import { createContext, forwardRef, useState, useCallback, useMemo, useContext,
|
|
|
3
3
|
import { clsx } from 'clsx';
|
|
4
4
|
import { extendTailwindMerge } from 'tailwind-merge';
|
|
5
5
|
import * as RPopover from '@radix-ui/react-popover';
|
|
6
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
|
|
8
8
|
var twMerge = extendTailwindMerge({
|
|
9
9
|
extend: {
|
|
@@ -3233,15 +3233,30 @@ forwardRef(function SLayout2({
|
|
|
3233
3233
|
},
|
|
3234
3234
|
[isControlled, onFoldedChange]
|
|
3235
3235
|
);
|
|
3236
|
-
const
|
|
3237
|
-
|
|
3238
|
-
|
|
3236
|
+
const [gnbState, setGnbState] = useState({ forceFull: false, menuVisible: true });
|
|
3237
|
+
const reportGnbState = useCallback(
|
|
3238
|
+
(next) => setGnbState(
|
|
3239
|
+
(prev) => prev.forceFull === next.forceFull && prev.menuVisible === next.menuVisible ? prev : next
|
|
3240
|
+
),
|
|
3241
|
+
[]
|
|
3239
3242
|
);
|
|
3240
|
-
const
|
|
3241
|
-
const
|
|
3243
|
+
const effectiveHeader = header === "full" || gnbState.forceFull ? "full" : "fix";
|
|
3244
|
+
const ctx = useMemo(
|
|
3245
|
+
() => ({
|
|
3246
|
+
type,
|
|
3247
|
+
header: effectiveHeader,
|
|
3248
|
+
useRail,
|
|
3249
|
+
folded: currentFolded,
|
|
3250
|
+
setFolded,
|
|
3251
|
+
reportGnbState
|
|
3252
|
+
}),
|
|
3253
|
+
[type, effectiveHeader, useRail, currentFolded, setFolded, reportGnbState]
|
|
3254
|
+
);
|
|
3255
|
+
const isFullHeader = effectiveHeader === "full";
|
|
3256
|
+
const gnbColumnWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (gnbState.menuVisible ? GNB_MENU_WIDTH : 0);
|
|
3242
3257
|
const frameStyle = isFullHeader ? {
|
|
3243
3258
|
gridTemplateColumns: `${currentFolded ? 0 : gnbColumnWidth}px 1fr`,
|
|
3244
|
-
gridTemplateRows: `${GNB_HEADER[
|
|
3259
|
+
gridTemplateRows: `${GNB_HEADER[effectiveHeader].topHeight} 1fr`,
|
|
3245
3260
|
// 메뉴가 미끄러지는 동안 페이지 열도 같은 길이로 따라와야 한 덩어리로 읽힌다.
|
|
3246
3261
|
// (fix 는 SGnb 가 제 폭을 직접 전환하므로 프레임이 손댈 게 없다)
|
|
3247
3262
|
transition: `grid-template-columns ${GNB_FOLD_MS}ms ease-out`
|
|
@@ -3251,7 +3266,7 @@ forwardRef(function SLayout2({
|
|
|
3251
3266
|
{
|
|
3252
3267
|
ref,
|
|
3253
3268
|
"data-type": type,
|
|
3254
|
-
"data-header":
|
|
3269
|
+
"data-header": effectiveHeader,
|
|
3255
3270
|
className: cn(
|
|
3256
3271
|
"box-border h-full overflow-hidden",
|
|
3257
3272
|
isFullHeader ? "grid" : "flex flex-row",
|
|
@@ -3284,9 +3299,10 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3284
3299
|
}, ref) {
|
|
3285
3300
|
const layout = useContext(SLayoutContext);
|
|
3286
3301
|
const type = typeProp ?? layout?.type ?? "box";
|
|
3287
|
-
const header = headerProp ?? layout?.header ?? "fix";
|
|
3288
3302
|
const folded = foldedProp ?? layout?.folded ?? false;
|
|
3289
3303
|
const useRail = useRailProp ?? layout?.useRail ?? false;
|
|
3304
|
+
const railHasLeaf = useRail && items.some((item) => !item.children || item.children.length === 0);
|
|
3305
|
+
const header = railHasLeaf ? "full" : headerProp ?? layout?.header ?? "fix";
|
|
3290
3306
|
const H = GNB_HEADER[header];
|
|
3291
3307
|
const M = GNB_MENU[type];
|
|
3292
3308
|
const C = GNB_COLORS[color];
|
|
@@ -3474,7 +3490,11 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3474
3490
|
};
|
|
3475
3491
|
const railItems = useRail ? items : [];
|
|
3476
3492
|
const menuItems = useRail ? items.find((item) => item.value === activeRail)?.children ?? [] : items;
|
|
3477
|
-
const
|
|
3493
|
+
const showMenu = !useRail || menuItems.length > 0;
|
|
3494
|
+
const bodyWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (showMenu ? GNB_MENU_WIDTH : 0);
|
|
3495
|
+
useEffect(() => {
|
|
3496
|
+
layout?.reportGnbState({ forceFull: railHasLeaf, menuVisible: showMenu });
|
|
3497
|
+
}, [layout, railHasLeaf, showMenu]);
|
|
3478
3498
|
const railFolded = folded && header === "fix";
|
|
3479
3499
|
const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth : bodyWidth;
|
|
3480
3500
|
const rootStyle = {
|
|
@@ -3484,6 +3504,13 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3484
3504
|
...style
|
|
3485
3505
|
};
|
|
3486
3506
|
const hideAfterSlide = (hidden) => `visibility 0s linear ${hidden ? `${GNB_FOLD_MS}ms` : "0s"}`;
|
|
3507
|
+
const foldInline = !!logo;
|
|
3508
|
+
const sideFoldStyle = {
|
|
3509
|
+
opacity: railFolded ? 0 : 1,
|
|
3510
|
+
position: railFolded ? "absolute" : "static",
|
|
3511
|
+
visibility: railFolded ? "hidden" : "visible",
|
|
3512
|
+
transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}`
|
|
3513
|
+
};
|
|
3487
3514
|
return /* @__PURE__ */ jsxs(
|
|
3488
3515
|
"div",
|
|
3489
3516
|
{
|
|
@@ -3495,12 +3522,14 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3495
3522
|
"data-color": color,
|
|
3496
3523
|
...rest,
|
|
3497
3524
|
children: [
|
|
3498
|
-
/* @__PURE__ */
|
|
3525
|
+
/* @__PURE__ */ jsx(
|
|
3499
3526
|
"div",
|
|
3500
3527
|
{
|
|
3501
3528
|
className: "relative box-border flex shrink-0 items-center overflow-hidden",
|
|
3502
3529
|
style: {
|
|
3503
3530
|
height: H.topHeight,
|
|
3531
|
+
// 인라인 간격은 컨테이너 gap 대신 각 요소 margin 으로 준다 — 런처↔폴드(8px)와
|
|
3532
|
+
// 폴드↔로고(top-gap)를 다르게 두기 위함. 접히면 런처·로고가 흐름에서 빠져 margin 도 무의미해진다.
|
|
3504
3533
|
padding: `0 ${railFolded ? GNB_RAIL.padding : H.topPaddingX}`,
|
|
3505
3534
|
borderBottom: `1px solid ${C.topBorder}`,
|
|
3506
3535
|
backgroundColor: C.panelBg,
|
|
@@ -3509,49 +3538,90 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3509
3538
|
// 아이콘 버튼은 자체 ghostButton 토큰을 쓰므로, 이 색은 logo 슬롯 상속용
|
|
3510
3539
|
color: C.itemColorDefault
|
|
3511
3540
|
},
|
|
3512
|
-
children:
|
|
3513
|
-
(
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3541
|
+
children: foldInline ? (
|
|
3542
|
+
// 런처(있을 때)·폴드·로고를 흐름에 나란히 둔다 → 폴드 버튼이 (런처와) 로고 사이에 선다.
|
|
3543
|
+
// 접히면 런처·로고가 페이드하며 흐름에서 빠지고(sideFoldStyle), 폴드 버튼만 남아 레일 가운데에 선다.
|
|
3544
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3545
|
+
onLauncherClick && /* @__PURE__ */ jsx(
|
|
3546
|
+
SGhostButton,
|
|
3547
|
+
{
|
|
3548
|
+
icon: "launcher",
|
|
3549
|
+
size: "xs",
|
|
3550
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
3551
|
+
ariaLabel: "app launcher",
|
|
3552
|
+
onClick: onLauncherClick,
|
|
3553
|
+
className: "shrink-0",
|
|
3554
|
+
style: { ...sideFoldStyle, marginRight: "8px" }
|
|
3555
|
+
}
|
|
3556
|
+
),
|
|
3557
|
+
/* @__PURE__ */ jsx(
|
|
3558
|
+
SGhostButton,
|
|
3559
|
+
{
|
|
3560
|
+
icon: "sidebar",
|
|
3561
|
+
size: "xs",
|
|
3562
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
3563
|
+
ariaLabel: "toggle navigation",
|
|
3564
|
+
ariaPressed: folded,
|
|
3565
|
+
onClick: handleFoldToggle,
|
|
3566
|
+
className: "shrink-0"
|
|
3567
|
+
}
|
|
3568
|
+
),
|
|
3569
|
+
/* @__PURE__ */ jsx(
|
|
3570
|
+
"div",
|
|
3571
|
+
{
|
|
3572
|
+
className: "flex shrink-0 items-center",
|
|
3573
|
+
style: { height: H.logoHeight, marginLeft: H.topGap, ...sideFoldStyle },
|
|
3574
|
+
children: logo
|
|
3575
|
+
}
|
|
3576
|
+
)
|
|
3577
|
+
] })
|
|
3578
|
+
) : (
|
|
3579
|
+
// 런처·로고는 좌측 그룹(흐름에서 빼 폭 축소 중 폴드 버튼을 밀어내지 않는다),
|
|
3580
|
+
// 폴드 버튼은 우측 끝(ml-auto)이라 폭·패딩이 줄면 저절로 레일 가운데에 선다.
|
|
3581
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3582
|
+
(onLauncherClick || logo) && /* @__PURE__ */ jsxs(
|
|
3583
|
+
"div",
|
|
3584
|
+
{
|
|
3585
|
+
className: "absolute inset-y-0 flex items-center",
|
|
3586
|
+
style: {
|
|
3587
|
+
left: H.topPaddingX,
|
|
3588
|
+
gap: H.topGap,
|
|
3589
|
+
opacity: railFolded ? 0 : 1,
|
|
3590
|
+
transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}, transform ${GNB_FOLD_MS}ms ease-out`,
|
|
3591
|
+
// 메뉴와 같은 방향으로 빠지되, 좁은 레일 안에서 꼬리가 비치지 않게 페이드가 거리를 대신한다
|
|
3592
|
+
transform: railFolded ? `translateX(-${H.topPaddingX})` : "none",
|
|
3593
|
+
visibility: railFolded ? "hidden" : "visible"
|
|
3594
|
+
},
|
|
3595
|
+
children: [
|
|
3596
|
+
onLauncherClick && /* @__PURE__ */ jsx(
|
|
3597
|
+
SGhostButton,
|
|
3598
|
+
{
|
|
3599
|
+
icon: "launcher",
|
|
3600
|
+
size: "xs",
|
|
3601
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
3602
|
+
ariaLabel: "app launcher",
|
|
3603
|
+
onClick: onLauncherClick,
|
|
3604
|
+
className: "shrink-0"
|
|
3605
|
+
}
|
|
3606
|
+
),
|
|
3607
|
+
logo && /* @__PURE__ */ jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo })
|
|
3608
|
+
]
|
|
3609
|
+
}
|
|
3610
|
+
),
|
|
3611
|
+
/* @__PURE__ */ jsx(
|
|
3612
|
+
SGhostButton,
|
|
3613
|
+
{
|
|
3614
|
+
icon: "sidebar",
|
|
3615
|
+
size: "xs",
|
|
3616
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
3617
|
+
ariaLabel: "toggle navigation",
|
|
3618
|
+
ariaPressed: folded,
|
|
3619
|
+
onClick: handleFoldToggle,
|
|
3620
|
+
className: "ml-auto shrink-0"
|
|
3621
|
+
}
|
|
3622
|
+
)
|
|
3623
|
+
] })
|
|
3624
|
+
)
|
|
3555
3625
|
}
|
|
3556
3626
|
),
|
|
3557
3627
|
/* @__PURE__ */ jsxs(
|
|
@@ -3587,7 +3657,7 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3587
3657
|
children: /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col p-0", style: { gap: GNB_RAIL.gap }, children: railItems.map(renderRailItem) })
|
|
3588
3658
|
}
|
|
3589
3659
|
),
|
|
3590
|
-
/* @__PURE__ */ jsx(
|
|
3660
|
+
showMenu && /* @__PURE__ */ jsx(
|
|
3591
3661
|
"nav",
|
|
3592
3662
|
{
|
|
3593
3663
|
"aria-label": ariaLabel,
|