Open kamruzzaman-gain opened 9 months ago
Unable to insert html into Quill React Component.
React version: "18.2.0" react-quill: "2.0.0" next:"14.1.0"
'use client'; import { useLazyQuery } from '@apollo/client'; import dynamic from 'next/dynamic'; import React, { useRef, useEffect, useState } from 'react'; import { getUserFullName, toastAlert } from '@/utils'; import 'react-quill/dist/quill.snow.css'; import { GET_ORGANIZATION_USERS } from '@/components/user-and-roles/graphql/queries/getOrganizationUsers.gql'; import { useSelector } from 'react-redux'; import { size } from 'lodash'; import { useDebounce } from '@/helpers/hooks'; const loadReactQuill = async () => { const { default: RQ } = await import('react-quill'); const reactQuillWithRef = ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />; return reactQuillWithRef; }; const ReactQuill = dynamic(loadReactQuill, { ssr: false }); const AppEditor = ({ readOnly = false, onChange, allowMention = false, value = '', placeholder = '', toolbarBottom = false, setMentionedIds = [] }) => { const editorRef = useRef(null); const quillContainerRef = useRef(null); const [showMentionList, setShowMentionList] = useState(false); const [mentionListPosition, setMentionListPosition] = useState({ top: 0, left: 0 }); const [cursorIndex, setCursorIndex] = useState({}); const userData = useSelector((state) => state.auth.user); const [agentsState, setAgentsState] = useState({ data: [], metaData: {}, loading: true, loaded: false, searchKeyword: '', optionData: { limit: 50, offset: 0, order: [['created_at', 'desc']] } }); const [getAgents] = useLazyQuery(GET_ORGANIZATION_USERS, { errorPolicy: 'all', fetchPolicy: 'no-cache', variables: { queryData: { roleName: 'org_agent', search_keyword: agentsState.searchKeyword }, optionData: agentsState.optionData }, onCompleted: (data) => { const currentUserId = userData?.user_id; const preparedData = data?.getOrganizationUsers?.data?.map((org_user) => { let title = org_user?.user?.first_name || org_user?.user.last_name ? getUserFullName(org_user?.user?.first_name, org_user?.user?.last_name) : org_user?.user?.email; if (org_user?.user?.id === currentUserId) { title += ' (You)'; } return { ...org_user, key: org_user?.id, title }; }) || []; setAgentsState((prevState) => ({ ...prevState, data: agentsState.optionData.offset === 0 ? preparedData : [...prevState.data, ...preparedData], metaData: data?.getOrganizationUsers?.meta_data || {}, loading: false, loaded: true })); }, onError: (error) => { console.error(error); setAgentsState((prevState) => ({ ...prevState, loading: false })); } }); const updateQueryData = useDebounce((value) => { setAgentsState((prev) => ({ ...prev, loading: true, searchKeyword: value, data: [], optionData: { ...prev.optionData, offset: 0 }, metaData: {} })); }, 500); const handleContentChange = (content, delta, source, editor) => { if (allowMention && source === 'user') { const cursorPosition = editor.getSelection()?.index; if (cursorPosition) { const textBeforeCursor = editor.getText(0, cursorPosition); const atIndex = textBeforeCursor.lastIndexOf('@'); const spaceIndex = textBeforeCursor.indexOf(' ', atIndex); if (atIndex > -1 && spaceIndex < 0) { const wordEnd = textBeforeCursor.length; const searchTerm = textBeforeCursor.substring(atIndex + 1, wordEnd); setCursorIndex({ startText: atIndex, endText: wordEnd }); updateQueryData(searchTerm); setShowMentionList(true); const bounds = editor.getBounds(cursorPosition); setMentionListPosition({ top: bounds.bottom, left: bounds.left }); } else { setShowMentionList(false); } } } onChange(content); }; const handleSelectMention = (mention) => { setMentionedIds((prev) => [...prev, mention.id]); const editor = editorRef.current.getEditor(); const range = editor.getSelection(true); if (range) { updateQueryData(''); editor.deleteText(cursorIndex.startText, cursorIndex.endText); editor.clipboard.dangerouslyPasteHTML( cursorIndex.startText, `<p><strong id='${mention.id}'>@${mention.title}</strong></p>` ); setShowMentionList(false); onChange(editor.root.innerHTML); } }; useEffect(() => { const handleClickOutside = (event) => { if (quillContainerRef.current && !quillContainerRef.current.contains(event.target)) { setShowMentionList(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); useEffect(() => { if (editorRef.current) { const editor = editorRef.current.getEditor(); if (editor && editor.container) { quillContainerRef.current = editor.container; } } }, [editorRef]); useEffect(() => { getAgents(); }, []); const modules = { toolbar: { container: [ [{ header: [1, 2, false] }], ['bold', 'italic', 'underline', 'strike', 'blockquote'], [({ list: 'ordered' }, { list: 'bullet' }, { indent: '-1' }, { indent: '+1' })], ['link', 'image', 'video'] ] } }; const formats = [ 'header', 'bold', 'italic', 'underline', 'strike', 'blockquote', 'list', 'bullet', 'indent', 'link', 'image', 'video' ]; const editorClassName = toolbarBottom ? 'editor-toolbar-bottom' : ''; return ( <div className={`react-quill-editor ${editorClassName} relative`}> <ReactQuill forwardedRef={editorRef} theme="snow" value={value} onChange={handleContentChange} readOnly={readOnly} modules={modules} formats={formats} placeholder={placeholder} /> {showMentionList && ( <div className="absolute z-10 mt-1 bg-white rounded border border-gray-300 shadow-lg" style={{ top: mentionListPosition.top, left: mentionListPosition.left }} > {size(agentsState.data) > 0 && agentsState.data.map((mention) => ( <div key={mention.id} className="px-4 py-2 cursor-pointer hover:bg-blue-100" onMouseDown={(e) => { e.preventDefault(); handleSelectMention(mention); }} > {mention.title} </div> ))} </div> )} </div> ); }; export default AppEditor;
const loadReactQuill = async () => { const { default: RQ } = await import('react-quill'); const Parchment = RQ.Quill.import('parchment'); const Block = Parchment.query('block'); class ButtonBlot extends Block { static create(value) { const node = super.create(value); node.setAttribute('class', 'button-class'); node.setAttribute('style', 'button-style'); return node; } } ButtonBlot.blotName = 'button'; ButtonBlot.tagName = 'button'; RQ.Quill.register(ButtonBlot); const reactQuillWithRef = ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />; return reactQuillWithRef; };
Did you solve this problem?
Issue Summary:
Unable to insert html into Quill React Component.
Environment:
React version: "18.2.0" react-quill: "2.0.0" next:"14.1.0"
Code snippet:
My Component file:
I have alos tried with parchment package but this also not work.
Here the Code sinppet