Jelajahi Sumber

feat: paste html rich text inside text

Vincent Chan 2 tahun lalu
induk
melakukan
e73465170a

+ 34 - 17
frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart

@@ -15,12 +15,13 @@ class HTMLConverter {
     final result = <Node>[];
     final delta = Delta();
 
-    for (final child in _document.body?.nodes.toList() ?? <html.Node>[]) {
+    final childNodes = _document.body?.nodes.toList() ?? <html.Node>[];
+    for (final child in childNodes) {
       if (child is html.Element) {
-        if (child.localName == "span") {
-          delta.insert(child.text);
-        } else if (child.localName == "strong") {
-          delta.insert(child.text, {"bold": true});
+        if (child.localName == "a" ||
+            child.localName == "span" ||
+            child.localName == "strong") {
+          _handleRichTextElement(delta, child);
         } else {
           _handleElement(result, child);
         }
@@ -59,6 +60,25 @@ class HTMLConverter {
   }
 
   _handleParagraph(List<Node> nodes, html.Element element) {
+    _handleRichText(nodes, element);
+  }
+
+  _handleRichTextElement(Delta delta, html.Element element) {
+    if (element.localName == "span") {
+      delta.insert(element.text);
+    } else if (element.localName == "a") {
+      final hyperLink = element.attributes["href"];
+      Map<String, dynamic>? attributes;
+      if (hyperLink != null) {
+        attributes = {"href": hyperLink};
+      }
+      delta.insert(element.text, attributes);
+    } else if (element.localName == "strong") {
+      delta.insert(element.text, {"bold": true});
+    }
+  }
+
+  _handleRichText(List<Node> nodes, html.Element element) {
     final image = element.querySelector("img");
     if (image != null) {
       _handleImage(nodes, image);
@@ -69,13 +89,10 @@ class HTMLConverter {
 
     for (final child in element.nodes.toList()) {
       if (child is html.Element) {
-        if (child.localName == "a") {
-          final hyperLink = child.attributes["href"];
-          Map<String, dynamic>? attributes;
-          if (hyperLink != null) {
-            attributes = {"href": hyperLink};
-          }
-          delta.insert(child.text, attributes);
+        if (child.localName == "a" ||
+            child.localName == "span" ||
+            child.localName == "strong") {
+          _handleRichTextElement(delta, element);
         } else {
           delta.insert(child.text);
         }
@@ -122,11 +139,11 @@ class HTMLConverter {
   }
 
   _handleListElement(List<Node> nodes, html.Element element) {
-    final delta = Delta();
-    delta.insert(element.text);
-    if (delta.operations.isNotEmpty) {
-      nodes.add(TextNode(
-          type: "text", attributes: {"subtype": "bullet-list"}, delta: delta));
+    final childNodes = element.nodes.toList();
+    for (final child in childNodes) {
+      if (child is html.Element) {
+        _handleRichText(nodes, child);
+      }
     }
   }
 }

+ 24 - 3
frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/copy_paste_handler.dart

@@ -10,8 +10,6 @@ _handleCopy() async {
 }
 
 _pasteHTML(EditorState editorState, String html) {
-  final converter = HTMLConverter(html);
-  final nodes = converter.toNodes();
   final selection = editorState.cursorSelection;
   if (selection == null) {
     return;
@@ -21,9 +19,31 @@ _pasteHTML(EditorState editorState, String html) {
   if (path.isEmpty) {
     return;
   }
-  path[path.length - 1]++;
+
+  final converter = HTMLConverter(html);
+  final nodes = converter.toNodes();
+
+  if (nodes.isEmpty) {
+    return;
+  } else if (nodes.length == 1) {
+    final firstNode = nodes[0];
+    final nodeAtPath = editorState.document.nodeAtPath(path)!;
+    final tb = TransactionBuilder(editorState);
+    final startOffset = selection.start.offset;
+    if (nodeAtPath.type == "text" && firstNode.type == "text") {
+      final textNodeAtPath = nodeAtPath as TextNode;
+      final firstTextNode = firstNode as TextNode;
+      tb.textEdit(textNodeAtPath,
+          () => Delta().retain(startOffset).concat(firstTextNode.delta));
+      tb.setAfterSelection(Selection.collapsed(Position(
+          path: path, offset: startOffset + firstTextNode.delta.length)));
+    }
+    tb.commit();
+    return;
+  }
 
   final tb = TransactionBuilder(editorState);
+  path[path.length - 1]++;
   tb.insertNodes(path, nodes);
   tb.commit();
 }
@@ -98,6 +118,7 @@ _handlePastePlainText(EditorState editorState, String plainText) {
     tb.insertNodes(path, nodes);
     tb.commit();
 
+    // fixme: don't set the cursor manually
     editorState.updateCursorSelection(Selection.collapsed(
         Position(path: nodes.last.path, offset: lines.last.length)));
   }