返回 presentation-ai
column-node.tsx
根目录 / src / components / plate / ui / column-node.tsx
1 "use client";
2
3 import { setColumns } from "@platejs/layout";
4 import { useDebouncePopoverOpen } from "@platejs/layout/react";
5 import { ResizableProvider } from "@platejs/resizable";
6 import { Trash2Icon, type LucideProps } from "lucide-react";
7 import { type TColumnElement } from "platejs";
8 import {
9 PlateElement,
10 useComposedRef,
11 useEditorRef,
12 useElement,
13 useReadOnly,
14 useRemoveNodeButton,
15 withHOC,
16 type PlateElementProps,
17 } from "platejs/react";
18 import type * as React from "react";
19
20 import { Button } from "@/components/plate/ui/button";
21 import {
22 Popover,
23 PopoverAnchor,
24 PopoverContent,
25 } from "@/components/plate/ui/popover";
26 import { Separator } from "@/components/plate/ui/separator";
27 import { cn } from "@/lib/utils";
28
29 const ColumnElementPrimitive = withHOC(
30 ResizableProvider,
31 function ColumnElement(props: PlateElementProps<TColumnElement>) {
32 return (
33 <div className="group/column relative">
34 <PlateElement
35 {...props}
36 ref={useComposedRef(props.ref)}
37 className="px-2 group-first/column:pl-0 group-last/column:pr-0"
38 >
39 <div className={cn("relative border border-transparent p-1.5")}>
40 {props.children}
41 </div>
42 </PlateElement>
43 </div>
44 );
45 },
46 );
47
48 export function ColumnElement(props: PlateElementProps<TColumnElement>) {
49 return <ColumnElementPrimitive {...props} />;
50 }
51
52 export function ColumnGroupElement(props: PlateElementProps) {
53 return (
54 <PlateElement className="mb-2" {...props}>
55 <ColumnFloatingToolbar>
56 <div className="grid w-full auto-cols-fr grid-flow-col rounded">
57 {props.children}
58 </div>
59 </ColumnFloatingToolbar>
60 </PlateElement>
61 );
62 }
63
64 function ColumnFloatingToolbar({ children }: React.PropsWithChildren) {
65 const editor = useEditorRef();
66 const readOnly = useReadOnly();
67 const element = useElement<TColumnElement>();
68
69 const { props: buttonProps } = useRemoveNodeButton({ element });
70
71 const isOpen = useDebouncePopoverOpen();
72
73 const onColumnChange = (widths: string[]) => {
74 setColumns(editor, {
75 at: element,
76 widths,
77 });
78 };
79
80 if (readOnly) return <>{children}</>;
81
82 return (
83 <Popover open={isOpen} modal={false}>
84 <PopoverAnchor>{children}</PopoverAnchor>
85 <PopoverContent
86 className="w-auto p-1"
87 onOpenAutoFocus={(e) => e.preventDefault()}
88 align="center"
89 side="top"
90 sideOffset={10}
91 >
92 <div className="box-content flex h-8 items-center">
93 <Button
94 variant="ghost"
95 className="size-8"
96 onClick={() => onColumnChange(["50%", "50%"])}
97 >
98 <DoubleColumnOutlined />
99 </Button>
100 <Button
101 variant="ghost"
102 className="size-8"
103 onClick={() => onColumnChange(["33%", "33%", "33%"])}
104 >
105 <ThreeColumnOutlined />
106 </Button>
107 <Button
108 variant="ghost"
109 className="size-8"
110 onClick={() => onColumnChange(["70%", "30%"])}
111 >
112 <RightSideDoubleColumnOutlined />
113 </Button>
114 <Button
115 variant="ghost"
116 className="size-8"
117 onClick={() => onColumnChange(["30%", "70%"])}
118 >
119 <LeftSideDoubleColumnOutlined />
120 </Button>
121 <Button
122 variant="ghost"
123 className="size-8"
124 onClick={() => onColumnChange(["25%", "50%", "25%"])}
125 >
126 <DoubleSideDoubleColumnOutlined />
127 </Button>
128
129 <Separator orientation="vertical" className="mx-1 h-6" />
130 <Button variant="ghost" className="size-8" {...buttonProps}>
131 <Trash2Icon />
132 </Button>
133 </div>
134 </PopoverContent>
135 </Popover>
136 );
137 }
138
139 const DoubleColumnOutlined = (props: LucideProps) => (
140 <svg
141 fill="none"
142 height="16"
143 viewBox="0 0 16 16"
144 width="16"
145 xmlns="http://www.w3.org/2000/svg"
146 {...props}
147 >
148 <path
149 clipRule="evenodd"
150 d="M8.5 3H13V13H8.5V3ZM7.5 2H8.5H13C13.55 2 14 2.45 14 3V13C14 13.55 13.55 14 13 14H8.5H7.5H3C2.45 14 2 13.55 2 13V3C2 2.45 2.45 2 3 2H7.5ZM7.5 13H3L3 3H7.5V13Z"
151 fill="currentColor"
152 fillRule="evenodd"
153 />
154 </svg>
155 );
156
157 const ThreeColumnOutlined = (props: LucideProps) => (
158 <svg
159 fill="none"
160 height="16"
161 viewBox="0 0 16 16"
162 width="16"
163 xmlns="http://www.w3.org/2000/svg"
164 {...props}
165 >
166 <path
167 clipRule="evenodd"
168 d="M9.25 3H6.75V13H9.25V3ZM9.25 2H6.75H5.75H3C2.45 2 2 2.45 2 3V13C2 13.55 2.45 14 3 14H5.75H6.75H9.25H10.25H13C13.55 14 14 13.55 14 13V3C14 2.45 13.55 2 13 2H10.25H9.25ZM10.25 3V13H13V3H10.25ZM3 13H5.75V3H3L3 13Z"
169 fill="currentColor"
170 fillRule="evenodd"
171 />
172 </svg>
173 );
174
175 const RightSideDoubleColumnOutlined = (props: LucideProps) => (
176 <svg
177 fill="none"
178 height="16"
179 viewBox="0 0 16 16"
180 width="16"
181 xmlns="http://www.w3.org/2000/svg"
182 {...props}
183 >
184 <path
185 clipRule="evenodd"
186 d="M11.25 3H13V13H11.25V3ZM10.25 2H11.25H13C13.55 2 14 2.45 14 3V13C14 13.55 13.55 14 13 14H11.25H10.25H3C2.45 14 2 13.55 2 13V3C2 2.45 2.45 2 3 2H10.25ZM10.25 13H3L3 3H10.25V13Z"
187 fill="currentColor"
188 fillRule="evenodd"
189 />
190 </svg>
191 );
192
193 const LeftSideDoubleColumnOutlined = (props: LucideProps) => (
194 <svg
195 fill="none"
196 height="16"
197 viewBox="0 0 16 16"
198 width="16"
199 xmlns="http://www.w3.org/2000/svg"
200 {...props}
201 >
202 <path
203 clipRule="evenodd"
204 d="M5.75 3H13V13H5.75V3ZM4.75 2H5.75H13C13.55 2 14 2.45 14 3V13C14 13.55 13.55 14 13 14H5.75H4.75H3C2.45 14 2 13.55 2 13V3C2 2.45 2.45 2 3 2H4.75ZM4.75 13H3L3 3H4.75V13Z"
205 fill="currentColor"
206 fillRule="evenodd"
207 />
208 </svg>
209 );
210
211 const DoubleSideDoubleColumnOutlined = (props: LucideProps) => (
212 <svg
213 fill="none"
214 height="16"
215 viewBox="0 0 16 16"
216 width="16"
217 xmlns="http://www.w3.org/2000/svg"
218 {...props}
219 >
220 <path
221 clipRule="evenodd"
222 d="M10.25 3H5.75V13H10.25V3ZM10.25 2H5.75H4.75H3C2.45 2 2 2.45 2 3V13C2 13.55 2.45 14 3 14H4.75H5.75H10.25H11.25H13C13.55 14 14 13.55 14 13V3C14 2.45 13.55 2 13 2H11.25H10.25ZM11.25 3V13H13V3H11.25ZM3 13H4.75V3H3L3 13Z"
223 fill="currentColor"
224 fillRule="evenodd"
225 />
226 </svg>
227 );
228
228 lines Plain Text