import React, { useEffect, useRef, useState } from 'react'; import { EditorProvider, useCurrentEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { Color } from '@tiptap/extension-color'; import ListItem from '@tiptap/extension-list-item'; import TextStyle from '@tiptap/extension-text-style'; import Placeholder from '@tiptap/extension-placeholder' import Image from '@tiptap/extension-image'; import IconButton from '@mui/material/IconButton'; import FormatBoldIcon from '@mui/icons-material/FormatBold'; import FormatItalicIcon from '@mui/icons-material/FormatItalic'; import StrikethroughSIcon from '@mui/icons-material/StrikethroughS'; import FormatClearIcon from '@mui/icons-material/FormatClear'; import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted'; import FormatListNumberedIcon from '@mui/icons-material/FormatListNumbered'; import CodeIcon from '@mui/icons-material/Code'; import ImageIcon from '@mui/icons-material/Image'; // Import Image icon import FormatQuoteIcon from '@mui/icons-material/FormatQuote'; import HorizontalRuleIcon from '@mui/icons-material/HorizontalRule'; import UndoIcon from '@mui/icons-material/Undo'; import RedoIcon from '@mui/icons-material/Redo'; import FormatHeadingIcon from '@mui/icons-material/FormatSize'; import DeveloperModeIcon from '@mui/icons-material/DeveloperMode'; import CustomImage from './CustomImage'; import Compressor from 'compressorjs' import ImageResize from 'tiptap-extension-resize-image'; // Import the ResizeImage extension import { isMobile } from '../../App'; const MenuBar = ({ setEditorRef, isChat }) => { const { editor } = useCurrentEditor(); const fileInputRef = useRef(null); if (!editor) { return null; } useEffect(() => { if (editor && setEditorRef) { setEditorRef(editor); } }, [editor, setEditorRef]); const handleImageUpload = async (event) => { const file = event.target.files[0]; let compressedFile await new Promise((resolve) => { new Compressor(file, { quality: 0.6, maxWidth: 1200, mimeType: 'image/webp', success(result) { const file = new File([result], 'name', { type: 'image/webp' }) compressedFile = file resolve() }, error(err) {} }) }) if (compressedFile) { const reader = new FileReader(); reader.onload = () => { const url = reader.result; editor.chain().focus().setImage({ src: url , style: "width: auto"}).run(); fileInputRef.current.value = ''; }; reader.readAsDataURL(compressedFile); } }; const triggerImageUpload = () => { fileInputRef.current.click(); // Trigger the file input click }; return (
editor.chain().focus().toggleBold().run()} disabled={ !editor.can() .chain() .focus() .toggleBold() .run() } // color={editor.isActive('bold') ? 'white' : 'gray'} sx={{ color: editor.isActive('bold') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().toggleItalic().run()} disabled={ !editor.can() .chain() .focus() .toggleItalic() .run() } // color={editor.isActive('italic') ? 'white' : 'gray'} sx={{ color: editor.isActive('italic') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().toggleStrike().run()} disabled={ !editor.can() .chain() .focus() .toggleStrike() .run() } // color={editor.isActive('strike') ? 'white' : 'gray'} sx={{ color: editor.isActive('strike') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().toggleCode().run()} disabled={ !editor.can() .chain() .focus() .toggleCode() .run() } // color={editor.isActive('code') ? 'white' : 'gray'} sx={{ color: editor.isActive('code') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().unsetAllMarks().run()} sx={{ color: editor.isActive('bold') || editor.isActive('italic') || editor.isActive('strike') || editor.isActive('code') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert', }} > editor.chain().focus().toggleBulletList().run()} // color={editor.isActive('bulletList') ? 'white' : 'gray'} sx={{ color: editor.isActive('bulletList') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().toggleOrderedList().run()} // color={editor.isActive('orderedList') ? 'white' : 'gray'} sx={{ color: editor.isActive('orderedList') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().toggleCodeBlock().run()} // color={editor.isActive('codeBlock') ? 'white' : 'gray'} sx={{ color: editor.isActive('codeBlock') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().toggleBlockquote().run()} // color={editor.isActive('blockquote') ? 'white' : 'gray'} sx={{ color: editor.isActive('blockquote') ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().setHorizontalRule().run()} disabled={!editor.can().chain().focus().setHorizontalRule().run()} sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert', }} > editor.chain().focus().toggleHeading({ level: 1 }).run()} // color={editor.isActive('heading', { level: 1 }) ? 'white' : 'gray'} sx={{ color: editor.isActive('heading', { level: 1 }) ? 'white' : 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().undo().run()} disabled={ !editor.can() .chain() .focus() .undo() .run() } sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }} > editor.chain().focus().redo().run()} disabled={ !editor.can() .chain() .focus() .redo() .run() } > {!isChat && ( <> )}
); }; const extensions = [ Color.configure({ types: [TextStyle.name, ListItem.name] }), TextStyle.configure({ types: [ListItem.name] }), StarterKit.configure({ bulletList: { keepMarks: true, keepAttributes: false, }, orderedList: { keepMarks: true, keepAttributes: false, }, }), Placeholder.configure({ placeholder: 'Start typing here...', // Add your placeholder text here }), ImageResize, ]; const content = ``; export default ({ setEditorRef, onEnter, disableEnter, isChat, maxHeightOffset, setIsFocusedParent, isFocusedParent, overrideMobile, customEditorHeight }) => { const [isFocused, setIsFocused] = useState(false); const extensionsFiltered = isChat ? extensions.filter((item)=> item?.name !== 'image') : extensions const editorRef = useRef(null); const setEditorRefFunc = (editorInstance) => { editorRef.current = editorInstance; setEditorRef(editorInstance) }; const handleFocus = () => { if(!isMobile) return // setIsFocused(true); setIsFocusedParent(true) }; const handleBlur = () => { const htmlContent = editorRef.current.getHTML(); if (!htmlContent?.trim() || htmlContent?.trim() === "

"){ // setIsFocused(false); // setIsFocusedParent(false) }; }; // useEffect(()=> { // setIsFocused(isFocusedParent) // },[isFocusedParent]) return ( } extensions={extensionsFiltered} content={content} onCreate={({ editor }) => { editor.on('focus', handleFocus); // Listen for focus event editor.on('blur', handleBlur); // Listen for blur event }} onUpdate={({ editor }) => { editor.on('focus', handleFocus); // Ensure focus is updated editor.on('blur', handleBlur); // Ensure blur is updated }} editorProps={{ attributes: { class: 'tiptap-prosemirror', style: isMobile && `overflow: auto; min-height: ${customEditorHeight ? '200px' : '0px'}; 200px; max-height:calc(100svh - ${ customEditorHeight ? customEditorHeight : '140px'})`, }, handleKeyDown(view, event) { if (!disableEnter && event.key === 'Enter') { if (event.shiftKey) { view.dispatch(view.state.tr.replaceSelectionWith(view.state.schema.nodes.hardBreak.create())); return true; } else { if (typeof onEnter === 'function') { onEnter(); } return true; } } return false; }, }} /> ) };