From f0b370cb5072a68147e8496de1f123721c63dba5 Mon Sep 17 00:00:00 2001 From: Oleg Isonen Date: Thu, 30 Jun 2022 21:57:58 +0200 Subject: [PATCH] serializing/deserializing content editable --- app/canvas/canvas.tsx | 22 +++++++++---------- .../wrapper-component/wrapper-component.tsx | 3 ++- .../text-toolbar/text-toolbar.tsx | 10 +++++++-- app/shared/text-editor/lexical.ts | 1 + .../text-editor/utils/to-lexical-nodes.tsx | 21 +++++++++++++----- .../text-editor/utils/to-updates.test.ts | 12 +++++++++- app/shared/text-editor/utils/to-updates.ts | 13 +++++------ 7 files changed, 55 insertions(+), 27 deletions(-) diff --git a/app/canvas/canvas.tsx b/app/canvas/canvas.tsx index 326db5f666..9af0639327 100644 --- a/app/canvas/canvas.tsx +++ b/app/canvas/canvas.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { DndProvider } from "react-dnd"; import { TouchBackend } from "react-dnd-touch-backend"; import store from "immerhin"; @@ -58,25 +58,25 @@ const useElementsTree = () => { const [rootInstance] = useRootInstance(); const [breakpoints] = useBreakpoints(); + const onChangeChildren: OnChangeChildren = useCallback((change) => { + store.createTransaction([rootInstanceContainer], (rootInstance) => { + if (rootInstance === undefined) return; + + const { instanceId, updates } = change; + setInstanceChildrenMutable(instanceId, updates, rootInstance); + }); + }, []); + return useMemo(() => { if (rootInstance === undefined) return; - const onChangeChildren: OnChangeChildren = (change) => { - store.createTransaction([rootInstanceContainer], (rootInstance) => { - if (rootInstance === undefined) return; - - const { instanceId, updates } = change; - setInstanceChildrenMutable(instanceId, updates, rootInstance); - }); - }; - return createElementsTree({ instance: rootInstance, breakpoints, Component: WrapperComponentDev, onChangeChildren, }); - }, [rootInstance, breakpoints]); + }, [rootInstance, breakpoints, onChangeChildren]); }; const useSubscribePreviewMode = () => { diff --git a/app/canvas/features/wrapper-component/wrapper-component.tsx b/app/canvas/features/wrapper-component/wrapper-component.tsx index f16204c7bc..d2066a28c4 100644 --- a/app/canvas/features/wrapper-component/wrapper-component.tsx +++ b/app/canvas/features/wrapper-component/wrapper-component.tsx @@ -1,4 +1,4 @@ -import { useCallback, MouseEvent, FormEvent, useRef, useMemo } from "react"; +import { useCallback, MouseEvent, FormEvent, useMemo } from "react"; import type { OnChangeChildren } from "~/shared/tree-utils"; import { type Instance, @@ -96,6 +96,7 @@ export const WrapperComponentDev = ({ {instance.children} ); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [isEditing]); if (editor) { diff --git a/app/designer/features/workspace/canvas-tools/text-toolbar/text-toolbar.tsx b/app/designer/features/workspace/canvas-tools/text-toolbar/text-toolbar.tsx index d1e4689de9..7103873931 100644 --- a/app/designer/features/workspace/canvas-tools/text-toolbar/text-toolbar.tsx +++ b/app/designer/features/workspace/canvas-tools/text-toolbar/text-toolbar.tsx @@ -1,6 +1,9 @@ import { type Publish } from "@webstudio-is/sdk"; import { useMemo, useState, type MouseEventHandler } from "react"; -import { useSelectionRect } from "~/designer/shared/nano-states"; +import { + useSelectedInstanceData, + useSelectionRect, +} from "~/designer/shared/nano-states"; import { ToggleGroup, type CSS } from "~/shared/design-system"; import { FontBoldIcon, FontItalicIcon, Link2Icon } from "~/shared/icons"; import { createInstance } from "~/shared/tree-utils"; @@ -85,6 +88,7 @@ type TextToolbarProps = { export const TextToolbar = ({ publish }: TextToolbarProps) => { const [selectionRect] = useSelectionRect(); + const [selectedIntsanceData] = useSelectedInstanceData(); const [element, setElement] = useState(null); const placement = useMemo(() => { if (selectionRect === undefined || element === null) return; @@ -92,7 +96,9 @@ export const TextToolbar = ({ publish }: TextToolbarProps) => { return getPlacement({ toolbarRect, selectionRect }); }, [selectionRect, element]); - if (selectionRect === undefined) return null; + if (selectionRect === undefined || selectedIntsanceData === undefined) { + return null; + } return ( { - const nodes = []; + const nodes: Array = []; + + let p = $createParagraphNode(); + nodes.push(p); for (const child of children) { + if (child === "\n") { + p = $createParagraphNode(); + nodes.push(p); + continue; + } + if (typeof child === "string") { - const paragraph = $createParagraphNode(); const textNode = $createTextNode(child); - paragraph.append(textNode); - nodes.push(paragraph); + p.append(textNode); continue; } diff --git a/app/shared/text-editor/utils/to-updates.test.ts b/app/shared/text-editor/utils/to-updates.test.ts index fe07439ca5..5c1850d8ec 100644 --- a/app/shared/text-editor/utils/to-updates.test.ts +++ b/app/shared/text-editor/utils/to-updates.test.ts @@ -50,7 +50,7 @@ describe("toUpdates", () => { }, } as const; const updates = toUpdates(state.root); - expect(updates).toStrictEqual(["a\nb"]); + expect(updates).toStrictEqual(["a", "\n", "b"]); }); test("text and instance", () => { @@ -83,6 +83,15 @@ describe("toUpdates", () => { children: [], }, }, + { + detail: 0, + format: 0, + mode: "normal", + style: "", + text: "c", + type: "text", + version: 1, + }, ], direction: "ltr", format: "", @@ -106,6 +115,7 @@ describe("toUpdates", () => { id: "62bcc02160a439686c7eabde", text: "b", }, + "c", ]); }); }); diff --git a/app/shared/text-editor/utils/to-updates.ts b/app/shared/text-editor/utils/to-updates.ts index 53fb2fea65..5d9f40a5d2 100644 --- a/app/shared/text-editor/utils/to-updates.ts +++ b/app/shared/text-editor/utils/to-updates.ts @@ -19,13 +19,7 @@ export const toUpdates = ( updates: ChildrenUpdates = [] ): ChildrenUpdates => { if (node.type === "text" && "text" in node) { - const last = updates[updates.length - 1]; - if (typeof last === "string") { - // Every new Paragraph node is basically just a new line. - updates[updates.length - 1] = `${last}\n${node.text}`; - } else { - updates.push(node.text); - } + updates.push(node.text); } if (node.type === "instance" && "instance" in node) { @@ -46,8 +40,13 @@ export const toUpdates = ( if ("children" in node) { for (const child of node.children) { + if (child.type === "paragraph" && updates.length !== 0) { + updates.push("\n"); + } + toUpdates(child, updates); } } + return updates; };