Skip to content
File

Blob: src/client/components/sidebar/page-tree.tsx

typescript65 lines
1import { useMemo } from "react";
2import { useParams } from "@tanstack/react-router";
3import { useWorkspacePages, useWorkspaceRole } from "@/client/components/workspace/use-workspace-view";
4import { useOnline } from "@/client/hooks/use-online";
5import { buildPageTreeIndex } from "@/client/lib/page-tree-model";
6import { PageTreeItem } from "./page-tree-item";
7 
8interface PageTreeProps {
9 alwaysShowActions?: boolean;
10 menuZIndex?: number;
11}
12 
13const EMPTY_CHILDREN: readonly never[] = [];
14 
15export function PageTree({ alwaysShowActions = false, menuZIndex }: PageTreeProps) {
16 const pages = useWorkspacePages();
17 const params = useParams({ strict: false }) as { pageId?: string };
18 const online = useOnline();
19 const workspaceRole = useWorkspaceRole() ?? "none";
20 
21 const index = useMemo(() => buildPageTreeIndex(pages), [pages]);
22 const activePages = index.activePages;
23 
24 const activeAncestorIds = useMemo(() => {
25 const ids = new Set<string>();
26 if (!params.pageId) return ids;
27 let cur = index.byId.get(params.pageId);
28 while (cur?.parent_id) {
29 ids.add(cur.parent_id);
30 cur = index.byId.get(cur.parent_id);
31 }
32 return ids;
33 }, [params.pageId, index]);
34 
35 const rootPages = index.childrenByParent.get(null) ?? EMPTY_CHILDREN;
36 
37 if (activePages.length === 0) {
38 return <div className="px-3 py-4 text-center text-xs text-zinc-400">No pages yet</div>;
39 }
40 
41 return (
42 <div className="relative min-h-full space-y-0.5 py-1">
43 {rootPages.map((page, i) => (
44 <div
45 key={page.id}
46 className="opacity-0 animate-slide-up"
47 style={{ animationDelay: `${Math.min(i, 7) * 60}ms` }}
48 >
49 <PageTreeItem
50 page={page}
51 depth={0}
52 index={index}
53 allPages={activePages}
54 alwaysShowActions={alwaysShowActions}
55 activeAncestorIds={activeAncestorIds}
56 menuZIndex={menuZIndex}
57 workspaceRole={workspaceRole}
58 online={online}
59 />
60 </div>
61 ))}
62 </div>
63 );
64}