'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 ); }