serializing/deserializing content editable

This commit is contained in:
Oleg Isonen committed 2022-06-30 21:57:58 +02:00
1 parent 5c7da8feeb
commit f0b370cb50
7 files changed
+55 -27

No files matched your search

+11 -11
View File
@@ -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 = () => {
@@ -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}
</Editor>
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEditing]);
if (editor) {
@@ -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<HTMLElement | null>(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 (
<Toolbar
+1
View File
@@ -10,6 +10,7 @@ export type {
SerializedElementNode,
SerializedTextNode,
SerializedRootNode,
ParagraphNode,
} from "lexical";
export {
ElementNode,
@@ -1,16 +1,27 @@
import { type Instance } from "@webstudio-is/sdk";
import { $createInstanceNode } from "../nodes/node-instance";
import { $createTextNode, $createParagraphNode } from "../lexical";
import {
$createTextNode,
$createParagraphNode,
type ParagraphNode,
} from "../lexical";
export const toLexicalNodes = (children: Instance["children"]) => {
const nodes = [];
const nodes: Array<ParagraphNode> = [];
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;
}
@@ -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",
]);
});
});
+6 -7
View File
@@ -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;
};