From 5247d43dabb108e2a0aaad52e7c66873fdb569f0 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Fri, 19 Aug 2022 23:44:20 +0300 Subject: [PATCH 01/10] first working version lazy loading the editor --- apps/designer/app/canvas/canvas.tsx | 13 ++------ .../wrapper-component/text-editor/editor.tsx | 32 +++++++++++++++---- .../text-editor/example-text-editor.tsx | 2 +- .../hooks/use-content-editable.tsx | 2 +- .../wrapper-component/text-editor/index.ts | 5 +-- .../text-editor/lazy-editor.tsx | 14 ++++++++ .../text-editor/plugins/plugin-instance.tsx | 1 - .../wrapper-component/wrapper-component.tsx | 14 ++++---- packages/react-sdk/tsconfig.json | 4 +-- 9 files changed, 55 insertions(+), 32 deletions(-) create mode 100644 apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx diff --git a/apps/designer/app/canvas/canvas.tsx b/apps/designer/app/canvas/canvas.tsx index e58857dc1d..9193e3ad60 100644 --- a/apps/designer/app/canvas/canvas.tsx +++ b/apps/designer/app/canvas/canvas.tsx @@ -45,10 +45,7 @@ import { registerContainers } from "./shared/immerhin"; import { useTrackHoveredElement } from "./shared/use-track-hovered-element"; import { usePublishScrollState } from "./shared/use-publish-scroll-state"; import { useDragAndDrop } from "./shared/use-drag-drop"; -import { - LexicalComposer, - config, -} from "~/canvas/features/wrapper-component/text-editor"; + import { setInstanceChildrenMutable } from "~/shared/tree-utils"; registerContainers(); @@ -133,18 +130,14 @@ export const Canvas = ({ data }: CanvasProps): JSX.Element | null => { if (elements === undefined) return null; - const children = ( - {elements} - ); - if (isPreviewMode) { - return children; + return elements; } return ( <> - {children} + {elements} ); }; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index 13b4bee56c..f33724b0e8 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -1,9 +1,15 @@ -import { type ChildrenUpdates, type Instance } from "@webstudio-is/react-sdk"; -import { RichTextPlugin, HistoryPlugin } from "./lexical"; +import type { ChildrenUpdates, Instance } from "@webstudio-is/react-sdk"; +import { + RichTextPlugin, + HistoryPlugin, + LexicalComposer, + useLexicalComposerContext, +} from "./lexical"; import { InstancePlugin } from "./plugins/plugin-instance"; import { ToolbarConnectorPlugin } from "./plugins/plugin-toolbar-connector"; import { OnChangePlugin } from "./plugins/plugin-on-change"; -import { memo } from "react"; +import { config } from "./config"; +import { useEffect } from "react"; type EditorProps = { children?: JSX.Element; @@ -12,12 +18,19 @@ type EditorProps = { onChange: (updates: ChildrenUpdates) => void; }; -export const Editor = ({ +const Editor = ({ + element, instance, editable, children, onChange, }: EditorProps) => { + const [editor] = useLexicalComposerContext(); + + useEffect(() => { + editor.setRootElement(element); + }, [element]); + return ( <> @@ -30,5 +43,12 @@ export const Editor = ({ ); }; -// Prevent rerender because in editing mode Editor controls the node. -export const EditorMemoized = memo(Editor, () => true); +const EditorConfigured = (props) => { + return ( + + + + ); +}; + +export default EditorConfigured; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx index 38c55084f3..0e4fe8e079 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx @@ -9,7 +9,7 @@ import { Box, Button } from "@webstudio-is/design-system"; import { LexicalComposer, ContentEditable } from "./lexical"; import { TreeViewPlugin } from "./plugins/tree-view-plugin"; import { config } from "./config"; -import { Editor } from "./editor"; +import Editor from "./editor"; type ExampleTextEditorProps = { onChange: (state: ChildrenUpdates) => void; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/hooks/use-content-editable.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/hooks/use-content-editable.tsx index 1c5a59704c..3edd0dae17 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/hooks/use-content-editable.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/hooks/use-content-editable.tsx @@ -1,4 +1,4 @@ -import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; +import { useLexicalComposerContext } from "../lexical"; import { useCallback, useEffect } from "react"; type RefCallback = (rootElement: null | HTMLElement) => void; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts b/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts index 03a81bb600..d442c978f0 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts @@ -1,4 +1 @@ -export { LexicalComposer } from "./lexical"; -export { config } from "./config"; -export { useContentEditable } from "./hooks/use-content-editable"; -export { EditorMemoized } from "./editor"; +export { Editor } from "./lazy-editor"; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx new file mode 100644 index 0000000000..655c6ff1fc --- /dev/null +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx @@ -0,0 +1,14 @@ +import { memo, lazy, Suspense } from "react"; + +const EditorLazy = lazy(() => import("./editor.tsx")); + +const EditorWithSuspense = (props) => { + return ( + Loading...}> + + + ); +}; + +// Prevent rerender because in editing mode Editor controls the node. +export const Editor = memo(EditorWithSuspense, () => true); diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx index 9b3857f056..3fea264882 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx @@ -27,7 +27,6 @@ const INSERT_INSTANCE_COMMAND: LexicalCommand = createCommand(); type InstancePluginProps = { children: Instance["children"]; }; - export const InstancePlugin = ({ children }: InstancePluginProps) => { const [editor] = useLexicalComposerContext(); useEffect(() => { diff --git a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx index 9e50e27eff..9a16adb378 100644 --- a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx @@ -1,4 +1,4 @@ -import { useCallback, MouseEvent, FormEvent, useMemo } from "react"; +import { useCallback, MouseEvent, FormEvent, useMemo, useRef } from "react"; import { type Instance, type CSS, @@ -11,7 +11,7 @@ import { import { useBreakpoints, useTextEditingInstanceId } from "~/shared/nano-states"; import { useCss } from "./use-css"; import { useEnsureFocus } from "./use-ensure-focus"; -import { EditorMemoized, useContentEditable } from "./text-editor"; +import { Editor } from "./text-editor"; import noop from "lodash.noop"; import { useSelectedElement } from "~/canvas/shared/nano-states"; @@ -31,24 +31,23 @@ export const WrapperComponentDev = ({ }: WrapperComponentDevProps) => { const className = useCss({ instance, css }); const [editingInstanceId] = useTextEditingInstanceId(); - const [, setSelectedElement] = useSelectedElement(); + const [element, setSelectedElement] = useSelectedElement(); const isEditing = editingInstanceId === instance.id; - const [editableRefCallback, editableProps] = useContentEditable(isEditing); const focusRefCallback = useEnsureFocus(); const refCallback = useCallback( (element) => { - if (isEditing) editableRefCallback(element); focusRefCallback(element); // When entering text editing we unmount the instance element, so we need to update the reference, otherwise we have a detached element referenced and bounding box will be wrong. if (element !== null) setSelectedElement(element); }, - [focusRefCallback, editableRefCallback, setSelectedElement, isEditing] + [focusRefCallback, setSelectedElement, isEditing] ); const userProps = useUserProps(instance.id); const readonlyProps = instance.component === "Input" ? { readOnly: true } : undefined; + const editableProps = isEditing ? { contentEditable: true } : undefined; const { Component } = components[instance.component]; @@ -78,7 +77,8 @@ export const WrapperComponentDev = ({ if (isEditing) { return ( - } onChange={(updates) => { diff --git a/packages/react-sdk/tsconfig.json b/packages/react-sdk/tsconfig.json index e599e806b6..d494fe77bc 100644 --- a/packages/react-sdk/tsconfig.json +++ b/packages/react-sdk/tsconfig.json @@ -3,10 +3,10 @@ "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts", "./src/**/*.tsx"], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2019"], + "lib": ["DOM", "DOM.Iterable", "es2020"], "module": "commonjs", "jsx": "react-jsx", - "target": "ES2019", + "target": "es2020", "strict": true, "baseUrl": ".", "paths": { From 93e4c8c26e96f5ab4076aa41224279d4e4400a33 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 20 Aug 2022 01:12:31 +0300 Subject: [PATCH 02/10] without passing element --- .../wrapper-component/text-editor/editor.tsx | 15 +++++---- .../hooks/use-content-editable.tsx | 31 ------------------- .../wrapper-component/wrapper-component.tsx | 5 ++- 3 files changed, 9 insertions(+), 42 deletions(-) delete mode 100644 apps/designer/app/canvas/features/wrapper-component/text-editor/hooks/use-content-editable.tsx diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index f33724b0e8..f9318fa648 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -10,6 +10,7 @@ import { ToolbarConnectorPlugin } from "./plugins/plugin-toolbar-connector"; import { OnChangePlugin } from "./plugins/plugin-on-change"; import { config } from "./config"; import { useEffect } from "react"; +import { useSelectedElement } from "~/canvas/shared/nano-states"; type EditorProps = { children?: JSX.Element; @@ -18,17 +19,15 @@ type EditorProps = { onChange: (updates: ChildrenUpdates) => void; }; -const Editor = ({ - element, - instance, - editable, - children, - onChange, -}: EditorProps) => { +const Editor = ({ instance, editable, children, onChange }: EditorProps) => { const [editor] = useLexicalComposerContext(); + const [element] = useSelectedElement(); useEffect(() => { - editor.setRootElement(element); + if (element === undefined) editor.setRootElement(element); + return () => { + editor.setRootElement(null); + }; }, [element]); return ( diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/hooks/use-content-editable.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/hooks/use-content-editable.tsx deleted file mode 100644 index 3edd0dae17..0000000000 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/hooks/use-content-editable.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useLexicalComposerContext } from "../lexical"; -import { useCallback, useEffect } from "react"; - -type RefCallback = (rootElement: null | HTMLElement) => void; - -const props = { - //autoCapitalize: false, - //autoComplete: false, - contentEditable: true, -}; - -export const useContentEditable = ( - isEditing: boolean -): [RefCallback, typeof props | undefined] => { - const [editor] = useLexicalComposerContext(); - - const ref: RefCallback = useCallback( - (rootElement) => { - editor.setRootElement(rootElement); - }, - [editor] - ); - - useEffect(() => { - if (isEditing === true) return; - // Lets unset the root element when we are not editing - editor.setRootElement(null); - }, [editor, isEditing]); - - return [ref, isEditing ? props : undefined]; -}; diff --git a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx index 9a16adb378..e4921062a0 100644 --- a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx @@ -1,4 +1,4 @@ -import { useCallback, MouseEvent, FormEvent, useMemo, useRef } from "react"; +import { useCallback, MouseEvent, FormEvent, useMemo } from "react"; import { type Instance, type CSS, @@ -31,7 +31,7 @@ export const WrapperComponentDev = ({ }: WrapperComponentDevProps) => { const className = useCss({ instance, css }); const [editingInstanceId] = useTextEditingInstanceId(); - const [element, setSelectedElement] = useSelectedElement(); + const [, setSelectedElement] = useSelectedElement(); const isEditing = editingInstanceId === instance.id; const focusRefCallback = useEnsureFocus(); @@ -78,7 +78,6 @@ export const WrapperComponentDev = ({ if (isEditing) { return ( } onChange={(updates) => { From 21975906ffb1d12c4e7d8a4bba0db6dc53220a66 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 20 Aug 2022 01:15:13 +0300 Subject: [PATCH 03/10] unused children --- .../canvas/features/wrapper-component/text-editor/editor.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index f9318fa648..d917271880 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -13,13 +13,12 @@ import { useEffect } from "react"; import { useSelectedElement } from "~/canvas/shared/nano-states"; type EditorProps = { - children?: JSX.Element; editable: JSX.Element; instance: Instance; onChange: (updates: ChildrenUpdates) => void; }; -const Editor = ({ instance, editable, children, onChange }: EditorProps) => { +const Editor = ({ instance, editable, onChange }: EditorProps) => { const [editor] = useLexicalComposerContext(); const [element] = useSelectedElement(); @@ -37,7 +36,6 @@ const Editor = ({ instance, editable, children, onChange }: EditorProps) => { {instance.children} - {children} ); }; From 085f7836ff6445b50b6eaaf7c8b31a538cd44e38 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 20 Aug 2022 08:47:51 +0300 Subject: [PATCH 04/10] fix setRootElement --- .../canvas/features/wrapper-component/text-editor/editor.tsx | 3 ++- .../features/wrapper-component/text-editor/lazy-editor.tsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index d917271880..6f630ab5f9 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -23,7 +23,8 @@ const Editor = ({ instance, editable, onChange }: EditorProps) => { const [element] = useSelectedElement(); useEffect(() => { - if (element === undefined) editor.setRootElement(element); + if (element === undefined) return; + editor.setRootElement(element); return () => { editor.setRootElement(null); }; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx index 655c6ff1fc..6d4ef29815 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx @@ -1,6 +1,6 @@ import { memo, lazy, Suspense } from "react"; -const EditorLazy = lazy(() => import("./editor.tsx")); +const EditorLazy = lazy(() => import("./editor")); const EditorWithSuspense = (props) => { return ( From d99adaf329247bf5d58cfdfdd663ec458168faa7 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sat, 20 Aug 2022 09:05:50 +0300 Subject: [PATCH 05/10] fix types, enable async imports in ts --- .../features/wrapper-component/text-editor/editor.tsx | 4 ++-- .../features/wrapper-component/text-editor/lazy-editor.tsx | 7 ++++--- packages/tsconfig/remix.json | 5 +++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index 6f630ab5f9..3f1d2910cf 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -12,7 +12,7 @@ import { config } from "./config"; import { useEffect } from "react"; import { useSelectedElement } from "~/canvas/shared/nano-states"; -type EditorProps = { +export type EditorProps = { editable: JSX.Element; instance: Instance; onChange: (updates: ChildrenUpdates) => void; @@ -41,7 +41,7 @@ const Editor = ({ instance, editable, onChange }: EditorProps) => { ); }; -const EditorConfigured = (props) => { +const EditorConfigured = (props: EditorProps) => { return ( diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx index 6d4ef29815..9ddfbab3a5 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx @@ -1,10 +1,11 @@ -import { memo, lazy, Suspense } from "react"; +import { memo, lazy, Suspense, ComponentProps } from "react"; +import type { EditorProps } from "./editor"; const EditorLazy = lazy(() => import("./editor")); -const EditorWithSuspense = (props) => { +const EditorWithSuspense = (props: EditorProps) => { return ( - Loading...}> + ); diff --git a/packages/tsconfig/remix.json b/packages/tsconfig/remix.json index aa57d759c2..80cd315097 100644 --- a/packages/tsconfig/remix.json +++ b/packages/tsconfig/remix.json @@ -5,8 +5,9 @@ "compilerOptions": { "noEmit": true, "jsx": "react-jsx", - "target": "ES2019", - "lib": ["DOM", "DOM.Iterable", "ES2019"] + "target": "ES2020", + "module": "ES2020", + "lib": ["DOM", "DOM.Iterable", "ES2020"] }, "include": ["app", "remix.env.d.ts"], "exclude": ["node_modules"] From 6fce218163c5926c7a06573bc569482cb92aa7aa Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Sun, 21 Aug 2022 00:11:32 +0300 Subject: [PATCH 06/10] now works with provider inside the editor --- .../{lazy-editor.tsx => editor-lazy.tsx} | 3 ++- .../wrapper-component/text-editor/editor.tsx | 17 +++++++++-------- .../wrapper-component/text-editor/index.ts | 2 +- .../wrapper-component/wrapper-component.tsx | 17 ++++++++--------- 4 files changed, 20 insertions(+), 19 deletions(-) rename apps/designer/app/canvas/features/wrapper-component/text-editor/{lazy-editor.tsx => editor-lazy.tsx} (81%) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx similarity index 81% rename from apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx rename to apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx index 9ddfbab3a5..0e99b87e13 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/lazy-editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx @@ -1,4 +1,4 @@ -import { memo, lazy, Suspense, ComponentProps } from "react"; +import { memo, lazy, Suspense } from "react"; import type { EditorProps } from "./editor"; const EditorLazy = lazy(() => import("./editor")); @@ -13,3 +13,4 @@ const EditorWithSuspense = (props: EditorProps) => { // Prevent rerender because in editing mode Editor controls the node. export const Editor = memo(EditorWithSuspense, () => true); +//export const Editor = EditorWithSuspense; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index 3f1d2910cf..76b7ce6788 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -11,6 +11,7 @@ import { OnChangePlugin } from "./plugins/plugin-on-change"; import { config } from "./config"; import { useEffect } from "react"; import { useSelectedElement } from "~/canvas/shared/nano-states"; +import { getInstanceIdFromElement } from "~/shared/dom-utils"; export type EditorProps = { editable: JSX.Element; @@ -18,18 +19,18 @@ export type EditorProps = { onChange: (updates: ChildrenUpdates) => void; }; -const Editor = ({ instance, editable, onChange }: EditorProps) => { +const useSetElement = (instance: Instance) => { const [editor] = useLexicalComposerContext(); const [element] = useSelectedElement(); - useEffect(() => { - if (element === undefined) return; - editor.setRootElement(element); - return () => { - editor.setRootElement(null); - }; - }, [element]); + if (element && getInstanceIdFromElement(element) === instance.id) { + editor.setRootElement(element); + } + }, [element, editor, instance]); +}; +const Editor = ({ instance, editable, onChange }: EditorProps) => { + useSetElement(instance); return ( <> diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts b/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts index d442c978f0..acb2865c26 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts @@ -1 +1 @@ -export { Editor } from "./lazy-editor"; +export { Editor } from "./editor-lazy"; diff --git a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx index e4921062a0..75b573d53f 100644 --- a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx @@ -32,7 +32,6 @@ export const WrapperComponentDev = ({ const className = useCss({ instance, css }); const [editingInstanceId] = useTextEditingInstanceId(); const [, setSelectedElement] = useSelectedElement(); - const isEditing = editingInstanceId === instance.id; const focusRefCallback = useEnsureFocus(); const refCallback = useCallback( @@ -41,13 +40,12 @@ export const WrapperComponentDev = ({ // When entering text editing we unmount the instance element, so we need to update the reference, otherwise we have a detached element referenced and bounding box will be wrong. if (element !== null) setSelectedElement(element); }, - [focusRefCallback, setSelectedElement, isEditing] + [focusRefCallback, setSelectedElement] ); const userProps = useUserProps(instance.id); const readonlyProps = instance.component === "Input" ? { readOnly: true } : undefined; - const editableProps = isEditing ? { contentEditable: true } : undefined; const { Component } = components[instance.component]; @@ -55,12 +53,13 @@ export const WrapperComponentDev = ({ ...userProps, ...rest, ...readonlyProps, - ...editableProps, // @todo merge className with props - className: className, - // @todo stop using id to free it up to the user - id: instance.id, + className, tabIndex: 0, + // @todo stop using id to free it up to the user + // we should replace id, data-component and data-id with "data-ws"=instance.id and grab the rest always over the id + // for this we need to also make search by id fast + id: instance.id, "data-component": instance.component, "data-id": instance.id, ref: refCallback, @@ -75,11 +74,11 @@ export const WrapperComponentDev = ({ }, }; - if (isEditing) { + if (editingInstanceId === instance.id) { return ( } + editable={} onChange={(updates) => { onChangeChildren({ instanceId: instance.id, updates }); }} From 254637eba56ea9245c77aace8b75d5593f4f2ef6 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 22 Aug 2022 12:11:07 +0200 Subject: [PATCH 07/10] make rendering instances work again --- .../wrapper-component/text-editor/config.ts | 2 +- .../text-editor/editor-lazy.tsx | 2 +- .../wrapper-component/text-editor/editor.tsx | 19 ++++++++++--------- .../text-editor/example-text-editor.tsx | 4 ++-- .../wrapper-component/wrapper-component.tsx | 11 ++++++++++- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/config.ts b/apps/designer/app/canvas/features/wrapper-component/text-editor/config.ts index 5e50a7a73a..d1c1b9adaf 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/config.ts +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/config.ts @@ -1,7 +1,7 @@ import { InstanceNode } from "./nodes/node-instance"; export const config = { - namespace: "ComponentTextEditor", + namespace: "WsTextEditor", onError(error: Error) { throw error; }, diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx index 0e99b87e13..a208f8248e 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx @@ -5,7 +5,7 @@ const EditorLazy = lazy(() => import("./editor")); const EditorWithSuspense = (props: EditorProps) => { return ( - + ); diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index 76b7ce6788..875c03e1ed 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -9,28 +9,29 @@ import { InstancePlugin } from "./plugins/plugin-instance"; import { ToolbarConnectorPlugin } from "./plugins/plugin-toolbar-connector"; import { OnChangePlugin } from "./plugins/plugin-on-change"; import { config } from "./config"; -import { useEffect } from "react"; +import { useCallback, useEffect } from "react"; import { useSelectedElement } from "~/canvas/shared/nano-states"; import { getInstanceIdFromElement } from "~/shared/dom-utils"; export type EditorProps = { - editable: JSX.Element; + renderEditable: (ref?: (element: HTMLElement | null) => void) => JSX.Element; instance: Instance; onChange: (updates: ChildrenUpdates) => void; }; const useSetElement = (instance: Instance) => { const [editor] = useLexicalComposerContext(); - const [element] = useSelectedElement(); - useEffect(() => { - if (element && getInstanceIdFromElement(element) === instance.id) { + return useCallback( + (element) => { editor.setRootElement(element); - } - }, [element, editor, instance]); + }, + [editor] + ); }; -const Editor = ({ instance, editable, onChange }: EditorProps) => { - useSetElement(instance); +const Editor = ({ instance, renderEditable, onChange }: EditorProps) => { + const refCallback = useSetElement(instance); + const editable = renderEditable(refCallback); return ( <> diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx index 0e4fe8e079..0109ffac62 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx @@ -100,7 +100,7 @@ export const ExampleTextEditor = ({ onChange }: ExampleTextEditorProps) => { }} > ( { background: "#eee", }} /> - } + )} instance={ { component: "Paragraph", diff --git a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx index 75b573d53f..dc74b02a46 100644 --- a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx @@ -78,7 +78,16 @@ export const WrapperComponentDev = ({ return ( } + renderEditable={(ref) => ( + { + props.ref(element); + ref?.(element); + }} + contentEditable={true} + /> + )} onChange={(updates) => { onChangeChildren({ instanceId: instance.id, updates }); }} From c825776240354735eae964e3e31523f4f1429e2c Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 22 Aug 2022 12:16:01 +0200 Subject: [PATCH 08/10] lint --- .../wrapper-component/text-editor/editor-lazy.tsx | 1 - .../features/wrapper-component/text-editor/editor.tsx | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx index a208f8248e..49e6ff5fff 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx @@ -13,4 +13,3 @@ const EditorWithSuspense = (props: EditorProps) => { // Prevent rerender because in editing mode Editor controls the node. export const Editor = memo(EditorWithSuspense, () => true); -//export const Editor = EditorWithSuspense; diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index 875c03e1ed..49d2292901 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -9,9 +9,7 @@ import { InstancePlugin } from "./plugins/plugin-instance"; import { ToolbarConnectorPlugin } from "./plugins/plugin-toolbar-connector"; import { OnChangePlugin } from "./plugins/plugin-on-change"; import { config } from "./config"; -import { useCallback, useEffect } from "react"; -import { useSelectedElement } from "~/canvas/shared/nano-states"; -import { getInstanceIdFromElement } from "~/shared/dom-utils"; +import { useCallback } from "react"; export type EditorProps = { renderEditable: (ref?: (element: HTMLElement | null) => void) => JSX.Element; @@ -19,7 +17,7 @@ export type EditorProps = { onChange: (updates: ChildrenUpdates) => void; }; -const useSetElement = (instance: Instance) => { +const useSetElement = () => { const [editor] = useLexicalComposerContext(); return useCallback( (element) => { @@ -30,7 +28,7 @@ const useSetElement = (instance: Instance) => { }; const Editor = ({ instance, renderEditable, onChange }: EditorProps) => { - const refCallback = useSetElement(instance); + const refCallback = useSetElement(); const editable = renderEditable(refCallback); return ( <> From 9e1754c5f27e5ff47fb2e3110b6854f93bd29437 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 22 Aug 2022 13:15:15 +0200 Subject: [PATCH 09/10] finalizye lazy rendering api --- .../text-editor/editor-lazy.tsx | 11 ++++- .../wrapper-component/text-editor/editor.tsx | 13 ++++-- .../text-editor/example-text-editor.tsx | 2 +- .../wrapper-component/wrapper-component.tsx | 43 ++++++++++--------- 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx index 49e6ff5fff..983597113c 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx @@ -3,9 +3,16 @@ import type { EditorProps } from "./editor"; const EditorLazy = lazy(() => import("./editor")); -const EditorWithSuspense = (props: EditorProps) => { +type EditorPropsWithSuspense = EditorProps & { + fallback: JSX.Element; +}; + +const EditorWithSuspense = ({ + fallback, + ...props +}: EditorPropsWithSuspense) => { return ( - + ); diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx index 49d2292901..ec8fba195c 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx @@ -11,8 +11,13 @@ import { OnChangePlugin } from "./plugins/plugin-on-change"; import { config } from "./config"; import { useCallback } from "react"; +type InstanceRenderProps = { + ref: (element: HTMLElement | null) => void; + contentEditable: boolean; +}; + export type EditorProps = { - renderEditable: (ref?: (element: HTMLElement | null) => void) => JSX.Element; + renderInstance: (renderProps: InstanceRenderProps) => JSX.Element; instance: Instance; onChange: (updates: ChildrenUpdates) => void; }; @@ -27,9 +32,9 @@ const useSetElement = () => { ); }; -const Editor = ({ instance, renderEditable, onChange }: EditorProps) => { - const refCallback = useSetElement(); - const editable = renderEditable(refCallback); +const Editor = ({ instance, renderInstance, onChange }: EditorProps) => { + const ref = useSetElement(); + const editable = renderInstance({ ref, contentEditable: true }); return ( <> diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx index 0109ffac62..673c6d8651 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/example-text-editor.tsx @@ -100,7 +100,7 @@ export const ExampleTextEditor = ({ onChange }: ExampleTextEditorProps) => { }} > ( + renderInstance={() => ( ( - { - props.ref(element); - ref?.(element); - }} - contentEditable={true} - /> - )} - onChange={(updates) => { - onChangeChildren({ instanceId: instance.id, updates }); - }} - /> - ); + const instanceElement = ( + {renderWrapperComponentChildren(children)} + ); + + if (editingInstanceId !== instance.id) { + return instanceElement; } return ( - {renderWrapperComponentChildren(children)} + ( + { + props.ref(element); + ref(element); + }} + /> + )} + onChange={(updates) => { + onChangeChildren({ instanceId: instance.id, updates }); + }} + /> ); }; From 2389f18ef60dcf10c36a8e4e931643f973083cb1 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Mon, 22 Aug 2022 13:18:50 +0200 Subject: [PATCH 10/10] cleanup --- .../features/wrapper-component/text-editor/editor-lazy.tsx | 4 ++-- .../wrapper-component/text-editor/plugins/plugin-instance.tsx | 1 + packages/asset-uploader/tsconfig.json | 2 +- packages/design-system/tsconfig.json | 4 ++-- packages/icons/tsconfig.json | 4 ++-- packages/react-sdk/tsconfig.json | 4 ++-- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx index 983597113c..83d1fbce93 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx @@ -3,14 +3,14 @@ import type { EditorProps } from "./editor"; const EditorLazy = lazy(() => import("./editor")); -type EditorPropsWithSuspense = EditorProps & { +type EditorWithSuspenseProps = EditorProps & { fallback: JSX.Element; }; const EditorWithSuspense = ({ fallback, ...props -}: EditorPropsWithSuspense) => { +}: EditorWithSuspenseProps) => { return ( diff --git a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx index 3fea264882..9b3857f056 100644 --- a/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx +++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/plugins/plugin-instance.tsx @@ -27,6 +27,7 @@ const INSERT_INSTANCE_COMMAND: LexicalCommand = createCommand(); type InstancePluginProps = { children: Instance["children"]; }; + export const InstancePlugin = ({ children }: InstancePluginProps) => { const [editor] = useLexicalComposerContext(); useEffect(() => { diff --git a/packages/asset-uploader/tsconfig.json b/packages/asset-uploader/tsconfig.json index 654f479a30..6b67ca9bbf 100644 --- a/packages/asset-uploader/tsconfig.json +++ b/packages/asset-uploader/tsconfig.json @@ -4,7 +4,7 @@ "include": ["./src/**/*.ts"], "compilerOptions": { "module": "commonjs", - "target": "ES2019", + "target": "ES2020", "strict": true, "baseUrl": ".", "outDir": "./lib" diff --git a/packages/design-system/tsconfig.json b/packages/design-system/tsconfig.json index e599e806b6..eb77362863 100644 --- a/packages/design-system/tsconfig.json +++ b/packages/design-system/tsconfig.json @@ -3,10 +3,10 @@ "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts", "./src/**/*.tsx"], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2019"], + "lib": ["DOM", "DOM.Iterable", "ES2020"], "module": "commonjs", "jsx": "react-jsx", - "target": "ES2019", + "target": "ES2020", "strict": true, "baseUrl": ".", "paths": { diff --git a/packages/icons/tsconfig.json b/packages/icons/tsconfig.json index e599e806b6..eb77362863 100644 --- a/packages/icons/tsconfig.json +++ b/packages/icons/tsconfig.json @@ -3,10 +3,10 @@ "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts", "./src/**/*.tsx"], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2019"], + "lib": ["DOM", "DOM.Iterable", "ES2020"], "module": "commonjs", "jsx": "react-jsx", - "target": "ES2019", + "target": "ES2020", "strict": true, "baseUrl": ".", "paths": { diff --git a/packages/react-sdk/tsconfig.json b/packages/react-sdk/tsconfig.json index d494fe77bc..eb77362863 100644 --- a/packages/react-sdk/tsconfig.json +++ b/packages/react-sdk/tsconfig.json @@ -3,10 +3,10 @@ "extends": "../../node_modules/@webstudio-is/tsconfig/base.json", "include": ["./src/**/*.ts", "./src/**/*.tsx"], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "es2020"], + "lib": ["DOM", "DOM.Iterable", "ES2020"], "module": "commonjs", "jsx": "react-jsx", - "target": "es2020", + "target": "ES2020", "strict": true, "baseUrl": ".", "paths": {