'use client';
import React, { useCallback, useState } from 'react';
import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';
import { isUrl } from '@udecode/plate';
import {
AudioPlugin,
FilePlugin,
ImagePlugin,
VideoPlugin,
} from '@udecode/plate-media/react';
import { useEditorRef } from '@udecode/plate/react';
import {
AudioLinesIcon,
FileUpIcon,
FilmIcon,
ImageIcon,
LinkIcon,
} from 'lucide-react';
import { toast } from 'sonner';
import { useFilePicker } from 'use-file-picker';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from './alert-dialog';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuTrigger,
useOpenState,
} from './dropdown-menu';
import { FloatingInput } from './input';
import {
ToolbarSplitButton,
ToolbarSplitButtonPrimary,
ToolbarSplitButtonSecondary,
} from './toolbar';
const MEDIA_CONFIG: Record<
string,
{
accept: string[];
icon: React.ReactNode;
title: string;
tooltip: string;
}
> = {
[AudioPlugin.key]: {
accept: ['audio/*'],
icon: ,
title: 'Insert Audio',
tooltip: 'Audio',
},
[FilePlugin.key]: {
accept: ['*'],
icon: ,
title: 'Insert File',
tooltip: 'File',
},
[ImagePlugin.key]: {
accept: ['image/*'],
icon: ,
title: 'Insert Image',
tooltip: 'Image',
},
[VideoPlugin.key]: {
accept: ['video/*'],
icon: ,
title: 'Insert Video',
tooltip: 'Video',
},
};
export function MediaToolbarButton({
children,
nodeType,
...props
}: DropdownMenuProps & { nodeType: string }) {
const currentConfig = MEDIA_CONFIG[nodeType];
const editor = useEditorRef();
const openState = useOpenState();
const [dialogOpen, setDialogOpen] = useState(false);
const { openFilePicker } = useFilePicker({
accept: currentConfig.accept,
multiple: true,
onFilesSelected: ({ plainFiles: updatedFiles }) => {
(editor as any).tf.insert.media(updatedFiles);
},
});
return (
<>
{
openFilePicker();
}}
onKeyDown={(e) => {
if (e.key === 'ArrowDown') {
e.preventDefault();
openState.onOpenChange(true);
}
}}
pressed={openState.open}
>
{currentConfig.icon}
e.stopPropagation()}
align="start"
alignOffset={-32}
>
openFilePicker()}>
{currentConfig.icon}
Upload from computer
setDialogOpen(true)}>
Insert via URL
{
setDialogOpen(value);
}}
>
>
);
}
function MediaUrlDialogContent({
currentConfig,
nodeType,
setOpen,
}: {
currentConfig: (typeof MEDIA_CONFIG)[string];
nodeType: string;
setOpen: (value: boolean) => void;
}) {
const editor = useEditorRef();
const [url, setUrl] = useState('');
const embedMedia = useCallback(() => {
if (!isUrl(url)) return toast.error('Invalid URL');
setOpen(false);
editor.tf.insertNodes({
children: [{ text: '' }],
name: nodeType === FilePlugin.key ? url.split('/').pop() : undefined,
type: nodeType,
url,
});
}, [url, editor, nodeType, setOpen]);
return (
<>
{currentConfig.title}
setUrl(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') embedMedia();
}}
label="URL"
placeholder=""
type="url"
autoFocus
/>
Cancel
{
e.preventDefault();
embedMedia();
}}
>
Accept
>
);
}