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/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
new file mode 100644
index 0000000000..83d1fbce93
--- /dev/null
+++ b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor-lazy.tsx
@@ -0,0 +1,22 @@
+import { memo, lazy, Suspense } from "react";
+import type { EditorProps } from "./editor";
+
+const EditorLazy = lazy(() => import("./editor"));
+
+type EditorWithSuspenseProps = EditorProps & {
+ fallback: JSX.Element;
+};
+
+const EditorWithSuspense = ({
+ fallback,
+ ...props
+}: EditorWithSuspenseProps) => {
+ return (
+
+
+
+ );
+};
+
+// 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/editor.tsx b/apps/designer/app/canvas/features/wrapper-component/text-editor/editor.tsx
index 13b4bee56c..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
@@ -1,23 +1,40 @@
-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 { useCallback } from "react";
-type EditorProps = {
- children?: JSX.Element;
- editable: JSX.Element;
+type InstanceRenderProps = {
+ ref: (element: HTMLElement | null) => void;
+ contentEditable: boolean;
+};
+
+export type EditorProps = {
+ renderInstance: (renderProps: InstanceRenderProps) => JSX.Element;
instance: Instance;
onChange: (updates: ChildrenUpdates) => void;
};
-export const Editor = ({
- instance,
- editable,
- children,
- onChange,
-}: EditorProps) => {
+const useSetElement = () => {
+ const [editor] = useLexicalComposerContext();
+ return useCallback(
+ (element) => {
+ editor.setRootElement(element);
+ },
+ [editor]
+ );
+};
+
+const Editor = ({ instance, renderInstance, onChange }: EditorProps) => {
+ const ref = useSetElement();
+ const editable = renderInstance({ ref, contentEditable: true });
return (
<>
@@ -25,10 +42,16 @@ export const Editor = ({
{instance.children}
- {children}
>
);
};
-// Prevent rerender because in editing mode Editor controls the node.
-export const EditorMemoized = memo(Editor, () => true);
+const EditorConfigured = (props: EditorProps) => {
+ 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..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
@@ -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;
@@ -100,7 +100,7 @@ export const ExampleTextEditor = ({ onChange }: ExampleTextEditorProps) => {
}}
>
(
{
background: "#eee",
}}
/>
- }
+ )}
instance={
{
component: "Paragraph",
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 1c5a59704c..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/react/LexicalComposerContext";
-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/text-editor/index.ts b/apps/designer/app/canvas/features/wrapper-component/text-editor/index.ts
index 03a81bb600..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,4 +1 @@
-export { LexicalComposer } from "./lexical";
-export { config } from "./config";
-export { useContentEditable } from "./hooks/use-content-editable";
-export { EditorMemoized } from "./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 9e50e27eff..426ddce932 100644
--- a/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx
+++ b/apps/designer/app/canvas/features/wrapper-component/wrapper-component.tsx
@@ -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";
@@ -32,18 +32,15 @@ export const WrapperComponentDev = ({
const className = useCss({ instance, css });
const [editingInstanceId] = useTextEditingInstanceId();
const [, 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]
);
const userProps = useUserProps(instance.id);
@@ -56,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,
@@ -76,20 +74,32 @@ export const WrapperComponentDev = ({
},
};
- if (isEditing) {
- return (
- }
- 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 });
+ }}
+ />
);
};
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 e599e806b6..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", "ES2019"],
+ "lib": ["DOM", "DOM.Iterable", "ES2020"],
"module": "commonjs",
"jsx": "react-jsx",
- "target": "ES2019",
+ "target": "ES2020",
"strict": true,
"baseUrl": ".",
"paths": {
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"]