refactor: FileTree
This commit is contained in:
parent
1ea2899bf4
commit
c923d80d30
|
|
@ -0,0 +1,107 @@
|
|||
import { Component, createSignal, Show } from "solid-js";
|
||||
import { type FileNode, type TocNode } from "../data-loader";
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
|
||||
/**
|
||||
* 文件树节点组件
|
||||
*/
|
||||
export const FileTreeNode: Component<{
|
||||
node: FileNode;
|
||||
currentPath: string;
|
||||
pathHeadings: Record<string, TocNode[]>;
|
||||
depth: number;
|
||||
onClose: () => void;
|
||||
}> = (props) => {
|
||||
const navigate = useNavigate();
|
||||
const [isExpanded, setIsExpanded] = createSignal(true);
|
||||
const isDir = !!props.node.children;
|
||||
const isActive = props.currentPath === props.node.path;
|
||||
|
||||
const handleClick = () => {
|
||||
if (isDir) {
|
||||
setIsExpanded(!isExpanded());
|
||||
} else {
|
||||
navigate(props.node.path);
|
||||
props.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const indent = props.depth * 12;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<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"
|
||||
}`}
|
||||
style={{ "padding-left": `${indent + 8}px` }}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Show when={isDir}>
|
||||
<span class="mr-1 text-gray-400">{isExpanded() ? "📂" : "📁"}</span>
|
||||
</Show>
|
||||
<Show when={!isDir}>
|
||||
<span class="mr-1 text-gray-400">📄</span>
|
||||
</Show>
|
||||
<span class="text-sm truncate">{props.node.name}</span>
|
||||
</div>
|
||||
<Show when={isDir && isExpanded() && props.node.children}>
|
||||
<div>
|
||||
{props.node.children!.map((child) => (
|
||||
<FileTreeNode
|
||||
node={child}
|
||||
currentPath={props.currentPath}
|
||||
pathHeadings={props.pathHeadings}
|
||||
depth={props.depth + 1}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 标题节点组件
|
||||
*/
|
||||
export const HeadingNode: Component<{
|
||||
node: TocNode;
|
||||
basePath: string;
|
||||
depth: number;
|
||||
}> = (props) => {
|
||||
const navigate = useNavigate();
|
||||
const anchor = props.node.title.toLowerCase().replace(/\s+/g, "-");
|
||||
const href = `${props.basePath}#${anchor}`;
|
||||
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
navigate(href);
|
||||
};
|
||||
|
||||
const indent = props.depth * 12;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<a
|
||||
href={href}
|
||||
class="block py-0.5 px-2 text-sm text-gray-600 hover:text-gray-900 hover:bg-gray-50 rounded truncate"
|
||||
style={{ "padding-left": `${indent + 8}px` }}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{props.node.title}
|
||||
</a>
|
||||
<Show when={props.node.children}>
|
||||
<div>
|
||||
{props.node.children!.map((child) => (
|
||||
<HeadingNode
|
||||
node={child}
|
||||
basePath={props.basePath}
|
||||
depth={props.depth + 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,115 +1,13 @@
|
|||
import { Component, createSignal, onMount, Show } from "solid-js";
|
||||
import { generateToc, type FileNode, type TocNode } from "../data-loader";
|
||||
import { useLocation, useNavigate } from "@solidjs/router";
|
||||
import { useLocation } from "@solidjs/router";
|
||||
import { FileTreeNode, HeadingNode } from "./FileTree";
|
||||
|
||||
interface SidebarProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件树节点组件
|
||||
*/
|
||||
const FileTreeNode: Component<{
|
||||
node: FileNode;
|
||||
currentPath: string;
|
||||
pathHeadings: Record<string, TocNode[]>;
|
||||
depth: number;
|
||||
}> = (props) => {
|
||||
const navigate = useNavigate();
|
||||
const [isExpanded, setIsExpanded] = createSignal(true);
|
||||
const isDir = !!props.node.children;
|
||||
const isActive = props.currentPath === props.node.path;
|
||||
|
||||
const handleClick = () => {
|
||||
if (isDir) {
|
||||
setIsExpanded(!isExpanded());
|
||||
} else {
|
||||
navigate(props.node.path);
|
||||
props.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const indent = props.depth * 12;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<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"
|
||||
}`}
|
||||
style={{ "padding-left": `${indent + 8}px` }}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Show when={isDir}>
|
||||
<span class="mr-1 text-gray-400">{isExpanded() ? "📂" : "📁"}</span>
|
||||
</Show>
|
||||
<Show when={!isDir}>
|
||||
<span class="mr-1 text-gray-400">📄</span>
|
||||
</Show>
|
||||
<span class="text-sm truncate">{props.node.name}</span>
|
||||
</div>
|
||||
<Show when={isDir && isExpanded() && props.node.children}>
|
||||
<div>
|
||||
{props.node.children!.map((child) => (
|
||||
<FileTreeNode
|
||||
node={child}
|
||||
currentPath={props.currentPath}
|
||||
pathHeadings={props.pathHeadings}
|
||||
depth={props.depth + 1}
|
||||
onClose={props.onClose}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 标题节点组件
|
||||
*/
|
||||
const HeadingNode: Component<{
|
||||
node: TocNode;
|
||||
basePath: string;
|
||||
depth: number;
|
||||
}> = (props) => {
|
||||
const navigate = useNavigate();
|
||||
const anchor = props.node.title.toLowerCase().replace(/\s+/g, "-");
|
||||
const href = `${props.basePath}#${anchor}`;
|
||||
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
e.preventDefault();
|
||||
navigate(href);
|
||||
};
|
||||
|
||||
const indent = props.depth * 12;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<a
|
||||
href={href}
|
||||
class="block py-0.5 px-2 text-sm text-gray-600 hover:text-gray-900 hover:bg-gray-50 rounded truncate"
|
||||
style={{ "padding-left": `${indent + 8}px` }}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{props.node.title}
|
||||
</a>
|
||||
<Show when={props.node.children}>
|
||||
<div>
|
||||
{props.node.children!.map((child) => (
|
||||
<HeadingNode
|
||||
node={child}
|
||||
basePath={props.basePath}
|
||||
depth={props.depth + 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 侧边栏组件
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export { Article } from './Article';
|
|||
export type { ArticleProps } from './Article';
|
||||
export { Sidebar } from './Sidebar';
|
||||
export type { SidebarProps } from './Sidebar';
|
||||
export { FileTreeNode, HeadingNode } from './FileTree';
|
||||
|
||||
// 导出数据类型
|
||||
export type { DiceProps } from './dice';
|
||||
|
|
|
|||
Loading…
Reference in New Issue