SCALE — Build Lab
UI部品 · REACT COMPONENT

セクションサイドバー

CATEGORYUI部品 TYPEReact Component EFFORT60〜180分 DIFFICULTY
PRIMARY CODE
tsx · components/layout/SectionSidebar.tsx
'use client';

import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { getSystemByPath } from '@/lib/systems';
import { getActiveParentHref } from '@/lib/subnav-groups';

interface Props {
  systemSidebarWidth: number;
  mobileOpen?: boolean;
}

export default function SectionSidebar({ systemSidebarWidth, mobileOpen }: Props) {
  const pathname = usePathname();
  const system = getSystemByPath(pathname);
  // サブページを開いているときも、subnav-groups から親タブURLを引いて active 表示する
  const activeHref = getActiveParentHref(pathname);

  if (!system) return null;

  return (
    <div
      className="fixed top-0 bottom-0 w-60 bg-bg2 border-r border-border hidden md:flex flex-col z-40 transition-[left] duration-200"
      style={{ left: `${systemSidebarWidth}px` }}
    >
      {/* System Title */}
      <div className="h-14 flex items-center gap-2 px-4 border-b border-border shrink-0">
        <span
          className="w-2 h-2 rounded-full shrink-0"
          style={{ background: system.color }}
        />
        <h2 className="text-sm font-semibold text-text truncate">{system.name}</h2>
      </div>

      {/* Section Navigation */}
      <nav className="flex-1 overflow-y-auto py-2 px-2">
        {system.sections.map((item) => {
          const isActive = activeHref === item.href ||
            pathname === item.href ||
            (item.href !== `/${system.id}` && pathname.startsWith(item.href + '/'));
          const Icon = item.icon;

          return (
            <Link
              key={item.href}
              href={item.href}
              className={`
                flex items-center gap-2.5 px-3 py-2 rounded-lg text-[13px] transition-colors mb-0.5
                ${isActive
                  ? 'bg-bg4 text-text font-medium'
                  : 'text-text2 hover:bg-bg3 hover:text-text'
                }
              `}
            >
              <Icon size={16} className="shrink-0" style={isActive ? { color: system.color } : undefined} />
              <span className="truncate">{item.label}</span>
            </Link>
          );
        })}
      </nav>
    </div>
  );
}

前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 全ダッシュボード共通

セクションサイドバー

:LiTarget: 用途

ダッシュボードの主セクション切替サイドバー。アイコン+テキスト。

:LiSparkle: 特徴

  • アイコン表示
  • アクティブ判定
  • 折り畳み対応

:LiCode: 実コード(SCALE Base より自動抽出)

:LiInfo: components/layout/SectionSidebar.tsx の中身そのもの。コピペ即可。

'use client';

import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { getSystemByPath } from '@/lib/systems';
import { getActiveParentHref } from '@/lib/subnav-groups';

interface Props {
  systemSidebarWidth: number;
  mobileOpen?: boolean;
}

export default function SectionSidebar({ systemSidebarWidth, mobileOpen }: Props) {
  const pathname = usePathname();
  const system = getSystemByPath(pathname);
  // サブページを開いているときも、subnav-groups から親タブURLを引いて active 表示する
  const activeHref = getActiveParentHref(pathname);

  if (!system) return null;

  return (
    <div
      className="fixed top-0 bottom-0 w-60 bg-bg2 border-r border-border hidden md:flex flex-col z-40 transition-[left] duration-200"
      style={{ left: `${systemSidebarWidth}px` }}
    >
      {/* System Title */}
      <div className="h-14 flex items-center gap-2 px-4 border-b border-border shrink-0">
        <span
          className="w-2 h-2 rounded-full shrink-0"
          style={{ background: system.color }}
        />
        <h2 className="text-sm font-semibold text-text truncate">{system.name}</h2>
      </div>

      {/* Section Navigation */}
      <nav className="flex-1 overflow-y-auto py-2 px-2">
        {system.sections.map((item) => {
          const isActive = activeHref === item.href ||
            pathname === item.href ||
            (item.href !== `/${system.id}` && pathname.startsWith(item.href + '/'));
          const Icon = item.icon;

          return (
            <Link
              key={item.href}
              href={item.href}
              className={`
                flex items-center gap-2.5 px-3 py-2 rounded-lg text-[13px] transition-colors mb-0.5
                ${isActive
                  ? 'bg-bg4 text-text font-medium'
                  : 'text-text2 hover:bg-bg3 hover:text-text'
                }
              `}
            >
              <Icon size={16} className="shrink-0" style={isActive ? { color: system.color } : undefined} />
              <span className="truncate">{item.label}</span>
            </Link>
          );
        })}
      </nav>
    </div>
  );
}

:LiFolder: ソースファイルのパス

/Users/oogushiyuuki/Library/CloudStorage/GoogleDrive-y-ogushi@scale-group.co.jp/マイドライブ/AI/scale-base/components/layout/SectionSidebar.tsx

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加