From ad964ac22b01022d73aea8b074fa957cec24844b Mon Sep 17 00:00:00 2001 From: Pavel Denisjuk Date: Tue, 17 Dec 2024 14:39:34 +0100 Subject: [PATCH] fix(app-website): decorate lexical renderers on first mount --- .../src/layouts/pages/Static/HeaderMobile.tsx | 1 + .../src/render/PageBuilder.tsx | 10 ++-- .../src/render/lexicalRendererDecorators.tsx | 10 ++++ .../elements/heading/LexicalHeading.tsx | 16 +++-- .../elements/paragraph/LexicalParagraph.tsx | 17 ++++-- packages/app-website/src/Website.tsx | 6 +- packages/app/src/App.tsx | 14 ++++- .../src/layouts/pages/Static/HeaderMobile.tsx | 1 + packages/react-composition/src/Context.tsx | 58 +++++++++++++------ 9 files changed, 92 insertions(+), 41 deletions(-) create mode 100644 packages/app-page-builder/src/render/lexicalRendererDecorators.tsx diff --git a/extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx b/extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx index e31b913cd0d..ad09d180b06 100644 --- a/extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx +++ b/extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx @@ -98,6 +98,7 @@ const HeaderMobileWrapper = styled.div` } > nav { + display: none; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; animation: slide-out 0.5s forwards; diff --git a/packages/app-page-builder/src/render/PageBuilder.tsx b/packages/app-page-builder/src/render/PageBuilder.tsx index 8dbe63ff019..984ca63afa0 100644 --- a/packages/app-page-builder/src/render/PageBuilder.tsx +++ b/packages/app-page-builder/src/render/PageBuilder.tsx @@ -2,17 +2,15 @@ import React from "react"; import { AddButtonClickHandlers } from "~/elementDecorators/AddButtonClickHandlers"; import { AddButtonLinkComponent } from "~/elementDecorators/AddButtonLinkComponent"; import { InjectElementVariables } from "~/render/variables/InjectElementVariables"; -import { LexicalParagraphRenderer } from "~/render/plugins/elements/paragraph/LexicalParagraph"; -import { LexicalHeadingRenderer } from "~/render/plugins/elements/heading/LexicalHeading"; -export const PageBuilder = () => { +export const PageBuilder = React.memo(() => { return ( <> - - ); -}; +}); + +PageBuilder.displayName = "PageBuilder"; diff --git a/packages/app-page-builder/src/render/lexicalRendererDecorators.tsx b/packages/app-page-builder/src/render/lexicalRendererDecorators.tsx new file mode 100644 index 00000000000..65bc981a453 --- /dev/null +++ b/packages/app-page-builder/src/render/lexicalRendererDecorators.tsx @@ -0,0 +1,10 @@ +import type { DecoratorsCollection } from "@webiny/app"; +import { ParagraphRenderer } from "@webiny/app-page-builder-elements/renderers/paragraph"; +import { HeadingRenderer } from "@webiny/app-page-builder-elements/renderers/heading"; +import { LexicalParagraphDecorator } from "~/render/plugins/elements/paragraph/LexicalParagraph"; +import { LexicalHeadingDecorator } from "~/render/plugins/elements/heading/LexicalHeading"; + +export const lexicalRendererDecorators: DecoratorsCollection = [ + [ParagraphRenderer.Component, [LexicalParagraphDecorator]], + [HeadingRenderer.Component, [LexicalHeadingDecorator]] +]; diff --git a/packages/app-page-builder/src/render/plugins/elements/heading/LexicalHeading.tsx b/packages/app-page-builder/src/render/plugins/elements/heading/LexicalHeading.tsx index 284fea7effd..c7acde5b85e 100644 --- a/packages/app-page-builder/src/render/plugins/elements/heading/LexicalHeading.tsx +++ b/packages/app-page-builder/src/render/plugins/elements/heading/LexicalHeading.tsx @@ -1,14 +1,16 @@ import React from "react"; import { - HeadingRenderer, - elementInputs + elementInputs, + HeadingRenderer } from "@webiny/app-page-builder-elements/renderers/heading"; import { usePageElements, useRenderer } from "@webiny/app-page-builder-elements"; import { assignStyles } from "@webiny/app-page-builder-elements/utils"; import { isValidLexicalData, LexicalHtmlRenderer } from "@webiny/lexical-editor"; +import type {ComponentDecorator} from "@webiny/app"; +import type { Renderer } from "@webiny/app-page-builder-elements/types"; -export const LexicalHeadingRenderer = HeadingRenderer.Component.createDecorator(Original => { - return function LexicalHeadingRenderer() { +export const LexicalHeadingDecorator: ComponentDecorator = Original => { + return function LexicalHeadingRenderer(props) { const { theme } = usePageElements(); const { getInputValues } = useRenderer(); const inputs = getInputValues(); @@ -29,6 +31,8 @@ export const LexicalHeadingRenderer = HeadingRenderer.Component.createDecorator( ); } - return ; + return ; }; -}); +}; + +export const LexicalHeadingRenderer = HeadingRenderer.Component.createDecorator(LexicalHeadingDecorator); diff --git a/packages/app-page-builder/src/render/plugins/elements/paragraph/LexicalParagraph.tsx b/packages/app-page-builder/src/render/plugins/elements/paragraph/LexicalParagraph.tsx index fb6bce057d9..357fecc0132 100644 --- a/packages/app-page-builder/src/render/plugins/elements/paragraph/LexicalParagraph.tsx +++ b/packages/app-page-builder/src/render/plugins/elements/paragraph/LexicalParagraph.tsx @@ -1,14 +1,16 @@ import React from "react"; import { - ParagraphRenderer, - elementInputs + elementInputs, + ParagraphRenderer } from "@webiny/app-page-builder-elements/renderers/paragraph"; import { usePageElements, useRenderer } from "@webiny/app-page-builder-elements"; import { assignStyles } from "@webiny/app-page-builder-elements/utils"; import { isValidLexicalData, LexicalHtmlRenderer } from "@webiny/lexical-editor"; +import type { ComponentDecorator } from "@webiny/app"; +import type { Renderer } from "@webiny/app-page-builder-elements/types"; -export const LexicalParagraphRenderer = ParagraphRenderer.Component.createDecorator(Original => { - return function LexicalParagraphRenderer() { +export const LexicalParagraphDecorator: ComponentDecorator = Original => { + return function LexicalParagraphRenderer(props) { const { theme } = usePageElements(); const { getInputValues } = useRenderer(); const inputs = getInputValues(); @@ -29,6 +31,9 @@ export const LexicalParagraphRenderer = ParagraphRenderer.Component.createDecora ); } - return ; + return ; }; -}); +}; + +export const LexicalParagraphRenderer = + ParagraphRenderer.Component.createDecorator(LexicalParagraphDecorator); diff --git a/packages/app-website/src/Website.tsx b/packages/app-website/src/Website.tsx index 229798fdfff..27986a23d4a 100644 --- a/packages/app-website/src/Website.tsx +++ b/packages/app-website/src/Website.tsx @@ -2,12 +2,13 @@ import React, { useMemo } from "react"; import { App, AppProps, Decorator, GenericComponent } from "@webiny/app"; import { ApolloProvider } from "@apollo/react-hooks"; import { CacheProvider } from "@emotion/react"; -import { Page } from "./Page"; -import { createApolloClient, createEmotionCache } from "~/utils"; import { ThemeProvider } from "@webiny/app-theme"; import { PageBuilderProvider } from "@webiny/app-page-builder/contexts/PageBuilder"; +import { lexicalRendererDecorators } from "@webiny/app-page-builder/render/lexicalRendererDecorators"; import { PageBuilder } from "@webiny/app-page-builder/render"; import { RouteProps } from "@webiny/react-router"; +import { createApolloClient, createEmotionCache } from "~/utils"; +import { Page } from "./Page"; import { LinkPreload } from "~/LinkPreload"; import { WebsiteLoaderCache } from "~/utils/WebsiteLoaderCache"; @@ -53,6 +54,7 @@ export const Website = ({ children, routes = [], providers = [], ...props }: Web debounceRender={debounceMs} routes={appRoutes} providers={[PageBuilderProviderHOC, ...providers]} + decorators={[...lexicalRendererDecorators]} > diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 2f88dff6229..c4f4f5d5ca5 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -13,7 +13,8 @@ import { GenericComponent, compose, Decorator, - HigherOrderComponent + HigherOrderComponent, + DecoratorsCollection } from "@webiny/react-composition"; import { Routes as SortRoutes } from "./core/Routes"; import { DebounceRender } from "./core/DebounceRender"; @@ -53,6 +54,7 @@ export interface AppProps { debounceRender?: number; routes?: Array; providers?: Array>>; + decorators?: DecoratorsCollection; children?: React.ReactNode | React.ReactNode[]; } @@ -62,7 +64,13 @@ interface ProviderProps { type ComponentWithChildren = React.ComponentType<{ children?: React.ReactNode }>; -export const App = ({ debounceRender = 50, routes = [], providers = [], children }: AppProps) => { +export const App = ({ + debounceRender = 50, + routes = [], + providers = [], + decorators = [], + children +}: AppProps) => { const [state, setState] = useState({ routes: routes.reduce((acc, item) => { return { ...acc, [item.path as string]: }; @@ -129,7 +137,7 @@ export const App = ({ debounceRender = 50, routes = [], providers = [], children return ( - + {children} diff --git a/packages/cwp-template-aws/template/common/extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx b/packages/cwp-template-aws/template/common/extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx index c254e2cb9aa..990974bf370 100644 --- a/packages/cwp-template-aws/template/common/extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx +++ b/packages/cwp-template-aws/template/common/extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx @@ -98,6 +98,7 @@ const HeaderMobileWrapper = styled.div` } > nav { + display: none; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; animation: slide-out 0.5s forwards; diff --git a/packages/react-composition/src/Context.tsx b/packages/react-composition/src/Context.tsx index 27b7fffd4ea..f7c08166a48 100644 --- a/packages/react-composition/src/Context.tsx +++ b/packages/react-composition/src/Context.tsx @@ -10,6 +10,7 @@ import { useCompositionScope } from "~/CompositionScope"; import { ComposedFunction, ComposeWith, + Decoratable, DecoratableComponent, DecoratableHook, Decorator, @@ -70,35 +71,56 @@ interface CompositionContext { const CompositionContext = createContext(undefined); +export type DecoratorsTuple = [Decoratable, Decorator[]]; +export type DecoratorsCollection = Array; + interface CompositionProviderProps { + decorators?: DecoratorsCollection; children: React.ReactNode; } -export const CompositionProvider = ({ children }: CompositionProviderProps) => { - const [components, setComponents] = useState(new Map()); +const composeComponents = ( + components: ComponentScopes, + decorators: Array<[GenericComponent | GenericHook, Decorator[]]>, + scope = "*" +) => { + const scopeMap: ComposedComponents = components.get(scope) || new Map(); + for (const [component, hocs] of decorators) { + const recipe = scopeMap.get(component) || { component: null, hocs: [] }; + + const newHocs = [...(recipe.hocs || []), ...hocs] as Decorator< + GenericHook | GenericComponent + >[]; + + scopeMap.set(component, { + component: compose(...[...newHocs].reverse())(component), + hocs: newHocs + }); + + components.set(scope, scopeMap); + } + + return components; +}; + +export const CompositionProvider = ({ decorators = [], children }: CompositionProviderProps) => { + const [components, setComponents] = useState(() => { + return composeComponents( + new Map(), + decorators.map(tuple => { + return [tuple[0].original, tuple[1]]; + }) + ); + }); const composeComponent = useCallback( ( - component: GenericHook | GenericComponent, + component: GenericComponent | GenericHook, hocs: HigherOrderComponent[], scope: string | undefined = "*" ) => { setComponents(prevComponents => { - const components = new Map(prevComponents); - const scopeMap: ComposedComponents = components.get(scope) || new Map(); - const recipe = scopeMap.get(component) || { component: null, hocs: [] }; - - const newHocs = [...(recipe.hocs || []), ...hocs] as Decorator< - GenericHook | GenericComponent - >[]; - - scopeMap.set(component, { - component: compose(...[...newHocs].reverse())(component), - hocs: newHocs - }); - - components.set(scope, scopeMap); - return components; + return composeComponents(new Map(prevComponents), [[component, hocs]], scope); }); // Return a function that will remove the added HOCs.