Skip to content

Commit

Permalink
fix(lexical-editor): normalize input value and improve onChange execu…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
Pavel910 committed Aug 14, 2024
1 parent 5dc8005 commit 0cd8b63
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 14 deletions.
27 changes: 14 additions & 13 deletions packages/lexical-editor/src/components/Editor/RichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,26 @@ import { LexicalErrorBoundary } from "@lexical/react/LexicalErrorBoundary";
import { makeDecoratable } from "@webiny/react-composition";
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import { RichTextEditorProvider } from "~/context/RichTextEditorContext";
import { isValidLexicalData } from "~/utils/isValidLexicalData";
import { UpdateStatePlugin } from "~/plugins/LexicalUpdateStatePlugin";
import { BlurEventPlugin } from "~/plugins/BlurEventPlugin/BlurEventPlugin";
import { LexicalValue, ToolbarActionPlugin } from "~/types";
import { Placeholder } from "~/ui/Placeholder";
import { generateInitialLexicalValue } from "~/utils/generateInitialLexicalValue";
import {
createTheme,
WebinyTheme,
ThemeEmotionMap,
toTypographyEmotionMap
} from "@webiny/lexical-theme";
import { allNodes } from "@webiny/lexical-nodes";
import { RichTextEditorProvider } from "~/context/RichTextEditorContext";
import { isValidLexicalData } from "~/utils/isValidLexicalData";
import { UpdateStatePlugin } from "~/plugins/LexicalUpdateStatePlugin";
import { BlurEventPlugin } from "~/plugins/BlurEventPlugin/BlurEventPlugin";
import { LexicalValue, ToolbarActionPlugin } from "~/types";
import { Placeholder } from "~/ui/Placeholder";
import { generateInitialLexicalValue } from "~/utils/generateInitialLexicalValue";
import { SharedHistoryContext, useSharedHistoryContext } from "~/context/SharedHistoryContext";
import {
LexicalEditorWithConfig,
useLexicalEditorConfig
} from "~/components/LexicalEditorConfig/LexicalEditorConfig";
import { normalizeInputValue } from "./normalizeInputValue";

export interface RichTextEditorProps {
children?: React.ReactNode | React.ReactNode[];
Expand All @@ -52,15 +53,14 @@ export interface RichTextEditorProps {
toolbarActionPlugins?: ToolbarActionPlugin[];
themeStylesTransformer?: (cssObject: Record<string, any>) => CSSObject;
toolbar?: React.ReactNode;
value: LexicalValue | null;
value: LexicalValue | null | undefined;
width?: number | string;
}

const BaseRichTextEditor = ({
toolbar,
staticToolbar,
onChange,
value,
nodes,
placeholder,
children,
Expand Down Expand Up @@ -103,6 +103,7 @@ const BaseRichTextEditor = ({
<Fragment key={plugin.name}>{plugin.element}</Fragment>
));

const value = normalizeInputValue(props.value);
const editorValue = isValidLexicalData(value) ? value : generateInitialLexicalValue();

const initialConfig = {
Expand All @@ -120,12 +121,12 @@ const BaseRichTextEditor = ({
editorState.read(() => {
if (typeof onChange === "function") {
const editorState = editor.getEditorState();
const isEmpty = $isRootTextContentEmpty(editor.isComposing(), true);
const isEditorEmpty = $isRootTextContentEmpty(editor.isComposing(), true);

const newValue = JSON.stringify(editorState.toJSON());

// We don't want to call "onChange" if editor text is empty, and original `value` is `undefined`.
if (value === undefined && isEmpty) {
// We don't want to call "onChange" if editor text is empty, and original `value` is empty.
if (!value && isEditorEmpty) {
return;
}

Expand All @@ -151,7 +152,7 @@ const BaseRichTextEditor = ({
themeEmotionMap={themeEmotionMap}
toolbarActionPlugins={props.toolbarActionPlugins}
>
{staticToolbar && staticToolbar}
{staticToolbar ? staticToolbar : null}
<div
/* This className is necessary for targeting of editor container from CSS files. */
className={"editor-shell"}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { LexicalValue, NormalizedInputValue } from "~/types";

const isValueEmpty = (value: any) => {
return [undefined, null, "", '""', "null"].includes(value);
};

/**
* Value passed to the `RichTextEditor` component can be anything. This function normalizes some of the more common shapes
* of input into a value that is either a `null` or a `LexicalValue`.
*/
export function normalizeInputValue(value: LexicalValue | null | undefined) {
if (isValueEmpty(value)) {
return null;
}

return value as NormalizedInputValue;
}
4 changes: 3 additions & 1 deletion packages/lexical-editor/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export type ToolbarType = "heading" | "paragraph" | string;
export type LexicalValue = string;

export type NormalizedInputValue = LexicalValue | null;

export { FontColorPicker } from "~/components/ToolbarActions/FontColorAction";

export type ImageActionType = "image-action";
Expand Down

0 comments on commit 0cd8b63

Please sign in to comment.