Skip to content

Commit

Permalink
fix(app-website): decorate lexical renderers on first mount (#4452)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel910 authored Dec 18, 2024
1 parent 9b7ddad commit 4e4de20
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 41 deletions.
1 change: 1 addition & 0 deletions extensions/theme/src/layouts/pages/Static/HeaderMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 4 additions & 6 deletions packages/app-page-builder/src/render/PageBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<>
<AddButtonLinkComponent />
<AddButtonClickHandlers />
<InjectElementVariables />
<LexicalParagraphRenderer />
<LexicalHeadingRenderer />
</>
);
};
});

PageBuilder.displayName = "PageBuilder";
10 changes: 10 additions & 0 deletions packages/app-page-builder/src/render/lexicalRendererDecorators.tsx
Original file line number Diff line number Diff line change
@@ -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]]
];
Original file line number Diff line number Diff line change
@@ -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<Renderer> = Original => {
return function LexicalHeadingRenderer(props) {
const { theme } = usePageElements();
const { getInputValues } = useRenderer();
const inputs = getInputValues<typeof elementInputs>();
Expand All @@ -29,6 +31,9 @@ export const LexicalHeadingRenderer = HeadingRenderer.Component.createDecorator(
);
}

return <Original />;
return <Original {...props} />;
};
});
};

export const LexicalHeadingRenderer =
HeadingRenderer.Component.createDecorator(LexicalHeadingDecorator);
Original file line number Diff line number Diff line change
@@ -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<Renderer> = Original => {
return function LexicalParagraphRenderer(props) {
const { theme } = usePageElements();
const { getInputValues } = useRenderer();
const inputs = getInputValues<typeof elementInputs>();
Expand All @@ -29,6 +31,9 @@ export const LexicalParagraphRenderer = ParagraphRenderer.Component.createDecora
);
}

return <Original />;
return <Original {...props} />;
};
});
};

export const LexicalParagraphRenderer =
ParagraphRenderer.Component.createDecorator(LexicalParagraphDecorator);
6 changes: 4 additions & 2 deletions packages/app-website/src/Website.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -53,6 +54,7 @@ export const Website = ({ children, routes = [], providers = [], ...props }: Web
debounceRender={debounceMs}
routes={appRoutes}
providers={[PageBuilderProviderHOC, ...providers]}
decorators={[...lexicalRendererDecorators]}
>
<LinkPreload />
<PageBuilder />
Expand Down
14 changes: 11 additions & 3 deletions packages/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -53,6 +54,7 @@ export interface AppProps {
debounceRender?: number;
routes?: Array<RouteProps>;
providers?: Array<Decorator<GenericComponent<ProviderProps>>>;
decorators?: DecoratorsCollection;
children?: React.ReactNode | React.ReactNode[];
}

Expand All @@ -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<State>({
routes: routes.reduce<RoutesByPath>((acc, item) => {
return { ...acc, [item.path as string]: <Route {...item} /> };
Expand Down Expand Up @@ -129,7 +137,7 @@ export const App = ({ debounceRender = 50, routes = [], providers = [], children

return (
<AppContext.Provider value={appContext}>
<CompositionProvider>
<CompositionProvider decorators={decorators}>
{children}
<BrowserRouter>
<Providers>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
58 changes: 40 additions & 18 deletions packages/react-composition/src/Context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useCompositionScope } from "~/CompositionScope";
import {
ComposedFunction,
ComposeWith,
Decoratable,
DecoratableComponent,
DecoratableHook,
Decorator,
Expand Down Expand Up @@ -70,35 +71,56 @@ interface CompositionContext {

const CompositionContext = createContext<CompositionContext | undefined>(undefined);

export type DecoratorsTuple = [Decoratable, Decorator<any>[]];
export type DecoratorsCollection = Array<DecoratorsTuple>;

interface CompositionProviderProps {
decorators?: DecoratorsCollection;
children: React.ReactNode;
}

export const CompositionProvider = ({ children }: CompositionProviderProps) => {
const [components, setComponents] = useState<ComponentScopes>(new Map());
const composeComponents = (
components: ComponentScopes,
decorators: Array<[GenericComponent | GenericHook, Decorator<any>[]]>,
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<ComponentScopes>(() => {
return composeComponents(
new Map(),
decorators.map(tuple => {
return [tuple[0].original, tuple[1]];
})
);
});

const composeComponent = useCallback(
(
component: GenericHook | GenericComponent,
component: GenericComponent | GenericHook,
hocs: HigherOrderComponent<any, any>[],
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.
Expand Down

0 comments on commit 4e4de20

Please sign in to comment.