瀏覽代碼

feat: copy styles of text delta

Vincent Chan 2 年之前
父節點
當前提交
c2a295d9cd
共有 1 個文件被更改,包括 84 次插入19 次删除
  1. 84 19
      frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart

+ 84 - 19
frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart

@@ -4,7 +4,7 @@ import 'package:flowy_editor/document/attributes.dart';
 import 'package:flowy_editor/document/node.dart';
 import 'package:flowy_editor/document/text_delta.dart';
 import 'package:flowy_editor/render/rich_text/rich_text_style.dart';
-import 'package:flowy_editor/service/internal_key_event_handlers/delete_text_handler.dart';
+import 'package:flutter/material.dart';
 import 'package:html/parser.dart' show parse;
 import 'package:html/dom.dart' as html;
 
@@ -22,6 +22,12 @@ const String tagStrong = "strong";
 const String tagSpan = "span";
 const String tagCode = "code";
 
+extension on Color {
+  String toRgbaString() {
+    return 'rgba($red, $green, $blue, $alpha)';
+  }
+}
+
 /// Converting the HTML to nodes
 class HTMLToNodesConverter {
   final html.Document _document;
@@ -104,29 +110,78 @@ class HTMLToNodesConverter {
     return node;
   }
 
+  Map<String, String> _cssStringToMap(String? cssString) {
+    final result = <String, String>{};
+    if (cssString == null) {
+      return result;
+    }
+
+    final entries = cssString.split(";");
+    for (final entry in entries) {
+      final tuples = entry.split(":");
+      if (tuples.length < 2) {
+        continue;
+      }
+      result[tuples[0]] = tuples[1];
+    }
+
+    return result;
+  }
+
   Attributes? _getDeltaAttributesFromHtmlAttributes(
       LinkedHashMap<Object, String> htmlAttributes) {
     final attrs = <String, dynamic>{};
     final styleString = htmlAttributes["style"];
-    if (styleString != null) {
-      final entries = styleString.split(";");
-      for (final entry in entries) {
-        final tuples = entry.split(":");
-        if (tuples.length < 2) {
-          continue;
-        }
-        if (tuples[0] == "font-weight") {
-          int? weight = int.tryParse(tuples[1]);
-          if (weight != null && weight > 500) {
-            attrs["bold"] = true;
-          }
-        }
+    final cssMap = _cssStringToMap(styleString);
+
+    final fontWeightStr = cssMap["font-weight"];
+    if (fontWeightStr != null) {
+      int? weight = int.tryParse(fontWeightStr);
+      if (weight != null && weight > 500) {
+        attrs["bold"] = true;
       }
     }
 
+    final backgroundColorStr = cssMap["background-color"];
+    final backgroundColor = _tryParseCssColorString(backgroundColorStr);
+    if (backgroundColor != null) {
+      attrs[StyleKey.backgroundColor] =
+          '0x${backgroundColor.value.toRadixString(16)}';
+    }
+
     return attrs.isEmpty ? null : attrs;
   }
 
+  Color? _tryParseCssColorString(String? colorString) {
+    if (colorString == null) {
+      return null;
+    }
+    final reg = RegExp(r'rgba\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)');
+    final match = reg.firstMatch(colorString);
+    if (match == null) {
+      return null;
+    }
+
+    if (match.groupCount < 4) {
+      return null;
+    }
+    final redStr = match.group(1);
+    final greenStr = match.group(2);
+    final blueStr = match.group(3);
+    final alphaStr = match.group(4);
+
+    final red = redStr != null ? int.tryParse(redStr) : null;
+    final green = greenStr != null ? int.tryParse(greenStr) : null;
+    final blue = blueStr != null ? int.tryParse(blueStr) : null;
+    final alpha = alphaStr != null ? int.tryParse(alphaStr) : null;
+
+    if (red == null || green == null || blue == null || alpha == null) {
+      return null;
+    }
+
+    return Color.fromARGB(alpha, red, green, blue);
+  }
+
   _handleRichTextElement(Delta delta, html.Element element) {
     if (element.localName == tagSpan) {
       delta.insert(element.text,
@@ -321,9 +376,19 @@ class NodesToHTMLConverter {
   String _attributesToCssStyle(Map<String, dynamic> attributes) {
     final cssMap = <String, String>{};
     if (attributes[StyleKey.backgroundColor] != null) {
-      cssMap["background-color"] = attributes[StyleKey.backgroundColor];
-    } else if (attributes[StyleKey.color] != null) {
-      cssMap["color"] = attributes[StyleKey.color];
+      final color = Color(
+        int.parse(attributes[StyleKey.backgroundColor]),
+      );
+      cssMap["background-color"] = color.toRgbaString();
+    }
+    if (attributes[StyleKey.color] != null) {
+      final color = Color(
+        int.parse(attributes[StyleKey.color]),
+      );
+      cssMap["color"] = color.toRgbaString();
+    }
+    if (attributes[StyleKey.bold] == true) {
+      cssMap["font-weight"] = "bold";
     }
     return _cssMapToCssStyle(cssMap);
   }
@@ -334,7 +399,7 @@ class NodesToHTMLConverter {
       if (previousValue.isEmpty) {
         return kv;
       }
-      return '$previousValue; $kv"';
+      return '$previousValue; $kv';
     });
   }
 
@@ -360,7 +425,7 @@ class NodesToHTMLConverter {
     for (final op in delta.operations) {
       if (op is TextInsert) {
         final attributes = op.attributes;
-        if (attributes != null && attributes[StyleKey.bold] == true) {
+        if (attributes != null) {
           if (attributes.length == 1 && attributes[StyleKey.bold] == true) {
             final strong = html.Element.tag(tagStrong);
             strong.append(html.Text(op.content));