From adf28a7cf10cc03fd0b4714d4b85ce2d061ca8b5 Mon Sep 17 00:00:00 2001 From: hypercross Date: Tue, 7 Jul 2026 09:27:04 +0800 Subject: [PATCH] feat: implement visibility logic for sidebar nodes Add support for hiding file tree nodes and headings based on path revelation status. This allows the sidebar to dynamically show only the relevant parts of the file structure and table of contents. --- src/components/FileTree.tsx | 8 ++++++-- src/components/Sidebar.tsx | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/components/FileTree.tsx b/src/components/FileTree.tsx index 4d04a52..4c22660 100644 --- a/src/components/FileTree.tsx +++ b/src/components/FileTree.tsx @@ -20,6 +20,7 @@ export const FileTreeNode: Component<{ pathHeadings: Record; depth: number; onClose: () => void; + isHidden?: (node: FileNode) => boolean; }> = (props) => { const navigate = useNavigate(); const isDir = !!props.node.children; @@ -41,7 +42,7 @@ export const FileTreeNode: Component<{ const indent = props.depth * 12; return ( -
+
))}
@@ -81,6 +83,7 @@ export const HeadingNode: Component<{ node: TocNode; basePath: string; depth: number; + isHidden?: (node: TocNode) => boolean; }> = (props) => { const navigate = useNavigate(); const anchor = props.node.id || ""; @@ -112,7 +115,7 @@ export const HeadingNode: Component<{ const indent = props.depth * 12; return ( -
+
))}
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 6ccba60..3edae62 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -2,6 +2,7 @@ import { Component, createMemo, createSignal, onMount, Show } from "solid-js"; import { generateToc, type FileNode, type TocNode } from "../data-loader"; import { useLocation } from "@solidjs/router"; import { FileTreeNode, HeadingNode } from "./FileTree"; +import { isPathRevealed } from "./stores/journalStream"; export interface SidebarProps { isOpen: boolean; @@ -33,6 +34,32 @@ const SidebarContent: Component = (props) => { props.pathHeadings[pathname] || props.pathHeadings[`${pathname}.md`] || [] ); }); + const revealedHeadings = createMemo(() => { + const pathname = decodeURIComponent(location.pathname); + const set = new Set(); + function traverse(node: TocNode, cb: (node: TocNode) => void) { + cb(node); + node.children?.forEach((child) => traverse(child, cb)); + } + for (const node of currentFileHeadings()) { + traverse(node, (anode) => { + if (isPathRevealed(pathname, anode.title)) { + traverse(anode, (each) => set.add(each.title)); + } + }); + } + return set; + }); + const isFileHidden = (node: FileNode): boolean => { + if (isPathRevealed(node.path)) return false; + if (node.children?.some((child) => !isFileHidden(child))) return false; + return true; + }; + const isHeadingHidden = (node: TocNode): boolean => { + if (revealedHeadings().has(node.title)) return false; + if (node.children?.some((child) => !isHeadingHidden(child))) return false; + return true; + }; return (
@@ -72,6 +99,7 @@ const SidebarContent: Component = (props) => { pathHeadings={props.pathHeadings} depth={0} onClose={props.onClose} + isHidden={isFileHidden} /> ))}
@@ -83,7 +111,12 @@ const SidebarContent: Component = (props) => { 本页 {currentFileHeadings().map((node) => ( - + ))}