Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: table html encoder and decoder added #449

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/src/plugins/html/encoder/parser/html_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export 'html_node_parser.dart';
export 'image_node_parser.dart';
export 'numbered_list_node_parser.dart';
export 'quote_node_parser.dart';
export 'table_node_parser.dart';
export 'text_node_parser.dart';
export 'todo_list_node_parser.dart';
69 changes: 69 additions & 0 deletions lib/src/plugins/html/encoder/parser/table_node_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:collection/collection.dart';
import 'package:html/dom.dart' as dom;

import '../../../../editor/block_component/table_block_component/util.dart';

class HtmlTableNodeParser extends HTMLNodeParser {
const HtmlTableNodeParser();

@override

Check warning on line 10 in lib/src/plugins/html/encoder/parser/table_node_parser.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/encoder/parser/table_node_parser.dart#L10

Added line #L10 was not covered by tests
String get id => TableBlockKeys.type;

@override
String transformNodeToHTMLString(
Node node, {
required List<HTMLNodeParser> encodeParsers,
}) {
assert(node.type == TableBlockKeys.type);

return toHTMLString(
transformNodeToDomNodes(node, encodeParsers: encodeParsers),
);
}

@override
List<dom.Node> transformNodeToDomNodes(
Node node, {
required List<HTMLNodeParser> encodeParsers,
}) {
final int rowsLen = node.attributes[TableBlockKeys.rowsLen],
colsLen = node.attributes[TableBlockKeys.colsLen];
final List<dom.Node> domNodes = [];

for (var i = 0; i < rowsLen; i++) {
final List<dom.Node> nodes = [];
for (var j = 0; j < colsLen; j++) {
final Node cell = getCellNode(node, j, i)!;

for (final childnode in cell.children) {
HTMLNodeParser? parser = encodeParsers.firstWhereOrNull(
(element) => element.id == childnode.type,
);

if (parser != null) {
nodes.add(
wrapChildrenNodesWithTagName(
HTMLTags.tabledata,
childNodes: parser.transformNodeToDomNodes(
childnode,
encodeParsers: encodeParsers,
),
),
);
}
}
}
final rowelement =
wrapChildrenNodesWithTagName(HTMLTags.tableRow, childNodes: nodes);

domNodes.add(rowelement);
}

final element =
wrapChildrenNodesWithTagName(HTMLTags.table, childNodes: domNodes);
return [
element,
];
}
}
1 change: 1 addition & 0 deletions lib/src/plugins/html/html_document.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ String documentToHTML(
const HTMLQuoteNodeParser(),
const HTMLHeadingNodeParser(),
const HTMLImageNodeParser(),
const HtmlTableNodeParser()
],
).encode(document);
}
Expand Down
142 changes: 140 additions & 2 deletions lib/src/plugins/html/html_document_decoder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import 'dart:convert';

import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/block_component/table_block_component/table_node.dart';
import 'package:html/dom.dart' as dom;
import 'package:html/parser.dart' show parse;

Expand Down Expand Up @@ -78,12 +79,14 @@
return _parseUnOrderListElement(element);
case HTMLTags.orderedList:
return _parseOrderListElement(element);
case HTMLTags.table:
return _parseTable(element);
case HTMLTags.list:
return [
_parseListElement(
element,
type: type,
)
),
];
case HTMLTags.paragraph:
return _parseParagraphElement(element);
Expand All @@ -96,6 +99,134 @@
}
}

Iterable<Node> _parseTable(dom.Element element) {
final List<Node> tablenodes = [];
int columnLenth = 0;
int rowLength = 0;
for (final data in element.children) {
final (col, row, rwdata) = _parsetableRows(data);
columnLenth = columnLenth + col;
rowLength = rowLength + row;

tablenodes.addAll(rwdata);
}

return [
TableNode(
node: Node(
type: TableBlockKeys.type,
attributes: {
TableBlockKeys.rowsLen: rowLength,
TableBlockKeys.colsLen: columnLenth,
TableBlockKeys.colDefaultWidth: TableDefaults.colWidth,
TableBlockKeys.rowDefaultHeight: TableDefaults.rowHeight,
TableBlockKeys.colMinimumWidth: TableDefaults.colMinimumWidth,
},
children: tablenodes,
),
).node,
];
}

(int, int, List<Node>) _parsetableRows(dom.Element element) {
final List<Node> nodes = [];
int colLength = 0;
int rowLength = 0;

for (final data in element.children) {
final tabledata = _parsetableData(data, rowPosition: rowLength);
if (colLength == 0) {
colLength = tabledata.length;
}
nodes.addAll(tabledata);
rowLength++;
}
return (colLength, rowLength, nodes);
}

Iterable<Node> _parsetableData(
dom.Element element, {
required int rowPosition,
}) {
final List<Node> nodes = [];
int columnPosition = 0;

for (final data in element.children) {
alihassan143 marked this conversation as resolved.
Show resolved Hide resolved
Attributes attributes = {
TableCellBlockKeys.colPosition: columnPosition,
TableCellBlockKeys.rowPosition: rowPosition,
};
if (data.attributes.isNotEmpty) {
final deltaAttributes = _getDeltaAttributesFromHTMLAttributes(
element.attributes,

Check warning on line 161 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L160-L161

Added lines #L160 - L161 were not covered by tests
) ??
{};
attributes.addAll(deltaAttributes);

Check warning on line 164 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L163-L164

Added lines #L163 - L164 were not covered by tests
}

List<Node> children;
if (data.children.isEmpty) {
children = [paragraphNode(text: data.text)];

Check warning on line 169 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L169

Added line #L169 was not covered by tests
} else {
children = _parseTableSpecialNodes(data).toList();
}

final node = Node(
type: TableCellBlockKeys.type,
attributes: attributes,
children: children,
);

nodes.add(node);
columnPosition++;
}

return nodes;
}

Iterable<Node> _parseTableSpecialNodes(dom.Element element) {
final List<Node> nodes = [];

if (element.children.isNotEmpty) {
for (final childrens in element.children) {
nodes.addAll(_parseTableDataElementsData(childrens));
}
} else {
nodes.addAll(_parseTableDataElementsData(element));

Check warning on line 195 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L195

Added line #L195 was not covered by tests
}
return nodes;
}

List<Node> _parseTableDataElementsData(dom.Element element) {
final List<Node> nodes = [];
final delta = Delta();
final localName = element.localName;

if (HTMLTags.formattingElements.contains(localName)) {
final attributes = _parserFormattingElementAttributes(element);
delta.insert(element.text, attributes: attributes);

Check warning on line 207 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L206-L207

Added lines #L206 - L207 were not covered by tests
} else if (HTMLTags.specialElements.contains(localName)) {
if (delta.isNotEmpty) {
nodes.add(paragraphNode(delta: delta));

Check warning on line 210 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L210

Added line #L210 was not covered by tests
}
nodes.addAll(
_parseSpecialElements(
element,
type: ParagraphBlockKeys.type,
),
);
} else if (element is dom.Text) {

Check warning on line 218 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L218

Added line #L218 was not covered by tests
// skip the empty text node

delta.insert(element.text);

Check warning on line 221 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L221

Added line #L221 was not covered by tests
}

if (delta.isNotEmpty) {
nodes.add(paragraphNode(delta: delta));

Check warning on line 225 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L225

Added line #L225 was not covered by tests
}
return nodes;
}

Attributes _parserFormattingElementAttributes(
dom.Element element,
) {
Expand Down Expand Up @@ -130,6 +261,7 @@
attributes = {AppFlowyRichTextKeys.href: href};
}
break;

case HTMLTags.strikethrough:
attributes = {AppFlowyRichTextKeys.strikethrough: true};
break;
Expand All @@ -152,7 +284,7 @@
level: level,
delta: delta,
),
...specialNodes
...specialNodes,
];
}

Expand Down Expand Up @@ -360,6 +492,10 @@
static const blockQuote = 'blockquote';
static const div = 'div';
static const divider = 'hr';
static const table = 'table';
static const tableRow = 'tr';
static const tableheader = "th";
static const tabledata = "td";
static const section = 'section';
static const font = 'font';

Expand All @@ -385,6 +521,7 @@
HTMLTags.orderedList,
HTMLTags.div,
HTMLTags.list,
HTMLTags.table,
HTMLTags.paragraph,
HTMLTags.blockQuote,
HTMLTags.checkbox,
Expand All @@ -396,6 +533,7 @@
return tag == h1 ||
tag == h2 ||
tag == h3 ||
tag == table ||

Check warning on line 536 in lib/src/plugins/html/html_document_decoder.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/plugins/html/html_document_decoder.dart#L536

Added line #L536 was not covered by tests
tag == checkbox ||
tag == paragraph ||
tag == div ||
Expand Down
Loading
Loading