Skip to content

Commit

Permalink
fix(lexical-editor): improve editor theme handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel910 committed Nov 18, 2024
1 parent f905515 commit a114435
Show file tree
Hide file tree
Showing 11 changed files with 47 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import {
createTheme,
WebinyTheme,
EditorTheme,
ThemeEmotionMap,
toTypographyEmotionMap
} from "@webiny/lexical-theme";
Expand Down Expand Up @@ -48,7 +48,7 @@ export interface RichTextEditorProps {
staticToolbar?: React.ReactNode;
styles?: React.CSSProperties;
tag?: string;
theme: WebinyTheme;
theme: EditorTheme;
themeEmotionMap?: ThemeEmotionMap;
toolbarActionPlugins?: ToolbarActionPlugin[];
themeStylesTransformer?: (cssObject: Record<string, any>) => CSSObject;
Expand Down Expand Up @@ -79,7 +79,7 @@ const BaseRichTextEditor = ({
props.themeEmotionMap ??
toTypographyEmotionMap(css, props.theme, props.themeStylesTransformer);

const editorTheme = useRef(createTheme());
const editorTheme = useRef(createTheme(props.theme));
const config = useLexicalEditorConfig();
const { historyState } = useSharedHistoryContext();
const placeholderElem = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
import { allNodes } from "@webiny/lexical-nodes";
import {
createTheme,
WebinyTheme,
EditorTheme,
ThemeEmotionMap,
toTypographyEmotionMap
} from "@webiny/lexical-theme";
Expand All @@ -22,7 +22,7 @@ import { RichTextEditorProvider } from "~/context/RichTextEditorContext";
interface LexicalHtmlRendererProps {
nodes?: Klass<LexicalNode>[];
value: LexicalValue | null;
theme: WebinyTheme;
theme: EditorTheme;
themeEmotionMap?: ThemeEmotionMap;
themeStylesTransformer?: (cssObject: Record<string, any>) => CSSObject;
}
Expand All @@ -35,7 +35,7 @@ export const LexicalHtmlRenderer = ({
}: LexicalHtmlRendererProps) => {
const themeEmotionMap =
props?.themeEmotionMap ?? toTypographyEmotionMap(css, theme, props.themeStylesTransformer);
const editorTheme = useRef(createTheme());
const editorTheme = useRef(createTheme(theme));
const editorValue = isValidLexicalData(value) ? value : generateInitialLexicalValue();

const initialConfig = {
Expand Down
6 changes: 3 additions & 3 deletions packages/lexical-editor/src/context/RichTextEditorContext.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import React, { createContext } from "react";
import { LexicalEditor } from "lexical";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import type { ThemeEmotionMap, WebinyTheme } from "@webiny/lexical-theme";
import type { ThemeEmotionMap, EditorTheme } from "@webiny/lexical-theme";
import { ToolbarActionPlugin } from "~/types";

export interface RichTextEditorContext {
editor: LexicalEditor;
theme?: WebinyTheme;
theme?: EditorTheme;
themeEmotionMap?: ThemeEmotionMap;
toolbarActionPlugins: ToolbarActionPlugin[];
}

export const RichTextEditorContext = createContext<RichTextEditorContext | undefined>(undefined);

interface RichTextEditorProviderProps {
theme: WebinyTheme;
theme: EditorTheme;
themeEmotionMap?: ThemeEmotionMap;
toolbarActionPlugins?: ToolbarActionPlugin[];
children?: React.ReactNode | React.ReactNode[];
Expand Down
4 changes: 2 additions & 2 deletions packages/lexical-nodes/src/FontColorNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export class FontColorNode extends TextNode {

override updateDOM(prevNode: FontColorNode, dom: HTMLElement, config: EditorConfig): boolean {
const isUpdated = super.updateDOM(prevNode, dom, config);
this.__color.updateFromTheme(config.theme);
this.__color.updateFromTheme(config.theme as EditorTheme);

dom.setAttribute(FontColorNodeAttrName, this.__color.getName());
dom.style.color = this.__color.getValue();
Expand All @@ -160,7 +160,7 @@ export class FontColorNode extends TextNode {

override createDOM(config: EditorConfig): HTMLElement {
const element = super.createDOM(config);
return this.addColorValueToHTMLElement(element, config.theme);
return this.addColorValueToHTMLElement(element, config.theme as EditorTheme);
}
}

Expand Down
6 changes: 3 additions & 3 deletions packages/lexical-nodes/src/HeadingNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
HeadingTagType,
SerializedHeadingNode as BaseSerializedHeadingNode
} from "@lexical/rich-text";
import { WebinyTheme, ThemeEmotionMap, findTypographyStyleByHtmlTag } from "@webiny/lexical-theme";
import { EditorTheme, ThemeEmotionMap, findTypographyStyleByHtmlTag } from "@webiny/lexical-theme";
import { ParagraphNode } from "~/ParagraphNode";
import { TypographyStylesNode, ThemeStyleValue, TextNodeThemeStyles } from "~/types";

Expand Down Expand Up @@ -73,7 +73,7 @@ export class HeadingNode
return new HeadingNode(node.getTag(), node.getTypographyStyleId(), node.__key);
}

protected updateElementWithThemeClasses(element: HTMLElement, theme: WebinyTheme): HTMLElement {
protected updateElementWithThemeClasses(element: HTMLElement, theme: EditorTheme): HTMLElement {
if (!theme?.emotionMap) {
return element;
}
Expand Down Expand Up @@ -103,7 +103,7 @@ export class HeadingNode

override createDOM(config: EditorConfig): HTMLElement {
const element = super.createDOM(config);
return this.updateElementWithThemeClasses(element, config.theme as WebinyTheme);
return this.updateElementWithThemeClasses(element, config.theme as EditorTheme);
}

static override importJSON(serializedNode: SerializeHeadingNode): BaseHeadingNode {
Expand Down
12 changes: 6 additions & 6 deletions packages/lexical-nodes/src/ListNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
SerializedElementNode,
Spread
} from "lexical";
import { EditorTheme, WebinyTheme, findTypographyStyleByHtmlTag } from "@webiny/lexical-theme";
import { EditorTheme, findTypographyStyleByHtmlTag } from "@webiny/lexical-theme";
import { addClassNamesToElement, removeClassNamesFromElement } from "@lexical/utils";
import { ListNodeTagType } from "@lexical/list/LexicalListNode";
import { $getListDepth, wrapInListItem } from "~/utils/listNode";
Expand Down Expand Up @@ -56,7 +56,7 @@ export class ListNode extends ElementNode {
override createDOM(config: EditorConfig): HTMLElement {
const tag = this.__tag;
const dom = document.createElement(tag);
const wTheme = config.theme as WebinyTheme;
const wTheme = config.theme as EditorTheme;

if (this.__start !== 1) {
dom.setAttribute("start", String(this.__start));
Expand Down Expand Up @@ -126,7 +126,7 @@ export class ListNode extends ElementNode {
}

override updateDOM(prevNode: ListNode, dom: HTMLElement, config: EditorConfig): boolean {
const wTheme = config.theme as WebinyTheme;
const wTheme = config.theme as EditorTheme;

if (prevNode.__tag !== this.__tag) {
return true;
Expand All @@ -137,7 +137,7 @@ export class ListNode extends ElementNode {
this.setDefaultThemeListStyleByTag(this.__tag, wTheme);
}

setListThemeClassNames(dom, config.theme, this, this.__themeStyleId);
setListThemeClassNames(dom, config.theme as EditorTheme, this, this.__themeStyleId);
dom.setAttribute(TypographyStyleAttrName, this.__themeStyleId);
return false;
}
Expand All @@ -157,7 +157,7 @@ export class ListNode extends ElementNode {
/*
* Set default styleId from first style that is found in the theme that contains current ul or ol tag
*/
private setDefaultThemeListStyleByTag(tag: string, theme: WebinyTheme) {
private setDefaultThemeListStyleByTag(tag: string, theme: EditorTheme) {
if (!tag) {
return;
}
Expand All @@ -182,7 +182,7 @@ export class ListNode extends ElementNode {
return this.__tag;
}

private isStyleExistInTheme(theme: WebinyTheme): boolean {
private isStyleExistInTheme(theme: EditorTheme): boolean {
return theme?.emotionMap ? !!theme?.emotionMap[this.__themeStyleId] : false;
}
}
Expand Down
10 changes: 5 additions & 5 deletions packages/lexical-nodes/src/ParagraphNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Spread
} from "lexical";
import { EditorConfig } from "lexical";
import { WebinyTheme, ThemeEmotionMap, findTypographyStyleByHtmlTag } from "@webiny/lexical-theme";
import { EditorTheme, ThemeEmotionMap, findTypographyStyleByHtmlTag } from "@webiny/lexical-theme";
import { addClassNamesToElement } from "@lexical/utils";
import { TypographyStylesNode, ThemeStyleValue, TextNodeThemeStyles } from "~/types";

Expand Down Expand Up @@ -76,7 +76,7 @@ export class ParagraphNode
return new ParagraphNode(node.getTypographyStyleId(), node.__key);
}

private updateElementWithThemeClasses(element: HTMLElement, theme: WebinyTheme): HTMLElement {
private updateElementWithThemeClasses(element: HTMLElement, theme: EditorTheme): HTMLElement {
if (!theme?.emotionMap) {
return element;
}
Expand Down Expand Up @@ -106,20 +106,20 @@ export class ParagraphNode

override createDOM(config: EditorConfig): HTMLElement {
const element = super.createDOM(config);
return this.updateElementWithThemeClasses(element, config.theme as WebinyTheme);
return this.updateElementWithThemeClasses(element, config.theme as EditorTheme);
}

override updateDOM(prevNode: ParagraphNode, dom: HTMLElement, config: EditorConfig): boolean {
const prevTypoStyleId = prevNode.getTypographyStyleId();
const nextTypoStyleId = this.getTypographyStyleId();

if (!nextTypoStyleId) {
this.updateElementWithThemeClasses(dom, config.theme as WebinyTheme);
this.updateElementWithThemeClasses(dom, config.theme as EditorTheme);
return false;
}

if (prevTypoStyleId !== nextTypoStyleId && nextTypoStyleId) {
this.updateElementWithThemeClasses(dom, config.theme as WebinyTheme);
this.updateElementWithThemeClasses(dom, config.theme as EditorTheme);
}
// Returning false tells Lexical that this node does not need its
// DOM element replacing with a new copy from createDOM.
Expand Down
11 changes: 3 additions & 8 deletions packages/lexical-nodes/src/QuoteNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ import {
NodeKey,
Spread
} from "lexical";
import {
EditorTheme,
ThemeEmotionMap,
WebinyTheme,
findTypographyStyleByHtmlTag
} from "@webiny/lexical-theme";
import { EditorTheme, ThemeEmotionMap, findTypographyStyleByHtmlTag } from "@webiny/lexical-theme";
import { addClassNamesToElement } from "@lexical/utils";
import {
QuoteNode as BaseQuoteNode,
Expand Down Expand Up @@ -114,7 +109,7 @@ export class QuoteNode extends BaseQuoteNode implements TextNodeThemeStyles, Typ

override createDOM(config: EditorConfig): HTMLElement {
const element = super.createDOM(config);
const wTheme = config.theme as WebinyTheme;
const wTheme = config.theme as EditorTheme;
const emotionThemeMap = wTheme?.emotionMap;

if (!emotionThemeMap) {
Expand All @@ -126,7 +121,7 @@ export class QuoteNode extends BaseQuoteNode implements TextNodeThemeStyles, Typ
this.setDefaultTypography(emotionThemeMap);
}

this.addThemeStylesToHTMLElement(element, config.theme);
this.addThemeStylesToHTMLElement(element, config.theme as EditorTheme);
return element;
}

Expand Down
6 changes: 3 additions & 3 deletions packages/lexical-nodes/src/TypographyNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ export class TypographyNode extends ElementNode {
}

addStylesHTMLElement(element: HTMLElement, theme: EditorTheme): HTMLElement {
const typographyStyleValue = theme?.emotionMap
? theme?.emotionMap[this.__styleId]
const typographyStyleValue = theme.emotionMap
? theme.emotionMap[this.__styleId]
: undefined;
if (typographyStyleValue) {
this.__css = typographyStyleValue.styles;
Expand Down Expand Up @@ -121,7 +121,7 @@ export class TypographyNode extends ElementNode {
override createDOM(config: EditorConfig): HTMLElement {
const tag = this.__tag;
const element = document.createElement(tag);
return this.addStylesHTMLElement(element, config.theme);
return this.addStylesHTMLElement(element, config.theme as EditorTheme);
}

override updateDOM(): boolean {
Expand Down
24 changes: 14 additions & 10 deletions packages/lexical-theme/src/createTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ import type { EditorThemeClasses } from "lexical";
import "./theme.css";
import { ThemeEmotionMap } from "~/types";

export type WebinyTheme = {
styles?: Record<string, any>;
emotionMap?: ThemeEmotionMap;
};

export type EditorTheme = WebinyTheme & EditorThemeClasses;
export type EditorTheme = {
styles: Record<string, any>;
emotionMap: ThemeEmotionMap;
} & EditorThemeClasses;

const defaultTheme: EditorTheme = {
styles: undefined,
emotionMap: undefined,
styles: {},
emotionMap: {},
characterLimit: "WebinyLexical__characterLimit",
code: "WebinyLexical__code",
codeHighlight: {
Expand Down Expand Up @@ -99,6 +97,12 @@ const defaultTheme: EditorTheme = {
inlineImage: "inline-editor-image"
};

export const createTheme = (): EditorTheme => {
return defaultTheme;
export const createTheme = ({
// eslint-disable-next-line @typescript-eslint/no-unused-vars
styles,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
emotionMap,
...theme
}: Partial<EditorTheme>): EditorTheme => {
return { ...defaultTheme, ...theme };
};
4 changes: 2 additions & 2 deletions packages/lexical-theme/src/utils/toTypographyEmotionMap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ThemeEmotionMap, TypographyHTMLTag } from "~/types";
import { WebinyTheme } from "~/createTheme";
import { EditorTheme } from "~/createTheme";
import { css as EmotionCSS } from "emotion";

/*
Expand All @@ -8,7 +8,7 @@ import { css as EmotionCSS } from "emotion";
*/
export const toTypographyEmotionMap = (
css: typeof EmotionCSS,
theme: WebinyTheme,
theme: EditorTheme,
themeStylesTransformer?: any
): ThemeEmotionMap => {
const map: ThemeEmotionMap = {};
Expand Down

0 comments on commit a114435

Please sign in to comment.