From 3a1f3b20ca0ee5fe0ed9993f4c059f9231f99f7d Mon Sep 17 00:00:00 2001 From: Akansha Sakhre Date: Tue, 11 Feb 2025 07:27:45 +0530 Subject: [PATCH] added support for rich text editor --- src/common/RichEditor.tsx | 29 +++++++++- src/components/UI/Form/EmojiInput/Editor.tsx | 55 ++++++++++--------- .../UI/Form/EmojiInput/EmojiInput.tsx | 49 +++++++++++++++-- .../UI/Form/WhatsAppEditor/WhatsAppEditor.tsx | 2 +- src/containers/HSM/HSM.tsx | 1 + 5 files changed, 103 insertions(+), 33 deletions(-) diff --git a/src/common/RichEditor.tsx b/src/common/RichEditor.tsx index a32db0337..1fc445f0b 100644 --- a/src/common/RichEditor.tsx +++ b/src/common/RichEditor.tsx @@ -2,7 +2,7 @@ import CallIcon from '@mui/icons-material/Call'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { Interweave } from 'interweave'; import { UrlMatcher } from 'interweave-autolink'; -import { $createParagraphNode, $createTextNode, $getRoot } from 'lexical'; +import { $createParagraphNode, $createTextNode, $getRoot, $getSelection, $isTextNode } from 'lexical'; // Indicates how to replace different parts of the text from WhatsApp to HTML. const regexForLink = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/gi; @@ -162,3 +162,30 @@ export const WhatsAppTemplateButton = (text: string) => { return result; }; + +export const getFormattedText = (editor: any) => { + let extractedText = ''; + + editor.update(() => { + const rootNode = $getRoot(); + const selection = $getSelection(); + + if (!rootNode) return; + + rootNode.getChildren().forEach((node) => { + if ($isTextNode(node)) { + let text = node.getTextContent(); + const format = node.getFormat(); + + // Check applied formatting + if (format & 1) text = `**${text}**`; // Bold + if (format & 2) text = `*${text}*`; // Italic + if (format & 16) text = `~~${text}~~`; // Strikethrough + + extractedText += text + ' '; + } + }); + }); + + return extractedText.trim(); +}; diff --git a/src/components/UI/Form/EmojiInput/Editor.tsx b/src/components/UI/Form/EmojiInput/Editor.tsx index 5cdfcd1b3..40e78b432 100644 --- a/src/components/UI/Form/EmojiInput/Editor.tsx +++ b/src/components/UI/Form/EmojiInput/Editor.tsx @@ -26,6 +26,7 @@ import { import { handleFormatterEvents, handleFormatting, setDefaultValue } from 'common/RichEditor'; import { FormatBold, FormatItalic, StrikethroughS } from '@mui/icons-material'; import { mergeRegister } from '@lexical/utils'; +import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'; export interface EditorProps { field: { name: string; onChange?: any; value: any; onBlur?: any }; @@ -75,32 +76,33 @@ export const Editor = ({ disabled = false, ...props }: EditorProps) => { return false; }, COMMAND_PRIORITY_LOW - ), - editor.registerCommand( - FORMAT_TEXT_COMMAND, - (event: any) => { - editor.update(() => { - const selection = $getSelection(); - const text = handleFormatting(selection?.getTextContent(), event); - - if (!selection?.getTextContent()) { - const newNode = $createTextNode(text); - selection?.insertNodes([newNode]); - const newSelection = $createRangeSelection(); - newSelection.anchor.set(newNode.getKey(), 1, 'text'); // Set the cursor between the backticks - newSelection.focus.set(newNode.getKey(), 1, 'text'); - $setSelection(newSelection); - } - if (selection?.getTextContent() && event) { - const newNode = $createTextNode(text); - selection?.insertNodes([newNode]); - editor.focus(); - } - }); - return false; - }, - COMMAND_PRIORITY_LOW ) + // editor.registerCommand( + // FORMAT_TEXT_COMMAND, + // (event: any) => { + // editor.update(() => { + // const selection = $getSelection(); + // const text = handleFormatting(selection?.getTextContent(), event); + + // if (!selection?.getTextContent()) { + // const newNode = $createTextNode(text); + // selection?.insertNodes([newNode]); + + // const newSelection = $createRangeSelection(); + // newSelection.anchor.set(newNode.getKey(), 1, 'text'); + // newSelection.focus.set(newNode.getKey(), 1, 'text'); + // $setSelection(newSelection); + // } + // if (selection?.getTextContent() && event) { + // const newNode = $createTextNode(text); + // selection?.insertNodes([newNode]); + // editor.focus(); + // } + // }); + // return false; + // }, + // COMMAND_PRIORITY_LOW + // ) ); }, [editor]); @@ -119,6 +121,7 @@ export const Editor = ({ disabled = false, ...props }: EditorProps) => { } }); }; + // console.log(editor.ge); const [activeFormats, setActiveFormats] = useState<{ bold: boolean; italic: boolean; strikethrough: boolean }>({ bold: false, @@ -217,7 +220,7 @@ export const Editor = ({ disabled = false, ...props }: EditorProps) => {
- } contentEditable={
diff --git a/src/components/UI/Form/EmojiInput/EmojiInput.tsx b/src/components/UI/Form/EmojiInput/EmojiInput.tsx index 4a9706402..793a3766a 100644 --- a/src/components/UI/Form/EmojiInput/EmojiInput.tsx +++ b/src/components/UI/Form/EmojiInput/EmojiInput.tsx @@ -5,7 +5,8 @@ import { EmojiPicker } from 'components/UI/EmojiPicker/EmojiPicker'; import { Editor } from './Editor'; import Styles from './EmojiInput.module.css'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { $createTextNode, $getSelection, $isRangeSelection } from 'lexical'; +import { $createTextNode, $getRoot, $getSelection, $isParagraphNode, $isRangeSelection, $isTextNode } from 'lexical'; +import { getFormattedText } from 'common/RichEditor'; export interface EmojiInputProps { field: { name: string; onChange?: any; value: any; onBlur?: any }; @@ -24,6 +25,7 @@ interface EmojiPickerProps { handleClickAway: any; showEmojiPicker: any; setShowEmojiPicker: any; + editor?: any; } export const EmojiInput = ({ @@ -35,6 +37,7 @@ export const EmojiInput = ({ ...props }: EmojiInputProps) => { const [showEmojiPicker, setShowEmojiPicker] = useState(false); + const [editor] = useLexicalComposerContext(); const handleClickAway = () => { setShowEmojiPicker(false); @@ -45,11 +48,49 @@ export const EmojiInput = ({ handleClickAway={handleClickAway} setShowEmojiPicker={setShowEmojiPicker} showEmojiPicker={showEmojiPicker} + editor={editor} /> ); const input = ( - + { + let formattedText = ''; + + editor.update(() => { + const rootNode = $getRoot(); + if (!rootNode) return; + + rootNode.getChildren().forEach((node) => { + if ($isParagraphNode(node)) { + let paragraphText = ''; + + node.getChildren().forEach((textNode) => { + if ($isTextNode(textNode)) { + let text = textNode.getTextContent(); + const format = textNode.getFormat(); + console.log(format); + + // ✅ Apply formatting styles + if (format & 1) text = `**${text}**`; // Bold + if (format & 2) text = `*${text}*`; // Italic + if (format & 16) text = `~~${text}~~`; // Strikethrough + + paragraphText += text; + } + }); + + formattedText += paragraphText + '\n'; // Preserve paragraph breaks + } + }); + }); + handleChange(value); + }} + form={form} + {...props} + /> ); return ( @@ -60,9 +101,7 @@ export const EmojiInput = ({ ); }; -const EmojiPickerComponent = ({ showEmojiPicker, setShowEmojiPicker, handleClickAway }: EmojiPickerProps) => { - const [editor] = useLexicalComposerContext(); - +const EmojiPickerComponent = ({ showEmojiPicker, setShowEmojiPicker, handleClickAway, editor }: EmojiPickerProps) => { const emojiStyles = { position: 'absolute', bottom: '60px', diff --git a/src/components/UI/Form/WhatsAppEditor/WhatsAppEditor.tsx b/src/components/UI/Form/WhatsAppEditor/WhatsAppEditor.tsx index 05f127244..f55a16dbb 100644 --- a/src/components/UI/Form/WhatsAppEditor/WhatsAppEditor.tsx +++ b/src/components/UI/Form/WhatsAppEditor/WhatsAppEditor.tsx @@ -56,7 +56,7 @@ export const WhatsAppEditor = ({ setEditorState, sendMessage, readOnly = false } editor.update(() => { const selection = $getSelection(); if (selection?.getTextContent() && formatter) { - const text = handleFormatting(formatter, selection?.getTextContent()); + const text = handleFormatting(selection?.getTextContent(), formatter); const newNode = $createTextNode(text); selection?.insertNodes([newNode]); } diff --git a/src/containers/HSM/HSM.tsx b/src/containers/HSM/HSM.tsx index 7ef53e5da..f23f97a0b 100644 --- a/src/containers/HSM/HSM.tsx +++ b/src/containers/HSM/HSM.tsx @@ -506,6 +506,7 @@ export const HSM = () => { helperText: 'You can provide variable values in your HSM templates to personalize the message. To add: click on the variable button and provide an example value for the variable in the field provided below', handleChange: (value: any) => { + // console.log('value', value); setBody(value); }, defaultValue: isEditing && editorState,