返回 presentation-ai
toc-node-static.tsx
根目录 / src / components / plate / ui / toc-node-static.tsx
1 import { BaseTocPlugin, isHeading, type Heading } from "@platejs/toc";
2 import { cva } from "class-variance-authority";
3 import {
4 NodeApi,
5 type NodeEntry,
6 type SlateEditor,
7 type TElement,
8 } from "platejs";
9 import { SlateElement, type SlateElementProps } from "platejs/static";
10
11 import { Button } from "@/components/plate/ui/button";
12
13 const headingItemVariants = cva(
14 "block h-auto w-full cursor-pointer truncate rounded-none px-0.5 py-1.5 text-left font-medium text-muted-foreground underline decoration-[0.5px] underline-offset-4 hover:bg-accent hover:text-muted-foreground",
15 {
16 variants: {
17 depth: {
18 1: "pl-0.5",
19 2: "pl-6.5",
20 3: "pl-12.5",
21 },
22 },
23 },
24 );
25
26 export function TocElementStatic(props: SlateElementProps) {
27 const { editor } = props;
28 const headingList = getHeadingList(editor);
29
30 return (
31 <SlateElement {...props} className="mb-1 p-0">
32 <div>
33 {headingList.length > 0 ? (
34 headingList.map((item) => (
35 <Button
36 key={item.title}
37 variant="ghost"
38 className={headingItemVariants({
39 depth: item.depth as 1 | 2 | 3,
40 })}
41 >
42 {item.title}
43 </Button>
44 ))
45 ) : (
46 <div className="text-sm text-gray-500">
47 Create a heading to display the table of contents.
48 </div>
49 )}
50 </div>
51 {props.children}
52 </SlateElement>
53 );
54 }
55
56 const headingDepth: Record<string, number> = {
57 h1: 1,
58 h2: 2,
59 h3: 3,
60 h4: 4,
61 h5: 5,
62 h6: 6,
63 };
64
65 const getHeadingList = (editor?: SlateEditor) => {
66 if (!editor) return [];
67
68 const options = editor.getOptions(BaseTocPlugin);
69
70 if (options.queryHeading) {
71 return options.queryHeading(editor);
72 }
73
74 const headingList: Heading[] = [];
75
76 const values = Array.from(
77 editor.api.nodes({
78 at: [],
79 match: (n) => isHeading(n),
80 }),
81 ) as NodeEntry<TElement>[];
82
83 Array.from(values, ([node, path]) => {
84 const { type } = node;
85 const title = NodeApi.string(node);
86 const depth = headingDepth[type]!;
87 const id = node.id as string;
88
89 if (title) {
90 headingList.push({ id, depth, path, title, type });
91 }
92 });
93
94 return headingList;
95 };
96
96 lines Plain Text