react-quill icon indicating copy to clipboard operation
react-quill copied to clipboard

Different custom toolbar, Quill editor breaks when page is refresh

Open naga-RajanR opened this issue 2 years ago • 3 comments

I am using quill version 1.3.5

I have a use case of displaying 4 editors in a single page. I have created a single quill editor component with custom toolbar. I used this component in the required page 4 times passing unique ID as a prop. Page and editor working as expected when navigating but the page breaks when we refresh the page or directly go to that page.

Am getting this error WhatsApp Image 2022-05-05 at 5 53 13 PM

these are the 3 files

editor component file ` import React from "react"; import ReactQuill from "react-quill"; import EditorToolbar, { modules, formats } from "./editorToolbar";

export const Editor = (props) => { return ( <div className="text-editor-container"> {props.id&&<div id={props.id+'-editor-container'} className="text-editor"> <EditorToolbar onSave={props.save} id={props.id}/> <ReactQuill theme="snow" value={props.value} id={props.id+'-id'} onChange={props.onChange} onBlur={props.save} placeholder="" className={props.id} modules={modules[props.id]} formats={formats} /> } ); }; export default Editor; `

Custom toolbar (EditorToolbar component) file: `

import React from "react"; import { Quill } from "react-quill"; import SaveIcon from "../../../../../assets/icon/save.png"; import ColorIcon from "../../../../../assets/icon/color.png"; import LinkIcon from "../../../../../assets/icon/link.png";

// Set custom icons for quill editor var icons = Quill.import("ui/icons"); icons["color"] = <img src="${ColorIcon}" alt=''/>; icons["link"] = <img src="${LinkIcon}" alt=''/>;

var Link = Quill.import('formats/link'); Link.sanitize = function(url) { // modify url if desired url=url.includes('http')?url:'https://'+url return url; }

const TradeMarkSymbol = () => ; const RegisteredSymbol = () => ®; const LessThanSymbol = () => ; const GreaterThanSymbol = () => ;

// Undo button const CustomUndo = () => ( <svg width="19" height="8" viewBox="0 0 19 8" fill="none" xmlns="http://www.w3.org/2000/svg"> ); // Redo button icon component for Quill editor const CustomRedo = () => ( <svg width="18" height="8" viewBox="0 0 18 8" fill="none" xmlns="http://www.w3.org/2000/svg"> ); // Save button component for Quill editor const CustomButton = () => ;

// Undo and redo functions for Custom Toolbar function undoChange() { this.quill.history.undo(); } function redoChange() { this.quill.history.redo(); }

function insertTrademark() { const cursorPosition = this.quill.getSelection().index; this.quill.insertText(cursorPosition, '™'); this.quill.setSelection(cursorPosition + 1); } function insertRegistered() { const cursorPosition = this.quill.getSelection().index; this.quill.insertText(cursorPosition, '®'); this.quill.setSelection(cursorPosition + 1); } function insertGreaterSymbol() { const cursorPosition = this.quill.getSelection().index; this.quill.insertText(cursorPosition, '≥'); this.quill.setSelection(cursorPosition + 1); } function insertLessSymbol() { const cursorPosition = this.quill.getSelection().index; this.quill.insertText(cursorPosition, '≤'); this.quill.setSelection(cursorPosition + 1); }

var bindings = { // custom keyboard bindings for shortcut keys in quill editor customScript: { key: 'S', shiftKey: true, shortKey:true, handler: function(range, context) { // Handle shift+b this.quill.formatText(range, 'script', 'super'); } }, customAlignCenter: { key: 'C', shiftKey: true, shortKey:true, handler: function(range, context) { this.quill.format('align', 'center'); } }, customAlignRight: { key: 'R', shiftKey: true, shortKey:true, handler: function(range, context) { this.quill.format('align', 'right'); } }, customAlignLeft: { key: 'L', shiftKey: true, shortKey:true, handler: function(range, context) { this.quill.format('align', ''); } }, customAlignJustify: { key: 'J', shiftKey: true, shortKey:true, handler: function(range, context) { this.quill.format('align', 'justify'); } }, };

const Delta = Quill.import('delta');

function matchMsWordList(node, delta) { // Clone the operations let ops = delta.ops.map((op) => Object.assign({}, op));

// Trim the front of the first op to remove the bullet/number let bulletOp = ops.find((op) => op.insert && op.insert.trim().length); if (!bulletOp) { return delta }

bulletOp.insert = bulletOp.insert.trimLeft(); let listPrefix = bulletOp.insert.match(/^.*?(^·|.)/) || bulletOp.insert[0]; bulletOp.insert = bulletOp.insert.substring(listPrefix[0].length, bulletOp.insert.length).trimLeft();

// Trim the newline off the last op let last = ops[ops.length-1]; last.insert = last.insert.substring(0, last.insert.length - 1);

// Determine the list type let listType = listPrefix[0].length === 1 ? 'bullet' : 'ordered';

// Determine the list indent let style = node.getAttribute('style').replace(/\n+/g, ''); let levelMatch = style.match(/level(\d+)/); let indent = levelMatch ? levelMatch[1] - 1 : 0;

// Add the list attribute ops.push({insert: '\n', attributes: {list: listType, indent}})

return new Delta(ops); }

function maybeMatchMsWordList(node, delta) { if (delta.ops[0].insert.trimLeft()[0] === '·') { return matchMsWordList(node, delta); }

return delta; }

const MSWORD_MATCHERS = [ ['p.MsoListParagraphCxSpFirst', matchMsWordList], ['p.MsoListParagraphCxSpMiddle', matchMsWordList], ['p.MsoListParagraphCxSpLast', matchMsWordList], ['p.MsoListParagraph', matchMsWordList], ['p.msolistparagraph', matchMsWordList], ['p.MsoNormal', maybeMatchMsWordList] ];

const handlers={ undo: undoChange, redo: redoChange, insertTrademark:insertTrademark, insertRegistered:insertRegistered, insertLessSymbol:insertLessSymbol, insertGreaterSymbol:insertGreaterSymbol }

// Modules object for setting up the Quill editor export const modules = { "rich-editor-synopsis": { toolbar: { container: "#rich-editor-synopsis", handlers: handlers, }, clipboard: { matchers: MSWORD_MATCHERS }, keyboard: { bindings: bindings }, history: { delay: 500, maxStack: 100, userOnly: true, }, }, "rich-editor-recommendation":{ toolbar: { container: "#rich-editor-recommendation", handlers: handlers, }, clipboard: { matchers: MSWORD_MATCHERS }, keyboard: { bindings: bindings }, history: { delay: 500, maxStack: 100, userOnly: true, }, }, "rich-editor-trials": { toolbar: { container: "#rich-editor-trials", handlers: handlers, }, clipboard: { matchers: MSWORD_MATCHERS }, keyboard: { bindings: bindings }, history: { delay: 500, maxStack: 100, userOnly: true, }, }, "rich-editor-references": { toolbar: { container: "#rich-editor-references", handlers: handlers, }, clipboard: { matchers: MSWORD_MATCHERS }, keyboard: { bindings: bindings }, history: { delay: 500, maxStack: 100, userOnly: true, }, }, };

// Formats objects for setting up the Quill editor export const formats = [ "header", "font", "size", "bold", "italic", "underline", "align", "strike", "script", "blockquote", "background", "list", "bullet", "indent", "link", "image", "color", "code-block", ];

// Quill Toolbar component WhatsApp Image 2022-05-09 at 10 54 13 AM

export default QuillToolbar;

`

My main page file;

` import React from 'react'

export default function editorPage() { return (

<RichEditor onChange={(val) => this.onModifyAccesshopeComment("synopsisComments",val,"rich-editor-synopsis")} save={this.saveDraft} value={this.state.synopsisComments} id="rich-editor-synopsis" /> <RichEditor onChange={(val) => this.onModifyAccesshopeComment("synopsisComments",val,"rich-editor-synopsis")} save={this.saveDraft} value={this.state.synopsisComments} id="rich-editor-synopsis" /> <RichEditor onChange={(val) => this.onModifyAccesshopeComment("synopsisComments",val,"rich-editor-synopsis")} save={this.saveDraft} value={this.state.synopsisComments} id="rich-editor-synopsis" /> <RichEditor onChange={(val) => this.onModifyAccesshopeComment("synopsisComments",val,"rich-editor-synopsis")} save={this.saveDraft} value={this.state.synopsisComments} id="rich-editor-synopsis" />
) }

`

naga-RajanR avatar May 09 '22 05:05 naga-RajanR

@naga-RajanR could you say if you are using React or Next, please? Btw, your main page file shows 4 Rich Editor components with the same ID, there aren't uniques.

josealvaradoo avatar May 15 '22 04:05 josealvaradoo

Hi @josealvaradoo am using react version 16. Yes sorry for that mistake. Actually it have different ids, this is my main page

` My main page file;

` import React from 'react'

export default function editorPage() { return (

<RichEditor onChange={(val) => this.onModifyAccesshopeComment("synopsisComments",val,"rich-editor-synopsis")} save={this.saveDraft} value={this.state.synopsisComments} id="rich-editor-synopsis" /> <RichEditor onChange={(val) => this.onModifyAccesshopeComment("recommendationComments",val,"rich-editor-recommendation")} save={this.saveDraft} value={this.state.recommendationComments} id="rich-editor-recommendation" /> <RichEditor onChange={(val) => this.onModifyAccesshopeComment("trialComments",val,"rich-editor-trial")} save={this.saveDraft} value={this.state.trialComments} id="rich-editor-trial" /> <RichEditor onChange={(val) => this.onModifyAccesshopeComment("referenceComments",val,"rich-editor-reference")} save={this.saveDraft} value={this.state.referenceComments} id="rich-editor-reference" />

) } `

naga-RajanR avatar May 17 '22 05:05 naga-RajanR

In case this helps anyone -- currently running into this issue, [email protected].

Removing the theme (i.e. passing theme={null} as a prop to the editor fixes the issue, but removes all styling. So it's not ideal 😅

bravodavid56 avatar Jul 07 '23 17:07 bravodavid56