返回 presentation-ai
text-case-toolbar-button.tsx
根目录 / src / components / plate / ui / text-case-toolbar-button.tsx
1 "use client";
2
3 import { BlockSelectionPlugin } from "@platejs/selection/react";
4 import { type DropdownMenuProps } from "@radix-ui/react-dropdown-menu";
5 import { type NodeEntry, type TText } from "platejs";
6 import { useEditorRef } from "platejs/react";
7 import * as React from "react";
8
9 import {
10 DropdownMenu,
11 DropdownMenuContent,
12 DropdownMenuGroup,
13 DropdownMenuItem,
14 DropdownMenuTrigger,
15 } from "@/components/plate/ui/dropdown-menu";
16 import { ToolbarButton } from "./toolbar";
17
18 export function TextCaseToolbarButton(props: DropdownMenuProps) {
19 const editor = useEditorRef();
20 const [open, setOpen] = React.useState(false);
21
22 const transformCase = (type: "uppercase" | "lowercase" | "titlecase") => {
23 const blockSelectionApi =
24 editor.getApi(BlockSelectionPlugin).blockSelection;
25 const selectedBlocks = blockSelectionApi
26 ? blockSelectionApi.getNodes()
27 : [];
28
29 console.log(selectedBlocks);
30
31 if (selectedBlocks.length > 0) {
32 editor.tf.withoutNormalizing(() => {
33 selectedBlocks.forEach(([_node, path]) => {
34 const entries = Array.from(
35 editor.api.nodes({ at: path, match: (n) => editor.api.isText(n) }),
36 ) as NodeEntry<TText>[];
37
38 console.log(entries);
39 entries.forEach(([textNode, textPath]) => {
40 const text: string = (textNode.text as string) || "";
41 if (!text) return;
42 let transformedText = "";
43 switch (type) {
44 case "uppercase":
45 transformedText = text.toUpperCase();
46 break;
47 case "lowercase":
48 transformedText = text.toLowerCase();
49 break;
50 case "titlecase":
51 transformedText = text.replace(
52 /\w\S*/g,
53 (txt: string) =>
54 txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase(),
55 );
56 break;
57 }
58 console.log(transformedText);
59
60 editor.tf.insertText(transformedText, { at: textPath });
61 });
62 });
63 });
64
65 setOpen(false);
66 return;
67 }
68
69 if (editor.selection) {
70 const selectedText = editor.api.string(editor.selection);
71
72 if (selectedText) {
73 let transformedText = "";
74
75 switch (type) {
76 case "uppercase":
77 transformedText = selectedText.toUpperCase();
78 break;
79 case "lowercase":
80 transformedText = selectedText.toLowerCase();
81 break;
82 case "titlecase":
83 transformedText = selectedText.replace(
84 /\w\S*/g,
85 (txt) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase(),
86 );
87 break;
88 }
89
90 editor.tf.select(editor.selection);
91 editor.tf.focus();
92 editor.tf.insertText(transformedText);
93 }
94 }
95 setOpen(false);
96 };
97
98 return (
99 <DropdownMenu open={open} onOpenChange={setOpen} modal={false} {...props}>
100 <DropdownMenuTrigger asChild>
101 <ToolbarButton pressed={open} tooltip="Change case">
102 <span className="text-sm font-bold">aA</span>
103 </ToolbarButton>
104 </DropdownMenuTrigger>
105
106 <DropdownMenuContent
107 className="ignore-click-outside/toolbar min-w-35"
108 align="start"
109 >
110 <DropdownMenuGroup>
111 <DropdownMenuItem onSelect={() => transformCase("uppercase")}>
112 UPPERCASE
113 </DropdownMenuItem>
114 <DropdownMenuItem onSelect={() => transformCase("lowercase")}>
115 lowercase
116 </DropdownMenuItem>
117 <DropdownMenuItem onSelect={() => transformCase("titlecase")}>
118 Title Case
119 </DropdownMenuItem>
120 </DropdownMenuGroup>
121 </DropdownMenuContent>
122 </DropdownMenu>
123 );
124 }
125
125 lines Plain Text