Files
writer-work-flow/apps/web/lib/nav/items.ts
Yaojia Wang 9b893a6c61 feat(ux): M1/M2 移动端可用 — 导航汉堡抽屉 + 写作工具条不溢出
M1 左导航响应式抽屉:抽出 lib/nav/items.ts(条目数据+激活判定,纯逻辑+vitest)
共用于桌面静态侧栏(hidden lg:block)与移动汉堡抽屉。新增通用 Drawer(遮罩/
Esc 关闭/打开聚焦,复用 CommandPalette a11y 模式)+ NavDrawer + NavItems;
LeftNav 改 <lg 隐藏,AppShell 顶栏加汉堡(aria-label 打开导航)。

M2 写作工具条不溢出 + 移动端可达章节/助手:Toolbar 改 flex-wrap,390px 不再
横切「尚未保存」;ChapterList/ChapterAssistant 抽出 *Content,Workbench 经
左/右 Drawer 在 <lg 触达目录/助手。

实景 390px 复验(browse):汉堡显示/静态导航隐藏/抽屉开合聚焦+Esc、工具条
两行不溢出、助手抽屉真注入面板。门禁:lint/typecheck 干净 · vitest 177 · build OK。
2026-06-20 11:26:14 +02:00

56 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 左导航条目数据 + 激活判定(纯逻辑,供桌面静态侧栏 / 移动抽屉共用UX §5.1/§3.1)。
// 组件只负责渲染;条目与高亮规则在此集中,避免侧栏与抽屉漂移。
export type ActiveNav =
| "write"
| "outline"
| "foreshadow"
| "review"
| "style"
| "codex"
| "rules"
| "skills";
export interface NavItem {
href: string;
label: string;
// 项目级入口的激活键(与 activeNav 比对高亮);全局项无 key。
key?: ActiveNav;
}
export const GLOBAL_NAV_ITEMS: readonly NavItem[] = [
{ href: "/", label: "作品库" },
{ href: "/settings/providers", label: "设置" },
];
// 项目内页展开的项目级入口UX §3.1)。
export function projectNavItems(projectId: string): NavItem[] {
return [
{ href: `/projects/${projectId}/write`, label: "写作", key: "write" },
{ href: `/projects/${projectId}/outline`, label: "大纲", key: "outline" },
{
href: `/projects/${projectId}/foreshadow`,
label: "伏笔",
key: "foreshadow",
},
{ href: `/projects/${projectId}/review`, label: "审稿", key: "review" },
{ href: `/projects/${projectId}/style`, label: "文风", key: "style" },
{ href: `/projects/${projectId}/codex`, label: "设定库", key: "codex" },
{ href: `/projects/${projectId}/rules`, label: "规则", key: "rules" },
{ href: `/projects/${projectId}/skills`, label: "技能库", key: "skills" },
];
}
export interface NavActiveContext {
pathname: string;
activeNav?: ActiveNav;
}
// 全局项按精确路径匹配;项目项按 activeNav 键匹配(或路径相等兜底)。
export function isNavItemActive(item: NavItem, ctx: NavActiveContext): boolean {
if (item.key !== undefined) {
return item.key === ctx.activeNav || ctx.pathname === item.href;
}
return ctx.pathname === item.href;
}