sellmate-design-system-react 0.5.0 → 0.6.1
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 +145 -57
- package/dist/components/SGnb/SGnb.cjs.map +1 -1
- package/dist/components/SGnb/SGnb.d.ts +7 -1
- package/dist/components/SGnb/SGnb.js +146 -58
- 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/components/SStepper/SStepper.cjs +3019 -0
- package/dist/components/SStepper/SStepper.cjs.map +1 -0
- package/dist/components/SStepper/SStepper.d.ts +16 -0
- package/dist/components/SStepper/SStepper.js +3017 -0
- package/dist/components/SStepper/SStepper.js.map +1 -0
- package/dist/components/SStepper/index.d.ts +2 -0
- package/dist/components/SStepper/stepper.config.d.ts +42 -0
- package/dist/index.cjs +500 -104
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +499 -105
- 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];
|
|
@@ -3420,9 +3436,24 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3420
3436
|
onMouseEnter: () => setHoveredKey(item.value),
|
|
3421
3437
|
onMouseLeave: () => setHoveredKey((prev) => prev === item.value ? null : prev),
|
|
3422
3438
|
children: [
|
|
3423
|
-
depth === 1 && item.icon && /* @__PURE__ */ jsx(
|
|
3439
|
+
depth === 1 && item.icon && /* @__PURE__ */ jsx(
|
|
3440
|
+
SIcon,
|
|
3441
|
+
{
|
|
3442
|
+
name: item.icon,
|
|
3443
|
+
size: M.depth1Icon,
|
|
3444
|
+
color: isSelected ? C.itemColorActive : C.itemIcon,
|
|
3445
|
+
className: "shrink-0"
|
|
3446
|
+
}
|
|
3447
|
+
),
|
|
3424
3448
|
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap flex flex-no-wrap items-center", children: [
|
|
3425
|
-
depth > 1 && iconBranch ? /* @__PURE__ */ jsx(
|
|
3449
|
+
depth > 1 && iconBranch ? /* @__PURE__ */ jsx(
|
|
3450
|
+
"div",
|
|
3451
|
+
{
|
|
3452
|
+
className: "w-[20px] flex items-center justify-center mr-[8px]",
|
|
3453
|
+
style: { height: M.itemLineHeight },
|
|
3454
|
+
children: "-"
|
|
3455
|
+
}
|
|
3456
|
+
) : null,
|
|
3426
3457
|
item.label
|
|
3427
3458
|
] }),
|
|
3428
3459
|
item.tag !== void 0 && item.tag !== "" && /* @__PURE__ */ jsx(
|
|
@@ -3474,16 +3505,28 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3474
3505
|
};
|
|
3475
3506
|
const railItems = useRail ? items : [];
|
|
3476
3507
|
const menuItems = useRail ? items.find((item) => item.value === activeRail)?.children ?? [] : items;
|
|
3477
|
-
const
|
|
3508
|
+
const showMenu = !useRail || menuItems.length > 0;
|
|
3509
|
+
const bodyWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (showMenu ? GNB_MENU_WIDTH : 0);
|
|
3510
|
+
useEffect(() => {
|
|
3511
|
+
layout?.reportGnbState({ forceFull: railHasLeaf, menuVisible: showMenu });
|
|
3512
|
+
}, [layout, railHasLeaf, showMenu]);
|
|
3478
3513
|
const railFolded = folded && header === "fix";
|
|
3479
3514
|
const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth : bodyWidth;
|
|
3515
|
+
const rightBorder = color === "light" ? `1px solid ${C.topBorder}` : void 0;
|
|
3480
3516
|
const rootStyle = {
|
|
3481
3517
|
width: rootWidth,
|
|
3482
3518
|
transition: `width ${GNB_FOLD_MS}ms ease-out`,
|
|
3483
|
-
...header === "full" ? { gridArea: "1 / 1 / -1 / -1", pointerEvents: "none" } : { backgroundColor: C.panelBg },
|
|
3519
|
+
...header === "full" ? { gridArea: "1 / 1 / -1 / -1", pointerEvents: "none" } : { backgroundColor: C.panelBg, borderRight: rightBorder },
|
|
3484
3520
|
...style
|
|
3485
3521
|
};
|
|
3486
3522
|
const hideAfterSlide = (hidden) => `visibility 0s linear ${hidden ? `${GNB_FOLD_MS}ms` : "0s"}`;
|
|
3523
|
+
const foldInline = !!logo;
|
|
3524
|
+
const sideFoldStyle = {
|
|
3525
|
+
opacity: railFolded ? 0 : 1,
|
|
3526
|
+
position: railFolded ? "absolute" : "static",
|
|
3527
|
+
visibility: railFolded ? "hidden" : "visible",
|
|
3528
|
+
transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}`
|
|
3529
|
+
};
|
|
3487
3530
|
return /* @__PURE__ */ jsxs(
|
|
3488
3531
|
"div",
|
|
3489
3532
|
{
|
|
@@ -3495,12 +3538,14 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3495
3538
|
"data-color": color,
|
|
3496
3539
|
...rest,
|
|
3497
3540
|
children: [
|
|
3498
|
-
/* @__PURE__ */
|
|
3541
|
+
/* @__PURE__ */ jsx(
|
|
3499
3542
|
"div",
|
|
3500
3543
|
{
|
|
3501
3544
|
className: "relative box-border flex shrink-0 items-center overflow-hidden",
|
|
3502
3545
|
style: {
|
|
3503
3546
|
height: H.topHeight,
|
|
3547
|
+
// 인라인 간격은 컨테이너 gap 대신 각 요소 margin 으로 준다 — 런처↔폴드(8px)와
|
|
3548
|
+
// 폴드↔로고(top-gap)를 다르게 두기 위함. 접히면 런처·로고가 흐름에서 빠져 margin 도 무의미해진다.
|
|
3504
3549
|
padding: `0 ${railFolded ? GNB_RAIL.padding : H.topPaddingX}`,
|
|
3505
3550
|
borderBottom: `1px solid ${C.topBorder}`,
|
|
3506
3551
|
backgroundColor: C.panelBg,
|
|
@@ -3509,49 +3554,90 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3509
3554
|
// 아이콘 버튼은 자체 ghostButton 토큰을 쓰므로, 이 색은 logo 슬롯 상속용
|
|
3510
3555
|
color: C.itemColorDefault
|
|
3511
3556
|
},
|
|
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
|
-
|
|
3557
|
+
children: foldInline ? (
|
|
3558
|
+
// 런처(있을 때)·폴드·로고를 흐름에 나란히 둔다 → 폴드 버튼이 (런처와) 로고 사이에 선다.
|
|
3559
|
+
// 접히면 런처·로고가 페이드하며 흐름에서 빠지고(sideFoldStyle), 폴드 버튼만 남아 레일 가운데에 선다.
|
|
3560
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3561
|
+
onLauncherClick && /* @__PURE__ */ jsx(
|
|
3562
|
+
SGhostButton,
|
|
3563
|
+
{
|
|
3564
|
+
icon: "launcher",
|
|
3565
|
+
size: "xs",
|
|
3566
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
3567
|
+
ariaLabel: "app launcher",
|
|
3568
|
+
onClick: onLauncherClick,
|
|
3569
|
+
className: "shrink-0",
|
|
3570
|
+
style: { ...sideFoldStyle, marginRight: "8px" }
|
|
3571
|
+
}
|
|
3572
|
+
),
|
|
3573
|
+
/* @__PURE__ */ jsx(
|
|
3574
|
+
SGhostButton,
|
|
3575
|
+
{
|
|
3576
|
+
icon: "sidebar",
|
|
3577
|
+
size: "xs",
|
|
3578
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
3579
|
+
ariaLabel: "toggle navigation",
|
|
3580
|
+
ariaPressed: folded,
|
|
3581
|
+
onClick: handleFoldToggle,
|
|
3582
|
+
className: "shrink-0"
|
|
3583
|
+
}
|
|
3584
|
+
),
|
|
3585
|
+
/* @__PURE__ */ jsx(
|
|
3586
|
+
"div",
|
|
3587
|
+
{
|
|
3588
|
+
className: "flex shrink-0 items-center",
|
|
3589
|
+
style: { height: H.logoHeight, marginLeft: H.topGap, ...sideFoldStyle },
|
|
3590
|
+
children: logo
|
|
3591
|
+
}
|
|
3592
|
+
)
|
|
3593
|
+
] })
|
|
3594
|
+
) : (
|
|
3595
|
+
// 런처·로고는 좌측 그룹(흐름에서 빼 폭 축소 중 폴드 버튼을 밀어내지 않는다),
|
|
3596
|
+
// 폴드 버튼은 우측 끝(ml-auto)이라 폭·패딩이 줄면 저절로 레일 가운데에 선다.
|
|
3597
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3598
|
+
(onLauncherClick || logo) && /* @__PURE__ */ jsxs(
|
|
3599
|
+
"div",
|
|
3600
|
+
{
|
|
3601
|
+
className: "absolute inset-y-0 flex items-center",
|
|
3602
|
+
style: {
|
|
3603
|
+
left: H.topPaddingX,
|
|
3604
|
+
gap: H.topGap,
|
|
3605
|
+
opacity: railFolded ? 0 : 1,
|
|
3606
|
+
transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}, transform ${GNB_FOLD_MS}ms ease-out`,
|
|
3607
|
+
// 메뉴와 같은 방향으로 빠지되, 좁은 레일 안에서 꼬리가 비치지 않게 페이드가 거리를 대신한다
|
|
3608
|
+
transform: railFolded ? `translateX(-${H.topPaddingX})` : "none",
|
|
3609
|
+
visibility: railFolded ? "hidden" : "visible"
|
|
3610
|
+
},
|
|
3611
|
+
children: [
|
|
3612
|
+
onLauncherClick && /* @__PURE__ */ jsx(
|
|
3613
|
+
SGhostButton,
|
|
3614
|
+
{
|
|
3615
|
+
icon: "launcher",
|
|
3616
|
+
size: "xs",
|
|
3617
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
3618
|
+
ariaLabel: "app launcher",
|
|
3619
|
+
onClick: onLauncherClick,
|
|
3620
|
+
className: "shrink-0"
|
|
3621
|
+
}
|
|
3622
|
+
),
|
|
3623
|
+
logo && /* @__PURE__ */ jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo })
|
|
3624
|
+
]
|
|
3625
|
+
}
|
|
3626
|
+
),
|
|
3627
|
+
/* @__PURE__ */ jsx(
|
|
3628
|
+
SGhostButton,
|
|
3629
|
+
{
|
|
3630
|
+
icon: "sidebar",
|
|
3631
|
+
size: "xs",
|
|
3632
|
+
intent: color === "dark" ? "inverse" : "default",
|
|
3633
|
+
ariaLabel: "toggle navigation",
|
|
3634
|
+
ariaPressed: folded,
|
|
3635
|
+
onClick: handleFoldToggle,
|
|
3636
|
+
className: "ml-auto shrink-0"
|
|
3637
|
+
}
|
|
3638
|
+
)
|
|
3639
|
+
] })
|
|
3640
|
+
)
|
|
3555
3641
|
}
|
|
3556
3642
|
),
|
|
3557
3643
|
/* @__PURE__ */ jsxs(
|
|
@@ -3587,7 +3673,7 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3587
3673
|
children: /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col p-0", style: { gap: GNB_RAIL.gap }, children: railItems.map(renderRailItem) })
|
|
3588
3674
|
}
|
|
3589
3675
|
),
|
|
3590
|
-
/* @__PURE__ */ jsx(
|
|
3676
|
+
showMenu && /* @__PURE__ */ jsx(
|
|
3591
3677
|
"nav",
|
|
3592
3678
|
{
|
|
3593
3679
|
"aria-label": ariaLabel,
|
|
@@ -3597,6 +3683,8 @@ var SGnb = forwardRef(function SGnb2({
|
|
|
3597
3683
|
paddingTop: M.bodyPaddingY,
|
|
3598
3684
|
paddingBottom: M.bodyPaddingY,
|
|
3599
3685
|
backgroundColor: C.panelBg,
|
|
3686
|
+
// full 은 상단바가 전폭이라 루트가 오른쪽 선을 못 그린다 → 메뉴 오른쪽(페이지 경계)이 대신 그린다.
|
|
3687
|
+
borderRight: header === "full" ? rightBorder : void 0,
|
|
3600
3688
|
pointerEvents: "auto"
|
|
3601
3689
|
},
|
|
3602
3690
|
children: /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col p-0", children: menuItems.map((item) => renderItem(item, 1)) })
|