From 2c5d5c7ba45b47510b117060dfc9dcfa2b1002ef Mon Sep 17 00:00:00 2001 From: Old Autumn Date: Tue, 30 Jan 2024 09:41:25 +0800 Subject: [PATCH] fix: add support for breakline and divider (#690) * add support for breakline * add test for br tag parse * fix test failure case. * feat: add html support --- .../encoder/parser/divider_node_parser.dart | 27 +++++++++++++++++++ .../html/encoder/parser/text_node_parser.dart | 4 ++- lib/src/plugins/html/html_document.dart | 2 ++ .../plugins/html/html_document_decoder.dart | 1 + .../encoder/document_html_encoder_test.dart | 7 +++-- .../encoder/parser/text_node_parser_test.dart | 26 +++++++++++++++++- 6 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 lib/src/plugins/html/encoder/parser/divider_node_parser.dart diff --git a/lib/src/plugins/html/encoder/parser/divider_node_parser.dart b/lib/src/plugins/html/encoder/parser/divider_node_parser.dart new file mode 100644 index 000000000..3f2bf1225 --- /dev/null +++ b/lib/src/plugins/html/encoder/parser/divider_node_parser.dart @@ -0,0 +1,27 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:html/dom.dart' as dom; + +class HTMLDividerNodeParser extends HTMLNodeParser { + const HTMLDividerNodeParser(); + + @override + String get id => DividerBlockKeys.type; + + @override + String transformNodeToHTMLString( + Node node, { + required List encodeParsers, + }) { + return toHTMLString( + transformNodeToDomNodes(node, encodeParsers: encodeParsers), + ); + } + + @override + List transformNodeToDomNodes( + Node node, { + required List encodeParsers, + }) { + return [dom.Element.tag(HTMLTags.divider)]; + } +} diff --git a/lib/src/plugins/html/encoder/parser/text_node_parser.dart b/lib/src/plugins/html/encoder/parser/text_node_parser.dart index 88f2d1198..7568b2ddf 100644 --- a/lib/src/plugins/html/encoder/parser/text_node_parser.dart +++ b/lib/src/plugins/html/encoder/parser/text_node_parser.dart @@ -30,7 +30,9 @@ class HTMLTextNodeParser extends HTMLNodeParser { encodeParsers: encodeParsers, ), ); - + if (domNodes.isEmpty) { + return [dom.Element.tag(HTMLTags.br)]; + } final element = wrapChildrenNodesWithTagName(HTMLTags.paragraph, childNodes: domNodes); return [element]; diff --git a/lib/src/plugins/html/html_document.dart b/lib/src/plugins/html/html_document.dart index c2fa3fd4a..e4d7f05c6 100644 --- a/lib/src/plugins/html/html_document.dart +++ b/lib/src/plugins/html/html_document.dart @@ -3,6 +3,7 @@ library delta_markdown; import 'dart:convert'; import 'package:appflowy_editor/src/core/document/document.dart'; +import 'package:appflowy_editor/src/plugins/html/encoder/parser/divider_node_parser.dart'; import 'package:appflowy_editor/src/plugins/html/html_document_decoder.dart'; import 'package:appflowy_editor/src/plugins/html/html_document_encoder.dart'; @@ -29,6 +30,7 @@ String documentToHTML( const HTMLHeadingNodeParser(), const HTMLImageNodeParser(), const HtmlTableNodeParser(), + const HTMLDividerNodeParser(), ], ).encode(document); } diff --git a/lib/src/plugins/html/html_document_decoder.dart b/lib/src/plugins/html/html_document_decoder.dart index 9a8852e5a..9820fd63a 100644 --- a/lib/src/plugins/html/html_document_decoder.dart +++ b/lib/src/plugins/html/html_document_decoder.dart @@ -473,6 +473,7 @@ class HTMLTags { static const h1 = 'h1'; static const h2 = 'h2'; static const h3 = 'h3'; + static const br = 'br'; static const orderedList = 'ol'; static const unorderedList = 'ul'; static const list = 'li'; diff --git a/test/plugins/html/encoder/document_html_encoder_test.dart b/test/plugins/html/encoder/document_html_encoder_test.dart index 9450e9581..5b17cd614 100644 --- a/test/plugins/html/encoder/document_html_encoder_test.dart +++ b/test/plugins/html/encoder/document_html_encoder_test.dart @@ -1,4 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor/src/plugins/html/encoder/parser/divider_node_parser.dart'; import 'package:flutter_test/flutter_test.dart'; void main() async { @@ -11,6 +12,7 @@ void main() async { const HTMLHeadingNodeParser(), const HTMLImageNodeParser(), const HtmlTableNodeParser(), + const HTMLDividerNodeParser(), ]; group('document_html_encoder_test.dart', () { setUpAll(() { @@ -34,7 +36,7 @@ void main() async { } const example = - '''

AppFlowyEditor

👋 Welcome to AppFlowy Editor

AppFlowy Editor is a highly customizable rich-text editor

Here is an example your you can give a try

Span element

Span element two

Span element three

This is an anchor tag!

Features!

  • [x] Customizable
  • [x] Test-covered
  • [ ] more to come!
  • First item
  • Second item
  • List element
This is a quote!

Code block

Italic one

Italic two

Bold tag

You can also use AppFlowy Editor as a component to build your own app.

Awesome features

If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!

'''; + '''

AppFlowyEditor

👋 Welcome to AppFlowy Editor

AppFlowy Editor is a highly customizable rich-text editor

Here is an example your you can give a try

Span element

Span element two

Span element three

This is an anchor tag!

Features!

  • [x] Customizable
  • [x] Test-covered
  • [ ] more to come!
  • First item
  • Second item
  • List element
This is a quote!

Code block

Italic one

Italic two

Bold tag

You can also use AppFlowy Editor as a component to build your own app.


Awesome features

If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!



'''; const delta = { 'document': { @@ -275,6 +277,7 @@ const delta = { ], }, }, + {'type': 'divider'}, { 'type': 'heading', 'data': { @@ -307,7 +310,7 @@ const delta = { }, }; const nestedHTML = - '''

Welcome to the playground

In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting. The playground is a demo environment built with @lexical/react. Try typing in some text with different formats.

Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!

If you'd like to find out more about Lexical, you can:

  • Playground code can be found here.
  • Playground code can be found here.

Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance 🙂.

'''; + '''

Welcome to the playground

In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of the editor. You can disable it by pressing on the settings control in the bottom-left of your screen and toggling the debug view setting. The playground is a demo environment built with @lexical/react. Try typing in some text with different formats.

Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!


If you'd like to find out more about Lexical, you can:

  • Playground code can be found here.
  • Playground code can be found here.

Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance 🙂.


'''; const nestedDelta = { 'document': { 'type': 'page', diff --git a/test/plugins/html/encoder/parser/text_node_parser_test.dart b/test/plugins/html/encoder/parser/text_node_parser_test.dart index 5a9f2be7b..24b7522a5 100644 --- a/test/plugins/html/encoder/parser/text_node_parser_test.dart +++ b/test/plugins/html/encoder/parser/text_node_parser_test.dart @@ -1,4 +1,5 @@ import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:appflowy_editor/src/plugins/html/encoder/parser/divider_node_parser.dart'; import 'package:flutter_test/flutter_test.dart'; void main() async { @@ -11,8 +12,9 @@ void main() async { const HTMLHeadingNodeParser(), const HTMLImageNodeParser(), const HtmlTableNodeParser(), + const HTMLDividerNodeParser(), ]; - group('html_text_node_parser.dart', () { + group('text_node_parser.dart', () { const text = 'Welcome to AppFlowy'; test('heading style', () { @@ -44,6 +46,19 @@ void main() async { ); }); + test('empty line', () { + final node = paragraphNode( + attributes: { + 'delta': [], + }, + ); + expect( + const HTMLTextNodeParser() + .transformNodeToHTMLString(node, encodeParsers: parser), + '
', + ); + }); + test('numbered list style', () { final node = numberedListNode( attributes: { @@ -95,6 +110,15 @@ void main() async { ); }); + test('divider', () { + final node = dividerNode(); + expect( + const HTMLDividerNodeParser() + .transformNodeToHTMLString(node, encodeParsers: parser), + '
', + ); + }); + test('fallback', () { final node = paragraphNode( attributes: {