Skip to content

Commit

Permalink
Merge pull request #136 from upneet-betalectic/Text-Editor
Browse files Browse the repository at this point in the history
Text editor Updated changes
  • Loading branch information
ShreyDhyani authored Dec 13, 2023
2 parents 64bea3d + f84cb50 commit 0114405
Show file tree
Hide file tree
Showing 13 changed files with 258 additions and 162 deletions.
11 changes: 11 additions & 0 deletions .changeset/late-ravens-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@locoworks/reusejs-react-text-editor": patch
---

Changes:

- Prop added for mentionsData.
- optional Prop added for mentionLookupservice , setup a default lookup service inside plugin.
- Changes setEditable to optional.
- Added Paragraph node after inserting table.
- Fixed error occuring when creating lists at start.
4 changes: 2 additions & 2 deletions components/text-editor/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ button.item.dropdown-item-active i {
background: #fff;
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3);
border-radius: 8px;
z-index: 50;
z-index: 9999;
}

.typeahead-popover ul {
Expand Down Expand Up @@ -730,7 +730,7 @@ button.item.dropdown-item-active i {

.mentions-menu {
width: 250px;
position: absolute;
position: relative;
}

.mention:focus {
Expand Down
119 changes: 90 additions & 29 deletions components/text-editor/plugins/MentionPlugin/MentionPlugin.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useState, useMemo, useCallback } from "react";
import React, {
useState,
useMemo,
useCallback,
useRef,
useEffect,
} from "react";
import * as ReactDOM from "react-dom";
import { TextNode } from "lexical";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
Expand Down Expand Up @@ -139,18 +145,70 @@ function MentionsTypeaheadMenuItem({
</li>
);
}

function defaultLookupService(
mentionString: string | null,
mentionsData: Array<{
mentionName: string;
label: string;
}>,
) {
const [results, setResults] = useState<
Array<{ mentionName: string; label: string }>
>([]);

function defaultSearch(
string: string,
callback: (results: Array<{ mentionName: string; label: string }>) => void,
): void {
setTimeout(() => {
const results = mentionsData.filter((mention) =>
mention.label.toLowerCase().includes(string.toLowerCase()),
);
callback(results);
}, 500);
}

useEffect(() => {
if (mentionString === null) {
setResults([]);
} else {
defaultSearch(mentionString, (newResults) => {
setResults(newResults);
});
}
}, [mentionString]);

return results;
}

type Props = {
useMentionLookupService: (mentionString: string | null) => Array<{
mentionsData: Array<{
mentionName: string;
label: string;
}>;
useMentionLookupService?: (
mentionString: string | null,
mentionsData: Array<{
mentionName: string;
label: string;
}>,
) => Array<{
mentionName: string;
label: string;
}>;
};

export default function NewMentionsPlugin({
mentionsData,
useMentionLookupService,
}: Props): JSX.Element | null {
const [editor] = useLexicalComposerContext();
const dropDownRef = useRef<null | HTMLDivElement>(null);
const [searchquery, setSearchquery] = useState<string | null>(null);
const results = useMentionLookupService(searchquery);
const results = useMentionLookupService
? useMentionLookupService(searchquery, mentionsData)
: defaultLookupService(searchquery, mentionsData);
const checkForSlashTriggerMatch = useBasicTypeaheadTriggerMatch("/", {
minLength: 0,
});
Expand Down Expand Up @@ -219,32 +277,35 @@ export default function NewMentionsPlugin({
menuRenderFn={(
anchorElementRef,
{ selectedIndex, selectOptionAndCleanUp, setHighlightedIndex },
) =>
anchorElementRef.current && results.length
? ReactDOM.createPortal(
<div className="typeahead-popover mentions-menu">
<ul>
{options.map((option, i: number) => (
<MentionsTypeaheadMenuItem
index={i}
isSelected={selectedIndex === i}
onClick={() => {
setHighlightedIndex(i);
selectOptionAndCleanUp(option);
}}
onMouseEnter={() => {
setHighlightedIndex(i);
}}
key={option.key}
option={option}
/>
))}
</ul>
</div>,
anchorElementRef.current,
)
: null
}
) => {
if (anchorElementRef.current && results.length) {
return ReactDOM.createPortal(
<div
className="typeahead-popover mentions-menu"
ref={dropDownRef}
>
<ul>
{options.map((option, i: number) => (
<MentionsTypeaheadMenuItem
index={i}
isSelected={selectedIndex === i}
onClick={() => {
setHighlightedIndex(i);
selectOptionAndCleanUp(option);
}}
onMouseEnter={() => {
setHighlightedIndex(i);
}}
key={option.key}
option={option}
/>
))}
</ul>
</div>,
anchorElementRef.current,
);
} else return null;
}}
/>
);
else {
Expand Down
2 changes: 1 addition & 1 deletion components/text-editor/plugins/TablePlugin/TableNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ export class TableNode extends DecoratorNode<JSX.Element> {

decorate(_: LexicalEditor, config: EditorConfig): JSX.Element {
return (
<Suspense>
<Suspense fallback={null}>
<TableComponent
nodeKey={this.__key}
theme={config.theme}
Expand Down
3 changes: 2 additions & 1 deletion components/text-editor/plugins/TablePlugin/TablePlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React, {
useState,
} from "react";
import {
$createParagraphNode,
$insertNodes,
COMMAND_PRIORITY_EDITOR,
createCommand,
Expand Down Expand Up @@ -185,7 +186,7 @@ export function TablePlugin({
Number(columns),
includeHeaders,
);
$insertNodes([tableNode]);
$insertNodes([tableNode, $createParagraphNode()]);
return true;
},
COMMAND_PRIORITY_EDITOR,
Expand Down
33 changes: 22 additions & 11 deletions components/text-editor/src/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { HeadingNode } from "@lexical/rich-text";
import { ListNode, ListItemNode } from "@lexical/list";
import { LinkNode, AutoLinkNode } from "@lexical/link";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { $getRoot, $insertNodes, EditorState, LexicalEditor } from "lexical";
import { $getRoot, $nodesOfType, EditorState, LexicalEditor } from "lexical";

import { EditorTheme } from "../theme";
import { MentionNode } from "../plugins/MentionPlugin/MentionNode";
Expand All @@ -31,8 +31,18 @@ import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
type EditorProps = {
editorRef: React.MutableRefObject<LexicalEditor | null>;
editState: boolean;
setEditable: React.Dispatch<React.SetStateAction<boolean>>;
useMentionLookupService: (mentionString: string | null) => Array<{
setEditable?: React.Dispatch<React.SetStateAction<boolean>>;
mentionsData: Array<{
mentionName: string;
label: string;
}>;
useMentionLookupService?: (
mentionString: string | null,
mentionsData: Array<{
mentionName: string;
label: string;
}>,
) => Array<{
mentionName: string;
label: string;
}>;
Expand All @@ -45,6 +55,7 @@ function Editor({
editorRef,
editState,
setEditable,
mentionsData,
useMentionLookupService,
convertFilesToImageUrl,
onChangeCallback,
Expand All @@ -55,7 +66,7 @@ function Editor({
const isEditable = useLexicalEditable();

useEffect(() => {
editor.setEditable(editState);
if (setEditable) editor.setEditable(editState);
}, [editState, editor]);

const placeholder = <div className="placeholder">{placeholderText}</div>;
Expand All @@ -66,6 +77,7 @@ function Editor({
const htmlString = $generateHtmlFromNodes(editor, null);
payload["html"] = htmlString;
payload["json"] = JSON.stringify(editor.getEditorState());
payload["mentions"] = $nodesOfType(MentionNode);

onChangeCallback?.(editorRef.current, payload);
});
Expand All @@ -80,14 +92,10 @@ function Editor({
const dom = parser.parseFromString(htmlData, "text/html");

const nodes = $generateNodesFromDOM(editor, dom);
if ($getRoot().getFirstChild() === null) {
$getRoot().select();
$insertNodes(nodes);
} else {
if ($getRoot().getFirstChild() !== null) {
$getRoot().clear();
$getRoot().select();
$insertNodes(nodes);
}
$getRoot().append(...nodes);
}
});
}, [htmlData]);
Expand Down Expand Up @@ -147,7 +155,10 @@ function Editor({
<AutoLinkPlugin matchers={MATCHERS} />
<TabIndentationPlugin />
<ImagesPlugin />
<MentionPlugin useMentionLookupService={useMentionLookupService} />
<MentionPlugin
mentionsData={mentionsData}
useMentionLookupService={useMentionLookupService}
/>
<NewTablePlugin cellEditorConfig={cellEditorConfig}>
<RichTextPlugin
contentEditable={
Expand Down
Loading

0 comments on commit 0114405

Please sign in to comment.