返回 presentation-ai
cursor-overlay.tsx
根目录 / src / components / plate / ui / cursor-overlay.tsx
1 "use client";
2
3 import { AIChatPlugin } from "@platejs/ai/react";
4 import {
5 useCursorOverlay,
6 type CursorData,
7 type CursorOverlayState,
8 } from "@platejs/selection/react";
9 import { RangeApi } from "platejs";
10 import { usePluginOption } from "platejs/react";
11
12 import { cn } from "@/lib/utils";
13
14 export function CursorOverlay() {
15 const { cursors } = useCursorOverlay();
16
17 return (
18 <>
19 {cursors.map((cursor) => (
20 <Cursor key={cursor.id} {...cursor} />
21 ))}
22 </>
23 );
24 }
25
26 function Cursor({
27 id,
28 caretPosition,
29 data,
30 selection,
31 selectionRects,
32 }: CursorOverlayState<CursorData>) {
33 const streaming = usePluginOption(AIChatPlugin, "streaming");
34 const { style, selectionStyle = style } = data ?? ({} as CursorData);
35 const isCursor = RangeApi.isCollapsed(selection);
36
37 if (streaming) return null;
38
39 return (
40 <>
41 {selectionRects.map((position, i) => {
42 return (
43 <div
44 key={i}
45 className={cn(
46 "pointer-events-none absolute z-10",
47 id === "selection" && "bg-brand/25",
48 id === "selection" && isCursor && "bg-primary",
49 )}
50 style={{
51 ...selectionStyle,
52 ...position,
53 }}
54 />
55 );
56 })}
57 {caretPosition && (
58 <div
59 className={cn(
60 "pointer-events-none absolute z-10 w-0.5",
61 id === "drag" && "w-px bg-brand",
62 )}
63 style={{ ...caretPosition, ...style }}
64 />
65 )}
66 </>
67 );
68 }
69
69 lines Plain Text