サブナビゲーション
:LiTarget: 用途
セクション内の二次ナビ。ピル型タブで切替。
:LiSparkle: 特徴
- ピル型タブ
- グルーピング
- アクティブ判定
:LiCode: 実コード(SCALE Base より自動抽出)
:LiInfo:
components/layout/SubNav.tsxの中身そのもの。コピペ即可。
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { findSubNavGroup, normalizePath } from '@/lib/subnav-groups';
// すべてのシステム配下ページの上部にこのSubNavが入る(layout.tsxで呼び出し)
// pathname からグループを自動判定し、該当グループのタブを表示する
export default function SubNav() {
const pathname = usePathname() || '';
const group = findSubNavGroup(pathname);
if (!group) return null;
const current = normalizePath(pathname);
// グループ内で active になるタブを1つだけ決定(完全一致優先、なければ最長prefix一致)
const activeIdx = (() => {
// 1. 完全一致(href または aliases)
for (let i = 0; i < group.tabs.length; i++) {
const t = group.tabs[i];
if (t.href === current) return i;
if (t.aliases?.includes(current)) return i;
}
// 2. prefix一致(システムルート /tasks 等は除外)。最長のものを選ぶ
const isRoot = (h: string) => h.split('/').filter(Boolean).length < 2;
let bestIdx = -1;
let bestLen = -1;
for (let i = 0; i < group.tabs.length; i++) {
const t = group.tabs[i];
if (!isRoot(t.href) && current.startsWith(t.href + '/') && t.href.length > bestLen) {
bestLen = t.href.length;
bestIdx = i;
}
for (const a of t.aliases || []) {
if (!isRoot(a) && current.startsWith(a + '/') && a.length > bestLen) {
bestLen = a.length;
bestIdx = i;
}
}
}
return bestIdx;
})();
return (
<div className="sticky -top-4 md:-top-6 z-30 mb-5 -mx-4 md:-mx-6 -mt-4 md:-mt-6 px-4 md:px-6 pt-4 md:pt-6 pb-1 bg-bg/95 backdrop-blur-md">
<div className="flex items-center gap-2 border-b border-[#222] overflow-x-auto">
{group.tabs.map((tab, i) => {
const active = i === activeIdx;
return (
<Link
key={tab.href}
href={tab.href}
className={`px-3 py-2 text-xs font-medium whitespace-nowrap border-b-2 transition-colors ${active ? 'border-blue-500 text-white' : 'border-transparent text-text-muted hover:text-text'}`}
>
{tab.label}
</Link>
);
})}
</div>
</div>
);
}
:LiFolder: ソースファイルのパス
/Users/oogushiyuuki/Library/CloudStorage/GoogleDrive-y-ogushi@scale-group.co.jp/マイドライブ/AI/scale-base/components/layout/SubNav.tsx
:LiHandPointer: 使い方
対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。
:LiAlertCircle: 注意事項
- 依存パッケージを忘れず追加