diff --git a/lib/src/plugins/html/encoder/delta_html_encoder.dart b/lib/src/plugins/html/encoder/delta_html_encoder.dart index abd189edd..21c0da665 100644 --- a/lib/src/plugins/html/encoder/delta_html_encoder.dart +++ b/lib/src/plugins/html/encoder/delta_html_encoder.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:appflowy_editor/appflowy_editor.dart'; -import 'package:flutter/material.dart'; import 'package:html/dom.dart' as dom; /// A [Delta] encoder that encodes a [Delta] to html. @@ -10,110 +9,97 @@ import 'package:html/dom.dart' as dom; class DeltaHtmlEncoder extends Converter> { @override List convert(Delta input) { - final childNodes = []; - - for (final op in input) { - if (op is TextInsert) { - final attributes = op.attributes; - - if (attributes != null) { - if (attributes.length == 1) { - final element = _applyAttributes(attributes, text: op.text); - childNodes.add(element); - } else { - final span = dom.Element.tag(HTMLTags.span); - final cssString = _attributesToCssStyle(attributes); - if (cssString.isNotEmpty) { - span.attributes['style'] = cssString; - } - span.append(dom.Text(op.text)); - childNodes.add(span); - } - } else { - childNodes.add(dom.Text(op.text)); - } - } - } - return childNodes; + return input + .whereType() + .map(convertTextInsertToDomNode) + .toList(); } - dom.Element _applyAttributes(Attributes attributes, {required String text}) { - if (attributes[AppFlowyRichTextKeys.bold] == true) { - final strong = dom.Element.tag(HTMLTags.strong); - strong.append(dom.Text(text)); - return strong; - } else if (attributes[AppFlowyRichTextKeys.underline] == true) { - final underline = dom.Element.tag(HTMLTags.underline); - underline.append(dom.Text(text)); - return underline; - } else if (attributes[AppFlowyRichTextKeys.italic] == true) { - final italic = dom.Element.tag(HTMLTags.italic); - italic.append(dom.Text(text)); - return italic; - } else if (attributes[AppFlowyRichTextKeys.strikethrough] == true) { - final del = dom.Element.tag(HTMLTags.del); - del.append(dom.Text(text)); - return del; - } else if (attributes[AppFlowyRichTextKeys.code] == true) { - final code = dom.Element.tag(HTMLTags.code); - code.append(dom.Text(text)); - return code; - } else if (attributes[AppFlowyRichTextKeys.href] != null) { - final anchor = dom.Element.tag(HTMLTags.anchor); - anchor.attributes['href'] = attributes[AppFlowyRichTextKeys.href]; - anchor.append(dom.Text(text)); - return anchor; - } else { - final paragraph = dom.Element.tag(HTMLTags.paragraph); - - paragraph.append(dom.Text(text)); - return paragraph; + dom.Node convertTextInsertToDomNode(TextInsert textInsert) { + final text = textInsert.text; + final attributes = textInsert.attributes; + + if (attributes == null) { + return dom.Text(text); + } + + // if there is only one attribute, we can use the tag directly + if (attributes.length == 1) { + return convertSingleAttributeTextInsertToDomNode(text, attributes); } + + return convertMultipleAttributeTextInsertToDomNode(text, attributes); } - String _attributesToCssStyle(Map attributes) { - final cssMap = {}; - if (attributes[BuiltInAttributeKey.highlightColor] != null) { - final color = Color( - int.tryParse(attributes[BuiltInAttributeKey.highlightColor]) ?? - 0xFFFFFFFF, - ); + dom.Element convertSingleAttributeTextInsertToDomNode( + String text, + Attributes attributes, + ) { + assert(attributes.length == 1); - cssMap['background-color'] = color.toRgbaString(); + final domText = dom.Text(text); + + // href is a special case, it should be an anchor tag + final href = attributes.href; + if (href != null) { + return dom.Element.tag(HTMLTags.anchor) + ..attributes['href'] = href + ..append(domText); } - if (attributes[BuiltInAttributeKey.textColor] != null) { - final color = Color( - int.parse(attributes[BuiltInAttributeKey.textColor]), - ); - cssMap['color'] = color.toRgbaString(); + + final keyToTag = { + AppFlowyRichTextKeys.bold: HTMLTags.strong, + AppFlowyRichTextKeys.italic: HTMLTags.italic, + AppFlowyRichTextKeys.underline: HTMLTags.underline, + AppFlowyRichTextKeys.strikethrough: HTMLTags.del, + AppFlowyRichTextKeys.code: HTMLTags.code, + null: HTMLTags.paragraph, + }; + + final tag = keyToTag[attributes.keys.first]; + return dom.Element.tag(tag)..append(domText); + } + + dom.Element convertMultipleAttributeTextInsertToDomNode( + String text, + Attributes attributes, + ) { + final span = dom.Element.tag(HTMLTags.span); + final cssString = convertAttributesToCssStyle(attributes); + if (cssString.isNotEmpty) { + span.attributes['style'] = cssString; } - if (attributes[BuiltInAttributeKey.bold] == true) { + span.append(dom.Text(text)); + return span; + } + + String convertAttributesToCssStyle(Map attributes) { + final cssMap = {}; + + if (attributes.bold) { cssMap['font-weight'] = 'bold'; } - final textDecoration = _textDecorationsFromAttributes(attributes); - if (textDecoration.isNotEmpty) { - cssMap['text-decoration'] = textDecoration; + + if (attributes.underline) { + cssMap['text-decoration'] = 'underline'; + } else if (attributes.strikethrough) { + cssMap['text-decoration'] = 'line-through'; } - if (attributes[BuiltInAttributeKey.italic] == true) { + if (attributes.italic) { cssMap['font-style'] = 'italic'; } - return _cssMapToCssStyle(cssMap); - } - String _cssMapToCssStyle(Map cssMap) { - return cssMap.entries.map((e) => '${e.key}: ${e.value}').join('; '); - } - - String _textDecorationsFromAttributes(Attributes attributes) { - final List textDecoration = []; - if (attributes[BuiltInAttributeKey.strikethrough] == true) { - textDecoration.add('line-through'); + final backgroundColor = attributes.backgroundColor; + if (backgroundColor != null) { + cssMap['background-color'] = backgroundColor.toRgbaString(); } - if (attributes[BuiltInAttributeKey.underline] == true) { - textDecoration.add('underline'); + + final color = attributes.color; + if (color != null) { + cssMap['color'] = color.toRgbaString(); } - return textDecoration.join(' '); + return cssMap.entries.map((e) => '${e.key}: ${e.value}').join('; '); } }