返回 presentation-ai
ImageControls.tsx
根目录 / src / components / presentation / floating-toolbar / ImageControls.tsx
1 "use client";
2
3 import {
4 AlignCenter,
5 AlignLeft,
6 AlignRight,
7 Pencil,
8 Trash2Icon,
9 } from "lucide-react";
10 import { useRemoveNodeButton } from "platejs/react";
11
12 import { Button } from "@/components/plate/ui/button";
13 import { Separator } from "@/components/plate/ui/separator";
14 import { ToolbarButton, ToolbarGroup } from "@/components/plate/ui/toolbar";
15 import {
16 DropdownMenu,
17 DropdownMenuContent,
18 DropdownMenuRadioGroup,
19 DropdownMenuRadioItem,
20 DropdownMenuTrigger,
21 } from "@/components/ui/dropdown-menu";
22 import {
23 Tooltip,
24 TooltipContent,
25 TooltipTrigger,
26 } from "@/components/ui/tooltip";
27 import { FLOATING_TOOLBAR_IGNORE_CLASS } from "./toolbar-interaction";
28 import { useToolbarContext } from "./ToolbarContext";
29
30 export function ImageControls() {
31 const { element, isImageElement, handleOpenImageEditor, editor } =
32 useToolbarContext();
33
34 // Get remove button props for the current element
35 const { props: removeButtonProps } = useRemoveNodeButton({
36 element: element as Parameters<typeof useRemoveNodeButton>[0]["element"],
37 });
38
39 // Get current alignment from element
40 const currentAlignment =
41 (element as { align?: "left" | "center" | "right" } | undefined)?.align ??
42 "center";
43
44 // Update alignment
45 const handleAlignmentChange = (value: string) => {
46 if (!element) return;
47 const path = editor.api.findPath(
48 element as unknown as Parameters<typeof editor.api.findPath>[0],
49 );
50 if (path) {
51 editor.tf.setNodes({ align: value }, { at: path });
52 }
53 };
54
55 // Only show for image elements
56 if (!isImageElement) {
57 return null;
58 }
59
60 return (
61 <ToolbarGroup>
62 {/* Edit Button */}
63 <Tooltip>
64 <TooltipTrigger asChild>
65 <ToolbarButton
66 onClick={() => handleOpenImageEditor("generate")}
67 size="sm"
68 >
69 <Pencil className="mr-1 size-3.5" />
70 Edit
71 </ToolbarButton>
72 </TooltipTrigger>
73 <TooltipContent>Edit Image</TooltipContent>
74 </Tooltip>
75
76 {/* Alignment Dropdown */}
77 <DropdownMenu modal={false}>
78 <Tooltip>
79 <TooltipTrigger asChild>
80 <DropdownMenuTrigger asChild>
81 <ToolbarButton size="sm">
82 {currentAlignment === "left" && (
83 <AlignLeft className="size-4" />
84 )}
85 {currentAlignment === "center" && (
86 <AlignCenter className="size-4" />
87 )}
88 {currentAlignment === "right" && (
89 <AlignRight className="size-4" />
90 )}
91 </ToolbarButton>
92 </DropdownMenuTrigger>
93 </TooltipTrigger>
94 <TooltipContent>Alignment</TooltipContent>
95 </Tooltip>
96 <DropdownMenuContent
97 align="start"
98 className={FLOATING_TOOLBAR_IGNORE_CLASS}
99 >
100 <DropdownMenuRadioGroup
101 value={currentAlignment}
102 onValueChange={handleAlignmentChange}
103 className={FLOATING_TOOLBAR_IGNORE_CLASS}
104 >
105 <DropdownMenuRadioItem value="left">
106 <AlignLeft className="mr-2 size-4" />
107 Left
108 </DropdownMenuRadioItem>
109 <DropdownMenuRadioItem value="center">
110 <AlignCenter className="mr-2 size-4" />
111 Center
112 </DropdownMenuRadioItem>
113 <DropdownMenuRadioItem value="right">
114 <AlignRight className="mr-2 size-4" />
115 Right
116 </DropdownMenuRadioItem>
117 </DropdownMenuRadioGroup>
118 </DropdownMenuContent>
119 </DropdownMenu>
120
121 <Separator orientation="vertical" className="mx-1 h-6" />
122
123 {/* Delete Button */}
124 <Button size="sm" variant="ghost" {...removeButtonProps}>
125 <Trash2Icon className="size-4" />
126 </Button>
127 </ToolbarGroup>
128 );
129 }
130
130 lines Plain Text