返回 presentation-ai
EmbedControls.tsx
根目录 / src / components / presentation / floating-toolbar / EmbedControls.tsx
1 "use client";
2
3 import { Link, Link2Off, Trash2 } from "lucide-react";
4 import { type TElement } from "platejs";
5
6 import {
7 extractEmbedId,
8 generateEmbedUrl,
9 getAllEmbedTypes,
10 } from "@/components/plate/ui/media-embeds";
11 import { ToolbarButton, ToolbarGroup } from "@/components/plate/ui/toolbar";
12 import {
13 Select,
14 SelectContent,
15 SelectItem,
16 SelectTrigger,
17 SelectValue,
18 } from "@/components/ui/select";
19 import { FLOATING_TOOLBAR_IGNORE_CLASS } from "./toolbar-interaction";
20 import { useToolbarContext } from "./ToolbarContext";
21
22 export function EmbedControls() {
23 const { editor, element, handleNodePropertyUpdate, isMediaEmbedElement } =
24 useToolbarContext();
25
26 if (!isMediaEmbedElement || !element) {
27 return null;
28 }
29
30 const provider =
31 typeof element.provider === "string" ? element.provider : "youtube";
32 const url = typeof element.url === "string" ? element.url : "";
33 const embedTypes = getAllEmbedTypes();
34
35 const handleProviderChange = (nextProvider: string) => {
36 handleNodePropertyUpdate("provider", nextProvider);
37 handleNodePropertyUpdate("url", "");
38 handleNodePropertyUpdate("id", "");
39 };
40
41 const normalizeUrl = () => {
42 if (!url.trim()) return;
43
44 handleNodePropertyUpdate("url", generateEmbedUrl(url, provider));
45 handleNodePropertyUpdate("id", extractEmbedId(url, provider) ?? "");
46 };
47
48 const removeUrl = () => {
49 handleNodePropertyUpdate("url", "");
50 handleNodePropertyUpdate("id", "");
51 };
52
53 const deleteEmbed = () => {
54 const path = editor.api.findPath(element as TElement);
55 if (!path) return;
56 editor.tf.removeNodes({ at: path });
57 };
58
59 return (
60 <>
61 <ToolbarGroup>
62 <Select value={provider} onValueChange={handleProviderChange}>
63 <SelectTrigger
64 className={`${FLOATING_TOOLBAR_IGNORE_CLASS} h-8 w-34 border-none bg-transparent px-2 text-xs shadow-none`}
65 >
66 <SelectValue placeholder="Provider" />
67 </SelectTrigger>
68 <SelectContent className={FLOATING_TOOLBAR_IGNORE_CLASS}>
69 {embedTypes.map(({ type, config }) => (
70 <SelectItem key={type} value={type}>
71 {config.name}
72 </SelectItem>
73 ))}
74 </SelectContent>
75 </Select>
76
77 <ToolbarButton
78 disabled={!url.trim()}
79 onClick={normalizeUrl}
80 size="sm"
81 tooltip="Normalize Embed Link"
82 >
83 <Link className="size-4" />
84 </ToolbarButton>
85
86 <ToolbarButton
87 disabled={!url.trim()}
88 onClick={removeUrl}
89 size="sm"
90 tooltip="Remove Embed Link"
91 >
92 <Link2Off className="size-4" />
93 </ToolbarButton>
94 </ToolbarGroup>
95
96 <ToolbarGroup>
97 <ToolbarButton
98 onClick={deleteEmbed}
99 size="sm"
100 tooltip="Delete Embed"
101 className="text-destructive hover:bg-destructive/10 hover:text-destructive"
102 >
103 <Trash2 className="size-4" />
104 </ToolbarButton>
105 </ToolbarGroup>
106 </>
107 );
108 }
109
109 lines Plain Text