"use client"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { useCallback, useEffect, useRef, useState } from "react"; import { Bookmark, Home, ShieldCheck } from "lucide-react"; import type { BoardWithCount, User } from "@/lib/api"; import { isStaff } from "@/lib/roles"; import BoardIcon, { boardColor } from "@/components/BoardIcon"; // 首页壳内板块切换:仅替换中栏帖子流,不离开工作台 function boardHref(id: number) { return `/?board=${id}`; } // 板块目录裸面板:只有 j13-module 本体,不含外层 nav / 宽度 / sticky 类, // 供侧栏注册表按管理端配置在任意侧栏渲染。 export function WorkspaceNavPanel({ boards, user, }: { boards: BoardWithCount[]; user?: User | null; }) { const params = useSearchParams(); const activeBoard = Number(params.get("board")) || 0; const showAdmin = !!user && isStaff(user.role); return (

浏览

全部讨论 {user ? ( 我的收藏 ) : null} {boards.length > 0 ? ( <>

板块

{boards.map((b) => ( {b.name} {b.post_count} 篇 ))} ) : null}
{showAdmin ? (
管理面板
) : null}
); } // 桌面左栏:整块模块,与右栏 j13-module 对齐 export function WorkspaceNav({ boards, user }: { boards: BoardWithCount[]; user?: User | null }) { return ( // sticky 必须挂在 flex 子项上:父级 items-start 会把内层缩成内容高,失去粘滞行程 ); } // 移动端:横向滑动板块条(两端渐隐指示「可左右滑动」);发帖入口由底部公共菜单承载 export function WorkspaceNavMobile({ boards }: { boards: BoardWithCount[] }) { const params = useSearchParams(); const activeBoard = Number(params.get("board")) || 0; const scrollRef = useRef(null); const [canScrollLeft, setCanScrollLeft] = useState(false); const [canScrollRight, setCanScrollRight] = useState(false); const updateScrollState = useCallback(() => { const el = scrollRef.current; if (!el) return; const max = el.scrollWidth - el.clientWidth; setCanScrollLeft(el.scrollLeft > 4); setCanScrollRight(max > 4 && el.scrollLeft < max - 4); }, []); useEffect(() => { updateScrollState(); const el = scrollRef.current; if (!el || typeof ResizeObserver === "undefined") return; const ro = new ResizeObserver(updateScrollState); ro.observe(el); return () => ro.disconnect(); }, [boards, updateScrollState]); // 进页 / 切板块时把激活 chip 滚到可视区中央 useEffect(() => { const el = scrollRef.current; if (!el) return; el.querySelector('[aria-current="page"]')?.scrollIntoView({ block: "nearest", inline: "center", }); }, [activeBoard, boards]); return (
全部 {boards.map((b) => ( {b.name} ))}
{/* 渐隐滑动指示:遮罩与背景同色,pointer-events-none 不挡触控 */} ); }