Skip to content

Commit

Permalink
added support for rich text editor
Browse files Browse the repository at this point in the history
  • Loading branch information
akanshaaa19 committed Feb 11, 2025
1 parent b82e13e commit 3a1f3b2
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 33 deletions.
29 changes: 28 additions & 1 deletion src/common/RichEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -162,3 +162,30 @@ export const WhatsAppTemplateButton = (text: string) => {

return result;
};

export const getFormattedText = (editor: any) => {
let extractedText = '';

Check warning on line 167 in src/common/RichEditor.tsx

View check run for this annotation

Codecov / codecov/patch

src/common/RichEditor.tsx#L167

Added line #L167 was not covered by tests

editor.update(() => {
const rootNode = $getRoot();
const selection = $getSelection();

Check warning on line 171 in src/common/RichEditor.tsx

View check run for this annotation

Codecov / codecov/patch

src/common/RichEditor.tsx#L169-L171

Added lines #L169 - L171 were not covered by tests

if (!rootNode) return;

rootNode.getChildren().forEach((node) => {

Check warning on line 175 in src/common/RichEditor.tsx

View check run for this annotation

Codecov / codecov/patch

src/common/RichEditor.tsx#L175

Added line #L175 was not covered by tests
if ($isTextNode(node)) {
let text = node.getTextContent();
const format = node.getFormat();

Check warning on line 178 in src/common/RichEditor.tsx

View check run for this annotation

Codecov / codecov/patch

src/common/RichEditor.tsx#L177-L178

Added lines #L177 - L178 were not covered by tests

// Check applied formatting
if (format & 1) text = `**${text}**`; // Bold
if (format & 2) text = `*${text}*`; // Italic
if (format & 16) text = `~~${text}~~`; // Strikethrough

extractedText += text + ' ';

Check warning on line 185 in src/common/RichEditor.tsx

View check run for this annotation

Codecov / codecov/patch

src/common/RichEditor.tsx#L185

Added line #L185 was not covered by tests
}
});
});

return extractedText.trim();

Check warning on line 190 in src/common/RichEditor.tsx

View check run for this annotation

Codecov / codecov/patch

src/common/RichEditor.tsx#L190

Added line #L190 was not covered by tests
};
55 changes: 29 additions & 26 deletions src/components/UI/Form/EmojiInput/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down Expand Up @@ -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]);

Expand All @@ -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,
Expand Down Expand Up @@ -217,7 +220,7 @@ export const Editor = ({ disabled = false, ...props }: EditorProps) => {
</span>
</div>
<div className={disabled ? styles?.disabled : styles.Editor} data-testid="resizer">
<PlainTextPlugin
<RichTextPlugin
placeholder={<Placeholder />}
contentEditable={
<div className={styles.editorScroller}>
Expand Down
49 changes: 44 additions & 5 deletions src/components/UI/Form/EmojiInput/EmojiInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -24,6 +25,7 @@ interface EmojiPickerProps {
handleClickAway: any;
showEmojiPicker: any;
setShowEmojiPicker: any;
editor?: any;
}

export const EmojiInput = ({
Expand All @@ -35,6 +37,7 @@ export const EmojiInput = ({
...props
}: EmojiInputProps) => {
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
const [editor] = useLexicalComposerContext();

const handleClickAway = () => {
setShowEmojiPicker(false);
Expand All @@ -45,11 +48,49 @@ export const EmojiInput = ({
handleClickAway={handleClickAway}
setShowEmojiPicker={setShowEmojiPicker}
showEmojiPicker={showEmojiPicker}
editor={editor}
/>
);

const input = (
<Editor field={{ name, value, onBlur }} picker={picker} onChange={handleChange} form={form} {...props} />
<Editor
field={{ name, value, onBlur }}
picker={picker}
onChange={(value: any) => {
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 (
Expand All @@ -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',
Expand Down
2 changes: 1 addition & 1 deletion src/components/UI/Form/WhatsAppEditor/WhatsAppEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}
Expand Down
1 change: 1 addition & 0 deletions src/containers/HSM/HSM.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 3a1f3b2

Please sign in to comment.