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.
This commit is contained in:
parent
c5e1167beb
commit
adf28a7cf1
|
|
@ -20,6 +20,7 @@ export const FileTreeNode: Component<{
|
|||
pathHeadings: Record<string, TocNode[]>;
|
||||
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 (
|
||||
<div>
|
||||
<div class={props.isHidden?.(props.node) === true ? "hidden" : ""}>
|
||||
<div
|
||||
class={`flex items-center py-1 px-2 cursor-pointer hover:bg-gray-100 rounded ${
|
||||
isActive() ? "bg-blue-50 text-blue-700" : "text-gray-700"
|
||||
|
|
@ -66,6 +67,7 @@ export const FileTreeNode: Component<{
|
|||
pathHeadings={props.pathHeadings}
|
||||
depth={props.depth + 1}
|
||||
onClose={props.onClose}
|
||||
isHidden={props.isHidden}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -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 (
|
||||
<div>
|
||||
<div class={props.isHidden?.(props.node) === true ? "hidden" : ""}>
|
||||
<div class="flex flex-row">
|
||||
<span
|
||||
class={`cursor-pointer mr-1 text-gray-400 text-xs w-3 shrink-0`}
|
||||
|
|
@ -140,6 +143,7 @@ export const HeadingNode: Component<{
|
|||
node={child}
|
||||
basePath={props.basePath}
|
||||
depth={props.depth + 1}
|
||||
isHidden={props.isHidden}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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<SidebarContentProps> = (props) => {
|
|||
props.pathHeadings[pathname] || props.pathHeadings[`${pathname}.md`] || []
|
||||
);
|
||||
});
|
||||
const revealedHeadings = createMemo(() => {
|
||||
const pathname = decodeURIComponent(location.pathname);
|
||||
const set = new Set<string>();
|
||||
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 (
|
||||
<div class="flex flex-col h-full">
|
||||
|
|
@ -72,6 +99,7 @@ const SidebarContent: Component<SidebarContentProps> = (props) => {
|
|||
pathHeadings={props.pathHeadings}
|
||||
depth={0}
|
||||
onClose={props.onClose}
|
||||
isHidden={isFileHidden}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -83,7 +111,12 @@ const SidebarContent: Component<SidebarContentProps> = (props) => {
|
|||
本页
|
||||
</h3>
|
||||
{currentFileHeadings().map((node) => (
|
||||
<HeadingNode node={node} basePath={location.pathname} depth={0} />
|
||||
<HeadingNode
|
||||
node={node}
|
||||
basePath={location.pathname}
|
||||
depth={0}
|
||||
isHidden={isHeadingHidden}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
|
|
|
|||
Loading…
Reference in New Issue