返回 presentation-ai
blockquote-node.tsx
根目录 / src / components / plate / ui / blockquote-node.tsx
1 "use client";
2
3 import { type TElement } from "platejs";
4 import {
5 PlateElement,
6 useReadOnly,
7 type PlateElementProps,
8 } from "platejs/react";
9
10 import { cn } from "@/lib/utils";
11
12 type TBlockquoteElement = TElement & {
13 author?: string;
14 };
15
16 interface BlockquoteAuthorProps {
17 author: string;
18 isReadOnly: boolean;
19 onChange: (author: string) => void;
20 onFocus: () => void;
21 }
22
23 function BlockquoteAuthor({
24 author,
25 isReadOnly,
26 onChange,
27 onFocus,
28 }: BlockquoteAuthorProps) {
29 if (isReadOnly) {
30 if (!author) return null;
31
32 return (
33 <footer className="mt-2 text-sm text-muted-foreground">- {author}</footer>
34 );
35 }
36
37 return (
38 <input
39 type="text"
40 value={author}
41 placeholder="Author name"
42 onFocus={onFocus}
43 onKeyDown={(e) => {
44 e.stopPropagation();
45 }}
46 onChange={(e) => onChange(e.target.value)}
47 onBlur={(e) => onChange(e.target.value)}
48 className={cn(
49 "mt-2 w-full border-none bg-transparent text-sm text-muted-foreground outline-none",
50 "placeholder:text-muted-foreground/60",
51 )}
52 aria-label="Block quote author"
53 />
54 );
55 }
56
57 export function BlockquoteElement(
58 props: PlateElementProps<TBlockquoteElement>,
59 ) {
60 const readOnly = useReadOnly();
61 const { children, element, ...plateProps } = props;
62 const author = element.author ?? "";
63
64 const handleAuthorChange = (newAuthor: string) => {
65 if (readOnly) return;
66
67 const blockquotePath = props.editor.api.findPath(element);
68 if (!blockquotePath) return;
69
70 props.editor.tf.setNodes({ author: newAuthor }, { at: blockquotePath });
71 };
72
73 const blurEditor = () => {
74 props.editor.tf.blur();
75 };
76
77 return (
78 <PlateElement
79 as="blockquote"
80 className="my-1 border-l-2 pl-6 italic"
81 element={element}
82 {...plateProps}
83 >
84 {children}
85
86 <div contentEditable={false} data-decor="true" data-slate-void="true">
87 <BlockquoteAuthor
88 author={author}
89 isReadOnly={readOnly}
90 onChange={handleAuthorChange}
91 onFocus={blurEditor}
92 />
93 </div>
94 </PlateElement>
95 );
96 }
97
97 lines Plain Text