refactor: html text into class

This commit is contained in:
Vincent Chan 2022-08-15 14:21:15 +08:00
parent e34ff50923
commit a3ffbe2f00

View File

@ -1,4 +1,5 @@
import 'dart:collection'; import 'dart:collection';
import 'dart:ui';
import 'package:flowy_editor/src/document/attributes.dart'; import 'package:flowy_editor/src/document/attributes.dart';
import 'package:flowy_editor/src/document/node.dart'; import 'package:flowy_editor/src/document/node.dart';
@ -8,23 +9,35 @@ import 'package:flutter/material.dart';
import 'package:html/parser.dart' show parse; import 'package:html/parser.dart' show parse;
import 'package:html/dom.dart' as html; import 'package:html/dom.dart' as html;
const String tagH1 = "h1"; class HTMLTag {
const String tagH2 = "h2"; static const h1 = "h1";
const String tagH3 = "h3"; static const h2 = "h2";
const String tagOrderedList = "ol"; static const h3 = "h3";
const String tagUnorderedList = "ul"; static const orderedList = "ol";
const String tagList = "li"; static const unorderedList = "ul";
const String tagParagraph = "p"; static const list = "li";
const String tagImage = "img"; static const paragraph = "p";
const String tagAnchor = "a"; static const image = "img";
const String tagItalic = "i"; static const anchor = "a";
const String tagBold = "b"; static const italic = "i";
const String tagUnderline = "u"; static const bold = "b";
const String tagDel = "del"; static const underline = "u";
const String tagStrong = "strong"; static const del = "del";
const String tagSpan = "span"; static const strong = "strong";
const String tagCode = "code"; static const span = "span";
const String tagBlockQuote = "blockquote"; static const code = "code";
static const blockQuote = "blockquote";
static const div = "div";
static bool isTopLevel(String tag) {
return tag == h1 ||
tag == h2 ||
tag == h3 ||
tag == paragraph ||
tag == div ||
tag == blockQuote;
}
}
extension on Color { extension on Color {
String toRgbaString() { String toRgbaString() {
@ -55,15 +68,15 @@ class HTMLToNodesConverter {
final result = <Node>[]; final result = <Node>[];
for (final child in childNodes) { for (final child in childNodes) {
if (child is html.Element) { if (child is html.Element) {
if (child.localName == tagAnchor || if (child.localName == HTMLTag.anchor ||
child.localName == tagSpan || child.localName == HTMLTag.span ||
child.localName == tagCode || child.localName == HTMLTag.code ||
child.localName == tagStrong || child.localName == HTMLTag.strong ||
child.localName == tagUnderline || child.localName == HTMLTag.underline ||
child.localName == tagItalic || child.localName == HTMLTag.italic ||
child.localName == tagDel) { child.localName == HTMLTag.del) {
_handleRichTextElement(delta, child); _handleRichTextElement(delta, child);
} else if (child.localName == tagBold) { } else if (child.localName == HTMLTag.bold) {
// Google docs wraps the the content inside the `<b></b>` tag. // Google docs wraps the the content inside the `<b></b>` tag.
// It's strange // It's strange
if (!_inParagraph) { if (!_inParagraph) {
@ -71,7 +84,7 @@ class HTMLToNodesConverter {
} else { } else {
result.add(_handleRichText(child)); result.add(_handleRichText(child));
} }
} else if (child.localName == tagBlockQuote) { } else if (child.localName == HTMLTag.blockQuote) {
result.addAll(_handleBlockQuote(child)); result.addAll(_handleBlockQuote(child));
} else { } else {
result.addAll(_handleElement(child)); result.addAll(_handleElement(child));
@ -105,19 +118,19 @@ class HTMLToNodesConverter {
List<Node> _handleElement(html.Element element, List<Node> _handleElement(html.Element element,
[Map<String, dynamic>? attributes]) { [Map<String, dynamic>? attributes]) {
if (element.localName == tagH1) { if (element.localName == HTMLTag.h1) {
return [_handleHeadingElement(element, tagH1)]; return [_handleHeadingElement(element, HTMLTag.h1)];
} else if (element.localName == tagH2) { } else if (element.localName == HTMLTag.h2) {
return [_handleHeadingElement(element, tagH2)]; return [_handleHeadingElement(element, HTMLTag.h2)];
} else if (element.localName == tagH3) { } else if (element.localName == HTMLTag.h3) {
return [_handleHeadingElement(element, tagH3)]; return [_handleHeadingElement(element, HTMLTag.h3)];
} else if (element.localName == tagUnorderedList) { } else if (element.localName == HTMLTag.unorderedList) {
return _handleUnorderedList(element); return _handleUnorderedList(element);
} else if (element.localName == tagOrderedList) { } else if (element.localName == HTMLTag.orderedList) {
return _handleOrderedList(element); return _handleOrderedList(element);
} else if (element.localName == tagList) { } else if (element.localName == HTMLTag.list) {
return _handleListElement(element); return _handleListElement(element);
} else if (element.localName == tagParagraph) { } else if (element.localName == HTMLTag.paragraph) {
return [_handleParagraph(element, attributes)]; return [_handleParagraph(element, attributes)];
} else { } else {
final delta = Delta(); final delta = Delta();
@ -236,23 +249,24 @@ class HTMLToNodesConverter {
} }
_handleRichTextElement(Delta delta, html.Element element) { _handleRichTextElement(Delta delta, html.Element element) {
if (element.localName == tagSpan) { if (element.localName == HTMLTag.span) {
delta.insert(element.text, delta.insert(element.text,
_getDeltaAttributesFromHtmlAttributes(element.attributes)); _getDeltaAttributesFromHtmlAttributes(element.attributes));
} else if (element.localName == tagAnchor) { } else if (element.localName == HTMLTag.anchor) {
final hyperLink = element.attributes["href"]; final hyperLink = element.attributes["href"];
Map<String, dynamic>? attributes; Map<String, dynamic>? attributes;
if (hyperLink != null) { if (hyperLink != null) {
attributes = {"href": hyperLink}; attributes = {"href": hyperLink};
} }
delta.insert(element.text, attributes); delta.insert(element.text, attributes);
} else if (element.localName == tagStrong || element.localName == tagBold) { } else if (element.localName == HTMLTag.strong ||
element.localName == HTMLTag.bold) {
delta.insert(element.text, {StyleKey.bold: true}); delta.insert(element.text, {StyleKey.bold: true});
} else if (element.localName == tagUnderline) { } else if (element.localName == HTMLTag.underline) {
delta.insert(element.text, {StyleKey.underline: true}); delta.insert(element.text, {StyleKey.underline: true});
} else if (element.localName == tagItalic) { } else if (element.localName == HTMLTag.italic) {
delta.insert(element.text, {StyleKey.italic: true}); delta.insert(element.text, {StyleKey.italic: true});
} else if (element.localName == tagDel) { } else if (element.localName == HTMLTag.del) {
delta.insert(element.text, {StyleKey.strikethrough: true}); delta.insert(element.text, {StyleKey.strikethrough: true});
} else { } else {
delta.insert(element.text); delta.insert(element.text);
@ -265,7 +279,7 @@ class HTMLToNodesConverter {
/// A container contains a <img /> will be regarded as a image block /// A container contains a <img /> will be regarded as a image block
Node _handleRichText(html.Element element, Node _handleRichText(html.Element element,
[Map<String, dynamic>? attributes]) { [Map<String, dynamic>? attributes]) {
final image = element.querySelector(tagImage); final image = element.querySelector(HTMLTag.image);
if (image != null) { if (image != null) {
final imageNode = _handleImage(image); final imageNode = _handleImage(image);
return imageNode; return imageNode;
@ -419,10 +433,10 @@ class NodesToHTMLConverter {
} }
_addElement(TextNode textNode, html.Element element) { _addElement(TextNode textNode, html.Element element) {
if (element.localName == tagList) { if (element.localName == HTMLTag.list) {
final isNumbered = textNode.attributes["subtype"] == StyleKey.numberList; final isNumbered = textNode.attributes["subtype"] == StyleKey.numberList;
_stashListContainer ??= _stashListContainer ??= html.Element.tag(
html.Element.tag(isNumbered ? tagOrderedList : tagUnorderedList); isNumbered ? HTMLTag.orderedList : HTMLTag.unorderedList);
_stashListContainer?.append(element); _stashListContainer?.append(element);
} else { } else {
if (_stashListContainer != null) { if (_stashListContainer != null) {
@ -524,10 +538,10 @@ class NodesToHTMLConverter {
} }
final childNodes = <html.Node>[]; final childNodes = <html.Node>[];
String tagName = tagParagraph; String tagName = HTMLTag.paragraph;
if (subType == StyleKey.bulletedList || subType == StyleKey.numberList) { if (subType == StyleKey.bulletedList || subType == StyleKey.numberList) {
tagName = tagList; tagName = HTMLTag.list;
} else if (subType == StyleKey.checkbox) { } else if (subType == StyleKey.checkbox) {
final node = html.Element.html('<input type="checkbox" />'); final node = html.Element.html('<input type="checkbox" />');
if (checked != null && checked) { if (checked != null && checked) {
@ -536,14 +550,14 @@ class NodesToHTMLConverter {
childNodes.add(node); childNodes.add(node);
} else if (subType == StyleKey.heading) { } else if (subType == StyleKey.heading) {
if (heading == StyleKey.h1) { if (heading == StyleKey.h1) {
tagName = tagH1; tagName = HTMLTag.h1;
} else if (heading == StyleKey.h2) { } else if (heading == StyleKey.h2) {
tagName = tagH2; tagName = HTMLTag.h2;
} else if (heading == StyleKey.h3) { } else if (heading == StyleKey.h3) {
tagName = tagH3; tagName = HTMLTag.h3;
} }
} else if (subType == StyleKey.quote) { } else if (subType == StyleKey.quote) {
tagName = tagBlockQuote; tagName = HTMLTag.blockQuote;
} }
for (final op in delta) { for (final op in delta) {
@ -551,26 +565,26 @@ class NodesToHTMLConverter {
final attributes = op.attributes; final attributes = op.attributes;
if (attributes != null) { if (attributes != null) {
if (attributes.length == 1 && attributes[StyleKey.bold] == true) { if (attributes.length == 1 && attributes[StyleKey.bold] == true) {
final strong = html.Element.tag(tagStrong); final strong = html.Element.tag(HTMLTag.strong);
strong.append(html.Text(op.content)); strong.append(html.Text(op.content));
childNodes.add(strong); childNodes.add(strong);
} else if (attributes.length == 1 && } else if (attributes.length == 1 &&
attributes[StyleKey.underline] == true) { attributes[StyleKey.underline] == true) {
final strong = html.Element.tag(tagUnderline); final strong = html.Element.tag(HTMLTag.underline);
strong.append(html.Text(op.content)); strong.append(html.Text(op.content));
childNodes.add(strong); childNodes.add(strong);
} else if (attributes.length == 1 && } else if (attributes.length == 1 &&
attributes[StyleKey.italic] == true) { attributes[StyleKey.italic] == true) {
final strong = html.Element.tag(tagItalic); final strong = html.Element.tag(HTMLTag.italic);
strong.append(html.Text(op.content)); strong.append(html.Text(op.content));
childNodes.add(strong); childNodes.add(strong);
} else if (attributes.length == 1 && } else if (attributes.length == 1 &&
attributes[StyleKey.strikethrough] == true) { attributes[StyleKey.strikethrough] == true) {
final strong = html.Element.tag(tagDel); final strong = html.Element.tag(HTMLTag.del);
strong.append(html.Text(op.content)); strong.append(html.Text(op.content));
childNodes.add(strong); childNodes.add(strong);
} else { } else {
final span = html.Element.tag(tagSpan); final span = html.Element.tag(HTMLTag.span);
final cssString = _attributesToCssStyle(attributes); final cssString = _attributesToCssStyle(attributes);
if (cssString.isNotEmpty) { if (cssString.isNotEmpty) {
span.attributes["style"] = cssString; span.attributes["style"] = cssString;
@ -584,24 +598,20 @@ class NodesToHTMLConverter {
} }
} }
if (tagName == tagBlockQuote) { if (tagName == HTMLTag.blockQuote) {
final p = html.Element.tag(tagParagraph); final p = html.Element.tag(HTMLTag.paragraph);
for (final node in childNodes) { for (final node in childNodes) {
p.append(node); p.append(node);
} }
final blockQuote = html.Element.tag(tagName); final blockQuote = html.Element.tag(tagName);
blockQuote.append(p); blockQuote.append(p);
return blockQuote; return blockQuote;
} else if (tagName != tagParagraph && } else if (!HTMLTag.isTopLevel(tagName)) {
tagName != tagH1 && final p = html.Element.tag(HTMLTag.paragraph);
tagName != tagH2 &&
tagName != tagH3 &&
tagName != tagBlockQuote) {
final p = html.Element.tag(tagParagraph);
for (final node in childNodes) { for (final node in childNodes) {
p.append(node); p.append(node);
} }
final result = html.Element.tag(tagList); final result = html.Element.tag(HTMLTag.list);
result.append(p); result.append(p);
return result; return result;
} else { } else {