From dcb06f292c8386249c1c47729ac1da5a52e55fe2 Mon Sep 17 00:00:00 2001 From: Maarten Vleugels Date: Wed, 27 Mar 2024 16:21:28 +0100 Subject: [PATCH 01/27] feat(editor): include event focus field, change responsive, undo, redo --- .../src/app/DocumenWidgetInline.tsx | 14 ++++++ packages/core/src/events.ts | 50 ++++++++++++++++++- .../SelectionFrameController.tsx | 16 ++++++ packages/editor/src/Editor.tsx | 29 ++++++++++- 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/apps/page-builder-demo/src/app/DocumenWidgetInline.tsx b/apps/page-builder-demo/src/app/DocumenWidgetInline.tsx index 0a8cf8ab..11ee6e17 100644 --- a/apps/page-builder-demo/src/app/DocumenWidgetInline.tsx +++ b/apps/page-builder-demo/src/app/DocumenWidgetInline.tsx @@ -33,8 +33,22 @@ export const DocumentWidgetInline: React.FC<{ } } + function handleMessageParentWindow(event: MessageEvent) { + if (event.data?.type?.includes("@easyblocks-editor")) { + editorIframeNode?.contentWindow?.postMessage( + { + type: event.data.type, + payload: event.data.payload, + }, + "*" + ); + } + } + editorIframeNode?.contentWindow?.addEventListener("message", handleMessage); + window.addEventListener("message", handleMessageParentWindow); + return () => { editorIframeNode?.contentWindow?.removeEventListener( "message", diff --git a/packages/core/src/events.ts b/packages/core/src/events.ts index e52d6936..ecebf7eb 100644 --- a/packages/core/src/events.ts +++ b/packages/core/src/events.ts @@ -1,5 +1,5 @@ import { serialize } from "@easyblocks/utils"; -import { NoCodeComponentEntry, SchemaProp } from "./types"; +import { ConfigDevices, NoCodeComponentEntry, SchemaProp } from "./types"; import { Component$$$SchemaProp } from "./compiler/schema"; type EasyblocksEditorEventData< @@ -119,6 +119,50 @@ type ItemMovedEvent = MessageEvent< > >; +type ChangeResponsiveEvent = MessageEvent< + EasyblocksEditorEventData< + "@easyblocks-editor/change-responsive", + { + device: keyof ConfigDevices; + } + > +>; + +type UndoEvent = MessageEvent< + EasyblocksEditorEventData< + "@easyblocks-editor/undo", + { + type: "@easyblocks-editor/undo"; + } + > +>; +type RedoEvent = MessageEvent< + EasyblocksEditorEventData< + "@easyblocks-editor/redo", + { + type: "@easyblocks-editor/redo"; + } + > +>; + +type SetFocussedFieldEvent = MessageEvent< + EasyblocksEditorEventData< + "@easyblocks-editor/focus-field", + { + target: Array | string; + } + > +>; + +type SelectComponentEvent = MessageEvent< + EasyblocksEditorEventData< + "@easyblocks-editor/select-component", + { + target: string; + } + > +>; + function itemMoved( payload: InferShopstoryEditorEventData["payload"] ): InferShopstoryEditorEventData { @@ -145,4 +189,8 @@ export type { RichTextChangedEvent, SelectionFramePositionChangedEvent, EasyblocksEditorEventData, + ChangeResponsiveEvent, + UndoEvent, + RedoEvent, + SetFocussedFieldEvent, }; diff --git a/packages/editor/src/EditableComponentBuilder/SelectionFrameController.tsx b/packages/editor/src/EditableComponentBuilder/SelectionFrameController.tsx index ae912670..610de5dd 100644 --- a/packages/editor/src/EditableComponentBuilder/SelectionFrameController.tsx +++ b/packages/editor/src/EditableComponentBuilder/SelectionFrameController.tsx @@ -122,6 +122,22 @@ function SelectionFrameController({ }; }); + React.useEffect(() => { + const onWindowMessage = (event: MessageEvent) => { + if (event.data.type === "@easyblocks-editor/focus-field") { + if (event.data.payload.target === path) { + node?.scrollIntoView({ + behavior: "smooth", + }); + } + } + }; + window.parent.addEventListener("message", onWindowMessage); + return () => { + window.parent.removeEventListener("message", onWindowMessage); + }; + }); + return (
{ function handleEditorEvents( - event: ComponentPickerOpenedEvent | ItemInsertedEvent | ItemMovedEvent + event: + | ComponentPickerOpenedEvent + | ItemInsertedEvent + | ItemMovedEvent + | ChangeResponsiveEvent + | SetFocussedFieldEvent + | UndoEvent + | RedoEvent ) { + if (event.data.type === "@easyblocks-editor/focus-field") { + handleSetFocussedField(event.data.payload.target); + } + + if (event.data.type === "@easyblocks-editor/change-responsive") { + setCurrentViewport(event.data.payload.device); + } + + if (event.data.type === "@easyblocks-editor/undo") { + undo(); + } + + if (event.data.type === "@easyblocks-editor/redo") { + redo(); + } + if (event.data.type === "@easyblocks-editor/component-picker-opened") { actions .openComponentPicker({ path: event.data.payload.path }) From 7ceab77ef3ab37d8a6cba13c9524ab2f32609531 Mon Sep 17 00:00:00 2001 From: Maarten Vleugels Date: Mon, 15 Apr 2024 15:57:17 +0200 Subject: [PATCH 02/27] feature(canvas): remove editor context from cavas --- packages/core/src/_internals.ts | 5 ++ packages/core/src/compiler/types.ts | 2 +- .../components/EasyblocksCanvasProvider.tsx | 84 +++++++++++++++++++ packages/editor/src/CanvasRoot/CanvasRoot.tsx | 19 +++-- packages/editor/src/EasyblocksEditor.tsx | 5 +- .../BlockControls.tsx | 20 +++-- .../SelectionFrameController.tsx | 21 ++++- packages/editor/src/Editor.tsx | 26 ++++++ packages/editor/src/EditorChildWindow.tsx | 71 +++++++++------- packages/editor/src/Placeholder.tsx | 11 ++- 10 files changed, 210 insertions(+), 54 deletions(-) create mode 100644 packages/core/src/components/EasyblocksCanvasProvider.tsx diff --git a/packages/core/src/_internals.ts b/packages/core/src/_internals.ts index 45bc630f..3973a621 100644 --- a/packages/core/src/_internals.ts +++ b/packages/core/src/_internals.ts @@ -27,6 +27,7 @@ export type { EditorContextType, InternalComponentDefinition, InternalRenderableComponentDefinition, + EditorActions, } from "./compiler/types"; export { EasyblocksMetadataProvider, @@ -39,3 +40,7 @@ export { } from "./components/ComponentBuilder/ComponentBuilder"; export { createTestCompilationContext, createFormMock } from "./testUtils"; export * from "./compiler/builtins/$richText/builders"; +export { + EasyblocksCanvasProvider, + useEasyblocksCanvasContext, +} from "./components/EasyblocksCanvasProvider"; diff --git a/packages/core/src/compiler/types.ts b/packages/core/src/compiler/types.ts index eb570050..735cb4b7 100644 --- a/packages/core/src/compiler/types.ts +++ b/packages/core/src/compiler/types.ts @@ -134,7 +134,7 @@ export type InternalComponentDefinitions = { components: InternalRenderableComponentDefinition[]; }; -type EditorActions = { +export type EditorActions = { notify: (message: string) => void; openComponentPicker: (config: { path: string; diff --git a/packages/core/src/components/EasyblocksCanvasProvider.tsx b/packages/core/src/components/EasyblocksCanvasProvider.tsx new file mode 100644 index 00000000..aa14dcc9 --- /dev/null +++ b/packages/core/src/components/EasyblocksCanvasProvider.tsx @@ -0,0 +1,84 @@ +import React, { + createContext, + ReactNode, + useContext, + useState, + useEffect, +} from "react"; +import { EditorContextType } from "../_internals"; +import { + CompilationMetadata, + CompiledShopstoryComponentConfig, + FetchOutputResources, +} from "../types"; + +type EasyblocksCanvasState = { + meta: CompilationMetadata | null; + compiled: CompiledShopstoryComponentConfig | null; + externalData: FetchOutputResources | null; + formValues: EditorContextType["form"]["values"] | null; + definitions: EditorContextType["definitions"] | null; + locale: EditorContextType["contextParams"]["locale"] | null; + locales: EditorContextType["locales"] | null; + isEditing: EditorContextType["isEditing"]; + devices: EditorContextType["devices"] | null; + focussedField: EditorContextType["focussedField"] | null; +}; + +const initialState: EasyblocksCanvasState = { + meta: null, + compiled: null, + externalData: null, + formValues: null, + definitions: null, + locale: null, + locales: null, + isEditing: false, + devices: null, + focussedField: null, +}; + +const EasyblocksCanvasContext = createContext< + EasyblocksCanvasState | undefined +>(undefined); + +type EasyblocksCanvasProviderProps = { + children: ReactNode; +}; + +const EasyblocksCanvasProvider: React.FC = ({ + children, +}) => { + const [state, setState] = useState(initialState); + + useEffect(() => { + const handler = (event: any) => { + if (event.data.type === "@easyblocks/canvas-data") { + const data = JSON.parse(event.data.data); + setState((prevState) => ({ ...prevState, ...data })); + } + }; + window.addEventListener("message", handler); + return () => { + window.removeEventListener("message", handler); + }; + }, []); + + return ( + + {children} + + ); +}; + +const useEasyblocksCanvasContext = () => { + const context = useContext(EasyblocksCanvasContext); + if (!context) { + throw new Error( + "useEasyblocksCanvasContext must be used within a EasyblocksCanvasProvider" + ); + } + return context; +}; + +export { EasyblocksCanvasProvider, useEasyblocksCanvasContext }; diff --git a/packages/editor/src/CanvasRoot/CanvasRoot.tsx b/packages/editor/src/CanvasRoot/CanvasRoot.tsx index fc0f0b83..a25e1f88 100644 --- a/packages/editor/src/CanvasRoot/CanvasRoot.tsx +++ b/packages/editor/src/CanvasRoot/CanvasRoot.tsx @@ -1,24 +1,31 @@ import React, { ReactNode } from "react"; import { useEditorGlobalKeyboardShortcuts } from "../useEditorGlobalKeyboardShortcuts"; +import { useEasyblocksCanvasContext } from "@easyblocks/core/_internals"; type CanvasRootProps = { children: ReactNode; }; function CanvasRoot(props: CanvasRootProps) { - const { editorContext } = window.parent.editorWindowAPI; + // const { editorContext } = window.parent.editorWindowAPI; + const { isEditing } = useEasyblocksCanvasContext(); - useEditorGlobalKeyboardShortcuts(editorContext); + // useEditorGlobalKeyboardShortcuts({editorContext}); return (
{ - if (editorContext.isEditing) { - editorContext.setFocussedField([]); + if (isEditing) { + window.parent.postMessage({ + type: "@easyblocks-editor/focus-field", + payload: { + target: [], + }, + }); } }} > - {editorContext.isEditing && ( + {isEditing && (