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.
Files changed (30) hide show
  1. package/README.md +20 -0
  2. package/dist/components/SDateRangePicker/SDateRangePicker.cjs +180 -37
  3. package/dist/components/SDateRangePicker/SDateRangePicker.cjs.map +1 -1
  4. package/dist/components/SDateRangePicker/SDateRangePicker.d.ts +6 -1
  5. package/dist/components/SDateRangePicker/SDateRangePicker.js +180 -37
  6. package/dist/components/SDateRangePicker/SDateRangePicker.js.map +1 -1
  7. package/dist/components/SGnb/SGnb.cjs +124 -54
  8. package/dist/components/SGnb/SGnb.cjs.map +1 -1
  9. package/dist/components/SGnb/SGnb.d.ts +7 -1
  10. package/dist/components/SGnb/SGnb.js +125 -55
  11. package/dist/components/SGnb/SGnb.js.map +1 -1
  12. package/dist/components/SKeyValueTable/SKeyValueTable.cjs +180 -37
  13. package/dist/components/SKeyValueTable/SKeyValueTable.cjs.map +1 -1
  14. package/dist/components/SKeyValueTable/SKeyValueTable.js +180 -37
  15. package/dist/components/SKeyValueTable/SKeyValueTable.js.map +1 -1
  16. package/dist/components/SLayout/SLayout.cjs +21 -6
  17. package/dist/components/SLayout/SLayout.cjs.map +1 -1
  18. package/dist/components/SLayout/SLayout.d.ts +17 -2
  19. package/dist/components/SLayout/SLayout.js +21 -6
  20. package/dist/components/SLayout/SLayout.js.map +1 -1
  21. package/dist/components/SPage/SPage.cjs +21 -6
  22. package/dist/components/SPage/SPage.cjs.map +1 -1
  23. package/dist/components/SPage/SPage.js +21 -6
  24. package/dist/components/SPage/SPage.js.map +1 -1
  25. package/dist/index.cjs +303 -90
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.js +303 -90
  28. package/dist/index.js.map +1 -1
  29. package/dist/styles.css +61 -0
  30. package/package.json +3 -1
@@ -3255,15 +3255,30 @@ react.forwardRef(function SLayout2({
3255
3255
  },
3256
3256
  [isControlled, onFoldedChange]
3257
3257
  );
3258
+ const [gnbState, setGnbState] = react.useState({ forceFull: false, menuVisible: true });
3259
+ const reportGnbState = react.useCallback(
3260
+ (next) => setGnbState(
3261
+ (prev) => prev.forceFull === next.forceFull && prev.menuVisible === next.menuVisible ? prev : next
3262
+ ),
3263
+ []
3264
+ );
3265
+ const effectiveHeader = header === "full" || gnbState.forceFull ? "full" : "fix";
3258
3266
  const ctx = react.useMemo(
3259
- () => ({ type, header, useRail, folded: currentFolded, setFolded }),
3260
- [type, header, useRail, currentFolded, setFolded]
3261
- );
3262
- const isFullHeader = header === "full";
3263
- const gnbColumnWidth = useRail ? GNB_RAIL_WIDTH + GNB_MENU_WIDTH : GNB_MENU_WIDTH;
3267
+ () => ({
3268
+ type,
3269
+ header: effectiveHeader,
3270
+ useRail,
3271
+ folded: currentFolded,
3272
+ setFolded,
3273
+ reportGnbState
3274
+ }),
3275
+ [type, effectiveHeader, useRail, currentFolded, setFolded, reportGnbState]
3276
+ );
3277
+ const isFullHeader = effectiveHeader === "full";
3278
+ const gnbColumnWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (gnbState.menuVisible ? GNB_MENU_WIDTH : 0);
3264
3279
  const frameStyle = isFullHeader ? {
3265
3280
  gridTemplateColumns: `${currentFolded ? 0 : gnbColumnWidth}px 1fr`,
3266
- gridTemplateRows: `${GNB_HEADER[header].topHeight} 1fr`,
3281
+ gridTemplateRows: `${GNB_HEADER[effectiveHeader].topHeight} 1fr`,
3267
3282
  // 메뉴가 미끄러지는 동안 페이지 열도 같은 길이로 따라와야 한 덩어리로 읽힌다.
3268
3283
  // (fix 는 SGnb 가 제 폭을 직접 전환하므로 프레임이 손댈 게 없다)
3269
3284
  transition: `grid-template-columns ${GNB_FOLD_MS}ms ease-out`
@@ -3273,7 +3288,7 @@ react.forwardRef(function SLayout2({
3273
3288
  {
3274
3289
  ref,
3275
3290
  "data-type": type,
3276
- "data-header": header,
3291
+ "data-header": effectiveHeader,
3277
3292
  className: cn(
3278
3293
  "box-border h-full overflow-hidden",
3279
3294
  isFullHeader ? "grid" : "flex flex-row",
@@ -3306,9 +3321,10 @@ var SGnb = react.forwardRef(function SGnb2({
3306
3321
  }, ref) {
3307
3322
  const layout = react.useContext(SLayoutContext);
3308
3323
  const type = typeProp ?? layout?.type ?? "box";
3309
- const header = headerProp ?? layout?.header ?? "fix";
3310
3324
  const folded = foldedProp ?? layout?.folded ?? false;
3311
3325
  const useRail = useRailProp ?? layout?.useRail ?? false;
3326
+ const railHasLeaf = useRail && items.some((item) => !item.children || item.children.length === 0);
3327
+ const header = railHasLeaf ? "full" : headerProp ?? layout?.header ?? "fix";
3312
3328
  const H = GNB_HEADER[header];
3313
3329
  const M = GNB_MENU[type];
3314
3330
  const C = GNB_COLORS[color];
@@ -3496,7 +3512,11 @@ var SGnb = react.forwardRef(function SGnb2({
3496
3512
  };
3497
3513
  const railItems = useRail ? items : [];
3498
3514
  const menuItems = useRail ? items.find((item) => item.value === activeRail)?.children ?? [] : items;
3499
- const bodyWidth = useRail ? GNB_RAIL_WIDTH + GNB_MENU_WIDTH : GNB_MENU_WIDTH;
3515
+ const showMenu = !useRail || menuItems.length > 0;
3516
+ const bodyWidth = (useRail ? GNB_RAIL_WIDTH : 0) + (showMenu ? GNB_MENU_WIDTH : 0);
3517
+ react.useEffect(() => {
3518
+ layout?.reportGnbState({ forceFull: railHasLeaf, menuVisible: showMenu });
3519
+ }, [layout, railHasLeaf, showMenu]);
3500
3520
  const railFolded = folded && header === "fix";
3501
3521
  const rootWidth = header === "full" ? "100%" : folded ? GNB_COMMON.foldWidth : bodyWidth;
3502
3522
  const rootStyle = {
@@ -3506,6 +3526,13 @@ var SGnb = react.forwardRef(function SGnb2({
3506
3526
  ...style
3507
3527
  };
3508
3528
  const hideAfterSlide = (hidden) => `visibility 0s linear ${hidden ? `${GNB_FOLD_MS}ms` : "0s"}`;
3529
+ const foldInline = !!logo;
3530
+ const sideFoldStyle = {
3531
+ opacity: railFolded ? 0 : 1,
3532
+ position: railFolded ? "absolute" : "static",
3533
+ visibility: railFolded ? "hidden" : "visible",
3534
+ transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}`
3535
+ };
3509
3536
  return /* @__PURE__ */ jsxRuntime.jsxs(
3510
3537
  "div",
3511
3538
  {
@@ -3517,12 +3544,14 @@ var SGnb = react.forwardRef(function SGnb2({
3517
3544
  "data-color": color,
3518
3545
  ...rest,
3519
3546
  children: [
3520
- /* @__PURE__ */ jsxRuntime.jsxs(
3547
+ /* @__PURE__ */ jsxRuntime.jsx(
3521
3548
  "div",
3522
3549
  {
3523
3550
  className: "relative box-border flex shrink-0 items-center overflow-hidden",
3524
3551
  style: {
3525
3552
  height: H.topHeight,
3553
+ // 인라인 간격은 컨테이너 gap 대신 각 요소 margin 으로 준다 — 런처↔폴드(8px)와
3554
+ // 폴드↔로고(top-gap)를 다르게 두기 위함. 접히면 런처·로고가 흐름에서 빠져 margin 도 무의미해진다.
3526
3555
  padding: `0 ${railFolded ? GNB_RAIL.padding : H.topPaddingX}`,
3527
3556
  borderBottom: `1px solid ${C.topBorder}`,
3528
3557
  backgroundColor: C.panelBg,
@@ -3531,49 +3560,90 @@ var SGnb = react.forwardRef(function SGnb2({
3531
3560
  // 아이콘 버튼은 자체 ghostButton 토큰을 쓰므로, 이 색은 logo 슬롯 상속용
3532
3561
  color: C.itemColorDefault
3533
3562
  },
3534
- children: [
3535
- (onLauncherClick || logo) && /* @__PURE__ */ jsxRuntime.jsxs(
3536
- "div",
3537
- {
3538
- className: "absolute inset-y-0 flex items-center",
3539
- style: {
3540
- left: H.topPaddingX,
3541
- gap: H.topGap,
3542
- opacity: railFolded ? 0 : 1,
3543
- transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}, transform ${GNB_FOLD_MS}ms ease-out`,
3544
- // 메뉴와 같은 방향으로 빠지되, 좁은 레일 안에서 꼬리가 비치지 않게 페이드가 거리를 대신한다
3545
- transform: railFolded ? `translateX(-${H.topPaddingX})` : "none",
3546
- visibility: railFolded ? "hidden" : "visible"
3547
- },
3548
- children: [
3549
- onLauncherClick && /* @__PURE__ */ jsxRuntime.jsx(
3550
- SGhostButton,
3551
- {
3552
- icon: "launcher",
3553
- size: "xs",
3554
- intent: color === "dark" ? "inverse" : "default",
3555
- ariaLabel: "app launcher",
3556
- onClick: onLauncherClick,
3557
- className: "shrink-0"
3558
- }
3559
- ),
3560
- logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo })
3561
- ]
3562
- }
3563
- ),
3564
- /* @__PURE__ */ jsxRuntime.jsx(
3565
- SGhostButton,
3566
- {
3567
- icon: "sidebar",
3568
- size: "xs",
3569
- intent: color === "dark" ? "inverse" : "default",
3570
- ariaLabel: "toggle navigation",
3571
- ariaPressed: folded,
3572
- onClick: handleFoldToggle,
3573
- className: "ml-auto shrink-0"
3574
- }
3575
- )
3576
- ]
3563
+ children: foldInline ? (
3564
+ // 런처(있을 )·폴드·로고를 흐름에 나란히 둔다 폴드 버튼이 (런처와) 로고 사이에 선다.
3565
+ // 접히면 런처·로고가 페이드하며 흐름에서 빠지고(sideFoldStyle), 폴드 버튼만 남아 레일 가운데에 선다.
3566
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3567
+ onLauncherClick && /* @__PURE__ */ jsxRuntime.jsx(
3568
+ SGhostButton,
3569
+ {
3570
+ icon: "launcher",
3571
+ size: "xs",
3572
+ intent: color === "dark" ? "inverse" : "default",
3573
+ ariaLabel: "app launcher",
3574
+ onClick: onLauncherClick,
3575
+ className: "shrink-0",
3576
+ style: { ...sideFoldStyle, marginRight: "8px" }
3577
+ }
3578
+ ),
3579
+ /* @__PURE__ */ jsxRuntime.jsx(
3580
+ SGhostButton,
3581
+ {
3582
+ icon: "sidebar",
3583
+ size: "xs",
3584
+ intent: color === "dark" ? "inverse" : "default",
3585
+ ariaLabel: "toggle navigation",
3586
+ ariaPressed: folded,
3587
+ onClick: handleFoldToggle,
3588
+ className: "shrink-0"
3589
+ }
3590
+ ),
3591
+ /* @__PURE__ */ jsxRuntime.jsx(
3592
+ "div",
3593
+ {
3594
+ className: "flex shrink-0 items-center",
3595
+ style: { height: H.logoHeight, marginLeft: H.topGap, ...sideFoldStyle },
3596
+ children: logo
3597
+ }
3598
+ )
3599
+ ] })
3600
+ ) : (
3601
+ // 런처·로고는 좌측 그룹(흐름에서 빼 폭 축소 중 폴드 버튼을 밀어내지 않는다),
3602
+ // 폴드 버튼은 우측 끝(ml-auto)이라 폭·패딩이 줄면 저절로 레일 가운데에 선다.
3603
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3604
+ (onLauncherClick || logo) && /* @__PURE__ */ jsxRuntime.jsxs(
3605
+ "div",
3606
+ {
3607
+ className: "absolute inset-y-0 flex items-center",
3608
+ style: {
3609
+ left: H.topPaddingX,
3610
+ gap: H.topGap,
3611
+ opacity: railFolded ? 0 : 1,
3612
+ transition: `opacity ${GNB_FOLD_MS}ms ease-out, ${hideAfterSlide(railFolded)}, transform ${GNB_FOLD_MS}ms ease-out`,
3613
+ // 메뉴와 같은 방향으로 빠지되, 좁은 레일 안에서 꼬리가 비치지 않게 페이드가 거리를 대신한다
3614
+ transform: railFolded ? `translateX(-${H.topPaddingX})` : "none",
3615
+ visibility: railFolded ? "hidden" : "visible"
3616
+ },
3617
+ children: [
3618
+ onLauncherClick && /* @__PURE__ */ jsxRuntime.jsx(
3619
+ SGhostButton,
3620
+ {
3621
+ icon: "launcher",
3622
+ size: "xs",
3623
+ intent: color === "dark" ? "inverse" : "default",
3624
+ ariaLabel: "app launcher",
3625
+ onClick: onLauncherClick,
3626
+ className: "shrink-0"
3627
+ }
3628
+ ),
3629
+ logo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", style: { height: H.logoHeight }, children: logo })
3630
+ ]
3631
+ }
3632
+ ),
3633
+ /* @__PURE__ */ jsxRuntime.jsx(
3634
+ SGhostButton,
3635
+ {
3636
+ icon: "sidebar",
3637
+ size: "xs",
3638
+ intent: color === "dark" ? "inverse" : "default",
3639
+ ariaLabel: "toggle navigation",
3640
+ ariaPressed: folded,
3641
+ onClick: handleFoldToggle,
3642
+ className: "ml-auto shrink-0"
3643
+ }
3644
+ )
3645
+ ] })
3646
+ )
3577
3647
  }
3578
3648
  ),
3579
3649
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -3609,7 +3679,7 @@ var SGnb = react.forwardRef(function SGnb2({
3609
3679
  children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "m-0 flex list-none flex-col p-0", style: { gap: GNB_RAIL.gap }, children: railItems.map(renderRailItem) })
3610
3680
  }
3611
3681
  ),
3612
- /* @__PURE__ */ jsxRuntime.jsx(
3682
+ showMenu && /* @__PURE__ */ jsxRuntime.jsx(
3613
3683
  "nav",
3614
3684
  {
3615
3685
  "aria-label": ariaLabel,